在应用层对密钥加密

本页介绍如何使用您在 Cloud Key Management Service (Cloud KMS) 中管理的密钥在应用层加密 Kubernetes Secret。此功能依赖于 Cloud KMS 的功能,因此您应该掌握密钥轮替信封加密

概览

默认情况下,GKE 会对以静态方式存储的客户内容进行加密,包括对 Secret 进行加密。GKE 为您处理和管理此默认加密,您无需进行任何其他操作。

“应用层 Secret 加密”为敏感数据(例如存储在 etcd 中的 Secret)提供额外一层安全保障。借助此功能,您可以使用由 Cloud KMS 管理的密钥来加密应用层的数据。这样做可以防止攻击者访问 etcd 的离线副本。

如需使用“应用层 Secret 加密”,您必须先创建 Cloud KMS 密钥并授予 GKE 服务帐号对密钥的访问权限。密钥必须位于集群所在的位置,以减少延迟并防止资源依赖于跨多个故障网域的服务。然后,您可以通过指定要使用的密钥,在新集群或现有集群上启用该功能。

信封加密

Kubernetes 使用 KMS 提供者对 Secret 进行信封加密,这表示系统将会使用本地密钥(通常称为“数据加密密钥 (DEK)”)来加密 Secret。DEK 本身使用另一个密钥(称为“密钥加密密钥” (KEK))进行加密。KEK 不由 Kubernetes 存储。

信封加密有两大好处:

  • KEK 可以轮替,而无需重新加密所有密文。这表示您可以更轻松地按照常规密钥轮替的最佳做法进行操作,而不会对性能产生重大影响。

  • 存储在 Kubernetes 中的 Secret 可依赖于外部信任根。这表示您可以针对自己的所有 Secret 使用中心信任根(例如硬件安全模块),而且访问离线容器的攻击者无法获取您的 Secret。

使用 GKE 中的“应用层 Secret 加密”功能时,您的 Secret 是使用 AES-CBC 提供者通过本地 DEK 在本地进行加密,而 DEK 则使用您在 Cloud KMS 中管理的 KEK 进行加密。

如需详细了解信封加密,请参阅信封加密

创建 Secret 时发生的情况

创建新的密文时,会出现以下情况:

  • Kubernetes API 服务器使用随机号码生成器为密文生成唯一的 DEK。

  • Kubernetes API 服务器在本地使用 DEK 来加密 Secret。

  • KMS 插件将 DEK 发送到 Cloud KMS 进行加密。KMS 插件使用您项目的 GKE 服务帐号对 Cloud KMS 进行身份验证。

  • Cloud KMS 对 DEK 进行加密,并将其发送回 KMS 插件。

  • Kubernetes API 服务器保存加密的密文和加密的 DEK。明文 DEK 不保存到磁盘。

当客户端从 Kubernetes API 服务器请求 Secret 时,逆向进行上述过程。

销毁密钥时发生的情况

当您销毁 Cloud KMS 中用于加密 GKE 中的 Secret 的密钥时,该 Secret 将不再可用。除非使用服务帐号令牌卷投影,否则 GKE 使用的服务帐号也会使用 Secret,如果密钥被销毁,这些 Secret 将无法使用。无法访问这些 Secret 意味着集群将无法启动。

在销毁密钥之前,建议您验证集群是否正在使用该密钥。您还可以在 Cloud KMS 中为密钥销毁创建提醒政策

准备工作

  • 为了完成本主题中的练习,您需要两个 Google Cloud 项目。

    • 密钥项目:您在此创建密钥加密密钥。

    • 集群项目:您在此创建支持“应用层 Secret 加密”的集群。

  • 在密钥项目中,确保您已启用 Cloud KMS API。

    启用 Cloud KMS API

  • 在密钥项目中,创建密钥环和密钥的用户需要拥有以下 IAM 权限:

    • cloudkms.keyRings.getIamPolicy
    • cloudkms.keyRings.setIamPolicy

    这些权限(和其他多项权限)会授予预定义的 roles/cloudkms.admin Identity and Access Management 角色。您可以参阅 Cloud KMS 文档,详细了解授予管理密钥的权限

  • 在集群项目中,确保您已启用 Google Kubernetes Engine API。

    启用 Google Kubernetes Engine API

  • 确保您已安装 Cloud SDK

  • gcloud 更新到最新版本:

    gcloud components update

