使用服务身份进行身份验证

Google Distributed Cloud (GDC) air-gapped 中的服务身份为工作负载或服务提供专用身份,以便以编程方式安全地访问资源和微服务。这些是应用或工作负载(而非人员)使用的特殊身份,不能用于人员登录。 与用户账号类似,您可以向服务身份授予权限和角色,以定义其获授权访问的内容。

您可能会注意到,此概念使用了两个术语。GDC 控制台主要使用术语“服务身份”,而 gdcloud 命令和 API 交互通常使用“服务帐号”。后者反映了底层 Kubernetes 自定义资源 ProjectServiceAccount 的名称。这两个术语都指同一事物:工作负载的非人员身份。本文档主要使用“服务身份”一词。

服务身份对于管理 GDC 基础架构非常有用,例如:

  • 内部分布式云服务和工作负载,用于安全地访问 Distributed Cloud 控制平面应用编程接口 (API)。例如,数据库服务与 Kubernetes API 交互以创建和删除数据库。
  • 分布式云中的客户工作负载,用于访问 Distributed Cloud 服务并进行授权的应用编程接口 (API) 调用。例如,服务身份可以使用 Vertex AI Workbench 笔记本管理客户,以使用 Speech-to-Text API 转录音频文件。
  • 外部工作负载与 Distributed Cloud 联合。例如,服务身份可以管理分布式云外部的应用,该应用可将文档数字化,但希望使用光学字符识别 (OCR) API 来替换其当前的 OCR 引擎。
  • Distributed Cloud 服务或系统控制器,用于安全地访问客户资源或用户集群。例如,服务身份可以管理身份验证和授权工作流,其中在管理员集群中运行的服务控制器需要在客户管理的用户集群中运行工作负载。

管理服务身份涉及服务身份(由 ProjectServiceAccount 资源表示)及其关联的凭据(公钥和私钥对)。您可以使用 GDC 控制台、gdcloud CLI 或 API 管理服务身份的账号。借助 gdcloud CLI,服务身份功能基于全局 ProjectServiceAccount API 构建。由于 ProjectServiceAccount 资源是在全局范围内配置的,因此它们可以在 gdcloud 宇宙中的所有可用区中运行。

准备工作

如需完成本文档中的任务,您必须拥有一个项目。如需了解如何 创建项目,请参阅 创建项目

您还必须拥有必要的权限并准备好环境。

请求 IAM 角色

如需在项目中创建、更新和删除服务身份,请与组织 IAM 管理员或项目 IAM 管理员联系,以请求 Project IAM Admin (project-iam-admin) 角色。

组织 IAM 管理员可以为组织内的任何项目授予此角色。项目 IAM 管理员可以在其特定项目的范围内授予此角色。

准备环境

如需使用命令行工具管理服务身份,请完成以下设置:

创建服务身份

创建服务身份包括创建账号及其关联的凭据。

创建账号

如需为服务身份创建账号,请使用 GDC 控制台、gdcloud CLI 或 API 在项目中创建 ProjectServiceAccount 资源。

控制台

  1. 登录 GDC 控制台。
  2. 在导航菜单中,选择身份和访问权限 > 服务身份
  3. 点击 创建服务身份。 此时会打开服务身份详细信息 页面。
  4. 服务身份名称 字段中,输入服务身份的名称。例如:testserviceidentity
  5. 点击创建

gdcloud

创建账号:

gdcloud iam service-accounts create NAME \
    --project=PROJECT

替换以下值:

  • NAMEProjectServiceAccount 的名称。该名称在项目命名空间中必须是唯一的。
  • PROJECT:要在其中创建服务 身份的项目。如果已设置 gdcloud init,请省略 --project 标志。

此命令会在 Management API 服务器上的项目命名空间中创建一个 ProjectServiceAccount

