Encripta Cloud Functions con claves de encriptación administradas por el cliente (CMEK)

1. Introducción

Descripción general

Cloud Functions es una solución de procesamiento ligera para que los desarrolladores creen funciones individuales y de un solo propósito que respondan a eventos de Cloud sin necesidad de administrar un servidor o un entorno de ejecución.

Puedes usar las claves de encriptación administradas por el cliente (CMEK) de Cloud Key Management Service para proteger Cloud Functions y los datos en reposo relacionados. Implementar una función con CMEK protege los datos asociados a través de una clave de encriptación que tienes bajo tu control. Este tipo de encriptación te permite satisfacer los requisitos de cumplimiento de ciertas industrias, como los servicios financieros. Dado que la clave es de tu propiedad y no la controla Google, nadie (ni siquiera tú) puede acceder a los datos protegidos por estas claves de encriptación cuando se inhabilitan o se destruyen.

En Cloud Functions, CMEK encripta lo siguiente:

  • El código fuente de la función que se sube para su implementación y que Google almacena en Cloud Storage, y que se usa en el proceso de compilación
  • Los resultados del proceso de compilación de la función, incluida la imagen de contenedor compilada a partir del código fuente de la función, cada instancia de la función que se implementa.
  • Datos en reposo para canales de transporte de eventos internos (solo 1ª gen.).

Puedes encontrar más información sobre qué datos están encriptados en la documentación de CMEK de Cloud Function.

Qué compilarás

En este codelab, se muestra cómo implementar una Cloud Function (de 1a o 2a gen.) encriptada con CMEK. En este codelab, se usa una Cloud Function pública, es decir, una que no requiere autenticación, con fines de demostración. Puedes invocar una función autenticada habilitada para CMEK, al igual que cualquier otra Cloud Function que requiera autenticación.

Qué aprenderás

  • Cómo crear una clave CMEK en un llavero de claves simétrico existente
  • Cómo crear un repositorio de Artifact Registry
  • Cómo configurar CMEK en una Cloud Function para la 1a y la 2a gen.

2. Configuración y requisitos

Requisitos previos

  • Accediste a la consola de Cloud
  • Ya implementaste una Cloud Function activada por HTTP (para verificar que tengas habilitadas las APIs y los roles adecuados).

Activar Cloud Shell

  1. En la consola de Cloud, haz clic en Activar Cloud Shell853e55310c205094.png.

55efc1aaa7a4d3ad.png

Si es la primera vez que inicias Cloud Shell, verás una pantalla intermedia que describe en qué consiste. Si apareció una pantalla intermedia, haz clic en Continuar.

9c92662c6a846a5c.png

El aprovisionamiento y la conexión a Cloud Shell solo tomará unos minutos.

9f0e51b578fecce5.png

Esta máquina virtual está cargada con todas las herramientas de desarrollo necesarias. Ofrece un directorio principal persistente de 5 GB y se ejecuta en Google Cloud, lo que mejora considerablemente el rendimiento de la red y la autenticación. Gran parte de tu trabajo en este codelab, si no todo, se puede hacer con un navegador.

Una vez que te conectes a Cloud Shell, deberías ver que estás autenticado y que el proyecto está configurado con tu ID del proyecto.

  1. En Cloud Shell, ejecuta el siguiente comando para confirmar que tienes la autenticación:
gcloud auth list

Resultado del comando

 Credentialed Accounts
ACTIVE  ACCOUNT
*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. Ejecuta el siguiente comando en Cloud Shell para confirmar que el comando de gcloud conoce tu proyecto:
gcloud config list project

Resultado del comando

[core]
project = <PROJECT_ID>

De lo contrario, puedes configurarlo con el siguiente comando:

gcloud config set project <PROJECT_ID>

Resultado del comando

Updated property [core/project].

3. Crea una clave y un llavero de claves nuevos para Cloud Functions

Asegúrate de que la API de Cloud KMS esté habilitada. Para ello, ejecuta el siguiente comando:

