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

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

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

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

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

借助舰队工作负载身份联合,您可以将Google Cloud API 和资源的 IAM 角色授予舰队中的实体,例如特定命名空间中的工作负载。默认情况下,舰队宿主项目使用 Google 管理的工作负载身份池为整个舰队中的实体配置身份。但是,在混合信任环境(例如多租户舰队)或运行独立集群的舰队宿主项目中,我们建议您为部分工作负载和集群配置单独的自行管理的工作负载身份池。

使用自行管理的工作负载身份池的实体在 IAM 政策中具有不同于使用舰队宿主项目的 Google 管理的工作负载身份池的实体的标识符。这样可确保向特定舰队命名空间中的主账号授予访问权限不会无意中向与该标识符匹配的任何其他主账号授予访问权限。

对于自行管理的工作负载身份池,您需要使用团队范围。借助团队范围,您可以按团队控制对部分舰队资源的访问权限。您可以将特定团队范围绑定到特定舰队成员集群,以允许该团队在这些集群上部署工作负载。在团队范围内,团队成员只能将工作负载部署到舰队命名空间

使用自行管理的工作负载身份池为团队范围工作负载提供身份具有以下优势:

  • 确保对舰队命名空间中实体的访问权限授予不会意外应用于其他命名空间或集群中的实体。
  • 通过将一组舰队集群绑定到团队范围,并在这些集群中将自行管理的池设置为身份提供方,来配置这些集群以从自行管理的池获取身份。
  • 通过仅在特定集群中将自行管理的池设置为身份提供方,配置团队范围的部分绑定集群,以从自行管理的池中获取身份。

混合信任环境中的身份相同性示例

请考虑以下场景:

  • 您有两个舰队成员集群:frontend-clusterfinance-cluster
  • 您尚未配置自行管理的工作负载身份池。
  • 您创建一个 finance-team 团队范围,并在该团队范围内创建一个 finance-ns 舰队命名空间。
  • finance-cluster 集群绑定到 finance-team 团队范围。
  • 您向 finance-ns 舰队命名空间中的 finance-sa Kubernetes ServiceAccount 授予 IAM 角色。

满足以下条件的任何工作负载都共享同一身份:

  • finance-ns 舰队命名空间中运行。
  • 使用 finance-sa ServiceAccount。

不过,如果 frontend-cluster 集群中的某人创建了 finance-ns Kubernetes 命名空间和 finance-sa ServiceAccount,他们会获得与 finance-cluster 集群中的工作负载相同的身份。这是因为整个舰队都使用舰队宿主项目的 Google 管理的工作负载身份池,并且主账号标识符未指定宿主集群。

请考虑对上述场景进行以下更改:

  • 您在舰队中设置了自行管理的工作负载身份池。
  • 您可以配置 finance-cluster 集群,使其从自行管理的池(而非 Google 管理的池)获取身份。
  • 您创建的 IAM 角色授予会指定主账号标识符中的自行管理的池,而不是 Google 管理的池。

finance-clusterfinance-ns 舰队命名空间中运行的工作负载现在可以从自行管理的池中获取身份。但是,frontend-cluster 集群中 finance-ns Kubernetes 命名空间中的实体会继续从舰队宿主项目的 Google 管理的工作负载身份池中获取身份。

这些变化带来了以下好处:

  • 您可以向 finance-ns 舰队命名空间中的实体显式授予角色。
  • frontend-cluster 集群中的实体无法获得相同的访问权限,因为 frontend-cluster 集群中的身份来自 Google 管理的工作负载身份池。

准备工作

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

    如果您使用 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 关联集群默认会在启用了舰队工作负载身份联合的情况下注册。其他关联集群如果满足必要的要求,则可以在启用了舰队工作负载身份联合的情况下注册。按照注册集群中针对您的集群类型的说明进行操作。

设置 IAM 工作负载身份池

在本部分中,您将在舰队宿主项目中创建一个新的 IAM 工作负载身份池,并向舰队服务代理授予对该新池的访问权限。

  1. 创建工作负载身份池:

    gcloud iam workload-identity-pools create POOL_NAME \
        --location=global \
        --project=POOL_HOST_PROJECT_ID \
        --mode=TRUST_DOMAIN
    

    替换以下内容:

    • POOL_NAME:新工作负载身份池的名称。
    • POOL_HOST_PROJECT_ID:您要在其中创建自行管理的工作负载身份池的项目的项目 ID。您可以使用任何 Google Cloud 项目,包括舰队宿主项目。
  2. 向舰队服务代理授予新工作负载身份池的 IAM Workload Identity Pool Admin 角色 (roles/iam.workloadIdentityPoolAdmin):

    gcloud iam workload-identity-pools add-iam-policy-binding POOL_NAME \
        --project=POOL_HOST_PROJECT_ID \
        --location=global \
        --member=serviceAccount:service-FLEET_HOST_PROJECT_NUMBER@gcp-sa-gkehub.iam.gserviceaccount.com \
        --role=roles/iam.workloadIdentityPoolAdmin \
        --condition=None
    

    FLEET_HOST_PROJECT_NUMBER 替换为舰队宿主项目的项目编号。

