打开网易新闻 查看精彩图片

去年Codecov泄露事件后,一位工程师花了两周时间做了个实验:如果攻击者已经混进了你的CI/CD流水线,什么防御手段真的能挡住他们?

结果让他后背发凉——大多数团队的流水线,距离生产环境被接管只差一个被污染的依赖包。

他把自己的实验过程公开了。不是理论推演,是真刀真枪地试了一圈:强制签名提交、换掉所有长期密钥、给容器加沙箱、搞无密钥签名。每一步都踩了坑,有些坑让他凌晨四点还在调配置。

第一道防线:让管理员也得找人批条子

第一道防线:让管理员也得找人批条子

他首先锁死了代码仓库的准入规则。强制签名提交,强制代码审查,连管理员权限也不放过。

「我知道这会让快速迭代变卡,」他在实验笔记里写,「但如果不这么干,一个被攻破的runner(运行器)就能重写你的主干历史,想推什么推什么。」

这个场景他反复推演:攻击者拿到某个开发者的GitHub账号,或者某个Action被植入恶意代码。没有强制审查的话,恶意提交可以直接进主干,然后跟着流水线自动部署。

代价是明显的。小团队里本来一个人就能 hotfix(热修复)上线,现在得等第二个人醒着。但他算了一笔账:一次生产事故的平均修复时间是6小时,而强制审查增加的延迟通常不到30分钟。

第二道防线:把AWS密钥从GitHub里连根拔掉

第二道防线:把AWS密钥从GitHub里连根拔掉

真正让他失眠的是另一件事。

「一个被攻破的GitHub Action在你的流水线里跑,它读到你的AWS_ACCESS_KEY_ID和AWS_SECRET_ACCESS_KEY,发给外部服务器。几秒钟后,别人就在往你的生产账号部署了。」

打开网易新闻 查看精彩图片

他干掉了所有长期有效的AWS密钥,换成OIDC(开放ID连接)。原理不复杂:runner向GitHub申请一个短期JWT令牌,AWS STS(安全令牌服务)把它换成临时凭证。没有密钥躺在GitHub的secrets里,攻击者就算拿到 runner 权限,也只能窃听到一个几分钟就过期的令牌。

配置过程却是一场噩梦。第一个错误是AccessDenied,持续了4小时。最后发现是IAM信任策略没接受GitHub的audience字段。

「AWS的OIDC文档像是从来没调过403的人写的,」他吐槽。但调通之后,整个攻击面缩小了一大截——哪怕某个job被攻破,凭证过期速度够快,能把爆炸半径限制在可接受范围。

他贴出了关键配置片段:权限块里必须声明id-token: write,否则拿不到JWT。这个细节文档里写了,但藏在第三层链接里。

第三道防线:给软件供应链上户口

第三道防线:给软件供应链上户口

代码来源搞定了,依赖和制品怎么办?

他用Syft生成SPDX(软件包数据交换格式)和CycloneDX格式的物料清单,基本上开箱即用。麻烦的是签名——最后选了Cosign做无密钥签名,依托Sigstore(签名存储)基础设施。

「Sigstore的流程一旦理解就不难了,但第一次绝对不明显。我把文档读了三次才搞明白『密钥』到底从哪来。」

相比GPG密钥管理的地狱——私钥轮换、谁保管签名密钥、离职人员怎么处理——无密钥方案把身份绑定到GitHub OIDC。验证时只需要一条命令,检查证书身份是否匹配GitHub域名模式。

他列了一个常见故障:如果验证失败提示「no matching signatures」,通常是身份没在策略里映射对。这个错误信息足够模糊,能让人排查两小时。

第四道防线:容器里再套一层牢笼

第四道防线:容器里再套一层牢笼

打开网易新闻 查看精彩图片

Docker的隔离他信不过,试了gVisor(runsc运行时)。

这部分「很痛苦」。自建runner、cgroups(控制组)配置冲突、随机报「unsupported runtime」错误,熬了几个晚上。

他没说最后有没有完全调通,只提了一句「仍在折腾」。但方向是明确的:如果构建环境本身不可信,就在它和宿主机之间再加一层系统调用拦截。

成本清单:哪些值得,哪些还在流血

成本清单:哪些值得,哪些还在流血

他最后摊了牌——

强制签名和审查:值得。摩擦可控,收益明确。

OIDC换密钥:值得。文档烂,但一次配好长期省心。

SBOM(软件物料清单)和无密钥签名:值得。供应链攻击越来越多,这成了基础 hygiene(卫生措施)。

gVisor加固:待定。投入产出比还没算清楚,小团队可能扛不住这个维护负担。

整篇实验笔记里最扎眼的是一个细节:他提到某个配置错误「浪费了3小时」,另一个「浪费了4小时」。这些数字背后是一个资深工程师的时间,换成经验更少的人可能是三倍。

安全加固的悖论在于,它总是和效率打架。但Codecov事件证明,不打这一架的后果更贵。