从舰队工作负载向 API 和服务进行身份验证

本页介绍了如何通过使用舰队工作负载身份联合来配置应用,以便对 Compute Engine API 或 AI Platform API 等 Google Cloud API 进行身份验证。

什么是舰队工作负载身份联合?

借助工作负载身份联合,您可以使用集群中的工作负载向 Google Cloud 进行身份验证,而无需下载、手动轮替和常规管理凭据。相反,工作负载会使用 Google Cloud 生成的短期令牌进行身份验证。

Workload Identity Federation for GKE 可提供一个项目级的工作负载身份池,在该池中,在 GKE 集群中运行的应用可获取身份。舰队工作负载身份联合可将 Workload Identity Federation for GKE 扩展到所有舰队成员集群,无论这些集群位于不同的项目中还是位于 Google Cloud 之外。如果已注册的集群启用了舰队成员资格中的工作负载身份联合,则可以使用舰队范围的工作负载身份池获取身份,从而让您能够在整个舰队(甚至多个项目)中配置 Google Cloud API 和其他服务的身份验证。

Connect Agent 还可以在某些集群类型上使用舰队工作负载身份联合作为舰队成员资格的一部分向 Google Cloud 进行身份验证,并且需要使用某些能够跨项目运行的 GKE Enterprise 功能,例如 Cloud Service Mesh。

工作负载身份联合池和身份相同性

借助舰队工作负载身份联合,您的舰队中的每个应用都会获得一个不同的联合身份,可用于对 Google Cloud 和您开发的其他服务进行身份验证。应用会获得 IAM 可以识别的主账号标识符。此标识符使用以下语法:

PREFIX://iam.googleapis.com/projects/FLEET_PROJECT_NUMBER/locations/global/workloadIdentityPools/FLEET_PROJECT_ID.svc.id.goog/SELECTOR

此语法包含以下字段:

  • PREFIX:IAM principalprincipalSet,具体取决于所选资源。
  • FLEET_PROJECT_ID.svc.id.goog:您的舰队的工作负载身份池。每个舰队都有一个固定的工作负载身份池,它是由系统自动为您创建的。
  • FLEET_PROJECT_NUMBER:舰队宿主项目的编号。
  • SELECTOR:资源选择器。如需查看支持的选择器列表,请参阅支持的主账号标识符

整个舰队共享一个舰队工作负载身份池,因此您可以向舰队中任何位置(包括其他项目或云端)的应用授予对同一资源的访问权限,而无需为每个集群管理访问权限。与其他支持舰队的功能一样,舰队工作负载身份联合依赖于相同性原则,即不同集群中具有相同名称和命名空间的 Kubernetes 对象将被视为同一项。

例如,如果您有一个应用,其后端部署在同一舰队中的多个集群上,并且需要向 Google Cloud API 进行身份验证,那么您可以配置应用,使 backend 命名空间中的所有工作负载均可访问该 API。如需详细了解舰队如何使用相同性(包括身份相同性),请参阅舰队的工作原理

启用舰队工作负载身份联合后,您可以通过指定相应的主账号标识符,在 IAM 许可政策中引用舰队中的主账号。例如,您可以在许可政策中引用特定 Kubernetes 命名空间中的特定 ServiceAccount。然后,使用该 ServiceAccount 的所有应用都可以访问 IAM 允许政策适用的 Google Cloud 资源。

凭据流程

