从 Microsoft Azure 访问资源

本文档介绍如何使用身份联合从 Microsoft Azure 访问 Google Cloud 资源。

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

准备工作

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

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

  2. 创建 Google Cloud 服务帐号

  3. 向服务帐号授予调用您的工作负载所需的 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 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"
}

将 Azure 添加为身份提供商

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

  • 提供商的 ID。

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

  • 您的 Azure 租户 ID

    特性映射列表,用于将 Azure 管理的身份的令牌上的声明映射到 Google 令牌上的特性。您可以使用 assertion 来引用 Azure 令牌,使用 google 来表示 Google 特性,使用 attribute 来表示自定义特性。

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

    您必须为 google.subject 提供映射。通常,我们建议将其映射到 assertion.sub,其中包含您将在下一部分中创建的代管式身份的对象 ID。这样可以提供在 IAM 角色绑定中使用的固定标识符。映射如下所示:

    google.subject=assertion.sub
    

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

    google.subject="azure::" + assertion.tid + "::" + assertion.sub
    

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

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

    attribute.tid=assertion.tid
    

    以下示例根据 assertion.oid 的值分配显示名:

    attribute.managed_identity_name={
    "8bb39bdb-1cc5-4447-b7db-a19e920eb111":"workload1",
    "55d36609-9bcf-48e0-a366-a3cf19027d2a":"workload2"
    }[assertion.oid]
    

    如需获取您可以引用的声明的完整列表,请获取工作负载中 Azure 虚拟机的访问令牌。在您的请求中,请将 resource 参数替换为工作负载身份池的完整资源名称。例如:

    curl

    curl -s
      'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://iam.googleapis.com/projects/project-number/locations/global/workloadIdentityPools/pool-id' \
      -H Metadata:true -H "Cache-Control: no-cache"
    

    PowerShell

    Invoke-WebRequest \
        -Uri 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://iam.googleapis.com/projects/project-number/locations/global/workloadIdentityPools/pool-id' \
        -Headers @{Metadata="true"}
    

    此代码会返回 Azure 虚拟机的访问令牌,您可以对其进行解码以查看可用的声明。

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

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

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

  • 显示名和说明。

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

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

    "e968c2ef-047c-498d-8d79-16ca1b61e77e" in assertion.groups
    

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

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

gcloud

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

gcloud beta iam workload-identity-pools providers create-oidc provider-id \
    --workload-identity-pool="pool-id" \
    --issuer-uri="https://sts.windows.net/azure-tenant-id" \
    --location="global" \
    --attribute-mapping="google.subject=assertion.sub"

响应如下所示:

Created WorkloadIdentityPoolProvider [provider-id].

REST

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

HTTP 方法和网址:

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

请求 JSON 正文:

{
  "issuerUrl": "https://sts.windows.net/azure-tenant-id"
}

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

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

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

为身份联合配置 Azure 租户

如需为身份联合准备 Azure 租户,请执行以下操作:

  1. 创建一个 Azure AD 应用和服务主帐号,并将其应用 ID URI 设置为您在上一部分创建的提供商的完整资源名称:

    https://iam.googleapis.com/projects/project-number/locations/global/workloadIdentityPools/pool-id/providers/provider-id
    
  2. 创建一个代管式身份,并记下其对象 ID。

  3. 将代管式身份分配给您要向其授予对 Google Cloud 资源的访问权限的虚拟机

授予模拟服务帐号的权限

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

如需为特定的代管式身份添加此角色绑定,请运行以下命令:

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/managed-identity-object-id"

如需为池中的所有身份添加此绑定,请运行以下命令:

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/azure-tenant-id"

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

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 或客户端库添加或撤消绑定。如需了解详情,请参阅授予、更改和撤消对资源的访问权限

将 Azure 令牌交换为 Google 令牌

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

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

  1. 使用 Azure Instance Metadata Service (IMDS) 来获取 Azure 访问令牌

  2. 将 Azure 访问令牌传递给 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": "azure-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

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

后续步骤