Autenticar para invocação (1ª geração)

Para invocar uma Cloud Run function autenticada, o principal subjacente precisa atender aos seguintes requisitos:

  • Ter permissão para invocar a função.
  • Fornecer um token de ID ao invocar a função.

O que é um principal? Conforme descrito em Proteger o Cloud Run functions, o produto oferece suporte a dois tipos diferentes de identidades, que também são chamadas de principais:

  • Contas de serviço: são contas especiais que servem como a identidade de um usuário que não é uma pessoa, como uma função, um aplicativo ou uma VM. É uma maneira de autenticar esses usuários que não são pessoas.
  • Contas de usuário: representam pessoas, sejam proprietários de contas individuais do Google ou como parte de uma entidade controlada pelo Google, como um Grupo do Google.

Consulte a Visão geral do IAM para saber mais sobre os conceitos básicos do IAM.

Para invocar uma função do Cloud Run autenticada, o principal precisa ter a permissão do IAM do invocador:

Para conceder essas permissões, use o comando add-invoker-policy-binding, conforme mostrado em Como autenticar a função para chamadas de função.

Para ter permissão a fim de criar, atualizar ou executar outras ações administrativas em uma função, o principal precisa ter um papel adequado. Os papéis incluem permissões que definem as ações possíveis para o principal. Para mais informações, consulte Como usar o IAM para autorizar o acesso.

A invocação de uma função pode ter mais complexidades. As funções orientadas por eventos só podem ser invocadas pela fonte do evento em que elas estão inscritas, mas as funções HTTP podem ser chamadas por diferentes tipos de identidades, originárias de lugares diferentes. O invocador pode ser um desenvolvedor que está testando a função ou outra função ou serviço que quer usar a função. Por padrão, essas identidades precisam fornecer um token de ID com a solicitação de autenticação. Além disso, a conta que está sendo usada também precisa ter recebido as permissões apropriadas.

Saiba mais sobre como gerar e usar tokens de ID.

Exemplos de autenticação

Nesta seção, mostramos alguns exemplos diferentes de autenticação para invocação.

Exemplo 1: autenticar testes de desenvolvedor

Como desenvolvedor, você precisa de acesso para criar, atualizar e excluir funções. Isso é concedido usando o processo normal (IAM).

No entanto, como desenvolvedor, talvez você precise invocar as funções para fins de teste. Para invocar uma função usando curl ou ferramentas semelhantes, observe o seguinte:

  • Atribua um papel à sua conta de usuário do Cloud Run functions que contenha a permissão para invocar.

    • cloudfunctions.functions.invoke. Isso geralmente ocorre usando o papel de Invocador. Por padrão, os papéis de administrador das funções do Cloud Run e de desenvolvedor das funções do Cloud Run têm essa permissão. Consulte Papéis do IAM das funções do Cloud Run para conferir a lista completa de papéis e as permissões associadas.
  • Se você estiver trabalhando na sua máquina local, configure o acesso da linha de comando ao inicializar o SDK do Google Cloud.

  • Forneça à solicitação as credenciais de autenticação como um token de ID gerado pelo Google, armazenado em um cabeçalho Authorization. Por exemplo, para receber um token de ID usando gcloud, execute o seguinte comando:

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

    Em que FUNCTION_URL é o URL da função Recupere esse URL na página do Cloud Run functions do console do Google Cloud ou execute o comando gcloud functions describe, como mostrado na primeira etapa do exemplo do comando de implantação da CLI do Google Cloud.

É possível usar tokens criados por gcloud para invocar funções HTTP em qualquer projeto, desde que sua conta tenha a permissão cloudfunctions.functions.invoke na função invocada. Para fins de desenvolvimento, use tokens de ID gerados por gcloud. No entanto, esses tokens não têm uma reivindicação de público, o que os torna suscetíveis a ataques de redirecionamento. Em ambientes de produção, use tokens de ID emitidos para uma conta de serviço com o público-alvo apropriado especificado. Essa abordagem melhora a segurança porque restringe o uso do token apenas ao serviço pretendido.

Como sempre, recomendamos que você aloque o conjunto mínimo de permissões necessárias para desenvolver e usar suas funções. Verifique se as políticas do IAM nas suas funções estão limitadas ao número mínimo de usuários e contas de serviço.

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

