在应用层对 Secret 加密


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


如需在 Google Cloud 控制台中直接遵循有关此任务的分步指导,请点击操作演示

操作演示


概览

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

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

如需使用应用层 Secret 加密,您必须先创建 Cloud KMS 密钥并授予 GKE 服务账号对密钥的访问权限。您可以使用拥有 Cloud KMS 支持的任何保护级别的密钥。

确保密钥与集群位于同一位置,以减少延迟并防止资源依赖于跨多个故障网域的服务。创建密钥后,您可以通过指定要使用的密钥,在新集群或现有集群上启用该功能。启用此功能后,GKE 会使用您的加密密钥加密所有新密文和现有密文。

信封加密

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

信封加密具有以下几项优点:

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

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

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

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

创建 Secret 时发生的情况

创建新的 Secret 时,会发生以下情况:

  1. Kubernetes API 服务器使用随机号码生成器为 Secret 生成唯一的 DEK。

  2. Kubernetes API 服务器在本地使用 DEK 来加密密文。

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

  4. Cloud KMS 使用 KEK 对 DEK 进行加密,并将其发送回 KMS 插件。

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

  6. Kubernetes API 服务器会创建一个缓存条目,从而将加密的 DEK 映射到明文 DEK。这样,API 服务器无需使用 Cloud KMS 即可解密 Secret。

当客户端从 Kubernetes API 服务器请求 Secret 时,会经历以下步骤:

  1. Kubernetes API 服务器检索加密的 Secret 和加密的 DEK。

  2. Kubernetes API 服务器检查现有映射条目的缓存,并在不使用 Cloud KMS 的情况下解密 Secret。

  3. 如果未找到缓存条目,则 KMS 插件会将 DEK 发送给 Cloud KMS 以使用 KEK 进行解密。然后,使用解密的 DEK 对 Secret 进行解密。

  4. Kubernetes API 服务器将解密的 Secret 返回给客户端。

销毁密钥时发生的情况

当您销毁 Cloud KMS 中用于加密 GKE 中的 Secret 的 KEK 时,该 Secret 不再可用,除非您将集群更新为先使用新的 KEK。

如果您打算在密钥轮替后销毁旧的 KEK 版本,请先使用新的 KEK 版本重新加密 Secret。

除非使用服务账号令牌卷投影,否则工作负载在 GKE 上使用的服务账号也会使用 Secret,如果密钥被销毁,则这些 Secret 会变得不可用。无法访问这些 Secret 意味着工作负载将失败。

但以下情况除外:

  • 已对作为已装载卷或环境变量的 Secret 具有访问权限的 Pod 保留访问权限。

  • 您销毁 KEK 后,Kubernetes API 服务器仍然可以使用缓存的 DEK 映射条目来解密 Secret。这允许重启或重新安排的 pod 访问 Secret,除非:

    • 集群控制层面重启。
    • Kubernetes API 服务器 Pod 重启。
    • Secret 的 DEK 映射条目不在 Kubernetes API 服务器缓存中。

在销毁 KEK 之前,请检查集群是否正在使用 KEK。您还可以在 Cloud KMS 中为密钥销毁创建提醒政策

准备工作

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

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

    • 集群项目:您在此创建支持应用层 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

  • 确保您已安装 Google Cloud CLI

  • gcloud 更新到最新版本:

    gcloud components update

创建 Cloud KMS 密钥

如需创建 Cloud KMS 密钥,您必须先创建密钥环。密钥和密钥环是区域级资源。创建密钥环时,请指定与您的 GKE 集群位置匹配的位置:

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

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

您可以使用 gcloud CLI 或 Google Cloud Console。

控制台

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

  1. 转到 Google Cloud 控制台中的密钥管理页面。

    前往 Key Management

  2. 点击创建密钥环

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

  4. 位置下拉列表中,选择您的 Kubernetes 集群的位置。

  5. 点击创建

接下来,创建密钥:

  1. 转到 Google Cloud 控制台中的密钥管理页面。

    前往 Key Management

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

  3. 点击创建密钥

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

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

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

  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:您在其中创建密钥环的 Cloud KMS 位置。
  • RING_NAME:密钥环的名称。
  • KEY_PROJECT_ID:您的密钥项目 ID。

授予使用密钥的权限

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

service-CLUSTER_PROJECT_NUMBER@container-engine-robot.iam.gserviceaccount.com

CLUSTER_PROJECT_NUMBER 替换为您的集群的项目编号。如需使用 gcloud CLI 查找项目编号,请运行以下命令:

gcloud projects describe CLUSTER_PROJECT_ID \
    --format="value(projectNumber)"

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

控制台

