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

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

使用场景

将 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 Platform 上适用于 NGINX Plus 的 All-Active HA

后续步骤

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