Autentica para la invocación (1ª gen.)

Para invocar una función de Cloud Functions autenticada, la principal subyacente debe cumplir con los siguientes requisitos:

  • Tener permiso para invocar la función
  • Proporcionar un token de ID cuando invoque la función

¿Qué es un principal? Como se describe en Protege tus Cloud Run Functions, Cloud Run Functions admite dos tipos diferentes de identidades, que también se llaman principales:

  • Cuentas de servicio: Estas son cuentas especiales que actúan como la identidad de una entidad que no es una persona, como una función, una aplicación o una VM. Te brindan una forma de autenticar a las entidades que no son personas.
  • Cuentas de usuario: Estas cuentas representan personas, ya sea como titulares de cuentas de Google individuales o como parte de una entidad controlada por Google, como un Grupo de Google.

Consulta la descripción general de IAM para obtener más información sobre los conceptos básicos de IAM.

Para invocar una función de Cloud Run Functions autenticada, el principal debe tener el permiso de IAM de invocador:

  • cloudfunctions.functions.invoke. Por lo general, esto se hace a través del rol de invocador.

Para otorgar estos permisos, usa el comando add-invoker-policy-binding, como se muestra en Autenticar la función para realizar llamadas.

Si quieres obtener permiso para crear, actualizar o realizar otras acciones administrativas en una función, la principal debe tener una función adecuada. Los roles incluyen permisos que definen las acciones que la principal puede realizar. Para obtener más información, consulta Usa IAM para autorizar el acceso.

Invocar una función puede tener complejidades adicionales. Solo la fuente del evento a la que están suscritas las funciones controladas por eventos puede invocarlas, pero las funciones de HTTP pueden ser invocadas por de diferentes tipos de identidades, originadas en diferentes lugares. El invocador puede ser un desarrollador que está probando la función o puede ser otra función o servicio que desee usarla. De forma predeterminada, estas identidades deben proporcionar un token de ID con la solicitud para autenticarse. Además, la cuenta que se usa también debe tener los permisos adecuados.

Obtén más información sobre cómo generar y usar tokens de ID.

Ejemplos de autenticación

En esta sección, se muestran algunos ejemplos diferentes de autenticación para la invocación.

Ejemplo 1: Autentica las pruebas de los desarrolladores

Como desarrollador, necesitas acceso para crear, actualizar y borrar funciones, y este se otorga mediante el proceso normal (de IAM).

Sin embargo, como desarrollador, es posible que debas invocar las funciones para realizar pruebas. Para invocar una función mediante curl o herramientas similares, ten en cuenta lo siguiente:

  • Asigna un rol a tu cuenta de usuario de Cloud Run Functions que contenga el permiso para invocar.

    • cloudfunctions.functions.invoke. Por lo general, esto se hace a través del rol de invocador. De manera predeterminada, los roles de Administrador de Cloud Run Functions y Desarrollador de Cloud Run Functions tienen este permiso. Consulta los roles de IAM de Cloud Run Functions para ver la lista completa de las funciones y los permisos asociados con estas.
  • Si trabajas desde tu máquina local, configura el acceso a la línea de comandos. Para ello, inicializa Google Cloud CLI.

  • Proporciona tu solicitud con credenciales de autenticación como un token de ID generado por Google almacenado en un encabezado Authorization. Por ejemplo, obtén un token de ID con gcloud mediante la ejecución del siguiente comando:

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

    donde FUNCTION_URL es la URL de tu función. Recupera esta URL desde la página de Cloud Run Functions de la consola de Google Cloud o ejecuta el comando gcloud functions describe como se muestra en el primer paso del ejemplo de comando de implementación de Google Cloud CLI.

Puedes usar tokens creados por gcloud para invocar funciones de HTTP en cualquier proyecto, siempre y cuando tu cuenta tenga el permiso cloudfunctions.functions.invoke en la función que se invoca. Para fines de desarrollo, usa tokens de ID generados por gcloud. Sin embargo, ten en cuenta que estos tokens no tienen una reclamación de público, lo que los hace susceptibles a los ataques de retransmisión. En entornos de producción, usa tokens de ID emitidos para una cuenta de servicio con el público adecuado especificado. Este enfoque mejora la seguridad gracias a la restricción del uso del token solo al servicio previsto.

Como siempre, te recomendamos asignar el conjunto mínimo de permisos necesarios para desarrollar y usar tus funciones. Asegúrate de que las políticas de IAM en tus funciones estén limitadas a la cantidad mínima de usuarios y cuentas de servicio.

Ejemplo 2: Autentica una función para realizar llamadas

Cuando compilas servicios que conectan varias funciones, es una buena idea asegurarte de que cada función solo pueda enviar solicitudes a un subconjunto específico de tus otras funciones. Por ejemplo, si tienes una función login, esta debería poder acceder a la función user profiles, pero probablemente no debería poder acceder a la función search.

