Usar una clave de Cloud HSM para la descarga de TLS con NGINX

En esta guía se proporcionan instrucciones para configurar NGINX de forma que use una clave de Cloud HSM para la descarga de TLS en Debian 11 (Bullseye). Es posible que tengas que modificar estos comandos para que funcionen con tu sistema operativo o distribución de Linux.

Puedes encontrar una versión de este tutorial basada en Terraform en el repositorio de GitHub kms-solutions.

Casos prácticos

Usar una clave de Cloud HSM con NGINX para la descarga de TLS ayuda a abordar las siguientes necesidades de seguridad de la empresa:

  • Quieres que tu servidor web NGINX descargue las operaciones criptográficas de TLS en Cloud HSM.
  • No te interesa almacenar la clave privada de tu certificado en el sistema de archivos local de la instancia de Compute Engine que aloja tu aplicación web.
  • Debes cumplir los requisitos normativos en los que las aplicaciones públicas necesitan que sus certificados estén protegidos por un HSM que tenga la certificación FIPS 140-2 de nivel 3.
  • Quieres usar NGINX para crear un proxy inverso con terminación TLS para proteger tu aplicación web.

Antes de empezar

Antes de continuar, sigue los pasos que se indican en Usar una clave de Cloud HSM con OpenSSL.

Una vez que se haya completado la configuración de OpenSSL, asegúrate de que esté instalada una versión reciente de nginx:

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

Recomendaciones de configuración de seguridad

Protege la instancia que aloja NGINX siguiendo estas recomendaciones:

  1. Sigue las instrucciones para crear y habilitar cuentas de servicio para instancias para alojar NGINX.

    1. Asigna los siguientes roles:
      • roles/cloudkms.signerVerifier
      • roles/cloudkms.viewer
  2. Configura las políticas de organización de la siguiente manera para limitar las IPs externas y la creación de claves de cuentas de servicio.

    • constraints/compute.vmExternalIpAccess
    • constraints/iam.disableServiceAccountKeyCreation
  3. Crea una subred personalizada que habilite el acceso privado de Google.

  4. Configura las reglas de cortafuegos.

  5. Crea una máquina virtual Linux y configúrala de la siguiente manera:

    • Selecciona la cuenta de servicio correcta que has creado anteriormente.
    • Selecciona la red que has creado anteriormente.
      • Añade las etiquetas adecuadas a las reglas de cortafuegos.
      • Asegúrate de que el campo "IP externa" de la subred esté configurado como none.
  6. Asigna a tu identidad el rol Usuario de túneles protegidos mediante IAP (roles/iap.tunnelResourceAccessor) en la instancia.

Crear y configurar una clave de firma alojada en Cloud KMS

En las siguientes secciones se detallan los pasos necesarios para crear y configurar una clave de firma alojada en Cloud KMS.

Crear una clave de firma alojada en Cloud KMS

Crea una EC-P256-SHA256clave de firmaGoogle Cloud de Cloud KMS en tu proyecto, en el conjunto de claves que hayas configurado anteriormente para OpenSSL:

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"

Acceder a tu VM mediante IAP

Conéctate a tu VM mediante IAP con el siguiente comando:

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

Si tienes algún problema, comprueba que has usado la marca --tunnel-through-iap. Además, confirma que tienes el rol de usuario de túneles protegidos mediante IAP (roles/iap.tunnelResourceAccessor) en la instancia de la identidad autenticada con la CLI de gcloud.

Crear un certificado con OpenSSL

En un entorno de producción, crea una solicitud de firma de certificado (CSR). Para obtener más información, consulta el ejemplo para generar una CSR. Proporciona el CSR a tu autoridad de certificación (CA) para que pueda crear un certificado para ti. Usa el certificado proporcionado por tu AC en las secciones siguientes.

A modo de ejemplo, puedes generar un certificado autofirmado con la clave de firma alojada en Cloud KMS. Para ello, OpenSSL te permite usar URIs PKCS #11 en lugar de una ruta normal, que identifican la clave por su etiqueta (en el caso de las claves de Cloud KMS, la etiqueta es el nombre de 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

