Valide a identidade da VM

Antes de uma aplicação enviar informações confidenciais para uma instância de máquina virtual (VM), a aplicação pode validar a identidade da instância através de tokens de identidade da instância assinados pela Google. Cada instância tem um Símbolo da Web JSON (JWT) exclusivo que inclui detalhes sobre a instância, bem como a assinatura RS256 da Google. As suas aplicações podem validar a assinatura com base nos certificados OAuth2 públicos da Google para confirmar a identidade da instância com a qual estabeleceram uma ligação.

O Compute Engine gera tokens de instância assinados apenas quando uma instância os pede a partir dos metadados da instância. As instâncias podem aceder apenas ao seu próprio token único e não aos tokens de outras instâncias.

Recomendamos que valide as identidades das suas instâncias nos seguintes cenários:

  • Quando inicia uma instância pela primeira vez, as suas aplicações podem ter de garantir que a instância à qual se ligaram tem uma identidade válida antes de transmitirem informações confidenciais à instância.
  • Quando as suas políticas exigem que armazene credenciais fora do ambiente do Compute Engine e envia regularmente essas credenciais para as suas instâncias para utilização temporária. As suas aplicações podem confirmar as identidades das instâncias sempre que precisarem de transmitir credenciais.

Os métodos de autenticação de instâncias da Google têm as seguintes vantagens:

  • O Compute Engine cria um token exclusivo sempre que uma instância o pede, e cada token expira no prazo de uma hora. Pode configurar as suas aplicações para aceitarem o token de identidade de uma instância apenas uma vez, o que reduz o risco de o token poder ser reutilizado por um sistema não autorizado.
  • Os tokens de metadados assinados usam a norma industrial aberta RFC 7519 e a camada de identidade OpenID Connect 1.0, pelo que as ferramentas e as bibliotecas existentes funcionam perfeitamente com os tokens de identidade.

Antes de começar

  • Compreenda como obter valores de metadados da instância.
  • Compreenda os básicos dos símbolos da Web JSON para saber como os usar nas suas aplicações.
  • Compreenda como criar e ativar contas de serviço nas suas instâncias. As suas instâncias têm de ter uma conta de serviço associada para poderem obter os respetivos tokens de identidade. A conta de serviço não requer autorizações de IAM para obter estes tokens de identidade.
  • Se ainda não o tiver feito, configure a autenticação. A autenticação valida a sua identidade para aceder a Trusted Cloud by S3NS serviços e APIs. Para executar código ou exemplos a partir de um ambiente de desenvolvimento local, pode autenticar-se no Compute Engine selecionando uma das seguintes opções:

    Para usar os Python exemplos nesta página num ambiente de desenvolvimento local, instale e inicialize a CLI gcloud e, em seguida, configure as Credenciais predefinidas da aplicação com as suas credenciais de utilizador.

      Instale a CLI Google Cloud e, em seguida, inicie sessão na CLI gcloud com a sua identidade federada.

      Create local authentication credentials for your user account:

      gcloud auth application-default login

      If an authentication error is returned, and you are using an external identity provider (IdP), confirm that you have signed in to the gcloud CLI with your federated identity.

    Para mais informações, consulte Set up authentication for a local development environment.

Validar a identidade de uma instância

Em alguns cenários, as suas aplicações têm de validar a identidade de uma instância em execução no Compute Engine antes de transmitir dados confidenciais para essa instância. Num exemplo típico, existe um sistema em execução fora do Compute Engine denominado "Host1" e uma instância do Compute Engine denominada "VM1". A VM1 pode estabelecer ligação ao Host1 e validar a identidade dessa instância com o seguinte processo:

  1. A VM1 estabelece uma ligação segura ao Host1 através de um protocolo de ligação segura à sua escolha, como HTTPS.

  2. A VM1 pede o respetivo token de identidade exclusivo ao servidor de metadados e especifica o público-alvo do token. Neste exemplo, o valor do público-alvo é o URI para Host1. O pedido ao servidor de metadados inclui o URI do público para que o Host1 possa verificar o valor mais tarde durante o passo de validação do token.

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

  4. A VM1 envia o token de identidade para o Host1 através da ligação segura existente.

  5. O anfitrião 1 descodifica o token de identidade para obter o cabeçalho do token e os valores do payload.

  6. O anfitrião 1 verifica se o token está assinado pela Google através da verificação do valor do público-alvo e da validação da assinatura do certificado em relação ao certificado público da Google.

  7. Se o token for válido, o Host1 prossegue com a transmissão e fecha a ligação quando terminar. O anfitrião 1 e quaisquer outros sistemas devem pedir um novo token para quaisquer ligações subsequentes à VM1.

Obter o token de identidade da instância

Quando a instância de máquina virtual recebe um pedido para fornecer o respetivo token de identidade, a instância pede esse token ao servidor de metadados através do processo normal para obter metadados da instância. Por exemplo, pode usar um dos seguintes métodos:

cURL

