使用 Workload Identity

本页面介绍了 Google Kubernetes Engine (GKE) 应用使用 Google API 所提供服务的推荐方式。

概览

由于 Workload Identity 具有增强的安全属性和可管理性,因此它是从 GKE 内运行的应用访问 Google Cloud 服务的推荐方法。如需了解从 GKE 访问 Google Cloud API 的其他方法,请参阅下面的替代方案部分。

术语

本文档介绍了 Kubernetes 服务帐号Google 服务帐号的区别。Kubernetes 服务帐号属于 Kubernetes 资源,而 Google 服务帐号专用于 Google Cloud。其他 Google Cloud 文档将 Google 服务帐号称为“服务帐号”。

概念

在 GKE 上运行的工作负载必须经过身份验证才能使用 Google Cloud API,例如 Compute API、Storage and Database API 或 Machine Learning API。

借助 Workload Identity,您可以将 Kubernetes 服务帐号配置为充当 Google 服务帐号。任何以 Kubernetes 服务帐号运行的应用在访问 Google Cloud API 时,都会自动以 Google 服务帐号身份进行身份验证。这让您可以为集群中的应用分配精细的身份和授权。

为实现 Kubernetes 服务帐号与 Google 服务帐号之间的安全映射,Workload Identity 引入了集群的工作负载身份池概念,此概念可让 Identity and Access Management (IAM) 能够信任和了解 Kubernetes 服务帐号凭据。

为 GKE 集群启用 Workload Identity 时,请将集群的工作负载身份池设置为 my-pool.svc.id.goog。这让您可以以以下成员名称身份对 Kubernetes 服务帐号进行身份验证:

serviceAccount:my-pool.svc.id.goog[k8s-namespace/ksa-name]

在此成员名称中:

  • my-pool.svc.id.goog 是在集群上设置的工作负载身份池。
  • ksa-name 是发出请求的 Kubernetes 服务帐号的名称。
  • k8s-namespace 是在其中定义了 Kubernetes 服务帐号的 Kubernetes 命名空间。

每个 Google Cloud 项目都只有一个固定的工作负载身份池 project-id.svc.id.goog,它是由系统自动为您创建的。

跨集群共享身份

所有共用名称、命名空间名称和工作负载身份池的 Kubernetes 服务帐号都会解析为同一成员名称,因此共享对 Google Cloud 资源的访问权限。如果多个集群包含相同的身份,这种做法可能很有用,但如果不能小心谨慎地管理 Kubernetes 服务帐号名称和命名空间,则可能有潜在风险。

例如,如果在集群上启用了 Workload Identity,则以下命令会向项目的任何集群中所有使用默认服务帐号和命名空间的 Kubernetes 工作负载授予相同的访问权限:

gcloud iam service-accounts add-iam-policy-binding \
  --role roles/iam.workloadIdentityUser \
  --member "serviceAccount:project-id.svc.id.goog[default/default]" \
  gsa-name@project-id.iam.gserviceaccount.com

限制

  • 目前,每个 Google Cloud 项目都只有一个固定的工作负载身份池 project-id.svc.id.goog,它是由系统自动为您创建的。

  • 目前,如果工作负载正在 GKE On-Prem 集群中运行,则不支持 Workload Identity。

  • Workload Identity 取代了使用元数据隐藏的需要,因此这两种方法不兼容。由元数据隐藏保护的敏感源数据也受 Workload Identity 的保护。

  • 在节点池上启用 GKE 元数据服务器后,Pod 将无法再访问 Compute Engine 元数据服务器。相反,这些 Pod 向 Metadata API 发出的请求将路由到 GKE 元数据服务器。唯一的例外情况是在主机网络上运行的 Pod(请参阅下一项)。

  • 主机网络上运行的 Pod 不能使用 Workload Identity。这些 Pod 向 Metadata API 发出的请求将路由到 Compute Engine 元数据服务器。

  • GKE 元数据服务器需要几秒钟的时间才能开始在新创建的 Pod 上运行。因此,在 Pod 生命周期的前几秒内尝试使用 Workload Identity 进行身份验证或授权可能会失败。重试调用会解决问题。

  • GKE 日志记录和监控代理将继续使用节点的服务帐号

  • Windows 节点不支持 Workload Identity。

  • Workload Identity 需要在 Google Cloud 上手动设置 Cloud Run for Anthos 才能继续发布请求指标

  • 如果创建集群时未使用 --disable-default-snat 标志,则 Workload Identity 会安装 ip-masq-agent