如需让特定命名空间中的应用使用舰队工作负载身份联合进行身份验证,请执行以下操作:

  1. 在该命名空间中部署包含以下信息的 ConfigMap:

    • 集群的工作负载身份池和身份提供方。
    • Kubernetes 将 ServiceAccount 令牌挂载到每个 Pod 的路径。此令牌是已签名的 JSON Web 令牌 (JWT)。

    此 ConfigMap 充当工作负载的应用默认凭据 (ADC) 文件。

  2. 创建 IAM 允许政策,向集群中主账号的主账号标识符(例如命名空间中的 ServiceAccount)授予对特定 Google Cloud 资源的访问权限。

  3. 确保命名空间中的工作负载在 Pod 规范中具有以下配置:

    • GOOGLE_APPLICATION_CREDENTIALS 环境变量设置为 Pod 中 ConfigMap 的装载路径。
    • 包含 ServiceAccount 令牌和您创建的 ConfigMap 的预测卷,该卷已装载到您在 GOOGLE_APPLICATION_CREDENTIALS 环境变量中指定的路径。
    • 容器中引用投影卷的卷装载。

当工作负载发出 Google Cloud API 调用时,会执行以下步骤:

  1. Google Cloud 身份验证库使用应用默认凭据 (ADC) 查找凭据。ADC 会检查您在 GOOGLE_APPLICATION_CREDENTIALS 环境变量中指定的路径,以查找身份验证令牌。
  2. ADC 身份验证库使用 ConfigMap 中的数据将您装载到 Pod 上的 ServiceAccount JWT 交换为来自 Security Token Service 的短期联合令牌,该令牌引用工作负载的主标识符。
  3. ADC 会在 API 请求中添加联合令牌。
  4. IAM 允许政策授权主账号标识符对 Google Cloud 资源执行所请求的操作。

适用于舰队工作负载身份联合的支持的主账号标识符

下表介绍了您可以在 IAM 中使用的选择器,以允许政策引用舰队中的正文:

主账号标识符类型 语法
使用特定 Kubernetes ServiceAccount 的所有 Pod 按名称选择 ServiceAccount:
principal://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/PROJECT_ID.svc.id.goog/subject/ns/NAMESPACE/sa/SERVICEACCOUNT

替换以下内容:

  • PROJECT_NUMBER:您的数字项目编号。如需获取项目编号,请参阅识别项目
  • PROJECT_ID:您的 Google Cloud 项目 ID。
  • NAMESPACE:Kubernetes 命名空间。
  • SERVICEACCOUNT:Kubernetes ServiceAccount 名称。

按 UID 选择 ServiceAccount:
principal://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/PROJECT_ID.svc.id.goog/kubernetes.serviceaccount.uid/SERVICEACCOUNT_UID

替换以下内容:

  • PROJECT_NUMBER:您的数字项目编号。如需获取项目编号,请参阅识别项目
  • PROJECT_ID:您的舰队项目 ID。
  • SERVICEACCOUNT_UID:API 服务器中 ServiceAccount 对象的 UID。

前期准备

  • 确保您已安装以下命令行工具:

    如果您使用 Cloud Shell 作为与 Google Cloud 交互的 Shell 环境,则系统会为您安装这些工具。

  • 确保您已初始化用于您项目的 gcloud CLI。

准备集群

在舰队中的应用能够接收联合身份之前,必须先将运行这些应用的集群注册到舰队并将其正确配置为使用舰队工作负载身份联合。以下部分介绍了如何为不同类型的集群设置舰队工作负载身份联合。

GKE

对于 GKE 集群,请执行以下操作:

  1. 在 Google Kubernetes Engine 集群上启用 Workload Identity Federation for GKE(如果尚未启用)。
  2. 将集群注册到舰队。

您还可以在集群创建和舰队注册过程中启用 Workload Identity Federation for GKE。

Google Cloud 外部的集群

以下类型的集群会在创建集群期间自动启用舰队工作负载身份联合,并注册到您的舰队:

  • Google Distributed Cloud on VMware(纯软件)
  • Google Distributed Cloud on Bare Metal(纯软件)
  • GKE on AWS
  • GKE on Azure

关联集群

使用 GKE Multi-Cloud API 注册的 EKS 和 AKS 关联集群默认会在启用了舰队工作负载身份联合的情况下注册。其他关联集群如果满足必要的要求,则可以在启用了舰队工作负载身份联合的情况下注册。按照注册集群中针对您的集群类型的说明进行操作。