为您的 GKE 服务账号授予 Cloud KMS 加密密钥加密者/解密者角色

  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 加密密钥加密者/解密者角色

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:您在其中创建密钥环的 Cloud KMS 位置。
  • RING_NAME:密钥环的名称。
  • SERVICE_ACCOUNT_NAME:GKE 服务账号的名称。
  • KEY_PROJECT_ID:您的密钥项目 ID。

如果是 Cloud HSM 密钥,确保该密钥具有足够的配额

如果使用 Cloud HSM 密钥,则包含该密钥的 Google Cloud 项目会受到密钥配额的限制。确保您有足够的配额将 Cloud HSM 密钥和应用层 Secret 加密结合使用。如果配额已用尽,您的节点可能会失去与集群控制平面的连接。

启用应用层 Secret 加密

您可以使用 gcloud CLI 或 Google Cloud 控制台在新的或现有 GKE Standard 和 GKE Autopilot 集群上启用应用层 Secret 加密。

启用应用层 Secret 加密后,建议您执行密钥轮替。您可以在 Cloud KMS 中配置自动密钥轮替。如需了解相关说明,请参阅配置自动轮替

在新集群上

您可以使用 Google Cloud 控制台或 gcloud CLI 创建启用了应用层 Secret 加密的新集群。

控制台 - Autopilot

如需创建启用了应用层 Secret 加密的 Autopilot 集群,请执行以下步骤:

  1. 转到 Google Cloud 控制台中的 Google Kubernetes Engine 页面。

    转到 Google Kubernetes Engine

  2. 点击 创建

  3. Autopilot 部分中,点击配置

  4. 根据需要配置集群。

  5. 在导航窗格中,点击高级选项,然后展开安全部分。

  6. 选中启用应用层 Secret 加密复选框,并选择您在创建 Cloud KMS 密钥中创建的密钥。

  7. 点击创建

控制台 - 标准

如需创建启用了应用层 Secret 加密的 Standard 集群,请执行以下步骤:

  1. 转到 Google Cloud 控制台中的 Google Kubernetes Engine 页面。

    转到 Google Kubernetes Engine

  2. 点击 创建

  3. 标准部分中,点击配置

  4. 根据需要配置集群。

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

  6. 选中启用应用层 Secret 加密复选框,并选择您在创建 Cloud KMS 密钥中创建的密钥。

  7. 点击创建

gcloud

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

gcloud container clusters create-auto CLUSTER_NAME \
    --cluster-version=latest \
    --region=COMPUTE_REGION \
    --database-encryption-key=projects/KEY_PROJECT_ID/locations/LOCATION/keyRings/RING_NAME/cryptoKeys/KEY_NAME \
    --project=CLUSTER_PROJECT_ID

替换以下内容:

  • CLUSTER_NAME:您为新集群选择的名称。
  • COMPUTE_REGION:要在其中创建集群的 Compute Engine 区域
  • KEY_PROJECT_ID:您的密钥项目 ID。
  • LOCATION:您在其中创建密钥环的 Cloud KMS 位置。
  • RING_NAME:密钥环的名称。
  • KEY_NAME:密钥的名称。
  • CLUSTER_PROJECT_ID:您的集群的项目 ID

您可以使用带有相同标志的 gcloud container clusters create 命令在新的标准集群上启用应用层 Secret 加密。

在现有集群上

您可以使用 gcloud CLI 或 Google Cloud 控制台更新现有集群以使用应用层 Secret 加密。GKE 使用您指定的加密密钥加密所有现有和新的 Secret。

控制台

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

  1. 转到 Google Cloud 控制台中的 Google Kubernetes Engine 页面。

    转到 Google Kubernetes Engine

  2. 点击要修改的集群的名称。

  3. 安全下的应用层 Secret 加密字段中,点击 修改应用层 Secret 加密

  4. 选中启用应用层 Secret 加密复选框,并选择您在创建 Cloud KMS 密钥中创建的密钥。

  5. 点击保存更改

gcloud

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

gcloud container clusters update CLUSTER_NAME \
    --region=COMPUTE_REGION \
    --database-encryption-key=projects/KEY_PROJECT_ID/locations/LOCATION/keyRings/RING_NAME/cryptoKeys/KEY_NAME \
    --project=CLUSTER_PROJECT_ID

替换以下内容:

  • CLUSTER_NAME:您的集群的名称。
  • COMPUTE_REGION:集群的 Compute Engine 区域
  • KEY_PROJECT_ID:您的密钥项目 ID。
  • LOCATION:您在其中创建密钥环的 Cloud KMS 位置。
  • RING_NAME:密钥环的名称。
  • KEY_NAME:密钥的名称。
  • CLUSTER_PROJECT_ID:您的集群的项目 ID

更新 Cloud KMS 密钥

您可以使用 gcloud CLI 或 Google Cloud Console 更新现有集群,以使用新的 Cloud KMS 密钥。

