这是agenix的一个扩展,它允许你摆脱维护secrets.nix
文件的麻烦,通过在需要的地方自动重新加密密钥。它还允许你为密钥定义多功能生成器,以便自动引导它们。这个扩展是一个仅支持flakes的项目,可以与常规的agenix一起使用。
要使用重新加密功能,你需要通过主密钥(YubiKey、FIDO2密钥、TPM或普通age身份)加密密钥并将其存储在你的仓库中,agenix-rekey将自动为任何需要这些密钥的主机重新加密。强烈推荐使用YubiKey/FIDO2密钥,它将为你提供流畅的重新加密体验。总之,你可以获得:
secrets.nix
。为了正常运行,agenix-rekey必须进行一些nix技巧。你可以在下面阅读更多关于它如何工作的信息。备注:
age-plugin-yubikey
0.4.0版本起,PIN只需要输入一次。使用密码保护的主密钥永远不会有这个好处,每次重新加密操作都需要输入密码。除非缓存密钥,否则无法避免这种情况,而我不想这样做。使用agenix-rekey时,你将拥有一个agenix
命令来在你的flake上运行与密钥相关的操作。这是对agenix提供的命令的替代,你不再需要原来的命令。有几个应用/子命令可以用来管理你的密钥:
agenix generate
:生成尚不存在且设置了生成器的任何密钥。agenix edit
:使用$EDITOR
创建/编辑密钥。可以加密现有文件。agenix rekey
:为需要的主机重新加密密钥。agenix <命令> --help
获取具体使用信息。一般工作流程非常简单,因为每当需要时,你都会自动收到运行agenix rekey
的提示(构建将失败并告诉你)。
要使用agenix-rekey,你需要将agenix-rekey添加到你的flake.nix
中,在你的主机中导入提供的NixOS模块,并在你的flake中公开一些信息,以便agenix-rekey知道在哪里查找密钥。还提供了flake-parts模块(参见本节末尾的示例)。
要获得agenix
命令,你可以使用nix shell github:oddlama/agenix-rekey
进入一个临时可用的shell,或者将提供的包agenix-rekey.packages.${system}.default
添加到你的devshell中,如下所示。
如果你不想使用包装器,你也可以通过你的flake直接调用脚本,使用nix run .#agenix-rekey.<system>.<app>
,这在你自己的脚本中可能很有用。
<details> <summary> 与flake-parts一起使用 </summary>{ inputs.flake-utils.url = "github:numtide/flake-utils"; inputs.agenix.url = "github:ryantm/agenix"; inputs.agenix-rekey.url = "github:oddlama/agenix-rekey"; # 确保覆盖nixpkgs版本以跟随你的flake, # 否则派生路径可能不匹配(当使用storageMode = "derivation"时), # 导致找不到重新加密的密钥! inputs.agenix-rekey.inputs.nixpkgs.follows = "nixpkgs"; # ... outputs = { self, nixpkgs, agenix, agenix-rekey }: { # 示例系统配置 nixosConfigurations.yourhostname = nixpkgs.lib.nixosSystem { system = "x86_64-linux"; modules = [ ./configuration.nix agenix.nixosModules.default agenix-rekey.nixosModules.default ]; }; # 在你的flake中公开必要的信息,以便agenix-rekey # 知道在哪里查找密钥和路径。 # # 确保这里传递的pkgs来自与你的主机在`nixosConfigurations`中使用的 # pkgs相同的nixpkgs版本,否则重新加密的派生将无法找到! agenix-rekey = agenix-rekey.configure { userFlake = self; nodes = self.nixosConfigurations; # colmena示例: # inherit ((colmena.lib.makeHive self.colmena).introspect (x: x)) nodes; }; } # 可选:仅当你想在devshell中使用agenix命令时才需要这部分。 // flake-utils.lib.eachDefaultSystem (system: rec { pkgs = import nixpkgs { inherit system; overlays = [ agenix-rekey.overlays.default ]; }; devShells.default = pkgs.mkShell { packages = [ pkgs.agenix-rekey ]; # ... }; }); }
</details>{ inputs.flake-parts.url = "github:hercules-ci/flake-parts"; inputs.agenix.url = "github:ryantm/agenix"; inputs.agenix-rekey.url = "github:oddlama/agenix-rekey"; # 确保覆盖nixpkgs版本以跟随你的flake, # 否则派生路径可能不匹配(当使用storageMode = "derivation"时), # 导致找不到重新加密的密钥! inputs.agenix-rekey.inputs.nixpkgs.follows = "nixpkgs"; # ... outputs = inputs: inputs.flake-parts.lib.mkFlake {inherit inputs;} { imports = [ inputs.agenix-rekey.flakeModule ]; perSystem = {config, pkgs, ...}: { # 将`config.agenix-rekey.package`添加到 你的devshell中, # 以便轻松访问`agenix`命令包装器。 devShells.default = pkgs.mkShell { nativeBuildInputs = [ config.agenix-rekey.package ]; }; # 如果你想更改考虑重新加密的主机,可以定义agenix-rekey.nodes。 # 参考agenix-rekey的flake.parts部分以查看所有可用选项。 agenix-rekey.nodes = inputs.self.nixosConfigurations; # (技术上不需要,因为它已经是默认值) }; }; }
你可以在两种存储模式之间选择用于重新加密的密钥,它们在根本上是不同的。你可以自由地在它们之间切换,更多信息请参见这里。
由于agenix-rekey只是agenix的一个扩展,你所知道的关于agenix的一切仍然照常适用。除了指定关于你的主密钥的元信息外,要使用重新加密功能,你唯一需要改变的是在你的密钥上指定rekeyFile
而不是file
。完整的设置过程如下:
对于每个主机,你必须提供一个用于重新加密的公钥,并选择用于解密存储在你仓库中的密钥的主身份。hostPubkey
显然对每个主机都不同,但所有其他选项(如你的主身份)通常在主机之间是相同的。你可以在下面的API参考中找到更多选项。
默认情况下,我们将使用本地存储模式,该模式将在你自己的仓库中存储重新加密的密钥。
{ age.rekey = { # 使用 `ssh-keyscan` 或查看 ~/.ssh/known_hosts 获取此值 hostPubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI..."; # 用于解密的主身份路径。详见选项说明。 masterIdentities = [ ./your-yubikey-identity.pub ]; #masterIdentities = [ "/home/myuser/master-key" ]; # 外部主密钥 #masterIdentities = [ # # 可以使用以下替代语法指定身份, # # 这可以避免加密时不必要的提示。 # { # identity = "/home/myuser/master-key.age"; # 密码保护的外部主密钥 # pubkey = "age1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs3290gq"; # 显式指定公钥 # } #]; storageMode = "local"; # 选择一个目录来存储此主机的重新加密秘密。 # 不能与其他主机共享。请从 flake 的根目录引用此路径, # 而不是使用直接路径字面量,如 ./secrets localStorageDir = ./. + "/secrets/rekeyed/${config.networking.hostName}"; }; }
使用 (r)age 和主密钥加密一些秘密。agenix-rekey
提供了一个名为 agenix
的命令行工具,
它允许您使用喜欢的 $EDITOR
轻松创建/编辑秘密,
并根据步骤 1 中的设置自动使用正确的身份进行解密和加密。
理想情况下,您应该已经按照安装部分的说明将其添加到开发环境中,
否则您可以通过 nix run github:oddlama/agenix-rekey -- <子命令> [选项]
临时运行该工具。
# 创建新的或编辑现有的秘密 agenix edit secret1.age # 或加密现有文件 agenix edit -i plain.txt secret1.age # 如果没有提供参数,将显示一个包含所有定义的秘密的交互式列表 # 以便您选择要创建/编辑的秘密 agenix edit # 或者,您当然可以使用 (r)age 手动加密内容 echo "secret" | rage -e -i ./your-yubikey-identity.pub > secret1.age
在选择 $EDITOR
时要小心,通过撤销历史或缓存等方式,它可能会泄露秘密信息。
对于 vim
和 nvim
,此应用程序会自动禁用相关选项以确保安全使用。
在配置中定义秘密并使用它。这类似于经典的 agenix,但现在使用 rekeyFile
代替 file
(它会为 file
生成定义)。
{ age.secrets.secret1.rekeyFile = ./secret1.age; services.someService.passwordFile = config.age.secrets.secret1.path; }
像往常一样使用 nixos-rebuild
或您喜欢的部署工具部署系统。
如果需要重新加密,您会在构建失败时收到提示。
由于我们刚刚完成初始设置,您应该立即重新加密:
> agenix rekey -a # 使用本地存储模式时,-a 会将它们添加到 git
别忘了afterwards添加重新加密的秘密,使它们对构建过程可见。
[!警告] 如果使用
storageMode = "derivation"
,agenix rekey
必须能够设置额外的 沙盒路径。为此,您需要将age.rekey.cacheDir
添加为全局额外沙盒路径 (不要将用户添加到 trusted-users 中,这基本上会授予他们 root 访问权限!):nix.settings.extra-sandbox-paths = ["/tmp/agenix-rekey.${config.users.users.youruser.uid}"];
有关用户无关设置的更多信息,请参见 issue #9。
[!注意] 如果使用
storageMode = "derivation"
,并且将配置部署到 远程系统,需要确保包含重新加密秘密的正确 derivation 从本地存储 复制到远程主机的存储中。任何在本地构建并使用
nix copy
(或等效工具)将 derivation 复制到 远程系统的工具都会自动工作,因此无需额外注意。 只有当您严格在远程系统上构建时,可能需要手动复制这些秘密。 您可以使用agenix rekey --show-out-paths
或直接引用nixosConfigurations.<host>.config.age.rekey.derivation
来定位它们。