在应用中使用舰队工作负载身份联合

以下步骤演示了如何在已注册的集群中配置工作负载以使用舰队工作负载身份联合:

  1. 查找集群工作负载身份池和身份提供方名称:

    gcloud container fleet memberships describe MEMBERSHIP_ID \
        --project=FLEET_PROJECT_ID \
        --format="table(authority.identityProvider,authority.workloadIdentityPool,name)"
    

    替换以下内容:

    • MEMBERSHIP_ID:集群成员资格名称。这通常是您的集群的名称。
    • FLEET_PROJECT_ID:舰队宿主项目的 ID。

    输出类似于以下内容:

    IDENTITY_PROVIDER: IDENTITY_PROVIDER
    WORKLOAD_IDENTITY_POOL: WORKLOAD_IDENTITY_POOL
    NAME: projects/FLEET_PROJECT_ID/locations/MEMBERSHIP_LOCATION/memberships/MEMBERSHIP_ID
    

    此输出包含以下信息:

    • IDENTITY_PROVIDER:集群的身份提供方。
    • MEMBERSHIP_LOCATION:舰队会员资格的位置。这通常与您的集群位置相同。
    • WORKLOAD_IDENTITY_POOL:与您的舰队关联的工作负载身份池的名称。此值的语法为 FLEET_PROJECT_ID.svc.id.goog
  2. 创建 Kubernetes 命名空间。您还可以使用任何现有命名空间,包括 default 命名空间。

    kubectl create namespace NAMESPACE
    

    NAMESPACE 替换为命名空间的名称。

  3. 在命名空间中创建新的 Kubernetes ServiceAccount。您还可以使用任何现有的 ServiceAccount,包括命名空间中的 default ServiceAccount。

    kubectl create serviceaccount KSA_NAME \
        --namespace=NAMESPACE
    

    KSA_NAME 替换为 ServiceAccount 的名称。

  4. 将以下清单保存为 adc-config-map.yaml。此 ConfigMap 包含工作负载的 ADC 配置。

    kind: ConfigMap
    apiVersion: v1
    metadata:
      namespace: NAMESPACE
      name: my-cloudsdk-config
    data:
      config: |
        {
          "type": "external_account",
          "audience": "identitynamespace:WORKLOAD_IDENTITY_POOL:IDENTITY_PROVIDER",
          "subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
          "token_url": "https://sts.googleapis.com/v1/token",
          "credential_source": {
            "file": "/var/run/secrets/tokens/gcp-ksa/token"
          }
        }
    
  5. 部署 ConfigMap:

    kubectl create -f adc-config-map.yaml
    
  6. 将以下清单保存为 workload-config.yaml

    apiVersion: v1
    kind: Pod
    metadata:
      name: my-pod
      namespace:  NAMESPACE
    spec:
      serviceAccountName: KSA_NAME
      containers:
      - name: sample-container
        image: google/cloud-sdk:slim
        command: ["sleep","infinity"]
        env:
        - name: GOOGLE_APPLICATION_CREDENTIALS
          value: /var/run/secrets/tokens/gcp-ksa/google-application-credentials.json
        volumeMounts:
        - name: gcp-ksa
          mountPath: /var/run/secrets/tokens/gcp-ksa
          readOnly: true
      volumes:
      - name: gcp-ksa
        projected:
          defaultMode: 420
          sources:
          - serviceAccountToken:
              path: token
              audience: WORKLOAD_IDENTITY_POOL
              expirationSeconds: 172800
          - configMap:
              name: my-cloudsdk-config
              optional: false
              items:
              - key: "config"
                path: "google-application-credentials.json"
    

    部署此工作负载时,Pod 中的 gcp-ksa 卷包含以下数据:

    • 您以名为 google-application-credentials.json 的文件形式部署的 ConfigMap 中的数据。此文件是 ADC 凭据配置文件
    • Kubernetes ServiceAccount 令牌为 token。Kubernetes 会将 ServiceAccount 的已签名 JWT 作为投影 ServiceAccount 令牌文件进行装载。

    Pod 中的容器会将 gcp-ksa 卷挂载到 /var/run/secrets/tokens/gcp-ksa 路径,并将 ADC 配置为在该路径中查找凭据配置 JSON 文件。

  7. 部署工作负载:

    kubectl apply -f workload-config.yaml
    

