使用 Cloud HSM 密钥通过 NGINX 进行 TLS 分流

本指南介绍了如何设置 NGINX,以便在 Debian 11 (Bullseye) 上使用 Cloud HSM 密钥进行 TLS 分流。您可能需要修改这些命令才能在您的操作系统或 Linux 发行版中使用。

您可以在 kms-solutions GitHub 代码库中找到本教程基于 Terraform 的蓝图版本。

使用场景

将 Cloud HSM 密钥与 NGINX 搭配使用以进行 TLS 分流有助于满足以下企业安全需求:

  • 您希望 NGINX Web 服务器将 TLS 加密操作分流到 Cloud HSM。
  • 您不想将证书的私钥存储在托管 Web 应用的 Compute Engine 实例的本地文件系统中。
  • 您需要满足法规要求,即面向公众的应用需要其证书由具有 FIPS 140-2 3 级认证的 HSM 保护。
  • 您想使用 NGINX 创建具有 TLS 终止的反向代理,以保护您的 Web 应用。

准备工作

在继续之前,请完成将 Cloud HSM 密钥与 OpenSSL 结合使用中的步骤。

OpenSSL 设置完成后,请确保安装了最新版本的 nginx

sudo apt-get update
sudo apt-get install libengine-pkcs11-openssl opensc nginx

安全配置建议

请按照以下建议来保护托管 NGINX 的实例:

  1. 按照为实例创建和启用服务账号中的说明托管 NGINX。

    1. 分配以下角色:
      • roles/cloudkms.signerVerifier
      • roles/cloudkms.viewer
  2. 请按如下方式配置组织政策,以限制外部 IP 和服务账号密钥的创建。

    • constraints/compute.vmExternalIpAccess
    • constraints/iam.disableServiceAccountKeyCreation
  3. 创建启用了专用 Google 访问通道的自定义子网。

  4. 配置防火墙规则。

  5. 创建一个 Linux 虚拟机,并按如下方式进行配置:

    • 选择您之前创建的正确服务账号。
    • 选择您之前创建的网络。
      • 为所有防火墙规则添加适当的标签。
      • 确保子网的“外部 IP”字段设置为 none
  6. 向您的身份授予实例上的 IAP-Secured Tunnel User (roles/iap.tunnelResourceAccessor) 角色。

创建和配置 Cloud KMS 托管的签名密钥

下一部分将详细介绍创建和配置 Cloud KMS 托管的签名密钥所需的步骤。

创建 Cloud KMS 托管的签名密钥

在您的Google Cloud 项目内,在您之前为 OpenSSL 配置的密钥环中创建 Cloud KMS EC-P256-SHA256 签名密钥:

gcloud kms keys create NGINX_KEY \
  --keyring "KEY_RING" --project "PROJECT_ID" \
  --location "LOCATION" --purpose "asymmetric-signing" \
  --default-algorithm "ec-sign-p256-sha256" --protection-level "hsm"

使用 IAP 通过 SSH 连接到虚拟机

使用以下命令通过 IAP 通过 SSH 连接到您的虚拟机:

gcloud compute ssh INSTANCE \
  --zone ZONE --tunnel-through-iap

如果您遇到问题,请确认您是否使用了 --tunnel-through-iap 标志。此外,请确认您已为使用 gcloud CLI 进行身份验证的实例分配了 IAP-Secured Tunnel User (roles/iap.tunnelResourceAccessor) 角色。

使用 OpenSSL 创建证书

对于生产环境,请创建证书签名请求 (CSR)。如需了解详情,请参阅生成 CSR 的示例。将 CSR 提供给证书授权机构 (CA),以便他们为您创建证书。在后续部分中,使用 CA 提供的证书。

例如,您可以使用 Cloud KMS 托管的签名密钥生成自签名证书。为此,OpenSSL 允许您使用 PKCS #11 URI(而非常规路径),通过其标签标识密钥(对于 Cloud KMS 密钥,标签是 CryptoKey 名称)。

openssl req -new -x509 -days 3650 -subj '/CN=CERTIFICATE_NAME/' \
  DIGEST_FLAG -engine pkcs11 -keyform engine \
  -key PKCS_KEY_TYPE=KEY_IDENTIFIER > CA_CERT

替换以下内容:

  • CERTIFICATE_NAME:证书的名称。
  • DIGEST_FLAG:非对称签名密钥使用的摘要算法。请根据键使用 -sha256-sha384-sha512
  • PKCS_KEY_TYPE:用于标识键的标识符类型。如需使用最新的密钥版本,请将 pkcs11:object 与密钥的名称搭配使用。如需使用特定密钥版本,请将 pkcs11:id 与密钥版本的完整资源 ID 搭配使用。
  • KEY_IDENTIFIER:密钥的标识符。如果您使用的是 pkcs11:object,请使用键的名称,例如 NGINX_KEY。如果您使用的是 pkcs11:id,请使用密钥或密钥版本的完整资源 ID,例如 projects/PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/NGINX_KEY/cryptoKeyVersions/KEY_VERSION
  • CA_CERT:您要保存证书文件的路径。

如果该命令失败,则 PKCS11_MODULE_PATH 可能设置不正确,或者您可能没有使用 Cloud KMS 签名密钥的适当权限。

您现在应该有一个如下所示的证书:

-----BEGIN CERTIFICATE-----
...
...
...
-----END CERTIFICATE-----

为 NGINX 安装证书

运行以下命令,创建放置您的公共证书的位置:

sudo mkdir /etc/ssl/nginx
sudo mv CA_CERT /etc/ssl/nginx

