访问具有私有 CA 证书的私有注册表


本页面介绍如何使用颁发注册表证书的证书授权机构 (CA) 的公钥允许 Google Kubernetes Engine (GKE) 中运行的工作负载访问私有映像注册表。

本页面适用于管理组织工作负载的访问权限的安全专家。如需详细了解我们在 Trusted Cloud by S3NS 内容中提及的常见角色和示例任务,请参阅常见的 GKE 用户角色和任务

在阅读本页面之前,请确保您熟悉 Secret Manager

访问私有注册表的运作方式

您可以在 Secret Manager 中存储用于为您的私有注册表颁发证书的 CA 的公钥,并配置哪些注册表完全限定域名 (FQDN) 使用该公钥进行证书验证。GKE 会在节点引导期间自动提取密钥并更新容器运行时注册表配置。当您部署的工作负载使用私有注册表中的容器映像时,需要执行以下步骤:

  1. 节点上的 kubelet 尝试从私有注册表中拉取映像。
  2. 注册表提供服务器端 TLS 证书。
  3. 容器运行时会以加密方式验证注册表证书,并确保 FQDN 与您指定的内容匹配。
  4. 如果验证通过,GKE 将拉取映像并安排工作负载。

优势

这种访问私有注册表的方法具有以下优势:

  1. 提高容器运行时配置的可靠性:使用 DaemonSet 等方法设置 containerd 配置会增加发生竞态条件的风险,在这种情况下,其他 DaemonSet 可能会在配置 DaemonSet 之前运行。
  2. 减少提权攻击的漏洞:无需运行修改容器运行时配置的特权 DaemonSet。
  3. 降低管理开销:Secret Manager 允许您将 CA 公钥存储在一个集中的位置;使用 IAM 管理对密钥的访问权限;并实施版本控制和注释。如需了解详情,请参阅 Secret Manager 产品概览
  4. 提高可审核性:Cloud Logging 已收集日志,包括在将证书添加到集群以及 GKE 节点拉取映像时收集日志。

价格

在本文档中,您将使用 Trusted Cloud的以下收费组件:

如需根据您的预计使用量来估算费用,请使用价格计算器

准备工作

在开始之前,请确保您已执行以下任务:

  • 启用 Google Kubernetes Engine API。
  • 启用 Google Kubernetes Engine API
  • 如果您要使用 Google Cloud CLI 执行此任务,请安装初始化 gcloud CLI。 如果您之前安装了 gcloud CLI,请运行 gcloud components update 以获取最新版本。
  • Enable the Secret Manager API.

    Enable the API

  • 您必须已拥有私有注册表和私有 CA 证书才能访问注册表。本指南不介绍如何设置私有注册表或创建证书。

要求

如需使用私有 CA 公钥访问私有注册表,您必须满足以下要求:

  • 您的集群必须使用 GKE 1.27.3-gke.1700 或更高版本。
  • 您必须使用带有 containerd 的 Container-Optimized OS 节点映像(该映像是所有 GKE 集群的默认设置),或者使用 1.33.0 版或更高版本的带有 containerd 的 Ubuntu 节点映像。不支持 Windows Server 节点映像。
  • 您的节点池必须具有 cloud-platform 访问权限范围,节点才能下载证书。如需了解详情,请参阅 GKE 中的访问权限范围。本文档介绍了如何在创建集群或节点池时设置访问权限范围。

限制

请考虑以下限制:

  • 您无法在低于 1.33.0 版的 Ubuntu 节点映像中使用私有 CA 证书。
  • 您无法在 Windows Server 节点中使用私有 CA 证书。
  • 每个集群最多支持五个用于私有注册表的私有 CA 证书。
  • 每个证书最多可以有 25 个完全限定域名 (FQDN)。
  • 每个网域只能在一个证书文件中使用。但是,支持证书软件包。
  • 证书必须采用 PEM 编码
  • 服务器不会自动轮替证书。如需了解详情,请参阅本文档中的轮替私有 CA 证书
  • FQDN 具有以下限制:
    • FQDN 长度上限为 255 个字符(包括特殊字符)。
    • FQDN 只能使用字母、数字和短划线 (-)。
    • 不支持 Punycode
    • 不支持通配符。

