Autentícate con la identidad de un agente en GKE

Los agentes de Google Kubernetes Engine (GKE) que tienen una identidad de agente pueden usarla para autenticarse en las Google Cloud APIs y en las herramientas y los servicios externos. Los agentes pueden usar su propia identidad o actuar en nombre de los usuarios finales. En este documento, se muestra a los desarrolladores de aplicaciones de agentes cómo configurar sus aplicaciones para autenticarse en varios recursos. Ya deberías saber cómo solicitar una identidad de agente para un agente de GKE.

Según el recurso al que necesite acceder el agente, es posible que el administrador de la plataforma deba configurar el almacén de credenciales del administrador de autenticación para ejecutar flujos de trabajo adicionales. Por ejemplo, para que un agente se autentique en GitHub en nombre de un usuario final, un proveedor de autenticación de OAuth de 3 segmentos en el administrador de autenticación debe controlar el acceso, la autorización y el redireccionamiento del usuario. Como desarrollador, debes modificar tu agente para que llame al proveedor de autenticación correcto y maneje la reanudación de la conversación para el usuario final.

Limitaciones

  • Consulta las limitaciones de la identidad del agente.
  • Puedes usar la biblioteca de autenticación de Google para obtener tokens de acceso y de ID vinculados solo para Python. Es posible que la biblioteca de autenticación no obtenga tokens vinculados para otros idiomas. Si usas otro idioma, cambia a tokens no vinculados configurando la variable de entorno GOOGLE_API_ENABLE_RUNTIME_BOUND_TOKEN en false.

Antes de comenzar

Antes de comenzar, asegúrate de haber realizado las siguientes tareas:

  • Habilita la API de Google Kubernetes Engine.
  • Habilitar la API de Google Kubernetes Engine
  • Si deseas usar Google Cloud CLI para esta tarea, instala y, luego, inicializa gcloud CLI. Si ya instalaste la gcloud CLI, ejecuta el comando gcloud components update para obtener la versión más reciente. Es posible que las versiones anteriores de gcloud CLI no admitan la ejecución de los comandos que se indican en este documento.

Roles obligatorios

Para obtener los permisos que necesitas para configurar agentes implementados en clústeres de GKE, pídele a tu administrador que te otorgue el rol de IAM de Desarrollador de Kubernetes Engine (roles/container.developer) en tu proyecto. Para obtener más información sobre cómo otorgar roles, consulta Administra el acceso a proyectos, carpetas y organizaciones.

También puedes obtener los permisos necesarios a través de roles personalizados o cualquier otro rol predefinido.

Autenticación en las Google Cloud APIs

Para autenticarse en las APIs de Google Cloud con la identidad propia del agente, este puede usar un token de acceso de identidad del agente desde el servidor de metadatos en tus nodos. Los cambios que tal vez debas realizar en tu código dependen de cómo llamas Google Cloud a las APIs, como se indica a continuación.

Usa las bibliotecas cliente de Cloud

Si usas una versión de las bibliotecas cliente de Cloud que incluye la versión 2.61.0 o posterior de la biblioteca google-auth, las credenciales predeterminadas de la aplicación (ADC) obtienen automáticamente un token de acceso de identidad del agente. No es necesario que realices cambios adicionales en tu código. Si habilitas la inserción de certificados para los Pods configurando la anotación iam.gke.io/inject-podcertificates: "true", el token de acceso se vinculará al certificado X.509 de forma predeterminada, a menos que inhabilite los tokens vinculados.

Para obtener tokens de acceso no vinculados cuando usas las bibliotecas cliente de Cloud, haz una de las siguientes acciones:

  • Habilita la inserción de certificados en tu Pod y configura la variable de entorno GOOGLE_API_ENABLE_RUNTIME_BOUND_TOKEN como false.
  • No habilites la inserción de certificados en tu Pod.

Usar llamadas directas a los endpoints de API de Google Cloud