API

  1. 创建 ProjectServiceAccount 自定义资源 YAML 文件,例如 my-project-sa.yaml

    apiVersion: resourcemanager.global.gdc.goog/v1
    kind: ProjectServiceAccount
    metadata:
      name: NAME
      namespace: PROJECT
    spec:
      keys:
      - algorithm: ALGORITHM
      id: KEY_ID
      key: BASE64_ENCODED_KEY
      validAfter: "START_TIME"
      validBefore: "EXPIRATION_TIME"
    

    执行以下变量替换操作:

    • NAMEProjectServiceAccount 资源的名称。该名称在项目命名空间中必须是唯一的。
    • PROJECT:要在其中创建服务身份的项目。
    • ALGORITHM:密钥的算法。仅支持 ES256 密钥。
    • KEY_ID:密钥的唯一标识符。该 ID 用于确定要验证的密钥。
    • BASE64_ENCODED_KEY:要验证的 PEM 格式的 base64 编码的公钥。用于生成此公钥的私钥应采用 ECDSA P256 PEM 格式。
    • START_TIME:密钥生效的开始时间,例如 2025-02-07T00:59:34Z
    • EXPIRATION_TIME:密钥的到期时间,例如 2026-02-07T00:59:34Z
  2. ProjectServiceAccount 自定义资源应用于全局 API 服务器:

    kubectl --kubeconfig GLOBAL_API_SERVER_KUBECONFIG apply -f my-project-sa.yaml
    

    GLOBAL_API_SERVER_KUBECONFIG 变量替换为全局 API 服务器的 kubeconfig 文件的路径。

创建凭据

如需让工作负载或应用以服务身份(由您创建的账号表示)进行身份验证,您需要生成凭据。 创建凭据涉及生成加密私钥和公钥对,并将公钥与服务身份相关联。

如需为服务身份创建凭据,请使用 GDC 控制台、gdcloud CLI 或 API。

控制台

  1. 登录 GDC 控制台。
  2. 在导航菜单中,选择身份和访问权限 > 服务身份
  3. 点击您要在密钥中添加的服务身份的名称。
  4. 点击 创建新密钥
  5. 新密钥会显示在密钥 列表中,并且系统会显示一个对话框,确认您已成功创建密钥。

gdcloud

gdcloud 命令会创建一个应用默认凭据 JSON 文件以及一个公钥和私钥对:

gdcloud iam service-accounts keys create APPLICATION_DEFAULT_CREDENTIALS_FILENAME \
    --project=PROJECT \
    --iam-account=NAME \
    --ca-cert-path=CA_CERTIFICATE_PATH

替换以下值:

  • APPLICATION_DEFAULT_CREDENTIALS_FILENAME: JSON 文件的名称。
  • PROJECT:选择要为其创建密钥的项目。 如果已设置 gdcloud init,则可以省略 --project 标志。
  • NAME:要为其添加 密钥的服务身份的名称。
  • CA_CERTIFICATE_PATH:可选:证书 授权机构 (CA) 证书路径,用于验证身份验证端点。 如果您未指定此路径,系统将使用系统 CA 证书。 您必须在系统 CA 证书中安装 CA。

Distributed Cloud 会将公钥添加到您用于验证私钥签名的 JSON Web 令牌 (JWT) 的 ProjectServiceAccount 密钥中。私钥会写入应用默认凭据 JSON 文件。

以下示例展示了应用默认凭据 JSON 文件:

{
"type": "gdch_service_account",
"format_version": "1",
"project": "project_name",
"private_key_id": "abcdef1234567890",
"private_key": "-----BEGIN PRIVATE KEY-----\nETC\n-----END PRIVATE KEY-----\n",
"name": "service_identity_name",
"ca_cert_path": "/path/to/ca.crt",
"token_uri": "https://service-identity.<Domain>/authenticate"
}

此示例使用以下值:

  • project:组织中的项目命名空间。
  • private_key_id:分配给密钥的 ID。
  • private_key:CLI 生成的 PEM 格式的 ECDSA P256 私钥。
  • name:服务身份的名称。
  • ca_cert_path:用于验证身份验证端点的证书授权机构 (CA) 证书的路径。
  • token_uri:身份验证端点的地址。

