在应用层对 Secret 加密

本教程介绍如何使用您在 Cloud Key Management Service (Cloud KMS) 中管理的密钥在应用层加密 Kubernetes Secret。加密 Secret 的过程为敏感工作负载提供了额外一层安全保障。

本页面适用于想要加密 Secret 的安全专家。如需详细了解我们在 Google Cloud 内容中提及的常见角色和示例任务,请参阅常见的 GKE 用户角色和任务

在阅读本页面之前,请确保您熟悉以下概念:


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

操作演示


概览

默认情况下,Google Kubernetes Engine (GKE) 会对静态存储的客户内容进行加密,包括 Secret。GKE 为您处理和管理此默认加密,您无需进行任何其他操作。 应用层 Secret 加密为存储在集群状态数据库中的敏感数据提供了额外一层安全防护。启用后,您可以使用由 Cloud KMS 管理的密钥在应用层加密此数据。

在 GKE 1.35 版及更高版本中,加密的具体 Kubernetes 资源取决于托管集群状态的键值对存储区,如下所示:

  • Spanner:应用层 Secret 加密功能可加密所有 Kubernetes API 对象。
  • etcd:应用层 Secret 加密主要用于加密 Secret。

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

确保密钥与集群位于同一位置,以减少延迟并防止资源依赖于跨多个故障网域的服务。创建密钥后,您可以通过指定要使用的密钥,在新集群或现有集群上启用该功能。启用此功能后,GKE 会使用您的加密密钥加密集群状态数据库中的对象。加密的具体 API 类型取决于您的集群使用的键值对存储区,如上文列表中所述。

信封加密

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

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

  • 与公钥加密相比,性能有所提升:GKE 仅在以下情况下使用 Cloud KMS API:使用 KEK 加密新的 DEK 或在本地缓存为空时解密 DEK。
  • 更好地大规模管理密钥:单个 KEK 可加密多个 DEK。您需要在 Cloud KMS 服务中存储的密钥数量远少于用于加密数据的密钥数量。
  • 能够使用中心信任根:存储在 Kubernetes 中的 Secret 可依赖于外部信任根。这意味着,您可以对所有 Secret 使用中心信任根(例如硬件安全模块)。离线访问容器的攻击者无法获取您的 Secret。

借助 GKE 中的应用层 Secret 加密功能,GKE 会使用本地 DEK 和 AES-CBC 提供方加密您的 Secret。GKE 使用您在 Cloud KMS 中管理的 KEK 对 DEK 进行加密。

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

数据加密后会怎么样

当您创建符合加密条件的新 Kubernetes 资源时,会发生以下情况:

  1. GKE 使用随机数生成器生成 DEK。

  2. GKE 会使用此 DEK 在控制平面中对数据进行本地加密。

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

  4. Cloud KMS 使用 KEK 对 DEK 进行加密,并将其发送回 GKE 控制平面。

  5. GKE 控制平面将加密的数据与加密的 DEK 一起存储在集群的数据库中。明文 DEK 不会写入磁盘,但会缓存到内存中以提高性能。

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

  1. GKE 控制平面从集群状态数据库检索加密数据。

  2. GKE 控制平面会检查内存缓存中是否存在纯文本 DEK。如果缓存中有明文 DEK,GKE 会使用它来解密数据。

  3. 如果 DEK 不在缓存中,GKE 会将加密后的 DEK 发送给 Cloud KMS 以使用您的 KEK 进行解密。

  4. GKE 从 Cloud KMS 接收解密的 DEK,并使用该 DEK 对数据进行解密。

  5. Kubernetes API 服务器将解密的数据返回给客户端。

销毁密钥时发生的情况

如果您打算在密钥轮替后销毁旧的 KEK 版本,请确保集群中的数据受新的 KEK 版本保护。具体流程取决于集群的加密范围(验证加密范围)。

您可以视需要保留之前的 KEK 版本,以避免重新加密 Secret,但您需要继续为 Cloud KMS 中的所有有效 KEK 付费。如需了解价格详情,请参阅 Cloud KMS 价格

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