Crie um pedido curl e inclua um valor no parâmetro audience. Opcionalmente, pode incluir o parâmetro format para especificar se quer ou não incluir detalhes do projeto e da instância no payload. Se usar o formato full, pode incluir o parâmetro licenses para especificar se quer 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 valida a identidade da instância. Por exemplo, o público pode ser um URL para a associaçã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 no payload. Especifique full para incluir estas informações na carga útil ou standard para omitir as informações da carga útil. O valor predefinido é standard. Para mais informações, consulte o artigo Formato do token de identidade.
  • LICENSES: um parâmetro opcional que especifica se os códigos de licença das imagens associadas a esta instância estão incluídos no payload. Especifique TRUE para incluir estas informações ou FALSE para omitir estas informações da carga útil. O valor predefinido é FALSE. Não tem efeito, a menos que format seja full

O servidor de metadados responde a este pedido com um token Web JSON assinado através do algoritmo RS256. O token inclui uma assinatura da Google e informações adicionais no payload. Pode enviar este token para outros sistemas e aplicações para que possam validar o token e confirmar a identidade da sua instância.

Python

Pode enviar um pedido simples da sua instância para o servidor de metadados usando métodos na biblioteca requests do Python. O exemplo seguinte pede e, em seguida, imprime um token de identidade da instância. O token é exclusivo da instância que faz este pedido.

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 este pedido com um token Web JSON assinado através do algoritmo RS256. O token inclui uma assinatura da Google e informações adicionais no payload. Pode enviar este token para outros sistemas e aplicações para que possam validar o token e confirmar a identidade da sua instância.

A validar o token

Depois de a sua aplicação receber um token de identidade da instância de uma instância do Compute Engine, pode validar o token através do seguinte processo.

  1. Receba o token da instância da máquina virtual, descodifique o token com um descodificador 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 da Google. Cada certificado público tem 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 útil com os valores esperados. Se a carga útil do token incluir detalhes sobre a instância e o projeto, a sua aplicação pode verificar os valores instance_id, project_id e zone. Esses valores são uma tupla globalmente única que confirma que a sua aplicação está a comunicar com a instância correta no projeto pretendido.

Pode descodificar e validar o token com qualquer ferramenta que quiser, mas um método comum é usar as bibliotecas para o seu idioma de preferência. Por exemplo, pode usar o método verify_token da biblioteca Google OAuth 2.0 para Python. O método verify_token faz corresponder o valor kid ao certificado adequado, verifica a assinatura, verifica a reivindicação de público-alvo e devolve os conteúdos 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 de a sua aplicação validar o token e o respetivo conteúdo, pode continuar a comunicar com essa instância através de uma ligação segura e, em seguida, fechar a ligação quando terminar. Para ligações subsequentes, peça uma nova chave à instância e valide 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 que certificados OAuth2 públicos tem de usar para validar a assinatura. O cabeçalho também inclui o valor alg para confirmar que a assinatura é gerada através do algoritmo RS256.

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

Payload

O payload contém a reivindicação de público-alvo aud. Se a instância tiver especificado format=full quando solicitou o token, o payload também inclui reivindicações sobre a instância da máquina virtual e o respetivo projeto. Quando pedir um token de formato completo, a especificação de licenses=TRUE também inclui reivindicaçõ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]: um URL que identifica quem emitiu o token. Para o Compute Engine, este valor é https://accounts.google.com.
  • [ISSUED_TIME]: uma data/hora Unix que indica quando o token foi emitido. Este valor é atualizado sempre que a instância pede um token ao servidor de metadados.
  • [EXPIRED_TIME]: uma data/hora no formato Unix que indica quando o token expira.
  • [AUDIENCE]: o URI exclusivo acordado entre a instância e o sistema que valida a identidade da instância. Por exemplo, o público-alvo pode ser um URL para a ligação entre os dois sistemas.
  • [SUBJECT]: o assunto do token, que é o ID exclusivo da conta de serviço que associou à sua instância.
  • [AUTHORIZED_PARTY]: a parte à qual o token de ID foi emitido, que é o ID exclusivo da conta de serviço que associou à sua instância.
  • [PROJECT_ID]: o ID do projeto onde criou a instância.
  • [PROJECT_NUMBER]: o número exclusivo do projeto onde 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. Este 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, este nome pode ser reutilizado em várias zonas. Por isso, use uma combinação dos valores project_id, zone e instance_id para identificar um ID da instância único. Os projetos com DNS global ativado têm um nome de instância único no projeto.
  • [CREATION_TIMESTAMP]: uma indicação de tempo Unix que indica quando criou a instância.
  • [INSTANCE_CONFIDENTIALITY]: 1 se a instância for uma VM confidencial.
  • [LICENSE_1] a [LICENSE_N]: os códigos de licença para imagens associadas a esta instância.

O seu payload pode ser semelhante ao seguinte exemplo:

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

A Google gera a assinatura através da codificação base64url do cabeçalho e do payload, e concatenando os dois valores. Pode verificar este valor com os certificados OAuth2 públicos para validar o token.

O que se segue?