Verifique a identidade da VM


Antes de um aplicativo enviar informações confidenciais para uma instância de máquina virtual (VM), o aplicativo pode verificar a identidade da instância usando tokens de identidade de instância assinados pelo Google. Cada instância possui um JSON Web Token (JWT) exclusivo que inclui detalhes sobre a instância, bem como a assinatura RS256 do Google. Seus aplicativos podem verificar a assinatura nos certificados Oauth2 públicos do Google para confirmar a identidade da instância com a qual estabeleceram uma conexão.

O Compute Engine gera tokens de instância assinados somente quando uma instância os solicita dos metadados da instância. As instâncias podem acessar apenas seu próprio token exclusivo e não os tokens de quaisquer outras instâncias.

Talvez você queira verificar as identidades das suas instâncias nos seguintes cenários:

  • Ao iniciar uma instância pela primeira vez, seus aplicativos talvez precisem garantir que a instância à qual eles se conectaram tenha uma identidade válida antes de transmitirem informações confidenciais à instância.
  • Quando suas políticas exigem que você armazene credenciais fora do ambiente do Compute Engine e você envia regularmente essas credenciais para suas instâncias para uso temporário. Seus aplicativos podem confirmar as identidades das instâncias sempre que precisarem transmitir credenciais.

Os métodos de autenticação de instância do Google têm os seguintes benefícios:

  • O Compute Engine cria um token exclusivo sempre que uma instância o solicita, e cada token expira em uma hora. Você pode configurar seus aplicativos para aceitarem o token de identidade de uma instância apenas uma vez, o que reduz o risco de o token ser reutilizado por um sistema não autorizado.
  • Os tokens de metadados assinados usam o padrão aberto da indústria RFC 7519 e a camada de identidade OpenID Connect 1.0 , para que as ferramentas e bibliotecas existentes funcionem perfeitamente com os tokens de identidade.

Antes de começar

  • Entenda como recuperar valores de metadados de instância .
  • Entenda os fundamentos dos JSON Web Tokens para saber como usá-los em seus aplicativos.
  • Entenda como criar e habilitar contas de serviço em suas instâncias. Suas instâncias devem ter uma conta de serviço associada a elas para que possam recuperar seus tokens de identidade. A conta de serviço não requer nenhuma permissão do IAM para recuperar esses tokens de identidade.
  • Se ainda não o fez, configure a autenticação. Autenticação é o processo pelo qual sua identidade é verificada para acesso a Google Cloud serviços e APIs. Para executar códigos ou amostras em um ambiente de desenvolvimento local, você pode se autenticar no Compute Engine selecionando uma das seguintes opções:

    Para usar os exemplos Python desta página em um ambiente de desenvolvimento local, instale e inicialize o gcloud CLI e e configure o Application Default Credentials com suas credenciais de usuário.

    1. Install the Google Cloud CLI.
    2. If you're using an external identity provider (IdP), you must first sign in to the gcloud CLI with your federated identity.

    3. To initialize the gcloud CLI, run the following command:

      gcloud init
    4. If you're using a local shell, then create local authentication credentials for your user account:

      gcloud auth application-default login

      You don't need to do this if you're using Cloud Shell.

      If an authentication error is returned, confirm that you have configured the gcloud CLI to use Workforce Identity Federation.

    Confira mais informações em Set up authentication for a local development environment.

Verificando a identidade de uma instância