准备工作

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

使用以下任一方法设定默认的 gcloud 设置:

  • 使用 gcloud init(如果您想要在系统引导下完成默认设置)。
  • 使用 gcloud config(如果您想单独设置项目 ID、区域和地区)。

使用 gcloud init

如果您收到 One of [--zone, --region] must be supplied: Please specify location 错误,请完成本部分。

  1. 运行 gcloud init 并按照说明操作:

    gcloud init

    如果您要在远程服务器上使用 SSH,请使用 --console-only 标志来防止命令启动浏览器:

    gcloud init --console-only
  2. 按照说明授权 gcloud 使用您的 Google Cloud 帐号。
  3. 创建新配置或选择现有配置。
  4. 选择 Google Cloud 项目。
  5. 选择默认的 Compute Engine 区域。

使用 gcloud config

  • 设置默认项目 ID
    gcloud config set project project-id
  • 如果您使用的是区域级集群,请设置默认计算区域
    gcloud config set compute/zone compute-zone
  • 如果您使用的是地区级集群,请设置默认计算地区
    gcloud config set compute/region compute-region
  • gcloud 更新到最新版本:
    gcloud components update

在集群上启用 Workload Identity

您可以使用 gcloud 工具在新集群或现有集群上启用 Workload Identity。

  1. 确保您已启用 IAM Service Account Credentials API。

    启用 IAM Credentials API

  2. 如需创建启用了 Workload Identity 的新集群,请使用以下命令:

    gcloud container clusters create cluster-name \
      --workload-pool=project-id.svc.id.goog
    

    替换以下内容:

    • cluster-name:您的集群的名称。
    • project-id:您的 Google Cloud 项目的 ID。

    此操作需要项目的 container.clusters.create 权限。

  3. 如需在现有集群上启用 Workload Identity,请使用以下命令修改集群:

    gcloud container clusters update cluster-name \
      --workload-pool=project-id.svc.id.goog
    

    现有节点池不受影响;新节点池默认为 --workload-metadata=GKE_METADATA

    此操作需要集群的 container.clusters.update 权限。

将应用迁移到 Workload Identity

选择最适合您的环境的迁移策略。您可以就地迁移节点池,也可以创建启用了 Workload Identity 的新节点池。如果您还需要修改应用以兼容此功能,我们建议您创建新的节点池。

向启用了 Workload Identity 的集群添加新的节点池,并手动将工作负载迁移到该池。只有在集群上启用 Workload Identity 后,此操作才能成功。

gcloud container node-pools create nodepool-name \
  --cluster=cluster-name \
  --workload-metadata=GKE_METADATA

如果集群启用了 Workload Identity,您可以通过明确指定 --workload-metadata=GCE_METADATA 在特定节点池中选择性地停用它。如需了解详情,请参阅保护集群元数据

方案 2:修改节点池

修改现有节点池以启用 GKE_METADATA。只有在集群上启用 Workload Identity 后,此更新才会成功。它会立即为已经部署到节点池的工作负载启用 Workload Identity。此更改可防止工作负载使用 Compute Engine 服务帐号,必须谨慎部署。

gcloud container node-pools update nodepool-name \
  --cluster=cluster-name \
  --workload-metadata=GKE_METADATA

此操作需要项目的 container.nodes.update 权限。

向 Google Cloud 进行身份验证

