Autenticar para invocação

Para invocar uma Função do Cloud autenticada, o principal subjacente precisa ter a permissão do IAM de invocador:

Para ter permissão para criar, atualizar ou executar outras ações administrativas em uma função, você precisa ter um principal que tenha um papel apropriado. Consulte Como usar o IAM para autorizar o acesso para mais informações.

Porém, invocar uma função pode ser um evento mais complexo. As funções orientadas por eventos só podem ser invocadas pela fonte do evento no qual elas estão inscritas, mas as funções HTTP podem ser chamadas por diferentes tipos de identidades. , originários 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 para se autenticar. Além disso, a conta que está sendo usada também precisa ter recebido as permissões apropriadas.

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.

Como 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 Functions que contenha a permissão para invocar.

    • A permissão cloudfunctions.functions.invoke. cloudfunctions.functions.invoke para funções de primeira geração. Isso geralmente ocorre por meio do papel Invocador do Cloud Functions. Por padrão, os papéis de administrador e de desenvolvedor do Cloud Functions têm essa permissão. Consulte Papéis de IAM do Cloud Functions para obter a lista completa de papéis e as permissões associadas.
    • run.routes.invoke para funções de 2a geração. Isso geralmente acontece usando o papel Invocador do Cloud Run.
  • 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 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. Certifique-se de que 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.

Como autenticar uma 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. Para as funções de primeira geração, o papel de invocador é o Cloud Functions Invoker (roles/cloudfunctions.invoker). Para funções de segunda geração, o papel de invocador é o Cloud Run Invoker (roles/run.invoker) e precisa ser concedido ao serviço subjacente.

Cloud Functions (1a geração):

Console

Para as funções de primeira geração, use o invocador do Cloud Functions:

  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 Save.

gcloud

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

gcloud functions add-iam-policy-binding RECEIVING_FUNCTION \
  --member='serviceAccount:CALLING_FUNCTION_IDENTITY' \
  --role='roles/cloudfunctions.invoker'

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

Cloud Functions (segunda geração):

Console

Para as funções de segunda geração, use o invocador do Cloud Run:

  1. Acesse o Console do Google Cloud:

    Acessar o Console do Google Cloud

  2. Na lista de serviços do Cloud Run, clique na caixa de seleção ao lado da função de recebimento. (Não clique na função em si.)

    O painel Permissões é aberto.

  3. Clique em Adicionar principal.

  4. Digite a identidade do serviço de chamada. Este é geralmente um endereço de e-mail, por padrão PROJECT_NUMBER-compute@developer.gserviceaccount.com.

    Observe que o número é diferente do ID e do nome do projeto. Encontre o número do projeto na página do Painel do console do Google Cloud.

  5. Selecione o papel Cloud Run Invoker no menu suspenso Selecionar um papel.

  6. Clique em Save.

gcloud

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

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

em que RECEIVING_FUNCTION é o nome da função de recebimento e 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 código 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.

Como gerar tokens de maneira programática

Depois que o código a seguir gera um token de ID, ele chama a função do Cloud 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();
  }
}

Como gerar tokens manualmente

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

Como usar o servidor de metadados

Use o servidor de metadados do Compute para buscar tokens de código 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.

Como trocar um JWT autoassinado por um token de ID assinado pelo Google

  1. Conceda o papel de Invocador do Cloud Functions (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