Utiliser l'authentification TLS mutuelle pour obtenir des identifiants éphémères

Ce tutoriel destiné aux développeurs explique comment mettre en œuvre sur Google Cloud un agent de service de jetons permettant aux applications clientes externes à Google Cloud d'obtenir des jetons d'accès OAuth à l'aide de l'authentification mutuelle mTLS (Transport Layer Security). L'utilisation de mTLS au lieu de clés de compte de service pour l'authentification vous permet d'intégrer votre infrastructure à clé publique (PKI) existante pour contrôler les applications autorisées à accéder aux API Google Cloud.

Les API fournies par Google Cloud reposent sur OAuth pour l'authentification. Avant de pouvoir interagir avec un service tel que Cloud Storage, vous devez obtenir un jeton d'accès OAuth. Les jetons d'accès sont de courte durée et valides uniquement pour un ensemble spécifique d'API.

Une application déployée sur Google Cloud utilise généralement le compte de service de l'instance de machine virtuelle sur laquelle elle est déployée ou la fonction Cloud sur laquelle elle s'exécute pour obtenir des jetons d'accès aux API et s'authentifier.

Dans un environnement hybride, vous pouvez déployer des applications externes à Google Cloud devant interagir avec les API Google Cloud. Par exemple, une application exécutée sur site peut avoir besoin d'interagir avec les API Cloud Storage pour importer ou récupérer des données.

Dans ce tutoriel, nous partons du principe que vous possédez des connaissances de base sur TLS, les certificats X.509 et les comptes de service Google Cloud.

Objectifs

  • Créer une autorité de certification personnalisée pour émettre des certificats de serveur et de client X.509.
  • Déployer sur Compute Engine un agent de service de jetons qui authentifie les appelants à l'aide de certificats clients X.509 et émet des jetons d'accès OAuth de courte durée aux appelants authentifiés.
  • Tester la configuration en exécutant un client qui s'authentifie à l'aide de mTLS.

Coûts

Ce tutoriel utilise les composants facturables suivants de Google Cloud :

Obtenez une estimation des coûts en fonction de votre utilisation prévue à l'aide du simulateur de coût. Les nouveaux utilisateurs de Google Cloud peuvent bénéficier d'un essai gratuit.

Avant de commencer

  1. Connectez-vous à votre compte Google Cloud. Si vous débutez sur Google Cloud, créez un compte pour évaluer les performances de nos produits en conditions réelles. Les nouveaux clients bénéficient également de 300 $ de crédits gratuits pour exécuter, tester et déployer des charges de travail.
  2. Dans Google Cloud Console, sur la page de sélection du projet, sélectionnez ou créez un projet Google Cloud.

    Accéder au sélecteur de projet

  3. Assurez-vous que la facturation est activée pour votre projet Cloud. Découvrez comment vérifier si la facturation est activée sur un projet.

  4. Activer les API IAM and Compute Engine.

    Activer les API

  5. Dans Google Cloud Console, sur la page de sélection du projet, sélectionnez ou créez un projet Google Cloud.

    Accéder au sélecteur de projet

  6. Assurez-vous que la facturation est activée pour votre projet Cloud. Découvrez comment vérifier si la facturation est activée sur un projet.

  7. Activer les API IAM and Compute Engine.

    Activer les API

Architecture

Dans cet exemple de scénario, vous utilisez l'outil de ligne de commande curl pour vous authentifier auprès de l'agent de service de jetons à l'aide de mTLS et obtenir un jeton d'accès. Vous utilisez ensuite ce jeton d'accès pour appeler une API Cloud Storage.

Le schéma suivant illustre la configuration que vous créez dans ce tutoriel :

Architecture pour l'authentification auprès de l'agent de service de jetons à l'aide du protocole mTLS

  • L'agent de service de jetons est mis en œuvre sous la forme d'une application Python et déployé sur une instance de VM dédiée dans Compute Engine. L'instance de VM est associée à un compte de service nommé token-broker.
  • Un deuxième compte de service nommé storage-reader fournit une identité spécifique à un rôle. L'agent de service de jetons utilise ce compte de service pour émettre des jetons d'accès, qui reflètent les autorisations IAM du compte de service storage-reader. Dans un scénario de production, vous pouvez avoir plusieurs comptes de service, chacun reflétant un rôle différent.
  • Le compte de service token-broker est autorisé à générer des jetons d'accès pour le compte de service storage-reader.