创建 Cloud KMS 密钥

创建密钥环时,请指定与您的 GKE 集群位置匹配的位置:

  • 地区级集群应使用超集位置的密钥环。例如,地区 us-central1-a 中的集群只能使用区域 us-central1 中的密钥。

  • 区域级集群应使用同一位置的密钥环。例如,区域 asia-northeast1 中的集群应使用区域 asia-northeast1 中的密钥环获得保护。

  • 无法在 Cloud KMS global 区域使用 GKE。

您可以使用 Google Cloud Console 或 gcloud 命令。

控制台

在密钥项目中创建密钥环:

  1. 转到 Cloud Console 中的加密密钥页面。

    转到“加密密钥”页面

  2. 点击创建密钥环

  3. 密钥环名称字段中,输入密钥环的名称。

  4. 位置下拉列表中,选择您的 Kubernetes 集群的位置。创建密钥环页面应如下所示:

    Google Cloud 网页界面中的“创建密钥环”屏幕

  5. 点击创建

接下来,创建密钥:

  1. 转到 Cloud Console 中的加密密钥页面。

    转到“加密密钥”页面

  2. 点击您要为其创建密钥的密钥环的名称。

  3. 点击创建密钥

  4. 密钥名称字段中,输入密钥的名称。

  5. 对于轮替周期开始日期,接受默认值,或者如果您要使用其他值,请设置密钥轮替周期和开始时间

  6. [可选] 如果您要为密钥添加标签,请在标签字段中,点击添加标签

    创建密钥页面应如下所示:

    Google Cloud 网页界面中的“创建密钥”屏幕

  7. 点击创建

gcloud

在密钥项目中创建密钥环:

gcloud kms keyrings create ring-name \
    --location location \
    --project key-project-id

其中:

  • ring-name 是您为密钥环选择的名称。
  • location 是您要创建密钥环的区域。
  • key-project-id 是您的密钥项目 ID。

创建密钥:

gcloud kms keys create key-name \
    --location location \
    --keyring ring-name \
    --purpose encryption \
    --project key-project-id

其中:

  • key-name 是您为密钥选择的名称。
  • location 是您创建密钥环的区域。
  • ring-name 是您的密钥环名称。
  • key-project-id 是您的密钥项目 ID。

授予使用密钥的权限

您集群项目中的 GKE 服务帐号名称如下:

service-cluster-project-number@container-engine-robot.iam.gserviceaccount.com

其中,cluster-project-number 是您的集群项目编号

如需向服务帐号授予访问权限,您可以使用 Google Cloud Console 或 gcloud 命令。

控制台

为您的 GKE 服务帐号授予 Cloud KMS CryptoKey Encrypter/Decrypter 角色

  1. 在 Google Cloud Console 中打开 Cloud Key Management Service 密钥浏览器。
    打开 Cloud KMS 密钥浏览器
  2. 点击包含所需密钥的密钥环的名称。

  3. 选中所需密钥对应的复选框。

    右侧窗格中的权限标签变为可用。

  4. 添加成员对话框中,指定您授予访问权限的 GKE 服务帐号的电子邮件地址。

  5. 选择角色下拉列表中,选择 Cloud KMS CryptoKey Encrypter/Decrypter

  6. 点击保存

gcloud

为您的 GKE 服务帐号授予 Cloud KMS CryptoKey Encrypter/Decrypter 角色

