使用客户管理的加密密钥 (CMEK) 加密 Cloud Functions 函数

1. 简介

概览

Cloud Functions 是一个轻量级计算解决方案,可供开发者创建单一用途的独立函数,无需管理服务器或运行时环境即可对云端事件作出响应。

您可以使用 Cloud Key Management Service 客户管理的加密密钥 (CMEK) 来保护 Cloud Functions 和相关的静态数据。使用 CMEK 部署函数时,系统会使用完全由您控制的加密密钥来保护与其关联的数据。此类加密可帮助您满足某些行业(例如金融服务)的合规性要求。由于密钥归您所有,不受 Google 控制,因此在停用或销毁这些加密密钥后,任何人都(包括您)无法访问受这些加密密钥保护的数据。

对于 Cloud Functions,CMEK 会加密以下内容:

  • 上传用于部署并由 Google 存储在 Cloud Storage 中的函数源代码,用于构建流程。
  • 函数构建流程的结果,包括根据函数源代码构建的容器映像,以及部署的函数的每个实例。
  • 内部事件传输渠道的静态数据(仅限第 1 代)。

如需详细了解哪些数据会加密,请参阅 Cloud Functions CMEK 文档

构建内容

此 Codelab 介绍了如何部署使用 CMEK 加密的 Cloud Functions 函数(第 1 代或第 2 代)。此 Codelab 使用公共 Cloud Functions 函数(即不需要身份验证的函数)进行演示。您可以调用经过身份验证且已启用 CMEK 的函数,就像调用任何其他需要身份验证的 Cloud Functions 函数一样。

学习内容

  • 如何在现有对称密钥环上创建 CMEK 密钥
  • 如何创建 Artifact Registry 代码库
  • 如何为第 1 代和第 2 代 Cloud Functions 函数配置 CMEK

2. 设置和要求

前提条件

  • 您已登录到 Cloud 控制台
  • 您之前部署了 HTTP 触发的 Cloud Functions 函数(以验证您是否启用了适当的角色和 API)

激活 Cloud Shell

  1. 在 Cloud Console 中,点击激活 Cloud Shell853e55310c205094

55efc1aaa7a4d3ad.png

如果这是您第一次启动 Cloud Shell,系统会显示一个中间屏幕,说明它是什么。如果您看到中间屏幕,请点击继续

9c92662c6a846a5c

预配和连接到 Cloud Shell 只需花几分钟时间。

9f0e51b578fecce5

这个虚拟机装有所需的所有开发工具。它提供了一个持久的 5 GB 主目录,并在 Google Cloud 中运行,大大增强了网络性能和身份验证功能。您在此 Codelab 中的大部分(即使不是全部)工作都可以通过浏览器完成。

在连接到 Cloud Shell 后,您应该会看到自己已通过身份验证,并且相关项目已设为您的项目 ID。

  1. 在 Cloud Shell 中运行以下命令以确认您已通过身份验证:
gcloud auth list

命令输出

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

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. 在 Cloud Shell 中运行以下命令,以确认 gcloud 命令了解您的项目:
gcloud config list project

命令输出

[core]
project = <PROJECT_ID>

如果不是上述结果,您可以使用以下命令进行设置:

gcloud config set project <PROJECT_ID>

命令输出

Updated property [core/project].

3. 为 Cloud Functions 创建新的密钥环和密钥

运行以下命令,确保已启用 Cloud KMS API:

gcloud services enable cloudkms.googleapis.com

首先,创建环境变量,以包含此 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)")"

接下来,创建一个密钥环,它是 Cloud KMS 密钥和密钥版本的根资源。

gcloud kms keyrings create $KEYRING_NAME --location $REGION

最后,您现在可以在 Cloud KMS 内的新密钥环中创建对称密钥

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

4. 创建支持 CMEK 的 Docker 格式的 Artifact Registry 代码库

在本部分中,您将在已启用 CMEK 的 Artifact Registry 中创建 Docker 格式的代码库。此密钥将与用于部署 Cloud Functions 函数的密钥相同。

首先,您需要 Artifact Registry 的服务账号。您可以通过运行以下命令来创建它:

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

使用以下命令向 Artifact Registry 服务账号授予 CryptoKey Encrypter/Decrypter IAM 角色 (roles/cloudkms.cryptoKeyEncrypterDecrypter),以获得对密钥的权限:

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

向将在 Artifact Registry 中创建代码库的原则授予相应角色,例如您当前有效的账号。您可以通过运行 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

现在,您可以创建启用了 CMEK 的 Docker 格式的代码库。

注意:该区域必须与 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

您可以运行以下命令来查看新的 Artifact Registry 代码库:

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

5. 向服务账号授予对密钥的访问权限(第 2 代)

本部分介绍如何为第 2 代函数创建服务账号。如果您要创建第 1 代函数,请继续学习下一部分。

您必须通过授予 CryptoKey Encrypter/Decrypter IAM 角色 (roles/cloudkms.cryptoKeyEncrypterDecrypter) 来向多个服务代理授予对密钥的访问权限。这些服务代理用于获取对存储在 Cloud Storage 中的源代码的访问权限,将函数映像存储在 Artifact Registry 中受 CMEK 保护的代码库中,以及部署由 CMEK 加密的 Cloud Functions 函数。

第 2 代函数的步骤

  1. 向 Cloud Run 服务代理授予对密钥的访问权限:
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. 向 Eventarc 服务代理授予对密钥的访问权限:
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. 向 Artifact Registry 服务代理授予对密钥的访问权限:
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. 向 Cloud Storage 服务代理授予对密钥的访问权限:
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

