S'authentifier à l'aide d'une identité d'agent dans GKE

Les agents Google Kubernetes Engine (GKE) disposant d'une identité d'agent peuvent l'utiliser pour s'authentifier auprès des API Google Cloud , ainsi que des outils et services externes. Les agents peuvent utiliser leur propre identité ou agir au nom des utilisateurs finaux. Ce document explique aux développeurs d'applications d'agent comment configurer leurs applications pour s'authentifier auprès de diverses ressources. Vous devez déjà savoir comment demander une identité d'agent pour un agent GKE.

Selon la ressource à laquelle l'agent doit accéder, il est possible que l'administrateur de votre plate-forme doive configurer le coffre d'identifiants du gestionnaire d'authentification pour exécuter des workflows supplémentaires. Par exemple, pour qu'un agent s'authentifie auprès de GitHub au nom d'un utilisateur final, un fournisseur d'authentification OAuth à trois étapes dans le gestionnaire d'authentification doit gérer la connexion, l'autorisation et la redirection de l'utilisateur. En tant que développeur, vous modifiez votre agent pour qu'il appelle le bon fournisseur d'authentification et gère la reprise de la conversation pour l'utilisateur final.

Limites

  • Consultez les limites concernant l'identité de l'agent.
  • Vous pouvez utiliser la bibliothèque d'authentification Google pour obtenir des jetons d'accès et d'identité liés uniquement pour Python. Il est possible que la bibliothèque d'authentification n'obtienne pas de jetons liés pour d'autres langues. Si vous utilisez une autre langue, passez aux jetons non liés en définissant la variable d'environnement GOOGLE_API_ENABLE_RUNTIME_BOUND_TOKEN sur false.

Avant de commencer

Avant de commencer, effectuez les tâches suivantes :

  • Activez l'API Google Kubernetes Engine.
  • Activer l'API Google Kubernetes Engine
  • Pour utiliser Google Cloud CLI pour cette tâche, installez puis initialisez la gcloud CLI. Si vous avez déjà installé la gcloud CLI, obtenez la dernière version en exécutant la commande gcloud components update. Il est possible que les versions antérieures de la gcloud CLI ne permettent pas d'exécuter les commandes de ce document.

Rôles requis

Pour obtenir les autorisations nécessaires pour configurer les agents déployés dans les clusters GKE, demandez à votre administrateur de vous accorder le rôle IAM Développeur Kubernetes Engine (roles/container.developer) sur votre projet. Pour en savoir plus sur l'attribution de rôles, consultez Gérer l'accès aux projets, aux dossiers et aux organisations.

Vous pouvez également obtenir les autorisations requises avec des rôles personnalisés ou d'autres rôles prédéfinis.

S'authentifier auprès des API Google Cloud

Pour s'authentifier auprès des API Google Cloud en tant qu'identité propre de l'agent, l'agent peut utiliser un jeton d'accès à l'identité de l'agent à partir du serveur de métadonnées sur vos nœuds. Les modifications que vous devrez peut-être apporter à votre code dépendent de la façon dont vous appelez les API Google Cloud , comme suit.

Utiliser les bibliothèques clientes Cloud

Si vous utilisez une version des bibliothèques clientes Cloud qui inclut la version 2.61.0 ou ultérieure de la bibliothèque google-auth, les Identifiants par défaut de l'application (ADC) obtiennent automatiquement un jeton d'accès à l'identité de l'agent. Vous n'avez pas besoin d'apporter d'autres modifications à votre code. Si vous activez l'injection de certificat pour les pods en définissant l'annotation iam.gke.io/inject-podcertificates: "true", le jeton d'accès est lié au certificat X.509 par défaut, sauf si vous désactivez les jetons liés.

Pour obtenir des jetons d'accès non liés lorsque vous utilisez les bibliothèques clientes Cloud, effectuez l'une des opérations suivantes :

  • Activez l'injection de certificat dans votre pod et définissez la variable d'environnement GOOGLE_API_ENABLE_RUNTIME_BOUND_TOKEN sur false.
  • N'activez pas l'injection de certificat dans votre pod.

Utiliser des appels directs aux points de terminaison de l'API Google Cloud