Pour activer l'authentification mTLS entre le client et l'agent de service de jetons, créez une infrastructure à clé publique simple :

  • Le client et le serveur reçoivent tous deux un certificat de machine.
  • Les certificats sont émis et signés par une autorité de certification racine commune.
  • L'autorité de certification racine est approuvée par le client et l'agent de service de jetons.

L'infrastructure à clé publique est illustrée dans le schéma suivant :

Infrastructure à clé publique

Déployer l'agent de service de jetons

Pour déployer l'agent de service de jetons, créez les comptes de service et une instance de VM, puis déployez le code Python sur l'instance de VM.

Créer un compte de service spécifique à un rôle

La procédure décrite ci-dessous permet de créer un compte de service spécifique au rôle afin de générer des jetons d'accès. Comme ce compte de service n'autorise qu'un accès en lecture à Cloud Storage, nommez-le storage-reader.

  1. Dans Google Cloud Console, ouvrez Cloud Shell.

    Ouvrir Cloud Shell

  2. Définissez votre ID de projet par défaut :

    gcloud config set project PROJECT_ID
    
  3. Définissez une région et une zone par défaut :

    gcloud config set compute/region us-central1
    gcloud config set compute/zone us-central1-a
    
  4. Créez un bucket Cloud Storage :

    gsutil mb gs://$GOOGLE_CLOUD_PROJECT-storage
    
  5. Créez le compte de service storage-reader :

    gcloud iam service-accounts create storage-reader --display-name="Storage Reader"
    
  6. Capturez le nom du nouveau compte de service dans une variable d'environnement :

    STORAGE_READER_SA=storage-reader@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com
    
  7. Accordez au compte de service storage-reader l'accès à Cloud Storage en lui attribuant le rôle roles/storage.objectViewer :

    gcloud projects add-iam-policy-binding $GOOGLE_CLOUD_PROJECT \
        --member serviceAccount:$STORAGE_READER_SA \
        --role roles/storage.objectViewer
    

Créer un compte de service pour l'agent de service de jetons

Créez le compte de service faisant office d'identité pour l'agent de service de jetons en procédant comme suit :

  1. Dans Cloud Shell, créez un compte de service pour l'agent de service de jetons :

    gcloud iam service-accounts create token-broker --display-name="Token Broker"
    
  2. Capturez le nom du nouveau compte de service dans une variable d'environnement :

    TOKEN_BROKER_SA=token-broker@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com
    
  3. Accordez au compte de service token-broker l'autorisation d'émettre des identifiants pour le compte de service storage-reader :

    gcloud iam service-accounts add-iam-policy-binding ${STORAGE_READER_SA} \
        --member=serviceAccount:${TOKEN_BROKER_SA} \
        --role=roles/iam.serviceAccountTokenCreator
    

Déployer l'agent de service de jetons

Créez une instance Compute Engine et déployez l'agent de service de jetons sur cette instance en procédant comme suit :

  1. Créez une règle de pare-feu qui autorise le trafic entrant HTTPS :

    gcloud compute firewall-rules create https-ingress --allow=tcp:443 --target-tags=https
    
  2. Créez une instance Compute Engine et attribuez-lui le compte de service token-broker que vous venez de créer comme identité. Fournissez un script de démarrage qui installe le code d'agent de service de jetons :

    gcloud compute instances create token-broker \
        --service-account=$TOKEN_BROKER_SA \
        --tags=https \
        --scopes https://www.googleapis.com/auth/cloud-platform \
        --metadata startup-script='#! /bin/bash
            sudo apt update -y
            sudo apt install git python3-pip -y
    
            git clone https://github.com/GoogleCloudPlatform/python-token-broker.git /opt/python-token-broker
            sudo pip3 install -r /opt/python-token-broker/requirements.txt'
    

La création de l'instance de VM peut prendre jusqu'à une minute.

Vous avez maintenant déployé une instance de VM et installé l'agent de service de jetons. Avant de pouvoir démarrer l'agent de service de jetons, vous devez créer un certificat de serveur et un certificat client.

Créer des certificats

Vous devez créer une autorité de certification, puis utiliser le certificat de l'autorité de certification pour signer les certificats de l'agent de service de jetons et du client. Dans un scénario de production, vous pouvez utiliser une autorité de certification existante pour émettre des certificats.

Créer une autorité de certification