本部分介绍了应用如何使用 Workload Identity 向 Google Cloud 进行身份验证。为此,请向应用分配一个 Kubernetes 服务帐号,并将其配置为充当 Google 服务帐号:

  1. 配置 kubectl 以与集群通信:

    gcloud container clusters get-credentials cluster-name
    

    cluster-name 替换为您在上一步中创建的集群的名称。

    此操作需要项目的 container.clusters.get 权限。

  2. 像大多数其他资源一样,Kubernetes 服务帐号也位于命名空间中。创建用于 Kubernetes 服务帐号的命名空间。

    kubectl create namespace k8s-namespace
    

    此操作需要集群内的创建命名空间 RBAC 权限

  3. 创建要用于您的应用的 Kubernetes 服务帐号:

    kubectl create serviceaccount --namespace k8s-namespace ksa-name
    

    替换以下内容:

    • k8s-namespace:您在上一步中创建的 Kubernetes 命名空间的名称。
    • ksa-name:您要用于 Kubernetes 服务帐号的名称。

    此操作需要命名空间内的创建服务帐号 RBAC 权限

    或者,您也可以使用默认命名空间或任意命名空间中的默认 Kubernetes 服务帐号。

  4. 为您的应用创建 Google 服务帐号。如果您已有一个服务帐号,则可以使用它,而不必创建新的服务帐号。该服务帐号不需要与您的集群位于同一项目中。您可以在您的组织中使用任何 Google 服务帐号。

    gcloud

    gsa-name 替换为您为服务帐号选择的名称。

    gcloud iam service-accounts create gsa-name
    

    Config Connector

    如果您已在集群上安装了 Config Connector,则可以使用 Config Connector 配置来创建新的 GKE 集群并启用 Workload Identity。

    注意:此步骤需要使用配置连接器。按照安装说明在您的集群上安装 Config Connector。

    apiVersion: iam.cnrm.cloud.google.com/v1beta1
    kind: IAMServiceAccount
    metadata:
      name: [GSA_NAME]
    spec:
      displayName: [GSA_NAME]
    如需部署此清单,请将它以 service-account.yaml 的形式下载到您的机器上。将 gsa-name 替换为您为服务帐号选择的名称,然后使用 kubectl 应用该清单。

    kubectl apply -f service-account.yaml

    此操作需要项目的 iam.serviceAccounts.create 权限。

    如需了解如何授权 Google 服务帐号访问 Google Cloud API,请参阅了解服务帐号

  5. 通过在 Kubernetes 服务帐号和 Google 服务帐号之间创建 IAM 政策绑定,允许 Kubernetes 服务帐号模拟 Google 服务帐号。此绑定允许 Kubernertes 服务帐号充当 Google 服务帐号。

    gcloud

    gcloud iam service-accounts add-iam-policy-binding \
      --role roles/iam.workloadIdentityUser \
      --member "serviceAccount:project-id.svc.id.goog[k8s-namespace/ksa-name]" \
      gsa-name@project-id.iam.gserviceaccount.com
    

    配置连接器

    注意:此步骤需要使用配置连接器。按照安装说明在您的集群上安装 Config Connector。

    apiVersion: iam.cnrm.cloud.google.com/v1beta1
    kind: IAMPolicy
    metadata:
      name: iampolicy-workload-identity-sample
    spec:
      resourceRef:
        apiVersion: iam.cnrm.cloud.google.com/v1beta1
        kind: IAMServiceAccount
        name: [GSA_NAME]
      bindings:
        - role: roles/iam.workloadIdentityUser
          members:
            - serviceAccount:[PROJECT_ID].svc.id.goog[[K8S_NAMESPACE]/[KSA_NAME]]
    如需部署此清单,请将它以 policy-binding.yaml 的形式下载到您的机器上。将 gsa-nameproject-idk8s-namespaceksa-name 替换为您的环境中的值,然后运行以下命令:

    kubectl apply -f policy-binding.yaml

    此操作需要项目的 iam.serviceAccounts.setIamPolicy 权限。

  6. 使用 Google 服务帐号的电子邮件地址,向 Kubernetes 服务帐号添加 iam.gke.io/gcp-service-account=gsa-name@project-id 注释。

    kubectl

    kubectl annotate serviceaccount \
      --namespace k8s-namespace \
      ksa-name \
      iam.gke.io/gcp-service-account=gsa-name@project-id.iam.gserviceaccount.com
    

    yaml

    apiVersion: v1
    kind: ServiceAccount
    metadata:
      annotations:
        iam.gke.io/gcp-service-account: gsa-name@project-id.iam.gserviceaccount.com
      name: ksa-name
      namespace: k8s-namespace
    

    此操作需要 Kubernetes 服务帐号的 RBAC 修改权限

  7. 使用运行 cloud-sdk 容器映像的 Kubernetes 服务帐号创建 Pod,并通过交互式会话连接到该 Pod,来验证服务帐号是否正确配置。

    kubectl

    kubectl run -it \
      --image google/cloud-sdk:slim \
      --serviceaccount ksa-name \
      --namespace k8s-namespace \
      workload-identity-test
    

    yaml

    apiVersion: v1
    kind: Pod
    metadata:
      name: workload-identity-test
      namespace: k8s-namespace
    spec:
      containers:
      - image: google/cloud-sdk:slim
        name: workload-identity-test
        command: ["sleep","infinity"]
      serviceAccountName: ksa-name
    

    google/cloud-sdk 映像中包含 gcloud 命令行工具,可以方便地使用 Google Cloud API。映像下载可能需要一段时间。

    此操作需要命名空间内的创建 Pod RBAC 权限。

    现在,您已连接到所创建的 Pod 中的交互式 shell。在 Pod 中运行以下命令:

    gcloud auth list
    

    如果服务帐号正确配置,Google 服务帐号电子邮件地址会列为活跃(也是唯一)的身份。这表明在默认情况下,Pod 会在调用 Google Cloud API 时使用 Google 服务帐号的权限。

