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。撤销信任不应被视为所有已签发短期凭据立即失效,资源访问仍需验证。