CI 里还在用长期密钥访问云?用 OIDC 令牌改成免密钥的 7 个步骤
本文环境组件版本 / 说明极狐GitLabJihuLab.comSaaS自管实例操作一致Runner自托管 Runnershell 执行器Linux云厂商支持 OIDC 的云AWS / Azure / GCP / Vault 之一CI 变量项目级AWS_ROLE_ARN改用 OIDC 后仅保留角色标识不再是密钥涉及功能 TierID 令牌基础版、专业版、旗舰版均可用说明本文所有报错日志、令牌负载、配置片段均来自真实配置过程为避免泄漏已对账号 ID、项目路径做脱敏。为什么要改长期密钥的三个结构性毛病先说清楚改造的收益再动手。长期密钥在 CI 场景下的问题不是可能泄露而是三个绕不开的结构性毛病凭证寿命与作业寿命完全不匹配。一个作业跑 3 分钟凭证却永久有效。泄露一次影响期是无限的。权限粒度只能靠密钥数量堆。想让预发和生产用不同权限就得配两套密钥、两个变量然后祈祷没人填错。轮换要做跨团队协调。密钥在哪个作业里被引用过往往只有最初配置的人知道于是先不轮换成为默认选项。OIDC 的做法是把这件事反过来作业运行时向平台申请一个短时效的身份令牌云侧验证令牌里的声明后发放临时凭证。整个授权流程是四步——云上建身份提供商建一个按群组、项目、分支或标签过滤的条件角色CI 作业携带 ID 令牌发起授权云验证通过后返回临时凭证。问题复现一个能用但不敢碰的 CI 变量改造前的配置长这样——把长期密钥塞进 CI 变量所有作业共用# 改造前 deploy: stage: deploy script: - aws s3 sync ./dist s3://my-bucket --delete variables: AWS_ACCESS_KEY_ID: $AWS_ACCESS_KEY_ID # 项目级 CI 变量长期密钥 AWS_SECRET_ACCESS_KEY: $AWS_SECRET_ACCESS_KEY这套配置跑了两年问题集中在三个地方密钥一旦写入就没人敢轮换怕弄挂流水线变量对有开发者权限的人全部可见轮换一次要通知所有引用方。第一次尝试换成 OIDC 时直接报错An error occurred (InvalidIdentityToken) when calling the AssumeRoleWithWebIdentity operation: Couldnt retrieve verification key from your identity provider.这是典型的 OIDC 配置没对齐。下面按步骤拆解。步骤 1先在作业里声明 id_tokensID 令牌是通过id_tokens关键字配置的配置好之后令牌会作为 CI/CD 变量提供给script、before_script、after_script使用job_needing_oidc_auth: id_tokens: OIDC_TOKEN: aud: https://oidc.provider.com script: - echo $OIDC_TOKENaud是受众声明必须和云侧身份提供商里配置的受众一致。这一步配错后面全是 401。步骤 2理解令牌里到底有什么在云侧写信任策略之前先把令牌解出来看一眼echo $OIDC_TOKEN | cut -d . -f2 | base64 -d | jq .实测输出已脱敏{ namespace_id: 72, namespace_path: my-group, project_id: 20, project_path: my-group/my-project, user_id: 1, user_login: sample-user, ref: main, ref_type: branch, ref_path: refs/heads/main, ref_protected: true, environment: production, environment_protected: true, deployment_tier: production, pipeline_source: push, runner_environment: self-hosted, project_visibility: private, jti: 235b3a54-b797-45c7-ae9a-f72d7bc6ef5b, iss: https://gitlab.cn, iat: 1681395193, nbf: 1681395188, exp: 1681398793, sub: project_path:my-group/my-project:ref_type:branch:ref:main, aud: https://oidc.provider.com }踩坑 1preferred_username不存在。极狐GitLab 的 ID 令牌里默认没有这个声明很多云厂商的教程直接照抄会匹配不上用user_login或sub代替。踩坑 2令牌只有几分钟寿命。ID 令牌使用 RS256 编码并用专用私钥签名过期时间设置为作业的超时时间如果没指定超时就是 5 分钟。所以不要把它存进产物或缓存里期待复用。步骤 3在云侧建身份提供商并写条件角色信任关系靠两个声明建立受众aud与主体sub。sub 的默认格式是project_path:{group}/{project}:ref_type:{type}:ref:{branch_name}常用的过滤写法project_path:mygroup/myproject:ref_type:branch:ref:* # 任意分支 project_path:mygroup/myproject:ref_type:branch:ref:main # 指定项目与分支 project_path:mygroup/*:ref_type:branch:ref:main # 群组下所有项目 project_path:mygroup/*:ref_type:tag:ref:1.0 # 按标签过滤如果用的是 JihuLab.com 提供的 OIDC 身份提供商AWS 侧可用的条件键为namespace_id、project_id、user_id、user_login、user_email、user_access_level、ref_protected、pipeline_source。{ Version: 2012-10-17, Statement: [ { Effect: Allow, Principal: { Federated: arn:aws:iam::123456789012:oidc-provider/oidc.jihulab.com }, Action: sts:AssumeRoleWithWebIdentity, Condition: { StringEquals: { oidc.jihulab.com:aud: https://oidc.provider.com, oidc.jihulab.com:ref_protected: true }, StringLike: { oidc.jihulab.com:sub: project_path:my-group/*:ref_type:branch:ref:main } } } ] }踩坑 3私有化部署没有这些条件键。上面那组条件键只适用于 JihuLab.com 的 OIDC 身份提供商私有化部署中 AWS 只支持sub声明作为条件键。照搬 SaaS 教程会静默匹配不上。另外官方明确提醒不要只用user_login或user_email做条件因为用户可以自行更改这两项。写信任策略时除了sub这种基于路径的声明还要同时带上namespace_id、project_id这类稳定唯一标识符——路径会变群组或项目重命名ID 不会。步骤 4作业里用令牌换临时凭证deploy: stage: deploy id_tokens: OIDC_TOKEN: aud: https://oidc.provider.com environment: name: production script: - CRED$(aws sts assume-role-with-web-identity --role-arn $AWS_ROLE_ARN --role-session-name gitlab-$CI_JOB_ID --web-identity-token $OIDC_TOKEN --query Credentials.[AccessKeyId,SecretAccessKey,SessionToken] --output text) - export AWS_ACCESS_KEY_ID$(echo $CRED | awk {print $1}) - export AWS_SECRET_ACCESS_KEY$(echo $CRED | awk {print $2}) - export AWS_SESSION_TOKEN$(echo $CRED | awk {print $3}) - aws s3 sync ./dist s3://my-bucket --delete注意environment: name: production。作业指定了环境之后令牌里才会带上environment、environment_protected、deployment_tier这些声明环境相关的 sub 字段在 18.7 中引入。步骤 5遇到 ID token issuance is disabled 就用 API 改 sub 组成这是我的环境里实际撞到的第二个报错ID token issuance is disabled in CI because this projects path was previously used by a different project.原因是配置的 sub 里project_path路径曾被另一个项目用过极狐GitLab 会阻止签发避免新项目继承旧项目的外部信任策略。解决办法是用 projects API 改 sub 的组成把project_id放到第一位curl --request PUT \ --header PRIVATE-TOKEN: $GITLAB_TOKEN \ --header Content-Type: application/json \ --url $CI_API_V4_URL/projects/$CI_PROJECT_ID \ --data {ci_id_token_sub_claim_components: [project_id, ref_type, ref]}改完之后 sub 变成project_id:20:ref_type:branch:ref:main记得同步更新云侧信任策略否则旧策略会全部匹配不上。步骤 6按环境分层收紧权限改造的价值不只是去掉密钥而是让权限能按环境细分。给预发和生产配不同的角色靠deployment_tier与environment_protected区分deploy_prod: stage: deploy id_tokens: OIDC_TOKEN: aud: https://oidc.provider.com environment: name: production deployment_tier: production rules: - if: $CI_COMMIT_BRANCH $CI_DEFAULT_BRANCH script: - ./scripts/deploy.sh prod $OIDC_TOKEN步骤 7验证与收尾验证脚本建议直接打印身份确认拿到的是临时凭证而不是长期密钥aws sts get-caller-identity # 预期输出中 Arn 形如 arn:aws:sts::123456789012:assumed-role/ci-deploy/gitlab-302最后一步是清理把项目级 CI 变量里的长期密钥删掉并在云侧把旧访问密钥置为禁用先别直接删观察两三天再回收。踩坑 4一个作业要访问多个服务时别共用同一个令牌。官方做法是声明多个 id 令牌各自带不同的aud这样第三方服务可以拒绝 aud 不匹配的令牌把泄露的影响面压到最小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踩坑 5忘了约束 Runner 类型。令牌里带runner_environment声明取值是gitlab-hosted或self-hosted。如果只允许自托管 Runner 触碰生产就在信任策略里把这个条件加上否则托管 Runner 也能拿到同样的角色。报错排查速查报错常见原因处理400: missing tokenID 令牌所需的基本组件缺失或未配置管理员查实例的exceptions_json.log定位具体方法GitLab::Ci::Jwt::NoSigningKeyError数据库中签名密钥缺失见下方 SQL 与修复片段401: unauthorized用了已弃用的$CI_JOB_JWT_V2provider_name不匹配aud不匹配未配置id_tokens解码令牌逐项比对 aud 与 subID token issuance is disabled项目路径曾被其他项目使用用 projects API 改 sub 组成签名密钥缺失时先在实例数据库里确认SELECT encrypted_ci_jwt_signing_key FROM application_settings;返回值为空则由实例管理员重新生成Rails 控制台key OpenSSL::PKey::RSA.new(2048).to_pem ApplicationSetting.find_each do |application_setting| application_setting.update(ci_jwt_signing_key: key) end改造前后对比维度改造前长期密钥改造后OIDC 令牌凭证寿命永久直到手动轮换等于作业超时时间未指定则 5 分钟轮换成本需通知所有引用方无需轮换可见范围有开发者权限即可读取变量作业运行时才签发权限粒度一个密钥对应一套权限可按项目、分支、环境、部署层级过滤Runner 要求常按环境部署专用 Runner实例 Runner 可安全访问多账户写在最后整套配置脚本含信任策略模板、projects API 调用、验证脚本我整理成了一份可直接改参数的版本放在主页置顶资源里自取也可以评论区留言OIDC我发你。如果你的环境是私有化部署记得只按sub写条件键。

