English
主导航

旧版 API

为 GitHub Actions 配置工作负载身份联合

通过将 GitHub 颁发的 OIDC 令牌交换为短期 OpenAI 访问令牌,将 GitHub Actions 用作工作负载身份提供商。这使得工作流无需在 GitHub 密钥中存储长期有效的 API 密钥即可向 OpenAI API 进行身份验证。

对于拥有 id-token: write 权限并请求身份令牌的工作流作业,GitHub 可以生成已签名的 OIDC JWT。OpenAI 在颁发 OpenAI 访问令牌之前,会验证令牌颁发者、受众、签名和映射属性。

设置 GitHub Actions

授予工作流或作业请求 GitHub OIDC 令牌的权限:

1
2
3
permissions:
  id-token: write
  contents: read

The id-token: write 权限允许作业请求 OIDC JWT。它不授予对存储库内容的写入权限。 contents: read 权限是以下操作所需的 actions/checkout.

使用在 OpenAI 工作负载身份提供商中配置的完全一致的受众来请求令牌。自定义 JavaScript 操作可以调用 core.getIDToken("your-wif-audience");Shell 步骤可以直接调用 GitHub 的 OIDC 请求 URL。包含保留 URL 字符(例如 https://api.openai.com/v1)的受众值在附加到请求 URL 之前应进行 URL 编码:

1
2
3
4
5
AUDIENCE="https://api.openai.com/v1"
ENCODED_AUDIENCE=$(jq -rn --arg audience "$AUDIENCE" '$audience | @uri')

TOKEN=$(curl -sSf -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
  "${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=${ENCODED_AUDIENCE}" | jq -r .value)

重要的 GitHub OIDC 声明包括:

  • iss:令牌颁发者。对于 GitHub Actions,它是 https://token.actions.githubusercontent.com.
  • aud:工作流请求的受众值。请将 OpenAI 配置为要求您所请求的精确值,例如 your-wif-audience or https://api.openai.com/v1.
  • sub:主主体字符串。GitHub 根据工作流元数据(例如存储库、分支、标签、拉取请求或环境)构建此字符串。
  • repository:运行工作流的存储库,例如 my-org/my-repo.
  • repository_owner:拥有存储库的组织或用户,例如 my-org.
  • ref:触发工作流的 Git 引用,例如 refs/heads/main or refs/tags/v1.0.0.
  • workflow:工作流声明。请使用 GitHub 发出的实际声明值,例如 deploy 如果那就是你工作中的工作流诉求。
  • workflow_ref:工作流文件路径和引用,例如 my-org/my-repo/.github/workflows/deploy.yml@refs/heads/main.
  • environment:GitHub 环境名称,例如 production,当作业使用环境时。
  • run_id, run_number, run_attempt,且 job_workflow_ref:运行和作业标识符,可用于审计或更高级的信任规则。

有关完整的声明列表和主体格式,请参阅 GitHub 的 OpenID Connect 参考.

验证令牌

在配置工作负载身份联合之前,请解码工作流运行器中的示例 GitHub OIDC 令牌并检查其声明。在工作流步骤中请求令牌后:

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

此命令解码 JWT 负载而不验证令牌签名。对于生产环境令牌,请使用本地解码器,并避免将生产环境令牌粘贴到第三方工具中。切勿记录原始的 GitHub OIDC 令牌或交换后得到的 OpenAI 访问令牌。

解码后的 GitHub Actions OIDC token 示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
  "iss": "https://token.actions.githubusercontent.com",
  "aud": "https://api.openai.com/v1",
  "sub": "repo:my-org/my-repo:environment:production",
  "repository": "my-org/my-repo",
  "repository_owner": "my-org",
  "ref": "refs/heads/main",
  "workflow": "deploy",
  "workflow_ref": "my-org/my-repo/.github/workflows/deploy.yml@refs/heads/main",
  "environment": "production",
  "run_id": "1234567890",
  "run_attempt": "1"
}

声明中显现。 iss, aud, repository, ref,且 workflow_ref 设置工作负载身份联合