但以下情况除外:

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

  • 您销毁 KEK 后,GKE 控制平面仍然可以使用缓存的 DEK 映射条目来解密数据。这可让重启或重新调度的 Pod 访问数据,除非发生以下情况之一:

    • 集群控制层面重启。
    • GKE 控制平面 Pod 会重启。
    • DEK 不在 GKE 控制平面缓存中。

在销毁 KEK 之前,请检查集群是否正在使用 KEK。您还可以在 Cloud KMS 中为密钥销毁创建提醒政策。 只有在您确定集群中没有数据使用先前版本加密时才销毁先前的 KEK 版本。在销毁 KEK 之前,请检查该密钥是否正在使用。如需了解详情,请参阅销毁和恢复密钥版本

您可以安排在可配置的时间段后销毁密钥版本。在此期间,如果您改变主意,可以恢复密钥版本以防止删除。不过,在达到安排的销毁时间并销毁相应的密钥版本后,此操作无法撤消。使用此密钥版本加密的任何数据都将永久无法解密。

准备工作

  • 为了完成本主题中的练习,您需要两个 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 区域中的密钥环予以保护。

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

您可以使用 gcloud CLI 或 Google Cloud 控制台。

控制台

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

  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 控制台或 gcloud CLI。

控制台

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

  1. 在 Google Cloud 控制台中,前往密钥管理页面。

    前往密钥管理

  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 控制台中,前往创建 Autopilot 集群页面。

    前往“创建 Autopilot 集群”

  2. 根据需要配置集群。

  3. 在导航窗格中,点击高级设置,然后展开安全部分。

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

  5. 点击创建

控制台 - 标准

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

  1. 在 Google Cloud 控制台中,前往创建一个 Kubernetes 集群页面。

    前往“创建一个 Kubernetes 集群”页面

  2. 根据需要配置集群。

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

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

  5. 点击创建

gcloud

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

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

替换以下内容:

  • CLUSTER_NAME:您为新集群选择的名称。
  • CONTROL_PLANE_LOCATION:集群控制平面的 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 加密会重启集群控制平面。在此过程中,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 \
    --location=CONTROL_PLANE_LOCATION \
    --database-encryption-key=projects/KEY_PROJECT_ID/locations/LOCATION/keyRings/RING_NAME/cryptoKeys/KEY_NAME \
    --project=CLUSTER_PROJECT_ID

替换以下内容:

  • CLUSTER_NAME:您的集群的名称。
  • CONTROL_PLANE_LOCATION:集群控制平面的 Compute Engine 位置。为区域级集群提供区域,或为可用区级集群提供可用区。
  • KEY_PROJECT_ID:您的密钥项目 ID。
  • LOCATION:您在其中创建密钥环的 Cloud KMS 位置。
  • RING_NAME:密钥环的名称。
  • KEY_NAME:密钥的名称。
  • CLUSTER_PROJECT_ID:您的集群的项目 ID

更新 Cloud KMS 密钥

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

将现有集群更新为使用新的 Cloud KMS 密钥会重启集群控制平面。在此过程中,GKE 会使用新密钥重新加密所有现有 Secret,因此预计操作会长时间运行。对于可用区级集群,控制平面在更新期间会变得不可用。

控制台

如需更新集群以使用新的 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 \
    --location=CONTROL_PLANE_LOCATION \
    --database-encryption-key=projects/KEY_PROJECT_ID/locations/LOCATION/keyRings/RING_NAME/cryptoKeys/KEY_NAME \
    --project=CLUSTER_PROJECT_ID

替换以下内容:

  • CLUSTER_NAME:您的集群的名称。
  • CONTROL_PLANE_LOCATION:集群控制平面的 Compute Engine 位置。为区域级集群提供区域,或为可用区级集群提供可用区。
  • KEY_PROJECT_ID:您的密钥项目 ID。
  • LOCATION:您在其中创建密钥环的 Cloud KMS 位置。
  • RING_NAME:密钥环的名称。
  • KEY_NAME:密钥的名称。
  • CLUSTER_PROJECT_ID:您的集群的项目 ID

停用应用层 Secret 加密

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

