从 Microsoft Azure 访问资源

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

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

准备工作

  1. 启用 IAM, Resource Manager, Service Account Credentials, and Security Token Service (STS) API。

    启用 API

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

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

  3. 为您的组织更新组织政策以允许来自 Azure 的联合。

  4. 创建 Google Cloud 服务帐号

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

创建工作负载身份池

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

如需创建新的工作负载身份池,您需要提供一个 ID。您还可以提供可选的说明和显示名。 ID 不能以 gcp- 开头;此前缀已预留给 Google 使用。

gcloud

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

gcloud 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"
}

将 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/providers/provider-id&object_id=managed-identity-object-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/providers/provider-id&object_id=managed-identity-object-id' \
        -Headers @{Metadata="true"}
    

    该响应是一个带有 access_token 字段的 JSON 对象,其中包含 Azure 虚拟机的访问令牌。要解码访问令牌并查看可用的声明,请按以下步骤操作:

    1. 复制完整的访问令牌。
    2. 在网络浏览器中,转到 https://jwt.ms/
    3. 将访问令牌粘贴到文本框中。
    4. 点击声明

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

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

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

  • 显示名和说明。

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

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

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

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

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

gcloud

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

gcloud 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 workload identity pool provider [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 正文:

{
  "attributeMapping": {
    "google.subject": "assertion.sub"
  },
  "oidc": {
    "issuerUri": "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/projects/project-number/locations/global/workloadIdentityPools/pool-id/group/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 或客户端库添加或撤消绑定。如需了解详情,请参阅授予、更改和撤消对资源的访问权限

生成 Google 凭据

如果您使用受支持的客户端库,则可以配置客户端库使其自动生成 Google 凭据。或者,您也可以手动生成 Azure 凭据,然后用它们换取 Google 凭据。

如果可能的话,我们建议您自动生成凭证,这样您就无需进行令牌交换过程。

自动生成凭据

如果您使用以下其中一种语言的客户端库访问 Google 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 软件包用户指南

如需配置客户端库以自动生成凭据,请运行 gcloud iam workload-identity-pools create-cred-config 命令以生成 JSON 配置文件:

gcloud iam workload-identity-pools create-cred-config \
    projects/project-number/locations/global/workloadIdentityPools/pool-id/providers/provider-id \
    --service-account=service-account-email \
    --output-file=filepath \
    --azure

替换以下值:

  • project-number:项目的数字 ID。
  • pool-id:Workload Identity 池的 ID。
  • provider-id:workload identity 池提供程序的 ID。
  • service-account-email:要模拟的服务帐号的电子邮件地址。
  • filepath:配置文件的文件路径。

生成配置文件后,将环境变量 GOOGLE_APPLICATION_CREDENTIALS 设置为配置文件的文件路径。此环境变量告知客户端库使用应用默认凭据进行身份验证。如需了解详情,请参阅自动查找凭据

手动交换凭据

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

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

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

    resource 查询参数设置为以下值:

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

    object_id 查询参数设置为您之前创建的代管式身份的对象 ID。

  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

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

后续步骤