通过代码使用 Workload Identity

通过代码向 Google Cloud 服务进行身份验证的过程,与使用 Compute Engine 元数据服务器进行身份验证的过程相同。使用 Workload Identity 时,您对实例元数据服务器发出的请求将路由到 GKE 元数据服务器。使用实例元数据服务器进行身份验证的现有代码(如使用 Google Cloud 客户端库的代码)在运行时不应进行任何修改。

了解 GKE 元数据服务器

GKE 元数据服务器是专为与 Kubernetes 搭配使用而设计的新元数据服务器。它以 daemonset 身份运行,每个集群节点上都有一个 Pod。该元数据服务器会拦截对 http://metadata.google.internal (169.254.169.254:80) 的 HTTP 请求(包括 GET /computeMetadata/v1/instance/service-accounts/default/token 等请求),以便为 Pod 配置充当的 Google 服务帐号检索令牌。指向元数据服务器的流量绝不会离开托管 Pod 的虚拟机实例。

GKE 元数据服务器仅实现部分 Compute Engine 元数据服务器端点,这些端点对于 Kubernetes 工作负载是相关且安全的:

  • /computeMetadata/v1/instance/attributes/cluster-location
  • /computeMetadata/v1/instance/attributes/cluster-name
  • /computeMetadata/v1/instance/attributes/cluster-uid
  • /computeMetadata/v1/instance/hostname
  • /computeMetadata/v1/instance/id
  • /computeMetadata/v1/project/numeric-project-id
  • /computeMetadata/v1/project/project-id
  • /computeMetadata/v1/instance/service-accounts
  • /computeMetadata/v1/instance/service-accounts/default
  • /computeMetadata/v1/instance/service-accounts/default/aliases
  • /computeMetadata/v1/instance/service-accounts/default/email
  • /computeMetadata/v1/instance/service-accounts/default/identity
  • /computeMetadata/v1/instance/service-accounts/default/identity?audience=audience
  • /computeMetadata/v1/instance/service-accounts/default/scopes
  • /computeMetadata/v1/instance/service-accounts/default/token
  • /computeMetadata/v1/instance/service-accounts/default/token?scopes=comma-separated-list-of-scopes

