本文介绍了 Homebrew 项目与 Alpha-Omega 和 OpenSSF 合作,为 homebrew-core 增加构建溯源(build provenance)功能。通过密码学方式证明 Homebrew CI 构建的所有瓶子(bottles)的真实性,从而提高软件供应链的透明度和安全性,降低恶意内部人员攻击的威胁。目前该功能处于公开测试阶段,建议高级用户尝试。
去年 11 月,我们宣布了与 Alpha-Omega 和 OpenSSF 的合作,以向 Homebrew 添加构建来源。
今天,我们很高兴地宣布,这项工作的核心已上线并进入公开测试阶段:homebrew-core 现在正在对官方 Homebrew CI 中构建的所有 bottles 进行加密证明。你可以使用我们的 brew verify
命令(目前是外部的,但很快就会上游)验证这些证明,你可以从我们的 tap 安装:
这意味着,从现在开始,由 Homebrew 构建的每个 bottle 都将附带一个加密可验证的声明,将 bottle 的内容绑定到生成它的特定工作流和其他构建时元数据。此元数据包括(除其他外)生成 bottle 的工作流的 git
提交和 GitHub Actions 运行 ID,使其成为 SLSA Build L2 兼容的证明:
实际上,这为 Homebrew 构建过程注入了更高的透明度,并且通过使用户不可能被欺骗安装非 CI 构建的 bottles,从而减少了受损或恶意内部人员构成的威胁。
这项工作仍处于早期 beta 阶段,并且涉及 Homebrew 和 GitHub 中仍在积极开发中的功能和组件。因此,我们不建议普通用户立即开始验证来源证明。
但是,对于喜欢冒险的人,请继续阅读!
Homebrew 是 macOS 和 Linux 的开源软件包管理器。Homebrew 的掌上明珠是 homebrew-core,这是一个默认的存储库,包含 7,000 多个精选的开源软件包,这些软件包默认随 Homebrew 的其余部分一起提供。homebrew-core 的软件包每年被下载数亿次,并构成了使用 macOS 进行开发的程序员的基本工具套件(node
、openssl
、python
、go
等)。
Homebrew 的核心功能之一是使用 bottles:每个软件包的预编译二进制发行版,可加快 brew install
速度并确保其在各个机器之间的一致性。当新的 formula(描述如何构建软件包的机器可读描述)更新或添加到 homebrew-core 时,Homebrew 的 CI(通过 BrewTestBot 编排)会自动触发一个流程来创建这些 bottles。
成功构建和测试 bottle 后,就该进行分发了。BrewTestBot 获取已编译的 bottle 并将其上传到 GitHub Packages,这是 Homebrew 为 homebrew-core 选择的托管服务。此步骤确保用户可以直接通过 Homebrew 的命令行界面访问和下载最新的软件版本。最后,BrewTestBot 更新对更改 formula 的引用,以包括最新的 bottle 构建,确保用户在下次 brew update
时收到更新的 bottle。
总而言之:Homebrew 的 bottle 自动化通过从软件构建过程中移除人员来提高 homebrew-core 的可靠性。通过这样做,它还消除了软件供应链中的一种特定风险:通过将 bottle 构建从单个 Homebrew 维护者提升到 Homebrew CI 中,它降低了维护者的受损开发机器可能被用于对更大的 Homebrew 用户群发起攻击的可能性 1。
与此同时,攻击者可以利用此方案的其他方面:具有足够权限的攻击者可能会将恶意版本直接上传到 homebrew-core 的 bottle 存储,从而可能利用 警报疲劳 来欺骗用户安装,尽管校验和不匹配。更令人担忧的是,受损或流氓的 Homebrew 维护者可能会秘密地替换 bottle 和其校验和,从而导致所有用户在以后被静默地入侵。
这种情况是软件供应链中的一个独特但仍然严重的弱点,而 构建来源可以很好地解决这个问题。
简而言之,构建来源提供了加密可验证的证据,证明软件程序包实际上是由预期的“构建身份”构建的,并且没有被特权攻击者篡改或秘密插入。实际上,构建来源提供了强大的加密摘要的完整性属性,并断言该工件是由可公开审核的构建基础设施生成的。
对于 Homebrew,该“构建身份”是 GitHub Actions 工作流,这意味着每个 bottle 构建的来源都证明了有价值的元数据,例如 GitHub 所有者和存储库、触发工作流的分支、触发工作流的事件,甚至是工作流从中运行的确切 git
提交。
此数据(以及更多!)被封装在机器可读的 in-toto 语句中,使下游消费者能够表达对单个证明的复杂策略:
构建来源和更普遍的来源不是万能药:它们不能替代应用程序级别的保护措施来防止软件降级或混淆攻击,并且它们不能阻止“ 与撒旦的私人对话” scenario,即软件本身是 恶意的或受损的。
尽管如此,来源 是 可审核的供应链的重要组成部分:它通过促使攻击者在可公开验证的时间线上提交到公共工件,从而迫使攻击者公开,并减少了攻击者可以隐藏其有效负载的不透明格式转换的数量。这在最近的 xz-utils 后门等案例中尤其突出,其中攻击者使用 上游源代码存储库与后门 tarball 分发之间的脱节 来维持其攻击的隐蔽性。换句话说:构建来源不会 阻止 完全恶意的维护者,但它 会 迫使他们的攻击公开以供审查和事件响应。
我们为 Homebrew 实施构建来源是基于 GitHub 的新 工件证明 功能构建的。我们获得了该功能的早期(私有测试版)访问权限,包括 [generate-build-provenance](https://github.com/github-early-access/generate-build-provenance)
操作和 [gh attestation](https://cli.github.com/manual/gh_attestation)
CLI,这使我们能够快速迭代一种可以轻松集成到 Homebrew 预先存在的 CI 中的设计。
这为我们提供了所有 当前和未来 bottle 构建的构建来源,但是我们面临一个问题:Homebrew 有很长的预先存在的 bottle “尾巴”,这些 bottle 仍然在公式中被引用,包括在 GitHub Actions 不再支持的(架构,操作系统版本)元组上构建的 bottle 2。此尾巴被广泛使用,这使我们陷入两难境地:
这些解决方案都不可行,因此我们寻求第三种解决方案。我们没有重建世界或选择性地验证,而是决定创建一组 回填的 构建证明,这些证明由完全不同的存储库(我们的 tap)和 工作流 签名。借助每个 bottle 后面的回填证明,验证看起来像一个瀑布:
publish-commit-bottles.yml
的 Homebrew/homebrew-core
。backfill_signatures.yml
的 trailofbits/homebrew-brew-verify
。这为我们提供了两全其美的优势:如果不存在来源或证明,则回填允许我们统一失败(消除降级),而无需重建每个旧的 homebrew-core bottle。然后,截止日期添加另一层保证,防止攻击者尝试使用回填证明来注入意外的 bottle。
我们希望随着 formula 转向较新的版本,回填 bottle 证明的尾巴会随着时间的推移而减少。一旦所有可到达的 bottles 都完全转向,Homebrew 将能够完全删除回填检查并断言完美的来源覆盖率!
如上所述:此功能处于 早期 beta 阶段。我们仍在解决已知的性能和 UX 问题;因此,我们 不 建议普通用户现在尝试。
话虽如此,喜欢冒险的早期采用者可以通过两种不同的界面尝试一下:
brew verify
命令,可通过我们的 第三方 tap 获得brew install
本身。对于 brew verify
,只需安装我们的第三方 tap。安装完成后,brew verify
子命令将变为可用:
brew update
brew tap trailofbits/homebrew-brew-verify
brew verify --help
brew verify bash
展望未来,我们将与 Homebrew 合作,将 brew verify
作为开发人员命令直接上游到 brew
中。
对于 brew install
本身,请在你的环境中设置 HOMEBREW_VERIFY_ATTESTATIONS=1
:
brew update
export HOMEBREW_VERIFY_ATTESTATIONS=1
brew install cowsay
无论你选择如何使用此新功能进行试验,都适用某些注意事项:
brew verify
和 brew install
都在内部包装 gh CLI,如果尚未安装,将在本地引导 gh
。我们打算在中期内用纯 Ruby 验证器替换我们对 gh attestation
的使用。gh
必须有权访问合适的访问凭据。如果你在使用 brew verify
或 brew install
时遇到初始故障,请尝试运行 gh auth login
或将 HOMEBREW_GITHUB_API_TOKEN
设置为具有最低权限的 个人访问Token。如果你在使用 brew install
进行实验时遇到错误或意外行为,请 报告错误!同样,对于 brew verify
:请将任何报告 直接发送给我们。
以上所有内容都与 homebrew-core 有关,它是 Homebrew 公式的官方存储库。但是 Homebrew 还支持第三方存储库(“taps”),这些存储库提供了 少数– 但– 重要 总 bottle 安装数量。这些存储库 也 应该有构建来源,我们 有一些想法 来实现这一目标!
更进一步,我们计划尝试 来源来源:Homebrew 的公式已经对它们的来源工件进行哈希固定,但是我们可以更进一步,并另外断言来源工件是由存储库(或其他签名标识)生成的,该存储库潜伏在其 URL 中或以其他方式嵌入到公式规范中。这将与 GitHub 的 工件证明 很好地结合在一起,从而启用假设的 DSL:
请继续关注此领域的更多更新,并且一如既往,请随时 联系我们!我们有兴趣合作改进 其他 开源 打包 生态系统,并且很乐意听到你的来信。
最后但并非最不重要的一点是,我们要感谢 Homebrew 的维护者 在整个过程中对他们的开发和审查。我们还要感谢 Dustin Ingram 在 原始提案 中的著作和设计、GitHub Package Security 团队,以及 Michael Winser 和 Alpha-Omega 的其余成员,感谢他们对更好、更安全的软件供应链的愿景和支持。
1 在不久的过去,Homebrew 的 bottles 是由维护者在他们自己的开发机器上生成的,并上传到共享的 Bintray 帐户。Mike McQuaid 的 2023 年演讲 提供了关于 Homebrew 向 CI/CD 构建过渡的历史的精彩概述。
2 或者很容易通过 自托管 runners 提供,Homebrew 将其用于某些构建。
- 原文链接: blog.trailofbits.com/202...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!