在以下任一场景中将 AWS 用作工作负载身份提供商:
- AWS 出站身份联合: 将 AWS STS 颁发的 OIDC JWT 从
GetWebIdentityToken兑换为短期 OpenAI 访问令牌。 - Amazon EKS: 将 Amazon EKS 投射的服务账户令牌兑换为短期 OpenAI 访问令牌。
OpenAI 支持来自出站身份联合的 AWS 颁发 OIDC JWT,以及由 Amazon EKS 颁发的 Kubernetes 投射服务账户令牌。OpenAI 不支持将 SigV4 签名请求或 AWS STS 临时访问密钥凭据作为工作负载身份联合主题令牌。
AWS 出站身份联合
AWS 出站身份联合允许 AWS 主体从 AWS STS 请求签名的 OIDC JWT,并将该令牌提供给外部服务。在 OpenAI 工作负载身份联合中,AWS 颁发的 JWT 是 OpenAI 在颁发 OpenAI 访问令牌之前验证的主题令牌。
设置 AWS 出站身份联合
为将要颁发令牌的 AWS 账户启用出站身份联合。有关设置详情,请参阅 AWS 指南: 出站身份联合入门.
aws iam enable-outbound-web-identity-federation记录 AWS 返回的特定于账户的颁发者 URL。您需要将此值配置为 OpenAI 工作负载身份提供商的颁发者,并且它必须与 iss AWS 颁发令牌中的声明相匹配。
The AWS STS GetWebIdentityToken 该 API 在 STS 全局端点上不可用。请配置 AWS CLI 或 SDK 以使用区域 STS 端点。
授予工作负载调用权限,最大生命周期为 300 秒: sts:GetWebIdentityToken。请在 IAM 中限制受众和最大令牌生命周期,以便 AWS 主体只能生成专为 OpenAI 预期的令牌。此示例允许以下受众的令牌 https://api.openai.com/v1 请求一个与您将在 OpenAI 工作负载身份提供商上配置的受众相同的 AWS 颁发 OIDC 令牌。请使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:GetWebIdentityToken",
"Resource": "*",
"Condition": {
"ForAllValues:StringEquals": {
"sts:IdentityTokenAudience": "https://api.openai.com/v1"
},
"NumericLessThanEquals": {
"sts:DurationSeconds": 300
}
}
}
]
}除非您的环境要求 ES384 验证 AWS 颁发的令牌 RS256 compatibility.
1
2
3
4
5
6
7
8
TOKEN=$(aws sts get-web-identity-token \
--audience "https://api.openai.com/v1" \
--signing-algorithm ES384 \
--duration-seconds 300 \
--tags Key=environment,Value=production \
Key=workload,Value=batch-ingest \
--query "WebIdentityToken" \
--output text)在配置工作负载身份联合之前,请在本地解码 AWS 颁发的样本令牌并检查其声明:
此命令在不验证令牌签名的情况下解码 JWT 负载。请在本地解码器中解码生产令牌,并避免将生产令牌粘贴到第三方工具中。
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 令牌类似于:
并非每个 AWS 颁发的令牌都包含所有的 AWS 特定声明。位于
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"iss": "https://abc123-def456-ghi789-jkl012.tokens.sts.global.api.aws",
"aud": "https://api.openai.com/v1",
"sub": "arn:aws:iam::123456789012:role/OpenAIWifRole",
"iat": 1716235422,
"exp": 1716235722,
"jti": "jwt-id-example",
"https://sts.amazonaws.com/": {
"aws_account": "123456789012",
"source_region": "us-west-2",
"org_id": "o-exampleorgid",
"principal_tags": {
"environment": "production"
},
"request_tags": {
"environment": "production",
"workload": "batch-ingest"
}
}
}下的声明取决于调用主体、会话上下文和请求标签。 https://sts.amazonaws.com/ 验证您计划在 OpenAI 中配置的声明:
受众以及 OpenAI 工作负载身份提供商的受众。
iss:必须与在 OpenAI 工作负载身份提供商中配置的特定 AWS 账户颁发者 URL 相匹配。aud:必须与GetWebIdentityTokenAWS 特定声明:在匹配账户、组织、主体标签或请求标签值之前,请使用解码后的令牌作为事实来源。sub:标识请求令牌的 IAM 主体 ARN。建议优先匹配精确的角色 ARN。- 使用解码后的负载将您收到的令牌与 OpenAI 中配置的颁发者、受众和映射值进行比较。大多数配置问题在您兑换令牌之前即可在
声明中显现。 iss, aud,且 sub 设置工作负载身份联合
在 OpenAI 中为 AWS 账户颁发者创建工作负载身份提供商,然后添加与 AWS 颁发令牌中的稳定声明相匹配的服务账户映射。
请先配置工作负载身份提供商,然后再创建服务账户映射。
设置工作负载身份提供商
创建工作负载身份提供商。
-
为一个唯一的值,例如 设置 名称 设置颁发者和受众。
aws-outbound-prod值。使用 描述,例如Production AWS outbound identity federation workloads,以帮助管理员识别该提供商。 -
OIDC 颁发者 URL 设置 设置为启用出站身份联合时返回的 AWS 特定于账户的颁发者 URL。此值必须与令牌的 声明相匹配。设置
iss受众 为传递给 的相同受众。GetWebIdentityToken。在此示例中,该值为https://api.openai.com/v1. -
使用 AWS OIDC 发现。 保持禁用 使用上传的 JWKS 进行令牌验证 状态。OpenAI 使用 AWS 颁发者的 OIDC 发现元数据和 JWKS 来验证 AWS 颁发的令牌。
-
仅在需要派生映射属性时才添加属性转换。 原始令牌匹配支持来自上述解码令牌示例的顶级标量声明,例如
sub,aud,且iss。AWS 特定的命名空间声明嵌套在https://sts.amazonaws.com/之下,因此在使用它们进行映射之前,请使用 CEL 括号表示法创建派生属性。例如,输入aws_environmentwith expressionassertion["https://sts.amazonaws.com/"]["principal_tags"]["environment"]to createopenai.aws_environment。在使用前,请验证样本令牌中的嵌套声明路径;如果无法评估转换,则映射解析将失败。对于openai.映射键,已以openai.开头的原始令牌声明将被忽略,除非配置了匹配的转换。
设置服务账户映射
-
创建服务账户映射。 设置 名称 为在工作负载身份提供商内唯一的值,例如
aws-role-openai-wif值。使用 描述,例如Production AWS role for OpenAI API workload,以说明哪个工作负载可以使用此映射。 -
匹配 AWS 主体。 设置 键 to
suband 值 为解码令牌中的 IAM 主体 ARN,例如arn:aws:iam::123456789012:role/OpenAIWifRole。对精确的sub声明进行匹配,可为 AWS 出站身份联合提供最强的隔离性。 -
根据需要添加额外的声明匹配。 您可以匹配任何可用的标量声明或已转换的属性。例如,如果您需要额外的信任边界,可以使用从 AWS 账户、组织、主体标签或请求标签声明派生的已转换属性。
-
选择 OpenAI 目标。 设置 项目 指向拥有目标服务账户的 OpenAI 项目。将 服务账户 设置为 AWS 工作负载可使用的 OpenAI 服务账户,例如
aws-outbound-prod-openai-wif. -
根据需要缩小 API 权限范围。 选择适当的 权限 例如
api.model.requestandapi.vector_store.read以进一步限制从此映射生成的访问令牌。将权限留空可避免添加特定于 WIF 的范围限制;该令牌仍会以映射的服务账户身份进行授权。
在代码中使用令牌
配置您的 OpenAI SDK 客户端,以向 AWS STS 请求 AWS 颁发的 OIDC 令牌,并将其换取 OpenAI 颁发的访问令牌。
设置 OPENAI_WIF_AUDIENCE 与在 OpenAI 工作负载身份提供者上配置的受众相同。主体令牌提供者将使用该受众调用 AWS STS, GetWebIdentityToken 返回 AWS 签发的 JWT 作为主体令牌,然后由 OpenAI SDK 将其交换为 OpenAI 签发的访问令牌。
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
import { GetWebIdentityTokenCommand, STSClient } from "@aws-sdk/client-sts";
import OpenAI from "openai";
import type { SubjectTokenProvider } from "openai/auth";
const identityProviderId = process.env.OPENAI_IDENTITY_PROVIDER_ID;
const serviceAccountId = process.env.OPENAI_SERVICE_ACCOUNT_ID;
const audience = process.env.OPENAI_WIF_AUDIENCE;
const awsRegion = process.env.AWS_REGION;
if (!identityProviderId || !serviceAccountId || !audience || !awsRegion) {
throw new Error(
"Set OPENAI_IDENTITY_PROVIDER_ID, OPENAI_SERVICE_ACCOUNT_ID, OPENAI_WIF_AUDIENCE, and AWS_REGION"
);
}
const sts = new STSClient({ region: awsRegion });
function awsOutboundWebIdentityTokenProvider(): SubjectTokenProvider {
return {
tokenType: "jwt",
getToken: async () => {
const response = await sts.send(
new GetWebIdentityTokenCommand({
Audience: [audience],
SigningAlgorithm: "ES384",
DurationSeconds: 300,
})
);
if (!response.WebIdentityToken) {
throw new Error("AWS STS did not return a web identity token.");
}
return response.WebIdentityToken;
},
};
}
const client = new OpenAI({
workloadIdentity: {
identityProviderId,
serviceAccountId,
provider: awsOutboundWebIdentityTokenProvider(),
},
});
const response = await client.responses.create({
model: "gpt-4.1-mini",
input: "Say hello from AWS outbound workload identity federation.",
});
console.log(response.output_text);Amazon EKS 投射服务账号令牌
通过将 EKS 签发的投射服务账号令牌交换为短期 OpenAI 访问令牌,将 Amazon EKS 用作工作负载身份提供者。
设置 EKS
为需要调用 OpenAI API 的 EKS 工作负载使用一个 Kubernetes ServiceAccount 。如果您还没有,请先创建一个:
kubectl create serviceaccount openai-wif --namespace defaultEKS 投射服务账号令牌使用格式为 sub 的 system:serviceaccount:<namespace>:<service-account-name>声明。对于上面的服务账号,其 sub 声明为 system:serviceaccount:default:openai-wif.
检索与 EKS 集群关联的 OIDC 颁发者 URL:
1
2
3
4
5
aws eks describe-cluster \
--name <cluster-name> \
--region <region> \
--query "cluster.identity.oidc.issuer" \
--output text输出示例:
https://oidc.eks.us-west-2.amazonaws.com/id/EXAMPLED539D4633E53DE1B716D3您在 OpenAI 工作负载身份提供商中配置的颁发者必须与此颁发者 URL 以及投影的 EKS 服务账号令牌中的 iss 声明相匹配。
使用 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: eks-sa-token
mountPath: /var/run/secrets/tokens
readOnly: true
volumes:
- name: eks-sa-token
projected:
sources:
- serviceAccountToken:
path: token
audience: "https://api.openai.com/v1"
expirationSeconds: 3600验证 EKS 令牌
在配置工作负载身份联合之前,请在本地解码一个示例投影服务账号令牌并检查其声明。在挂载了投影令牌的运行中的 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 令牌类似于:
解码后的 EKS 投影服务账号令牌类似于以下内容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"iss": "https://oidc.eks.us-west-2.amazonaws.com/id/EXAMPLED539D4633E53DE1B716D3",
"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 中为 EKS 颁发者创建工作负载身份提供商,然后添加与投影令牌中的属性相匹配的服务账号映射。
设置工作负载身份提供商
创建工作负载身份提供商。
-
为一个唯一的值,例如 设置 名称 设置颁发者和受众。
aws-eks-prod值。使用 描述,例如Production EKS cluster, 以帮助管理员识别集群。 -
OIDC 颁发者 URL 设置 设置为启用出站身份联合时返回的 AWS 特定于账户的颁发者 URL。此值必须与令牌的 指向由
aws eks describe-cluster --query "cluster.identity.oidc.issuer"返回的颁发者。此值必须与iss在 projected EKS 服务账号令牌中声明的。设置为 为传递给 与在 projected 服务账号令牌卷上配置的受众相同。在此示例中,该值为https://api.openai.com/v1. -
使用 EKS OIDC 发现。 保持禁用 使用上传的 JWKS 进行令牌验证 已禁用。OpenAI 使用 EKS 颁发者的 OIDC 发现元数据和 JWKS 来验证 projected 服务账号令牌。
-
仅在需要派生映射属性时才添加属性转换。 诸如以下原始令牌声明
sub,aud,且iss可以在映射断言中直接使用。例如,创建一个名为以下名称的转换属性subjectwith expressionassertion.sub。在控制面板中,输入subject作为属性名称;OpenAI 会将其存储为openai.subject,您可以在映射中引用它。Note: 已经以以下内容开头的原始令牌声明
openai.映射键,已以openai.开头的原始令牌声明将被忽略,除非配置了匹配的转换。
设置服务账户映射
-
创建服务账户映射。 设置 名称 为 Workload Identity Provider 中的唯一值,例如
openai-mapping-eks值。使用 描述,例如Workload Identity Provider Mapping for EKS Workloads,以说明哪个工作负载可以使用此映射。 -
匹配 EKS 服务账号主体。 设置 键 to
suband 值 tosystem:serviceaccount:default:openai-wif。您可以匹配任何可用的声明或转换属性。基于sub进行匹配是限制最严格的选项,因为它能唯一标识一个 Kubernetes 服务账号。 -
选择 OpenAI 目标。 设置 项目 指向拥有目标服务账户的 OpenAI 项目。将 服务账户 为 EKS 工作负载可以使用的 OpenAI 服务账号,例如
aws-eks-prod-openai-wif如果您希望为此映射创建新的服务账号而不是复用现有账号,请选中Create a new service account in this project如果您希望为此映射创建新的服务账号,而不是复用现有账号。 -
根据需要缩小 API 权限范围。 选择适当的 权限 例如
api.model.requestandapi.vector_store.read以进一步限制从此映射生成的访问令牌。将权限留空可避免添加特定于 WIF 的范围限制;该令牌仍会以映射的服务账户身份进行授权。
在代码中使用令牌
配置您的 OpenAI SDK 客户端以读取 projected EKS 服务账号令牌,并将其换取 OpenAI 颁发的访问令牌。
使用已挂载的令牌路径(例如 /var/run/secrets/tokens/token)作为 SDK 工作负载身份联合提供商的主题令牌源。SDK 会将该 EKS 令牌换取 OpenAI 颁发的访问令牌,并使用该 OpenAI 令牌来认证 API 请求。
以下示例初始化了一个带有自定义主题令牌提供商的 OpenAI 客户端。该提供商从已挂载的文件路径中读取 projected EKS 服务账号令牌,并将其用作工作负载身份联合的主题令牌。
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 mountedEksServiceAccountTokenProvider(path: string): SubjectTokenProvider {
return {
tokenType: "jwt",
getToken: async () => {
const token = (await readFile(path, "utf8")).trim();
if (!token) {
throw new Error("The mounted EKS service account token file is empty.");
}
return token;
},
};
}
const client = new OpenAI({
workloadIdentity: {
identityProviderId,
serviceAccountId,
provider: mountedEksServiceAccountTokenProvider(tokenPath),
},
});
const response = await client.responses.create({
model: "gpt-4.1-mini",
input: "Say hello from AWS workload identity federation.",
});
console.log(response.output_text);AWS 最佳实践
- 为每个工作负载使用专用的 AWS 身份。为 AWS 出站身份联合使用单独的 IAM 角色,并为 EKS 工作负载使用单独的 Kubernetes 服务账号。
- 为 OpenAI 访问配置专用的受众。在 AWS 颁发或 EKS projected 令牌以及 OpenAI Workload Identity Provider 配置中使用相同的受众值。
- 保持令牌生命周期合理简短。对于 AWS 出站身份联合,请使用 IAM 条件,例如
sts:DurationSeconds;对于 EKS,请设置合适的投射令牌过期时间。 - 优先使用精确的主体匹配。对于 AWS 出站令牌,请匹配完整的 IAM 主体 ARN;对于 EKS 令牌,请匹配完整的 Kubernetes 服务账户主体。
- 将映射范围限定在稳定的边界内。当账户、组织、命名空间或转换后的属性能够在不创建宽泛信任规则的前提下缩减访问权限时,请使用它们。
- 在交换令牌时重新加载令牌。在需要时请求 AWS 出站令牌,并从挂载文件路径读取 EKS 投射令牌,以便自动获取已轮换的令牌。
- 仅授予工作负载所需的权限。使用映射级别的权限来进一步收窄目标 OpenAI 服务账户所授予的访问权限。