Haz los cambios siguientes:

  • CERTIFICATE_NAME: un nombre para el certificado.
  • DIGEST_FLAG: el algoritmo de resumen usado por la clave de firma asimétrica. Usa -sha256, -sha384 o -sha512 en función de la clave.
  • PKCS_KEY_TYPE: el tipo de identificador que se usa para identificar la clave. Para usar la versión de clave más reciente, usa pkcs11:object con el nombre de la clave. Para usar una versión de clave específica, usa pkcs11:id con el ID de recurso completo de la versión de clave.
  • KEY_IDENTIFIER: identificador de la clave. Si usas pkcs11:object, utiliza el nombre de la clave (por ejemplo, NGINX_KEY). Si usas pkcs11:id, utiliza el ID de recurso completo de la clave o de la versión de la clave. Por ejemplo, projects/PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/NGINX_KEY/cryptoKeyVersions/KEY_VERSION.
  • CA_CERT: la ruta en la que quieres guardar el archivo de certificado.

Si el comando falla, es posible que PKCS11_MODULE_PATH se haya configurado incorrectamente o que no tengas los permisos adecuados para usar la clave de firma de Cloud KMS.

Ahora debería tener un certificado con este aspecto:

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

Instalar el certificado para NGINX

Ejecuta los siguientes comandos para crear una ubicación en la que colocar tu certificado público:

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

Configurar el entorno para usar la biblioteca PKCS #11

En las siguientes secciones se detallan los pasos necesarios para preparar y probar tu entorno.

Preparar configuraciones de biblioteca para NGINX

Permite que NGINX registre sus operaciones del motor PKCS #11 con la biblioteca de la siguiente manera:

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

Crea un archivo de configuración de biblioteca vacío con los permisos adecuados para NGINX.

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

Edita el archivo de configuración vacío y añade la configuración necesaria, tal como se muestra en el siguiente fragmento:

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

Probar la configuración de OpenSSL

Ejecuta el siguiente comando:

openssl engine -tt -c -v pkcs11

Debería aparecer lo siguiente:

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

Configurar NGINX para usar Cloud HSM

Permite la descarga de TLS editando algunos archivos de NGINX. Primero, edita el archivo /etc/nginx/nginx.conf en dos lugares para añadir algunas directivas que configuren NGINX para usar PKCS #11.

Después del bloque event y antes del bloque http, añade las siguientes directivas:

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

En el mismo archivo /etc/nginx/nginx.conf, configura las directivas SSL para usar tu certificado y su clave privada en Cloud HSM. En el bloque http, añada los siguientes atributos:

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.

Tu archivo /etc/nginx/nginx.conf debería tener un aspecto similar a este:

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;

        #...
        #...

}

Configurar NGINX para que detecte el tráfico TLS

Edita el archivo /etc/nginx/sites-enabled/default para monitorizar el tráfico TLS. Descomenta la configuración de SSL en el bloque server. El cambio resultante debería ser similar al siguiente ejemplo:


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

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

        # ...
        # ...
}

Proporcionar variables de entorno al servicio NGINX

Ejecuta el siguiente comando:

sudo systemctl edit nginx.service

En el editor que se abra, añade las siguientes líneas y sustituye LIBPATH por el valor de la ubicación donde hayas instalado 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"

Una vez que haya configurado estos valores, deberá ejecutar el siguiente comando para que estén disponibles:

sudo systemctl daemon-reload

Reiniciar NGINX con la descarga de TLS

Ejecuta el siguiente comando para que NGINX se reinicie y use la configuración actualizada:

sudo systemctl start nginx

Prueba NGINX usa la descarga de TLS en tu Cloud HSM

Usa openssl s_client para probar la conexión con tu servidor NGINX ejecutando el siguiente comando:

openssl s_client -connect localhost:443

El cliente debe completar el handshake de SSL y pausar. El cliente está esperando que introduzcas información, como se muestra a continuación:

# 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.

Los registros de auditoría deberían mostrar ahora las operaciones de tu llave NGINX_KEY. Para ver los registros, ve a Cloud Logging en la consola de Cloud. En el proyecto que has estado usando, añade el siguiente filtro:

resource.type="cloudkms_cryptokeyversion"

Después de ejecutar la consulta, deberías ver las operaciones de claves asimétricas de tu clave NGINX_KEY.

Configuraciones opcionales

Es posible que tengas que crear un balanceador de carga de red de paso a través externo para exponer tu servidor NGINX con una IP externa.

Si necesitas usar NGINX como proxy inverso con balanceo de carga, considera la posibilidad de actualizar el archivo de configuración de NGINX. Consulta más información sobre cómo configurar NGINX como proxy inverso en el artículo Alta disponibilidad totalmente activa para NGINX Plus en Google Cloud Platform.

Pasos siguientes

Ahora has configurado tu servidor NGINX para que use la descarga de TLS en Cloud HSM.