控制台

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

  1. 转到 Google Cloud 控制台中的 Google Kubernetes Engine 页面。

    转到 Google Kubernetes Engine

  2. 点击要修改的集群的名称。

  3. 安全下的应用层 Secret 加密字段中,点击 修改应用层 Secret 加密

  4. 选择要使用的新加密密钥。

  5. 点击保存更改

gcloud

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

gcloud container clusters update CLUSTER_NAME \
    --region=COMPUTE_REGION \
    --database-encryption-key=projects/KEY_PROJECT_ID/locations/LOCATION/keyRings/RING_NAME/cryptoKeys/KEY_NAME \
    --project=CLUSTER_PROJECT_ID

替换以下内容:

  • CLUSTER_NAME:您的集群的名称。
  • COMPUTE_REGION:集群的 Compute Engine 区域
  • KEY_PROJECT_ID:您的密钥项目 ID。
  • LOCATION:您在其中创建密钥环的 Cloud KMS 位置。
  • RING_NAME:密钥环的名称。
  • KEY_NAME:密钥的名称。
  • CLUSTER_PROJECT_ID:您的集群的项目 ID

停用应用层 Secret 加密

如需停用应用层 Secret 加密,您可以使用 gcloud CLI 或 Google Cloud Console。

控制台

  1. 转到 Google Cloud 控制台中的 Google Kubernetes Engine 页面。

    转到 Google Kubernetes Engine

  2. 点击要修改的集群的名称。

  3. 安全下的应用层 Secret 加密字段中,点击 修改应用层 Secret 加密

  4. 取消选中启用应用层 Secret 加密复选框。

  5. 点击保存更改

gcloud

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

gcloud container clusters update CLUSTER_NAME \
    --region=COMPUTE_REGION \
    --disable-database-encryption \
    --project=CLUSTER_PROJECT_ID

替换以下内容:

验证应用层 Secret 加密是否已启用

您可以使用 Google Cloud 控制台或 gcloud CLI 检查集群是否正在使用“应用层 Secret 加密”。

控制台

  1. 转到 Google Cloud 控制台中的 Google Kubernetes Engine 页面。

    转到 Google Kubernetes Engine

  2. 点击要修改的集群的名称。

  3. 安全下,验证应用层 Secret 加密字段是否显示 Enabled 并列出正确的密钥。

gcloud

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

gcloud container clusters describe CLUSTER_NAME \
    --region=COMPUTE_REGION \
    --format='value(databaseEncryption)' \
    --project=CLUSTER_PROJECT_ID

替换以下内容:

如果集群使用应用层 Secret 加密,则输出类似于以下内容:

keyName=projects/KEY_PROJECT_ID/locations/LOCATION/keyRings/RING_NAME/cryptoKeys/KEY_NAME;state=ENCRYPTED

轮替密钥

我们建议您定期轮替密钥,包括在启用应用层 Secret 加密后。如需了解如何配置自动密钥轮替或手动轮替密钥,请参阅轮替密钥

执行密钥轮替时,现有 Secret 仍使用先前的密钥加密密钥 (KEK) 版本进行加密。如需确保使用较新的 KEK 版本封装 Secret,请在密钥轮替后重新加密 Secret

例如,您创建并存储了一个 Secret (Secret1)。它使用 DEK1 加密,后者自身用 KEKv1 封装。

KEK 轮替之后,您需要重新加密 Secret1,使其由 DEK2 进行封装,而后者又使用 KEKv2(轮替的 KEK)进行封装。

重新加密 Secret

执行密钥轮替后,您应该重新加密 Secret,以使用新版本的 KEK 封装它们。虽然您无法使用 gcloud CLI 配置自动重新加密,但可以使用 CronJob 定期运行重新加密命令。

要在密钥轮替后手动重新加密 Secret,请至少等待三个小时,以使新版本变得一致。然后,使用如下命令触及每个 Secret 以强制重新加密:

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

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

限制

  • 对于应用层 Secret 加密,GKE 最多支持每个集群有 30000 个 Secret。如果存储的 Secret 数超过 30000 个,则集群在升级时可能会不稳定,导致工作负载可能发生服务中断。
  • 确保每个命名空间中 Secret 的元数据的平均大小低于 5KiB。如果元数据的平均大小高于 5KiB,则集群可能会进入错误状态,其中某些 Secret 在启用该功能或停用该功能后会加密,而其他 Secret 会解密。
  • 您必须选择与集群位于同一区域的密钥。例如,us-central1-a 中的可用区级集群只能使用区域 us-central1 中的密钥。对于区域级集群,密钥必须位于同一位置,以减少延迟并防止资源依赖于跨多个故障域的服务。

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

问题排查

Cloud KMS 密钥已停用。

GKE 的默认服务账号不能将已停用的 Cloud KMS 密钥用于应用层 Secret 加密

如需重新启用已停用的密钥,请参阅启用已停用的密钥版本

后续步骤