Skip to content

Secrets (sops-nix)

All network secrets (passwords, keys, tokens) live encrypted in a single file managed by sops-nix 🡕. It is committed to git in encrypted form; only key holders can decrypt it.

# usr/secrets/secrets.yaml (chiffré au repos)
default-password-hash: ...
user:
alice:
password-hash: ...

Decryption relies on two age 🡕 keys:

KeyWhereRole
Admin key~/.config/sops/age/keys.txtEdit secrets from the admin workstation
Infra key/etc/sops/age/infra.keyDecrypt on each host at deploy time
  • The admin key derives from your SSH key (~/.ssh/id_ed25519, via ssh-to-age).
  • The infra key is generated once, then pushed to each host.
  • Recipients are listed in usr/secrets/.sops.yaml (admin + infra).

A single command prepares all the secrets machinery:

Fenêtre de terminal
just configure-admin-host

It is idempotent and creates, as needed:

  • the deployer’s SSH key (nix);
  • a random password, known to no one, for the nix account: it is only accessed via its key;
  • the admin key (from the SSH key) and the infra key (age-keygen);
  • the .sops.yaml file (recipients);
  • the fingerprint of the default password (just passwd-default);
  • the Harmonia binary cache signing key;
  • all internal secrets of the declared services (see below).

The framework sorts each secret into one of two families. The reference list lives in dnf/lib/secrets.nix ; each host of the farm is the authority on what is claimed.

Internal — the value only makes sense to machines, no one remembers it. just configure-admin-host draws them at random, once and for all :

FamilyExamples
Kanidm OAuth2 clientsoidc-secret-<client>, oauth2-proxy cookie
Backuprestic-password-<zone>, restic/<host>/rest-password
MatrixMAS secrets, livekit-secret, bridge tokens, turn-secret
IdentitiesKanidm fallback passwords, internal TLS pair
ServicesAdGuard Home, Nextcloud, Grafana, SearXNG, Vaultwarden, Garage, MinIO…

External — the value comes from elsewhere, DNF cannot invent it :

FamilyOrigin
Account passwordschosen by a human (just passwd)
smtp/password (with network.smtp), wifi-password-<zone>provider, box, operator
mautrix-telegram-api-*my.telegram.org
luks-passphrase, yubikey/...typed at boot, or derived from the physical key
alertmanager-matrix-*account created on the server (just configure-alert-bot)
CommandEffect
just passwd-defaultRecords the fleet default password (hash only)
just passwd <user>Sets a user’s password
just configure-admin-hostGenerates missing internal secrets (OIDC, backup, service tokens…)
just yubikey <user>Enrolls a YubiKey (derived LUKS secret + luks-passphrase)
just luks <host>Provisions disk encryption (shared + per-host passphrases)
just sopsEdits the secrets file by hand ($EDITOR)

After a change, deploy to propagate it:

Fenêtre de terminal
just passwd alice
just apply @user-alice # deploys the new hash

A host can only decrypt secrets with the infra key. It is placed automatically by just configure <host>, or manually:

Fenêtre de terminal
just push-key <host> # copies /etc/sops/age/infra.key to the host

Rotation replaces the infra key across the entire fleet, in three verified steps:

  1. Introduce the new key

    Fenêtre de terminal
    just rotate init # new key + re-encryption (old + new)
  2. Distribute to all hosts

    Fenêtre de terminal
    just rotate push-keys # each host receives both keys
    just apply '*'
  3. Finalize

    Fenêtre de terminal
    just rotate finalize # discards the old key (after verification)
    just apply '*'

just invite connects on its own to the /admin panel with the plaintext vaultwarden-admin-token value. A manually set Argon2 hash forces it to ask for the token; just configure-admin-host reports it.

To migrate delete the entry with just sops, run just configure-admin-host again, then just apply hcs. Vaultwarden restarts with the new token. The panel remains protected by the network : it only responds from the LAN and the tailnet.

The project’s secrets are encrypted for the old admin machine’s keys. just configure-admin-host therefore refuses to create new ones: first restore /home/nix/.ssh/id_ed25519 and /etc/sops/age/infra.key (owned by nix, mode 600).

just bootstrap-keys creates the keys and the sops skeleton under a given root, for example /mnt from the installer, with the credentials NixOS will give the nix account. See the Justfile.