Verificar la identidad de la máquina virtual


Antes de que una aplicación envíe información confidencial a una instancia de máquina virtual (VM), la aplicación puede verificar la identidad de la instancia utilizando tokens de identidad de instancia firmados por Google. Cada instancia tiene un token web JSON (JWT) único que incluye detalles sobre la instancia, así como la firma RS256 de Google. Sus aplicaciones pueden verificar la firma con los certificados públicos Oauth2 de Google para confirmar la identidad de la instancia con la que han establecido una conexión.

Compute Engine genera tokens de instancia firmados solo cuando una instancia los solicita a partir de los metadatos de la instancia. Las instancias solo pueden acceder a su propio token único y no a los tokens de otras instancias.

Es posible que desee verificar las identidades de sus instancias en los siguientes escenarios:

  • Cuando inicia una instancia por primera vez, es posible que sus aplicaciones deban asegurarse de que la instancia a la que se conectaron tenga una identidad válida antes de transmitir información confidencial a la instancia.
  • Cuando sus políticas requieren que almacene credenciales fuera del entorno de Compute Engine y envía periódicamente esas credenciales a sus instancias para uso temporal. Sus aplicaciones pueden confirmar las identidades de las instancias cada vez que necesiten transmitir credenciales.

Los métodos de autenticación de instancias de Google tienen los siguientes beneficios:

  • Compute Engine crea un token único cada vez que una instancia lo solicita y cada token caduca en una hora. Puede configurar sus aplicaciones para que acepten el token de identidad de una instancia solo una vez, lo que reduce el riesgo de que un sistema no autorizado pueda reutilizar el token.
  • Los tokens de metadatos firmados utilizan el estándar abierto de la industria RFC 7519 y la capa de identidad OpenID Connect 1.0 , por lo que las herramientas y bibliotecas existentes funcionarán perfectamente con los tokens de identidad.

Antes de comenzar

Verificar la identidad de una instancia

En algunos escenarios, tus aplicaciones deben verificar la identidad de una instancia que se ejecuta en Compute Engine antes de transmitir datos confidenciales a esa instancia. En un ejemplo típico, hay un sistema ejecutándose fuera de Compute Engine llamado "Host1" y una instancia de Compute Engine llamada "VM1". VM1 puede conectarse al Host1 y validar la identidad de esa instancia con el siguiente proceso:

  1. VM1 establece una conexión segura con el Host1 a través de un protocolo de conexión segura de su elección, como HTTPS.

  2. VM1 solicita su token de identidad único al servidor de metadatos y especifica la audiencia del token. En este ejemplo, el valor de audiencia es el URI del Host1. La solicitud al servidor de metadatos incluye el URI de audiencia para que Host1 pueda verificar el valor más adelante durante el paso de verificación del token.

  3. Google genera un nuevo token de identidad de instancia único en formato JWT y lo proporciona a VM1. La carga útil del token incluye varios detalles sobre la instancia y también incluye el URI de audiencia. Lea Contenido del token para obtener una descripción completa del contenido del token.

  4. VM1 envía el token de identidad al Host1 a través de la conexión segura existente.

  5. Host1 decodifica el token de identidad para obtener el encabezado del token y los valores de carga útil.

  6. Host1 verifica que el token esté firmado por Google comprobando el valor de la audiencia y verificando la firma del certificado con el certificado público de Google .

  7. Si el token es válido, Host1 continúa con la transmisión y cierra la conexión cuando finaliza. Host1 y cualquier otro sistema deben solicitar un nuevo token para cualquier conexión posterior a VM1.

Obtención del token de identidad de la instancia

Cuando la instancia de su máquina virtual recibe una solicitud para proporcionar su token de identidad, la instancia solicita ese token al servidor de metadatos mediante el proceso normal para obtener metadatos de la instancia . Por ejemplo, podría utilizar uno de los siguientes métodos:

rizo

Cree una solicitud curl e incluya un valor en el parámetro audience . Opcionalmente, puede incluir el parámetro format para especificar si desea o no incluir detalles del proyecto y de la instancia en la carga útil. Si utiliza el formato full , puede incluir el parámetro licenses para especificar si desea incluir códigos de licencia en la carga útil.

curl -H "Metadata-Flavor: Google" \
'http://metadata/computeMetadata/v1/instance/service-accounts/default/identity?audience=AUDIENCE&format=FORMAT&licenses=LICENSES'

