Skip to content

Secrets

Secrets — login passwords, WiFi keys, disk passphrases — are kept as encrypted files in version control with sops-nix. Each is decrypted where it is needed: some by every machine at boot, some only by the people who administer the machines. This chapter covers the trust model and how the pieces fit together; the Baseline chapters on Login passwords, WiFi and Disk encryption cover managing each kind.

Every secret is a YAML file under secrets/, encrypted with sops and committed to the repository. Only the values are encrypted; the keys, the file names, and .sops.yaml itself stay in clear text.

Who may decrypt a file is declared in .sops.yaml, as a list of age keys — one per reader. A machine becomes a reader through its SSH host key (converted to an age key with ssh-to-age); a person becomes one through their own SSH key. Every host is set up to decrypt its secrets at boot with its own SSH host key, /etc/ssh/ssh_host_ed25519_key, by secrets.nix.

SecretReadersDecrypted by
passwords.yamlevery machine + adminseach host, at boot
wifi.yamlevery machine + adminseach host, at boot
disks/<user>.yamlthat user + lead admina person, at install

Passwords and WiFi credentials are decrypted by the machines themselves, so both files have the same readers — every machine, plus the admins (one rule in .sops.yaml can therefore cover them together).

A disk passphrase is what protects a machine when it is stolen; handing it to the host would defeat the encryption, so hosts are deliberately kept out of the readers of disks/*.yaml. Each person’s passphrase sits in its own file that only they can decrypt, yet we can flexibly add other readers — an admin or the install automation — when a machine needs setting up.

.sops.yaml
keys:
users:
- &alice ssh-ed25519 AAAA…
- &bob ssh-ed25519 AAAA…
hosts:
- &laptop age1…
stores:
yaml:
indent: 2
creation_rules:
- path_regex: secrets/(passwords|wifi)\.yaml$
key_groups:
- age:
- *laptop
- *alice
- *bob
- path_regex: secrets/disks/alice\.yaml$
key_groups:
- age:
- *alice

Open a secret with, e.g., sops secrets/passwords.yaml to edit its contents; it decrypts the file into your editor and re-encrypts it on save. Who may decrypt a file is declared separately, in .sops.yaml. To change that set, edit .sops.yaml first, then re-encrypt the file to match:

Terminal window
sops updatekeys secrets/passwords.yaml

On a running host, once it can decrypt them, each kind of secret feeds a different part of the system.

Login passwords. Every name passwords.yaml lists is applied as that user’s users.users.<name>.hashedPasswordFile; names that are not users on the host are ignored.

WiFi. Every entry in the wifi list becomes a NetworkManager connection named after its SSID. A wifi-provision service reconciles the connections at boot, deleting the ones it previously created that the list no longer names.

Disk encryption. The automatic layout (see Disks) puts everything on a LUKS2 volume. Each of a host’s owners — its normal users, listed by disk.owners — has a keyslot on that volume, unlocked by the passphrase kept in secrets/disks/<user>.yaml. Version control is the source of truth for who may unlock a disk.