Autentique para invocação (1.ª geração)

Para invocar uma função do Cloud Run autenticada, o principal subjacente tem de cumprir os seguintes requisitos:

  • Ter autorização para invocar a função.
  • Fornecer um token de ID quando invoca a função.

O que é um principal? Conforme descrito no artigo Proteja as suas funções do Cloud Run, as funções do Cloud Run suportam dois tipos diferentes de identidades, que também são denominadas principais:

  • Contas de serviço: estas são contas especiais que servem como identidade de uma pessoa não humana, como uma função, uma aplicação ou uma VM. Dão-lhe uma forma de autenticar estas entidades que não são pessoas.
  • Contas de utilizador: estas contas representam pessoas, quer como titulares individuais de uma Conta Google, quer como parte de uma entidade controlada pela Google, como um Grupo Google.

Consulte a vista geral do IAM para saber mais acerca dos conceitos básicos do IAM.

Para invocar uma função do Cloud Run autenticada, o principal tem de ter a autorização do IAM do invocador:

  • cloudfunctions.functions.invoke. Normalmente, isto é feito através da função Invoker.

Para conceder estas autorizações, use o comando add-invoker-policy-binding, conforme mostrado em Autenticar a função para chamar funções.

Para ter autorização para criar, atualizar ou realizar outras ações administrativas numa função, o principal tem de ter uma função adequada. As funções incluem autorizações que definem as ações que o principal tem autorização para realizar. Consulte o artigo Usar o IAM para autorizar o acesso para mais informações.

A invocação de uma função pode ter complexidades adicionais. As funções acionadas por eventos só podem ser invocadas pela origem do evento à qual estão subscritas, mas as funções HTTP podem ser invocadas por diferentes tipos de identidades, originárias de diferentes locais. O invocador pode ser um programador que está a testar a função ou outra função ou serviço que quer usar a função. Por predefinição, estas identidades têm de fornecer um token de ID com o pedido para se autenticarem. Além disso, a conta usada também tem de ter as autorizações adequadas.

Saiba mais sobre como gerar e usar tokens de ID.

Exemplos de autenticação

Esta secção mostra alguns exemplos diferentes de autenticação para invocação.

Exemplo 1: autentique os testes de programadores

Como programador, precisa de acesso para criar, atualizar e eliminar funções, e este acesso é concedido através do processo normal (IAM).

No entanto, como programador, pode ter de invocar as suas funções para fins de teste. Para invocar uma função através de curl ou ferramentas semelhantes, tenha em atenção o seguinte:

  • Atribua uma função à sua conta de utilizador das funções do Cloud Run que contenha a autorização invoke.

    • cloudfunctions.functions.invoke. Normalmente, isto é feito através da função Invoker. Por predefinição, as funções Administrador das funções do Cloud Run e Programador das funções do Cloud Run têm esta autorização. Consulte o artigo Funções de IAM do Cloud Run Functions para ver a lista completa de funções e as respetivas autorizações associadas.
  • Se estiver a trabalhar a partir da sua máquina local, configure o acesso à linha de comandos inicializando a CLI gcloud.

  • Indique o seu pedido com credenciais de autenticação como um token de ID gerado pela Google armazenado num cabeçalho Authorization. Por exemplo, para obter um token de ID com gcloud, execute o seguinte comando:

    curl  -H "Authorization: Bearer $(gcloud auth print-identity-token)" \
      https://FUNCTION_URL

    em que FUNCTION_URL é o URL da sua função. Obtenha este URL na página de funções do Cloud Run daGoogle Cloud consola ou executando o comando gcloud functions describe, conforme mostrado no primeiro passo do exemplo de comando de implementação da CLI Google Cloud.

Pode usar tokens criados por gcloud para invocar funções HTTP em qualquer projeto, desde que a sua conta tenha a autorização cloudfunctions.functions.invoke na função que está a ser invocada. Para fins de programação, use tokens de ID gerados pela gcloud. No entanto, tenha em atenção que estes tokens não têm uma reivindicação de público-alvo, o que os torna suscetíveis a ataques de retransmissão. Em ambientes de produção, use tokens de ID emitidos para uma conta de serviço com o público-alvo adequado especificado. Esta abordagem melhora a segurança ao restringir a utilização de tokens apenas ao serviço pretendido.

Como sempre, recomendamos que atribua o conjunto mínimo de autorizações necessárias para desenvolver e usar as suas funções. Certifique-se de que as políticas de IAM nas suas funções estão limitadas ao número mínimo de utilizadores e contas de serviço.

Exemplo 2: autenticar a função para chamadas de funções

Ao criar serviços que ligam várias funções, é uma boa ideia garantir que cada função só pode enviar pedidos a um subconjunto específico das suas outras funções. Por exemplo, se tiver uma função login, deve poder aceder à função user profiles, mas provavelmente não deve poder aceder à função search.