Créez une autorité de certification que vous pouvez utiliser pour émettre des certificats en procédant comme suit :

  1. Dans Cloud Shell, créez un répertoire :

    mkdir token-broker-tutorial && cd token-broker-tutorial
    
  2. Définissez un mot de passe pour la clé privée de l'autorité de certification :

    CA_PASSWORD=SecretPassword
    
  3. Générez une clé privée pour l'autorité de certification et protégez-la à l'aide du mot de passe que vous venez de définir :

    openssl genrsa \
        -aes256 \
        -passout pass:$CA_PASSWORD \
        -out ca.key 4096
    
  4. Générez le certificat pour l'autorité de certification racine. Étant donné que le certificat est uniquement destiné au tutoriel, spécifiez une validité de 30 jours seulement :

    openssl req -new \
        -x509 \
        -passin pass:$CA_PASSWORD \
        -sha256 \
        -days 30 \
        -key ca.key -out ca.crt \
        -subj '/O=Test CA/C=US'
    

Votre dossier contient désormais deux fichiers :

  • Le fichier ca.key contient la clé privée de l'autorité de certification. Ce fichier est protégé par un mot de passe et ne sera pas partagé avec le client ou l'agent de service de jetons.
  • Le fichier ca.crt contient le certificat CA racine. Vous partagez ce certificat avec le client et le serveur.

Émettre un certificat de serveur

Vous pouvez utiliser votre autorité de certification pour émettre un certificat pour l'instance de VM de l'agent de service de jetons en procédant comme suit :

  1. Générez une clé privée pour l'agent de service de jetons :

    openssl genrsa -out token-broker.key 2048
    
  2. Recherchez l'adresse IP externe attribuée à l'instance de VM de l'agent de service de jetons. Comme la VM n'a pas de nom DNS public, vous utilisez l'adresse IP comme nom commun dans le certificat TLS.

    TOKEN_BROKER_FQDN=$(gcloud compute instances describe token-broker --format='get(networkInterfaces[0].accessConfigs[0].natIP)')
    
  3. Créez une demande de signature de certificat (CSR) :

    openssl req -new -key token-broker.key -sha256 \
        -out token-broker.csr \
        -nodes -subj "/CN=$TOKEN_BROKER_FQDN"
    
  4. Générez un certificat valide pendant 30 jours et faites-le signer par l'autorité de certification :

    openssl x509 -req \
        -days 30 \
        -sha256 \
        -in token-broker.csr \
        -out token-broker.crt \
        -CA ca.crt -CAkey ca.key \
        -passin pass:$CA_PASSWORD \
        -set_serial 1
    
  5. Combinez le certificat et la clé privée dans un seul fichier :

    cat token-broker.key >> token-broker.crt
    
  6. Copiez le certificat du serveur et le certificat CA sur l'instance de VM :

    gcloud compute scp ca.crt token-broker.crt token-broker:~
    
  7. Déplacez le certificat de serveur et le certificat CA vers le répertoire de l'agent de service de jetons :

    gcloud compute ssh token-broker --command 'sudo mv ca.crt token-broker.crt /opt/python-token-broker'
    
  8. Supprimez les fichiers temporaires :

    rm -f token-broker.csr token-broker.key
    

Le fichier token-broker.crt contient le certificat du serveur, y compris la clé privée correspondante. Le certificat a été signé par votre autorité de certification de test, ce qui permet au serveur de s'authentifier auprès du client.

Émettre un certificat client

