Autenticazione per le chiamate

Per richiamare una funzione Cloud Run autenticata, il principale sottostante deve soddisfare i seguenti requisiti:

  • Avere l'autorizzazione per richiamare la funzione.
  • Fornisci un token di identità quando viene invocata la funzione.

Che cos'è un preside? Come descritto in Proteggere le funzioni Cloud Run, le funzioni Cloud Run supportano due diversi tipi di identità, chiamate anche principali:

  • Account di servizio: si tratta di account speciali che fungono da identità di un soggetto non umano, come una funzione, un'applicazione o una VM. Ti offrono un modo per autenticare queste entità non persone.
  • Account utente: questi account rappresentano persone, come singoli detentori di Account Google o come parte di un'entità controllata da Google, come un gruppo Google.

Consulta la panoramica di IAM per saperne di più sui concetti di base di IAM.

Per richiamare una funzione Cloud Run autenticata, l'entità deve disporre dell'autorizzazione IAM invoker:

  • run.routes.invoke. In genere, tramite il ruolo Invoker di Cloud Run. Questa permission deve essere assegnata alla risorsa del servizio Cloud Run.

Per concedere queste autorizzazioni, utilizza il comando add-invoker-policy-binding, come mostrato in Autenticazione delle chiamate di funzione alle funzioni.

Per avere l'autorizzazione a creare, aggiornare o eseguire altre azioni amministrative su una funzione, il principale deve disporre di un ruolo appropriato. I ruoli includono le autorizzazioni che definiscono le azioni consentite all'entità. Per ulteriori informazioni, consulta Utilizzare IAM per autorizzare l'accesso.

L'invocazione di una funzione può comportare complessità aggiuntive. Le funzioni basate sugli eventi possono essere richiamate solo dall'origine evento a cui sono iscritte, ma le funzioni HTTP possono essere richiamate da diversi tipi di identità, provenienti da origini diverse. L'invoker potrebbe essere uno sviluppatore che sta testando la funzione o un'altra funzione o un servizio che vuole utilizzarla. Per impostazione predefinita, queste identità devono fornire un token ID con la richiesta per autenticarsi. Inoltre, all'account utilizzato devono essere state concesse anche le autorizzazioni appropriate.

Scopri di più su come generare e utilizzare i token ID.

Esempi di autenticazione

Questa sezione mostra alcuni esempi diversi di autenticazione per invocazione.

Esempio 1: autenticazione dei test per gli sviluppatori

In qualità di sviluppatore, devi avere accesso per creare, aggiornare ed eliminare le funzioni. Questo accesso viene concesso utilizzando la procedura normale (IAM).

Tuttavia, in qualità di sviluppatore, potresti dover richiamare le funzioni per scopi di test. Per richiamare una funzione utilizzando curl o strumenti simili, tieni presente quanto segue:

  • Assegna un ruolo all'account utente delle funzioni Cloud Run contenente l'autorizzazione di chiamata.

  • Se lavori dalla tua macchina locale, configura l'accesso da riga di comando inizializzando Google Cloud CLI.

  • Fornisci alla richiesta le credenziali di autenticazione come token ID generato da Google memorizzato in un'intestazione Authorization. Ad esempio, per ottenere un token ID utilizzando gcloud, esegui il seguente comando:

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

    dove FUNCTION_URL è l'URL della funzione. Recupera questo URL dalla pagina Funzioni Cloud Run della console Google Cloud o eseguendo il comando gcloud functions describe come mostrato nel primo passaggio dell'esempio del comando di deployment di Google Cloud CLI.

Puoi utilizzare i token creati da gcloud per invocare le funzioni HTTP in qualsiasi progetto, a condizione che il tuo account disponga dell'autorizzazione cloudfunctions.functions.invoke per la funzione invocata. A scopo di sviluppo, utilizza i token ID generati da gcloud. Tuttavia, tieni presente che questi token non dispongono di una rivendicazione per il pubblico, il che li rende vulnerabili ad attacchi di inoltro. Negli ambienti di produzione, utilizza gli token di identità emessi per un account di servizio con il segmento di pubblico appropriato specificato. Questo approccio migliora la sicurezza limitando l'utilizzo dei token solo al servizio previsto.

Come sempre, ti consigliamo di allocare l'insieme minimo di autorizzazioni necessarie per sviluppare e utilizzare le funzioni. Assicurati che i criteri IAM per le funzioni siano limitati al numero minimo di utenti e account di servizio.

Esempio 2: autenticazione della funzione per le chiamate di funzione

Quando crei servizi che collegano più funzioni, è buona norma assicurarti che ogni funzione possa inviare richieste solo a un sottoinsieme specifico delle altre funzioni. Ad esempio, se hai una funzione login, dovrebbe essere in grado di accedere alla funzione user profiles, ma probabilmente non dovrebbe essere in grado di accedere alla funzione search.

Per configurare la funzione di ricezione in modo che accetti richieste da una funzione di chiamata specifica, devi concedere il ruolo Invoker appropriato all'account di servizio della funzione di chiamata nella funzione di ricezione. Il ruolo invoker è Invoker di Cloud Run (roles/run.invoker) e deve essere concessi sul servizio sottostante:

