将 Kubernetes 用作工作负载身份提供商,通过交换投影的 Kubernetes 服务账号令牌来获取短期 OpenAI 访问令牌。
设置 Kubernetes
本指南假设已启用 Kubernetes 服务账号令牌投影,这在现代 Kubernetes 版本中默认可用。OpenAI 工作负载身份联合需要兼容 OIDC 的投影服务账号令牌。不支持存储在 Secrets 中的旧版 Kubernetes 服务账号令牌。
为需要调用 OpenAI API 的 EKS 工作负载使用一个 Kubernetes ServiceAccount 针对需要调用 OpenAI API 的工作负载。如果尚未拥有,请创建一个:
kubectl create serviceaccount openai-wif --namespace default获取 Kubernetes 集群的 OIDC 签发者:
kubectl get --raw /.well-known/openid-configuration | jq -r .issuer即使您上传了 JWKS,并且 OpenAI 不会针对 OIDC 签发者执行 JWKS 发现,该签发者也必须与工作负载身份提供商中配置的签发者相匹配。
获取集群 JWKS 并保存返回的密钥集。配置工作负载身份提供商时将需要用到它:
kubectl get --raw /openid/v1/jwks使用 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: ksa-token
mountPath: /var/run/secrets/tokens
readOnly: true
volumes:
- name: ksa-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 令牌类似于:
解码后的 Kubernetes 投影服务账号令牌类似于:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"iss": "https://kubernetes.example.com",
"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 中为 Kubernetes 签发者创建工作负载身份提供商,然后添加与投影令牌中的属性相匹配的服务账号映射。
设置工作负载身份提供商
创建工作负载身份提供商。
-
为一个唯一的值,例如 设置 名称 设置颁发者和受众。
kubernetes-prod值。使用 描述,例如Production Kubernetes cluster, 以帮助管理员识别集群。 -
OIDC 颁发者 URL 设置 设置为启用出站身份联合时返回的 AWS 特定于账户的颁发者 URL。此值必须与令牌的 指向由
kubectl get --raw /.well-known/openid-configuration | jq -r .issuer返回的颁发者。此值必须与iss投影令牌中的声明。设置 为传递给 为在投影服务账号令牌卷上配置的相同不透明受众(audience)字符串。在此示例中,该值为https://api.openai.com/v1. -
上传 Kubernetes JWKS。 启用 使用上传的 JWKS 进行令牌验证,然后设置 JWKS JSON to the output from
kubectl get --raw /openid/v1/jwks。OpenAI 使用此公钥集来验证投影的 Kubernetes 服务账号令牌。请上传包含周围keys.Note: 对于自托管 Kubernetes 集群,OpenAI 仅支持本地 JWKS 模式。请上传集群返回的 JWKS;OpenAI 不会针对配置的签发者执行 OIDC 发现。OpenAI 仍会将配置的签发者与令牌中的
iss字段进行比较。如果您的集群轮换服务账号签名密钥,请更新工作负载身份提供商配置中上传的 JWKS。由不在已配置 JWKS 中的密钥签名的令牌将被拒绝。如果 JWKS 包含多个活跃公钥,请包含完整的
keysarray. -
仅在需要派生映射属性时才添加属性转换。 诸如以下原始令牌声明
sub,aud,且iss可直接用于映射断言中。如果您计划基于转换后的属性而非原始令牌声明进行匹配,控制台将应用openai.前缀;例如,输入workload_subjectwith expressionassertion.subto createopenai.workload_subject。对于已经以以下内容开头的原始令牌声明openai.映射键,已以openai.开头的原始令牌声明将被忽略,除非配置了匹配的转换。
设置服务账户映射
-
创建服务账户映射。 设置 名称 为 Workload Identity Provider 中的唯一值,例如
openai-mapping-kubernetes值。使用 描述,例如Workload Identity Provider Mapping for Kubernetes Workloads,以说明哪个工作负载可以使用此映射。 -
匹配 Kubernetes 服务账号主体。 设置 键 to
suband 值 tosystem:serviceaccount:default:openai-wif。对于 Kubernetes 服务账号,主体格式为system:serviceaccount:<namespace>:<service-account-name>. -
选择 OpenAI 目标。 设置 项目 指向拥有目标服务账户的 OpenAI 项目。将 服务账户 为 Kubernetes 工作负载可以使用的 OpenAI 服务账号,例如
kubernetes-prod-openai-wif如果您希望为此映射创建新的服务账号而不是复用现有账号,请选中Create a new service account in this project如果您希望为此映射创建新的服务账号,而不是复用现有账号。 -
根据需要缩小 API 权限范围。 选择适当的 权限 例如
api.model.requestandapi.vector_store.read以进一步限制从此映射生成的访问令牌。将权限留空可避免添加特定于 WIF 的范围限制;该令牌仍会以映射的服务账户身份进行授权。
在代码中使用令牌
配置你的 OpenAI SDK 客户端,以读取投射的 Kubernetes 令牌并将其换取 OpenAI 颁发的访问令牌。
使用已挂载的令牌路径(例如 /var/run/secrets/tokens/token,作为 SDK 工作负载身份联合提供者的主体令牌源。SDK 会将该 Kubernetes 令牌换取为 OpenAI 颁发的访问令牌,并使用该 OpenAI 令牌来验证 API 请求。
以下示例使用自定义主体令牌提供者初始化 OpenAI 客户端。该提供者会从挂载的文件路径中读取投射的 Kubernetes 服务账号令牌,并将其用作工作负载身份联合的主体令牌。
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 mountedServiceAccountTokenProvider(path: string): SubjectTokenProvider {
return {
tokenType: "jwt",
getToken: async () => {
const token = (await readFile(path, "utf8")).trim();
if (!token) {
throw new Error("The mounted service account token file is empty.");
}
return token;
},
};
}
const client = new OpenAI({
workloadIdentity: {
identityProviderId,
serviceAccountId,
provider: mountedServiceAccountTokenProvider(tokenPath),
},
});
const response = await client.responses.create({
model: "gpt-4.1-mini",
input: "Say hello from Kubernetes workload identity federation.",
});
console.log(response.output_text);Kubernetes 最佳实践
- 使用稳定的 OIDC 颁发者。颁发者 URL 必须与投射的服务账号令牌
iss声明相匹配,并且在集群升级和维护操作期间应保持稳定。 - 妥善保管签名密钥。任何拥有集群服务账号签名密钥访问权限的人都可以伪造令牌,而这些令牌可能会被 OpenAI 接受。
- 为 OpenAI 集成使用专门的服务账号。避免复用同时也用于不相关基础设施或应用访问的服务账号。
- 保持上传的 JWKS 处于最新状态。OpenAI 使用配置的 JWKS 在本地 JWKS 模式下验证工作负载身份令牌,因此在轮换到新的签名密钥之前,请先更新工作负载身份提供者。
- 尽量减少自定义声明的复杂性。建议优先匹配标准声明,例如
subandaud,或直接从这些声明派生的转换属性。 - 将命名空间的归属管理视为安全模型的一部分。如果命名空间管理员可以创建服务账号,请确保映射范围已进行适当的限定,以防止意外的权限提升。
- 监控颁发者和签名密钥的变更。在未更新工作负载身份提供者 JWKS 的情况下轮换签名密钥可能会导致令牌交换失败。