干掉流水线里的长期密钥:用 OIDC 让 CI/CD 与云以临时凭证对接

每个流水线里都躺着一对 AWS AK/SK,或者一个万年不换的 kubeconfig------这不是段子,是绝大多数团队的现状。本文聊聊如何用 OIDC 把这些长期凭证从流水线里彻底清出去。

一、先算一笔账:长期密钥的真实成本

把云厂商的 Access Key 写进 CI/CD 变量里,是很多团队「让流水线能部署」的最快路径。但这条路径的隐性成本会随时间复利式增长:

  1. 泄露面不可控 。密钥一旦进入 CI/CD 变量体系,就会被带进作业环境、日志、镜像构建层,甚至被 env 一句命令打出来。流水线日志的访问面远比想象中大。
  2. 轮换是永久负债。密钥需要定期轮换,而轮换意味着同时更新 N 个项目的变量、重启 N 条流水线、祈祷没有遗漏。
  3. 权限粒度对不上。AK/SK 绑定的是 IAM 用户,与「哪个项目、哪个分支、哪次部署」没有天然对应关系。一个拥有生产写入权限的密钥被测试流水线复用,是最常见的越权姿势。
  4. 审计只能事后追。日志里只看得到同一个 AK 在操作,分不清是哪个项目哪条流水线发的。

业界对这个问题已经有共识性答案:OIDC(OpenID Connect)联合认证 。CI 系统不持有任何长期密钥,而是在每次作业运行时签发一个短生命周期的 JWT 令牌,云厂商验证令牌后换发临时凭证。极狐GitLab CI/CD 从 15.7 起原生支持这一机制(使用 ID 令牌进行 OIDC 认证)。

二、原理:一次没有密钥的握手

整个流程里没有任何静态凭证,信任建立在 JWT 的签名与声明之上:

sequenceDiagram participant GL as 极狐GitLab CI/CD participant R as Runner participant Cloud as 云厂商 / Vault GL->>R: 派发作业(含 ID Token) R->>Cloud: 携带 JWT 调用 AssumeRole / 换取凭证 Cloud->>Cloud: 用 JWKS 公钥验证签名 Cloud->>Cloud: 校验 aud / sub 声明与信任策略 Cloud->>R: 返回临时凭证(短生命周期) R->>Cloud: 用临时凭证执行部署

关键在于两个声明:

  • aud(audience) :令牌的预期受众,在 .gitlab-ci.yml 里显式声明,云厂商侧配置拒绝受众不匹配的令牌;
  • sub(subject) :作业身份的唯一描述,默认格式为 project_path:{group}/{project}:ref_type:{type}:ref:{branch_name},云侧信任策略据此做细粒度过滤。

这个设计的精髓是:「身份」不再是密钥本身,而是这次作业的上下文------哪个项目、哪个分支、是不是受保护分支,全都写进了不可伪造的令牌里。

三、落地:三步完成改造

第一步:在流水线里声明 ID Token

ID 令牌通过 id_tokens 关键字配置,一个作业可以按不同受众声明多个令牌:

yaml 复制代码
job_with_id_tokens:
  id_tokens:
    FIRST_ID_TOKEN:
      aud: https://first.service.com
    SECOND_ID_TOKEN:
      aud: https://second.service.com
  script:
    - first-service-authentication-script.sh $FIRST_ID_TOKEN
    - second-service-authentication-script.sh $SECOND_ID_TOKEN

aud 绑定是纵深防御的关键:即便令牌泄露,它也只能通过受众匹配的那一个服务验证,把爆炸半径压到最小。

需要注意的历史包袱:老的 CI_JOB_JWT / CI_JOB_JWT_V2 变量在 15.9 已弃用、17.0 起移除。如果你的流水线里还有这两个变量的引用,升级版本前必须先迁移到 id_tokens,否则作业会在需要令牌的场景直接失败。

第二步:在云侧建立 OIDC 身份提供方与条件角色

以通用流程为例(AWS、Azure、GCP、HashiCorp Vault 的具体配置见官方连接到云服务文档):

  1. 在云厂商侧注册一个 OIDC 身份提供方,信任源指向极狐GitLab 实例的 JWKS 端点;
  2. 创建一个条件角色,其信任策略校验 JWT 的 subaud

sub 的过滤规则非常灵活:

过滤目标 示例
仅 main 分支 project_path:mygroup/myproject:ref_type:branch:ref:main
任意分支 project_path:mygroup/myproject:ref_type:branch:ref:*
群组下所有项目 project_path:mygroup/*:ref_type:branch:ref:main
某个 tag project_path:mygroup/*:ref_type:tag:ref:1.0

这意味着你可以精确到「只有 mygroup/myproject 的 main 分支能换取生产环境的凭证」,测试分支的作业在云侧直接被拒绝------粒度是「分支级」,远超 IAM 用户模型。

当作业声明了 environment 时,sub 声明中还可以包含环境相关字段(如 environment_protecteddeployment_tier),信任策略可以进一步要求「只有部署到受保护环境的作业才能拿到生产凭证」,把 CI 权限模型与环境模型打通。

第三步:作业内换取临时凭证并使用

以 AWS 为例的典型作业:

yaml 复制代码
publish:
  stage: publish
  id_tokens:
    AWS_OIDC_TOKEN:
      aud: sts.amazonaws.com
  script:
    - >
      aws sts assume-role-with-web-identity
      --role-arn arn:aws:iam::<account>:role/gitlab-deploy
      --role-session-name "gitlab-$CI_PIPELINE_ID"
      --web-identity-token $AWS_OIDC_TOKEN
      > /tmp/aws_creds.json
    - export AWS_ACCESS_KEY_ID=$(jq -r .Credentials.AccessKeyId /tmp/aws_creds.json)
    - export AWS_SECRET_ACCESS_KEY=$(jq -r .Credentials.SecretAccessKey /tmp/aws_creds.json)
    - export AWS_SESSION_TOKEN=$(jq -r .Credentials.SessionToken /tmp/aws_creds.json)
    - ./deploy.sh

凭证只在本次作业的生命周期内有效,作业结束即失效,不存在「忘了删」的问题。对于 HashiCorp Vault 场景,还可以直接使用 secrets 关键字,Runner 会自动用 ID Token 完成认证并注入密钥,连脚本都不用写。

四、踩坑复盘

改造过程中高频出现的几个问题:

  1. aud 不匹配是最常见的失败原因 。云侧身份提供方配置的 audience 必须与 id_tokens 中声明的 aud 逐字一致。排查时先 echo $OIDC_TOKEN | cut -d. -f2 | base64 -d 解出 payload 对着看。
  2. sub 匹配是字符串精确匹配(除通配符外) 。项目改名、转移群组都会导致 sub 变化、信任策略失配。GitLab 提供了通过项目 API 将 sub 的首段切换为 project_id 的选项,用不可变 ID 绑定信任策略,可彻底规避路径变更问题。
  3. 别忽略供应链审查 。官方文档明确提示:为流水线配置 OIDC,意味着所有流水线都获得了对目标环境的 JWT 访问能力。上线前应做一次供应链安全审查,重点是 sub 过滤范围不要用过于宽泛的通配符。
  4. 临时凭证也有权限上限。换来的凭证继承条件角色的权限,角色策略仍要遵循最小权限------OIDC 解决的是「凭证怎么来」,不豁免「凭证能干什么」的治理。

五、写在最后

把长期密钥从流水线里清出去,本质上是一次信任模型的重构:从「持有秘密的人可以部署」变成「声明了正确上下文的作业可以部署」。前者靠流程约束,后者靠密码学保证。改造的工作量是一次性的(注册 IdP、建角色、改流水线),收益是永久性的(零轮换、零驻留、分支级粒度、天然审计字段)。

如果你的团队正在做安全合规整改,或者即将升级版本(旧 JWT 变量已移除),这正好是一个动手的窗口期。完整文档见:ID 令牌 OIDC 认证连接到云服务

相关推荐
richard_first1 小时前
50.5% 美国人认为 AI 恋爱可能算出轨:AI 正在从“工具“变成“关系主体“
人工智能·microsoft
snow@li2 小时前
服务器运维:阿里云安装软件gitlab/智能体协助安装/智能体协同运维
运维·gitlab
天空属于哈夫克317 小时前
企业微信AI开发:如何让AI根据用户消息自动生成回复?
人工智能·microsoft
染指111019 小时前
110.Agent-LangChain核心组件-Messages消息和提示词工程
人工智能·microsoft·langchain·agents
小弥儿21 小时前
GitHub今日热榜 | 2026-09-08:微软 markitdown 冲进前三
学习·microsoft·开源·github
极小狐1 天前
CI 流水线提速实战:缓存设计与依赖代理的工程实践
缓存·ci/cd·gitlab·devops
举个栗子。1 天前
Generative AI for Beginners:微软官方 21 课生成式 AI 入门教程,从零掌握 AI 应用开发
人工智能·microsoft
一切皆是因缘际会1 天前
具身智能
人工智能·microsoft·生活
吴佳浩 Alben1 天前
构建企业级 DevOps 排错 Agent:从日志告警到自动化修复 PR
大数据·人工智能·语言模型·架构·自动化·ai编程·devops