API

  1. 生成公钥和私钥对。以下命令以 openssl 为例,这是一个常用的工具。

    openssl ecparam -name prime256v1 -genkey -noout -out "key.pem"
    openssl ec -in "key.pem" -pubout > "pub.pem"
    
  2. 对公钥进行 Base64 编码并检索其密钥 ID:

    KEY_ID=$(openssl pkey -in key.pem -pubout -outform der | openssl dgst -sha256 | sed 's/^.* //')
    BASE64_ENCODED_KEY=$(cat pub.pem | base64)
    
  3. 创建或更新 ProjectServiceAccount 自定义资源 YAML 文件,包括上一步中生成的密钥信息:

    apiVersion: resourcemanager.global.gdc.goog/v1
    kind: ProjectServiceAccount
    metadata:
      name: NAME
      namespace: PROJECT
    spec:
      keys:
      - algorithm: ALGORITHM
      id: KEY_ID
      key: BASE64_ENCODED_KEY
      validAfter: "START_TIME"
      validBefore: "EXPIRATION_TIME"
    

    执行以下变量替换操作:

    • NAMEProjectServiceAccount 资源的名称。该名称在项目命名空间中必须是唯一的。
    • PROJECT:您要在其中创建密钥的项目。
    • ALGORITHM:密钥的算法。仅支持 ES256 密钥。
    • KEY_ID:密钥的唯一标识符。该 ID 用于确定要验证的密钥。
    • BASE64_ENCODED_KEY:要验证的 PEM 格式的 base64 编码的公钥。用于生成此公钥的私钥应采用 ECDSA P256 PEM 格式。
    • START_TIME:密钥生效的开始时间,例如 2025-02-07T00:59:34Z
    • EXPIRATION_TIME:密钥的到期时间,例如 2026-02-07T00:59:34Z
  4. ProjectServiceAccount 自定义资源应用于全局 API 服务器:

    kubectl --kubeconfig GLOBAL_API_SERVER_KUBECONFIG apply -f my-project-sa.yaml
    

    GLOBAL_API_SERVER_KUBECONFIG 变量替换为全局 API 服务器的 kubeconfig 文件的路径。

  5. 创建包含私钥的应用默认凭据 JSON 文件。确保 JSON 文件中的 KEY_ID 变量设置为与您在 ProjectServiceAccount 规范中使用的 KEY_ID 变量相同的值。

    cat <<EOF > "key_file.json"
    {
      "format_version": "1",
      "name": "NAME",
      "private_key": "$(tr '\n' '\t' < "key.pem" | sed 's/\t/\\n/g')",
      "private_key_id": "KEY_ID",
      "project": "PROJECT",
      "token_uri": "AUTH_URL",
      "type": "gdch_service_account"
    }
    EOF
    

    执行以下变量替换操作:

    • NAME:服务身份的名称。
    • KEY_ID:密钥的唯一标识符。该 ID 用于确定要验证的密钥,并且必须与 ProjectServiceAccount 规范中使用的 KEY_ID 值匹配。
    • PROJECT:组织中的项目命名空间。
    • AUTH_URL:身份验证端点的地址。

如需详细了解如何使用生成的密钥文件以服务 身份进行身份验证,请参阅 以服务身份进行身份验证和使用服务身份。本部分介绍了如何使用 gdcloud CLI 中的密钥以及如何以编程方式使用客户端库中的密钥。

查看服务身份

本部分介绍了如何查看服务身份及其关联的公钥。

查看服务身份列表

如需查看项目中的服务身份列表,请使用 GDC 控制台或 gdcloud CLI。

控制台

  1. 登录 GDC 控制台。
  2. 选择一个项目。
  3. 在导航菜单中,依次点击身份和访问权限 > 服务身份 ,以查看项目的服务身份列表。

gdcloud

列出项目中的服务身份账号:

gdcloud iam service-accounts list \
    --project=PROJECT

PROJECT 替换为项目 ID。

查看服务身份的公钥列表

列出在项目中注册的服务身份账号的公钥:

gdcloud iam service-accounts keys list \
    --project=PROJECT \
    --iam-account=NAME

替换以下内容:

  • PROJECT:项目 ID。
  • NAME:要使用的服务身份账号的名称。

授予服务身份的权限

如需向服务身份授予权限,请使用 GDC 控制台或 gdcloud CLI 创建角色绑定,为其分配一个或多个角色。

控制台

  1. 登录 GDC 控制台。
  2. 选择一个项目。
  3. 在导航菜单中,选择身份和访问权限 > 访问权限
  4. 成员列表中,点击添加成员。 您会看到用户和角色 页面。
  5. 成员类型 列表中,选择服务身份
  6. 服务身份 列表中,选择要为其分配角色绑定的服务身份。
  7. 角色 列表中,选择要分配给服务身份的角色,例如 Backup Creator
  8. 可选:如需添加其他角色,请点击 添加其他角色。 选择其他角色。
  9. 点击添加

gdcloud

您可以将服务身份账号绑定到项目命名空间中的角色,也可以绑定到其他命名空间中的角色。

  • 将账号绑定到项目命名空间中的角色:

    gdcloud iam service-accounts add-iam-policy-binding \
        --project=PROJECT \
        --role=ROLE \
        --iam-account=NAME
    

    替换以下内容:

    • PROJECT:要在其中创建角色 绑定的项目。如果已设置 gdcloud init,则可以省略 --project 标志。
    • ROLE:要分配给 账号的预定义角色。请以 Role/name 格式指定角色,其中 Role 是 Kubernetes 类型 IAMRolename 是预定义 角色的名称。例如,如需分配 Project Viewer 角色,请将角色设置为 IAMRole/project-viewer
    • NAME:要使用的服务身份账号 的名称。
  • 将账号绑定到其他命名空间中的角色:

    gdcloud iam service-accounts add-iam-policy-binding \
        --role=ROLE \
        --role-namespace=ROLE_NAMESPACE \
        --iam-account=NAME
    

    替换以下内容:

    • ROLE:要分配给 账号的预定义角色。请以 Role/name 格式指定角色,其中 Role 是 Kubernetes 类型 IAMRolename 是预定义 角色的名称。例如,如需分配 Project Viewer 角色,请将角色设置为 IAMRole/project-viewer
    • ROLE_NAMESPACE:要与账号绑定的角色的命名空间(项目命名空间除外)。
    • NAME:要使用的服务身份账号 的名称。