Para configurar a função de receção para aceitar pedidos de uma função de chamada específica, tem de conceder a função de invocador adequada à conta de serviço da função de chamada na função de receção. A função de invocador é a função de invocador das funções do Cloud Run (roles/cloudfunctions.invoker):

Consola

Use o invocador de funções do Cloud Run:

  1. Aceda à Google Cloud consola:

    Aceda à Google Cloud consola

  2. Clique na caixa de verificação junto à função de receção. (Não clique na própria função.)

  3. Clique em Autorizações na parte superior do ecrã. O painel Autorizações é aberto.

  4. Clique em Adicionar principal.

  5. No campo Novos principais, introduza a identidade da função de chamada. Este deve ser o email de uma conta de serviço.

  6. Selecione a função Cloud Functions > Cloud Functions Invoker no menu pendente Selecionar uma função.

  7. Clique em Guardar.

gcloud

Use o comando gcloud functions add-invoker-policy-binding:

gcloud functions add-invoker-policy-binding RECEIVING_FUNCTION \
  --member='serviceAccount:CALLING_FUNCTION_IDENTITY'

O comando add-invoker-policy-binding adiciona uma associação da política de IAM da função de invocador que permite que o membro (principal) especificado invoque a função especificada. A CLI do Google Cloud deteta automaticamente a geração de funções e adiciona a função Invoker correta (cloudfunctions.invoker para a 1.ª geração).

Substitua o seguinte:

  • RECEIVING_FUNCTION: o nome da função de receção.
  • CALLING_FUNCTION_IDENTITY: a identidade da função de chamada, um email de conta de serviço.

Uma vez que vai invocar a função de receção, a função de chamada também tem de fornecer um token de ID assinado pela Google para autenticação. Este é um processo de 2 passos:

  1. Crie um token de ID assinado pela Google com o campo de público-alvo (aud) definido como o URL da função de receção.

  2. Inclua o token de ID num cabeçalho Authorization: Bearer ID_TOKEN no pedido à função.

A forma mais fácil e fiável de gerir este processo é usar as bibliotecas de autenticação, conforme mostrado abaixo, para gerar e usar este token.

Gere tokens de ID

Esta secção descreve diferentes formas de gerar o token de ID de que um principal precisa para invocar uma função.

O acesso não autenticado sem um token de ID é possível, mas tem de ser ativado. Consulte o artigo Usar o IAM para autorizar o acesso para mais informações.

Gere tokens programaticamente

Depois de o código seguinte gerar um token de ID, chama a sua função do Cloud Run com esse token em seu nome. Este código funciona em qualquer ambiente onde as bibliotecas possam obter credenciais de autenticação, incluindo ambientes que suportam credenciais predefinidas da aplicação locais.

Node.js

/**
 * TODO(developer): Uncomment these variables before running the sample.
 */

// Cloud Functions uses your function's url as the `targetAudience` value
// const targetAudience = 'https://project-region-projectid.cloudfunctions.net/myFunction';
// For Cloud Functions, endpoint (`url`) and `targetAudience` should be equal
// const url = targetAudience;


const {GoogleAuth} = require('google-auth-library');
const auth = new GoogleAuth();

async function request() {
  console.info(`request ${url} with target audience ${targetAudience}`);
  const client = await auth.getIdTokenClient(targetAudience);

  // Alternatively, one can use `client.idTokenProvider.fetchIdToken`
  // to return the ID Token.
  const res = await client.fetch(url);
  console.info(res.data);
}

request().catch(err => {
  console.error(err.message);
  process.exitCode = 1;
});

Python

import urllib

import google.auth.transport.requests
import google.oauth2.id_token


def make_authorized_get_request(endpoint, audience):
    """
    make_authorized_get_request makes a GET request to the specified HTTP endpoint
    by authenticating with the ID token obtained from the google-auth client library
    using the specified audience value.
    """

    # Cloud Functions uses your function's URL as the `audience` value
    # audience = https://project-region-projectid.cloudfunctions.net/myFunction
    # For Cloud Functions, `endpoint` and `audience` should be equal

    req = urllib.request.Request(endpoint)

    auth_req = google.auth.transport.requests.Request()
    id_token = google.oauth2.id_token.fetch_id_token(auth_req, audience)

    req.add_header("Authorization", f"Bearer {id_token}")
    response = urllib.request.urlopen(req)

    return response.read()

Go


import (
	"context"
	"fmt"
	"io"

	"google.golang.org/api/idtoken"
)