Reemplace lo siguiente:

  • AUDIENCE : el URI único acordado tanto por la instancia como por el sistema que verifica la identidad de la instancia. Por ejemplo, la audiencia podría ser una URL para la conexión entre los dos sistemas.
  • FORMAT : el parámetro opcional que especifica si los detalles del proyecto y de la instancia se incluyen o no en la carga útil. Especifique full para incluir esta información en la carga útil o standard para omitir la información de la carga útil. El valor predeterminado es standard . Para obtener más información, consulte Formato del token de identidad .
  • LICENSES : un parámetro opcional que especifica si los códigos de licencia para las imágenes asociadas con esta instancia se incluyen en la carga útil. Especifique TRUE para incluir esta información o FALSE para omitir esta información de la carga útil. El valor predeterminado es FALSE . No tiene efecto a menos que format esté full

El servidor de metadatos responde a esta solicitud con un token web JSON firmado mediante el algoritmo RS256 . El token incluye una firma de Google e información adicional en la carga útil. Puede enviar este token a otros sistemas y aplicaciones para que puedan verificar el token y confirmar la identidad de su instancia.

Pitón

Puede enviar una solicitud simple desde su instancia al servidor de metadatos utilizando métodos en la biblioteca requests de Python. El siguiente ejemplo solicita y luego imprime un token de identidad de instancia. El token es exclusivo de la instancia que realiza esta solicitud.

import requests

AUDIENCE_URL = "http://www.example.com"
METADATA_HEADERS = {"Metadata-Flavor": "Google"}
METADATA_VM_IDENTITY_URL = (
    "http://metadata.google.internal/computeMetadata/v1/"
    "instance/service-accounts/default/identity?"
    "audience={audience}&format={format}&licenses={licenses}"
)
FORMAT = "full"
LICENSES = "TRUE"


def acquire_token(
    audience: str = AUDIENCE_URL, format: str = "standard", licenses: bool = True
) -> str:
    """
    Requests identity information from the metadata server.

    Args:
        audience: the unique URI agreed upon by both the instance and the
            system verifying the instance's identity. For example, the audience
            could be a URL for the connection between the two systems.
        format: the optional parameter that specifies whether the project and
            instance details are included in the payload. Specify `full` to
            include this information in the payload or standard to omit the
            information from the payload. The default value is `standard`.
        licenses: an optional parameter that specifies whether license
            codes for images associated with this instance are included in the
            payload. Specify TRUE to include this information or FALSE to omit
            this information from the payload. The default value is FALSE.
            Has no effect unless format is `full`.

    Returns:
        A JSON Web Token signed using the RS256 algorithm. The token includes a
        Google signature and additional information in the payload. You can send
        this token to other systems and applications so that they can verify the
        token and confirm that the identity of your instance.
    """
    # Construct a URL with the audience and format.
    url = METADATA_VM_IDENTITY_URL.format(
        audience=audience, format=format, licenses=licenses
    )

    # Request a token from the metadata server.
    r = requests.get(url, headers=METADATA_HEADERS)
    # Extract and return the token from the response.
    r.raise_for_status()
    return r.text

El servidor de metadatos responde a esta solicitud con un token web JSON firmado mediante el algoritmo RS256 . El token incluye una firma de Google e información adicional en la carga útil. Puede enviar este token a otros sistemas y aplicaciones para que puedan verificar el token y confirmar la identidad de su instancia.

Verificando el token

Después de que tu aplicación reciba un token de identidad de instancia de una instancia de Compute Engine, puede verificar el token mediante el siguiente proceso.

  1. Reciba el token de la instancia de la máquina virtual, decodifique el token usando un decodificador RS256 JWT y lea el contenido del encabezado para obtener el valor kid .

  2. Verifique que el token esté firmado comparándolo con el certificado público de Google . Cada certificado público tiene un valor kid que corresponde al valor kid en el encabezado del token.

  3. Si el token es válido, compare el contenido de la carga útil con los valores esperados. Si la carga útil del token incluye detalles sobre la instancia y el proyecto, su aplicación puede verificar los valores de instance_id , project_id y zone . Esos valores son una tupla única a nivel mundial que confirma que su aplicación se está comunicando con la instancia correcta en el proyecto deseado.

Puede decodificar y verificar el token utilizando cualquier herramienta que desee, pero un método común es utilizar las bibliotecas del idioma que elija. Por ejemplo, puede utilizar el método verify_token de la biblioteca Google OAuth 2.0 para Python. El método verify_token hace coincidir el valor kid con el certificado apropiado, verifica la firma, verifica el reclamo de audiencia y devuelve el contenido de la carga útil del token.

import google.auth.transport.requests
from google.oauth2 import id_token
def verify_token(token: str, audience: str) -> dict:
    """Verify token signature and return the token payload.

    Args:
        token: the JSON Web Token received from the metadata server to
            be verified.
        audience: the unique URI agreed upon by both the instance and the
            system verifying the instance's identity.

    Returns:
        Dictionary containing the token payload.
    """
    request = google.auth.transport.requests.Request()
    payload = id_token.verify_token(token, request=request, audience=audience)
    return payload

