让可重复构建肉眼可见

我们的生活正全面步入数字领域,个人数据流转于各类软件之中。随着软件对生活的介入程度日益加深,确保软件值得信赖已变得前所未有的重要。目前,手机用户往往只能盲目相信开发者不会不当共享或滥用其私人数据。显然,许多软件供应商已经辜负了这份信任。事实证明,仅仅寄希望于开发者的良知是行不通的。当用户被迫去信任某家公司而非软件本身时,平台巨头的权力会被放大,而这种权力中心化往往导致监管与问责的缺失。虽然开源、自动化审查和代码审计可以验证源码的可靠性,但计算机实际运行的是二进制文件而非源代码。因此,应用发布流程必须能够确保设备上运行的二进制文件与受信任的源码完全一致。这套流程被称为可重现构建。

F-Droid 维护着一套完整的自由软件系统,旨在以全自动化的方式安全地发布 Android 应用。自 2010 年起,f-droid.org 网站便一直基于该系统运行。通过可复现构建技术,我们可以将设备上运行的实际应用与其原始代码建立起强有力的关联。一旦源代码经过了彻底的审查与理解,可复现构建就能让我们将这份信任同样延伸到最终的二进制安装包上。若要确保软件值得信赖,可复现构建是不可或缺的。因此,F-Droid 从 2015 年开始 就一直在提供可复现构建版本。

去年,我们获得了 NLnet 基金会的资助,用于重构我们的构建体系,并效仿 Debian 的可复现构建标准。我们的核心目标是:通过大规模重构来排查可重现性问题;简化验证重构工具的部署流程;以及向最终用户提供更多关于可重现构建的透明信息。这将提升整个Android 自由软件生态系统的安全性与可靠性。

将可重复构建呈现给用户

尽管可复现构建至关重要,但要将其意义直观地呈现给用户却很困难。可复现构建的核心应在于通过一个可审计的过程实现自动化确认。为了充分发挥其效用,这种机制不能建立在“假设某人操作无误”的基础之上。应用开发者由于亲手构建程序,非常容易理解这一点,因为他们可以亲自检查并运行这些流程。然而,我们该如何让普通用户也能体验到这种保障呢?

目前,f-droid.org 上的每个应用详情页都包含一个“可复现状态”链接。我们的验证服务器会根据构建结果显示 ✔️️ 或 💔:✔️️ 表示复现构建程序生成了完全相同的 APK 文件,而 💔 则表示未能复现。IzzyOnDroid 仓库则开发了一套更完善的徽章系统,为每个复现构建程序显示 ✅。此外,还有一个五级示意图,展示了构建过程中涉及的具体环节。尽管像 Archlinux 和 Debian 这样的项目也提供了面向开发者的工具来反馈可复现构建的情况,但它们并未在包管理 GUI 等面向用户的界面中展示这些信息。

像这样的徽章很容易添加,用户也习惯于看到它们。例如,当使用HTTPS时,浏览器会显示一个 🔒。Keyoxide 使用 OpenPGP 来验证各种在线身份之间的链接,并旨在使其对非技术用户易于使用。Bluesky 已经试验过具有一定程度 身份认证的徽章。然后是 Twitter 著名的徽章,它基本上只代表用户付了钱 或 Elon Musk 批准了它。

这些小徽章到底意味着什么?它们能提供什么?这些徽章代表着网站的控制者,因此它们仍然需要信任一个人。如何向用户可靠地展示可信度?可复现构建最终是为了运行一个已知的过程,以确认实际上没有发生任何改变。如果用户只信任徽章,那么他们就不会运行验证过程,我们又回到了信任人而不是源代码的状态。就用户感知而言,使用徽章会让我们陷入非常复杂的境地,甚至可能误导那些不了解在网页上添加一个徽章是多么微不足道的人。

用户能够运行一个有意义的验证过程吗?

F-Droid的创建目标是为用户提供经过人工审核的已验证免费软件。这些是相当技术性的过程,非技术人员不太可能遵循。这意味着大多数用户依赖他人的判断。F-Droid的流程及其生成的数据会被发布,以便其他人轻松审核结果并提供他们的判断。