Si no usas las bibliotecas cliente de Cloud para interactuar con un servicio, puedes usar la identidad del agente para autenticarte en una API de Google Cloud de la siguiente manera:

  1. Obtén un token de acceso del servidor de metadatos en el nodo. Puedes obtener un token con uno de los siguientes métodos:

    • Tokens de acceso vinculados: Usa la biblioteca de Python google-auth, que descubre el certificado X.509 del Pod y obtiene automáticamente tokens de acceso vinculados de forma predeterminada. Para otros lenguajes de programación, usa tokens no vinculados.

    • Tokens de acceso no vinculados: Si el Pod no tiene el paquete de credenciales de identidad del agente, usa la biblioteca de autenticación de Google para tu lenguaje de programación. La biblioteca de autenticación obtiene automáticamente tokens de acceso no vinculados y actualiza los tokens que vencen por ti. Para las aplicaciones de Python en Pods que tienen el paquete de credenciales, configura la variable de entorno GOOGLE_API_ENABLE_RUNTIME_BOUND_TOKEN en la especificación del Pod como false, como en el siguiente ejemplo:

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

      Esta variable de entorno evita que la biblioteca obtenga tokens de acceso vinculados y tokens de ID.

  2. En el caso de los tokens de acceso vinculados, envía la solicitud al extremo de mTLS de la API y, luego, incluye la cadena de certificados X.509 de identidad del agente en el transporte HTTP. Si usas la biblioteca de autenticación de Google para Python, la biblioteca controla la configuración del transporte HTTP por ti.

En el siguiente ejemplo, se muestra cómo usar la biblioteca de autenticación de Google para Python para obtener un token de acceso vinculado y realizar una solicitud al extremo de mTLS de 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())

Autenticación en herramientas y servicios externos

Para autenticarte en herramientas y servicios externos, puedes configurar tu agente para que obtenga las credenciales requeridas del administrador de autenticación de identidad del agente. Un administrador de la plataforma configura varios proveedores de autenticación en el administrador de autenticación, cada uno de los cuales administra flujos de trabajo y credenciales de autenticación específicos. Modificas el código de la aplicación para llamar a un proveedor de autenticación específico y, según el flujo de trabajo de autenticación, para controlar el consentimiento del usuario y la reanudación de la conversación. Los cambios específicos que realices en tu agente dependerán de lo que necesites acceder, de la siguiente manera:

El administrador de autenticación controla los flujos de trabajo de autenticación correspondientes y le otorga al agente acceso a las credenciales encriptadas, que luego se pueden incluir en las solicitudes al servicio externo. Para obtener más información sobre lo que debe hacer el administrador de tu plataforma para configurar estos proveedores de autenticación y otorgar acceso a la identidad de tu agente, consulta Flujos de trabajo de autenticación para agentes.

Autenticarse en otros agentes

En las arquitecturas multiagente, los agentes suelen colaborar invocando directamente a agentes pares o servicios descendentes. Puedes establecer comunicación directa entre las cargas de trabajo de los agentes con tokens de identidad. Puedes obtener un token de ID vinculado o no vinculado del servidor de metadatos de GKE y usarlo para autenticarte directamente en otros agentes.

Para obtener un token de ID y usarlo en una solicitud HTTP, usa la biblioteca de autenticación de Google para Python. La biblioteca controla automáticamente la detección de certificados y la adquisición de tokens de ID. Si usas un lenguaje de programación diferente, es posible que la biblioteca de autenticación de Google no obtenga tokens de ID vinculados. En su lugar, cambia a tokens de ID no vinculados.

Obtén un token de ID

Para solicitar un token de ID en el código de tu agente, usa la biblioteca de autenticación de Google para tu lenguaje de programación. Puedes usar la biblioteca para solicitar tokens de ID vinculados o no vinculados, de la siguiente manera:

  • Tokens de ID vinculados: Usa la anotación iam.gke.io/inject-podcertificates: "true" para habilitar la inserción de certificados en tu Pod. La biblioteca de autenticación para Python solicita automáticamente un token de ID vinculado a un certificado del servidor de metadatos de GKE. Usa tokens de ID vinculados cuando te autentiques entre agentes que se ejecutan en Google Cloud con mTLS.
  • Tokens de ID no vinculados:

    • Habilita la inserción de certificados para tu Pod y realiza una de las siguientes acciones:
      • En el código de la aplicación, en la función id_token.fetch_id_token, establece el argumento bind_id_token en un valor de False. Este argumento hace que la biblioteca de autenticación solicite tokens de ID no vinculados. Las solicitudes de tokens de acceso no se ven afectadas.
      • En la especificación del Pod, establece la variable de entorno GOOGLE_API_ENABLE_RUNTIME_BOUND_TOKEN en un valor de false. Esta variable de entorno evita que la biblioteca solicite tokens de acceso vinculados y tokens de ID.
    • No habilites la inserción de certificados para tu Pod. La biblioteca de autenticación obtiene un token de ID no vinculado, ya que no hay un paquete de credenciales en el Pod.

    Usa tokens de ID no vinculados cuando te autentiques en Google Cloud APIs, servicios externos o cualquier otro agente a través de una conexión que no sea de mTLS.

