![]()
「你的Nix部署看起来很干净,但很可能不是。」Alexandra Selldorff在Manifest的SBOM扫描日志里敲下这句话时,手里正攥着一份让团队沉默的报告:那些号称"可复现、无依赖地狱"的Nix环境,47%藏着声明文件里没写的幽灵依赖。
作为Manifest的工程负责人,她每天的工作是给软件供应链拍X光。NixOS社区吹了十几年的"纯函数式部署",在她眼里像个精心打扫过的房间——表面一尘不染,掀开地毯全是灰尘。这不是技术信仰崩塌,是审计工具终于跟上了营销话术。
Nix的干净承诺,卡在哪一步
Nix的核心卖点很简单:用声明式配置(声明式配置,即通过代码描述系统终态而非安装步骤)锁定整个依赖树,同一套配置在任何机器上构建出比特级一致的环境。理论上,你从Git仓库拉取`flake.nix`,就能复现一台2021年的生产服务器。
问题是"声明"和"实际"之间有条缝。
Selldorff的团队扫描了127个公开Nix项目,发现近半数在构建时拉取了未在锁文件中显式声明的资源。有些是构建脚本里硬编码的curl下载,有些是插件系统自动抓取的最新版工具链,还有些是开发者本地缓存的"临时补丁"忘了提交。
这些幽灵依赖不会破坏日常运行。Nix的构建缓存机制(Nix Store的哈希寻址)甚至能正常复现它们——只要原始URL还能访问。但一旦上游仓库下线、版本被覆盖,或者安全补丁发布,你的"可复现构建"就变成了薛定谔的盒子:今天能跑,明天可能哈希对不上,却没人知道少了什么。
正方:Nix社区说"这是用户误用"
NixOS基金会的技术委员会成员Eelco Dolstra(Nix原始作者)在邮件列表回应过类似质疑。他的论点很直接:Nix提供了纯函数式的构建沙盒,但开发者可以选择不用。那些绕过`fetchurl`校验、在`buildPhase`里偷偷wget的行为,相当于把安全带剪了再抱怨车祸。
社区的主流声音支持这一立场。2023年NixCon的调研显示,82%的"构建不可复现"案例源于开发者使用了`impureEnvVars`(非纯环境变量)或自定义构建脚本。官方文档明确警告过这些逃逸舱口,只是大多数人没读。
更激进的辩护者认为,Selldorff的扫描方法论本身有偏。Manifest的SBOM工具基于运行时依赖分析,而Nix的设计哲学是"构建时确定性优先"。两者的时间窗口不同:构建时干净的环境,运行时可能动态加载额外库;反过来,构建时的幽灵依赖如果从未被实际链接,对运行时安全就是噪音。
反方:工具没拦住人,就是工具的锅
Selldorff在回应Dolstra时甩了一组数据:那47%的幽灵依赖里,61%发生在官方推荐的`mkDerivation`模板内,而非开发者自定义脚本。换句话说,最标准的写法照样漏。
她举了个具体案例。某流行的Nix Python环境管理工具,在`shell.nix`里封装了`poetry2nix`(Poetry依赖的Nix封装器)。这个封装器为了加速构建,默认启用了`preferWheels`——从PyPI直接拉取预编译wheel,而非从源码构建。wheel的内容不会进入Nix Store的哈希校验,相当于在纯函数式城堡底下挖了条地道。
「这不是用户剪安全带,是安全带本身有个出厂没修的洞。」她在技术博客写道。
更深层的问题在于Nix的生态系统激励。Nixpkgs(官方软件包仓库)有超过8万个包,维护者不可能逐行审计每个构建脚本。而"能跑就行"的PR文化,让大量包裹层(wrapper)代码以"临时方案"的名义进仓,一待就是五年。
Manifest的扫描逻辑,到底准不准
争论的核心是"干净"的定义权。Nix社区说"声明文件+锁文件=干净",Selldorff说"实际构建行为与声明一致=干净"。两种标准差了一个观测层。
Manifest的SBOM工具采用动态追踪:在沙盒里运行完整构建流程,抓取所有网络请求和文件系统操作,再与静态声明比对。这种方法能发现`flake.lock`没记录的`curl | bash`,但也会把正常的构建时工具(比如编译器本身)标记为"潜在幽灵"。
Selldorff承认有假阳性。她的团队手动复核了扫描结果,剔除了Nix Store内置工具链的干扰后,硬性的"声明外依赖"仍占31%。这些不是误报,是实实在在的哈希逃逸。
一个典型场景是Git子模块。Nix的`fetchGit`支持递归拉取子模块,但子模块的锁文件不会被纳入主仓库的`flake.lock`哈希计算。如果子模块维护者强制推送了重写历史,你的构建哈希不变,内容已经变了——而Nix的"纯函数"承诺对此毫无感知。
工程师该站哪边
我的判断是:双方都在说真话,但说的不是同一件事。
Nix的"纯函数式部署"是个数学概念,针对的是构建系统的内部一致性。你给它相同的输入,它保证相同的输出——这个承诺在Nix Store的边界内是成立的。但输入本身的完整性,Nix并不担保,它假设你提供的源代码和URL是诚实的。
Selldorff的扫描针对的是工程现实:人不会永远诚实,工具不会永远被正确使用。当她发现47%的项目有幽灵依赖时,她不是在否定Nix的设计,是在指出"设计"到"落地"的衰减曲线。
对25-40岁的技术从业者来说,关键问题不是"Nix好不好",而是"我的审计工具能不能覆盖Nix的逃逸舱口"。Manifest开源了他们的SBOM扫描器(基于BuildKit的细粒度追踪),但集成到CI需要改流水线。Nix社区也在推进`contentAddressedDerivations`(内容寻址推导),试图把运行时实际内容纳入哈希校验——这能解决部分问题,但增加了构建缓存的失效频率。
两者都在进化,只是节奏不同。工程师的务实选择是:继续用Nix,但把Selldorff的扫描逻辑塞进发布流程。干净不是声明出来的,是测出来的。
Manifest的扫描报告最后附了条脚注:那47%的项目里,有12个在收到披露后三周内修复了幽灵依赖,方式是给`buildPhase`加了`disallowedRequisites`(禁止依赖项)限制。剩下的35个,维护者回复"不影响功能,暂不处理"。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
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.