Em alguns cenários, seus aplicativos precisam verificar a identidade de uma instância em execução no Compute Engine antes de transmitir dados confidenciais a essa instância. Em um exemplo típico, há um sistema em execução fora do Compute Engine chamado "Host1" e uma instância do Compute Engine chamada "VM1". VM1 pode se conectar ao Host1 e validar a identidade dessa instância com o seguinte processo:

  1. VM1 estabelece uma conexão segura com Host1 por meio de um protocolo de conexão segura de sua escolha, como HTTPS.

  2. VM1 solicita seu token de identidade exclusivo do servidor de metadados e especifica o público do token. Neste exemplo, o valor do público é o URI do Host1. A solicitação ao servidor de metadados inclui o URI do público para que Host1 possa verificar o valor posteriormente durante a etapa de verificação do token.

  3. O Google gera um novo token de identidade de instância exclusivo no formato JWT e o fornece à VM1. A carga útil do token inclui vários detalhes sobre a instância e também inclui o URI do público. Leia o Conteúdo do Token para obter uma descrição completa do conteúdo do token.

  4. VM1 envia o token de identidade para Host1 pela conexão segura existente.

  5. Host1 decodifica o token de identidade para obter o cabeçalho do token e os valores da carga útil.

  6. Host1 verifica se o token foi assinado pelo Google verificando o valor do público e verificando a assinatura do certificado em relação ao certificado público do Google .

  7. Se o token for válido, Host1 prossegue com a transmissão e fecha a conexão quando esta for finalizada. Host1 e quaisquer outros sistemas devem solicitar um novo token para quaisquer conexões subsequentes com a VM1.

Obtendo o token de identidade da instância

Quando sua instância de máquina virtual recebe uma solicitação para fornecer seu token de identidade, a instância solicita esse token do servidor de metadados usando o processo normal para obter metadados da instância . Por exemplo, você pode usar um dos seguintes métodos:

curvatura

Crie uma solicitação curl e inclua um valor no parâmetro audience . Opcionalmente, você pode incluir o parâmetro format para especificar se deseja ou não incluir detalhes do projeto e da instância na carga útil. Se estiver usando o formato full , você poderá incluir o parâmetro licenses para especificar se deseja ou não incluir códigos de licença na carga útil.

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

Substitua o seguinte:

  • AUDIENCE : o URI exclusivo acordado entre a instância e o sistema que verifica a identidade da instância. Por exemplo, o público poderia ser uma URL para a conexão entre os dois sistemas.
  • FORMAT : o parâmetro opcional que especifica se os detalhes do projeto e da instância estão incluídos ou não na carga útil. Especifique full para incluir essas informações na carga ou standard para omitir as informações da carga. O valor padrão é standard . Para obter mais informações, consulte Formato do token de identidade .
  • LICENSES : um parâmetro opcional que especifica se os códigos de licença para imagens associadas a esta instância estão incluídos na carga útil. Especifique TRUE para incluir essas informações ou FALSE para omitir essas informações da carga útil. O valor padrão é FALSE . Não tem efeito a menos que format esteja full

O servidor de metadados responde a esta solicitação com um JSON Web Token assinado usando o algoritmo RS256 . O token inclui uma assinatura do Google e informações adicionais na carga útil. Você pode enviar esse token para outros sistemas e aplicativos para que eles possam verificar o token e confirmar a identidade da sua instância.

Pitão

Você pode enviar uma solicitação simples da sua instância para o servidor de metadados usando métodos na biblioteca requests do Python. O exemplo a seguir solicita e imprime um token de identidade da instância. O token é exclusivo da instância que faz essa solicitação.

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

O servidor de metadados responde a esta solicitação com um JSON Web Token assinado usando o algoritmo RS256 . O token inclui uma assinatura do Google e informações adicionais na carga útil. Você pode enviar esse token para outros sistemas e aplicativos para que eles possam verificar o token e confirmar a identidade da sua instância.

Verificando o token

Depois que seu aplicativo receber um token de identidade de uma instância do Compute Engine, ele poderá verificar o token usando o processo a seguir.

  1. Receba o token da instância da máquina virtual, decodifique o token usando um decodificador JWT RS256 e leia o conteúdo do cabeçalho para obter o valor kid .

  2. Verifique se o token está assinado comparando-o com o certificado público do Google . Cada certificado público possui um valor kid que corresponde ao valor kid no cabeçalho do token.

  3. Se o token for válido, compare o conteúdo da carga com os valores esperados. Se a carga do token incluir detalhes sobre a instância e o projeto, seu aplicativo poderá verificar os valores de instance_id , project_id e zone . Esses valores são uma tupla globalmente exclusiva que confirma que seu aplicativo está se comunicando com a instância correta no projeto desejado.

