全球每天有超过3000万个容器在运行,其中绝大多数都在/run/secrets目录里裸奔着数据库密码、API密钥和TLS证书。这个数字来自Docker Hub的公开统计,而真正的企业级部署量可能再翻10倍。
一位在基础设施领域摸爬滚打15年的老工程师最近公开吐槽:这个目录就像酒店走廊里的保险箱,密码写在箱盖上,所有人路过都能瞄一眼。更荒诞的是,这个"保险箱"的位置还是公开的——任何拿到容器访问权限的攻击者,第一反应就是去/run/secrets翻找。
1. 为什么/run/secrets成了众矢之的
容器编排工具(如Kubernetes、Docker Swarm)设计了一套"机密"机制:把敏感信息挂载到容器的/run/secrets路径,让应用读取。初衷是好的——比直接写死在镜像里强。
但问题在于:这些文件默认是可重复读取的普通文件,且权限设置宽松。一旦攻击者通过供应链漏洞或应用漏洞拿到容器内的任意代码执行能力,读取这个目录的成本接近于零。
作者Spots(Libera IRC上的ID)在博客里写道:「我感觉这些文件应该只能读一次,然后以某种方式变得不可访问,但我一直没能说服Linux内核做到这一点。」
这不是技术无知。Spots的背景足够硬核——能随手提到"umount需要root权限""文件描述符在读取后关闭"这种细节的人,显然在Linux内核和容器安全领域浸淫多年。连他都卡住了,说明问题确实棘手。
2. 那些"看起来可行"的偏方
Spots向安全圈的朋友和同事广泛征集方案,得到的反馈堪称一场"安全 hackathon"的缩影。
最流行的是"用环境变量替代文件"。但Spots的反驳很毒:攻击者只需要执行ENV命令就能全量导出,比诱导应用服务器显示特定文件还容易。这相当于把保险箱从走廊搬到了没上锁的抽屉里。
另一个思路是自定义入口脚本(entrypoint):容器启动后,立即用空白的tmpfs覆盖/run/secrets。原理是利用Linux的挂载命名空间——非root用户无法卸载(umount),而原机密文件在覆盖后不再可见。
但Spots自己评价这是"really cheesy hack"——真的很 cheesy 的 hack。它依赖两个假设:应用必须在启动时一次性读完所有机密,且之后绝不重新读取。现实中,很多应用会在运行期动态重载配置,或者优雅重启时需要重新认证。
更隐蔽的风险是文件描述符泄漏。如果某个进程在tmpfs覆盖前打开了机密文件,描述符可能一直持有旧文件的引用——攻击者通过/proc/self/fd依然能读到。
3. Linux内核为什么不配合
核心矛盾在于:Linux的文件系统没有"自毁式读取"的原生语义。
Spots想要的机制类似量子力学的"观测即坍缩"——文件内容在被读取一次后,对后续访问返回空或触发权限错误。但POSIX标准和Linux VFS(虚拟文件系统层)都没有这种设计。
有人可能会想到Linux的 capabilities 或 SELinux。但这些都是权限控制,不是访问次数控制。你可以限制谁能读,但无法限制读几次。
tmpfs的挂载覆盖方案之所以"cheesy",是因为它用空间换时间——用一个新的空文件系统遮蔽旧路径,而不是真正销毁数据。旧数据在内存中可能仍有残留,直到被垃圾回收。对于 nation-state 级别的对手,这不够干净。
4. 供应链攻击放大了这个漏洞
Spots特别强调"especially in the age of daily supply chain exploits"——尤其是在供应链攻击日更的时代。这不是修辞。
2023-2024年的公开记录显示,PyPI、npm、Maven Central 等仓库的恶意包投放频率从每月数起上升到每周数十起。攻击链条通常是:开发者引入恶意依赖 → 构建时执行恶意代码 → 窃取CI/CD环境中的机密 → 横向移动。
在这种场景下,/run/secrets的只读一次性假设完全失效。恶意代码在构建阶段就能读取机密,而容器甚至还没启动。
云原生安全公司Chainguard的工程师曾在KubeCon上分享过一个案例:某企业使用"安全"的机密文件挂载,但构建缓存层保留了旧版本的机密文件快照。攻击者通过缓存投毒,拿到了三个月前轮换掉的旧密钥——而旧密钥在下游系统中仍未失效。
5. 可能的出路在哪里
Spots的提问方式很有意思:「Is anyone else even thinking this is a problem?」——还有人觉得这是个问题吗?
潜台词是:行业可能已经习惯了这种"足够好"的安全水位。机密管理的主流方案(HashiCorp Vault、AWS Secrets Manager、Kubernetes external-secrets)都在往"动态短期凭证"方向演进,而不是加固/run/secrets本身。
但动态凭证也有盲区。Spots的场景是"容器启动时需要静态机密"——比如数据库连接串、TLS私钥。这些无法完全动态化,必须在某个时刻以明文形式出现在内存中。
一个未被充分探索的方向是eBPF。Linux内核的这项机制允许在不修改内核源码的情况下注入安全策略。理论上可以编写eBPF程序,拦截对/run/secrets的open()调用,在首次读取后自动撤销访问权限。
但这需要容器运行时(containerd、cri-o)的深度配合,且eBPF程序的自身安全又是另一个话题。
Spots最后留下了IRC联系方式,邀请同行继续讨论。这个细节本身说明问题尚无标准答案——否则不会用"old-school"的方式求援。
如果你正在维护生产环境的容器集群,你的机密文件现在是以什么形态存在的?是挂载的文件、环境变量,还是已经迁移到动态凭证?Spots在Libera IRC等你的答案。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.