在 OpenAI 中为 AWS 账户颁发者创建工作负载身份提供商,然后添加与 AWS 颁发令牌中的稳定声明相匹配的服务账户映射。

在 OpenAI 中为 GitHub Actions 创建工作负载身份提供者,然后添加与您信任的 GitHub 工作流声明相匹配的服务账号映射。

设置工作负载身份提供商

创建工作负载身份提供商。

  1. 为一个唯一的值,例如 设置 名称 设置颁发者和受众。 github-actions-prod值。使用 描述,例如 Production GitHub Actions workflows,以帮助管理员识别该提供商。

  2. OIDC 颁发者 URL 设置 设置为启用出站身份联合时返回的 AWS 特定于账户的颁发者 URL。此值必须与令牌的 to https://token.actions.githubusercontent.com。将 为传递给 设置为您的工作流请求的确切受众,例如 your-wif-audience or https://api.openai.com/v1.

  3. 使用 GitHub OIDC 发现。 保持禁用 使用上传的 JWKS 进行令牌验证 已禁用。OpenAI 使用 GitHub 的 OIDC 发现元数据和 JWKS 来验证 GitHub 签名的令牌。

  4. 仅在需要派生映射属性时才添加属性转换。 原始 GitHub 声明,例如 repository, ref,且 workflow 可以直接在映射断言中使用。如果您创建了派生属性,控制台会自动应用 openai. 前缀;例如,输入 github_repository with expression assertion.repository to create openai.github_repository。对于已经以以下内容开头的原始令牌声明 openai. 映射键,已以 openai. 开头的原始令牌声明将被忽略,除非配置了匹配的转换。

设置服务账户映射

  1. 创建服务账户映射。 设置 名称 为 Workload Identity Provider 中的唯一值,例如 github-actions-main-deploy值。使用 描述,例如 Production deploy workflow on main,用于说明哪个工作流可以使用该映射。

  2. 添加精确的声明断言。 为每个必须匹配的 GitHub 声明添加一行 and 。OpenAI 要求每个已配置的行都匹配后,才会颁发访问令牌。对于生产部署工作流,请使用类似以下的断言:

    iss == "https://token.actions.githubusercontent.com"
    aud == "https://api.openai.com/v1"
    repository == "my-org/my-repo"
    ref == "refs/heads/main"
    workflow_ref == "my-org/my-repo/.github/workflows/deploy.yml@refs/heads/main"

    在适合工作流的地方,优先选择 workflow_ref 优于 workflow 用于特权映射,因为管理员通常打算信任特定的工作流文件路径和引用。工作流名称可以被重命名,并且多个工作流文件可以共享相同的名称。

    在映射界面中,将它们作为键/值行输入,例如 repository with my-org/my-repo, ref with refs/heads/main,且 workflow_ref with my-org/my-repo/.github/workflows/deploy.yml@refs/heads/main。如果作业使用了 GitHub 环境,还需添加 environment with production.

    Caution: 避免使用过于宽泛的映射,例如仅信任 repository_owner == "my-org",除非该所有者命名空间下的每个存储库都应该能够生成 OpenAI 访问令牌。

  3. 选择 OpenAI 目标。 设置 项目 指向拥有目标服务账户的 OpenAI 项目。将 服务账户 到 GitHub 工作流可以使用的 OpenAI 服务账号,例如 github-actions-prod-deploy.

  4. 根据需要缩小 API 权限范围。 选择适当的 权限 例如 api.model.request and api.vector_store.read 以进一步限制从此映射生成的访问令牌。将权限留空可避免添加特定于 WIF 的范围限制;该令牌仍会以映射的服务账户身份进行授权。

在工作流中使用令牌

配置您的 OpenAI SDK 客户端以请求 GitHub OIDC 令牌,并将其交换为 OpenAI 颁发的访问令牌。

The workflow must grant id-token: write 权限,并将工作负载身份联合设置传递给 SDK 代码。SDK 将从以下位置请求 GitHub OIDC 令牌: ACTIONS_ID_TOKEN_REQUEST_URL and ACTIONS_ID_TOKEN_REQUEST_TOKEN GitHub 向作业公开的环境变量,然后使用交换得到的 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
name: deploy

on:
  push:
    branches:
      - main
  workflow_dispatch:

permissions:
  id-token: write
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v4

      - name: Run OpenAI SDK code
        env:
          OPENAI_WIF_AUDIENCE: ${{ vars.OPENAI_WIF_AUDIENCE }}
          OPENAI_IDENTITY_PROVIDER_ID: ${{ vars.OPENAI_IDENTITY_PROVIDER_ID }}
          OPENAI_SERVICE_ACCOUNT_ID: ${{ vars.OPENAI_SERVICE_ACCOUNT_ID }}
        run: node ./scripts/call-openai.js

OPENAI_WIF_AUDIENCE, OPENAI_IDENTITY_PROVIDER_ID,且 OPENAI_SERVICE_ACCOUNT_ID 存储为 GitHub Actions 变量。它们用于标识提供商和服务账户,但并非持有者凭据。

以下示例使用自定义主题令牌提供程序初始化 OpenAI 客户端。该提供程序会为配置的受众请求 GitHub OIDC 令牌,并将其用作工作负载身份联合的主题令牌。

使用 GitHub Actions OIDC 令牌进行身份验证
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
59
60
61
62
63
64
65
66
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 requestURL = process.env.ACTIONS_ID_TOKEN_REQUEST_URL;
const requestToken = process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN;

if (
  !identityProviderId ||
  !serviceAccountId ||
  !audience ||
  !requestURL ||
  !requestToken
) {
  throw new Error(
    "Set OPENAI_IDENTITY_PROVIDER_ID, OPENAI_SERVICE_ACCOUNT_ID, OPENAI_WIF_AUDIENCE, and run inside GitHub Actions with id-token: write"
  );
}

function githubActionsOIDCTokenProvider(
  requestURL: string,
  requestToken: string,
  audience: string
): SubjectTokenProvider {
  return {
    tokenType: "jwt",
    getToken: async () => {
      const url = new URL(requestURL);
      url.searchParams.set("audience", audience);

      const response = await fetch(url, {
        headers: { Authorization: `bearer ${requestToken}` },
      });

      if (!response.ok) {
        throw new Error(
          `Failed to request GitHub OIDC token: ${response.status} ${response.statusText}`
        );
      }

      const body = (await response.json()) as { value?: string };
      if (!body.value) {
        throw new Error("GitHub OIDC token response did not include a value.");
      }

      return body.value;
    },
  };
}

const client = new OpenAI({
  workloadIdentity: {
    identityProviderId,
    serviceAccountId,
    provider: githubActionsOIDCTokenProvider(requestURL, requestToken, audience),
  },
});

const response = await client.responses.create({
  model: "gpt-4.1-mini",
  input: "Say hello from GitHub Actions workload identity federation.",
});

console.log(response.output_text);

GitHub Actions 最佳实践

  • 对生产部署使用环境保护。在工作流访问生产 OpenAI 资源之前,要求通过审批或分支限制。
  • 按仓库限制映射。尽可能匹配特定于仓库的声明,而不是允许组织内所有仓库进行访问。
  • 按分支或工作流限制映射。考虑匹配诸如 repository, ref, environment, or workflow_ref to limit token issuance.
  • 为 CI/CD 和生产工作负载使用单独的 OpenAI 服务账户。构建流水线通常与已部署的应用程序需要不同的权限。
  • 避免向不受信任的 fork 中的拉取请求授予访问权限。来自 fork 的拉取请求可能会执行受攻击者控制的代码,因此不应向其提供生产凭据。
  • 使用短期交换。GitHub OIDC 令牌用于临时身份验证,应仅在需要时进行交换。
  • 审计仓库所有权变更。仓库的转移、重命名和权限更改可能会影响现有映射背后的安全假设。
  • 优先使用精确声明匹配。匹配诸如 repository, ref,且 environment 而不是依赖于组织范围的信任关系。