- Published on
Debian 开始阻止不可复现的软件包迁移到 testing — 这道关卡究竟挡住了什么,又没挡住什么
- Authors

- Name
- Youngju Kim
- @fjvbn20031
- 引言 — 一句话的政策变化,以及这句话真正的意思
- 什么才算得上可复现构建
- 压力测试与真实复现 — 本文最关键的区分
- reproduce.debian.net 是怎么运转的
- 关卡:britney 实际挡住了什么
- 数字 — 截至 2026 年 5 月 9 日
- 是什么在破坏复现
- 诚实的局限
- 那你的 CI 能从这里搬走什么
- 结语
- 参考资料
引言 — 一句话的政策变化,以及这句话真正的意思
2026 年 5 月 10 日凌晨 4 点 47 分(UTC),Paul Gevers 代表发布团队在 debian-devel-announce 上发了一封邮件。标题平淡无奇,就是"bits from the release team",他在开头这样表述 — 现在大约是 forky 发布周期的一半,他想宣布一件"代码上是一小步,但承诺上是一次巨大飞跃"("a small step in code, but a giant leap in commitment")的事。
那次飞跃的正文只有两句话。
Reproducibility
===============
Aided by the efforts of the Reproducible Builds project [1], we've decided it's
time to say that Debian must ship reproducible packages. Since yesterday, we
have enabled our migration software to block migration of new packages that
can't be reproduced [2] or existing packages (in testing) that regress in
reproducibility.
(因为写的是"since yesterday",所以实际生效的日子是 2026 年 5 月 9 日。)
技术媒体只截取了前一句。Phoronix 用的标题是"Debian Must Now Ship Reproducible Packages",The Register 用的是"Debian 14 cracks down on unreproducible packages"。只看标题,会以为 Debian 从此不再接受任何不可复现的软件包。
但把后一句按字面读下来,范围要窄得多。被挡住的是 (1) 不可复现的新软件包的迁移,以及 (2) 已经在 testing 里、可复现性发生回归的软件包的迁移。仅此两种。已经坐在 testing 里的那几百个不可复现的软件包,原封不动。
本文关心的正是这道落差 — Debian 到底打开了什么开关,这项检查在技术上是怎么运转的,现在的数字走到了哪一步,以及从这个案例里,你自己的构建流水线到底能偷师什么。
什么才算得上可复现构建
Reproducible Builds 项目的定义是这样的:
A build is reproducible if given the same source code, build environment and build instructions, any party can recreate bit-by-bit identical copies of all specified artifacts.
这里的关键在于三个条件都要"相同"这个前提 — 相同的源码、相同的构建环境、相同的构建指令。在这个前提下,无论谁来构建,都应该得到逐比特相同的产物。可复现性说的不是"在任意环境下都得到相同结果",而是"重建出指定环境后得到相同结果"。这个区分在后面会一直绊住人。
为什么要这么做。问题是这样的 — 自由软件的源码是公开的,但大多数人安装的是预编译好的二进制文件。而这两者是否真的对应,没有人知道,哪怕是构建这个二进制文件的人自己也不知道。如果构建机器被攻陷了,或者编译器被植入了后门,无论怎么审计源码都查不出来。可复现构建正是用来补上这个洞的 — 只要多个互相独立的当事方能从同一份源码里做出同样的比特,就能验证发布的二进制文件确实来自那份源码。
不过公平起见,也应该原样带上这个项目自己附加的注解。这是 Holger Levsen 在 5 月的发布会上用的说法:
Reproducible Builds are an important building block in making supply chains more secure. Nothing more, nothing less. (Un)secure software build reproducibly still remains (un)secure software.
以可复现的方式构建出来的脆弱软件,依然是脆弱软件。可复现性只保证"这个二进制文件来自这份源码",完全不保证那份源码本身是安全的。
压力测试与真实复现 — 本文最关键的区分
Debian 有两套系统在看可复现性,混淆这两者会让你把数字读得面目全非。
tests.reproducible-builds.org 是较老的一套,从 2015 年运转至今。用 Paul Gevers 的说法,这是一种压力测试 — 故意在不同条件下把同一个软件包构建两遍。幻灯片给这种方式起的名字是方差测试(variance testing),目的是猎捕"可能干扰软件包复现的因素"。它是故意去晃动变量,找出薄弱环节。
reproduce.debian.net 是较新的一套,2024 年才启动,这次接入关卡的正是它。目的正好相反。照搬幻灯片原话:
The goal of reproduce.debian.net is to replicate the same build process that is used by Debian during package publication -- not to seek out additional sources of variance.
它不会去找额外的变化来源,而是试图原样复制 Debian 发布软件包时实际用过的那套构建过程。
Paul Gevers 在 5 月 30 日的后续邮件里再次强调了这个区别,这段话是本文的核心,照原样引用:
there is a very clear difference in the way reproducibility is tested on https://tests.reproducible-builds.org/ vs https://reproduce.debian.net/. While the former is a stress test, the latter tries, with the info in the .buildinfo file, to reproduce what we ship. It's the latter case what some people call "easy", but it's also what matters to our users: can the binaries they use be reproduced.
他先承认了有人把后者叫作"简单"。接着马上反驳 — 不管简不简单,对用户真正重要的是这一边:用户自己实际用的那个二进制文件,能不能被复现出来。
作为一个工程判断,这值得细品。如果把关卡接到压力测试上,通过率会低得多,政策也会被再拖上好几年。他们选择接到"简单"的这一边,才让这个开关得以现在就打开。这意味着他们选的是当下就能落地执行的标准,而不是等一个完美的标准 — 这正是 LWN 文章评论区指出的那一点:这项要求的范围仅限于"在 Debian 构建环境的一个实例内构建",可复现性不是非黑即白的二分,而是一道光谱。
reproduce.debian.net 是怎么运转的
架构本身诚实又简单,它就是一个 rebuilderd 实例。
rebuilderd 是 kpcyrd 用 Rust 写的工具,开发始于 2019 年的 Marrakech 峰会,支持 Arch、Fedora、Debian、Tails 的重建。Arch 用的实例大约有 5 个,Fedora 用的有 1 个,Debian 用的有 5 到 6 个。也就是说,这不是专门为 Debian 新造的东西,而是把已经在其他发行版上跑着的东西原样搬了过来。
reproduce.debian.net 做的事情是这样的:
Attempts to bit-for-bit identically rebuild each Debian binary package found in the distribution archive, using the .buildinfo file produced when the buildd originally built the package. For each distributed package, rebuilderd calls debrebuild that calls debootsnap, mmdebstrap and finally sbuild to build that package within a user namespace.
这里的轴心是 .buildinfo 文件。Debian 的构建守护进程(buildd)在构建软件包时,会一并留下一份记录构建环境的 control 文件,写明当时安装了哪些软件包、什么版本。重建时读取这份文件,还原出那个环境,在里面重新构建一遍,再把结果和已发布的二进制文件逐比特比较。
ftp.debian.org 正在分发的二进制软件包
│
│ buildd 最初构建时留下的 .buildinfo
│ (= 当时那次构建环境的记录)
▼
rebuilderd
└─► debrebuild ─► debootsnap ─► mmdebstrap ─► sbuild
└─ 在 user namespace 内重新构建
│
▼
与已发布的二进制文件逐比特比较
│
├─► 相同 ─► good
└─► 不同 ─► bad (用 diffoscope 确认差在哪里)
一个题外话但挺有意思的事实:幻灯片里 Holger 指出,.buildinfo 事实上就是一份 SBOM — 如今人人挂在嘴边的 Software Bill of Materials,和 Debian 早在 2014 年就设计出来的 .buildinfo 文件,大致是同一件东西。
覆盖范围按 2026 年 5 月 9 日发布时的口径是这样的:从 trixie 起覆盖 main 和 non-free-firmware 的全部内容,涵盖的套件有 experimental、unstable、forky、trixie、trixie-security、trixie-updates、trixie-proposed-updates、trixie-backports。架构方面,除 s390x 和 loong64 之外全部覆盖(trixie 还包含 armel)。发布当时,他们为每个架构分别起了一个实例,一共跑着 8 个。
loong64 缺席并不是偶然。根据同一封 5 月 10 日邮件的另一段,loong64 在两周前才刚被加进档案库,因此几乎所有软件包都得在全部架构上重新构建一遍,CI 队列因此积压,邮件附带说明请大家耐心等待。这说明每当有新架构加入,可复现性基础设施也得跟着追上去。
关卡:britney 实际挡住了什么
在 Debian 里,软件包上传到 unstable 之后,满足一定条件就会"迁移"(migration)到 testing。决定这次迁移的软件是 britney,而下一个稳定版发布,就是靠冻结 testing 做出来的。也就是说,挡住 testing 迁移,就意味着这个软件包进不了下一个发布版本。
发布幻灯片的说法是这样的:
britney now finally has been configured to not let (most) unreproducible packages migrate to testing!
这里要留意的词是括号里的 most。不是全部。幻灯片自己列出的实际规则是这样的。
迁移会被挡住的情形:
- 不可复现的新(NEW)软件包
- 回归 — 原本可以复现的软件包,变得不可复现了
紧接着附上的几条限定:
- 可以按需求解除阻塞(can be unblocked on request)
- 可以设允许列表(allowlist)(目前的例子:udeb)
- 特定架构的结果也可以被忽略
发布团队的立场,幻灯片上是这样整理的:
- 所有软件包都应当可复现
- 但目前只挡住新的不可复现软件包和回归
- 不可复现的软件包目前还不算 RC 缺陷,也还不会被自动删除
最后一条道出了这项政策的真实力度。在 Debian 里,RC(release-critical)缺陷是会挡住发布的严重等级,放置太久,软件包会自动从 testing 里被踢出去。不可复现目前还没到那个等级。所以那些已经混进 testing 的不可复现软件包,实际上就成了祖父条款下的既得利益者。
发布者本人还就 forky 明确地钉了一句:
for forky we will be rather liberal with accepting unreproducible packages.
在 forky 上,接受不可复现软件包这件事会相当宽松。对于下一个发布版本 duke,幻灯片以提问的方式留白 — 要不要把 main 里不可复现软件包为零定为目标?打着一个问号,还没定下来。
Paul Gevers 5 月 30 日的后续邮件也是同一个方向。他说原本的发布里忘了提,补充道:
I forgot to mention that while we of course want all packages in testing to reproduce, we recognize there will be exceptions (which numbers hopefully will reduce over time). If you think your package needs an exception, please file a bug against the release.debian.org pseudo package (option 4 "other" in reportbug for now) and explain why you're not able to fix the issue.
例外情况会存在,如果需要,就去 release.debian.org 这个伪软件包上提一个缺陷,解释清楚为什么修不了。申请例外的通道是正式开放的。
归纳起来,这道关卡是一个 棘轮(ratchet) — 把门槛抬高一格,然后不让它再滑回去的装置。"从现在起新进来的必须可复现,原本已经可复现的不能再坏掉。"它不是用来清理既有债务的装置,而是用来阻止债务继续增长的装置。如果这听起来不够震撼,不妨反过来想 — 对一个运转着数万个软件包的项目而言,在没有拦住回归的机制之前就想着提高复现率,无异于往一只漏底的水缸里灌水。
Holger 的幻灯片对这层政治属性写得相当坦率。
100% reproducible is a political decision and nothing technical. We need to change debian-policy!
100% 可复现是一项政治决定,和技术无关。debian-policy 自 2017 年以来,只用"should"这个词说软件包应当可复现地构建,把它改成"must"是一套政策修订流程。所以发布团队选择在政策改变之前,先用迁移关卡把它强制执行起来。用幻灯片的话说,"In practice the release team will enforce this before it becomes official Debian policy."
数字 — 截至 2026 年 5 月 9 日
以下全部是 Holger Levsen 在 MiniDebConf Hamburg 2026 发布会(2026 年 5 月 9 日)幻灯片上写的 reproduce.debian.net 数据,是发布当时的一个快照,现在可能已经变了。
forky(将成为下一个稳定版发布的套件):
| 架构 | good | bad sources |
|---|---|---|
| arch:all | 98.3% | 419 |
| amd64 | 97.2% | 512 |
| arm64 | 97.7% | 408 |
| armhf | 97.1% | 507 |
| i386 | 97.0% | 523 |
| ppc64el | 91.1% | 531 |
| riscv64 | 91.6% | 338 |
作为对照,其他套件:
| 套件 / 架构 | good | bad sources |
|---|---|---|
| unstable arch:all | 98.0% | 515 |
| unstable amd64 | 97.6% | 469 |
| trixie arch:all | 91.7% | 2001 |
| trixie amd64 | 95.6% | 803 |
| trixie-backports arch:all | 79.5% | 53 |
| trixie-backports amd64 | 83.5% | 32 |
| trixie-security arch:all | 82.9% | 17 |
有几点值得读一读。
主力架构和其他架构之间的落差。 amd64 是 97.2%,ppc64el 却只有 91.1%,差了 6.1 个百分点。可复现性是一个需要按架构分别达成的属性,关注和人力集中的架构会先爬上去。关卡之所以附带"特定架构的结果也可以被忽略"这条限定,原因就在这张表里。
backports 偏低。 trixie-backports 是 79.5% / 83.5%,明显偏低。这可以理解为,作为往稳定版上加装新版本的通道,它的构建环境更容易跑偏。
压力测试和真实复现,几乎收敛到了同一个数值。 幻灯片用"The diff between theory and practice today?"这个标题,把两个数字并排摆在一起 — CI(压力测试)上是 96.6%,amd64.reproduce.debian.net(真实复现)上是 96.54%。前面把后者称为"简单"的那一边,但实际上两个数值只差 0.06 个百分点。当然,这两套系统针对的套件和统计方式都不一样,严格来说不是同口径的比较,但作为对"是不是因为测试简单数字才被灌了水"这种怀疑的反证,已经足够有意思了。幻灯片在这里附了一句注解 — "we've only been doing this for 10 months & the freeze..."
可以在自己的系统上直接确认。 装上 debian-repro-status 这个软件包,就能知道自己系统里现在装的软件包,有多少个不可复现。幻灯片上放了发布者自己系统的输出。
$ sudo apt install debian-repro-status
$ debian-repro-status
INFO debian-repro-status > 60/2268 packages are not reproducible.
INFO debian-repro-status > Your system is 97.35% reproducible.
在我看来,这个工具是最令人印象深刻的部分。它意味着可复现性不再只是网站仪表盘上的一个数字,而是变成了在自己笔记本上一行命令就能确认的属性。
是什么在破坏复现
幻灯片列举原因的方式,本身就是个笑话。
Common reasons for unreproducibilities:
timestamps, timestamps, timestamps
timestamps, timestamps, timestamps
build paths, build paths
all the rest
(and somewhere in there there might be backdoors...)
时间戳出现了六次,构建路径两次,剩下的全部只算一次。这就是这个项目过去 12 年里到底在跟什么较劲的总结。最后一行道出了为什么要打这场枯燥的仗 — 说不定哪个角落里,就藏着一个后门。
针对时间戳的标准解法是 SOURCE_DATE_EPOCH。它的规范是这样写的:
SOURCE_DATE_EPOCH specifies the last modification of something, usually the source code, measured in the number seconds since the Unix epoch, ie. January 1st 1970, 00:00:00 UTC.
只要发行版把这个环境变量统一设置好,构建工具就会打上这个值,而不是当前时间。不过也得连带读一下 Reproducible Builds 文档自己附的那条限定 — "Setting the system clock is not enough for reliable reproducible builds."光固定时钟是不够的。SOURCE_DATE_EPOCH 只处理时间戳这一个问题,对非确定性的构建过程、或不稳定的依赖带来的偏差,它无能为力。
给个规模上的感觉:截至 2026 年 5 月 8 日的 CI 统计显示,已经修复了 4,347 个与可复现性相关的缺陷(大部分已推回上游),还有 262 个补丁在排队。12 年里每天推进那么几个,攒出了今天这 97%。没有魔法。
而那份还没搞定的清单,让人保持谦逊。幻灯片说,在最受欢迎的 1,337 个软件包里,有 22 个源码包不可复现,还点了名 — ffmpeg、vlc、bluez、nss、grub2、graphviz、pipewire、qtwebengine、bind9 等等。更扎心的是,build-essential 依赖关系里的 5,500 个软件包中,有 96 个不可复现,名单里有 rustc、python3.12、python3.13、ghc、gcc-13-cross、qemu、valgrind、chromium。
编译器和解释器自己,都还没做到可复现。这是关于"这事为什么难"最精炼的一个说明。
但这不代表没有进展。根据 2026 年 6 月的报告,debian-installer 的上传首次复现成功,debian:sid 容器变成了 100% 可复现。diffoscope 光是那一个月就出了 319、320、321、322、323 五个版本。
诚实的局限
放下想要善意解读的冲动,梳理下来是这样的。
标题比政策更强硬。 "Debian must ship reproducible packages"是一句很强的话,但真正打开的机制只挡住新软件包和回归。既有的不可复现软件包原地不动,既不算 RC 缺陷,也不会被自动删除。发布者明确说了在 forky 上会走宽松路线。
能溜走的门有好几扇。 按需求解除阻塞、允许列表(目前是 udeb)、按架构忽略结果、向 release.debian.org 申请例外。每一条单独看都合理,但合在一起看,这道关卡的实际强度取决于发布团队的自由裁量。
检查的范围很窄。 reproduce.debian.net 原样复制 Debian 自己的构建环境,然后拿去比对。换一条构建路径、换一个内核、换一个语言区域是否也能复现,这道关卡根本不问。正如 LWN 评论指出的,"可复现"不是非黑即白的二分,而是一道光谱,这道关卡只是在那道光谱上选了一个点。Paul Gevers 反驳说这个点恰恰是对用户最重要的那个点,这也站得住脚 — 两个命题可以同时为真。
工具还很粗糙。 这是 Paul Gevers 本人在后续邮件里承认的一点 — 对于因 NT_GNU_BUILD_ID 差异而不可复现的软件包,目前的 diffoscope 日志并不理想。真正的差异被裁掉了,本该只在 dbgsym 软件包里才看得到,而那个包目前还不在自动比较的范围内。也就是说,维护者想搞清楚"为什么不行",日志有时候根本不给答案。
信任集中在一处。 这是发布者自己提出来的问题。幻灯片上写着 — "Because do you really want to put all your trust in me???"眼下的结构,是相信 reproduce.debian.net 这一个特定实例做出的判定。可复现构建本来的用意,是让多个互相独立的当事方各自去验证,验证者只有一个的话,这层用意就只实现了一半。所以幻灯片呼吁大家多架几个 rebuilderd 实例,并把用透明性日志分散信任、做交叉比对列为未来的工作。
那你的 CI 能从这里搬走什么
从这个案例里,有几件事是可以推而广之的。但 Debian 是一个积累了数万个软件包、12 年人力投入的发行版,你的服务不是。不要原样照搬。
可以搬走的东西 1 — 棘轮比目标更管用。 Debian 实际打开的开关,不是"让一切都可复现",而是"挡住新增的债务,挡住回归"。这不是可复现性独有的道理。无论是测试覆盖率、类型覆盖率还是 lint 警告,想一口气清理掉既有债务的尝试大多会失败,而挡住回归的关卡大多会成功。把既有的违规项放进允许列表冻结起来,只挡新增和回归 — 和 Debian 把 udeb 放进允许列表是同一个套路。
可以搬走的东西 2 — 一份记录构建环境的文件。 .buildinfo 的想法很简单:构建的时候,把当时有什么、是什么版本,记在产物旁边。有了它,以后就能还原那个环境;没有它,复现连起点都没有。这大致就是如今大家挂在嘴边的 SBOM,而 Debian 早在 2014 年就做出来了。把 lockfile 提交进版本库、用摘要而不是标签固定基础镜像、记录工具链版本,都是同一路数的实践。
可以搬走的东西 3 — SOURCE_DATE_EPOCH。 12 年的数据得出的结论是,时间戳是最常见的元凶。如果你的工具支持这个环境变量,打开它的成本几乎为零。
可以搬走的东西 4 — 把复现检查放进单独的作业里。 重要的是 rebuilderd 不在构建流水线内部,而是从外部把已发布的产物重新做一遍,这个结构本身很关键。把复现检查内联塞进主 CI,构建时间会翻倍,最后一定会被关掉。部署之后异步重建、异步比对的单独流水线,才是现实中能落地的做法。
不该做的事。 说句实话,逐比特的可复现性,对大多数团队来说是过度的。
- 如果你的部署产物不需要被外人验证 — 内部服务用容器跑,镜像摘要也固定住了 — 那可复现性带来的额外保证并不大,因为"那个摘要就是那个摘要"这件事本来就已经确定了。
- 可复现性真正值钱的场景,是分发对象是大量互不信任的第三方,他们需要各自独立验证源码和二进制文件是否对应 — 发行版、浏览器、钱包、通讯软件、固件。这正是 Debian 花 12 年做这件事的原因。
- 如果你处在中间地带,比起全部都要求可复现,不如只把一个核心产物定为复现对象。Debian 也不是一口气把整棵树都做了。
- 而且,把可复现性当成安全对策来卖是不诚实的。重复前面那句话 — 以可复现的方式构建出来的脆弱软件,依然是脆弱软件。
结语
2026 年 5 月 9 日 Debian 发生的事,可以归纳成这样:britney 开始读取 reproduce.debian.net 的判定,并开始挡住不可复现的新软件包、以及可复现性发生回归的软件包迁移到 testing。截至发布时,forky 的 amd64 复现率是 97.2%,还剩 512 个不可复现的源码包。
这是一项比标题所说要窄得多的举措。例外申请通道开着,有允许列表,可以按架构忽略,既有的不可复现软件包原封不动,而且明确说了在 forky 上会走宽松路线。检查的范围也仅限于复制 Debian 自己的构建环境。
即便如此,这件事依然有意义,因为 12 年来一直只是"should"的东西,第一次真的开始挡住了点什么。修改政策文档需要走一整套项目级的共识流程,但打开这道关卡,只需要改一下 britney 的配置。用最初那份发布邮件的话说,这是代码上的一小步。发布团队先做了那一小步,照幻灯片的说法,选择了"在成为官方 Debian 政策之前,先在实务上强制执行"这条路。
从这里学到的,或许不是可复现性本身,而是这套方法论。把目标宣布为 100%,但只打开棘轮的一格。不去等一个完美的标准,而是选一个现在就能强制执行的标准,正面接下"这是简单标准"的批评,然后回答说,即便如此,对用户重要的还是这一边。至于 duke 会不会以零为目标,目前还是一个问号。
参考资料
- bits from the release team — Paul Gevers, debian-devel-announce, 2026 年 5 月 10 日(原始公告)
- Re: bits from the release team — Paul Gevers, debian-devel, 2026 年 5 月 30 日(例外流程与两套测试系统的区别)
- reproduce.debian.net: Reproducing Debian in the real world — Holger Levsen, MiniDebConf Hamburg 2026, 2026 年 5 月 9 日(全部数据的出处)
- Reproducible Builds in May 2026 — 月度报告
- Reproducible Builds in June 2026 — 月度报告(debian-installer、debian:sid 容器、diffoscope 319–323)
- Debian to require reproducible builds — LWN, 2026 年 5 月 11 日
- reproduce.debian.net — 重建判定仪表盘
- rebuilderd — kpcyrd 用 Rust 编写的重建守护进程
- SOURCE_DATE_EPOCH 规范
- 可复现构建的定义
- Debian Release Team: Debian Must Now Ship Reproducible Packages — Phoronix
- Debian 14 cracks down on unreproducible packages — The Register