从配置 DaemonSet 迁移

在 GKE Standard 集群中,您可以部署特权 DaemonSet 来修改容器运行时配置。此方法直接修改每个节点上的 containerd 配置。

如果您使用特权 DaemonSet 来配置对私有注册表的访问权限,请在使用本文档之前考虑以下事项:

  • 在 Secret Manager 中存储私钥 CA 公钥时,只会配置对私有注册表的访问权限。不支持其他与注册表相关的配置。
  • 启用此功能会导致集群使用 containerd 的 CRI 主机路径配置模型,该模型与之前的配置模型不兼容。如果您的任何 DaemonSet 会修改 containerd 主机配置(例如对于不安全的私有注册表、镜像或代理),请更新 DaemonSet 以使用 CRI 主机路径模型。

    如需了解 CRI 主机路径模型中的可用字段,请参阅 containerd GitHub 代码库中的注册表配置

启用此功能后,GKE 会将 CRI 主机路径配置模型应用于集群中的新节点。现有节点将继续使用之前的配置模型,直到在升级等事件期间重新创建为止。

更新 DaemonSet 以支持两种配置模型

为了降低配置 DaemonSet 无法在支持特定配置模型的节点上运行的风险,请确保您的 DaemonSet 根据节点上的 containerd 配置文件有条件地使用特定的配置模型。如需查看实现此条件逻辑的 DaemonSet 示例,请参阅 GoogleCloudPlatform/k8s-node-tools GitHub 代码库中的 insecure-registry-config.yaml 清单

将 CA 公钥存储在 Secret Manager 中

将颁发私有注册表证书的私有 CA 的公钥作为 Secret 存储在 Secret Manager 中。如需了解相关说明,请参阅 Secret Manager 文档中的创建 Secret

配置从 GKE 对 Secret Manager 的访问权限

如需确保集群的 IAM 服务账号具有从 Secret Manager 拉取 Secret 所需的权限,请让您的管理员为集群的 IAM 服务账号授予针对 Secret 的以下 IAM 角色:

如需详细了解如何授予角色,请参阅管理对项目、文件夹和组织的访问权限

这些预定义角色包含从 Secret Manager 拉取 Secret 所需的权限。如需查看所需的确切权限,请展开所需权限部分:

所需权限

如需从 Secret Manager 中拉取 Secret,您需要拥有以下权限:

  • resourcemanager.projects.get
  • resourcemanager.projects.list
  • secretmanager.secrets.get
  • secretmanager.secrets.list
  • secretmanager.versions.get
  • secretmanager.versions.list
  • secretmanager.versions.access

您的管理员也可以使用自定义角色或其他预定义角色为集群的 IAM 服务账号授予这些权限。

如果您没有将自定义 IAM 服务账号与集群或节点池相关联(推荐的方法),则集群会使用 Compute Engine 默认服务账号

可能的话,我们建议您使用具有最低权限的 IAM 服务账号配置集群和节点池。如需查看相关说明,请参阅使用最小权限服务账号

创建运行时配置文件

如需在 GKE 中为私有注册表使用私有 CA 证书,请创建一个 YAML 文件来修改 containerd 配置。

  1. 获取您的 Trusted Cloud 项目编号:

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

    输出是您的数字项目编号。

  2. 将以下配置保存为 containerd-configuration.yaml

    privateRegistryAccessConfig:
      certificateAuthorityDomainConfig:
      - gcpSecretManagerCertificateConfig:
          secretURI: "projects/PROJECT_NUMBER/secrets/SECRET_NAME/versions/SECRET_VERSION"
        fqdns:
          - "FQDN1"
          - "FQDN2"
      enabled: true
    

    替换以下内容:

    • PROJECT_NUMBER:您在上一步获得的项目编号。
    • SECRET_VERSION:Secret Manager 中 Secret 的版本号。您可以选择使用版本别名,但我们建议您使用版本号,以避免因管理造成的复杂性。
    • FQDN1FQDN2:您的私有注册表的完全限定域名。如果为该地址颁发了证书,您也可以使用 IPv4 地址,但我们不建议这样做。