替代方法:模拟 IAM 服务账号

或者,您也可以在集群中配置 Kubernetes ServiceAccount,以模拟 IAM 服务账号并执行 IAM 服务账号可以执行的任何授权操作。此方法可能会增加维护开销,因为您必须在 IAM 和 Kubernetes 中管理服务账号的配对。

在大多数情况下,我们建议您直接引用 IAM 中的 Kubernetes 主账号,并按照在应用中使用舰队工作负载身份联合中的说明操作,以便授予对 Google Cloud 资源的访问权限。

  1. 查找集群工作负载身份池和身份提供方名称:

    gcloud container fleet memberships describe MEMBERSHIP_ID \
        --project=FLEET_PROJECT_ID \
        --format="table(authority.identityProvider,authority.workloadIdentityPool,name)"
    

    替换以下内容:

    • MEMBERSHIP_ID:集群成员资格名称。这通常是您的集群的名称。
    • FLEET_PROJECT_ID:舰队宿主项目的 ID。

    输出类似于以下内容:

    IDENTITY_PROVIDER: IDENTITY_PROVIDER
    WORKLOAD_IDENTITY_POOL: WORKLOAD_IDENTITY_POOL
    NAME: projects/FLEET_PROJECT_ID/locations/MEMBERSHIP_LOCATION/memberships/MEMBERSHIP_ID
    

    此输出包含以下信息:

    • IDENTITY_PROVIDER:集群的身份提供方。
    • MEMBERSHIP_LOCATION:成员资格的位置。这通常与您的集群位置相同。
    • WORKLOAD_IDENTITY_POOL:与您的舰队关联的工作负载身份池的名称。此值的语法为 FLEET_PROJECT_ID.svc.id.goog
  2. 创建一个您的应用可以模拟的 IAM 服务账号。您也可以使用任何现有的 IAM 服务账号。

    gcloud iam service-accounts create IAM_SA_NAME \
        --project=IAM_SA_PROJECT_ID
    

    替换以下内容:

    • IAM_SA_NAME:您的 IAM 服务账号的名称。
    • IAM_SA_PROJECT_ID:包含您的 IAM 服务账号的项目的项目 ID。该项目可以与您的舰队宿主项目不同。
  3. 通过添加必要的 IAM 允许政策,向 IAM 服务账号授予访问 Google Cloud API 所需的所有权限。您可以使用 gcloud iam service-accounts add-iam-policy-binding其他方法来执行此操作。如需在每项服务的文档中查找使用 Google Cloud API 所需的权限,请参阅了解角色中预定义角色及其必要权限的完整列表。

  4. 在命名空间中创建 Kubernetes ServiceAccount。您还可以使用现有的 Kubernetes 服务账号和任何命名空间,包括 default ServiceAccount 和 default 命名空间。

    kubectl create serviceaccount KSA_NAME \
        --namespace=NAMESPACE
    

    替换以下内容:

    • KSA_NAME:ServiceAccount 的名称。
    • NAMESPACE:命名空间的名称。
  5. 创建一个 IAM 许可政策,让集群中特定命名空间中的 Kubernetes ServiceAccount 能够模拟 IAM 服务账号:

    gcloud iam service-accounts add-iam-policy-binding IAM_SA_NAME@IAM_SA_PROJECT_ID.iam.gserviceaccount.com \
        --project=IAM_SA_PROJECT_ID \
        --role=roles/iam.workloadIdentityUser \
        --member="serviceAccount:WORKLOAD_IDENTITY_POOL[NAMESPACE/KSA_NAME]"
    

    WORKLOAD_IDENTITY_POOL 替换为工作负载身份池的名称。

  6. 将以下清单保存为 adc-config-map.yaml。此 ConfigMap 包含工作负载的 ADC 配置。

    kind: ConfigMap
    apiVersion: v1
    metadata:
      namespace: K8S_NAMESPACE
      name: my-cloudsdk-config
    data:
      config: |
        {
          "type": "external_account",
          "audience": "identitynamespace:WORKLOAD_IDENTITY_POOL:IDENTITY_PROVIDER",
          "service_account_impersonation_url": "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/IAM_SA_NAME@GSA_PROJECT_ID.iam.gserviceaccount.com:generateAccessToken",
          "subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
          "token_url": "https://sts.googleapis.com/v1/token",
          "credential_source": {
            "file": "/var/run/secrets/tokens/gcp-ksa/token"
          }
        }
    

    替换以下内容:

    • IAM_SA_NAME:要模拟的 IAM 服务账号的名称。
    • IAM_SA_PROJECT_ID:IAM 服务账号的项目 ID。
  7. 将以下清单保存为 workload-config.yaml

    apiVersion: v1
    kind: Pod
    metadata:
      name: my-pod
      namespace:  K8S_NAMESPACE
    spec:
      serviceAccountName: KSA_NAME
      containers:
      - name: my-container
        image: my-image
        command: ["sleep","infinity"]
        env:
          - name: GOOGLE_APPLICATION_CREDENTIALS
            value: /var/run/secrets/tokens/gcp-ksa/google-application-credentials.json
        volumeMounts:
        - name: gcp-ksa
          mountPath: /var/run/secrets/tokens/gcp-ksa
          readOnly: true
      volumes:
      - name: gcp-ksa
        projected:
          defaultMode: 420
          sources:
          - serviceAccountToken:
              path: token
              audience: WORKLOAD_IDENTITY_POOL
              expirationSeconds: 172800
          - configMap:
              name: my-cloudsdk-config
              optional: false
              items:
                - key: "config"
                  path: "google-application-credentials.json"
    
    

    部署此工作负载时,Pod 中的 gcp-ksa 卷包含以下数据:

    • 您以名为 google-application-credentials.json 的文件形式部署的 ConfigMap 中的数据。此文件是 ADC 凭据配置文件
    • Kubernetes ServiceAccount 令牌为 token。Kubernetes 会将 ServiceAccount 的已签名 JWT 作为投影 ServiceAccount 令牌文件进行装载。

    Pod 中的容器会将 gcp-ksa 卷挂载到 /var/run/secrets/tokens/gcp-ksa 路径,并将 ADC 配置为在该路径中查找凭据配置 JSON 文件。

  8. 部署工作负载:

    kubectl apply -f workload-config.yaml
    