撤消访问权限

  1. 使用 IAM 撤消对 Google 服务帐号的访问权限:

    gcloud

    gcloud iam service-accounts remove-iam-policy-binding \
      --role roles/iam.workloadIdentityUser \
      --member "serviceAccount:project-id.svc.id.goog[k8s-namespace/ksa-name]" \
      gsa-name@gsa-project-id.iam.gserviceaccount.com
    

    替换以下内容:

    • project-id:GKE 集群的项目 ID 容器。
    • k8s-namespace:您的 Kubernetes 服务帐号所在的 Kubernetes 命名空间的名称。
    • ksa-name:将撤消其访问权限的 Kubernetes 服务帐号的名称。
    • gsa-name:Google 服务帐号的名称。
    • gsa-project-id:包含 Google 服务帐号的项目 ID。

    Config Connector

    如果您使用 Config Connector 创建服务帐号,请通过 kubectl 删除服务帐号。

    kubectl delete -f service-account.yaml
    

    此操作需要服务帐号的 iam.serviceAccounts.setIamPolicy 权限。

    缓存的令牌最长可能需要 30 分钟才会过期。您可以使用此命令检查缓存的令牌是否已过期:

    gcloud auth list
    

    如果此命令的输出结果中不再包含 gsa-name@project-id.iam.gserviceaccount.com,则表明缓存的令牌已经过期。

  2. 从 Kubernetes 服务帐号中移除注释。这是可选步骤,因为访问权限已被 IAM 撤消。

    kubectl annotate serviceaccount \
      --namespace k8s-namespace \
      ksa-name \
      iam.gke.io/gcp-service-account-
    

问题排查

如果您的应用无法向 Google Cloud 进行身份验证,请确保以下设置正确配置:

  1. 确保您已在包含 GKE 集群的项目中启用 IAM Service Account Credentials API。

    启用 IAM Credentials API

  2. 通过验证集群是否设置了工作负载身份池,确保已在集群上启用 Workload Identity。

    gcloud container clusters describe cluster-name \
      --format="value(workloadIdentityConfig.workloadPool)"
    
  3. 确保已在运行应用的节点池上配置 GKE 元数据服务器 (GKE_METADATA):

    gcloud container node-pools describe node-pool-name \
      --cluster=cluster-name \
      --format="value(config.workloadMetadataConfig.mode)"
    
  4. 确保已正确注释 Kubernetes 服务帐号

    kubectl describe serviceaccount \
      --namespace k8s-namespace \
      ksa-name
    

    注释应采用以下格式:

    iam.gke.io/gcp-service-account: gsa-name@project-id.iam.gserviceaccount.com
    
  5. 确保 Google 服务帐号正确配置

    gcloud iam service-accounts get-iam-policy \
      gsa-name@project-id.iam.gserviceaccount.com
    

    验证是否存在采用以下格式的绑定:

    - members:
      - serviceAccount:project-id.svc.id.goog[k8s-namespace/ksa-name]
      role: roles/iam.workloadIdentityUser
    
  6. 如果您制定了集群网络政策,请确保允许端口 988 上通向 127.0.0.1/32 的出站流量:

    kubectl describe networkpolicy network-policy-name
    

在集群上停用 Workload Identity

  1. 在每个节点池上停用 Workload Identity:

    gcloud container node-pools update nodepool-name \
      --cluster=cluster-name \
      --workload-metadata=GCE_METADATA
    

    对集群中的每个节点池重复此命令。

  2. 在集群中停用 Workload Identity:

    gcloud container clusters update cluster-name \
      --disable-workload-identity
    

    此操作需要集群的 container.clusters.update 权限。

在组织中停用 Workload Identity

从安全角度来看,Workload Identity 允许 GKE 声明可向 Google Cloud 资源进行身份验证并获得 Google Cloud 资源授权的 Kubernetes 服务帐号身份。对于已采取措施将工作负载与 Google Cloud 资源隔离(例如禁止创建服务帐号禁止创建服务帐号密钥)的管理员,可能还希望为组织停用 Workload Identity。

请参阅这些说明以了解如何为组织停用 Workload Identity

Workload Identity 的替代方案

您可以采用两种替代方法从 GKE 访问 Cloud API。不过在 Workload Identity 发布后,我们不再推荐使用这些方法,因为这些方法需要作出一定的妥协。

  1. 导出服务帐号密钥并将其存储为 Kubernetes Secret。Google 服务帐号密钥会在 10 年后失效,可以手动轮替。如果有未被发现的安全漏洞,则导出服务帐号密钥可能会导致安全漏洞范围扩大。

  2. 使用节点的 Compute Engine 服务帐号。您可以项目中的任何 IAM 服务帐号身份运行节点池。如果您在创建节点池期间未指定服务帐号,则 GKE 将使用项目的 Compute Engine 默认服务帐号。Compute Engine 服务帐号由该节点上部署的所有工作负载共享。这会导致权限过度预配。

后续步骤