Ensuite, vous devez émettre un certificat pour le client en effectuant la procédure décrite ci-dessous. Pour simuler un environnement sur site, vous exécutez le client sur Cloud Shell. Le certificat client doit donc être émis pour l'hôte Cloud Shell :

  1. Déterminez le nom de domaine complet de l'hôte Cloud Shell :

    CLIENT_IP=$(curl http://metadata.google.internal/computeMetadata/v1/instance/network-interfaces/0/access-configs/0/external-ip -H  "Metadata-Flavor: Google")
    CLIENT_FQDN=$(dig +short -x $CLIENT_IP)
    
  2. Générez une clé privée pour le client :

    openssl genrsa -out client.key 2048
    
  3. Créez une demande de signature de certificat (CSR) qui utilise le nom de domaine complet de l'hôte Cloud Shell (sans le point final) comme nom commun.

    openssl req -new -key client.key \
        -out client.csr -days 14 \
        -nodes -subj "/CN=${CLIENT_FQDN:0:-1}"
    
  4. Générez le certificat et faites-le signer par l'autorité de certification :

    openssl x509 -req -days 14 -sha256 \
        -in client.csr -out client.crt \
        -CA ca.crt -CAkey ca.key \
        -passin pass:$CA_PASSWORD \
        -set_serial 1
    
  5. Supprimez le fichier temporaire client.csr :

    rm -f client.csr
    

Le dossier contient désormais deux fichiers supplémentaires :

  • Le fichier client.crt contient le certificat du client. Il est signé par l'autorité de certification racine et permet au client de s'authentifier auprès du serveur.
  • Le fichier client.key contient la clé privée qui correspond au certificat client.

Vous disposez désormais de tous les certificats dont vous avez besoin.

Tester l'agent de service de jetons

Pour tester votre déploiement, démarrez l'agent de service de jetons et utilisez-le pour obtenir un jeton d'accès pour Cloud Storage.

Démarrer l'agent de service de jetons

Démarrez le script de l'agent de service de jetons sur l'instance de VM en procédant comme décrit ci-dessous. Comme vous souhaitez que l'agent de service de jetons continue de s'exécuter en arrière-plan, utilisez un deuxième onglet Cloud Shell :

  1. Ouvrez un deuxième onglet Cloud Shell.
  2. Définissez votre ID de projet par défaut :

    gcloud config set project PROJECT_ID
    
  3. Définissez une région et un rôle par défaut :

    gcloud config set compute/region us-central1
    gcloud config set compute/zone us-central1-a
    
  4. Démarrez le serveur via SSH :

    STORAGE_READER_SA=storage-reader@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com
    
    gcloud compute ssh token-broker --command "(cd /opt/python-token-broker && sudo python3 token_broker.py 0.0.0.0 443 token-broker.crt  $STORAGE_READER_SA ca.crt)"
    

    Le résultat suivant s'affiche, ce qui indique que l'agent de service de jetons attend des connexions :

    INFO:root:Starting server on 0.0.0.0:443
    

Laissez la fenêtre Cloud Shell ouverte afin de pouvoir observer les messages de journal affichés à l'écran.

Obtenir un jeton d'accès pour accéder à Cloud Storage

Maintenant que l'agent de service de jetons est en cours d'exécution, vous pouvez l'utiliser pour obtenir des jetons d'accès en procédant comme suit :

  1. Revenez au premier onglet Cloud Shell.
  2. Exécutez la commande curl suivante pour obtenir un jeton d'accès.

    1. Transmettez le certificat client (--cert) et la clé (--key) en tant que paramètres afin que l'outil de ligne de commande curl puisse s'authentifier auprès de l'agent de service de jetons.
    2. Transmettez le certificat CA racine (--cacert) pour permettre à l'outil de ligne de commande curl de vérifier l'authenticité de l'agent de service de jetons :
    curl \
        --cert client.crt --key client.key \
        --cacert ca.crt \
        https://$TOKEN_BROKER_FQDN/token
    

    La sortie affiche un jeton d'accès :

    ya29.c.KvoBsAeK5QV2Qg...
    

    Ce jeton est émis à l'aide de l'identité du compte de service storage-reader et vous donne accès au bucket Cloud Storage que vous avez créé précédemment.

  3. Vérifiez que le jeton vous donne accès à Cloud Storage. Commencez par capturer le jeton d'accès dans une variable :

    TOKEN=$(curl --cacert ./ca.crt --key client.key --cert ./client.crt https://$TOKEN_BROKER_FQDN/token)
    
  4. Appelez l'API Cloud Storage et transmettez le jeton d'accès comme en-tête :

    curl \
        -H 'Accept: application/json' \
        -H "Authorization: Bearer ${TOKEN}" \
        https://storage.googleapis.com/storage/v1/b/$GOOGLE_CLOUD_PROJECT-storage/o
    

    Le résultat suivant apparaît, indiquant que vous vous êtes authentifié et qu'il n'y a aucun objet dans le bucket Cloud Storage :

    {
      "kind": "storage#objects"
    }
    

Nettoyer

Pour éviter que les ressources utilisées lors de ce tutoriel soient facturées sur votre compte Google Cloud, supprimez le projet contenant les ressources, ou conservez le projet et supprimez les ressources individuelles.

  1. Dans Cloud Console, accédez à la page Gérer les ressources :

    Accéder à la page Gérer les ressources

  2. Dans la liste des projets, sélectionnez le projet que vous souhaitez supprimer, puis cliquez sur Supprimer.
  3. Dans la boîte de dialogue, saisissez l'ID du projet, puis cliquez sur Arrêter pour supprimer le projet.

Étape suivante