I’m trying to figure out how to properly use agenix for
users.users.<name>.passwordFile on a fresh install with flakes. I ran into a chicken-and-egg problem with the password and the ssh keys when I tried to add this to the draft config I’ve been learning in.
I’ve pruned and sanitized my draft config and posted it here on github. The
main branch uses
passwordFile without agenix. It’s not as fast or polished as
nixos-up, but it has LUKS, and I’m experimenting with darling erasure.
My goal is to get to a (fairly) minimal example, without a lot of wrappers, on how to bootstrap a new system using agenix for secret management, while keeping the LUKS and darling erasure components. Then I may revise to use ragenix, and compare with sops-nix.
I’ve come up with one potential solution while pruning the example to share, so I’ll start adding agenix in another branch, step-by-step.
In the meantime, if anyone can point me to a working example, or has other advice on how to improve the config, please let me know.
My most recent effort at adding secrets with
agenix is in the agenix branch now.
ed8550c I added
agenix as an input, and included it as an installed package in the live iso and the installed system
a4ef773 On my development machine, I followed the agenix tutorial and:
- generated two new
ed25519 keys without passphrases – one for the user and the other for the system
ssh-keygen -t ed25519 -f /tmp/.ssh/nix/flynn/id_ed25519 -C ""
ssh-keygen -t ed25519 -f /tmp/.ssh/nix/encom/id_ed25519 -C ""
secrets.nix with those keys specified to decode
- edited the secret file using:
and pasted in the hashed password output of:
nix run github:ryantm/agenix -- -e "hashedPassword.age" -i /tmp/.ssh/nix/flynn/id_ed25519
mkpasswd -m sha512crypt | xclip -sel clip
- added both
hashedPassword.age to the repo
- added the secret to
config.age.secrets.hashedPassword.path instead of the earlier out-of-band hashed password file
Then I boot into the live iso, connect the network, and copy the keys from my development machine to
/root/.ssh on the live iso.
- I also tried by copying the keys to the
nixos user of the iso, but don’t expect that to work because
nixos-install needs to be run with
sudo or as
agenix gets an error from
nixos-install, which prevents decryption, so
/run/agenix/hashedPassword does not exist and the user
flynn does not have a password set in the resulting system.
complete error information:
[nixos@nixos:~]$ sudo nixos-install --flake github:bluesquall/tabula-rasa/agenix#encom --no-root-password
building the flake in github:bluesquall/tabula-rasa/8f0a004a1abf7e7e94c7df0429fe99794542cc78...
installing the boot loader...
[agenix] symlinking new secrets to /run/agenix (generation 1)...
[agenix] decrypting root secrets...
decrypting '/nix/store/mk50w869fvji6x7qh0mff4z469xyx7hf-hashedPassword.age' to '/run/agenix.d/1/hashedPassword'...
Error: No such file or directory (os error 2)
[ Did rage not do what you expected? Could an error be more useful? ]
[ Tell us: https://str4d.xyz/rage/report ]
chmod: cannot access '/run/agenix.d/1/hashedPassword.tmp': No such file or directory
chown: cannot access '/run/agenix.d/1/hashedPassword.tmp': No such file or directory
mv: cannot stat '/run/agenix.d/1/hashedPassword.tmp': No such file or directory
Activation script snippet 'agenixRoot' failed (1)
warning: password file ‘/run/agenix/hashedPassword’ does not exist
[agenix] decrypting non-root secrets...
setting up /etc...
Copied "/nix/store/cfhpzlarbhfw3scj91dcz5ai04ayfzik-systemd-249.7/lib/systemd/boot/efi/systemd-bootx64.efi" to "/boot/EFI/systemd/systemd-bootx64.efi".
Copied "/nix/store/cfhpzlarbhfw3scj91dcz5ai04ayfzik-systemd-249.7/lib/systemd/boot/efi/systemd-bootx64.efi" to "/boot/EFI/BOOT/BOOTX64.EFI".
Random seed file /boot/loader/random-seed successfully written (512 bytes).
Not installing system token, since we are running in a virtualized environment.
Created EFI boot entry "Linux Boot Manager".
- Instead of cutting onto the clipboard from another terminal, if
EDITOR=nvim, you can generate the password using
:r! mkpasswd -msha512crypt <passwd> and wipe that entry from the vim command history with
:call histdel(":", "mkpasswd").
Probably not surprising, but the branch using
ragenix runs into the same error.
Along a different path, it seems like I got
sops-nix to work in this branch. There was a hint that perhaps the keys need to be on the root subvolume due to boot & build ordering. I’m not sure whether that applies to
(r)agenix as well, but I’ll look into it when I get time to circle back.
Revising the sops-nix branch to enable darling erasure was a bit complicated, but eventually very simple. The exercise actually makes me question if there’s any system state that I would want to persist through boots that isn’t:
- required for bootstrapping in the first place (or almost required)
- a secret that sops-nix or agenix should track
- a non-secret that can be plaintext in the config
(Note that I don’t consider
/home to be system state, so it is on a different subvolume that won’t be wiped by darling erasure.)
It seems impossible to use the same flake for the initial build and for subsequent rebuilds, unless you set everything you want to persist from the start. This effectively makes it necessary information for bootstrapping.
Since I couldn’t make sops-nix happy with the private keys on a separate subvolume, I had to put them on the root subvolume & take a snapshot of it with the minimal files required to bootstrap from. This makes the
root-blank snapshot and the
persist subvolume unnecessary.
I’ll circle back to the (r)agenix approach with this perspective shift after I’ve experimented a bit more with the working sops-nix branch.
This was actually due to a problem with re-keying the secrets file when I switched over from bootstrapping with a user’s
age key to bootstrapping with a pre-generated
ssh-ed25519 host key.
I assumed that adding the
age public key derived from the pre-generated
ssh-ed25519 host key to
.sops.yaml and then editing the secret with
sops -e path/to/secrets.yaml would encode the secret for the new public key, but it turns out that you need
sops updatekeys path/to/secrets.yaml to re-key.
Once that was fixed, using
sops.age.sshKeyPaths = [ "/persist/etc/ssh/ssh_host_ed25519_key" ]; worked just fine.
The perspective shift still applies, though, so I’ll continue to evaluate my alternate approach that uses a bootstrapped snapshot of root rather than symlinking from a separate subvolume.
Looks like both agenix and ragenix now work with the host key. I’ll revisit sometime later with the user key.
In the meantime, the live system iso includes
agenix as a program so that you can re-key the secrets if you have the user key.