Docker 支持 GitHub Actions OIDC,CI/CD 告别长期密钥
2026 年 7 月 31 日,Docker 官方博客宣布为 GitHub Actions 提供 OIDC(OpenID Connect)连接。这一能力让 CI/CD 流水线可以用"短时、每次运行独立"的令牌完成 Docker Hub 认证,替代过去长期存储在仓库里的个人访问令牌(PAT)或组织访问令牌(OAT)。
对靠 GitHub Actions 自动构建镜像的团队来说,这几乎是零成本的安全升级:不用改构建逻辑,不用新增基础设施,只换一种认证方式,就把"泄露一次密钥等于账号裸奔"的风险大幅压下去。尤其对同时维护十几个仓库、几十条流水线的团队,过去散落在各仓库 Secret 里的 PAT,现在统一收口到组织级的连接配置里,审计和权限回收都省心得多。
长期密钥的痛点
每个需要推送或拉取 Docker 镜像的 GitHub Actions 工作流,过去都要用一个存成 GitHub Secret 的 PAT 或 OAT 来认证。这些凭据长期有效,一旦泄露,攻击者可以拉取私有镜像、甚至推送恶意镜像,而且这种访问会一直持续到被发现并撤销。轮换靠人工,随着流水线变多,要追踪的凭据越来越多,过期令牌也常常成为审计中的常见问题。
OIDC 方案的思路,与 AWS 的 GitHub Actions OIDC、GCP 的 Workload Identity Federation 一脉相承:不再信任"你存了什么",而是信任"你来自哪里"。
| 维度 | 传统 PAT/OAT | Docker OIDC 连接 |
|---|---|---|
| 令牌有效期 | 数月到数年 | 单次运行,几分钟过期 |
| 存储方式 | 仓库 Secret | 不存储,运行时动态签发 |
| 泄露影响 | 长期可复用 | 一次泄露即失效 |
| 权限范围 | 组织/账号级 | ruleset 限定的资源 |
| 轮换成本 | 人工,易遗漏 | 无需轮换 |
工作流程是怎样的
- GitHub 签发一个带签名的身份令牌(JWT),内含仓库、分支、环境等本次运行的信息;
- 工作流通过
docker/login-action把令牌提交给 Docker; - Docker 用 GitHub 公钥验证签名,并按管理员在控制台配置的 ruleset 规则比对;
- 校验通过后,Docker 返回一个短时有效、且作用域限定在该 ruleset 资源上的访问令牌;
- 后续的
docker pull、docker push、docker build照常工作。
整个过程中没有任何存储的密钥或 API token,短时令牌几分钟内过期且无法复用。
迁移步骤
- 创建连接:登录 Docker Home,进入组织的 OIDC 连接页面,创建连接并配置 ruleset(每个连接最多 5 条),用
repo:组织/仓库:ref:refs/heads/main这类 subject claim 精确锁定仓库与分支。 - 更新工作流:加上
permissions: id-token: write,并在docker/login-action中通过DOCKERHUB_OIDC_CONNECTIONID指定连接 ID。 - 验证并清理:跑一次流水线确认成功,然后从仓库 Secrets 中删除旧的 PAT/OAT。
改造后的工作流大致长这样:
name: build-and-push
on:
push:
branches: [main]
permissions:
contents: read
id-token: write # 让工作流能拿到本次运行的 OIDC 令牌
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
oidc-connection-id: ${{ secrets.DOCKERHUB_OIDC_CONNECTIONID }}
- run: |
docker build -t myorg/app:latest .
docker push myorg/app:latest
oidc-connection-id 指向管理员在控制台创建的连接,GitHub 侧不需要再存任何长期 Docker 凭据——仓库里那个 DOCKERHUB_TOKEN 用完即可删除。对多仓库、多分支的团队,建议按"生产/预发"分别建连接,把 ruleset 的 subject claim 写得更细,避免一个连接覆盖过多资源。
迁移中的几个注意点
- 规则匹配要留足空间:ruleset 用 subject claim 精确匹配仓库和分支,写太宽会失去"限定范围"的意义,写太死(比如把 ref 锁死在
refs/heads/main)又会让 PR 预览、feature 分支构建失败。常见做法是对发布分支单独建一条严格 ruleset,其他分支用一条宽松些的兜底。 - 失败排查有迹可循:如果登录步骤报
oidc相关错误,先去 Docker 控制台的 OIDC 连接页面看最近尝试记录,对照 GitHub 返回的 subject claim 是否落在 ruleset 里,多半是"分支没匹配上"或"连接 ID 填错"。 - 权限最小化同样适用:OIDC 只解决"认证",不改变"授权"。给连接绑定的组织账号/命名空间权限仍然按最小可用原则配置,别顺手开成全组织可写。
- 先小后大:挑一个低风险仓库先迁移跑通,确认日志、镜像标签都正常后再铺开到核心流水线,出问题回退也快——毕竟旧 PAT 在清理前一直是可用的。
需要说明的是:现有 PAT/OAT 依然可用,迁移完全按你自己的节奏进行;本地开发和其他 CI 提供方仍使用 PAT/OAT,Docker 会视需求逐步扩展到更多 CI 平台。
16IDC 观察
"密钥上云、身份下沉"正在成为部署自动化的默认姿势。对站点与 SaaS 团队来说,把 CI 凭据从"长期静态"改为"短期动态"是性价比极高的安全改进——它不改变构建方式,却显著缩小了密钥泄露的爆炸半径。想系统搭建流水线,可以先看GitHub Actions CI/CD 指南;容器与编排基础可以参考Docker 部署入门和Kubernetes 入门指南。更多环境部署内容请查看环境部署分类。
原文来源:https://www.docker.com/blog/docker-oidc-connections-for-github-actions-available-for-docker-orgs/
参考:https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/about-security-hardening-with-openid-connect(GitHub OIDC 安全加固文档)