将自行管理的池添加到舰队配置

在本部分中,您将使用舰队工作负载身份联合启用自行管理的池,并将您创建的池添加到舰队配置。本部分还提供了有关如何创建新团队范围和舰队命名空间的说明。如果您的舰队已配置团队范围和舰队命名空间,请跳过这些步骤。

  1. 在舰队级启用舰队工作负载身份联合:

    gcloud beta container fleet workload-identity enable \
      --project=FLEET_HOST_PROJECT_ID
    

    FLEET_HOST_PROJECT_ID 替换为您的舰队宿主项目的项目 ID。

  2. 将自行管理的工作负载身份池添加到舰队配置:

    gcloud beta container fleet workload-identity scope-tenancy-pool set POOL_NAME
    

    POOL_NAME 替换为您自行管理的工作负载身份池的名称。此值的语法如下:

    POOL_NAME.global.POOL_HOST_PROJECT_NUMBER.workload.id.goog
    
  3. 创建新的团队范围。如果您已有团队范围和舰队命名空间,请跳到验证工作负载身份池配置部分。

    gcloud container fleet scopes create SCOPE_NAME
    

    SCOPE_NAME 替换为新团队范围的名称。

  4. 在团队范围内创建新的舰队命名空间:

    gcloud container fleet scopes namespaces create NAMESPACE_NAME \
        --scope=SCOPE_NAME
    

    NAMESPACE_NAME 替换为新舰队命名空间的名称。

  5. 将舰队中的集群绑定到团队范围:

    gcloud container fleet memberships bindings create BINDING_NAME \
        --membership=FLEET_CLUSTER_NAME \
        --location=global \
        --scope=SCOPE_NAME
    

    替换以下内容:

    • BINDING_NAME:新成员资格绑定的名称。
    • FLEET_CLUSTER_NAME:要绑定到团队范围的现有舰队集群的名称。

验证工作负载身份池配置

在本部分中,您需要确保自行管理的工作负载身份池配置成功。

  1. 描述舰队成员资格配置:

    gcloud container fleet memberships describe FLEET_CLUSTER_NAME \
        --location=global
    

    FLEET_CLUSTER_NAME 替换为舰队中绑定到任何团队范围的现有舰队集群的名称。

    输出类似于以下内容:

    authority:
    ...
      scopeTenancyIdentityProvider: https://gkehub.googleapis.com/projects/FLEET_HOST_PROJECT_ID/locations/global/memberships/FLEET_CLUSTER_NAME
      scopeTenancyWorkloadIdentityPool: POOL_NAME.global.FLEET_HOST_PROJECT_NUMBER.workload.id.goog
      workloadIdentityPool: FLEET_HOST_PROJECT_ID.svc.id.goog
    ...
    

    此输出应包含以下字段:

    • scopeTenancyIdentityProvider:在团队范围内舰队命名空间中运行的工作负载的身份提供方。该值是集群的资源标识符。
    • scopeTenancyWorkloadIdentityPool:工作负载身份池,团队范围内舰队命名空间中的工作负载从中获取标识符。其值是您自行管理的工作负载身份池,格式为 POOL_NAME.global.FLEET_HOST_PROJECT_NUMBER.workload.id.goog
    • workloadIdentityPool:舰队宿主项目的 Google 管理的工作负载身份池的名称,舰队中的所有其他工作负载默认都从该身份池获取身份。
  2. 可选:检查您的工作负载身份池是否具有与舰队命名空间同名的命名空间:

    gcloud iam workload-identity-pools namespaces list \
        --workload-identity-pool=POOL_NAME \
        --location=global
    

    输出类似于以下内容:

    ---
    description: Fleet namespace NAMESPACE_NAME
    name: projects/FLEET_HOST_PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_NAME/namespaces/NAMESPACE_NAME
    state: ACTIVE
    

您的舰队现在可以使用自行管理的工作负载身份池,来获取在舰队命名空间中运行的工作负载的身份。如需开始使用自行管理池,请配置特定集群如何获取身份,如下一部分中所述。

让工作负载使用自行管理的身份池