gcloud services enable cloudkms.googleapis.com

Primero, crea variables de entorno que contengan el nombre del llavero de claves, el nombre de la clave, la región y otras variables que se usan en este codelab.

KEYRING_NAME="keyring-functions"
REGION="us-central1"
KEY_NAME="key-encrypted-function"
PROJECT_ID=$(gcloud config get-value project)
PROJECT_NUMBER="$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')"
USER_EMAIL="$(gcloud config list account --format "value(core.account)")"

A continuación, crea un llavero de claves, que será el recurso raíz de las claves de Cloud KMS y sus versiones.

gcloud kms keyrings create $KEYRING_NAME --location $REGION

Por último, ahora puedes crear una clave simétrica en tu llavero de claves nuevo dentro de Cloud KMS.

gcloud kms keys create $KEY_NAME --keyring $KEYRING_NAME --location $REGION --purpose "encryption"

4. Crea un repositorio de Artifact Registry con formato Docker habilitado para CMEK

En esta sección, crearás un repositorio con formato Docker en Artifact Registry que tiene CMEK habilitadas. Esta clave será la misma que se usó para implementar tu Cloud Function.

Primero, necesitarás la cuenta de servicio para Artifact Registry. Para crearlo, ejecuta este comando:

gcloud beta services identity create --service=artifactregistry.googleapis.com --project=$PROJECT_ID

Usa el siguiente comando para otorgar el rol de IAM de Encriptador o Desencriptador de CryptoKey (roles/cloudkms.cryptoKeyEncrypterDecrypter) a la cuenta de servicio de Artifact Registry para que tenga permisos para la clave:

gcloud kms keys add-iam-policy-binding \
  $KEY_NAME --location $REGION --keyring=$KEYRING_NAME \
  --member serviceAccount:service-$PROJECT_NUMBER@gcp-sa-artifactregistry.iam.gserviceaccount.com \
  --role roles/cloudkms.cryptoKeyEncrypterDecrypter

Y otorga el rol al principio que creará el repositorio en Artifact Registry, p.ej., tu cuenta activa actual. Puedes verificar tu cuenta activa actual ejecutando gcloud auth list.

gcloud kms keys add-iam-policy-binding \
       $KEY_NAME --location $REGION --keyring=$KEYRING_NAME \
       --member user:$USER_EMAIL \
       --role roles/cloudkms.cryptoKeyEncrypterDecrypter

Ahora puedes crear un repositorio con formato Docker habilitado con CMEK.

Nota: La región debe ser la misma que la clave CMEK.

REPO_NAME=my-cmek-encrypted-repo 

KEY_FULLPATH=projects/"$PROJECT_ID"/locations/"$REGION"/keyRings/"$KEYRING_NAME"/cryptoKeys/"$KEY_NAME" 

gcloud artifacts repositories create $REPO_NAME \
    --repository-format=docker \
    --location=$REGION \
    --kms-key=$KEY_FULLPATH \
    --async

Puedes ver tu nuevo repositorio de Artifact Registry ejecutando este comando:

gcloud artifacts repositories describe $REPO_NAME --location=$REGION

5. Otorga a las cuentas de servicio acceso a la clave (2a gen.)

En esta sección, se explica cómo crear cuentas de servicio para funciones de 2a gen. Si estás creando una función de 1a gen., continúa con la siguiente sección.

Debes otorgar a varios agentes de servicio acceso a la clave mediante el rol de IAM de Encriptador/Desencriptador de CryptoKey (roles/cloudkms.cryptoKeyEncrypterDecrypter). Estos agentes de servicio se usan para obtener acceso al código fuente almacenado en Cloud Storage, almacenar imágenes de funciones en un repositorio protegido por CMEK en Artifact Registry y para implementar una Cloud Function encriptada con CMEK.

Pasos para Functions de la 2a gen.

  1. Otorga al agente de servicio de Cloud Run acceso a la clave:
CLOUDRUN_SA=service-$PROJECT_NUMBER@serverless-robot-prod.iam.gserviceaccount.com

