Utiliser une clé Cloud HSM pour l'externalisation TLS avec NGINX

Ce guide explique comment configurer NGINX pour utiliser une clé Cloud HSM pour l'externalisation TLS sur Debian 11 (Bullseye). Vous devrez peut-être modifier ces commandes pour qu'elles fonctionnent avec votre OS ou votre distribution Linux.

Vous trouverez une version de ce tutoriel basée sur un plan Terraform dans le dépôt GitHub kms-solutions.

Cas d'utilisation

L'utilisation d'une clé Cloud HSM avec NGINX pour l'externalisation TLS permet de répondre aux besoins de sécurité d'entreprise suivants:

  • Vous souhaitez que votre serveur Web NGINX transfère les opérations cryptographiques TLS vers Cloud HSM.
  • Vous ne souhaitez pas stocker la clé privée de votre certificat dans le système de fichiers local de l'instance Compute Engine qui héberge votre application Web.
  • Vous devez respecter les exigences réglementaires lorsque les applications publiques doivent protéger leurs certificats par un HSM certifié FIPS 140-2 de niveau 3.
  • Vous souhaitez utiliser NGINX pour créer un proxy inverse avec une terminaison TLS afin de protéger votre application Web.

Avant de commencer

Avant de continuer, suivez la procédure décrite dans la section Utiliser une clé Cloud HSM avec OpenSSL.

Une fois la configuration d'OpenSSL terminée, assurez-vous qu'une version récente de nginx est installée:

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

Recommandations de configuration de la sécurité

Sécurisez votre instance qui héberge NGINX en suivant les recommandations suivantes:

  1. Suivez les instructions pour créer et activer des comptes de service pour les instances afin d'héberger NGINX.

    1. Attribuez les rôles suivants :
      • roles/cloudkms.signerVerifier
      • roles/cloudkms.viewer
  2. Configurez les règles d'administration comme suit pour limiter les adresses IP externes et la création de clés de compte de service.

    • constraints/compute.vmExternalIpAccess
    • constraints/iam.disableServiceAccountKeyCreation
  3. Créez un sous-réseau personnalisé qui active l'accès privé à Google.

  4. Configurer les règles de pare-feu

  5. Créez une VM Linux et configurez-la comme suit:

    • Sélectionnez le compte de service approprié que vous avez créé précédemment.
    • Sélectionnez le réseau que vous avez créé précédemment.
      • Ajoutez des libellés appropriés pour toutes les règles de pare-feu.
      • Assurez-vous que le champ "adresse IP externe" du sous-réseau est défini sur none.
  6. Attribuez à votre identité le rôle Utilisateur de tunnels sécurisés par IAP (roles/iap.tunnelResourceAccessor) sur l'instance.

Créer et configurer une clé de signature hébergée par Cloud KMS

Les sections suivantes décrivent les étapes à suivre pour créer et configurer une clé de signature hébergée par Cloud KMS.

Créer une clé de signature hébergée par Cloud KMS

Créez une clé de signature EC-P256-SHA256 Cloud KMS dans votre projetGoogle Cloud , dans le trousseau de clés que vous avez précédemment configuré pour 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"

Se connecter en SSH à votre VM à l'aide d'IAP

Connectez-vous en SSH à votre VM à l'aide d'IAP avec la commande suivante:

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

Si vous rencontrez un problème, vérifiez que vous avez utilisé l'option --tunnel-through-iap. Vérifiez également que vous disposez du rôle "Utilisateur de tunnels sécurisés par IAP" (roles/iap.tunnelResourceAccessor) sur l'instance pour l'identité authentifiée avec la gcloud CLI.

Créer un certificat avec OpenSSL

Pour un environnement de production, créez une requête de signature de certificat (CSR). Pour en savoir plus, consultez l'exemple de génération d'une requête de signature de certificat. Fournissez la CSR à votre autorité de certification (CA) afin qu'elle puisse créer un certificat pour vous. Utilisez le certificat fourni par votre autorité de certification dans les sections suivantes.

À titre d'exemple, vous pouvez générer un certificat autosigné avec la clé de signature hébergée par Cloud KMS. Pour ce faire, OpenSSL vous permet d'utiliser les URI PKCS #11 au lieu d'un chemin d'accès standard, en identifiant la clé par son libellé (pour les clés Cloud KMS, le libellé est le nom de la 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