如需让工作负载使用自行管理的池,您可以使用 Kubernetes ConfigMap 在舰队成员集群中配置特定的舰队命名空间。借助这种按集群、按命名空间的配置,您可以进一步缩小访问权限授予范围,从整个舰队命名空间缩小到在特定集群中的特定舰队命名空间中运行的工作负载。

  1. 连接到舰队成员集群:

    gcloud container clusters get-credentials FLEET_CLUSTER_NAME \
        --project=CLUSTER_PROJECT_ID \
        --location=CLUSTER_LOCATION
    

    替换以下内容:

    • FLEET_CLUSTER_NAME:已绑定到团队范围的舰队成员集群的名称。
    • CLUSTER_PROJECT_ID:集群项目的项目 ID。
    • CLUSTER_LOCATION:集群的位置。
  2. 获取自行管理的工作负载身份池的完整名称。您后面需要用到它。

    kubectl get membership membership -o json | jq -r ".spec.scope_tenancy_workload_identity_pool"
    

    输出类似于以下内容:

    POOL_NAME.global.FLEET_HOST_PROJECT_NUMBER.workload.id.goog
    
  3. 获取团队范围的身份提供方的名称。您后面需要用到它。

    kubectl get membership membership -o json | jq -r ".spec.scope_tenancy_identity_provider"
    

    输出类似于以下内容:

    https://gkehub.googleapis.com/projects/FLEET_HOST_PROJECT_ID/locations/global/memberships/FLEET_CLUSTER_NAME
    
  4. 在文本编辑器中,将以下 ConfigMap 的 YAML 清单保存为 self-managed-pool.yaml

    kind: ConfigMap
    apiVersion: v1
    metadata:
      namespace: NAMESPACE_NAME
      name: google-application-credentials
    data:
      config: |
        {
          "type": "external_account",
          "audience": "identitynamespace:SELF_MANAGED_POOL_FULL_NAME: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"
          }
        }
    

    替换以下内容:

    • NAMESPACE_NAME:舰队命名空间的名称。
    • SELF_MANAGED_POOL_FULL_NAME:本部分前面步骤的输出中自行管理的工作负载身份池的全名。例如 example-pool.global.1234567890.workload.id.goog
    • IDENTITY_PROVIDER:本部分前面步骤的输出中的身份提供方名称。例如 https://gkehub.googleapis.com/projects/1234567890/locations/global/memberships/example-cluster.
  5. 在集群中部署 ConfigMap:

    kubectl create -f self-managed-pool.yaml
    

部署 ConfigMap 会向 GKE 指示,相应命名空间中的工作负载应使用自行管理的工作负载身份池来获取身份。

向主账号授予 IAM 角色

在本部分中,您将在舰队命名空间中创建 Kubernetes ServiceAccount,并向该 ServiceAccount 授予 IAM 角色。使用此 ServiceAccount 的 Pod 随后可以访问您授予该角色的 Google Cloud 资源。

  1. 在舰队命名空间中创建 Kubernetes ServiceAccount:

    kubectl create serviceaccount SERVICEACCOUNT_NAME \
        --namespace=NAMESPACE_NAME
    

    替换以下内容:

    • SERVICEACCOUNT_NAME:新 ServiceAccount 的名称。
    • NAMESPACE_NAME:舰队命名空间的名称。
  2. 向 ServiceAccount 授予 IAM 角色。以下示例命令会向 ServiceAccount 授予存储桶的 Storage Object Viewer 角色 (roles/storage.objectViewer):

    gcloud storage buckets add-iam-policy-binding gs://BUCKET_NAME \
        --member=principal://iam.googleapis.com/projects/FLEET_HOST_PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_NAME.global.FLEET_HOST_PROJECT_NUMBER.workload.id.goog/subject/ns/NAMESPACE_NAME/sa/SERVICEACCOUNT_NAME \
        --role=roles/storage.objectViewer \
        --condition=None
    

member 标志包含您创建的新 ServiceAccount 的主账号标识符。工作负载发送到 Google CloudAPI 的请求使用联合访问令牌。此联合访问令牌包含发送请求的实体的主账号标识符。如果允许政策中授予目标资源角色的主账号与联合访问令牌中的主账号一致,则可以继续进行身份验证和授权。

部署使用自行管理型池的工作负载

