# Secrets (sops-nix) Secrets are encrypted with [sops](https://github.com/getsops/sops) and decrypted at activation by [sops-nix](https://github.com/Mic92/sops-nix). Each host decrypts with its own SSH host key (converted to an age key in-memory — no separate host private key is ever stored on disk). Tooling (`sops`, `age`, `ssh-to-age`) is available via `nix develop` in this repo, and installed system-wide on every machine via `machines/core/sops.nix`. ## Adding a secret to an existing host ``` nix develop -c sops secrets/.yaml ``` This opens `$EDITOR` on the decrypted content. Add your key, save, quit — sops re-encrypts on write. Then declare it in `machines//secrets.nix`: ```nix sops.secrets.my-secret = { owner = "someuser"; # defaults to root group = "someuser"; mode = "0400"; restartUnits = [ "foo.service" ]; # if a running service needs to pick it up }; ``` Consume it in the owning module as `config.sops.secrets.my-secret.path` (resolves to `/run/secrets/my-secret`) — never inline the decrypted value into a Nix string, since that copies it into the world-readable `/nix/store`. Then: ``` nix build '.#nixosConfigurations..config.system.build.toplevel' sudo nixos-rebuild test --flake '.#' # verify, then: sudo nixos-rebuild switch --flake '.#' ``` **The secrets file must be `git add`ed before `nix build`** — untracked files are invisible to flake evaluation, and `sops.validateSopsFiles` needs it in the Nix store. ## Adding a new host 1. On the new host: `ssh-to-age < /etc/ssh/ssh_host_ed25519_key.pub` to get its age public key. 2. In `.sops.yaml`: add a `&host_` anchor with that key, and a `creation_rules` entry for `secrets/.yaml` including both the new host key and `*admin_finn`. 3. Create `machines//secrets.nix`, add it to that machine's `configuration.nix` imports. 4. If the host needs to read an existing shared file, run `sops updatekeys secrets/.yaml` after step 2 so it's re-encrypted to include the new recipient. ## Rotating keys ``` # after editing .sops.yaml (new/removed recipient): nix develop -c sops updatekeys secrets/.yaml ``` for every file the change affects. `updatekeys` requires you to already be able to decrypt the file — this is why `*admin_finn` is a recipient on every rule, as the standing fallback. Reinstalling a host regenerates its SSH host key, which changes its age identity. Any file previously encrypted to that host must be `updatekeys`'d with the new key, or the host's next activation fails to decrypt. ## Where private keys live - **Operator** (you): `~/.config/sops/age/keys.txt` — sops' default lookup path, so `SOPS_AGE_KEY_FILE` never needs to be set. Never committed. Back this up out-of-band (password manager, offline copy) — if it's lost, only the surviving host keys can still decrypt their own files, and any file that was only ever readable by an operator-only rule becomes permanently unreadable. - **Hosts**: `/etc/ssh/ssh_host_ed25519_key` — already managed by NixOS, converted to age in-memory at activation time. Nothing extra to back up. ## Layout ``` .sops.yaml # age recipients + creation rules (which keys can decrypt which files) secrets/.yaml # per-host encrypted secrets (default sopsFile for that host) machines/core/sops.nix # applied to every machine: decryption config, sops/age/ssh-to-age on PATH machines//secrets.nix # per-host: which secrets exist, owner/group/mode ```