Si deseas configurar la función receptora para que acepte solicitudes de una función de llamada específica, debes otorgar el rol de invocador a la cuenta de servicio de la función que llama en la función receptora. El rol de invocador es Invocador de Cloud Run Functions (roles/cloudfunctions.invoker):

Console

Usa el Invocador de Cloud Run Functions:

  1. Ve a la consola de Google Cloud:

    Ir a la consola de Google Cloud

  2. Haz clic en la casilla de verificación junto a la función receptora. (No hagas clic en la función).

  3. Haz clic en Permisos en la parte superior de la pantalla. Se abrirá el panel Permisos.

  4. Haz clic en Agregar principal.

  5. En el campo Principales nuevas, ingresa la identidad de la función que realiza la llamada. Debería ser el correo electrónico de una cuenta de servicio.

  6. Selecciona Cloud Functions > Invocador de Cloud Functions en el menú desplegable Seleccionar un rol.

  7. Haz clic en Guardar.

gcloud

Usa el comando gcloud functions add-invoker-policy-binding:

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

Con el comando add-invoker-policy-binding, se agrega una vinculación de política de IAM de función de invocador que permite que el miembro especificado (principal) invoque la función concreta. Google Cloud CLI detecta automáticamente la generación de funciones y agrega el rol de Invocador correcto (cloudfunctions.invoker para la 1ª gen.).

Reemplaza lo siguiente:

  • RECEIVING_FUNCTION: Es el nombre de la función receptora.
  • CALLING_FUNCTION_IDENTITY: Es la identidad de la función que realiza la llamada, un correo electrónico de cuenta de servicio.

Debido a que invocará la función receptora, la función que realiza la llamada también debe proporcionar un token de ID firmado por Google para autenticarse. Este es un proceso en dos pasos:

  1. Crea un token de ID firmado por Google con el campo de público (aud) configurado en la URL de la función receptora.

  2. Incluye el token de ID en un encabezado Authorization: Bearer ID_TOKEN en la solicitud a la función.

La forma más fácil y confiable de administrar este proceso es usar las bibliotecas de autenticación como se muestra a continuación para generar y emplear este token.

Genera tokens de ID

En esta sección, se describen diferentes formas de generar el token de ID que un principal necesita para invocar una función.

El acceso no autenticado sin un token de ID es posible, pero debe habilitarse. Para obtener más información, consulta Usa IAM para autorizar el acceso.

Genera tokens de manera programática

Después de que el siguiente código genere un token de ID, llama a la función de Cloud Run Functions con ese token en tu nombre. Este código funciona en cualquier entorno en el que las bibliotecas puedan obtener credenciales de autenticación, incluidos los que admitan credenciales predeterminadas de la aplicación locales.

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

Genera tokens de forma manual

Si invocas una función y, por algún motivo, no puedes usar las bibliotecas de autenticación, hay dos maneras de obtener el token de ID manualmente, ya sea mediante el Servidor de metadatos de Compute o mediante la creación de un JWT autofirmado y intercambiarlo por un token de ID firmado por Google.

Usa el servidor de metadatos

Puedes usar el servidor de metadatos de Compute para obtener tokens de identidad con un público específico de la siguiente manera:

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

Reemplaza AUDIENCE por la URL de la función que invocas. Puedes recuperar esta URL como se describe en la sección anterior Autentica las pruebas de desarrolladores.

Intercambia un JWT autofirmado por un token de ID firmado por Google

  1. Otorga el rol de Invocador (roles/cloudfunctions.invoker) a la cuenta de servicio de la función que llama en la función receptora.

  2. Crea una cuenta de servicio y una clave y descarga el archivo con la clave privada (en formato JSON) en el host desde el que la función o el servicio que realiza la llamada realiza sus solicitudes.

  3. Crea un JWT con el encabezado configurado como {"alg":"RS256","typ":"JWT"}. La carga útil debe incluir una reclamación target_audience configurada para la URL de la función receptora y las reclamaciones iss y sub configuradas para la dirección de correo electrónico de la cuenta de servicio que se usó antes. También debe incluir las reclamaciones exp y iat. La reclamación aud se debe configurar como https://www.googleapis.com/oauth2/v4/token.

  4. Usa la clave privada que descargaste antes para firmar el JWT.

  5. Mediante el uso de este JWT, envía una solicitud POST a https://www.googleapis.com/oauth2/v4/token. Los datos de autenticación deben incluirse en el encabezado y el cuerpo de la solicitud.

    En el encabezado:

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

    En el cuerpo, haz lo siguiente:

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

    Reemplaza $JWT por el JWT que acabas de crear.

    Esto muestra otro JWT que incluye un id_token firmado por Google.

Envía tu solicitud GET/POST a la función receptora. Incluye el token de ID firmado por Google en un encabezado Authorization: Bearer ID_TOKEN_JWT de la solicitud.

¿Qué sigue?