相关新闻

智能体与代码安全一体化选型指南:跳出功能清单,以运行时硬指标衡量真实防护能力

智能体与代码安全一体化选型指南:跳出功能清单,以运行时硬指标衡量真实防护能力

导语随着 AI 智能体、代码大模型深度融入研发流程,企业安全建设正面临双重挑战:一方面,AI 代码助手、代码智能体大规模生成代码,引入大量隐藏漏洞、供应链风险;另一方面,具备自主工具调用能力的 Agent&…

2026/10/3 14:17:49 阅读更多 →
中医儿科学论文开题别硬扛:我会这样搭配 AI 文献综述搭子 [特殊字符]

中医儿科学论文开题别硬扛:我会这样搭配 AI 文献综述搭子 [特殊字符]

最近很多同学在问:有没有热门的 AI 论文生成软件推荐? 这个问题对中医儿科学专业尤其现实。比如你要做一个很典型的毕业题目:《小儿厌食症脾失健运证中医外治法临床观察》,前期既要读中医儿科古籍、现代临床研究,又要…

2026/10/3 14:17:58 阅读更多 →
claude code接入deepseek报“API Error: 400”的排查与修复:把settings改到TaoToken

claude code接入deepseek报“API Error: 400”的排查与修复:把settings改到TaoToken

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/3 14:18:01 阅读更多 →