以服务身份进行身份验证和使用服务身份

创建服务身份并 生成其密钥文件后,您可以使用该密钥文件 向 GDC 服务和集群进行身份验证。 通过身份验证,您可以获取 API 调用的短期有效访问令牌或配置集群访问权限。

您可以通过两种方式以服务身份进行身份验证:

使用 gdcloud CLI 进行身份验证

如需使用服务身份执行操作,您必须先激活其密钥文件。激活后,您可以使用 gdcloud CLI 获取令牌或生成 kubeconfig 文件。

使用密钥向服务身份授权

gdcloud auth activate-service-account 命令使用服务身份向 gdcloud CLI 进行身份验证。这样,您就可以使用服务身份账号权限(而不是用户账号)对 Distributed Cloud 资源执行操作。

使用密钥向服务身份授权:

  1. 创建凭据密钥文件, 如果您还没有。

  2. 运行以下命令以激活服务身份:

    gdcloud auth activate-service-account --key-file=KEY_FILE
    

    KEY_FILE 替换为凭据密钥 文件的路径,通常采用 JSON 格式。

    成功激活后,gdcloud 会使用服务身份的凭据,而不是您的用户凭据。

向服务身份授权后,您可以使用 gdcloud 输出访问令牌。您可以将此令牌用作不记名令牌,以对 HTTP 请求进行身份验证。 该令牌会授予对 AUDIENCES 参数中定义的服务的访问权限。

输出指定服务身份账号的身份令牌:

gdcloud auth print-identity-token --audiences=AUDIENCES

AUDIENCES 替换为令牌的预期接收者或服务。只能指定一个受众群体。

生成 kubeconfig 文件

向服务身份授权后,您可以生成 kubeconfig 文件以向 Kubernetes 集群进行身份验证。

  1. 设置 gdcloud core/organization_console_url 属性:

    gdcloud config set core/organization_console_url
    https://GDC_URL
    

    GDC_URL 替换为组织的网址。

  2. 生成 kubeconfig 文件以使用活跃的服务身份访问集群:

    • 对于地区集群

      export KUBECONFIG=KUBECONFIG_PATH
      gdcloud clusters get-credentials CLUSTER_NAME --zone ZONE
      

      替换以下内容:

      • KUBECONFIG_PATH:您要将生成的 kubeconfig 文件保存到的路径 。
      • CLUSTER_NAME:地区集群的名称。

      • ZONE:集群所在的地区的名称。

    • 对于全局 API 服务器

      export KUBECONFIG=KUBECONFIG_PATH
      gdcloud clusters get-credentials global-api
      

      KUBECONFIG_PATH 替换为要将生成的 kubeconfig 文件保存到的路径。

系统会生成 kubeconfig 文件,并将其配置为以服务身份进行身份验证。以下示例展示了一个 YAML 文件:

apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: <REDACTED>
    server: <REDACTED>
  name: cluster-name
contexts:
- context:
    cluster: cluster-name
    user: gdch_console-<REDACTED>_cluster-name
  name: cluster-name-gdch_console-<REDACTED>_cluster-name
current-context: cluster-name-gdch_console-gdc1-staging-gpcdemolabs-com_cluster-name
kind: Config
preferences: {}
users:
- name: gdch_console-<REDACTED>_cluster-name
  user:
    exec:
      apiVersion: client.authentication.k8s.io/v1
      args:
      - --audience=<REDACTED>
      command: gdcloud-k8s-auth-plugin
      env: null
      installHint: Run 'gdcloud components install gdcloud-k8s-auth-plugin' to use
        plugin
      interactiveMode: Never
      provideClusterInfo: false

以编程方式使用客户端库进行身份验证

以下示例展示了如何使用 标准 Google Cloud 客户端库以编程方式为 GDC 管理 API 生成 Security Token Service (STS) 访问令牌。这些 库使用 生成的服务帐号密钥文件进行身份验证 并获取令牌。此方法不需要任何先前的 gdcloud 授权。

