Federation:Pool、Provider 与 token 文件

验证:未记录
适用版本
未记录
验证范围
尚未登记验证记录。

Workload Identity Federation 的 OIDC 文件配置参考。

推荐做法#

  • Provider 约束 issuer、audience、仓库/分支/工作流;业务访问权另由 IAM 授予。

同一 Pool 共享身份命名空间,多个 Provider 的 subject / 属性碰撞可能把不同来源当成同一身份。 映射要能区分来源;不互信的环境使用独立 Pool。

  • token 文件保存短期证明,external_account JSON 指定来源和交换目标;不上传真实 token。

常用操作#

替换为实际资源;本段不依赖 lab.env。

PROJECT_ID='REPLACE_PROJECT_ID'
POOL='REPLACE_POOL'
PROVIDER='REPLACE_PROVIDER'

gcloud iam workload-identity-pools providers describe "$PROVIDER" \
  --project="$PROJECT_ID" --location=global --workload-identity-pool="$POOL"

容易踩的坑#

  • create-cred-config 只生成配置,不签发外部 token 或授予权限。
  • GitHub 的 OIDC 请求环境变量仅存在于 runner,本机不能直接照抄。

发布工作流

配置说明与完整示例(按需展开)

1. 要验证什么#

本篇对应 凭据 JSON 参考 的 external_account JSON:外部 token 文件保存 GitHub 签发的短期身份证明,凭据配置 JSON告诉认证工具去哪里读它、如何与 GCP 交换凭据。JSON 本身不是 token,也不包含 SA 私钥。

GitHub Actions 签发 OIDC token → 写入 oidc-token.txt
external_account 配置读取该文件 → GCP STS 验证并交换短期凭据
impersonate lab-oidc-reader → 读取 Bucket 中的 example.txt

前置:完成 IAM 读写实验 并保留 Bucket 的 example.txt;有一个启用 Actions、默认分支为 main 的 GitHub 仓库。无需部署 Cloud Run,也无需解除禁止创建 SA 密钥的策略。以下 GCP 配置由有权限的管理员在本机完成,访问验证在 GitHub runner 中完成。

2. 本机建立信任与资源授权#

沿用环境篇的 PROJECT_ID,设置 Bucket 和 GitHub 仓库名称:

source lab.env
export IAM_BUCKET="${PROJECT_ID}-iam-lab"
export GH_REPOSITORY='替换为OWNER/REPO'
export PROJECT_NUMBER="$(gcloud projects describe "$PROJECT_ID" --format='value(projectNumber)')"
export OIDC_SA="lab-oidc-reader@${PROJECT_ID}.iam.gserviceaccount.com"

仓库的数字 ID 可用已登录的 GitHub CLI 查询(需安装 gh,首次使用先执行 gh auth login):

export GH_REPO_ID="$(gh api "repos/$GH_REPOSITORY" --jq '.id')"
export GH_OWNER_ID="$(gh api "repos/$GH_REPOSITORY" --jq '.owner.id')"

Pool 与 Provider 是什么关系#

Pool 组织外部身份,可包含多个 Provider;Provider 定义对具体外部来源的信任规则。

本实验中的对象 含义 对应命令
Pool:oidc-file-lab 容纳本实验外部身份的 identity pool workload-identity-pools create oidc-file-lab
Provider:github 信任 GitHub 签发的 OIDC token,并配置身份映射及接受条件 providers create-oidc github --workload-identity-pool=oidc-file-lab

github 是自己指定的 Provider ID;真正说明 token 来自 GitHub 的是 --issuer-uri=https://token.actions.githubusercontent.com。--attribute-mapping 把 token 中的信息映射成 GCP 可识别的身份属性,--attribute-condition 限制允许进入的仓库、分支和工作流。

Workload Identity Pool 的 Provider 目前支持以下四种类型:

Provider 类型 验证的身份证明 创建命令(省略共同前缀 gcloud iam workload-identity-pools)
OIDC(OpenID Connect) 外部平台签发的 OIDC token,例如 GitHub Actions、GitLab providers create-oidc
AWS 使用 AWS 身份凭据生成的签名请求 providers create-aws
SAML(Security Assertion Markup Language) 外部身份系统签发的 SAML 断言 providers create-saml
X.509 X.509 客户端证书 providers create-x509