// `makeGetRequest` makes a request to the provided `targetURL`
// with an authenticated client using audience `audience`.
func makeGetRequest(w io.Writer, targetURL string, audience string) error {
	// For Cloud Functions, endpoint (`serviceUrl`) and `audience` are the same.
	// Example `audience` value (Cloud Functions): https://<PROJECT>-<REGION>-<PROJECT_ID>.cloudfunctions.net/myFunction
	// (`targetURL` and `audience` will differ for GET parameters)
	ctx := context.Background()

	// client is a http.Client that automatically adds an "Authorization" header
	// to any requests made.
	client, err := idtoken.NewClient(ctx, audience)
	if err != nil {
		return fmt.Errorf("idtoken.NewClient: %w", err)
	}

	resp, err := client.Get(targetURL)
	if err != nil {
		return fmt.Errorf("client.Get: %w", err)
	}
	defer resp.Body.Close()
	if _, err := io.Copy(w, resp.Body); err != nil {
		return fmt.Errorf("io.Copy: %w", err)
	}

	return nil
}

Java

import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.auth.http.HttpCredentialsAdapter;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.auth.oauth2.IdTokenCredentials;
import com.google.auth.oauth2.IdTokenProvider;
import java.io.IOException;

public class Authentication {

  // makeGetRequest makes a GET request to the specified Cloud Run or
  // Cloud Functions endpoint `serviceUrl` (must be a complete URL), by
  // authenticating with an ID token retrieved from Application Default
  // Credentials using the specified `audience`.
  //
  // For Cloud Functions, endpoint (`serviceUrl`) and `audience` are the same.
  // Example `audience` value (Cloud Functions): https://project-region-projectid.cloudfunctions.net/myFunction
  public static HttpResponse makeGetRequest(String serviceUrl, String audience) throws IOException {
    GoogleCredentials credentials = GoogleCredentials.getApplicationDefault();
    if (!(credentials instanceof IdTokenProvider)) {
      throw new IllegalArgumentException("Credentials are not an instance of IdTokenProvider.");
    }
    IdTokenCredentials tokenCredential =
        IdTokenCredentials.newBuilder()
            .setIdTokenProvider((IdTokenProvider) credentials)
            .setTargetAudience(audience)
            .build();

    GenericUrl genericUrl = new GenericUrl(serviceUrl);
    HttpCredentialsAdapter adapter = new HttpCredentialsAdapter(tokenCredential);
    HttpTransport transport = new NetHttpTransport();
    HttpRequest request = transport.createRequestFactory(adapter).buildGetRequest(genericUrl);
    return request.execute();
  }
}

Gere tokens manualmente

Se estiver a invocar uma função e, por algum motivo, não conseguir usar as bibliotecas de autenticação, existem duas formas de obter o token de ID manualmente: através do servidor de metadados do Compute ou criando um JWT autoassinado e trocando-o por um token de ID assinado pela Google.

Use o servidor de metadados

Pode usar o servidor de metadados de computação para obter tokens de ID com um público-alvo específico da seguinte forma:

curl "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=AUDIENCE" \
     -H "Metadata-Flavor: Google"

Substitua AUDIENCE pelo URL da função que está a invocar. Pode obter este URL conforme descrito na secção Autenticação de testes de programadores acima.

Troque um JWT autoassinado por um token de ID assinado pela Google

  1. Conceda a função de invocador (roles/cloudfunctions.invoker) à conta de serviço da função de chamada na função de receção.

  2. Crie uma conta de serviço e uma chave e transfira o ficheiro com a chave privada (em formato JSON) para o anfitrião a partir do qual a função ou o serviço de chamada faz os respetivos pedidos.

  3. Crie um JWT com o cabeçalho definido como {"alg":"RS256","typ":"JWT"}. A carga útil deve incluir uma reivindicação target_audience definida para o URL da função de receção e reivindicações iss e sub definidas para o endereço de email da conta de serviço usado acima. Também deve incluir reivindicações exp e iat. A reivindicação aud deve ser definida como https://www.googleapis.com/oauth2/v4/token.

  4. Use a chave privada transferida acima para assinar o JWT.

  5. Com este JWT, envie um pedido POST para https://www.googleapis.com/oauth2/v4/token. Os dados de autenticação têm de ser incluídos no cabeçalho e no corpo do pedido.

    No cabeçalho:

    Authorization: Bearer $JWT - where $JWT is the JWT you just created
    Content-Type: application/x-www-form-urlencoded
    

    No corpo:

    grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=$JWT
    

    Substitua $JWT pelo JWT que acabou de criar

    Isto devolve outro JWT que inclui um id_token assinado pela Google.

Envie o seu pedido GET/POST para a função de receção. Inclua o token de ID assinado pela Google num cabeçalho Authorization: Bearer ID_TOKEN_JWT no pedido.

O que se segue?