Si vous n'utilisez pas les bibliothèques clientes Cloud pour interagir avec un service, vous pouvez utiliser l'identité de l'agent pour vous authentifier auprès d'une API Google Cloud en procédant comme suit :

  1. Obtenez un jeton d'accès à partir du serveur de métadonnées sur le nœud. Vous pouvez obtenir un jeton à l'aide de l'une des méthodes suivantes :

    • Jetons d'accès liés : utilisez la bibliothèque Python google-auth, qui détecte le certificat X.509 du pod et obtient automatiquement des jetons d'accès liés par défaut. Pour les autres langages de programmation, utilisez des jetons non liés.

    • Jetons d'accès non liés : si le pod ne dispose pas du bundle d'informations d'identité de l'agent, utilisez la bibliothèque d'authentification Google pour votre langage de programmation. La bibliothèque d'authentification obtient automatiquement des jetons d'accès non liés et actualise les jetons expirés pour vous. Pour les applications Python dans les pods qui disposent du bundle d'identifiants, définissez la variable d'environnement GOOGLE_API_ENABLE_RUNTIME_BOUND_TOKEN dans la spécification de votre pod sur false, comme dans l'exemple suivant :

      # Multiple lines are omitted here.
      spec:
        containers:
        - name: example-agent
          image: example-image
          env:
          - name: GOOGLE_API_ENABLE_RUNTIME_BOUND_TOKEN
            value: "false"
      # Multiple lines are omitted here.
      

      Cette variable d'environnement empêche la bibliothèque d'obtenir des jetons d'accès et des jetons d'identité liés.

  2. Pour les jetons d'accès liés, envoyez la requête au point de terminaison mTLS de l'API et incluez la chaîne de certificats X.509 de l'identité de l'agent dans le transport HTTP. Si vous utilisez la bibliothèque d'authentification Google pour Python, elle gère la configuration du transport HTTP pour vous.

L'exemple suivant montre comment utiliser la bibliothèque d'authentification Google pour Python afin d'obtenir un jeton d'accès lié et d'envoyer une requête au point de terminaison mTLS Cloud Storage :

import google.auth
from google.auth.transport.requests import AuthorizedSession


def call_storage_api_mtls(bucket_name: str) -> None:
    # Discover the Pod's X.509 certificate chain by using the auth library
    credentials, project = google.auth.default(
        scopes=["https://www.googleapis.com/auth/cloud-platform"]
    )
    # Configure the mTLS session by using the Pod's certificate chain
    session = AuthorizedSession(credentials)
    session.configure_mtls_channel()
    # Call the Google Cloud mTLS endpoint
    mtls_url = f"https://storage.mtls.googleapis.com/storage/v1/b/{bucket_name}/o"
    response = session.get(mtls_url)
    response.raise_for_status()
    print(response.json())

S'authentifier auprès d'outils et de services externes

Pour vous authentifier auprès d'outils et de services externes, vous pouvez configurer votre agent afin qu'il obtienne les identifiants requis auprès du gestionnaire d'authentification des identités d'agent. Un administrateur de plate-forme configure différents fournisseurs d'authentification dans le gestionnaire d'authentification, chacun gérant des workflows et des identifiants d'authentification spécifiques. Vous modifiez le code d'application pour appeler un fournisseur d'authentification spécifique et, selon le workflow d'authentification, pour gérer le consentement utilisateur et la reprise de la conversation. Les modifications spécifiques que vous apportez à votre agent dépendent de ce à quoi vous devez accéder, comme suit :

Le gestionnaire d'authentification gère les workflows d'authentification correspondants et donne à l'agent l'accès aux identifiants chiffrés, qui peuvent ensuite être inclus dans les requêtes adressées au service externe. Pour savoir ce que l'administrateur de votre plate-forme doit faire pour configurer ces fournisseurs d'authentification et accorder l'accès à l'identité de votre agent, consultez Workflows d'authentification pour les agents.

S'authentifier auprès d'autres agents

Dans les architectures multi-agents, les agents collaborent fréquemment en appelant directement des agents pairs ou des services en aval. Vous pouvez établir une communication directe entre les charges de travail de l'agent à l'aide de jetons d'identité. Vous pouvez obtenir un jeton d'identité lié ou non lié à partir du serveur de métadonnées GKE et l'utiliser pour vous authentifier directement auprès d'autres agents.

Pour obtenir un jeton d'identité et l'utiliser dans une requête HTTP, utilisez la bibliothèque d'authentification Google pour Python. La bibliothèque gère automatiquement la découverte des certificats et l'acquisition des jetons d'identité. Si vous utilisez un autre langage de programmation, il est possible que la bibliothèque d'authentification Google n'obtienne pas de jetons d'ID liés. Passez plutôt aux jetons d'identité non liés.

