为工作负载身份联合配置基于证书的访问权限

本文档介绍了如何使用 X.509 证书工作负载身份联合配置基于证书的访问权限。

基于证书的访问使用双向 TLS (mTLS) 在 TLS 握手期间对客户端和服务器进行身份验证。在此过程中,mTLS 绑定会根据传输上下文纳入政策,并使用 TLS 会话中客户端证书的状态来做出授权决策。

对于 X.509 工作负载身份联合,mTLS 绑定可确保整个身份验证流程安全地绑定到可信的工作负载。这样可以降低凭据被盗的风险,因为身份验证与特定的可信端点相关联。

基于证书的访问权限的工作负载身份联合配置概览

下面简要介绍了为工作负载身份联合配置基于证书的访问权限的流程:

  1. 通过配置与 X.509 证书信任锚点的信任关系,建立工作负载身份联合。

  2. 创建基于证书的访问权限级别。

  3. 将访问权限级别添加到强制执行 mTLS 绑定的情境感知访问权限政策。

准备工作

验证您是否满足以下前提条件:

  • 最新版本的 Google Cloud CLI

    如需更新到最新版本的 Google Cloud CLI,请运行以下命令:

    gcloud components update
    

    如果您需要安装 Google Cloud CLI,请参阅安装 Google Cloud CLI

  • 使用 X.509 证书信任锚的工作负载身份联合配置

  • 如需使用此功能,请填写以下表单以加入许可名单:许可名单申请表单。 您被添加到许可名单后,我们会与您联系。

为证书创建访问权限级别

  1. 创建 mTLS 访问权限级别。mTLS 访问权限级别会在确定对资源的访问权限时验证证书。

    控制台

    在 Access Context Manager 中,创建自定义访问权限级别,然后在 CEL 表达式字段中输入以下表达式:request.auth.matchesMtlsTokens(origin) == true

    gcloud

    如需创建自定义访问权限级别,请运行以下命令:

       gcloud access-context-manager levels create ACCESS_LEVEL_NAME 
    --title=TITLE
    --custom-level-spec=FILE
    --description=DESCRIPTION
    --policy=POLICY_NAME

    替换以下内容:

    • ACCESS_LEVEL_NAME:访问权限级别的名称。
    • TITLE:访问权限级别的标题。
    • FILE:一个 YAML 文件,其中包含以下内容:request.auth.matchesMtlsTokens(origin) == true
    • DESCRIPTION:访问权限级别的说明。
    • POLICY_NAME:访问权限政策的名称。
  2. 将您创建的访问权限级别导出到环境变量。此变量将在后续步骤中使用。

      export ACCESS_LEVEL_ID=ACCESS_LEVEL_ID
      

    ACCESS_LEVEL_ID 替换为访问权限级别名称,例如 accessPolicies/12345/accessLevels/acl_1

