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


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

运作方式

您可以在 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 节点拉取映像时收集日志。

价格

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

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

准备工作

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

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

    启用 API

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

使用要求

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

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

限制

请考虑以下限制:

  • 您无法在 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. 获取 Google Cloud 项目编号:

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

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

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

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

    替换以下内容:

    • 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. 进入 Google 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 配置应用于现有集群

问题排查

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