Obtenir un jeton d'ID

Pour demander un jeton d'identité dans le code de votre agent, utilisez la bibliothèque d'authentification Google pour votre langage de programmation. Vous pouvez utiliser la bibliothèque pour demander des jetons d'identité liés ou non liés, comme suit :

  • Jetons d'identité liés : utilisez l'annotation iam.gke.io/inject-podcertificates: "true" pour activer l'injection de certificat pour votre pod. La bibliothèque d'authentification pour Python demande automatiquement un jeton d'identité lié à un certificat au serveur de métadonnées GKE. Utilisez des jetons d'identité liés lorsque vous vous authentifiez entre des agents qui s'exécutent sur Google Cloud à l'aide de mTLS.
  • Jetons d'ID non liés :

    • Activez l'injection de certificat pour votre pod, puis effectuez l'une des opérations suivantes :
      • Dans le code de votre application, dans la fonction id_token.fetch_id_token, définissez l'argument bind_id_token sur la valeur False. Cet argument permet à la bibliothèque d'authentification de demander des jetons d'identité non liés. Les demandes de jetons d'accès ne sont pas concernées.
      • Dans la spécification de votre pod, définissez la variable d'environnement GOOGLE_API_ENABLE_RUNTIME_BOUND_TOKEN sur la valeur false. Cette variable d'environnement empêche la bibliothèque de demander des jetons d'accès et des jetons d'identité liés.
    • N'activez pas l'injection de certificat pour votre pod. La bibliothèque d'authentification obtient un jeton d'identité non lié, car il n'y a pas de bundle d'identifiants dans le pod.

    Utilisez des jetons d'identité non liés lorsque vous vous authentifiez auprès des API Google Cloud , des services externes ou d'autres agents à l'aide d'une connexion non-mTLS.

Les exemples suivants vous montrent comment demander un jeton d'identité lié ou non lié pour un agent dont l'injection d'identifiants est activée :

  • Demandez un jeton d'identité lié :

    import google.auth.transport.requests
    from google.oauth2 import id_token
    
    # Application Default Credentials automatically requests a certificate-bound
    # ID token.
    def get_bound_id_token(target_audience: str) -> str:
        auth_req = google.auth.transport.requests.Request()
        return id_token.fetch_id_token(auth_req, audience=target_audience)
    

    Le jeton d'identité lié inclut l'empreinte du certificat SHA-256 de la chaîne de certificats X.509 du pod dans le paramètre cnf.x5t#S256.

  • Demandez un jeton d'identité non lié :

    import google.auth.transport.requests
    from google.oauth2 import id_token
    
    def get_unbound_id_token(target_audience: str) -> str:
        auth_req = google.auth.transport.requests.Request()
        return id_token.fetch_id_token(
            auth_req,
            audience=target_audience,
            # Always get an unbound ID token, even if the Pod has a credential
            # bundle.
            bind_id_token=False,
        )
    

Utiliser le jeton d'identité dans une requête adressée à un autre agent

Une fois que vous avez obtenu un jeton d'identité pour votre agent, vous pouvez l'utiliser pour vous authentifier directement auprès d'un autre agent. La façon dont vous authentifiez la connexion dépend de l'utilisation ou non d'un jeton d'identité lié :

  • Pour les jetons d'identité liés, établissez une connexion mTLS avec l'agent de réception et authentifiez la connexion à l'aide des deux identifiants suivants du répertoire /var/run/secrets/workload-spiffe-credentials/ dans le pod :
    • Ensemble d'identifiants de l'agent dans le fichier x509.credential-bundle.private-key.pem, qui contient la chaîne de certificats feuille pour le pod.
    • Bundle de confiance du cluster dans le fichier TRUST_DOMAIN.spiffe-trust-bundle.pem. Ce fichier contient le certificat CA racine pour l'agent de réception. Il est utilisé pour valider la chaîne de certificats de l'agent de réception lors de l'établissement du handshake mTLS. Les agents appelant et recevant doivent se trouver dans le même pool d'identités d'agent.
  • Pour les jetons d'identité non liés, établissez une connexion non mTLS avec l'agent de réception.