您在舰队命名空间中应用的 Kubernetes 清单必须配置为从自行管理的池中获取身份。您部署的需要调用 Google Cloud API 的工作负载应包含以下字段:

  • metadata.namespace:舰队命名空间的名称。
  • spec.serviceAccountName:舰队命名空间中 Kubernetes ServiceAccount 的名称。
  • spec.containers.env:名为 GOOGLE_APPLICATION_CREDENTIALS 的环境变量,用于指示应用默认凭证 (ADC) 文件的路径。
  • spec.containers.volumeMounts:一个只读卷,可让容器使用 ServiceAccount 的不记名令牌。
  • spec.volumes:将 ServiceAccount 令牌装载到 Pod 中的投影卷。令牌的受众群体是自行管理的工作负载身份池。包含舰队工作负载身份联合配置的 ConfigMap 是卷的来源。

如需查看正确配置的清单文件示例,请参阅验证来自工作负载的身份验证部分。

验证工作负载的身份验证

本部分提供了一些可选说明,用于验证您是否正确配置了自行管理的工作负载身份池,方法是列出示例 Cloud Storage 存储桶的内容。您将创建一个存储桶,向舰队命名空间中的 ServiceAccount 授予该存储桶的一个角色,并部署一个 Pod 来尝试访问该存储桶。

  1. 创建 Cloud Storage 存储桶:

    gcloud storage buckets create gs://FLEET_HOST_PROJECT_ID-workload-id-bucket \
        --location=LOCATION \
        --project=FLEET_HOST_PROJECT_ID
    
  2. 向舰队命名空间中的 ServiceAccount 授予该存储桶的 roles/storage.objectViewer 角色:

    gcloud storage buckets add-iam-policy-binding gs://FLEET_HOST_PROJECT_ID-workload-id-bucket \
        --condition=None \
        --role=roles/storage.objectViewer \
        --member=principal://iam.googleapis.com/projects/FLEET_PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_NAME.global.FLEET_HOST_PROJECT_NUMBER.workload.id.goog/subject/ns/NAMESPACE_NAME/sa/SERVICEACCOUNT_NAME
    

    替换以下内容:

    • FLEET_HOST_PROJECT_NUMBER:舰队宿主项目的项目编号。
    • POOL_NAME:您自行管理的工作负载身份池的名称。
    • NAMESPACE_NAME:您要在其中运行 Pod 的舰队命名空间的名称。
    • SERVICEACCOUNT_NAME:Pod 应使用的 Kubernetes ServiceAccount 的名称。
  3. 将以下清单保存为 pod-bucket-access.yaml

    apiVersion: v1
    kind: Pod
    metadata:
      name: bucket-access-pod
      namespace:  NAMESPACE_NAME
    spec:
      serviceAccountName: SERVICEACCOUNT_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: POOL_NAME.global.FLEET_HOST_PROJECT_NUMBER.workload.id.goog
              expirationSeconds: 172800
          - configMap:
              name: my-cloudsdk-config
              optional: false
              items:
              - key: "config"
                path: "google-application-credentials.json"
    

    替换以下内容:

    • NAMESPACE_NAME:您要在其中运行 Pod 的舰队命名空间的名称。
    • SERVICEACCOUNT_NAME:Pod 应使用的 Kubernetes ServiceAccount 的名称。
    • POOL_NAME:您自行管理的工作负载身份池的名称。
    • FLEET_HOST_PROJECT_NUMBER:舰队宿主项目的项目编号。
  4. 在您的集群中部署 Pod:

    kubectl apply -f pod-bucket-access.yaml
    
  5. 在 Pod 中打开 shell 会话:

    kubectl exec -it bucket-access-pod -n NAMESPACE_NAME -- /bin/bash
    
  6. 尝试列出存储桶中的对象:

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

    输出如下所示:

    {
      "kind": "storage#objects"
    }
    

您可以选择性地验证不同舰队成员集群中的类似命名空间和 ServiceAccount 是否无法断言同一身份。在使用了舰队工作负载身份联合但没有舰队命名空间或自行管理的池配置的集群中,请执行以下步骤:

  1. 创建一个新的 Kubernetes 命名空间,其名称与您设置自行管理的工作负载身份池的舰队命名空间相同。
  2. 创建一个新的 Kubernetes ServiceAccount,其名称与您在前面部分中授予 IAM 角色的 ServiceAccount 的名称相同。
  3. 部署使用同一 ServiceAccount 和命名空间的 Pod,但其 spec.volumes.projected.sources.serviceAccountToken 字段指定 Google 管理的工作负载身份池。此池的语法如下:

    FLEET_HOST_PROJECT_ID.svc.id.goog
    
  4. 尝试通过 Pod 中的 shell 会话访问 Cloud Storage 存储桶。

输出应为 401: Unauthorized 错误,因为使用 Google 管理的工作负载身份池的 Pod 的主账号标识符与使用自行管理的池的 Pod 的主账号标识符不同。

后续步骤