gcloud kms keys add-iam-policy-binding key-name \
  --location location \
  --keyring ring-name \
  --member serviceAccount:service-account-name \
  --role roles/cloudkms.cryptoKeyEncrypterDecrypter \
  --project key-project-id

其中:

  • key-name 是您的密钥名称。
  • location 是您创建密钥环的区域。
  • ring-name 是您的密钥环名称。
  • service-account-name 是您的 GKE 服务帐号名称。
  • key-project-id 是您的密钥项目 ID。

启用“应用层 Secret 加密”

在新集群上

您可以使用 Google Cloud Console 或 gcloud 工具创建新集群。

控制台

  1. 访问 Cloud Console 中的 Google Kubernetes Engine 菜单。

    访问 Google Kubernetes Engine 菜单

  2. 点击创建集群按钮。

  3. 根据需要配置集群和节点池。

  4. 在导航窗格的集群下,点击安全

  5. 选择启用应用层 Secret 加密,并选择您在创建 Cloud KMS 密钥中创建的数据库加密密钥。

  6. 继续配置集群。

  7. 点击创建

gcloud

如需创建支持“应用层 Secret 加密”的集群,请在创建命令中指定 --database-encryption-key 参数的值。

gcloud container clusters create cluster-name \
  --cluster-version=latest \
  --zone zone \
  --database-encryption-key projects/key-project-id/locations/location/keyRings/ring-name/cryptoKeys/key-name \
  --project cluster-project-id

其中:

  • cluster-name 是您为集群选择的名称。
  • zone 是您要创建实例的地区。
  • key-project-id 是您的密钥项目 ID。
  • location 是您的密钥环的位置。
  • ring-name 是您的密钥环名称。
  • key-name 是您的密钥名称。
  • cluster-project-id 是您的集群项目 ID。

在现有集群上

只要符合以下任一陈述,您就可以更新现有集群以使用“应用层 Secret 加密”:

  • 集群版本高于或等于 v1.11.9 且低于 v1.12.0。
  • 集群版本高于或等于 v1.12.7。

您可以使用 Google Cloud Console 或 gcloud 命令。

控制台

如需更新集群以支持“应用层 Secret 加密”,请执行以下操作:

  1. 访问 Cloud Console 中的 Google Kubernetes Engine 菜单。

    访问 Google Kubernetes Engine 菜单

  2. 点击要修改的集群的“修改”图标。该图标为铅笔形状。

  3. 启用应用层 Secret 加密,并选择您在创建 Cloud KMS 密钥中创建的数据库加密密钥。

  4. 点击保存

gcloud

要在现有集群上启用“应用层 Secret 加密”,请运行以下命令:

gcloud container clusters update cluster-name \
  --zone zone \
  --database-encryption-key projects/key-project-id/locations/location/keyRings/ring-name/cryptoKeys/key-name \
  --project cluster-project-id

其中:

  • cluster-name 是您为集群选择的名称。
  • zone 是您要创建实例的地区。
  • key-project-id 是您的密钥项目 ID。
  • location 是您的密钥环的位置。
  • ring-name 是您的密钥环名称。
  • key-name 是您的密钥名称。
  • cluster-project-id 是您的集群项目 ID。

更新 Cloud KMS 密钥

控制台

如需更新集群以使用新的 Cloud KMS 密钥,请执行以下操作:

  1. 访问 Cloud Console 中的 Google Kubernetes Engine 菜单。

    访问 Google Kubernetes Engine 菜单

  2. 点击要修改的集群的“修改”图标。该图标为铅笔形状。

  3. 应用层 Secret 加密下,选择要使用的新加密密钥。

  4. 点击保存

gcloud

如需更新现有集群以使用新的 Cloud KMS 密钥,请运行以下命令:

gcloud container clusters update cluster-name \
  --zone zone \
  --database-encryption-key projects/key-project-id/locations/location/keyRings/ring-name/cryptoKeys/key-name \
  --project cluster-project-id