最新新闻

MRAM+K64F工业存储方案:掉电不丢数据的硬件基座

MRAM+K64F工业存储方案:掉电不丢数据的硬件基座

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 7:15:51 阅读更多 →
重学网工之-ppp配置

重学网工之-ppp配置

任务:R1与R2使用PPP协议,且R1作为网关,R2需从R1获取ip地址配置思路: R1配置: 1.先给R1和R2新增2SA口 2.R1进入S1口将端口类型改为ppp,并给端口配置ip地址 3.新建一个名为ppp1的地址池,宣告网段10.12.1.0/24…

2026/10/4 7:15:51 阅读更多 →
手把手教你学Simulink——UPS系统中双向DC-AC逆变器并联均流控制仿真

手把手教你学Simulink——UPS系统中双向DC-AC逆变器并联均流控制仿真

目录 手把手教你学Simulink——UPS系统中双向DC-AC逆变器并联均流控制仿真 一、为什么UPS并联必须专门做均流 二、系统方案与示例参数 三、Simulink建模步骤 1. 主功率层(每台一个Inverter子系统) 2. 单台控制核:电压外环+电流内环 3. 方案A:平均电流/主从均流(UPS冗…

2026/10/4 7:15:51 阅读更多 →
RK3568麒麟系统rootfs提取定制与重打包完整实战指南

RK3568麒麟系统rootfs提取定制与重打包完整实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 7:15:51 阅读更多 →
2026年Work Agent品类全解读:AI办公助手的新进化方向

2026年Work Agent品类全解读:AI办公助手的新进化方向

这种变化背后,就是AI从问答工具向执行工具的核心跃迁,而支撑这种能力的核心品类,就是最近行业内讨论度持续走高的Work Agent。本文将围绕这个新兴品类的核心定义、能力边界、主流产品和未来走向展开完整梳理,帮所有对AI办公应用感…

2026/10/4 7:15:50 阅读更多 →
用AI统一招聘标准:岗位要求、简历筛选与面试评价的语义对齐实践

用AI统一招聘标准:岗位要求、简历筛选与面试评价的语义对齐实践

1. 招聘标准不统一的真实困境与AI介入的切入点1.1 一个让HR和业务部门都头疼的老问题我做了十多年技术团队管理,带过的团队从十几人到上百人不等,招聘这件事几乎每年都要折腾好几轮。最让我头疼的不是招不到人,而是招进来的人跟当初面试时聊的…

2026/10/4 7:14:50 阅读更多 →

日新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/2 10:36:31 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/3 9:42:35 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/3 9:42:36 阅读更多 →