Você pode decodificar e verificar o token usando qualquer ferramenta que desejar, mas um método comum é usar as bibliotecas da linguagem de sua escolha. Por exemplo, você pode usar o método verify_token da biblioteca Google OAuth 2.0 para Python. O método verify_token corresponde o valor kid ao certificado apropriado, verifica a assinatura, verifica a declaração de público e retorna o conteúdo da carga útil do 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

Depois que seu aplicativo verificar o token e seu conteúdo, ele poderá se comunicar com essa instância por meio de uma conexão segura e, em seguida, fechar a conexão quando terminar. Para conexões subsequentes, solicite um novo token da instância e verifique novamente a identidade da instância.

Conteúdo do token

O token de identidade da instância contém três partes principais:

O cabeçalho inclui o valor kid para identificar quais certificados Oauth2 públicos você deve usar para verificar a assinatura. O cabeçalho também inclui o valor alg para confirmar que a assinatura é gerada usando o algoritmo RS256 .

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

Carga útil

A carga contém a reivindicação de público- aud . Se a instância especificou format=full quando solicitou o token, a carga útil também incluirá declarações sobre a instância da máquina virtual e seu projeto. Ao solicitar um token de formato completo, especificar licenses=TRUE também incluirá declarações sobre as licenças associadas à instância.

{
   "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]"
      ]
    }
  }
}

Onde:

  • [TOKEN_ISSUER] : uma URL que identifica quem emitiu o token. Para o Compute Engine, esse valor é https://accounts.google.com .
  • [ISSUED_TIME] : um carimbo de data/hora unix que indica quando o token foi emitido. Esse valor é atualizado sempre que a instância solicita um token do servidor de metadados.
  • [EXPIRED_TIME] : um carimbo de data/hora unix que indica quando o token expira.
  • [AUDIENCE] : o URI exclusivo acordado entre a instância e o sistema que verifica a identidade da instância. Por exemplo, o público poderia ser uma URL para a conexão entre os dois sistemas.
  • [SUBJECT] : o assunto do token, que é o ID exclusivo da conta de serviço que você associou à sua instância.
  • [AUTHORIZED_PARTY] : a parte para a qual o token de ID foi emitido, que é o ID exclusivo da conta de serviço que você associou à sua instância.
  • [PROJECT_ID] : o ID do projeto onde você criou a instância.
  • [PROJECT_NUMBER] : o número exclusivo do projeto onde você criou a instância.
  • [ZONE] : a zona onde a instância está localizada.
  • [INSTANCE_ID] : o ID exclusivo da instância à qual este token pertence. Esse ID é exclusivo no projeto e na zona.
  • [INSTANCE_NAME] : o nome da instância à qual este token pertence. Se o seu projeto usar DNS zonal , esse nome poderá ser reutilizado entre zonas, portanto, use uma combinação dos valores project_id , zone e instance_id para identificar um ID de instância exclusivo. Projetos com DNS global habilitado têm nomes de instância exclusivos em todo o projeto.
  • [CREATION_TIMESTAMP] : um carimbo de data/hora Unix que indica quando você criou a instância.
  • [INSTANCE_CONFIDENTIALITY] : 1 se a instância for uma VM confidencial .
  • [LICENSE_1] até [LICENSE_N] : os códigos de licença para imagens associadas a esta instância.

Sua carga útil pode ser semelhante ao exemplo a seguir:

{
  "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"
      ]
    }
  }
}

Assinatura

O Google gera a assinatura codificando base64url o cabeçalho e a carga útil e concatenando os dois valores. Você pode verificar esse valor em relação aos certificados Oauth2 públicos para verificar o token.

O que vem a seguir