在下一部分中,您将了解如何创建和部署由 CMEK 加密的函数。

6. 向服务账号授予对密钥的访问权限(第 1 代)

本部分介绍如何为第 1 代函数创建服务账号。如果您之前为第 2 代函数创建了服务账号,请继续学习下一部分。

您必须通过授予 CryptoKey Encrypter/Decrypter IAM 角色 (roles/cloudkms.cryptoKeyEncrypterDecrypter) 来向多个服务代理授予对密钥的访问权限。这些服务代理用于获取对存储在 Cloud Storage 中的源代码的访问权限,将函数映像存储在 Artifact Registry 中受 CMEK 保护的代码库中,以及部署由 CMEK 加密的 Cloud Functions 函数。

第 1 代函数的步骤

  1. 向 Cloud Functions 服务代理授予对密钥的访问权限:
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. 向 Artifact Registry 服务代理授予对密钥的访问权限:
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. 向 Cloud Storage 服务代理授予对密钥的访问权限:
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

在下一部分中,您将了解如何创建和部署由 CMEK 加密的函数。

7. 创建 CMEK 加密的函数(第 2 代)

本部分介绍了如何创建第 2 代函数。您可以继续执行下一部分,了解第 1 代说明。

现在,您已拥有一个配置了 CMEK 的 Artifact Registry 代码库,并已向 Cloud Functions 授予对您密钥的访问权限,接下来您可以部署一个使用 CMEK 密钥加密的函数。

第 2 代函数的步骤:

为函数创建源代码

虽然此 Codelab 使用的是 Node.js,但您可以使用任何受支持的运行时

首先,创建一个目录并使用 cd 命令进入该目录。

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

然后,创建 package.json 文件。

touch package.json

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

接下来,创建 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

部署使用 CMEK 加密的第 2 代 Cloud Functions 函数

注意:以下示例展示了如何使用当前目录中的源代码部署函数。确保您与函数的源代码位于同一目录中。

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

您可以通过运行以下命令从生成的输出中查看 CMEK 密钥

gcloud functions describe $FUNCTION_NAME –region $REGION |grep kmsKeyName

测试第 2 代函数

您可以通过对函数执行 curl 命令来测试该函数:

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

curl $FUNCTION_URL

会产生以下结果:

Hello World!

只要启用了加密密钥,函数就会向调用方返回成功消息。不过,一旦停用加密密钥,调用方就会收到错误消息。

在下一部分中,您将了解在停用键后调用函数时会发生什么情况。

8. 创建 CMEK 加密的函数(第 1 代)

本部分介绍了如何创建第 1 代函数。如果您之前已经创建了第 2 代函数,请继续学习下一部分。

现在,您已拥有一个配置了 CMEK 的 Artifact Registry 代码库,并已向 Cloud Functions 授予对您密钥的访问权限,接下来您可以部署一个使用 CMEK 密钥加密的函数。

第 1 代函数的步骤:

为第 1 代函数创建源代码

虽然此 Codelab 使用的是 Node.js,但您可以使用任何受支持的运行时

首先,创建一个目录并使用 cd 命令进入该目录。

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

接下来,创建 package.json 文件。

touch package.json

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

然后,创建 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

部署使用 CMEK 加密的第 1 代 Cloud Functions 函数

注意:以下示例展示了如何使用当前目录中的源代码部署函数。确保您与函数的源代码位于同一目录中。

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

您可以通过运行以下命令从生成的输出中查看 CMEK 密钥

gcloud functions describe $FUNCTION_NAME –region $REGION |grep kmsKeyName

测试第 1 代函数

您可以通过对函数执行 curl 命令来测试该函数:

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

curl $FUNCTION_URL

会产生以下结果:

Hello World!

只要启用了加密密钥,函数就会向调用方返回成功消息。不过,一旦停用加密密钥,调用方就会收到错误消息。

在下一部分中,您将了解在停用键后调用函数时会发生什么情况。

9. 调用已停用加密密钥的 CMEK 加密函数

在最后一部分中,您将使键失效,然后再次调用函数以查看产生的错误。

停用加密密钥

您可以运行以下命令来停用密钥。由于此 Codelab 仅创建一个密钥版本,因此您将停用版本 1。

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

您应该会看到生成的信息:

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

使用已停用的键调用函数

现在,再次 curl 该函数。

curl $FUNCTION_URL

并且您这次不会收到 Hello World 响应。

在 Cloud Functions 函数的日志中,您会看到

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

尝试在 CMEK 密钥停用时查看资源

在此部分中,当 CMEK 密钥停用时,您会看到以下资源变得不可用:

  • 函数源代码
  • 从源代码构建容器映像

例如,访问 Cloud Functions 函数的“Source”标签页时在提取归档时出错。如果您尝试直接在 Cloud Storage 中查看包含源代码的 .zip 文件,也会收到类似的错误。

ac3307bb05d30e19.png

此外,您也无权将容器映像用于 Artifact Registry 中的函数。例如,如果您尝试将该容器映像部署到 Cloud Run,则会收到提示未找到该映像的错误。

如需查看完整的加密资源列表,请参阅 CMEK 函数文档。

10. 恭喜

恭喜,您已完成此 Codelab!

所学内容

  • 如何在现有对称密钥环上创建 CMEK 密钥
  • 如何创建 Artifact Registry 代码库
  • 如何为 Cloud Functions 函数配置 CMEK

了解详情

您可以通过以下链接详细了解 Cloud Functions 和 CMEK: