使用 Workload Identity

本文档介绍如何在 Google Kubernetes Engine (GKE) 集群上启用和配置 Workload Identity。Workload Identity 允许 GKE 集群中的工作负载模拟 Identity and Access Management (IAM) 服务帐号来访问 Google Cloud 服务。

如需详细了解 Workload Identity 的工作原理,请参阅 Workload Identity

限制

  • GKE 会为每个 Google Cloud 项目创建一个固定的 Workload Identity 池,格式为 PROJECT_ID.svc.id.goog

  • Workload Identity 不再要求使用元数据隐藏。Workload Identity 也可以保护曾经由元数据隐藏进行保护的敏感元数据。

  • 当 GKE 在节点池上启用 GKE 元数据服务器时,Pod 将无法再访问 Compute Engine 元数据服务器。相反,GKE 元数据服务器会拦截从这些 pod 向元数据端点发出的请求,但主机网络上运行的 Pod 除外。

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

  • GKE 元数据服务器需要几秒钟的时间才能开始接受新创建的 Pod 发出的请求。因此,尝试在 Pod 生命周期的前几秒内使用 Workload Identity 进行身份验证可能会失败。重试调用会解决问题。如需了解详情,请参阅下面的问题排查部分

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

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

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

  • Workload Identity 将每个节点的 GKE 元数据服务器连接限制为 200 个,以避免内存问题。如果您的节点数超出此限制,则可能会遇到超时情况。

准备工作

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

  • 确保您已启用 Google Kubernetes Engine API。
  • 启用 Google Kubernetes Engine API
  • 确保您已安装 Cloud SDK
  • 使用以下方法之一为您的项目设置默认 gcloud 命令行工具设置:
    • 使用 gcloud init(如果您想要在系统引导下完成项目默认设置)。
    • 使用 gcloud config(如果您想要单独设置项目 ID、可用区和区域。

    gcloud init

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

      gcloud init

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

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

    gcloud config

    1. 设置默认项目 ID
      gcloud config set project PROJECT_ID
    2. 设置默认的 Compute Engine 区域(例如 us-central1):
      gcloud config set compute/region COMPUTE_REGION
    3. 设置默认的 Compute Engine 可用区(例如 us-central1-c):
      gcloud config set compute/zone COMPUTE_ZONE
    4. gcloud 更新到最新版本:
      gcloud components update

    通过设置默认位置,您可以避免 gcloud 工具中出现以下错误:One of [--zone, --region] must be supplied: Please specify location

在集群上启用 Workload Identity

您可以使用 gcloud 工具在新的或现有的 GKE 标准集群上启用 Workload Identity。默认情况下,GKE Autopilot 集群上会启用 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。
  3. 如需在现有集群上启用 Workload Identity,请使用以下命令修改集群:

    gcloud container clusters update CLUSTER_NAME \
        --workload-pool=PROJECT_ID.svc.id.goog
    

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

将应用迁移到 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 元数据服务器。 只有在集群上启用 Workload Identity 后,此更新才会成功。修改节点池会立即为已部署到节点池的工作负载启用 Workload Identity。此更改可防止工作负载使用 Compute Engine 服务帐号,必须谨慎部署。

gcloud container node-pools update NODEPOOL_NAME \
    --cluster=CLUSTER_NAME \
    --workload-metadata=GKE_METADATA

向 Google Cloud 进行身份验证

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

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

    gcloud container clusters get-credentials CLUSTER_NAME
    

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

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

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

    kubectl create serviceaccount KSA_NAME \
        --namespace K8S_NAMESPACE
    

    替换以下内容:

    • KSA_NAME:新 Kubernetes 服务帐号的名称。
    • K8S_NAMESPACE:您在上一步中创建的 Kubernetes 命名空间的名称。
  4. 将您的应用配置为使用 Kubernetes 服务帐号:

    spec:
      serviceAccountName: KSA_NAME
    
  5. 为您的应用创建 IAM 服务帐号,或使用现有 IAM 服务帐号。您可以在组织中的任何项目中使用任何 IAM 服务帐号。对于 Config Connector,请为所选服务帐号应用 IAMServiceAccount 对象。

    gcloud

    如需使用 gcloud 工具创建新的 IAM 服务帐号,请运行以下命令。

    gcloud iam service-accounts create GSA_NAME
    

    GSA_NAME 替换为新的 IAM 服务帐号的名称。

    Config Connector

    如需将新的或现有的 IAM 服务帐号与 Config Connector 结合使用,请应用以下配置文件。

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

    apiVersion: iam.cnrm.cloud.google.com/v1beta1
    kind: IAMServiceAccount
    metadata:
      name: [GSA_NAME]
    spec:
      displayName: [GSA_NAME]
    如需部署此清单,请将它以 service-account.yaml 的形式下载到您的机器上。

    使用 kubectl 应用清单:

    kubectl apply -f service-account.yaml
    

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

  6. 确保您的 Google 服务帐号具有所需的 IAM 角色。您可以使用以下命令授予其他角色:

    gcloud projects add-iam-policy-binding PROJECT_ID \
        --member "serviceAccount:GSA_NAME@PROJECT_ID.iam.gserviceaccount.com" \
        --role "ROLE_NAME"
    

    替换以下内容:

    • PROJECT_ID:您的 Google Cloud 项目 ID。
    • GSA_NAME:您的 Google 服务帐号的名称。
    • ROLE_NAME:要分配给服务帐号的 IAM 角色,如 roles/spanner.viewer
  7. 通过在 Kubernetes 服务帐号和 Google 服务帐号之间创建 IAM 政策绑定,允许 Kubernetes 服务帐号模拟 Google 服务帐号。此绑定允许 Kubernertes 服务帐号充当 IAM 服务帐号。

    gcloud

    gcloud iam service-accounts add-iam-policy-binding GSA_NAME@PROJECT_ID.iam.gserviceaccount.com \
        --role roles/iam.workloadIdentityUser \
        --member "serviceAccount:PROJECT_ID.svc.id.goog[K8S_NAMESPACE/KSA_NAME]"
    

    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
    
  8. 使用 IAM 服务帐号的电子邮件地址,向 Kubernetes 服务帐号添加 iam.gke.io/gcp-service-account=GSA_NAME@PROJECT_ID 注释。

    kubectl

    kubectl annotate serviceaccount KSA_NAME \
        --namespace K8S_NAMESPACE \
        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
    
  9. 通过使用运行特定于操作系统的容器映像的 Kubernetes 服务帐号创建 Pod,然后通过交互式会话连接到该服务帐号,验证服务帐号是否配置正确。

    对于 Linux 节点

    1. 使用运行 cloud-sdk 容器映像的 Kubernetes 服务帐号创建 Pod:

      将以下配置保存为 wi-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
      

      创建 pod:

      kubectl apply -f wi-test.yaml
      

      在 Pod 中打开交互式会话:

      kubectl exec -it workload-identity-test \
          --namespace K8S_NAMESPACE -- /bin/bash
      

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

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

      curl -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/instance/service-accounts/
      

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

    对于 Windows Server 节点

    1. 使用运行 servercore 容器映像的 Kubernetes 服务帐号创建 Pod:

      apiVersion: v1
      kind: Pod
      metadata:
        name: workload-identity-test
        namespace: K8S_NAMESPACE
      spec:
        containers:
        - image: IMAGE_NAME
          name: workload-identity-test
          command: ["powershell.exe", "sleep", "3600"]
        serviceAccountName: KSA_NAME
        nodeSelector:
          kubernetes.io/os: windows
          cloud.google.com/gke-os-distribution: windows_ltsc
      

      IMAGE_NAME 替换为以下容器 servercore 映像值之一:

      Windows Server 节点映像 容器 servercore 映像
      WINDOWS_LTSC,
      WINDOWS_LTSC_CONTAINERD
      mcr.microsoft.com/windows/servercore:ltsc2019
      WINDOWS_SAC,
      WINDOWS_SAC_CONTAINERD
      检查 GKE 节点版本和 Windows SAC 版本之间的版本映射。对于 Windows Server 1909 版,请指定 mcr.microsoft.com/windows/servercore:1909;否则,请指定 mcr.microsoft.com/windows/servercore:20H2

      在 Pod 中打开交互式会话:

      kubectl exec -it workload-identity-test \
          --namespace K8S_NAMESPACE -- powershell
      
    2. 现在,您已连接到所创建的 Pod 中的交互式 shell。 在 Pod 中运行以下 powershell 命令:

      Invoke-WebRequest  -Headers @{"Metadata-Flavor"="Google"} -Uri  http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/email  -UseBasicParsing
      

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

通过代码使用 Workload Identity

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

撤消访问权限

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

    gcloud

    gcloud iam service-accounts remove-iam-policy-binding GSA_NAME@GSA_PROJECT_ID.iam.gserviceaccount.com \
        --role roles/iam.workloadIdentityUser \
        --member "serviceAccount:PROJECT_ID.svc.id.goog[K8S_NAMESPACE/KSA_NAME]"
    

    替换以下内容:

    • PROJECT_ID:GKE 集群的项目 ID。
    • K8S_NAMESPACE:您的 Kubernetes 服务帐号所在的 Kubernetes 命名空间的名称。
    • KSA_NAME:将撤消其访问权限的 Kubernetes 服务帐号的名称。
    • GSA_NAME:IAM 服务帐号的名称。
    • GSA_PROJECT_ID:IAM 服务帐号的项目 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 KSA_NAME \
        --namespace K8S_NAMESPACE iam.gke.io/gcp-service-account-
    

问题排查

Pod 无法向 Google Cloud 进行身份验证

如果您的应用无法向 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)"
    

    如果您尚未指定 gcloud 的默认可用区或区域,则您还可能需要在运行此命令时指定 --region--zone 标志。

  3. 确保已在运行应用的节点池上配置 GKE 元数据服务器 (GKE_METADATA):

    gcloud container node-pools describe NODEPOOL_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. 如果您制定了集群网络政策,请确保对于运行 1.21.0-gke.1000 之前的 GKE 版本的集群,允许端口 988 上流向 127.0.0.1/32 的出站流量,或对于运行 GKE 1.21.0-gke.1000 及更高版本的集群,允许端口 988 上流向 169.254.169.252/32 的出站流量。

    kubectl describe networkpolicy NETWORK_POLICY_NAME
    

Pod 启动时发生超时错误

GKE 元数据服务器需要几秒钟的时间才能开始在新创建的 Pod 上接受请求。因此,对于配置了较短超时的应用和 Google Cloud 客户端库,尝试在 Pod 生命周期内的前几秒内使用 Workload Identity 进行身份验证可能会失败。

如果您遇到超时错误,可以更改应用代码以等待几秒钟并重试。或者,您也可以部署 initContainer,等待 GKE 元数据服务器运行完毕,然后再运行 Pod 的主容器。

以下是一个具有示例 initContainer 的 Pod:

apiVersion: v1
kind: Pod
metadata:
  name: pod-with-initcontainer
spec:
  serviceAccountName: KSA_NAME
  initContainers:
  - image:  gcr.io/google.com/cloudsdktool/cloud-sdk:326.0.0-alpine
    name: workload-identity-initcontainer
    command:
    - '/bin/bash'
    - '-c'
    - |
      curl -s -H 'Metadata-Flavor: Google' 'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token' --retry 30 --retry-connrefused --retry-max-time 30 > /dev/null || exit 1
  containers:
  - image: gcr.io/your-project/your-image
    name: your-main-application-container

由于控制层面不可用,Workload Identity 失败

当集群控制层面不可用时,元数据服务器将无法返回 Workload Identity。对元数据服务器的调用会返回状态代码 500。

相应日志条目在日志浏览器中可能类似于以下内容:

dial tcp 35.232.136.58:443: connect: connection refused

这会导致 Workload Identity 不可用。

在集群维护(例如轮替 IP 地址、升级控制层面虚拟机或者调整集群或节点池的大小)期间,控制层面可能在可用区集群上无法使用。请参阅选择区域控制层面或可用区控制层面,以了解控制层面可用性。切换到区域集群可以消除此问题。

Workload Identity 失败

如果 GKE 元数据服务器由于某种原因被阻止,则 Workload Identity 将失败。

如果您使用的是 Istio,则应将以下应用级注解添加到使用 Workload Identity 的所有工作负载:

"traffic.sidecar.istio.io/excludeOutboundIPRanges=169.254.169.254/32"

或者,您也可以更改 global.proxy.excludeIPRanges Istio ConfigMap 键来执行相同的操作。

在集群上停用 Workload Identity

您只能在 GKE Standard 集群上停用 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

后续步骤