Python

import google.auth
from google.auth.transport import requests
import requests as reqs

api_endpoint = "https://console.org-1.zone1.google.gdch.test"

# Load the GDC credentials (via GOOGLE_APPLICATION_CREDENTIALS or default path)
creds, project_id = google.auth.default()

# Apply the required audience for the GDC Management API
creds = creds.with_gdch_audience(api_endpoint)

def fetch_gdch_token():
    sesh = reqs.Session()
    # Note: Set verify to False only if bypassing self-signed cert verification.
    # Otherwise, rely on the "ca_cert_path" present in your gdch_credentials.json
    sesh.verify = False
    req = requests.Request(session=sesh)

    # Refresh the credentials to fetch the STS access token
    creds.refresh(req)

    print("Access Token:", creds.token)

fetch_gdch_token()

Java

import com.google.auth.oauth2.GdchCredentials;
import com.google.auth.oauth2.GoogleCredentials;

import java.io.FileInputStream;
import java.io.InputStream;

public class GdchAuthExample {
    public static void main(String[] args) throws Exception {
        // Path to your service account JSON file
        String credentialsPath = "/path/to/gdch/credentials.json";
        String apiAudience = "https://console.org-1.zone1.google.gdch.test";

        try (InputStream credentialsStream = new FileInputStream(credentialsPath)) {
            // Load the generic Google credentials from the stream
            GoogleCredentials credentials = GoogleCredentials.fromStream(credentialsStream);

            // Verify it loaded as GdchCredentials
            if (credentials instanceof GdchCredentials) {
                // Cast and attach the targeted GDC audience
                GdchCredentials gdch = ((GdchCredentials) credentials).createWithGdchAudience(apiAudience);

                // Force negotiation with the GDC Auth Server to fetch the token
                gdch.refreshIfExpired();

                System.out.println("Access Token: " + gdch.getAccessToken().getTokenValue());
            } else {
                System.err.println("The provided file is not a valid GdchCredentials format.");
            }
        }
    }
}

Go

package main

import (
  "context"
  "fmt"
  "log"

  "cloud.google.com/go/auth/credentials"
)

func main() {
  ctx := context.Background()
  apiEndpoint := "https://console.org-1.zone1.google.gdch.test"

  // Load default credentials. The STSAudience parameter is specifically
  // required for the GDC auth flow to correctly negotiate the token.
  creds, err := credentials.DetectDefault(&credentials.DetectOptions{
    STSAudience: apiEndpoint,
  })
  if err != nil {
    log.Fatalf("Failed to detect credentials: %v", err)
  }

  // Fetch an access token
  token, err := creds.Token(ctx)
  if err != nil {
    log.Fatalf("Failed to retrieve token: %v", err)
  }

  fmt.Printf("Access Token: %s\n", token.Value)
}

删除服务身份

删除服务身份后,ProjectServiceAccount 及其关联的公钥将被删除,现有私钥将失效,并且应用将无法再通过该服务身份访问项目资源。

如需删除服务身份,请使用 GDC 控制台或 gdcloud CLI。

控制台

  1. 登录 GDC 控制台。
  2. 在导航菜单中,选择身份和访问权限 > 服务身份
  3. 选中要删除的服务身份的复选框。
  4. 点击删除
  5. 系统会显示确认对话框。在通过在下方输入以下内容进行确认 字段中,输入 remove
  6. 点击删除

gdcloud

运行以下命令以删除服务身份账号:

gdcloud iam service-accounts delete NAME \
    --project=PROJECT

替换以下内容:

  • NAME:要删除的服务身份账号的名称。
  • PROJECT:项目 ID。

删除凭据

如果您想停用特定的密钥对,而不删除整个服务身份账号,可以从服务身份的账号中删除其公钥。此操作会使相应的私钥失效。

如需删除公钥,请使用 GDC 控制台或 gdcloud CLI。

控制台

  1. 登录 GDC 控制台。
  2. 在导航菜单中,选择身份和访问权限 > 服务身份
  3. 点击具有要删除的密钥的服务身份的名称。
  4. 点击 删除
  5. 在确认对话框中,点击删除

gdcloud

从项目中的服务身份账号中移除具有密钥 ID 的公钥:

gdcloud iam service-accounts keys delete KEY_ID \
    --project=PROJECT \
    --iam-account=NAME

替换以下内容:

  • KEY_ID:密钥的唯一标识符。
  • PROJECT:项目 ID。
  • NAME:服务身份账号的名称。