从共享信任舰队工作负载向 Google Cloud API 进行身份验证

本页面介绍了如何配置应用,以便从在整个舰队中具有共享信任模型的舰队向 Compute Engine API 或 AI Platform API 等Google Cloud API 进行身份验证。如果您的舰队在整个舰队中具有混合信任模型,请参阅从混合信任舰队工作负载向 Google Cloud API 进行身份验证预览版)。

本页面适用于平台管理员和运维人员,以及希望以编程方式从舰队工作负载向 Google CloudAPI 进行身份验证的安全工程师。如需详细了解我们在 Google Cloud文档中提及的用户角色和示例任务,请参阅常见的 GKE 用户角色和任务

在阅读本页面之前,请确保您熟悉以下概念:

共享信任环境的舰队工作负载身份联合简介

舰队工作负载身份联合使您可以使用内置的 Google Cloud 和 Kubernetes 身份验证机制从舰队工作负载向Google Cloud API 进行身份验证。借助舰队工作负载身份联合,无需使用安全性较低的方法(例如在 Pod 中装载访问令牌或存储长期有效的凭证)。

默认情况下,舰队宿主项目使用 Google 管理的工作负载身份池为整个舰队中的实体预配身份。舰队或舰队宿主项目中具有相同 IAM 标识符的任何实体都会被 IAM 视为同一实体。在共享信任环境(例如单租户舰队)中大规模授予访问权限时,这种隐式身份相同性非常有用,在这种情况下,其他实体是否无意中获得了类似的资源权限并不重要。

前期准备

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

    如果您使用 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 凭据配置文件
    • 作为 token 的 Kubernetes ServiceAccount 令牌。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 凭据配置文件
    • 作为 token 的 Kubernetes ServiceAccount 令牌。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 application-default print-access-token)" \
    "https://storage.googleapis.com/storage/v1/b/FLEET_PROJECT_ID-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 软件包用户指南

后续步骤

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