从 OIDC 身份提供商访问资源

本文档介绍如何使用身份联合从支持 OpenID Connect (OIDC) 的身份提供商访问 Google Cloud 资源。

传统上,在 Google Cloud 外部运行的应用使用服务帐号密钥来访问 Google Cloud 资源。通过使用身份联合,您可以让外部身份模拟服务帐号。这样一来,您的工作负载便可以使用短期有效的访问令牌直接访问 Google Cloud 资源,而与服务帐号密钥相关联的维护和安全负担也得以消除。

准备工作

  1. 确保您具有 Workload Identity Pool Admin 角色 (roles/iam.workloadIdentityPoolAdmin)。

    此外,IAM Owner (roles/owner) 和 Editor (roles/editor) 基本角色还具有配置身份联合的权限。您不应在生产环境中授予基本角色,但可以在开发或测试环境中授予这些角色。

  2. 为您的组织更新组织政策,以允许来自身份提供商的联合身份服务。

  3. 创建 Google Cloud 服务帐号

  4. 向服务帐号授予调用您的工作负载所需的 Google Cloud API 的访问权限

创建工作负载身份池

工作负载身份池是外部身份集合的容器。工作负载身份池彼此隔离,但是单个池可以模拟任意数量的服务帐号。一般说来,我们建议为您的每个环境(例如开发环境、预演环境或生产环境)创建一个新池。

如需创建新的工作负载身份池,您需要提供一个 ID。您还可以提供可选的说明和显示名。

gcloud

执行 gcloud beta iam workload-identity-pools create 命令可创建工作负载身份池:

gcloud beta iam workload-identity-pools create pool-id \
    --location="global" \
    --description="description" \
    --display-name="display-name"

响应如下所示:

Created workload identity pool [pool-id].

REST

projects.locations.workloadIdentityPools.create 方法可创建工作负载身份池。

HTTP 方法和网址:

POST https://iam.googleapis.com/v1beta/projects/project-id/locations/global/workloadIdentityPools?workloadIdentityPoolId=pool-id

请求 JSON 正文:

{
  "description": "description",
  "display-name": "display-name"
}

如需发送您的请求,请展开以下选项之一:

该方法会返回长时间运行的 Operation,类似于以下内容:

{
  "name": "projects/project-number/locations/global/workloadIdentityPools/pool-id/operations/operation-id"
}

添加 OIDC 身份提供商

如需为工作负载身份池配置 OIDC 身份提供商,请至少提供以下内容:

  • 提供商的 ID。

  • 本文档上一部分中的工作负载身份池 ID。

  • 提供商的颁发者 URI。其格式通常为 https://example.com。如需查找该 URI,请参阅提供商有关 OIDC 集成的文档。

  • 特性映射列表,用于将外部令牌上的声明映射到 Google 令牌上的特性。您可以使用 assertion 来引用外部凭据,使用 google 来表示 Google 特性,使用 attribute 来表示自定义特性。

    Google 特性有两个:google.subjectgoogle.groups。您可以在 IAM 角色绑定中引用这些特性。google.subject 也会显示在 Cloud Logging 日志条目中。

    您必须为 google.subject 提供映射。通常,我们建议将其映射到 assertion.sub,这样可提供在 IAM 角色绑定中使用的固定标识符。映射如下所示:

    google.subject=assertion.sub
    

    对于更复杂的断言,您可以使用通用表达式语言。例如,如果工作负载身份池包含多个身份提供商,则您可以附加前缀来区分它们:

    google.subject="provider-a::" + assertion.sub
    

    google.subject 字段不能超过 127 个字符。

    您还可以指定自定义特性。例如,以下代码将 assertion.foo 映射到 attribute.bar

    attribute.bar=assertion.foo
    

    如需查看您可以引用的声明的完整列表,请参阅提供商的访问令牌文档。

    如需在表达式中引用声明的特定部分,请使用 CEL extract() 函数,该函数会根据您提供的模板从声明中提取值。如需详细了解 extract(),请参阅从特性中提取值

    如需检查凭据是否包含声明,请使用 has() 函数。

  • 允许的目标设备列表,用于指定外部凭据上的 aud 字段可以包含哪些值。您最多可以配置 10 个目标设备,每个目标设备最多 256 个字符。如需了解 aud 的默认值,请参阅提供商的文档。

    或者,如果身份提供商允许您配置 aud 的自定义值,则您可以将允许的目标设备参数留空,并将 aud 的值设置为工作负载身份提供商的完整资源名称。HTTP 前缀是可选的;例如:

    //iam.googleapis.com/projects/project-number/locations/global/workloadIdentityPools/pool-id/providers/provider-id
    https://iam.googleapis.com/projects/project-number/locations/global/workloadIdentityPools/pool-id/providers/provider-id
    

    无论是哪种情况,系统都会拒绝任何不包含某个允许值的令牌交换请求。

您还可以提供几个可选参数:

  • 显示名和说明。

  • 指定主帐号必须提供的特性的条件。该条件可以应用于外部凭据上的声明或 Google 凭据上的特性。任何不符合该条件的请求都会被拒绝。

    条件的格式为返回布尔值的 CEL 表达式。例如,以下代码会拒绝来自非特定群组成员的任何身份的请求:

    group in assertion.groups
    

    如果身份提供商面向公众提供服务,则我们强烈建议您使用条件。如需详细了解条件的常见用例,请参阅工作负载身份联合概览。

以下示例演示了如何添加身份提供商:

gcloud

执行 gcloud beta iam workload-identity-pools providers create-oidc 命令可添加身份提供商:

gcloud beta iam workload-identity-pools providers create-oidc provider-id \
    --workload-identity-pool="pool-id" \
    --issuer-uri="issuer-uri" \
    --location="global" \
    --attribute-mapping="google.subject=assertion.sub"

响应如下所示:

Created workload identity pool provider [provider-id].

REST

projects.locations.workloadIdentityPools.providers.create 方法可添加 OIDC 身份提供商。

HTTP 方法和网址:

POST https://iam.googleapis.com/v1beta/projects/project-id/locations/global/workloadIdentityPools/pool-id/providers?workloadIdentityPoolProviderId=provider-id

请求 JSON 正文:

{
  "issuerUrl": "issuer-uri"
}

如需发送您的请求,请展开以下选项之一:

该方法会返回长时间运行的 Operation,类似于以下内容:

{
  "name": "projects/project-number/locations/global/workloadIdentityPools/pool-id/providers/provider-id/operations/operation-id"
}

授予模拟服务帐号的权限

外部身份无法直接访问大多数 Google Cloud 资源。而是让这些身份为服务帐号授予 Workload Identity User 角色 (roles/iam.workloadIdentityUser) 来模拟服务帐号。

如需为特定身份添加此绑定,请使用您映射到 google.subject 的值:

gcloud iam service-accounts add-iam-policy-binding service-account-email \
    --role roles/iam.workloadIdentityUser \
    --member "principal://iam.googleapis.com/projects/project-number/locations/global/workloadIdentityPools/pool-id/subject/subject"

如需为群组的所有成员添加此绑定,请运行以下命令:

gcloud iam service-accounts add-iam-policy-binding service-account-email \
    --role roles/iam.workloadIdentityUser \
    --member "principalSet://iam.googleapis.com/project/project-number/workloadIdentityPools/pool-id/groups/group-name"

您还可以根据自定义特性授予访问权限。例如:

gcloud iam service-accounts add-iam-policy-binding service-account-email \
    --role="roles/iam.workloadIdentityUser" \
    --member="principalSet://iam.googleapis.com/projects/project-number/locations/global/workloadIdentityPools/pool-id/attribute.custom-attribute-name/custom-attribute-value"

如需撤消访问权限,请将 add-iam-policy-binding 替换为 remove-iam-policy-binding

您还可以使用 REST API 或客户端库添加或撤消绑定。如需了解详情,请参阅授予、更改和撤消对资源的访问权限

将外部令牌交换为 Google 令牌

一旦外部身份能够模拟服务帐号,您就可以将其凭据交换为 Google 凭据。

如需交换凭据,请执行以下操作:

  1. 从身份提供商获取 OIDC ID 令牌(如需了解详细说明,请参阅身份提供商的文档)。

  2. 将 OIDC ID 令牌传递给 Security Token Service token() 方法,以获取联合访问令牌:

    REST

    token 方法可将第三方令牌交换为 Google 令牌。

    在使用下面的请求数据之前,请先进行以下替换:

    • project-number:您的 Google Cloud 项目编号。
    • pool-id:您在本教程前面创建的工作负载身份池的 ID。
    • provider-id:您在本教程前面配置的身份提供商的 ID。

    HTTP 方法和网址:

    POST https://sts.googleapis.com/v1beta/token

    请求 JSON 正文:

    {
      "audience": "//iam.googleapis.com/projects/project-number/locations/global/workloadIdentityPools/pool-id/providers/provider-id",
      "grantType": "urn:ietf:params:oauth:grant-type:token-exchange",
      "requestedTokenType": "urn:ietf:params:oauth:token-type:access_token",
      "scope": "https://www.googleapis.com/auth/cloud-platform",
      "subjectTokenType": "urn:ietf:params:oauth:token-type:jwt",
      "subjectToken": "oidc-id-token"
    }
    

    如需发送您的请求,请展开以下选项之一:

     

    该方法会返回联合令牌。

  3. 调用 generateAccessToken() 以将联合令牌交换为服务帐号访问令牌。有限数量的 Google Cloud API 支持联合令牌;所有 Google Cloud API 都支持服务帐号访问令牌。

    REST

    Service Account Credentials API 的 serviceAccounts.generateAccessToken 方法为服务帐号生成 OAuth 2.0 访问令牌。

    在使用下面的请求数据之前,请先进行以下替换:

    • project-id:您的 Google Cloud 项目 ID。
    • sa-id:您的服务帐号的 ID。此 ID 可以是服务帐号的电子邮件地址(格式为 sa-name@project-id.iam.gserviceaccount.com)或服务帐号的唯一数字 ID。
    • token:联合访问令牌。

    HTTP 方法和网址:

    POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/sa-name@project-id.iam.gserviceaccount.com:generateAccessToken

    请求 JSON 正文:

    {
      "scope": [
        "https://www.googleapis.com/auth/cloud-platform"
      ]
    }
    

    如需发送您的请求,请展开以下选项之一:

    如果 generateAccessToken 请求成功,响应正文会包含一个 OAuth 2.0 访问令牌和一个到期时间。然后,便可以使用 accessToken 代表服务帐号验证请求,直至到达 expireTime 时限:

    {
      "accessToken": "eyJ0eXAi...NiJ9",
      "expireTime": "2020-04-07T15:01:23.045123456Z"
    }
    

拥有服务帐号的访问令牌后,您可以通过在请求的 Authorization 标头中添加该令牌来使用它调用 Google Cloud API:

Authorization: Bearer service-account-access-token

该请求会以服务帐号的身份获得授权。

后续步骤