Remplacez les éléments suivants :

  • CERTIFICATE_NAME : nom du certificat.
  • DIGEST_FLAG: algorithme de condensé utilisé par la clé de signature asymétrique. Utilisez -sha256, -sha384 ou -sha512 en fonction de la clé.
  • PKCS_KEY_TYPE: type d'identifiant utilisé pour identifier la clé. Pour utiliser la dernière version de la clé, utilisez pkcs11:object avec le nom de la clé. Pour utiliser une version de clé spécifique, utilisez pkcs11:id avec l'ID de ressource complet de la version de clé.
  • KEY_IDENTIFIER: identifiant de la clé. Si vous utilisez pkcs11:object, utilisez le nom de la clé (par exemple, NGINX_KEY). Si vous utilisez pkcs11:id, utilisez l'ID de ressource complet de la clé ou de la version de la clé (projects/PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/NGINX_KEY/cryptoKeyVersions/KEY_VERSION, par exemple).
  • CA_CERT: chemin d'accès au fichier de certificat.

Si la commande échoue, il est possible que PKCS11_MODULE_PATH ait été défini de manière incorrecte ou que vous ne disposiez pas des autorisations nécessaires pour utiliser la clé de signature Cloud KMS.

Vous devriez maintenant avoir un certificat semblable à celui-ci :

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

Installer votre certificat pour NGINX

Exécutez les commandes suivantes pour créer un emplacement où placer votre certificat public:

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

Configurer votre environnement pour utiliser la bibliothèque PKCS #11

Les sections suivantes décrivent les étapes nécessaires pour préparer et tester votre environnement.

Préparer des configurations de bibliothèque pour NGINX

Autorisez NGINX à consigner ses opérations de moteur PKCS #11 avec la bibliothèque avec les éléments suivants:

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

Créez un fichier de configuration de bibliothèque vide avec les autorisations appropriées pour NGINX.

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

Modifiez le fichier de configuration vide et ajoutez la configuration nécessaire, comme indiqué dans l'extrait de code suivant:

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

Tester votre configuration OpenSSL

Exécutez la commande suivante :

openssl engine -tt -c -v pkcs11

La sortie obtenue doit ressembler à ceci :

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

Configurer NGINX pour utiliser Cloud HSM

Autorisez le transfert TLS en modifiant quelques fichiers NGINX. Commencez par modifier le fichier /etc/nginx/nginx.conf à deux endroits pour ajouter quelques directives afin de configurer NGINX pour qu'il utilise PKCS #11.

Après le bloc event et avant le bloc http, ajoutez les directives suivantes:

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

Dans le même fichier /etc/nginx/nginx.conf, configurez les directives SSL pour utiliser votre certificat et sa clé privée dans Cloud HSM. Dans le bloc http, ajoutez les attributs suivants:

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.

Votre fichier /etc/nginx/nginx.conf doit se présenter comme suit:

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;

        #...
        #...

}

Configurer NGINX pour écouter le trafic TLS

Modifiez le fichier /etc/nginx/sites-enabled/default pour écouter le trafic TLS. Annulez la mise en commentaire de la configuration SSL dans le bloc server. La modification qui en résulte doit se présenter comme suit:


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

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

        # ...
        # ...
}

Fournir des variables d'environnement au service NGINX

Exécutez la commande suivante :

sudo systemctl edit nginx.service

Dans l'éditeur qui s'affiche, ajoutez les lignes suivantes et remplacez LIBPATH par la valeur de l'emplacement où vous avez installé 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"

Une fois que vous avez configuré ces valeurs, vous devez exécuter la commande suivante pour les rendre disponibles:

sudo systemctl daemon-reload

Redémarrer NGINX avec le transfert TLS

Exécutez la commande suivante pour que NGINX redémarre et utilise la configuration mise à jour:

sudo systemctl start nginx

Tester si NGINX utilise le transfert TLS vers votre Cloud HSM

Utilisez openssl s_client pour tester la connexion à votre serveur NGINX en exécutant la commande suivante:

openssl s_client -connect localhost:443

Le client doit terminer le handshake SSL et suspendre l'opération. Le client attend votre réponse, comme indiqué ci-dessous:

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

Vos journaux d'audit devraient maintenant afficher les opérations effectuées sur votre clé NGINX_KEY. Pour afficher les journaux, accédez à Cloud Logging dans votre console Cloud. Dans le projet que vous utilisez, ajoutez le filtre suivant:

resource.type="cloudkms_cryptokeyversion"

Une fois la requête exécutée, vous devriez voir des opérations de clé asymétrique sur votre clé NGINX_KEY.

Configurations facultatives

Vous devrez peut-être créer un équilibreur de charge réseau passthrough externe pour exposer votre serveur NGINX avec une adresse IP externe.

Si vous devez utiliser NGINX en tant que proxy inverse avec équilibrage de charge, envisagez de mettre à jour le fichier de configuration NGINX. Pour en savoir plus sur la configuration de NGINX en tant que proxy inverse, consultez HA tout actif pour NGINX Plus sur la plate-forme Google Cloud.

Étapes suivantes

Vous avez maintenant configuré votre serveur NGINX pour qu'il utilise le transfert TLS vers Cloud HSM.