其中:

  • cluster-name 是您为集群选择的名称。
  • zone 是您要创建实例的地区。
  • key-project-id 是您的密钥项目 ID。
  • location 是您的密钥环的位置。
  • ring-name 是您的密钥环名称。
  • key-name 是您的密钥名称。
  • cluster-project-id 是您的集群项目 ID。

停用“应用层 Secret 加密”

您可以使用 Google Cloud Console 或 gcloud 命令。

控制台

  1. 访问 Cloud Console 中的 Google Kubernetes Engine 菜单。

    访问 Google Kubernetes Engine 菜单

  2. 点击要修改的集群的“修改”图标。该图标为铅笔形状。

  3. 停用应用层 Secret 加密

  4. 点击保存

gcloud

如需停用“应用层 Secret 加密”,请运行以下命令:

gcloud container clusters update cluster-name \
  --zone zone \
  --disable-database-encryption \
  --project cluster-project-id

其中:

  • cluster-name 是您为集群选择的名称。
  • zone 是您要创建实例的地区。
  • cluster-project-id 是您的集群项目 ID。

验证“应用层 Secret 加密”已启用

您可以使用 Google Cloud Console 或 gcloud 命令检查集群是否正在使用“应用层 Secret 加密”。

控制台

  1. 访问 Cloud Console 中的 Google Kubernetes Engine 菜单。

    访问 Google Kubernetes Engine 菜单

  2. 点击要修改的集群的名称。系统会打开集群的详细信息页面。

  3. 验证应用层 Secret 加密已启用,且列出了正确的密钥。

gcloud

检查集群是否正在使用“应用层 Secret 加密”:

gcloud container clusters describe cluster-name \
  --zone compute-zone \
  --format 'value(databaseEncryption)' \
  --project cluster-project-id

其中:

  • cluster-name 是现有集群的名称。
  • compute-zone 是集群地区的名称。
  • cluster-project-id 是您的集群项目 ID。

如果集群正在使用“应用层 Secret 加密”,则响应包含 EncryptionConfig

keyName=projects/project/locations/location/keyRings/ring-name/cryptoKeys/key-name;state=ENCRYPTED

限制

密钥位置

您从中选择密钥的位置必须是使用该密钥的集群所在的区域。例如,us-central1-a 中的地区级集群只能使用区域 us-central1 中的密钥。对于区域级集群,密钥必须位于同一位置,以减少延迟并防止资源依赖于跨多个故障网域的服务。

密钥轮替

当您执行密钥轮替时,现有密钥仍使用之前的 KEK 进行加密。如需确保使用较新的 KEK 封装一个 Secret,请在密钥轮替后创建一个新的 Secret。

例如,您创建并存储了一个 Secret (Secret1)。它使用 DEK1 加密,后者自身用 KEKv1 封装。在 KEKv1 轮替之前,您创建另一个 Secret (Secret2)。Secret2 会获得自己的密钥 DEK2KEKv1 将再次用于加密 DEK。

KEK 轮替之后,创建另一个 Secret (Secret3),它将使用 DEK3 进行加密。DEK3 又使用 KEKv2(轮替的 KEK)加密。

如需了解如何手动轮替加密 Secret 的 KEK,请参阅重新加密 Secret

重新加密 Secret

目前无法强制自动重新加密您的 Secret。您可以根据需要通过创建新的密钥版本手动轮替 KEK:

gcloud kms keys versions create --location location \
   --keyring ring-name \
   --key key-name \
   --primary \
   --project key-project-id

然后,通过轻触每个 Secret 来强制 GKE 重新加密:

kubectl get secrets --all-namespaces -o json | kubectl annotate --overwrite -f - encryption-key-rotation-time="time"

time 替换为指示轮替发生时间的字符串(例如 20200909-090909)。

EncryptionConfig

目前,GKE 中仅支持来自 Cloud KMS 的密钥。您不能使用其他 Kubernetes KMS 提供者或其他加密提供者

后续步骤