验证舰队工作负载身份联合设置

在本部分中,您将创建一个 Cloud Storage 存储桶,并从使用舰队工作负载身份联合的 Pod 访问该存储桶。在执行这些步骤之前,请按照在应用中使用舰队工作负载身份联合部分中的说明,确保您已配置工作负载身份联合。

本部分不介绍如何使用 IAM 服务账号模拟方法验证工作负载身份联合。

  1. 查找项目的数字编号:

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

    输出类似于以下内容:

    1234567890
    
  2. 创建 Cloud Storage 存储桶,请运行以下命令:

    gcloud storage buckets create gs://FLEET_PROJECT_ID-test-bucket \
        --location=LOCATION
    

    LOCATION 替换为 Google Cloud 位置。

  3. 创建一个 IAM 允许政策,授予您创建的服务账号对存储桶的访问权限:

    gcloud storage buckets add-iam-policy-binding gs://FLEET_PROJECT_ID-test-bucket \
        --condition=None \
        --role=roles/storage.objectViewer \
        --member=principal://iam.googleapis.com/projects/FLEET_PROJECT_NUMBER/locations/global/workloadIdentityPools/FLEET_PROJECT_ID.svc.id.goog/subject/ns/NAMESPACE/sa/KSA_NAME
    

    替换以下内容:

    • FLEET_PROJECT_NUMBER:您的数字项目编号。
    • FLEET_PROJECT_ID:您的项目 ID。
    • NAMESPACE:运行上一部分中的 Pod 的 Kubernetes 命名空间的名称。
    • KSA_NAME:您在上一部分中创建的 Pod 使用的 Kubernetes ServiceAccount 的名称。
  4. 在 Pod 中创建 shell 会话:

    kubectl exec -it pods/my-pod --namespace=NAMESPACE -- /bin/bash
    
  5. 尝试列出存储桶中的对象:

    curl -X GET -H "Authorization: Bearer $(gcloud auth print-access-token)" \
    "https://storage.googleapis.com/storage/v1/b/test-bucket/o"
    

    输出如下所示:

    {
      "kind": "storage#objects"
    }
    