create-oidc 是类型,github 是自定义 ID;同种类型可接入不同来源。上表只针对 Workload,不适用于 Workforce。Provider 命令

多个仓库可共用 Provider,通过条件与 IAM binding 限权;本实验只信任指定仓库。

同 Pool 共用身份命名空间:不同 Provider 映射出相同 google.subject 或自定义属性,可能匹配同一 IAM 授权。需隔离时使用不同 Pool;创建 Provider 本身不授予 Bucket 权限。映射实践

创建并授权#

操作者需要启用 API、创建 SA、管理 identity pool /Provider、修改 SA 及 Bucket 的 IAM 的权限;这些管理权限不授予实验 SA。创建本实验专用身份和信任规则:

gcloud services enable iam.googleapis.com sts.googleapis.com iamcredentials.googleapis.com storage.googleapis.com --project="$PROJECT_ID"
gcloud iam service-accounts create lab-oidc-reader --project="$PROJECT_ID"
gcloud iam workload-identity-pools create oidc-file-lab --location=global --project="$PROJECT_ID"
gcloud iam workload-identity-pools providers create-oidc github \
  --workload-identity-pool=oidc-file-lab --location=global --project="$PROJECT_ID" \
  --issuer-uri=https://token.actions.githubusercontent.com \
  --attribute-mapping='google.subject=assertion.sub,attribute.repository_id=assertion.repository_id' \
  --attribute-condition="assertion.repository_owner_id == '$GH_OWNER_ID' && assertion.repository_id == '$GH_REPO_ID' && assertion.ref == 'refs/heads/main' && assertion.environment == 'gcp-oidc-lab' && assertion.event_name == 'workflow_dispatch' && assertion.workflow_ref == '$GH_REPOSITORY/.github/workflows/gcp-oidc-file.yml@refs/heads/main'"
gcloud iam service-accounts add-iam-policy-binding "$OIDC_SA" \
  --project="$PROJECT_ID" --role=roles/iam.workloadIdentityUser \
  --member="principalSet://iam.googleapis.com/projects/$PROJECT_NUMBER/locations/global/workloadIdentityPools/oidc-file-lab/attribute.repository_id/$GH_REPO_ID"
gcloud storage buckets add-iam-policy-binding "gs://$IAM_BUCKET" \
  --member="serviceAccount:$OIDC_SA" --role=roles/storage.objectViewer

这里用 workloadIdentityUser 允许受信任的外部身份 impersonate SA;它与本机人类用户使用 Token Creator 的实验不同。SA 自身只获得 Bucket 读取 role。首次授权需等待传播,通常约 2 分钟,也可能更久。流水线 federated identity

3. 在 GitHub runner 获取 token 并生成配置#

在仓库 Settings → Environments 创建 gcp-oidc-lab,限定只允许 main 部署,按团队需求配置审核保护。为该 Environment 添加三个 Variables:GCP_PROJECT(项目 ID)、GCP_PROJECT_NUMBER(上面的项目编号)、GCP_IAM_BUCKET(Bucket 名,不带 gs://)。这些是标识信息,不需要添加 SA 私钥 Secret。

将下面内容保存为仓库的 .github/workflows/gcp-oidc-file.yml,提交到 main。固定文件名、分支和 Environment 与第 2 节的信任条件对应;修改时同步调整 Provider 条件。保护该工作流及 main 的修改权限,有权修改获信任工作流的人也可能使用它的云访问权限。

name: GCP OIDC file lab
on:
  workflow_dispatch:
permissions: {}
jobs:
  read-bucket:
    runs-on: ubuntu-latest
    environment: gcp-oidc-lab
    timeout-minutes: 10
    permissions:
      id-token: write
    env:
      PROJECT_ID: ${{ vars.GCP_PROJECT }}
      PROJECT_NUMBER: ${{ vars.GCP_PROJECT_NUMBER }}
      IAM_BUCKET: ${{ vars.GCP_IAM_BUCKET }}
    steps:
    - uses: google-github-actions/setup-gcloud@aa5489c8933f4cc7a4f7d45035b3b1440c9c10db # v3
    - name: Read through file-sourced federation
      shell: bash
      run: |
        set -euo pipefail
        umask 077
        export TOKEN_FILE="$RUNNER_TEMP/oidc-token.txt"
        export CRED_FILE="$RUNNER_TEMP/external-account.json"
        export CLOUDSDK_CONFIG="$RUNNER_TEMP/gcloud-oidc-file-lab"
        export WIF_PROVIDER="projects/$PROJECT_NUMBER/locations/global/workloadIdentityPools/oidc-file-lab/providers/github"
        export OIDC_AUDIENCE="https://iam.googleapis.com/$WIF_PROVIDER"
        trap 'rm -f "$TOKEN_FILE" "$CRED_FILE"; rm -rf "$CLOUDSDK_CONFIG"' EXIT

        # GitHub runner 专用环境变量:向 GitHub 请求短期 OIDC token,只写文件。
        python3 - <<'PY'
        import json, os, urllib.parse, urllib.request
        url = os.environ["ACTIONS_ID_TOKEN_REQUEST_URL"]
        separator = "&" if "?" in url else "?"
        url += separator + urllib.parse.urlencode({"audience": os.environ["OIDC_AUDIENCE"]})
        request = urllib.request.Request(url, headers={
            "Authorization": "Bearer " + os.environ["ACTIONS_ID_TOKEN_REQUEST_TOKEN"]
        })
        with urllib.request.urlopen(request, timeout=30) as response:
            token = json.load(response)["value"]
        with open(os.environ["TOKEN_FILE"], "w") as output:
            output.write(token)
        PY

        # 生成 type=external_account 的配置;文件来源是刚取得的 GitHub token。
        gcloud iam workload-identity-pools create-cred-config "$WIF_PROVIDER" \
          --service-account="lab-oidc-reader@$PROJECT_ID.iam.gserviceaccount.com" \
          --credential-source-file="$TOKEN_FILE" \
          --subject-token-type=urn:ietf:params:oauth:token-type:jwt \
          --output-file="$CRED_FILE"

        # 只展示结构标识,不打印 token 或完整凭据。
        python3 - <<'PY'
        import json, os
        with open(os.environ["CRED_FILE"]) as source:
            config = json.load(source)
        assert config["type"] == "external_account"
        print("type:", config["type"])
        print("credential_source.file:", config["credential_source"]["file"])
        PY

        # CLI 明确加载此配置;ADC 应用则通过环境变量读取同一配置。
        export GOOGLE_APPLICATION_CREDENTIALS="$CRED_FILE"
        gcloud auth login --cred-file="$CRED_FILE" --quiet
        gcloud storage cat "gs://$IAM_BUCKET/example.txt" --project="$PROJECT_ID"

id-token: write 允许向 GitHub 申请 OIDC token,不授予 GCP 写权限。两个 ACTIONS_ID_TOKEN_REQUEST_* 变量由 GitHub 注入,仅在 runner 可用。OIDC 参数

TOKEN_FILE 与 CRED_FILE 的内容#

以下仅展示文件格式,不可直接用于认证:

TOKEN_FILE($RUNNER_TEMP/oidc-token.txt)中是一行字符串,三个 Base64URL 编码部分由句点分隔,没有 JSON 外壳,也不带 Bearer 前缀:

<编码后的header>.<编码后的payload>.<签名>

将 JWT 中间的 payload 解码后可见以下字段;token 文件仍保存上面的字符串:

{
  "iss": "https://token.actions.githubusercontent.com",
  "aud": "https://iam.googleapis.com/projects/<PROJECT_NUMBER>/locations/global/workloadIdentityPools/oidc-file-lab/providers/github",
  "sub": "<GitHub为当前任务生成的subject>",
  "repository_owner_id": "<GH_OWNER_ID>",
  "repository_id": "<GH_REPO_ID>",
  "ref": "refs/heads/main",
  "environment": "gcp-oidc-lab",
  "event_name": "workflow_dispatch",
  "workflow_ref": "<OWNER>/<REPO>/.github/workflows/gcp-oidc-file.yml@refs/heads/main",
  "iat": 1791594000,
  "exp": 1791594300
}

iat 和 exp 是签发、过期的 Unix 时间戳;这里只展示部分字段,实际值由 GitHub 生成。解码不等于验签,GCP 仍需验证签名和各项条件。不要把真实 token 打印到工作流日志或提交到仓库。

CRED_FILE($RUNNER_TEMP/external-account.json)是 create-cred-config 生成的 JSON,关键结构如下;实际工具版本可能附加其他字段:

{
  "type": "external_account",
  "audience": "//iam.googleapis.com/projects/<PROJECT_NUMBER>/locations/global/workloadIdentityPools/oidc-file-lab/providers/github",
  "subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
  "token_url": "https://sts.googleapis.com/v1/token",
  "credential_source": {
    "file": "/home/runner/work/_temp/oidc-token.txt"
  },
  "service_account_impersonation_url": "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/lab-oidc-reader@<PROJECT_ID>.iam.gserviceaccount.com:generateAccessToken"
}
配置字段 本实验中的作用
type 标明这是外部身份凭据配置
audience 指定向 GCP 的哪个 Provider 进行凭据交换;这里是 STS 的目标参数,区别于上面 GitHub JWT 自身的 aud 字段
subject_token_type 声明输入的 GitHub token 是 JWT,不表示本配置文件是 JWT
token_url GCP STS 凭据交换 endpoint
credential_source.file 指向 TOKEN_FILE 的实际路径;配置不复制 token 内容
service_account_impersonation_url 换取凭据后,继续获取指定 SA 的短期访问令牌

认证时工具按 CRED_FILE 读取 TOKEN_FILE,经 STS 交换并 impersonate SA,最后用 SA access token 读取 Bucket。配置生成

4. 验收与排错#

在 Actions → GCP OIDC file lab → Run workflow 选择 main 运行。日志应显示 type: external_account、token 文件路径和 hello IAM,且没有要求登录 Google 用户或提供 SA 私钥。隔离的 CLOUDSDK_CONFIG 避免复用其他 CLI 登录;文件位于 runner 临时目录,step 结束会清理,不上传为 artifact。

本示例的文件来源不会自动更新 token,适合短时实验;token 过期后重新运行 Job 获取新 token,不能把下载的旧 token 当长期凭据使用。正式流水线通常使用 认证 Action 管理这一流程。

  • token 获取失败:检查 Job 的 id-token: write,以及是否在 GitHub runner 内执行。
  • STS 交换失败:检查 audience、仓库数字 ID、main 分支、Environment、工作流路径及 Provider 是否启用。
  • Service Account impersonation 失败:检查 workloadIdentityUser 绑定及 IAM 传播。
  • Bucket 读取被拒绝:检查 SA 的 Object Viewer 授权、Bucket 名与对象是否存在;应区分认证失败和资源授权失败。

可选负向实验:在另一分支使用相同工作流发起运行。Environment 分支限制应先拒绝;即使其限制被遗漏,Provider 的 main 条件也应拒绝身份交换。不要把网络超时或 token 过期算作权限验证通过。

5. 清理#

先删除或禁用该工作流,再由管理员执行下列命令;保留原 IAM 实验的 Bucket 和 reader:

gcloud storage buckets remove-iam-policy-binding "gs://$IAM_BUCKET" \
  --member="serviceAccount:$OIDC_SA" --role=roles/storage.objectViewer
gcloud iam workload-identity-pools delete oidc-file-lab --location=global --project="$PROJECT_ID"
gcloud iam service-accounts delete "$OIDC_SA" --project="$PROJECT_ID"

同时删除 GitHub 的实验 Environment 及其 Variables。撤销信任不应被视为所有已签发短期凭据立即失效,资源访问仍需验证。

搜索文档

输入关键词,搜索所有文档。

↑ ↓ 选择 · Enter 打开 · Esc 关闭