如需了解这些字段的说明,请参阅“可用的 containerd 配置选项”表中的 privateRegistryAccessConfig

将 containerd 配置应用于新集群

本部分介绍如何在创建新的 GKE 集群时应用 containerd 配置文件。

运行以下命令:

gcloud container clusters create-auto CLUSTER_NAME \
    --location=LOCATION \
    --scopes="cloud-platform" \
    --containerd-config-from-file="PATH_TO_CONFIG_FILE"

替换以下内容:

  • CLUSTER_NAME:新集群的名称。
  • LOCATION:新集群的 Compute Engine 位置。
  • PATH_TO_CONFIG_FILE:您创建的配置文件的路径,例如 ~/containerd-configuration.yaml

您可以运行带有相同选项的 gcloud container clusters create 命令,在新的 Standard 集群上启用私有注册表配置。

将 containerd 配置应用于现有集群

本部分介绍如何将 containerd 配置应用于现有集群和节点。

检查访问权限范围

现有集群必须具有 cloud-platform 访问权限范围才能使用此功能。本部分介绍如何检查访问权限范围,并使用新的或修改后的私有注册表配置文件更新现有集群。

如需详细了解新集群中的默认访问权限范围,请参阅 GKE 中的访问权限范围

检查 Autopilot 访问权限范围

运行以下命令:

gcloud container clusters describe CLUSTER_NAME \
    --location=LOCATION \
    --flatten=nodeConfig \
    --format='csv[delimiter="\\n",no-heading](oauthScopes)'

如果您的集群没有 https://www.googleapis.com/auth/cloud-platform 访问权限范围,请创建一个具有此访问权限范围的新集群。

检查 Standard 访问权限范围

如需检查 Standard 集群访问权限范围,请检查节点池:

gcloud container node-pools describe NODE_POOL_NAME \
    --cluster=CLUSTER_NAME \
    --location=LOCATION \
    --flatten=nodeConfig \
    --format='csv[delimiter="\\n",no-heading](oauthScopes)'

NODE_POOL_NAME 替换为节点池的名称。

如果您的集群没有 https://www.googleapis.com/auth/cloud-platform 访问权限范围,请创建一个具有 cloud-platform 访问权限范围的新节点池,并删除现有节点池。

更新集群以使用配置文件

运行以下命令:

gcloud container clusters update CLUSTER_NAME \
    --location=LOCATION \
    --containerd-config-from-file="PATH_TO_CONFIG_FILE"

在 Standard 集群中重新创建节点

如果您的 Standard 集群不使用自动升级,则您必须手动重新创建节点池以应用新配置。如需触发手动重新创建节点的操作,请将集群升级到其已在使用的 GKE 版本。

gcloud container clusters upgrade CLUSTER_NAME \
    --location=LOCATION \
    --cluster-version=VERSION

VERSION 替换为集群已在使用的同一 GKE 补丁版本。

验证您的集群是否可以访问私有注册表

运行以下命令:

gcloud container clusters describe CLUSTER_NAME \
    --location=LOCATION \
    --flatten="nodePoolDefaults.nodeConfigDefaults.containerdConfig"

输出类似于以下内容:

    containerdConfig:
      privateRegistryAccessConfig:
        certificateAuthorityDomainConfig:
        - fqdns:
          - 203.0.113.105
          gcpSecretManagerCertificateConfig:
            secretUri: projects/123456789012/secrets/example-secret-name/versions/1
        enabled: true

部署访问私有映像的工作负载