将环境配置为使用 PKCS #11 库

下一部分将详细介绍准备和测试环境所需的步骤。

为 NGINX 准备库配置

使用以下命令允许 NGINX 使用库记录其 PKCS #11 引擎操作:

sudo mkdir /var/log/kmsp11
sudo chown www-data /var/log/kmsp11

创建一个空的库配置文件,并为 NGINX 授予适当的权限。

sudo touch /etc/nginx/pkcs11-config.yaml
sudo chmod 744 /etc/nginx/pkcs11-config.yaml

修改空配置文件,并添加所需的配置,如以下代码段所示:

# cat /etc/nginx/pkcs11-config.yaml
---
tokens:
  - key_ring: "projects/PROJECT_ID/locations/LOCATION/keyRings/KEY_RING"
log_directory: "/var/log/kmsp11"

测试 OpenSSL 配置

运行以下命令:

openssl engine -tt -c -v pkcs11

您将看到如下所示的输出:

(pkcs11) pkcs11 engine
 [RSA, rsaEncryption, id-ecPublicKey]
     [ available ]
     SO_PATH, MODULE_PATH, PIN, VERBOSE, QUIET, INIT_ARGS, FORCE_LOGIN

配置 NGINX 以使用 Cloud HSM

通过修改几个 NGINX 文件来允许 TLS 分流。首先,在两个位置修改 /etc/nginx/nginx.conf 文件,添加一些指令以配置 NGINX 以使用 PKCS #11。

event 代码块之后、http 代码块之前,添加以下指令:

ssl_engine pkcs11;
env KMS_PKCS11_CONFIG=/etc/nginx/pkcs11-config.yaml;

在同一 /etc/nginx/nginx.conf 文件中,配置 SSL 指令以在 Cloud HSM 中使用您的证书及其私钥。在 http 代码块中,添加以下属性:

ssl_certificate "/etc/ssl/nginx/CA_CERT";
ssl_certificate_key "engine:pkcs11:PKCS_KEY_TYPE=KEY_IDENTIFIER";
ssl_protocols TLSv1.2 TLSv1.3; # Consider changing the default to only TLS1.2 or newer

# Consider defining the `ssl_ciphers` to use ciphers approved by your security teams and handle
# appropriate client compatibility requirements.

您的 /etc/nginx/nginx.conf 文件应如下所示:

user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
        worker_connections 768;
        # multi_accept on;
}

ssl_engine pkcs11;
env KMS_PKCS11_CONFIG=/etc/nginx/pkcs11-config.yaml;

http {

        #...
        #...

        # SSL configuration
        ssl_certificate "/etc/ssl/nginx/CA_CERT";
        ssl_certificate_key "engine:pkcs11:pkcs11:object=NGINX_KEY";
        ssl_protocols TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
        # ssl_ciphers YOUR_CIPHERS
        ssl_prefer_server_ciphers on;

        #...
        #...

}

配置 NGINX 以监听 TLS 流量

修改 /etc/nginx/sites-enabled/default 文件以监听 TLS 流量。取消注释 server 块中的 SSL 配置。所产生的更改应如下例所示:


server {
        listen 80 default_server;
        listen [::]:80 default_server;

        # SSL configuration
        listen 443 ssl default_server;
        listen [::]:443 ssl default_server;

        # ...
        # ...
}

向 NGINX 服务提供环境变量

运行以下命令:

sudo systemctl edit nginx.service

在随即打开的编辑器中,添加以下代码行,并将 LIBPATH 替换为 libkmsp11.so 的安装位置的值:

[Service]
Environment="GRPC_ENABLE_FORK_SUPPORT=1"
Environment="KMS_PKCS11_CONFIG=/etc/nginx/pkcs11-config.yaml"
Environment="PKCS11_MODULE_PATH=LIBPATH/libkmsp11-1.0-linux-amd64/libkmsp11.so"

配置这些值后,您需要运行以下命令才能使其可用:

sudo systemctl daemon-reload

重启启用了 TLS 分流的 NGINX

运行以下命令,以便 NGINX 重启并使用更新后的配置:

sudo systemctl start nginx

测试 NGINX 是否使用 TLS 分流到您的 Cloud HSM

使用 openssl s_client 运行以下命令,测试与 NGINX 服务器的连接:

openssl s_client -connect localhost:443

客户端应完成 SSL 握手并暂停。 客户正在等待您的输入,如下所示:

# completes SSL handshake
# ...
# ...
# ...
    Verify return code: 18 (self signed certificate)
# ...
    Max Early Data: 0
---
read R BLOCK

# When the client pauses, it’s waiting for instructions.
# Have the client get the index.html file in the root path (“/”), by typing the following:

GET /

# Press enter.
# You should now see the default NGINX index.html file.

您的审核日志现在应显示对 NGINX_KEY 密钥的操作。如需查看日志,请在 Cloud 控制台中前往 Cloud Logging。在您一直在使用的项目中,添加以下过滤条件:

resource.type="cloudkms_cryptokeyversion"

运行查询后,您应该会看到对 NGINX_KEY 密钥的非对称密钥操作。

可选配置

您可能需要创建外部直通式网络负载平衡器,以便使用外部 IP 公开 NGINX 服务器。

如果您需要将 NGINX 用作具有负载均衡功能的反向代理,请考虑更新 NGINX 配置文件。如需详细了解如何将 NGINX 配置为反向代理,请参阅 Google Cloud 平台上 NGINX Plus 的所有活动高可用性

后续步骤

现在,您已将 NGINX 服务器配置为将 TLS 分流到 Cloud HSM。