Ao criar serviços que conectam várias funções, é recomendável garantir que cada função envie solicitações somente a um subconjunto específico de outras funções. Por exemplo, se você tiver uma função login, ela deve ser capaz de acessar a função user profiles, mas não a função search.

Para configurar a função de recebimento para aceitar solicitações de uma função de chamada específica, conceda o papel do invocador correto à conta de serviço da função de chamada na função de recebimento. O papel do invocador é o invocador de funções do Cloud Run (roles/cloudfunctions.invoker):

Console

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

  1. Acesse o Console do Google Cloud:

    Acessar o Console do Google Cloud

  2. Clique na caixa de seleção ao lado da função de recebimento. Não clique na função em si.

  3. Clique em Permissões na parte superior da tela. O painel Permissões é aberto.

  4. Clique em Adicionar principal.

  5. No campo Novos principais, insira a identidade da função de chamada. Ela deve ser um e-mail da conta de serviço.

  6. Selecione o papel Cloud Functions > Invocador do Cloud Functionsno menu suspenso Selecionar um papel.

  7. Clique em Salvar.

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 vinculação de política do IAM do papel de invocador que permite que o membro especificado (principal) invoque a função especificada. A CLI do Google Cloud detecta automaticamente a geração da função e adiciona o papel de invocador correto (cloudfunctions.invoker para a primeira geração).

Substitua:

  • RECEIVING_FUNCTION: o nome da função de recebimento.
  • CALLING_FUNCTION_IDENTITY: a identidade da função de chamada, um e-mail de conta de serviço.

Como ela invoca a função de recebimento, a função de chamada também precisa fornecer um token de ID assinado pelo Google para autenticação. Esse processo é realizado em duas etapas:

  1. Criar um token de ID assinado pelo Google com o campo de público-alvo (aud) definido como a URL da função de recebimento.

  2. Incluir o token de código em um cabeçalho Authorization: Bearer ID_TOKEN na solicitação para a função.

A maneira mais fácil e confiável de gerenciar esse processo é usar as bibliotecas de autenticação, conforme mostrado abaixo, para gerar e empregar esse token.

Gerar tokens de ID

Nesta seção, descrevemos diferentes maneiras de gerar o token de ID necessário para que um principal invoque uma função.

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

Gerar tokens de modo programático

Depois que o código a seguir gera um token de ID, ele chama a função do Cloud Run com esse token em seu nome. Esse código funciona em qualquer ambiente em que as bibliotecas possam receber credenciais de autenticação, incluindo ambientes compatíveis com o Application Default Credentials local.

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.request({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();
  }
}

Gerar tokens de modo manual

Se você estiver invocando uma função e, por algum motivo, não for possível usar as bibliotecas de autenticação, haverá duas maneiras de receber o token de ID manualmente: usando o servidor de metadados do Compute ou criando um JWT autoassinado e o trocando por um token de ID assinado pelo Google.

Usar o servidor de metadados

Use o servidor de metadados do Compute para buscar tokens de ID com um público-alvo específico da seguinte maneira:

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 você está invocando. Você pode recuperar esse URL conforme descrito na seção Como autenticar testes de desenvolvedor acima.

Trocar um JWT autoassinado por um token de ID assinado pelo Google

  1. Conceda o papel de Invocador (roles/cloudfunctions.invoker) à conta de serviço da função de chamada na função de recebimento.

  2. Crie uma conta de serviço e uma chave e faça o download do arquivo com a chave privada (no formato JSON) para o host de onde a função ou serviço de chamada faz as solicitações

  3. Crie um JWT com o cabeçalho definido como {"alg":"RS256","typ":"JWT"}. O payload precisa incluir uma declaração target_audience definida para o URL da função de recebimento e as declarações iss e sub definidas para o endereço de e-mail da conta de serviço usada acima Também deve incluir exp e iat declarações A declaração aud precisa ser definida como https://www.googleapis.com/oauth2/v4/token

  4. Use a chave privada, cujo download foi feito, acima

  5. Usando este JWT, envie uma solicitação POST para https://www.googleapis.com/oauth2/v4/token Os dados de autenticação precisam ser incluídos no cabeçalho e no corpo da solicitação.

    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 você acabou de criar.

    Isso retorna outro JWT, que inclui um id_token assinado pelo Google.

Envie sua solicitação GET/POST para a função de recebimento. Inclua o token de ID assinado pelo Google em um cabeçalho Authorization: Bearer ID_TOKEN_JWT na solicitação.

A seguir