gcloud kms keys add-iam-policy-binding $KEY_NAME \
--keyring=$KEYRING_NAME \
--location=$REGION \
--member=serviceAccount:$CLOUDRUN_SA \
--role=roles/cloudkms.cryptoKeyEncrypterDecrypter
  1. Otorga al agente de servicio de Eventarc acceso a la clave:
EVENTARC_SA=service-$PROJECT_NUMBER@gcp-sa-eventarc.iam.gserviceaccount.com

gcloud kms keys add-iam-policy-binding $KEY_NAME \
--keyring=$KEYRING_NAME \
--location=$REGION \
--member=serviceAccount:$EVENTARC_SA \
--role=roles/cloudkms.cryptoKeyEncrypterDecrypter
  1. Otorga al agente de servicio de Artifact Registry acceso a la clave:
AR_SA=service-$PROJECT_NUMBER@gcp-sa-artifactregistry.iam.gserviceaccount.com

gcloud kms keys add-iam-policy-binding $KEY_NAME \
--keyring=$KEYRING_NAME \
--location=$REGION \
--member=serviceAccount:$AR_SA \
--role=roles/cloudkms.cryptoKeyEncrypterDecrypter
  1. Otorga a los agentes de servicio de Cloud Storage acceso a la clave:
STORAGE_SA=service-$PROJECT_NUMBER@gs-project-accounts.iam.gserviceaccount.com

gcloud kms keys add-iam-policy-binding $KEY_NAME \
--keyring=$KEYRING_NAME \
--location=$REGION \
--member=serviceAccount:$STORAGE_SA \
--role=roles/cloudkms.cryptoKeyEncrypterDecrypter

En la siguiente sección, aprenderás a crear e implementar una función encriptada con CMEK.

6. Otorga a las cuentas de servicio acceso a la clave (1a gen.)

En esta sección, se explica la creación de cuentas de servicio para funciones de 1a gen. Si ya creaste cuentas de servicio para una función de 2a gen., continúa con la siguiente sección.

Debes otorgar a varios agentes de servicio acceso a la clave mediante el rol de IAM de Encriptador/Desencriptador de CryptoKey (roles/cloudkms.cryptoKeyEncrypterDecrypter). Estos agentes de servicio se usan para obtener acceso al código fuente almacenado en Cloud Storage, almacenar imágenes de funciones en un repositorio protegido por CMEK en Artifact Registry y para implementar una Cloud Function encriptada con CMEK.

Pasos para Functions de 1a gen.

  1. Otorga al agente de servicio de Cloud Functions acceso a la clave:
FUNCTION_SA=service-$PROJECT_NUMBER@gcf-admin-robot.iam.gserviceaccount.com

gcloud kms keys add-iam-policy-binding $KEY_NAME \
--keyring=$KEYRING_NAME \
--location=$REGION \
--member=serviceAccount:$FUNCTION_SA \
--role=roles/cloudkms.cryptoKeyEncrypterDecrypter
  1. Otorga al agente de servicio de Artifact Registry acceso a la clave:
AR_SA=service-$PROJECT_NUMBER@gcp-sa-artifactregistry.iam.gserviceaccount.com

gcloud kms keys add-iam-policy-binding $KEY_NAME \
--keyring=$KEYRING_NAME \
--location=$REGION \
--member=serviceAccount:$AR_SA \
--role=roles/cloudkms.cryptoKeyEncrypterDecrypter
  1. Otorga a los agentes de servicio de Cloud Storage acceso a la clave:
STORAGE_SA=service-$PROJECT_NUMBER@gs-project-accounts.iam.gserviceaccount.com

gcloud kms keys add-iam-policy-binding $KEY_NAME \
--keyring=$KEYRING_NAME \
--location=$REGION \
--member=serviceAccount:$STORAGE_SA \
--role=roles/cloudkms.cryptoKeyEncrypterDecrypter

En la siguiente sección, aprenderás a crear e implementar una función encriptada con CMEK.