Console

Utilizza Cloud Run Invoker:

  1. Vai alla console Google Cloud:

    Vai alla console Google Cloud

  2. Nell'elenco dei servizi Cloud Run, fai clic sulla casella di controllo accanto alla funzione di ricezione. Non fare clic sulla funzione stessa.

    Viene visualizzato il riquadro Autorizzazioni.

  3. Fai clic su Aggiungi entità.

  4. Inserisci l'identità del servizio di chiamata. Di solito si tratta di un indirizzo email, per impostazione predefinita PROJECT_NUMBER-compute@developer.gserviceaccount.com.

    Tieni presente che il numero di progetto è diverso dall'ID progetto e dal nome del progetto. Puoi trovare il numero del progetto nella pagina Dashboard della console Google Cloud.

  5. Seleziona il ruolo Cloud Run Invoker dal menu a discesa Seleziona un ruolo.

  6. Fai clic su Salva.

gcloud

Utilizza il comando gcloud functions add-invoker-policy-binding:

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

Il comando add-invoker-policy-binding aggiunge un vincolo del criterio IAM del ruolo invocatore che consente al membro (entità) specificato di invocare la funzione specificata. Google Cloud CLI rileva automaticamente la generazione della funzione e aggiunge il ruolo Invoker corretto (run.invoker per le funzioni Cloud Run).

Sostituisci quanto segue:

  • RECEIVING_FUNCTION: il nome della funzione di ricezione.
  • CALLING_FUNCTION_IDENTITY: l'identità della funzione di chiamata, un'email dell'account di servizio.

Poiché chiamerà la funzione di ricezione, la funzione di chiamata deve anche fornire un token ID firmato da Google per l'autenticazione. Si tratta di un processo in due passaggi:

  1. Crea un token ID firmato da Google con il campo pubblico (aud) impostato sull'URL della funzione di ricezione.

  2. Includi il token ID in un'intestazione Authorization: Bearer ID_TOKEN nella richiesta alla funzione.

Il modo più semplice e affidabile per gestire questa procedura è utilizzare le librerie di autenticazione, come mostrato di seguito, per generare e utilizzare questo token.

Genera token ID

Questa sezione descrive i diversi modi in cui puoi generare l'token ID di cui un principale ha bisogno per invocare una funzione.

L'accesso non autenticato senza un token ID è possibile, ma deve essere abilitato. Per ulteriori informazioni, consulta Utilizzo di IAM per autorizzare l'accesso.

Generare token in modo programmatico

Dopo che il codice seguente ha generato un token ID, chiama la funzione con quel token per tuo conto. Questo codice funziona in qualsiasi ambiente in cui le librerie possono ottenere le credenziali di autenticazione, inclusi gli ambienti che supportano le credenziali predefinite dell'applicazione locali.

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()

Vai


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

Generare token manualmente

Se stai richiamando una funzione e per qualche motivo non puoi utilizzare le librerie di autenticazione, puoi ottenere il token ID manualmente in due modi: utilizzando il server di metadati di Compute o creando un JWT autofirmato e scambiandolo con un token ID firmato da Google.

Utilizzare il server di metadati

Puoi utilizzare il server di metadati di Compute per recuperare i token ID con un segmento di pubblico specifico come segue:

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

Sostituisci AUDIENCE con l'URL della funzione che stai richiamando. Puoi recuperare questo URL come descritto nella sezione Test di autenticazione dello sviluppatore riportata sopra.

Scambiare un JWT autofirmato con un token ID firmato da Google

  1. Concedi il ruolo Invoker (roles/cloudfunctions.invoker) all'account di servizio della funzione chiamante nella funzione di ricezione.

  2. Crea un account di servizio e una chiave e scarica il file con la chiave privata (in formato JSON) sull'host da cui la funzione o il servizio chiamante effettua le richieste.

  3. Crea un JWT con l'intestazione impostata su {"alg":"RS256","typ":"JWT"}. Il payload deve includere un claim target_audience impostato sull'URL della funzione di ricezione e i claim iss e sub impostati sull'indirizzo email dell'account di servizio utilizzato sopra. Deve includere anche le rivendicazioni exp e iat. L'affermazione aud debe essere impostata su https://www.googleapis.com/oauth2/v4/token.

  4. Utilizza la chiave privata scaricata sopra per firmare il JWT.

  5. Utilizzando questo JWT, invia una richiesta POST all'indirizzo https://www.googleapis.com/oauth2/v4/token. I dati di autenticazione devono essere inclusi nell'intestazione e nel corpo della richiesta.

    Nell'intestazione:

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

    Nel corpo:

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

    Sostituisci $JWT con il token JWT che hai appena creato

    Viene restituito un altro JWT che include un id_token firmato da Google.

Invia la richiesta GET/POST alla funzione di ricezione. Includi il token ID firmato da Google in un Authorization: Bearer ID_TOKEN_JWT header nella richiesta.

Passaggi successivi