Después de que su aplicación verifique el token y su contenido, puede proceder a comunicarse con esa instancia a través de una conexión segura y luego cerrar la conexión cuando finalice. Para conexiones posteriores, solicite un nuevo token de la instancia y vuelva a verificar la identidad de la instancia.

Contenido del token

El token de identidad de la instancia contiene tres partes principales:

El encabezado incluye el valor kid para identificar qué certificados públicos Oauth2 debe usar para verificar la firma. El encabezado también incluye el valor alg para confirmar que la firma se genera utilizando el algoritmo RS256 .

{
  "alg": "RS256",
  "kid": "511a3e85d2452aee960ed557e2666a8c5cedd8ae",
}

Carga útil

La carga útil contiene el reclamo de audiencia aud . Si la instancia especificó format=full cuando solicitó el token, la carga útil también incluye notificaciones sobre la instancia de la máquina virtual y su proyecto. Al solicitar un token de formato completo, especificar licenses=TRUE también incluirá notificaciones sobre las licencias asociadas con la instancia.

{
   "iss": "[TOKEN_ISSUER]",
   "iat": [ISSUED_TIME],
   "exp": [EXPIRED_TIME],
   "aud": "[AUDIENCE]",
   "sub": "[SUBJECT]",
   "azp": "[AUTHORIZED_PARTY]",
   "google": {
    "compute_engine": {
      "project_id": "[PROJECT_ID]",
      "project_number": [PROJECT_NUMBER],
      "zone": "[ZONE]",
      "instance_id": "[INSTANCE_ID]",
      "instance_name": "[INSTANCE_NAME]",
      "instance_creation_timestamp": [CREATION_TIMESTAMP],
      "instance_confidentiality": [INSTANCE_CONFIDENTIALITY],
      "license_id": [
        "[LICENSE_1]",
          ...
        "[LICENSE_N]"
      ]
    }
  }
}

Dónde:

  • [TOKEN_ISSUER] : una URL que identifica quién emitió el token. Para Compute Engine, este valor es https://accounts.google.com .
  • [ISSUED_TIME] : una marca de tiempo Unix que indica cuándo se emitió el token. Este valor se actualiza cada vez que la instancia solicita un token del servidor de metadatos.
  • [EXPIRED_TIME] : una marca de tiempo Unix que indica cuándo caduca el token.
  • [AUDIENCE] : el URI único acordado tanto por la instancia como por el sistema que verifica la identidad de la instancia. Por ejemplo, la audiencia podría ser una URL para la conexión entre los dos sistemas.
  • [SUBJECT] : el asunto del token, que es el ID único de la cuenta de servicio que asoció con su instancia.
  • [AUTHORIZED_PARTY] : la parte a la que se emitió el token de identificación, que es la identificación única de la cuenta de servicio que asoció con su instancia.
  • [PROJECT_ID] : el ID del proyecto donde creaste la instancia.
  • [PROJECT_NUMBER] : el número único para el proyecto donde creaste la instancia.
  • [ZONE] : la zona donde se encuentra la instancia.
  • [INSTANCE_ID] : el ID único de la instancia a la que pertenece este token. Esta identificación es única dentro del proyecto y la zona.
  • [INSTANCE_NAME] : el nombre de la instancia a la que pertenece este token. Si su proyecto usa DNS zonal , este nombre se puede reutilizar en todas las zonas, por lo tanto, use una combinación de los valores project_id , zone instance_id para identificar una ID de instancia única. Los proyectos con DNS global habilitado tienen un nombre de instancia único en todo el proyecto.
  • [CREATION_TIMESTAMP] : una marca de tiempo de Unix que indica cuándo creó la instancia.
  • [INSTANCE_CONFIDENTIALITY] : 1 si la instancia es una VM confidencial .
  • [LICENSE_1] a [LICENSE_N] : los códigos de licencia para las imágenes asociadas con esta instancia.

Su carga útil podría ser similar al siguiente ejemplo:

{
  "iss": "https://accounts.google.com",
  "iat": 1496953245,
  "exp": 1496956845,
  "aud": "https://www.example.com",
  "sub": "107517467455664443765",
  "azp": "107517467455664443765",
  "google": {
    "compute_engine": {
      "project_id": "my-project",
      "project_number": 739419398126,
      "zone": "us-west1-a",
      "instance_id": "152986662232938449",
      "instance_name": "example",
      "instance_creation_timestamp": 1496952205,
      "instance_confidentiality": 1,
      "license_id": [
        "1000204"
      ]
    }
  }
}

Firma

Google genera la firma mediante base64url que codifica el encabezado y la carga útil y concatena los dos valores. Puede comparar este valor con los certificados públicos Oauth2 para verificar el token.

¿Qué sigue?