同样,可重现构建只有在知道如何从源代码构建应用程序时才有实际意义。信任来自于有人实际运行重建过程,并看到生成的二进制文件是相同的。很少有用户愿意思考运行重建。如果应用程序只是标记一个复选框,那意味着用户必须信任有人做了正确的事情,没有信息表明他们可以自己验证。将徽章链接到更多信息会有帮助,但即便如此,这仍然不同于他们自己运行验证过程。

可重复构建在用于捕捉构建之间的差异时最有用。然后,这些差异可以被突出显示,从而提供潜在问题的线索。一个非开发人员可以验证什么样的内容?HTTPS 提供了一个更简单的技术示例。如果用户想要验证 HTTPS,他们可以将 URL 放入不同的浏览器或像 wget 这样的工具中。验证 HTTPS 的门槛并不高。互联网用户通常理解 URL 的基本含义及其代表的内容。在几十年的努力下,用户已经被教育如何理解域名和 URL。用户也清楚地知道,一个 URL 可以代表一个组织、公司或个人。

在 f-droid.org 上,应用程序的元数据可以包含一个 Binaries: URL,原始开发者可以在此发布他们自己签名的版本。构建过程会从该 URL 下载签名版本,以便与刚在我们的 buildserver 上构建的版本进行比较。该 URL 也可以呈现给用户。他们可以理解 https://ltt.rs/fdroid/repo/Ltt.rs-0.4.4-release.apk 和 https://f-droid.org/repo/rs.ltt.android_19.apk 来自不同的域名,这些域名可以分别进行控制。当然,这些 URL 也可能指向同一服务器上的同一文件,因此仍然需要一定程度的信任。这类情况更像是证据而非证明。Android 应用上的签名必须来自特定的签名密钥。但是,仍然不得不信任某人在选择加密方法、管理签名密钥等方面做了正确的处理。域名就像签名密钥:域名和签名密钥 可以 是分开的。私有签名密钥如果被共享或泄露,就会失去其意义,不再能够证明签名内容来自可信方。

用户可以用这些 URL 做什么?他们可以下载这些 URL,并使用任何自己会用的工具进行比较。这可以是像 https://try.diffoscope.org/ 这样简单的在线工具,也可以是本地安装的工具。如果有大量用户以多种不同方式进行验证,整个生态系统将受益最多。Keybase 显示社交媒体账户之间的链接,并提供客户端应用可以验证的加密证明,展示了一种基于 URL 去中心化验证过程的方法。

归根结底,很明显,可复现构建应该被视为需要采取的行动,而不是某人已完成的复选框。为此,在 f-droid.org 上每个应用的可复现性状态页面列出了该应用的所有验证尝试,并指向 diffoscope 输出以突出问题所在。在 F-Droid 客户端页面 上,很容易看出在升级 Gradle 时可复现构建被破坏的情况。我们利用这些信息修复了 Gradle 设置,使构建再次可复现。即使是非技术用户,也可以进行某些类型的可复现性验证,这为探索提供了非常丰厚的土壤。

运行重建器

理论上,可重现构建意味着任何人都可以确切地知道自己计算设备上运行的内容。为了实现可重现构建的全部承诺,第三方需要独立验证并运行构建过程,以确认源码与二进制文件匹配。例如,官方的 Debian 软件包也由 Linux Mint 和 gLinux 重新构建。F-Droid 生态系统是围绕仓库组织的。每个仓库都是一个信任根,我们投入了大量精力让用户理解这一点。这意味着需要向只熟悉集中式生态系统的用户解释,比如“我的设备自带的应用商店”。这也适用于重新构建者,因为仓库是重新构建者获取所有源代码的入口点。对于 f-droid.org,该入口点称为 fdroiddata;对于 Guardian Project 仓库,则是 https://gitlab.com/guardianproject/fdroid-metadata,等等。因此,F-Droid 重新构建者,也称为 验证服务器,是基于该仓库的 Git URL 和提交 ID 的。

无法自行运行重建的用户将不得不信任其他人来执行该操作。大多数用户已经信任许多人和组织。我们的目标是让任何人都能够非常容易地设置和运行重建器。然后,用户信任的组织,比如 EDRi、CIPIT 或 EFF,可以运行重建器并发布结果。例如,公司可以为 f-droid.org 运行自己内部的重建器,就像 Google 为他们的 gLinux 重新构建 Debian/testing 一样。这些重建器 可以提供 签名数据流,以建立用户信任的图像。如果你有兴趣运行重建器,请 联系我们!