En los siguientes ejemplos, se muestra cómo solicitar un token de ID vinculado o no vinculado para un agente que tiene habilitada la inserción de credenciales:

  • Solicita un token de ID vinculado:

    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)
    

    El token de identidad vinculado incluye la huella digital del certificado SHA-256 de la cadena de certificados X.509 del Pod en el parámetro cnf.x5t#S256.

  • Solicita un token de ID no vinculado:

    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,
        )
    

Usa el token de ID en una solicitud a otro agente

Después de obtener un token de ID para tu agente, puedes usarlo para autenticarte directamente en otro agente. La forma en que autenticas la conexión depende de si usas un token de ID vinculado, de la siguiente manera:

  • Para los tokens de ID vinculados, establece una conexión mTLS con el agente receptor y autentica la conexión con las siguientes credenciales del directorio /var/run/secrets/workload-spiffe-credentials/ en el Pod:
    • Es el paquete de credenciales de identidad del agente que se encuentra en el archivo x509.credential-bundle.private-key.pem, el cual contiene la cadena de certificados de hoja para el Pod.
    • Es el paquete de confianza del clúster que se encuentra en el archivo TRUST_DOMAIN.spiffe-trust-bundle.pem. Este archivo contiene el certificado de la AC raíz del agente receptor y se usa para validar la cadena de certificados del agente receptor durante el protocolo de enlace mTLS. Los agentes de llamada y recepción deben estar en el mismo grupo de identidades de agentes.
  • En el caso de los tokens de ID no vinculados, establece una conexión que no sea de mTLS con el agente receptor.

En los siguientes ejemplos, se muestra cómo enviar una solicitud a otro agente con un token de ID vinculado o no vinculado:

  • Token de ID vinculado: Incluye el token de identidad vinculado en el encabezado Authorization: Bearer de la solicitud que envías al extremo de mTLS del agente receptor. Autentica la conexión TLS con el certificado X.509 y la clave privada del 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())
    

    Reemplaza TRUST_DOMAIN por el dominio de confianza de tu grupo de identidades de agentes.

  • Token de ID no vinculado: Incluye el token en el encabezado Authorization: Bearer de tu solicitud al agente de pares:

    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())
    

Valida la solicitud en el agente receptor

En el agente receptor, valida el token de ID que se encuentra en la solicitud entrante de la siguiente manera. Puedes usar bibliotecas criptográficas como Tink para realizar estos pasos de verificación en lugar de escribir código personalizado.

  1. Extrae el token de identidad del encabezado Authorization: Bearer de la solicitud.
  2. Verifica que la reclamación iss (emisor) en el token de ID sea el grupo de identidades del agente que llama. El emisor es uno de los siguientes, según si el agente que llama se encuentra en un proyecto que pertenece a una organización:
    • Proyecto que pertenece a una organización: https://sts.googleapis.com/v1/organizations/ORGANIZATION_ID/locations/global/workloadIdentityPools/agents.global.org-ORGANIZATION_ID.system.id.goog, donde ORGANIZATION_ID es el ID de la organización que contiene el proyecto del agente de llamada.
    • Proyecto que no pertenece a una organización: https://sts.googleapis.com/v1/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/agents.global.proj-PROJECT_NUMBER.system.id.goog, donde PROJECT_NUMBER es el número de proyecto del clúster de GKE del agente de llamada.
  3. Descubre el URI del conjunto de claves web JSON (JWKS) para el emisor y almacena en caché las claves web JSON (JWK) públicas. El extremo de JWKS tiene el formato ISSUER_URL/openid/jwks, donde ISSUER_URL es la URL del emisor.
  4. Valida la firma del token con la siguiente información del encabezado JOSE del token de ID:
    • Es el JWK público que coincide con el parámetro de encabezado kid.
    • Es el algoritmo criptográfico que coincide con el parámetro de encabezado alg, como RS256.
  5. Verifica que la huella digital del certificado SHA-256 que se encuentra en el parámetro cnf.x5t#S256 coincida con la huella digital del certificado X.509 que el agente de llamada usó para autenticar la conexión de mTLS.
  6. Verifica los siguientes reclamos en el token de ID:
    • La hora de vencimiento de la reclamación exp es futura.
    • El público de la reclamación aud es el agente receptor.
  7. Autoriza la solicitud según el ID de SPIFFE que se encuentra en el reclamo sub (sujeto) del token.

¿Qué sigue?