7. Crea una función encriptada con CMEK (2a gen.)

En esta sección, se explica cómo crear funciones de 2a gen. Puedes continuar con la siguiente sección para obtener instrucciones sobre la 1a gen.

Ahora que tienes un repositorio de Artifact Registry configurado con CMEK habilitadas y le otorgaste a Cloud Functions acceso a tu clave, ahora puedes implementar una función encriptada con tu clave CMEK.

Pasos para Functions de la 2a gen.:

Crea el código fuente para la función

Aunque este codelab utiliza Node.js, puedes usar cualquier entorno de ejecución compatible.

Primero, crea un directorio y desplázate a él con el comando cd.

mkdir ~/cmek-function-2ndgen && cd $_

Luego, crea el archivo package.json.

touch package.json

echo '{
  "dependencies": {
    "@google-cloud/functions-framework": "^2.1.0"
  }
}
' > package.json

A continuación, crea el archivo de origen index.js.

touch index.js

echo 'const functions = require("@google-cloud/functions-framework");

functions.http("helloWorld", (req, res) => {
 res.send(`Hello ${req.query.name || req.body.name || "World"}!`);
});' > index.js

Implementa la Cloud Function de 2a gen. con encriptación CMEK

Nota: En el siguiente ejemplo, se muestra cómo implementar una función con fuentes de tu directorio actual. Asegúrate de estar en el mismo directorio que el código fuente de la función.

FUNCTION_NAME=protect-me-cmek-2ndgen
ENTRY_POINT=helloWorld

REPO_FULLPATH=projects/"$PROJECT_ID"/locations/"$REGION"/repositories/$REPO_NAME

gcloud beta functions deploy $FUNCTION_NAME  \
--gen2 \
--region $REGION \
--kms-key $KEY_FULLPATH \
--docker-repository $REPO_FULLPATH \
--source . \
--trigger-http \
--allow-unauthenticated \
--runtime nodejs16 \
--entry-point $ENTRY_POINT

Para ver la clave CMEK en el resultado, ejecuta este comando

las funciones de gcloud describen $FUNCTION_NAME –region $REGION | grep kmsKeyName

Prueba la función de 2a gen.

Para probar la función, puedes usar curl:

FUNCTION_URL="$(gcloud functions describe $FUNCTION_NAME --region $REGION --format='get(serviceConfig.uri)')"

curl $FUNCTION_URL

lo que da como resultado lo siguiente:

Hello World!

Siempre que la clave de encriptación esté habilitada, la función mostrará el resultado correcto al emisor. Sin embargo, una vez que se inhabilite la clave de encriptación, el emisor recibirá un error.

En la siguiente sección, verás lo que sucede cuando invocas la función después de que se inhabilita la clave.

8. Crea una función encriptada con CMEK (1a gen.)

En esta sección, se explica cómo crear funciones de 1a gen. Si ya creaste una función de 2a gen., continúa con la siguiente sección.

Ahora que tienes un repositorio de Artifact Registry configurado con CMEK habilitadas y le otorgaste a Cloud Functions acceso a tu clave, ahora puedes implementar una función encriptada con tu clave CMEK.

Pasos para las funciones de 1a gen.:

Crea el código fuente para la función de 1a gen.

Aunque este codelab utiliza Node.js, puedes usar cualquier entorno de ejecución compatible.

Primero, crea un directorio y desplázate a él con el comando cd.

mkdir ~/cmek-function-1stgen && cd $_

A continuación, crea el archivo package.json.

touch package.json

echo '{
    "name": "function-cmek-codelab",
    "version": "0.0.1"
}' > package.json

Luego, crea el archivo de origen index.js.

touch index.js

echo "exports.helloWorld = (req, res) => {
    let message = req.query.message || req.body.message || 'Hello World!';
    res.status(200).send(message);
};" > index.js

Implementa la Cloud Function de 1a gen. con encriptación CMEK

Nota: En el siguiente ejemplo, se muestra cómo implementar una función con fuentes de tu directorio actual. Asegúrate de estar en el mismo directorio que el código fuente de la función.