Les exemples suivants montrent comment envoyer une requête à un autre agent à l'aide d'un jeton d'identité lié ou non lié :

  • Jeton d'identité lié : incluez le jeton d'identité lié dans l'en-tête Authorization: Bearer de la requête que vous envoyez au point de terminaison mTLS de l'agent destinataire. Authentifiez la connexion TLS à l'aide du certificat X.509 et de la clé privée du pod :

    import ssl
    import urllib3
    
    BUNDLE_PATH = "/var/run/secrets/workload-spiffe-credentials/x509.credential-bundle.private-key.pem"
    TRUST_BUNDLE_PATH = "/var/run/secrets/workload-spiffe-credentials/TRUST_DOMAIN.spiffe-trust-bundle.pem"
    
    def call_peer_agent_bound_mtls(target_mtls_url: str, target_audience: str) -> None:
    
        # Configure the mTLS context by using the certificate chain and trust
        # bundle from the Pod.
        ctx = ssl.create_default_context(cafile=TRUST_BUNDLE_PATH)
        ctx.load_cert_chain(BUNDLE_PATH)
        http = urllib3.PoolManager(ssl_context=ctx, assert_hostname=False)
    
        # Call a peer agent's mTLS endpoint by using the bound ID token and Pod
        # certificate chain.
        bound_id_token = get_bound_id_token(target_audience)
        response = http.request(
            "POST",
            target_mtls_url,
            headers={"Authorization": f"Bearer {bound_id_token}"},
            json={"task": "analyze_data"},
            timeout=10,
        )
        print(response.json())
    

    Remplacez TRUST_DOMAIN par le domaine de confiance pour votre pool d'identités d'agent.

  • Jeton d'identité non lié : incluez le jeton dans l'en-tête Authorization: Bearer de votre requête à l'agent pair :

    import requests
    
    def call_peer_agent_unbound(target_url: str, target_audience: str) -> None:
        unbound_id_token = get_unbound_id_token(target_audience)
        # Send the request by using a standard TLS connection or plain HTTP.
        response = requests.post(
            target_url,
            headers={"Authorization": f"Bearer {unbound_id_token}"},
            json={"task": "analyze_data"},
            timeout=10,
        )
        response.raise_for_status()
        print(response.json())
    

Valider la demande dans l'agent de réception

Dans l'agent de réception, validez le jeton d'identité qui se trouve dans la requête entrante en procédant comme suit. Vous pouvez utiliser des bibliothèques de chiffrement telles que Tink pour effectuer ces étapes de validation au lieu d'écrire du code personnalisé.

  1. Extrayez le jeton d'identité de l'en-tête Authorization: Bearer de la requête.
  2. Vérifiez que la revendication iss (émetteur) du jeton d'ID correspond au pool d'identités de l'agent appelant. L'émetteur est l'un des suivants, selon que l'agent appelant se trouve dans un projet appartenant à une organisation :
    • Projet appartenant à une organisation : https://sts.googleapis.com/v1/organizations/ORGANIZATION_ID/locations/global/workloadIdentityPools/agents.global.org-ORGANIZATION_ID.system.id.goog, où ORGANIZATION_ID correspond à l'ID de l'organisation contenant le projet de l'agent appelant.
    • Projet qui n'appartient pas à une organisation : https://sts.googleapis.com/v1/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/agents.global.proj-PROJECT_NUMBER.system.id.goog, où PROJECT_NUMBER correspond au numéro de projet du cluster GKE de l'agent appelant.
  3. Découvrez l'URI du jeu de clés Web JSON (JWKS) pour l'émetteur et mettez en cache les clés Web JSON (JWKS) publiques. Le point de terminaison du JWKS est au format ISSUER_URL/openid/jwks, où ISSUER_URL correspond à l'URL de l'émetteur.
  4. Validez la signature du jeton à l'aide des informations suivantes provenant de l'en-tête JOSE du jeton d'identité :
    • Le JWK public qui correspond au paramètre d'en-tête kid.
    • Algorithme cryptographique correspondant au paramètre d'en-tête alg, tel que RS256.
  5. Vérifiez que l'empreinte du certificat SHA-256 figurant dans le paramètre cnf.x5t#S256 correspond à l'empreinte du certificat X.509 utilisé par l'agent appelant pour authentifier la connexion mTLS.
  6. Vérifiez les revendications suivantes dans le jeton d'identité :
    • L'heure d'expiration de la revendication exp est dans le futur.
    • L'audience dans la revendication aud est l'agent destinataire.
  7. Autorisez la requête en fonction de l'ID SPIFFE qui se trouve dans la revendication sub (sujet) du jeton.

Étapes suivantes