从 AWS 访问资源

本文档介绍如何使用身份联合从 Amazon Web Services (AWS) 访问 Google Cloud 资源。

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

准备工作

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

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

  2. 创建 AWS 角色并记下其 Amazon 资源名称 (ARN)。

  3. 创建 Google Cloud 服务帐号

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

创建工作负载身份池

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

如需创建新的工作负载身份池,您需要提供一个 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 WorkloadIdentityPool [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"
}

将 AWS 添加为身份提供商

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

  • 提供商的 ID。

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

  • 您的 AWS 帐号 ID

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

  • 显示名和说明。

  • 特性映射列表,用于将 AWS 令牌上的特性映射到 Google 令牌上的特性。默认情况下,每个池都使用以下特性映射,这些映射涵盖了大多数常见场景:

    Google AWS 说明
    google.subject assertion.arn IAM 正在对其进行身份验证的主帐号。这也是显示在 Cloud Logging 日志条目中的主体。此映射会自动填充 ARN(采用 arn:aws:sts::account-id:assumed-role/aws-role/aws-session-name 格式)。
    attribute.aws_role AWS 角色 AWS 角色(采用 arn:aws:sts::account-id:assumed-role/aws-role 格式)。

    您还可以指定能够在 IAM 绑定中引用的自定义映射。您可以使用 assertion 来引用 AWS 凭据,使用 google 来表示 Google 特性,使用 attribute 来表示自定义特性。例如,除了 google.subject 的默认映射之外,以下代码将 attribute.aws_account 映射到 assertion.account

    google.subject=assertion.arn,
    attribute.aws_account=assertion.account
    

    请参阅 GetCallerIdentity() 文档,以查看您可以引用的 AWS 令牌特性列表。 请注意,AWS 文档中的特性使用驼峰式大小写,而特性映射使用小写;例如,Account 变为 assertion.account

    对于更复杂的断言,您可以使用通用表达式语言。例如:

    attribute.environment=assertion.arn.contains(":instance-profile/Production") ? "prod" : "test"
    

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

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

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

    特性条件的格式为返回布尔值的 CEL 表达式。例如,以下代码会拒绝来自没有特定 AWS 角色的任何身份的请求:

    attribute.aws_role == "role-mapping"
    

    如需详细了解特性条件的常见使用场景,请参阅工作负载身份联合概览。

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

gcloud

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

gcloud beta iam workload-identity-pools providers create-aws provider-id \
    --workload-identity-pool="pool-id"
    --account-id="aws-account-id"
    --location="global"

响应如下所示:

Created WorkloadIdentityPoolProvider [provider-id].

REST

projects.locations.workloadIdentityPools.providers.create 方法可将 AWS 添加为提供商。

HTTP 方法和网址:

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

请求 JSON 正文:

{
  "aws": {
    "accountId": "aws-account-id"
  }
}

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

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

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

授予模拟服务帐号的权限

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

如需为 AWS 角色添加此绑定,请使用以下格式:

attribute.aws_role/arn:aws:sts::aws-account-id:assumed-role/aws-role-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.aws_role/arn:aws:sts::aws-account-id:assumed-role/aws-role-name"

如需为 AWS 用户添加此绑定,请使用以下格式:

subject/arn:aws:sts::aws-account-id:assumed-role/aws-role-name/aws-session-name

如需了解如何从 AWS ARN 提取 AWS 角色会话,请参阅有关 IAM 标识符的 AWS 文档。

以下示例演示了如何为 AWS 用户添加绑定:

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/arn:aws:sts::aws-account-id:assumed-role/aws-role-name/aws-session-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/arn:aws:sts::aws-account-id:aws-role-name"

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

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

将 AWS 令牌交换为 Google 令牌

一旦 AWS 角色或用户能够模拟服务帐号,您就可以将其 AWS 凭据交换为 Google 凭据。作为交换过程的一部分,您会将发送给 AWS GetCallerIdentity() 方法的请求的序列化版本传递给 Security Token Service。这使 Google Cloud 可以验证 AWS 主帐号的身份,并确认它有权模拟服务帐号。

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

  1. 获取临时 AWS 凭据

  2. 使用 Signature Version 4 创建一个发送给 AWS GetCallerIdentity() 方法的序列化签名请求

    该请求包含以下字段:

    • urlGetCallerIdentity() 的 AWS STS 端点的网址,例如 https://sts.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15。地区端点也受支持。
    • method:HTTP 请求方法:POST
    • headers:HTTP 请求标头,其中必须包含以下内容:
      • Authorization:请求签名。
      • hosturl 字段的主机名,例如 sts.amazonaws.com
      • x-amz-date:您将发送请求的时间,格式为 ISO 8601 Basic 字符串。此时间通常设置为当前时间,并用于防止重放攻击。
      • x-goog-cloud-target-resource:身份提供商的完整资源名称。例如:
        //iam.googleapis.com/projects/project-number/locations/global/workloadIdentityPools/pool-id/providers/provider-id
        

    请求类似于以下内容:

    {
      "url": "https://sts.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15",
      "method": "POST",
      "headers": [
        {
          "key": "Authorization",
          "value" : "AWS4-HMAC-SHA256 Credential=AKIASOZTBDV4D7ABCDEDF/20200228/us-east-1/sts/aws4_request, SignedHeaders=host;x-amz-date,Signature=abcedefdfedfd"
        },
        {
          "key": "host",
          "value": "sts.amazonaws.com"
        },
        {
          "key": "x-amz-date",
          "value": "20200228T225005Z"
        },
        {
          "key": "x-goog-cloud-target-resource",
          "value": "//iam.googleapis.com/projects/12345678/locations/global/workloadIdentityPools/my-pool/providers/my-aws-provider"
        }
      ]
    }
    
  3. 将序列化请求传递给 Security Token Service token() 方法,以便将 AWS 凭据交换为联合访问令牌:

    REST

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

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

    • project-number:您的 Google Cloud 项目编号。
    • pool-id:您在本教程前面创建的工作负载身份池的 ID。
    • provider-id:您在本教程前面配置的 AWS 身份提供商的 ID。
    • aws-request:发送给 GetCallerIdentity() 的序列化签名请求,其格式为网址转义的 JSON。

    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:aws:token-type:aws4_request",
      "subjectToken": "aws-request"
    }
    

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

     

    该方法会返回联合令牌。

  4. 调用 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 access-token

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

后续步骤