重写 buildserver

这也是一个重新思考核心 buildserver 基础设施运作方式契机。为了实现大规模批量构建,构建环境的效率亟待提升。该架构是 F-Droid 现存最古老的部分之一,自项目诞生以来一直处于自然演进之中。

一个仅使用自由软件的小型团队,竟然能构建出如此稳定且安全的自动化系统,这确实令人惊叹。Debian 为这类系统的构建奠定了坚实基础,对此我们深表感激。我们的 buildserver 就是一个绝佳的例子:其架构自 2010 年搭建以来,在几乎不需要 F-Droid 团队人工干预的情况下,始终运行得非常可靠。令人欣慰的是,我们的项目规模已发展到了一个新阶段,以至于现有的单轨架构已难以应付庞大的应用编译需求。这套基础架构最初并非为支撑目前的发布规模而设计,虽然通过大量的巧妙优化和技术手段勉强维持现状,但为了提升处理速度并满足用户对 f-droid.org 日益增长的应用需求,我们必须对其进行更新换代。

我们借此机会彻底重构了 buildserver 的基础架构。F-Droid 的工作原理是将源代码元数据转换为 Android APK 等二进制包文件。我们技术栈中的许多组件其实可以由更优秀的现有软件替代。首先,我们希望重构工具链,使其不再受限于特定的工具或框架。例如,目前我们必须依赖 Vagrant 来运行虚拟机,且 fdroid build 命令也依赖用于控制 Vagrant 的自定义代码。其次,只要发现现有框架的性能优于我们自有的代码,我们就会致力于将其替换。

像 Python 和 Blender 这样理念相似的项目都基于 Buildbot 构建。此外,像 WebKit 和 LLVM 这样拥有丰富维护资源的项目也依赖 Buildbot,这确保了该工具能得到良好的维护。它还配备了详尽的文档。相比于过去那种定制化、缺乏文档且需自行维护的旧代码,这无疑是一个巨大的进步。同时,我们也从那些不再使用 Buildbot 的项目身上吸取了经验:

  • 从 Flathub 的 Buildbot 配置中,我们学到了很多经验,因为从架构上看,它与我们的项目高度相似。他们最近转而采用了一套专有的替代方案,以便更紧密地接入 GitHub 这一专有平台,同时应对硬件赞助商撤资带来的挑战。与我们目前的努力方向一致,Flathub 也在推动其工具链的去平台化,使其能够独立于底层的构建自动化平台运行。

  • Chromium 的 CI 在 Buildbot 上运行了多年。他们之所以迁移到自己开发的 LUCI 项目,是因为他们需要比 Buildbot 更为复杂的功能。LUCI 并不适合 f-droid.org,因为它缺乏文档,比 Buildbot 复杂得多,而且似乎除了 Google 之外没有其他用户。等到 f-droid.org 达到 Chromium 的规模时,LUCI 或许才值得考虑;在此之前,我们可以像当年的 Chromium 一样,依托 Buildbot 共同成长。

我们新的 Buildbot 实例已经投入 https://verification.f-droid.org 的生产环境使用。它支持四个并行构建,迈出了实现大规模并行化的第一步。由于它是在 Podman 而非 Vagrant/libvirt 中运行构建,因此可以托管在廉价的 VPS(虚拟专用服务器)上。目前,我们的 Buildbot 设置还有一个预发布实例,它在裸机上运行 Vagrant/libvirt。该代码将取代生产环境中的 buildserver。生产环境的构建需要 QEMU/KVM 提供的强隔离性,因此 Podman 并不适用于生产环境。我们目前正在安排安全审计,之后我们将把这套设置推向生产环境,并支持并行构建。

该项目为生产流程所有阶段的并行化奠定了基础,包括构建软件包、生成索引、APK 签名等。Buildbot 的配置也使得通过增加硬件来运行更多并行任务变得非常容易。核心目标是建立一个固定的每日时间表,在每天结束时发布所有已完成的构建版本。这样我们就能根据需要轻松增加资源,以确保按计划推进。

这项工作由来自你和 NLnet 的捐赠支持。为了确保 F-Droid 可以继续这项工作,请访问捐赠页面并做出你力所能及的贡献