在本部分中,您将部署一个静态 Pod 来引用私有注册表中的映像。

  1. 将以下清单保存为 private-registry-pod.yaml

    apiVersion: v1
    kind: Pod
    metadata:
      name: private-registry-pod
    spec:
      containers:
      - name: private-image
        image: IMAGE_NAME
    

    IMAGE_NAME 替换为您的私有映像名称。

  2. 部署 Pod:

    kubectl create -f private-registry-pod.yaml
    

轮替私有 CA 证书

Secret Manager 和 GKE 无法在 Secret Manager 中自动轮替私有 CA 证书。如需执行证书轮替,请执行以下步骤。这些步骤要求您重新创建现有节点两次。我们建议您在计划停机期间执行证书轮替,以最大限度地减少工作负载中断的影响。

  1. 创建一个同时包含新旧证书的 PEM 编码证书软件包。
  2. 在 Secret Manager 中将软件包添加为新的 Secret 版本
  3. 使用新的 Secret 版本号更新运行时配置文件 secretURI 字段。
  4. 更新集群以使用新的 Secret 版本
  5. 获取更新操作的时间戳:

    gcloud container operations list \
        --filter="operationType ~ UPDATE_CLUSTER AND targetLink ~ CLUSTER_NAME" \
        --sort-by=startTime \
        --limit=1 \
        --format='value(endTime)'
    

    输出类似于以下内容:

    2024-01-31T09:27:30.864308964Z
    
  6. 查找在更新操作结束之前创建的节点:

    kubectl get nodes -o json | jq ".items[] |
    select(.metadata.creationTimestamp | fromdateiso8601 < $(date -d
    CLUSTER_UPDATE_TIMESTAMP +%s)) | .metadata.name"
    

    CLUSTER_UPDATE_TIMESTAMP 替换为上一步中的时间戳。

    输出是尚未使用更新后的配置重新创建的节点名称的列表。当输出为空白时,请继续执行下一步。

  7. 仅使用新证书在 Secret Manager 中创建 Secret 的新版本。

  8. 重复上述步骤以更新集群,获取操作时间戳,并验证节点是否使用新的 Secret 版本。

  9. 从 Secret Manager 中删除旧的 Secret 版本。

在 Logging 中查看审核日志

本部分介绍如何使用 Logging 检查 GKE 是否在节点上安装了您的 Secret 版本。

  1. 前往 Trusted Cloud 控制台中的 Logs Explorer 页面:

    转到 Logs Explorer

  2. 指定以下查询:

    resource.type="gce_instance"
    textPayload:"Installed certificate \\\"projects/PROJECT_NUMBER/secrets/SECRET_NAME/versions/SECRET_VERSION\\\""
    

    如果您的证书安装成功,则输出类似于以下内容:

    "Installed certificate "projects/PROJECT_NUMBER/secrets/SECRET_NAME/versions/SECRET_VERSION""
    

    如果证书安装失败,则输出类似于以下内容:

    "Failed to install certificate "projects/PROJECT_NUMBER/secrets/SECRET_NAME/versions/SECRET_VERSION""
    

最佳做法

我们建议您在使用此功能时遵循以下最佳实践:

  • 请勿为 Secret Manager Secret 版本使用别名。使用为每个 Secret 版本自动生成的版本号。随着时间的推移,别名可能会指向不同的证书版本,这可能会导致在跟踪工作负载使用的特定版本时让情况变得复杂。
  • 使用维护窗口和排除项控制 GKE 何时可以重新创建节点以应用更新后的 containerd 配置。
  • 在 Secret 级层(而不是项目级层)提供对 Secret 的访问权限。

停用 containerd 配置选项

如需移除自定义配置,请执行以下操作:

  1. 更新配置文件,在要停用的配置项中指定 enabled: false,并删除该项中的任何其他字段,如以下示例所示:

    privateRegistryAccessConfig:
      enabled: false
  2. 将更新后的配置文件应用到集群。如需了解相关说明,请参阅将 containerd 配置应用于现有集群

问题排查

如需了解问题排查步骤,请参阅排查容器运行时问题