通过代码进行身份验证

当您使用 Cloud 客户端库时,身份验证库会自动使用 ADC 查找凭据,以向 Google Cloud 服务进行身份验证。您必须使用支持工作负载身份联合的 Cloud 客户端库。下面列出了要求的最低 Cloud 客户端库版本,以及有关如何检查当前版本的说明:

C++

大多数 C++ 版 Google Cloud 客户端库 通过使用 ChannelCredentials 对象支持身份联合,该对象是通过调用 grpc::GoogleDefaultCredentials() 创建的。如要初始化此凭据,必须使用 gRPC 的 1.36.0 版本或更高版本构建客户端库。

C++ 版 Cloud Storage 客户端库使用的是 REST API,而不是 gRPC,因此不支持身份联合。

Go

如果 Go 客户端库使用 golang.org/x/oauth2 模块的 v0.0.0-20210218202405-ba52d332ba99 版本或更高版本,则客户端库支持身份联合。

如要查看客户端库使用的模块版本,请运行以下命令:

cd $GOPATH/src/cloud.google.com/go
go list -m golang.org/x/oauth2

Java

如果 Java 客户端库使用 com.google.auth:google-auth-library-oauth2-http 工件的 0.24.0 版或更高版本,则客户端支持身份联合。

如需查看该客户端库使用的工件版本,请在应用目录中运行以下 Maven 命令:

mvn dependency:list -DincludeArtifactIds=google-auth-library-oauth2-http

Node.js

如果 Node.js 客户端库使用 google-auth-library 软件包的 7.0.2 版本或更高版本,则客户端支持身份联合。

如要查看客户端库使用的软件包版本,请在应用目录中运行以下命令:

npm list google-auth-library

创建 GoogleAuth 对象时,您可以指定项目 ID,也可以允许 GoogleAuth 自动查找项目 ID。如要自动查找项目 ID,配置文件中的服务账号必须具有项目的 Browser 角色 (roles/browser) 或具有同等权限的角色。如需了解详情,请参阅 google-auth-library 软件包的 README

Python

如果 Python 客户端库使用 google-auth 软件包的 1.27.0 版本或更高版本,则客户端支持身份联合。

如要检查客户端库使用的软件包版本,请在已安装该软件包的环境中运行以下命令:

pip show google-auth

如要为身份验证客户端指定项目 ID,您可以设置 GOOGLE_CLOUD_PROJECT 环境变量,也可以允许客户端自动查找项目 ID。如要自动查找项目 ID,配置文件中的服务账号必须具有项目的 Browser 角色 (roles/browser) 或具有同等权限的角色。如需了解详情,请参阅 google-auth 软件包用户指南

后续步骤

了解使用舰队工作负载身份联合时组织舰队的最佳实践