控制台

  1. 前往 Google Cloud 控制台中的 Google Kubernetes Engine 页面。

    转到 Google Kubernetes Engine

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

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

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

  5. 点击保存更改

gcloud

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

gcloud container clusters update CLUSTER_NAME \
    --location=CONTROL_PLANE_LOCATION \
    --disable-database-encryption \
    --project=CLUSTER_PROJECT_ID

替换以下内容:

  • CLUSTER_NAME:您的集群的名称。
  • CONTROL_PLANE_LOCATION:集群控制平面的 Compute Engine 位置。为区域级集群提供区域,或为可用区级集群提供可用区。
  • CLUSTER_PROJECT_ID:您的集群的项目 ID

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

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

控制台

  1. 前往 Google Cloud 控制台中的 Google Kubernetes Engine 页面。

    转到 Google Kubernetes Engine

  2. 点击您要检查的集群的名称。

  3. 安全下,找到应用层 Secret 加密字段。此字段中的值用于指明哪些对象已加密,如下所示:

    • 已启用(所有对象):集群状态数据库中的所有 Kubernetes API 对象均已加密。
    • 已启用:仅加密 Secret。

gcloud

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

gcloud container clusters describe CLUSTER_NAME \
    --location=CONTROL_PLANE_LOCATION \
    --format='value(databaseEncryption.state)' \
    --project=CLUSTER_PROJECT_ID

替换以下内容:

  • CLUSTER_NAME:您的集群的名称。
  • CONTROL_PLANE_LOCATION:集群控制平面的 Compute Engine 位置。为区域级集群提供区域,或为可用区级集群提供可用区。
  • CLUSTER_PROJECT_ID:您的集群的项目 ID

输出会指明哪些对象已加密,如下所示:

  • ALL_OBJECTS_ENCRYPTION_ENABLED:集群状态数据库中的所有 Kubernetes API 对象均已加密。
  • ENCRYPTED:只有 Secret 会被加密。

如果输出为 DECRYPTED,则表示应用层 Secret 加密已停用。

轮替密钥

最佳实践

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

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

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

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

密钥版本轮替是一项具有最终一致性的操作,新的密钥版本可能需要一段时间的延迟才能生效。如需了解详情,请参阅密钥版本的一致性

重新加密 Secret

执行密钥轮替后,您应该重新加密 Secret,以使用新版本的 KEK 封装它们。您可以通过以下方式之一重新加密 Secret:

  • 通过在终端中运行 kubectl 命令来手动执行。
  • 通过使用周期性工作负载(例如 CronJob)定期运行 kubectl 命令来自动执行。

进行密钥轮替后,请等待至少三个小时,以使新版本变得一致。然后,运行如下命令来触发重新加密:

kubectl get secrets --namespace=NAMESPACE -o json \
    | kubectl annotate --overwrite -f - encryption-key-rotation-time="TIME"

替换以下内容:

  • NAMESPACE:要在其中更新 Secret 的命名空间。在 Standard 集群中,您可以选择改用 --all-namespaces 标志,通过同一命令更新集群中的每个 Secret。在 Autopilot 集群中,您只能更新自己拥有的命名空间。
  • TIME:一个字符串,用于指示轮替发生的时间(例如 20200909-090909)。

轮替密钥后,先前的密钥版本会继续存在,并且可能会产生费用。密钥版本销毁是永久性的,因此请先确保不再使用之前的密钥版本,然后再销毁它。如需了解详情,请参阅查看密钥使用情况

限制

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

    密钥不必与集群位于同一项目中。如需详细了解 Cloud KMS 支持的位置,请参阅Google Cloud 位置

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

问题排查

如需了解如何排查应用层 Secret 加密问题(包括解决 Secret 加密更新失败的相关问题),请参阅排查应用层 Secret 加密问题

Cloud KMS 密钥已停用

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

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

Cloud KMS 密钥版本已被销毁

如果集群状态包含以下消息:KEY_VERSION_URI is not enabled, current state is: DESTROYED,则表示用于应用层 Secret 加密的密钥版本已销毁

KEY_VERSION_URI 替换为密钥版本的 URI。

后续步骤