NixOS 与密钥管理

Lobsters Hottest 工具

摘要

教程介绍 NixOS 的密钥管理选项,比较 sops-nix、agenix 和 ragenix 工具,并提供使用 sops-nix 进行加密密钥管理的实际示例。

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

缓存时间: 2026/05/08 22:32

# NixOS 与密钥管理 来源:https://isabelroses.com/blog/nixos-and-secrets/ 首先说明一下,我使用 NixOS 已经有 3 年了,期间用过 NixOS 上主流的 6 种(如果算上 ragenix 则是 7 种)密钥管理工具。包括: - sops-nix(https://github.com/Mic92/sops-nix) - agenix(https://github.com/ryantm/agenix)或 ragenix(https://github.com/yaxitech/ragenix) - 充分发挥文件系统作用但会丧失另一台机器上的可复现性 - 将密钥放入私有 git 仓库 - 使用 git-crypt 将密钥与主仓库一起存储 - 直接将密钥写入 nix 配置 在继续之前,我想说的是,如果你打算分享你的机器或公开你的配置,**绝对不要使用后三种方法**。这是因为 nix store 是全局可读的,有权限访问该机器的人都能读取密钥。在撰写本文时,我发现这一点尤其重要,因为存在以下漏洞:CVE-2026-31431(copyfail)(https://copy.fail/)以及 CVE-2026-43284 和 CVE-2026-43500(dirtyfrag)(https://github.com/V4bel/dirtyfrag)。因此,我不会介绍这些选项。但这并不是说我在这方面是完全清白的。我至少泄露过两次密钥¹(https://github.com/isabelroses/dotfiles/blob/f84c2265720107530d9d9c85e61aed47bc2f839c/hosts/hydra/settings.nix#L63)²(https://github.com/isabelroses/dotfiles/blob/796597ead9c7ea70413faae1a695cb9fb43c9536/env.nix),如果你进一步查找,肯定还能找到更多。 ## sops-nix 我对 sops-nix 的态度可以说是又爱又恨。它是我最初使用的密钥管理工具,但在我 3 年前刚开始使用时,它真的很难上手和配置。对于当时像我这样的非技术用户来说,我花了很大功夫才让它工作起来。这也最终导致我因为不理解它而删除了它。然而,现在回想起来,它其实比我想象的更容易,而且文档也大大改进了。现在 sops 原生支持使用 SSH 密钥来加密和解密密钥,这是一个巨大的进步。遗憾的是,sops-nix 在这方面落后了,参见 sops-nix#779(https://github.com/Mic92/sops-nix/pull/779)和 sops-nix#922(https://github.com/Mic92/sops-nix/pull/922)。 sops-nix 的使用方法是创建一个 yaml 文件来制定加密和解密密钥的规则。例如: ```yaml keys: - &isabel ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMQDiHbMSinj8twL9cTgPOfI6OMexrTZyHX27T8gnMj2 creation_rules: - path_regex: secrets/*.yaml key_groups: - age: - *isabel ``` 然后你可以使用 `sops` 命令行工具来加密和解密密钥。命令可能类似于 `sops secrets/shush.yaml`。这将打开你选择的编辑器来配置 yaml 文件。退出编辑器后,数据将被加密,可能看起来像这样: ```yaml hello: ENC[AES256_GCM,data:5ar0KQ==,iv:WpVEI/BetAloDP/9+4y28udJ04Loh4EBXFm5E8Sln7s=,tag:15IWu728tKQUYJHx9roVrQ==,type:str] sops: age: - recipient: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMQDiHbMSinj8twL9cTgPOfI6OMexrTZyHX27T8gnMj2 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDk1NDQzZyBuYnhk djVoS2F4dHZMUE9LUE5xb3htamVDTnpoTVBPN05BOEZheFJ6a3hJCk1tZ0JoMlhJ WWdrVEViUC91VXk3emVETUdSV2tDcHd5dTlKYlJjWGhxcm8KLS0tIFlaczBRRXVQ emtZZlUvdEFhU29YSnc3dHNOWHdlamtORCtwN0wxdGQ2ekUKNYpnUt83rFILe/A2 RiXGYQoDTj3NF6t5szFWeCWXftWZFmsLBhQ59PDpfnrk+cHWXILhxIifrJjlDoHh 9+i8Yw== -----END AGE ENCRYPTED FILE----- lastmodified: "2026-05-08T15:21:51Z" mac: ENC[AES256_GCM,data:o7aa6vz7qAkS93XPK9adlT5b5382n5c1egTGGft847mYkCM6A2TAOQhMdcrHsN90aY7f64rglt0LaKFrUBOAh8hN04cSvNLykJ7iYYFq+rnADt3HQbjyVcYcZKTeMJ+797Uus26CW24reFENTtqum6VeL1FU78bVEh6/eS03V0E=,iv:Z2w4RbPC4c16VvxAPi4kydR+cNoEkKr4KsXoKHjn+OY=,tag:i+2eSHAscqBvHdZI9T250A==,type:str] unencrypted_suffix: _unencrypted version: "3.12.2" ``` 这是完全正常的,你可以用之前相同的命令继续编辑它们。文件加密后,你仍然需要将配置引入其中。sops-nix 附带了一个模块来处理这些繁琐的工作,所以典型的配置可能看起来像这样: ```nix { sops = { defaultSopsFile = ./secrets/shush.yaml; age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ]; secrets."hello" = { owner = "isabel"; group = "users"; mode = "0400"; }; }; } ``` 在激活时,sops-nix 使用主机的 SSH 密钥解密文件,并将明文放到 `/run/secrets/`,这是一个 tmpfs,所以密钥永远不会触及磁盘。任何需要该值的程序只需要读取该路径。另一个我 heavily 依赖的功能是模板。如果你的配置在用户之间共享或被其他人引用,这尤其有用。但除此之外也有用途,例如,如果一个服务想要一个混合了纯文本和一到两个密钥值的配置文件,你不需要加密整个文件: ```nix { config, ... }: { sops.templates."mailserver.env".content = '' SMTP_USER=isabel SMTP_PASSWORD=${config.sops.placeholder."mailserver/smtp_password"} ''; } ``` ## Agenix Agenix 与 sops-nix 的方式不同,因为它让你感觉更像是 nix,因为你通过 `secrets.nix` 文件来配置所有密钥及其访问权限。值得注意的是,你也可以配置哪些密钥可以访问哪些密钥。这个文件可能看起来有点像这样: ```nix let isabel = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL0idNvgGiucWgup/mP78zyC23uFjYq0evcWdjGQUaBH"; host1 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPJDyIr/FSz1cJdcoW69R+NrWzwGK/+3gJpqD1t8L2zE"; host2 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKzxQgondgEYcLpcPdJLrTdNgZ2gznOHCAxMdaceTUT1"; in { "secret1.age".publicKeys = [ isabel host1 ]; "secret2.age".publicKeys = [ isabel host2 ]; } ``` 从这一点来看,你需要添加 `secret1.age` 和 `secret2.age` 文件。这应该使用 `agenix` CLI 来完成,命令可能类似于 `agenix -e secret1.age`,这个相同的命令可以在以后用来编辑这些文件。将 agenix 接入主机看起来与 sops-nix 相当相似,但由于每个密钥都是自己的文件,所以表面积更小: ```nix { age.secrets.secret1 = { file = ./secrets/secret1.age; owner = "isabel"; group = "users"; mode = "0400"; }; } ``` 在启动时,主机的 SSH 密钥用于将每个 `.age` 文件解密到 `/run/agenix/`,同样是 tmpfs。让大多数人掉坑里的部分是重新加密。每次你添加新主机或轮换密钥时,`secrets.nix` 中 `publicKeys` 列表发生变化的每个密钥都需要重新加密。`agenix --rekey` 命令会为你完成这项工作,但它需要*当前*其中一个接收者的私钥才能首先读取现有的密文。在实践中,这意味着重新加密发生在你最信任的机器上,而不是在你试图启动的新主机上。 ## 使用文件系统 这种方法的问题在于,你的配置不再完全描述你的机器,这就是为什么我从未尝试使用这种方法。如果重新安装,你必须记住将每个文件以正确的所有权放回正确的位置。在进行恢复时,这更是一场灾难,当你凌晨 2 点重建服务器时,这一点比你想象的更重要。要避免的是 `builtins.readFile "/var/lib/myservice/token"` 或类似的操作。这会在求值时读取文件并将内容嵌入到 nix store 中,而 nix store 是全局可读的,这正是引言中警告的失败模式。始终通过选项如 `services.*.environmentFiles`(https://search.nixos.org/options?channel=unstable&query=services.*.environmentFiles)将*路径*传递给服务。对于单个服务器或笔记本电脑,这也许没问题。对于任何你称之为"集群"的东西,或者任何你想能够仅从配置完全重建的东西,请改用 sops-nix 或 agenix。 ## 两大工具之间的比较 使用 sops-nix 的主要原因是将尽可能多的数据打包到一个文件中,这有其各自的优缺点。对我来说,这主要意味着我可以把更多的邮件服务器密钥放在一个文件中,而不是像 agenix 那样分散在 5 个左右的文件中。Agenix 在简单性方面胜出。没有要学习的 yaml 架构,没有要同步的 `.sops.yaml`,而 `secrets.nix` 文件就是 nix,所以你已经用于主机和用户的相同 `let ... in` 绑定也适用于密钥。心理模型是"一个密钥,一个文件,一个接收者列表",这与我对访问控制的思考方式很好地映射。诚实的答案是,这两个工具都能解决问题,此时的差异主要是人体工程学方面的。如果你从零开始,并且有多个服务每个都需要一组相关的密钥,sops-nix 会更好地扩展。如果你从零开始,并且主要是一些独立的令牌,agenix 会以更少的仪式让你达到目的。同样重要的是要注意,在当前这个时刻,agenix **不是**后量子安全的(https://github.com/ryantm/agenix#threat-modelwarnings)。然而,同样的问题也适用于 sops-nix,但不是由于 sops 本身的限制(如 agenix 在 age CLI 中的限制),而是因为 sops-nix 不支持后量子 age 密钥 sops-nix#885(https://github.com/Mic92/sops-nix/issues/885)。自从我写这篇文章以来,我被纠正了,age 和 sops 确实支持后量子安全加密密钥。自 age#578(https://github.com/FiloSottile/age/issues/578)关闭并发布 v1.3.0(https://github.com/FiloSottile/age/releases/tag/v1.3.0)以来。当你生成 age 密钥时,更改命令以包含 `-pq`,所以命令现在可能看起来像 `age-keygen -pq -o key.txt`。 ## 结论 在三年间轮换列表中的每个选项后,我大致得出的结论是: - **sops-nix** 是我在主机上作为强大工具持续使用的。如果我有一些超过手指和脚趾数量的密钥的任何形似邮件服务器的东西,我会首先选择它。 - **agenix** 是每当有人问我关于 NixOS 机器中的密钥问题时我会选择的。它是仍然给你 proper per-host 访问控制的最小移动部件。 - **文件系统** 对于每台机器上一两个确实不应该存在于仓库中的值是可以的,只要你记住你让未来的你负责把它们放回去。 - **原始列表上的其他所有内容**、私人仓库、git-crypt、nix 配置中的明文。我**不会**推荐。 如果你正在选择你的第一个密钥工具,选择 agenix,熟悉这个流程,只有在你真正感受到一个密钥一个文件的痛苦后才升级到 sops-nix。

相似文章

我喜欢的 NixOS 声明式安装方式

Michael Stapelberg

一份关于使用 nixos-anywhere 等工具通过网络声明式安装 NixOS 的指南,重点强调在版本控制下管理配置文件。

SOPS + Age 和 Sealed Secrets

Lobsters Hottest

一篇博客文章,解释如何将 SOPS 与 Age 结合用于在集群外加密 Secrets,并使用 Bitnami Sealed Secrets 在集群内解密,从而实现 Kubernetes 的 GitOps 工作流。

Nix Flakes 及其在 Guix 中的对应物

Lobsters Hottest

详细比较 Nix Flakes 与 Guix 包管理系统中的对应物,涵盖依赖声明、锁定、纯净性、输出、开发环境和系统配置。