构建缓存残留比特权容器更易被忽视
在 CI/CD 流水线中收紧执行环境:从特权容器、密钥暴露到构建节点隔离
我先说个实话:我盯了好一阵子 --privileged,直到被另一个问题绊了一跤,才发现自己一直看错了地方。
特权容器这件事,好歹是显式的。它写在配置里,把 include、extends 和共享模板展开之后,grep 一下能揪出来。它危险,但它不隐蔽,也不会跟你玩捉迷藏。
缓存残留不一样。它不在任何一份配置里,没有哪一行写着"这个目录会被下一个 job 读到"。它是上一次构建留下的遗产,默认被继承,而且大多数人对它毫无戒心。
具体点说,共享 runner 上的缓存目录、工作区、后台进程,还有那个被改写的 ~/.docker/config.json,都是跨构建传递的。上一个 job 往里塞了什么,下一个 job 就照着吃什么。写进去的东西甚至不必是恶意的,一个顺手留下的临时凭据、一处被改过的配置,就够了。而这一整条路径,任何权限扫描器都不会替你报出来。
这台机器上一次跑的是什么
我后来把节点隔离的思路压缩成了一句自问:这台机器上一次跑的是什么?
如果答案是"一个来自 fork 的 PR",那它接下来就不该跑发布任务。把 job 按可信度分开——外部代码、内部分支、发布与签名——外部那一类只跑临时节点,用完即销毁,节点上不给任何凭证。半可信的可以复用节点,但每次构建前清工作区,缓存按分支隔离。这一点最容易被漏掉:缓存不是性能优化,它是一条跨构建的写入通道。
一个特别朴素的验收点
收紧之后怎么确认真的生效了?常规探测当然要做:能不能读宿主机设备、能不能摸到云元数据地址、环境变量里还残留生产凭证吗。但我更喜欢一个更笨的办法——同一个 commit,收紧前后各构建一次,产物应该一致。
如果产物变了,那基本就是原来看不见的地方吃了节点残留的状态。这不是"参数调错了",这本身就是节点没隔离的证据,值得当缺陷去修,而不是把配置改回去。
所以现在我看流水线,grep 关键字只是第一步。真正要盯的,是那些没出现在任何配置里、却实实在在跨构建传下去的东西。特权标志会喊,缓存不会。

参与讨论
缓存隔离这块确实太容易被忽视了