为工作负载身份池创建情境感知访问权限绑定

  1. 设置以下环境变量。

    export ORG_ID=ORG_ID
    export CALLER_PROJECT_ID=CALLER_PROJECT_ID
    export FEDERATED_PRINCIPAL=FEDERATED_PRINCIPAL
    

    替换以下内容:

    • ORG_ID:您的组织的 ID。
    • CALLER_PROJECT_ID:用于调用 API 的项目的 ID。
    • FEDERATED_PRINCIPAL:工作负载身份池中遵循上下文感知访问权限政策的身份主账号的名称。您可以使用以下任一选项:

      格式为 - principal://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/subject/SUBJECT_ATTRIBUTE_VALUE 的单个身份

      池中的所有身份,格式为 - principalSet://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/*

    gcloud

    gcloud alpha access-context-manager cloud-bindings create \
    --organization=ORG_ID \
    --federated-principal=FEDERATED_PRINCIPAL \
    --level=ACCESS_LEVEL_ID
    --dry-run-level=DRY_RUN_ACCESS_LEVEL_ID
    

    替换以下内容:

    • ACCESS_LEVEL_ID:访问权限级别名称。
    • DRY_RUN_ACCESS_LEVEL_ID:试运行访问权限级别名称。我们建议您先启用试运行政策绑定,以了解对现有流量的潜在影响。

    curl

    1. 创建包含上下文感知访问权限绑定的 JSON 文件。

      即使该字段是重复的,您也只能在请求中提供一个访问权限级别。您可以使用以下类型的联合身份正文:

      • 单个身份: principal://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/subject/SUBJECT_ATTRIBUTE_VALUE
      • 池中的所有身份: principalSet://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/*
      echo { \
        \"principal\": { \
          \"federatedPrincipal\": \"${FEDERATED_PRINCIPAL:?}\" \
        },\
        \"accessLevels\": [\"${ACCESS_LEVEL_ID:?}\"] \
      } \
      >> request.json
      
    2. 使用 curl 发送以下 HTTP 请求。

      curl -H "X-Goog-User-Project: ${CALLER_PROJECT_ID:?}" -X POST \
         -H "Authorization: Bearer $(gcloud auth print-access-token)" \
         -H "Content-Type: application/json; charset=utf-8" \
         -d @request.json \
       "https://accesscontextmanager.googleapis.com/v1alpha/organizations/${ORG_ID:?}/gcpUserAccessBindings"
      

使用 Google Cloud 客户端库进行授权

如需使用Google Cloud 客户端库授权工作负载身份联合工作负载,请完成以下步骤。

  1. 创建为员工身份联合身份验证配置的应用默认凭据 (ADC) 文件。

    gcloud iam workload-identity-pools create-cred-config IDENTITY_POOL_ID \
    --credential-cert-path WORKLOAD_CERTIFICATE_PATH \
    --credential-cert-private-key-path WORKLOAD_KEY_PATH \
    --output-file ADC_FILE_OUTPUT_PATH
    

    替换以下内容:

    • IDENTITY_POOL_ID:工作负载身份池的 ID。
    • WORKLOAD_CERTIFICATE_PATH:工作负载的证书文件的路径。
    • WORKLOAD_KEY_PATH:工作负载私钥文件的路径。
    • ADC_FILE_OUTPUT_PATH:ADC 文件的输出路径。

    此命令还会在 gcloud CLI 默认配置目录中生成一个证书配置文件。证书配置文件支持初始身份验证,并为后续对 Google Cloud 资源的请求建立 mTLS 握手。

  2. 设置一个环境变量,使其指向 ADC 文件。这样一来,Google 客户端库就能发现您的凭据。

    export GOOGLE_APPLICATION_CREDENTIALS=${application_default_credentials.json}
    

    如果您在生成 ADC 文件时省略 --output-file 实参,则此步骤为可选步骤。如果您省略该实参,系统会从 gcloud CLI 默认配置目录创建和读取 ADC 文件。

  3. 如需建立并测试对 Google Cloud API 的访问权限,请完成以下步骤。您可以使用 Go 或 Python。

    Go

    1. 使用以下示例创建 Go 文件,例如 golang_test.go

      package golang_test
      
      import (
           "io"
           "log"
           "testing"
      
           "cloud.google.com/go/auth/credentials"
           "cloud.google.com/go/auth/httptransport"
      )
      
      func TestGoExample(t *testing.T) {
      
           scopes := []string{
                   "https://www.googleapis.com/auth/pubsub", // Scope for Pub/Sub access
                   // Add other scopes as needed
           }
      
           dopts := credentials.DetectOptions{
                   Scopes: scopes,
           }
      
           // Create httptransport.Options with the scopes
           opts := &httptransport.Options{
                   DetectOpts: &dopts,
           }
           hc, err := httptransport.NewClient(opts)
           if err != nil {
                   t.Fatalf("NewHTTPClient: %v", err)
           }
      
           resp, err := hc.Get("https://pubsub.mtls.googleapis.com/v1/projects/PROJECT_ID/topics")
           if err != nil {
                   t.Fatalf("Get: %v", err)
           }
           t.Logf("Status: %s", resp.Status)
      
           t.Cleanup(func() {
                  resp.Body.Close()
           })
      
           b, err := io.ReadAll(resp.Body)
           if err != nil {
                  t.Fatal(err)
           }
           log.Println(string(b))
      }
      

      PROJECT_ID 替换为您的 gcloud CLI 项目 ID。

    2. 如需在 Compute Engine 虚拟机上运行测试,请使用以下命令。

    go mod init example.com
    go mod tidy
    go test -v golang_test.go --count=1
    

    Python

    1. 使用以下示例创建测试文件,例如 python_test.py

      import google.auth
      import google.auth.transport.requests
      import requests
      
      def test_go_example():
      # Define the required scopes for your application
      scopes = [
         "https://www.googleapis.com/auth/pubsub",  # Scope for Pub/Sub access
         # Add other scopes as needed
      ]
      
      # Obtain Application Default Credentials (ADC) with the specified scopes
      credentials, _ = google.auth.default(scopes=scopes)
      
      # Create an authorized HTTP session using the ADC credentials
      authed_session = google.auth.transport.requests.AuthorizedSession(credentials)
      
      try:
      # Make a GET request to the Pub/Sub API endpoint
      response = authed_session.get(
          "https://pubsub.mtls.googleapis.com/v1/projects/PROJECT_ID/topics"
      )
      
      # Check if the request was successful
      response.raise_for_status()  # Raise an exception for error statuses
      
      # Log the response status and content
      print(f"Status: {response.status_code}")
      print(response.text)
      
      except requests.exceptions.RequestException as e:
      print(f"Error making the request: {e}")
      
      if __name__ == "__main__":
      test_go_example()
      

      PROJECT_ID 替换为您的 gcloud CLI 项目 ID。

    2. 如需在 Compute Engine 虚拟机上运行测试,请完成以下步骤。

      1. 设置 Python 虚拟环境。
      2. 安装必需的库。

        pip install google-auth google-auth-httplib2 requests
        
      3. 运行测试:

        python3 python_test.py
        

使用纯 HTTP 请求进行授权

如需使用纯 HTTP 请求授权工作负载身份联合工作负载,请完成以下步骤。

  1. 通过标准 mTLS 握手从 Google Cloud Security Token Service 获取与证书绑定的访问令牌

  2. 使用从 Security Token Service 获取的访问令牌调用 Google Cloud 服务。此示例查询 Cloud Storage。

    $ curl --key ${workload_key.pem} --cert ${workload_cert.pem} -X GET 'https://storage.mtls.googleapis.com/{replace_with_your_resources}' -H "Authorization: Bearer $ACCESS_TOKEN"
    
  3. mTLS 绑定会强制使用 mTLS。运行以下命令,验证非 mTLS 连接是否会因未经授权而失败。

    $ curl -X GET 'https://storage.googleapis.com/{replace_with_your_resources}' -H "Authorization: Bearer $ACCESS_TOKEN"
    

列出政策绑定

如需列出 Workload Identity Federation 的政策绑定,请运行以下命令。

gcloud

以下命令会列出给定组织内的特定绑定,并过滤出适用于联合身份主账号的绑定。

gcloud alpha access-context-manager cloud-bindings list \
--organization=ORG_ID \
--filter='principal:federatedPrincipal'

curl

curl -H "X-Goog-User-Project: ${CALLER_PROJECT_ID:?}" -X GET \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
"https://accesscontextmanager.googleapis.com/v1alpha/organizations/${ORG_ID:?}/gcpUserAccessBindings?filter=principal%3Afederated_principal"

更新政策绑定

如需更新政策绑定,请将新的访问权限级别添加到 JSON 文件,然后运行以下命令。

gcloud

gcloud alpha access-context-manager cloud-bindings update \
--binding=BINDING_ID \
--level=NEW_ACCESS_LEVEL_ID

curl

curl -H "X-Goog-User-Project: ${CALLER_PROJECT_ID:?}" -X PATCH \
 -H "Authorization: Bearer $(gcloud auth print-access-token)" \
 -H "Content-Type: application/json; charset=utf-8" \
 -d @request.json \
"https://accesscontextmanager.googleapis.com/v1alpha/organizations/${ORG_ID:?}/gcpUserAccessBindings/${BINDING_ID:?}?updateMask=access_levels"

删除政策绑定

如需删除政策绑定,请运行以下命令。

gcloud

gcloud alpha access-context-manager cloud-bindings delete \
--binding=BINDING_ID

curl

curl -H "X-Goog-User-Project: ${CALLER_PROJECT_ID:?}" -X DELETE \
   -H "Authorization: Bearer $(gcloud auth print-access-token)" \
"https://accesscontextmanager.googleapis.com/v1alpha/organizations/${ORG_ID:?}/gcpUserAccessBindings/${BINDING_ID:?}"

问题排查

以下是一些常见问题以及建议的解决方法:

  • 错误403 Forbidden, user does not have permission.

    操作:检查 IAM 政策,验证工作负载身份池是否具有对您的 Google Cloud 资源的访问权限。

  • 错误Unauthorized_client: Could not obtain a value for google.subject from the given credential.

    操作:后端无法根据属性映射从客户端证书中提取 google.subject 的值。检查您的客户端证书,验证用于进行映射的字段是否具有值。

  • 如果您在启用情境感知访问权限后遇到意外的访问拒绝情况,可以使用以下命令移除情境感知访问权限绑定,从而快速解除流量封锁:

    gcloud alpha access-context-manager cloud-bindings delete
    

    恢复访问权限后,请查看审核日志,以确定请求遭拒的原因。