Homebrew构建溯源技术揭秘
Homebrew快速回顾
Homebrew是macOS和Linux的开源包管理器。其核心是homebrew-core,这是一个包含7000多个精选开源软件包的默认仓库,随Homebrew默认提供。homebrew-core的软件包每年被下载数亿次,构成了使用macOS进行开发的程序员的基础工具套件(如node、openssl、python、go等)。
Homebrew的一个核心特性是使用bottles:每个软件包的预编译二进制分发,可加速brew install并确保在不同机器间的一致性。当新公式(描述软件包如何构建的机器可读说明)更新或添加到homebrew-core时,Homebrew的CI(通过BrewTestBot编排)会自动触发创建这些bottles的过程。
成功构建并测试bottle后,进入分发阶段。BrewTestBot将编译好的bottle上传到GitHub Packages(Homebrew为homebrew-core选择的托管服务)。这一步确保用户可以通过Homebrew的命令行界面直接访问和下载最新软件版本。最后,BrewTestBot更新对变更公式的引用以包含最新的bottle构建,确保用户在下一次brew update时收到更新的bottle。
总之:Homebrew的bottle自动化通过将人类从软件构建过程中移除,提高了homebrew-core的可靠性。这样做也消除了一种特定的供应链风险:通过将bottle构建从单个Homebrew维护者转移到Homebrew CI,降低了维护者被入侵的开发机器被用于对更大Homebrew用户群发起攻击的可能性。
同时,该方案还有其他方面可能被攻击者利用:拥有足够权限的攻击者可能直接将恶意构建上传到homebrew-core的bottle存储,可能利用警报疲劳诱使用户安装,尽管存在校验和不匹配。更令人担忧的是,被入侵或恶意的Homebrew维护者可能秘密替换bottle及其校验和,导致所有用户从此静默安装被入侵的软件。
这种情况是软件供应链中一个独特但严重的弱点,构建溯源可以很好地解决这个问题。
构建溯源
简而言之,构建溯源提供密码学可验证的证据,证明软件包确实由预期的"构建身份"构建,而非被有权限的攻击者篡改或秘密插入。实际上,构建溯源提供了强密码学摘要的完整性属性,结合了构件由公开可审计的构建基础设施产生的断言。
对于Homebrew,该"构建身份"是GitHub Actions工作流,意味着每个bottle构建的溯源证明有价值的元数据,如GitHub所有者和仓库、触发工作流的分支、触发工作流的事件,甚至工作流运行的精确git提交。
这些数据(及更多!)封装在机器可读的in-toto声明中,使下游消费者能够对单个证明表达复杂策略:
构建溯源和更一般的溯源不是万能药:它们不能替代应用级保护来防止软件降级或混淆攻击,也不能防止软件本身恶意或被入侵的"与撒旦私下对话"场景。
尽管如此,溯源是可审计供应链的有价值构建块:它通过将攻击者承诺到公开可验证时间线上的公共构件,迫使攻击者公开行动,并减少攻击者可以隐藏其负载的不透明格式转换数量。这在最近xz-utils后门等案例中尤其突出,攻击者利用上游源仓库和后门tarball分发之间的脱节来维持攻击的隐蔽性。换句话说:构建溯源不会阻止完全恶意的维护者,但会迫使他们的攻击公开以供审查和事件响应。
我们的实现
我们对Homebrew构建溯源的实现基于GitHub的新构件证明功能。我们早期(私有测试版)访问了该功能,包括generate-build-provenance操作和gh证明CLI,这使我们能够快速迭代设计,轻松集成到Homebrew的现有CI中。
这为我们提供了所有当前和未来bottle构建的构建溯源,但我们面临一个问题:Homebrew有很长的"尾巴"是仍被公式引用的现有bottle,包括在GitHub Actions不再支持的(架构,操作系统版本)元组上构建的bottle。这个尾巴被广泛使用,使我们陷入两难:
尝试重建所有旧bottle。这在技术性和逻辑上不可行,既因为GitHub Actions自身支持的运行器变化,也因为macOS版本间的重大工具链变化。
仅验证存在的bottle构建溯源。这实际上会在构建溯源的预期安全合约中打洞,允许攻击者通过剥离任何溯源元数据降级到较低完整性程度。
这些解决方案都不可行,因此我们寻求第三种。既不重建世界也不选择性验证,我们决定创建一组回填构建证明,由完全不同的仓库(我们的tap)和工作流签名。每个bottle背后有回填证明,验证看起来像瀑布:
我们首先检查与"上游"仓库绑定的构建溯源,具有预期工作流,即Homebrew/homebrew-core与publish-commit-bottles.yml。
如果"上游"溯源不存在,我们在指定截止日期前检查回填身份的回填证明,即trailofbits/homebrew-brew-verify与backfill_signatures.yml。
如果两者都不存在,则产生硬失败。
这给了我们两全其美:回填允许我们在没有溯源或证明时统一失败(消除降级),而不必重建每个旧的homebrew-core bottle。截止日期然后增加了额外保证层,防止攻击者尝试使用回填证明注入意外bottle。
我们期望回填bottle证明的尾巴随时间减少,随着公式转向新版本。一旦所有可达bottle完全转向,Homebrew将能够完全移除回填检查并断言完美溯源覆盖!
今日验证溯源
如上所述:此功能处于早期测试版。我们仍在解决已知性能和UX问题;因此,我们不建议普通用户尝试。
尽管如此,冒险的早期采用者可以通过两个不同界面尝试:
专用的brew verify命令,可通过我们的第三方tap获得
早期的上游集成到brew install本身。
对于brew verify,只需安装我们的第三方tap。安装后,brew verify子命令将可用:
|
|
未来,我们将与Homebrew合作将brew verify直接上游化到brew作为开发者命令。
对于brew install本身,在环境中设置HOMEBREW_VERIFY_ATTESTATIONS=1:
|
|
无论你选择如何试验这些新功能,某些注意事项适用:
brew verify和brew install内部包装gh CLI,如果未安装将在本地引导gh。我们计划中期用纯Ruby验证器替换gh证明的使用。
构建溯源测试版依赖于认证的GitHub API端点,意味着gh必须具有合适的访问凭证。如果brew verify或brew install初始失败,尝试运行gh auth login或设置HOMEBREW_GITHUB_API_TOKEN为具有最小权限的个人访问令牌。
如果在试验brew install时遇到错误或意外行为,请报告!同样,对于brew verify:请直接将任何报告发送给我们。
展望未来
以上所有内容涉及homebrew-core,Homebrew公式的官方仓库。但Homebrew也支持第三方仓库(“taps”),它们提供了少数但显著数量的总体bottle安装。这些仓库也应得构建溯源,我们有实现的想法!
更远地,我们计划尝试源溯源:Homebrew的公式已经哈希固定其源构件,但我们可以更进一步,额外断言源构件由URL中潜在或嵌入公式规范的仓库(或其他签名身份)产生。这将与GitHub的构件证明很好地组合,启用假设的DSL:
请关注此领域的进一步更新,并一如既往,随时联系我们!我们有兴趣合作改进其他开源打包生态系统,并乐意听取您的意见。
最后但同样重要,我们要感谢Homebrew的维护者在此过程中的开发和审查。我们还要感谢Dustin Ingram对原始提案的创作和设计,GitHub包安全团队,以及Michael Winser和Alpha-Omega的其余成员对更好、更安全软件供应链的愿景和支持。