在以下任一场景中,将 Google Cloud 用作工作负载身份提供方:
- Google 工作负载身份: 将向已挂载的 Google 服务账号签发的、经 Google 签名的 OIDC 令牌,换取短期有效的 OpenAI 访问令牌。
- Google Kubernetes Engine: 将投影的 GKE 服务账号令牌换取短期有效的 OpenAI 访问令牌。
Google 工作负载身份
Google Cloud 工作负载可以从 Google 元数据服务器请求签名的 OIDC 身份令牌,而无需存储长期的服务账号密钥。在 OpenAI 工作负载身份联合中,Google 身份令牌是 OpenAI 在签发 OpenAI 访问令牌之前验证的主体令牌。此流程适用于使用挂载的 Google 服务账号的 Compute Engine、Cloud Run、GKE 工作负载,以及其他公开元数据服务器身份端点的 Google 托管运行时环境。
设置 Google 工作负载身份
为需要调用 OpenAI API 的工作负载创建一个 Google 服务账号。有关完整的设置流程,请参阅 Google 指南: 创建服务账号.
例如,使用 Google Cloud CLI 创建服务账号:
1
2
3
gcloud iam service-accounts create openai-wif \
--description="Service account for OpenAI workload identity federation" \
--display-name="OpenAI workload identity federation"创建 Compute Engine 虚拟机并挂载该服务账号,或者将服务账号挂载到运行您应用程序的 Google Cloud 资源上。该资源必须在运行时能够调用 Google 元数据服务器。有关虚拟机设置的详细信息,请参阅 Google 指南: 创建使用用户管理的服务账号的虚拟机.
请勿为此流程创建或下载服务账号密钥。工作负载使用挂载的服务账号和元数据服务器来请求短期有效的 OIDC 令牌。
获取 Google 身份令牌
从挂载了服务账号的 Google Cloud 资源中,向元数据服务器请求包含已配置受众的 OIDC 身份令牌。此令牌是 OpenAI 用于换取 OpenAI 签发的访问令牌的主体令牌。
1
2
3
4
5
AUDIENCE="https://api.openai.com/v1"
TOKEN=$(curl -sS -G -H "Metadata-Flavor: Google" \
"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity" \
--data-urlencode "audience=${AUDIENCE}")元数据服务器将返回一个经 Google 签名的 JWT。有关元数据服务器身份端点的更多信息,请参阅 Google 指南: 验证虚拟机身份.
验证令牌
在配置工作负载身份联合之前,请在本地解码一个 Google 身份令牌样本并检查其声明:
1
2
3
4
5
6
7
8
9
TOKEN="$TOKEN" python3 - <<'PY'
import base64
import json
import os
payload = os.environ["TOKEN"].split(".")[1]
payload += "=" * (-len(payload) % 4)
print(json.dumps(json.loads(base64.urlsafe_b64decode(payload)), indent=2))
PY解码后的 AWS 颁发 OIDC 令牌类似于:
解码后的 Google 元数据服务器身份令牌类似于如下格式:
1
2
3
4
5
6
7
8
9
10
{
"iss": "https://accounts.google.com",
"aud": "https://api.openai.com/v1",
"azp": "110123456789012345678",
"sub": "110123456789012345678",
"email": "openai-wif@my-project.iam.gserviceaccount.com",
"email_verified": true,
"iat": 1716235422,
"exp": 1716239022
}声明中显现。 iss, aud, email,且 sub 设置工作负载身份联合
在 OpenAI 中为 AWS 账户颁发者创建工作负载身份提供商,然后添加与 AWS 颁发令牌中的稳定声明相匹配的服务账户映射。
在 OpenAI 中为 Google 签发的身份令牌创建工作负载身份提供方,然后添加一个与令牌中的稳定声明相匹配的服务账号映射。
设置工作负载身份提供商
创建工作负载身份提供商。
-
为一个唯一的值,例如 设置 名称 设置颁发者和受众。
google-workload-identity-prod值。使用 描述,例如Production Google Cloud workloads,以帮助管理员识别该提供商。 -
OIDC 颁发者 URL 设置 设置为启用出站身份联合时返回的 AWS 特定于账户的颁发者 URL。此值必须与令牌的 to
https://accounts.google.com。将 为传递给 至您的工作负载从 Google 元数据服务器请求的自定义受众,例如https://api.openai.com/v1。此值必须与令牌的audclaim. -
使用 Google OIDC 发现。 保持禁用 使用上传的 JWKS 进行令牌验证 已禁用。OpenAI 使用 Google 的 OIDC 发现元数据和 JWKS 来验证 Google 签名的身份令牌。
-
如果需要派生映射属性,请添加属性转换。 For example, enter
subjectwith expressionassertion.subto createopenai.subject来自主题声明。仪表板会自动应用openai.前缀。对于已经以以下内容开头的原始令牌声明:openai.映射键,已以openai.开头的原始令牌声明将被忽略,除非配置了匹配的转换。
设置服务账户映射
-
创建服务账户映射。 设置 名称 为 Workload Identity Provider 中的唯一值,例如
compute-openai-wif值。使用 描述,例如Production Compute Engine OpenAI API workload,以说明哪个工作负载可以使用此映射。 -
匹配稳定的 Google 服务账号声明。 为每个必须匹配的 GitHub 声明添加一行 键 and 值 每一项需要匹配的声明对应一行。使用
sub作为主要身份绑定,因为它既稳定又唯一。您还可以额外匹配emailfor readability. -
选择 OpenAI 目标。 设置 项目 指向拥有目标服务账户的 OpenAI 项目。将 服务账户 Google Cloud 工作负载可以使用的 OpenAI 服务账号,例如
google-workload-identity-prod-openai-wif. -
根据需要缩小 API 权限范围。 选择适当的 权限 例如
api.model.requestandapi.vector_store.read以进一步限制从此映射生成的访问令牌。将权限留空可避免添加特定于 WIF 的范围限制;该令牌仍会以映射的服务账户身份进行授权。
在代码中使用令牌
配置您的 OpenAI SDK 客户端,使其从元数据服务器请求 Google 身份令牌,并将其换取 OpenAI 颁发的访问令牌。
设置 OPENAI_WIF_AUDIENCE 配置为 Workload Identity Provider 受众的自定义受众。SDK 会请求该受众的 Google 身份令牌,将其换取 OpenAI 颁发的访问令牌,并使用 OpenAI 令牌来验证 API 请求。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import OpenAI from "openai";
import type { SubjectTokenProvider } from "openai/auth";
const metadataEndpoint =
"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity";
const identityProviderId = process.env.OPENAI_IDENTITY_PROVIDER_ID;
const serviceAccountId = process.env.OPENAI_SERVICE_ACCOUNT_ID;
const audience = process.env.OPENAI_WIF_AUDIENCE;
if (!identityProviderId || !serviceAccountId || !audience) {
throw new Error(
"Set OPENAI_IDENTITY_PROVIDER_ID, OPENAI_SERVICE_ACCOUNT_ID, and OPENAI_WIF_AUDIENCE"
);
}
function googleMetadataIdentityTokenProvider(audience: string): SubjectTokenProvider {
return {
tokenType: "jwt",
getToken: async () => {
const url = new URL(metadataEndpoint);
url.searchParams.set("audience", audience);
url.searchParams.set("format", "full");
const response = await fetch(url, {
headers: { "Metadata-Flavor": "Google" },
});
if (!response.ok) {
throw new Error(
`Google metadata token request failed with status ${response.status}.`
);
}
const token = (await response.text()).trim();
if (!token) {
throw new Error("Google metadata server did not return an identity token.");
}
return token;
},
};
}
const client = new OpenAI({
workloadIdentity: {
identityProviderId,
serviceAccountId,
provider: googleMetadataIdentityTokenProvider(audience),
},
});
const response = await client.responses.create({
model: "gpt-4.1-mini",
input: "Say hello from Google Cloud workload identity federation.",
});
console.log(response.output_text);Google Kubernetes Engine
将 Google Kubernetes Engine 用作 Workload Identity Provider,方法是将 GKE 颁发的投影服务账号令牌换取短期 OpenAI 访问令牌。
GKE 工作负载可以使用以下任一方式进行身份验证:
- 由集群 OIDC 颁发者颁发的投影 Kubernetes 服务账号令牌。
- 通过 GKE Workload Identity 获取的 Google 服务账号身份令牌,其中 Kubernetes 服务账号绑定到 Google 服务账号。
当您希望 OpenAI 直接信任集群的 OIDC 颁发者时,请使用投影 Kubernetes 服务账号令牌。当您的工作负载已经依赖于 Google 服务账号身份,并且希望 OpenAI 信任 Google 颁发的身份令牌时,请使用 GKE Workload Identity。
如果您的 GKE 工作负载配置了 GKE Workload Identity,并且可以从元数据服务器请求 Google 身份令牌,请按照 Google 工作负载身份 上述说明,而不是 GKE 投射令牌流程。
设置 GKE
这些说明假定使用托管式 GKE 集群。对于自管理的 Kubernetes 集群,请使用 Kubernetes 指南.
为需要调用 OpenAI API 的 EKS 工作负载使用一个 Kubernetes ServiceAccount 用于需要调用 OpenAI API 的 GKE 工作负载。如果您还没有,请创建它:
kubectl create serviceaccount openai-wif --namespace default检索与 GKE 集群关联的颁发者 URL:
kubectl get --raw /.well-known/openid-configuration | jq -r .issuer输出示例:
https://container.googleapis.com/v1/projects/my-project/locations/us-central1/clusters/openai-wif您在 OpenAI 工作负载身份提供商中配置的颁发者必须与此颁发者 URL 以及投影的 EKS 服务账号令牌中的 iss 投影 GKE 服务账号令牌中的声明。
使用 OpenAI 预期的受众和适合您工作负载的过期时间来配置投影服务账号令牌。OpenAI 会验证令牌的颁发者、签名、受众和过期时间。在此示例中,令牌文件挂载于 /var/run/secrets/tokens/token, 使用的受众为 https://api.openai.com/v1, 并在 3600 秒后过期。如果投影令牌的受众与 OpenAI 工作负载身份提供商的受众相匹配,您可以使用不同的受众:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: v1
kind: Pod
metadata:
name: openai-wif-app
namespace: default
spec:
serviceAccountName: openai-wif
containers:
- name: app
image: my-image
volumeMounts:
- name: gke-sa-token
mountPath: /var/run/secrets/tokens
readOnly: true
volumes:
- name: gke-sa-token
projected:
sources:
- serviceAccountToken:
path: token
audience: "https://api.openai.com/v1"
expirationSeconds: 3600验证令牌
在配置工作负载身份联合之前,请在本地解码一个示例投影服务账号令牌并检查其声明。在挂载了投影令牌的运行中的 Pod 上执行:
1
2
3
4
5
6
7
8
9
10
11
TOKEN=$(kubectl exec -n default openai-wif-app -- cat /var/run/secrets/tokens/token)
TOKEN="$TOKEN" python3 - <<'PY'
import base64
import json
import os
payload = os.environ["TOKEN"].split(".")[1]
payload += "=" * (-len(payload) % 4)
print(json.dumps(json.loads(base64.urlsafe_b64decode(payload)), indent=2))
PY解码后的 AWS 颁发 OIDC 令牌类似于:
解码后的 GKE 投影服务账号令牌类似于:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"iss": "https://container.googleapis.com/v1/projects/my-project/locations/us-central1/clusters/openai-wif",
"aud": ["https://api.openai.com/v1"],
"sub": "system:serviceaccount:default:openai-wif",
"iat": 1716235422,
"exp": 1716239022,
"kubernetes.io": {
"namespace": "default",
"serviceaccount": {
"name": "openai-wif",
"uid": "11111111-2222-3333-4444-555555555555"
}
}
}声明中显现。 iss, aud,且 sub 设置工作负载身份联合
在 OpenAI 中为 AWS 账户颁发者创建工作负载身份提供商,然后添加与 AWS 颁发令牌中的稳定声明相匹配的服务账户映射。
在 OpenAI 中为 GKE 颁发者创建工作负载身份提供者,然后添加与投射令牌中的属性相匹配的服务账号映射。
设置工作负载身份提供商
创建工作负载身份提供商。
-
为一个唯一的值,例如 设置 名称 设置颁发者和受众。
google-gke-prod值。使用 描述,例如Production GKE cluster, 以帮助管理员识别集群。 -
OIDC 颁发者 URL 设置 设置为启用出站身份联合时返回的 AWS 特定于账户的颁发者 URL。此值必须与令牌的 指向由
kubectl get --raw /.well-known/openid-configuration | jq -r .issuer返回的颁发者。此值必须与iss投射 GKE 服务账号令牌中的声明。设置 为传递给 与在 projected 服务账号令牌卷上配置的受众相同。在此示例中,该值为https://api.openai.com/v1. -
使用 GKE OIDC 发现。 保持禁用 使用上传的 JWKS 进行令牌验证 已禁用。OpenAI 使用 GKE 颁发者的 OIDC 发现元数据和 JWKS 来验证投射的服务账号令牌。
-
如果需要派生映射属性,请添加属性转换。 For example, enter
gke_subjectwith expressionassertion.subto createopenai.gke_subject。控制台会应用openai.前缀。对于已经以以下内容开头的原始令牌声明:openai.映射键,已以openai.开头的原始令牌声明将被忽略,除非配置了匹配的转换。
设置服务账户映射
-
创建服务账户映射。 设置 名称 为 Workload Identity Provider 中的唯一值,例如
default-openai-wif值。使用 描述,例如Default namespace GKE OpenAI API workload,以说明哪个工作负载可以使用此映射。 -
匹配 GKE 服务账号主体。 设置 键 to
suband 值 tosystem:serviceaccount:default:openai-wif。对于 GKE 服务账号,主体格式为system:serviceaccount:<namespace>:<service-account-name>. -
选择 OpenAI 目标。 设置 项目 指向拥有目标服务账户的 OpenAI 项目。将 服务账户 映射到 GKE 工作负载可使用的 OpenAI 服务账号,例如
google-gke-prod-openai-wif. -
根据需要缩小 API 权限范围。 选择适当的 权限 例如
api.model.requestandapi.vector_store.read以进一步限制从此映射生成的访问令牌。将权限留空可避免添加特定于 WIF 的范围限制;该令牌仍会以映射的服务账户身份进行授权。
在代码中使用令牌
配置您的 OpenAI SDK 客户端以读取投射的 GKE 服务账号令牌,并将其换取 OpenAI 颁发的访问令牌。
使用已挂载的令牌路径(例如 /var/run/secrets/tokens/token,作为 SDK 工作负载身份联合提供者的主体令牌来源。SDK 会将该 GKE 令牌换取 OpenAI 颁发的访问令牌,并使用该 OpenAI 令牌对 API 请求进行身份验证。
以下示例使用自定义主体令牌提供者初始化 OpenAI 客户端。该提供者从挂载的文件路径读取投射的 GKE 服务账号令牌,并将其作为工作负载身份联合的主体令牌。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import { readFile } from "node:fs/promises";
import OpenAI from "openai";
import type { SubjectTokenProvider } from "openai/auth";
const tokenPath = "/var/run/secrets/tokens/token";
const identityProviderId = process.env.OPENAI_IDENTITY_PROVIDER_ID;
const serviceAccountId = process.env.OPENAI_SERVICE_ACCOUNT_ID;
if (!identityProviderId || !serviceAccountId) {
throw new Error("Set OPENAI_IDENTITY_PROVIDER_ID and OPENAI_SERVICE_ACCOUNT_ID");
}
function mountedGkeServiceAccountTokenProvider(path: string): SubjectTokenProvider {
return {
tokenType: "jwt",
getToken: async () => {
const token = (await readFile(path, "utf8")).trim();
if (!token) {
throw new Error("The mounted GKE service account token file is empty.");
}
return token;
},
};
}
const client = new OpenAI({
workloadIdentity: {
identityProviderId,
serviceAccountId,
provider: mountedGkeServiceAccountTokenProvider(tokenPath),
},
});
const response = await client.responses.create({
model: "gpt-4.1-mini",
input: "Say hello from Google GKE workload identity federation.",
});
console.log(response.output_text);Google Cloud 最佳实践
- 为每个工作负载使用专用的 Google 服务账号。避免在无关的服务或环境之间共享服务账号。
- 使用工作负载身份流程,而不是长期有效的服务账号密钥。对于可以使用元数据服务器身份令牌或 GKE 工作负载身份的工作负载,避免分发和轮替 JSON 密钥文件。
- 将身份范围限制在最小实用工作负载边界内。为单个应用程序提供独立的服务账号可以实现更清晰的审计和最小权限访问。
- 谨慎使用基于属性的映射。尽可能优先使用稳定的标识符(例如服务账号主体声明),而不是可变的元数据。
- 将生产环境与非生产环境的项目分开。独立的项目可降低意外共享权限的风险,并简化审计。
- 仅授予所需的 IAM 权限。将 Google 身份限制为工作负载所需的最低权限。
- 监控服务账号的使用情况。异常的令牌交换可能表明配置发生偏移或工作负载已遭到破坏。