FUNCTION_NAME=protect-me-cmek-1stgen
ENTRY_POINT=helloWorld

REPO_FULLPATH=projects/"$PROJECT_ID"/locations/"$REGION"/repositories/$REPO_NAME

gcloud functions deploy $FUNCTION_NAME  \
--region $REGION \
--kms-key $KEY_FULLPATH \
--docker-repository $REPO_FULLPATH \
--source . \
--trigger-http \
--allow-unauthenticated \
--runtime nodejs16 \
--entry-point $ENTRY_POINT

Para ver la clave CMEK en el resultado, ejecuta este comando

las funciones de gcloud describen $FUNCTION_NAME –region $REGION | grep kmsKeyName

Prueba la función de 1a gen.

Para probar la función, puedes usar curl:

FUNCTION_URL="$(gcloud functions describe $FUNCTION_NAME --region $REGION --format='get(httpsTrigger.url)')"

curl $FUNCTION_URL

lo que da como resultado lo siguiente:

Hello World!

Siempre que la clave de encriptación esté habilitada, la función mostrará el resultado correcto al emisor. Sin embargo, una vez que se inhabilite la clave de encriptación, el emisor recibirá un error.

En la siguiente sección, verás lo que sucede cuando invocas la función después de que se inhabilita la clave.

9. Invoca una función encriptada con CMEK en la que se haya inhabilitado la clave de encriptación

En esta sección final, invalidarás la clave y volverás a invocar la función para ver el error resultante.

Inhabilitar la clave de encriptación

Puedes ejecutar este comando para inhabilitar la clave. Dado que este codelab solo crea una versión de la clave, inhabilitarás la versión 1.

gcloud kms keys versions disable 1 \
    --key=$KEY_NAME \
    --keyring=$KEYRING_NAME \
    --location=$REGION

y deberías ver la información resultante:

algorithm: GOOGLE_SYMMETRIC_ENCRYPTION
createTime: '2023-04-11T03:30:49.111832653Z'
generateTime: '2023-04-11T03:30:49.111832653Z'
name: projects/dogfood-gcf-saraford/locations/us-central1/keyRings/myKeyRing/cryptoKeys/encrypted-function/cryptoKeyVersions/1
protectionLevel: SOFTWARE
state: DISABLED

Invoca la función con una clave inhabilitada

Ahora, vuelve a ejecutar la función curl.

curl $FUNCTION_URL

y no recibirás una respuesta Hello World esta vez.

En los registros de la Cloud Function, verás lo siguiente:

User's CMEK key has been disabled. CMEK key: projects/<PROJECT-NAME>/locations/us-central1/keyRings/myKeyRing/cryptoKeys/encrypted-function

Intentar ver los recursos cuando la clave CMEK está inhabilitada

En esta sección, verás que los siguientes recursos dejarán de estar disponibles cuando se inhabilite la clave CMEK:

  • Código fuente de la función
  • Compilación de imágenes de contenedor a partir del código fuente

Por ejemplo, si visitas la pestaña Fuente de la Cloud Function, se muestra un error cuando se recupera el archivo. Si intentas ver el archivo ZIP que contiene el código fuente directamente en Cloud Storage, recibirás un error similar al siguiente.

ac3307bb05d30e19.png

Además, no tendrás acceso para usar la imagen de contenedor para la función de Artifact Registry. Por ejemplo, si intentas implementar esa imagen de contenedor en Cloud Run, recibirás un error que indica que no se encontró la imagen.

Consulta la documentación de las funciones de CMEK para obtener una lista completa de los recursos encriptados.

10. Felicitaciones

¡Felicitaciones! Completaste el codelab.

Temas abordados

  • Cómo crear una clave CMEK en un llavero de claves simétrico existente
  • Cómo crear un repositorio de Artifact Registry
  • Cómo configurar CMEK en una Cloud Function

Más información

Puedes encontrar más información sobre Cloud Functions y CMEK en los siguientes vínculos: