首页 GitHub Actions OIDC + AWS 集成踩坑实录
文章
取消

GitHub Actions OIDC + AWS 集成踩坑实录

用 GitHub Actions 做 CI/CD 时,通过 OIDC 直接获取 AWS 临时凭证是官方推荐的做法——省去了管理长期 Access Key 的麻烦。但在实际配置过程中,几个不起眼的小细节就能卡住半天。

问题现象

Terraform CI 流水线的第一步 configure-aws-credentials 反复重试后失败:

1
2
3
4
Assuming role with OIDC
Assuming role with OIDC
...
Error: Could not assume role with OIDC: Not authorized to perform sts:AssumeRoleWithWebIdentity

sts:AssumeRoleWithWebIdentity 被拒绝,说明请求到达了 AWS STS,但 IAM 信任策略不允许这次身份交换。

排查过程

Step 1:信任策略看起来没问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
  "Effect": "Allow",
  "Principal": {
    "Federated": "arn:aws:iam::xxxx:oidc-provider/token.actions.githubusercontent.com"
  },
  "Action": "sts:AssumeRoleWithWebIdentity",
  "Condition": {
    "StringLike": {
      "token.actions.githubusercontent.com:sub": "repo:my-org/my-repo:*"
    },
    "StringEquals": {
      "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
    }
  }
}

从格式上看,OIDC provider ARN 指向 token.actions.githubusercontent.comaud 设为 sts.amazonaws.comsub 用了通配符——都是标准配置,看不出问题。

Step 2:角色 ARN 确认正确

检查 GitHub Secrets 中的 AWS_ROLE_ARN,与管理账号中的 IAM Role ARN 一致。

Step 3:OIDC Provider 证书指纹

这里我用了 thumbprint_list = [],想用根 CA 验证代替硬编码指纹。但搜索后发现,GitHub 在 2025–2026 年轮换了证书,旧的指纹可能已经失效。

Step 4:根因——仓库名下划线陷阱

直到检查 git remote URL 才发现:

配置
git remotegit@github.com:my-org/my_repo.git
terraform.tfvars github_repomy-org/my-repo

仓库名是 my_repo(下划线),但信任策略条件写的是 my-org/my-repo:*(横线)。

GitHub OIDC token 的 sub claim 使用实际的仓库名my_org/my_repo。信任策略里的横线版本根本不匹配,所以 STS 每次都拒绝。

两个修复

修复一:仓库名对齐

terraform.tfvars 修正为:

1
github_repo = "my-org/my_repo"

修复二:证书指纹动态获取

原来硬编码的 thumbprint_list

1
thumbprint_list = ["xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"]

改为用 data.tls_certificate 自动拉取当前指纹:

1
2
3
4
5
6
7
8
9
data "tls_certificate" "github_oidc" {
  url = "https://token.actions.githubusercontent.com"
}

resource "aws_iam_openid_connect_provider" "github" {
  url            = "https://token.actions.githubusercontent.com"
  client_id_list = ["sts.amazonaws.com"]
  thumbprint_list = [data.tls_certificate.github_oidc.certificates[0].sha1_fingerprint]
}

这样当 GitHub 轮换证书时,下次 terraform apply 会自动更新,无需手动修改代码。

修复后的连锁发现

信任策略修好后 CI 有了新进展,但紧接着又遇到了新问题。

Provider Profile 问题

主 provider 配置了 profile = "my-profile"——这在本地开发时用于指定 AWS Profile,但 CI 环境中不存在这个命名配置文件,导致 plan 失败。

解法:CI 的 terraform plan 命令追加 -var="aws_profile=",将 profile 覆盖为空,让 CI 直接使用 OIDC 获取的凭证:

1
2
- name: Terraform Plan
  run: terraform plan -var="aws_profile=" -out=tfplan

github-script 中的 steps 引用问题

CI 中有一段脚本需要判断 terraform plan 的结果是否有变更:

1
2
3
4
5
6
7
8
9
10
11
12
13
- name: Check Plan Changes
  id: plan
  run: terraform plan -no-color -out=tfplan

- name: Comment Plan
  uses: actions/github-script@v7
  env:
    PLAN_OUTPUT: $
    PLAN_OUTCOME: $
  with:
    script: |
      const output = process.env.PLAN_OUTPUT;
      const outcome = process.env.PLAN_OUTCOME;

关键在于把 steps.plan.outcome 通过 env 传进 github-script,而不是在 JavaScript 里直接引用 steps——actions/github-script 的 v7 运行时中并没有 steps 这个上下文对象。

经验总结

问题原因教训
OIDC 鉴权失败仓库名下划线/横线不匹配OIDC token 的 sub claim 使用实际仓库名,注意跟 git remote URL 对齐
证书指纹失效GitHub 轮换证书未同步data.tls_certificate 动态获取,不要硬编码
CI plan 失败Provider 配置了本地 ProfileCI 环境通过 -var 覆盖无关的变量
github-script 报错直接引用 steps通过 env 传入,避免直接访问运行时上下文

GitHub Actions OIDC 的官方推荐配置一直在更新——截止 2026 年,最佳实践是使用 data.tls_certificate 动态获取指纹,并确保 IAM Role 的信任策略中的 sub 条件与 GitHub 仓库名精确一致。如果未来 GitHub 再次轮换证书,动态获取的方式可以零改动继续工作。

本文由作者按照 CC BY 4.0 进行授权

IAM Permission Boundary 实战 - 堵住 CreateRole 提权路径

AWS Data Perimeter 实战:多层数据外泄防护体系