# SOPS Secret Management Atomic secret provisioning for NixOS using [sops-nix](https://github.com/Mic92/sops-nix). ## Overview Secrets are encrypted with `age` using SSH host keys, ensuring: - No plaintext secrets in the repository - Secrets are decrypted at activation time - Each host can only decrypt its own secrets ## Setup ### 1. Get Host's Age Public Key After a host is installed, extract its age key from the SSH host key: ```bash nix-shell -p ssh-to-age --run 'ssh-keyscan -t ed25519 | ssh-to-age' ``` Or locally on the host: ```bash nix-shell -p ssh-to-age --run 'cat /etc/ssh/ssh_host_ed25519_key.pub | ssh-to-age' ``` ### 2. Configure .sops.yaml Add the host key to `.sops.yaml`: ```yaml keys: - &admin_key age1e8p35795htf7twrejyugpzw0qja2v33awcw76y4gp6acnxnkzq0s935t4t - &main_key age1... # cryodev-main - &pi_key age1... # cryodev-pi creation_rules: - path_regex: hosts/cryodev-main/secrets.yaml$ key_groups: - age: - *admin_key - *main_key - path_regex: hosts/cryodev-pi/secrets.yaml$ key_groups: - age: - *admin_key - *pi_key ``` ### 3. Create Secrets File ```bash sops hosts//secrets.yaml ``` This opens your editor. Add secrets in YAML format: ```yaml tailscale: auth-key: "tskey-..." some-service: password: "secret123" ``` ## Usage in Modules ### Declaring Secrets ```nix { config, ... }: { sops.secrets.my-secret = { # Optional: set owner/group owner = "myservice"; group = "myservice"; }; } ``` ### Using Secrets Reference the secret path in service configuration: ```nix { services.myservice = { passwordFile = config.sops.secrets.my-secret.path; }; } ``` ### Using Templates For secrets that need to be embedded in config files: ```nix { sops.secrets."netdata/stream-api-key" = { }; sops.templates."netdata-stream.conf" = { content = '' [stream] enabled = yes api key = ${config.sops.placeholder."netdata/stream-api-key"} ''; owner = "netdata"; }; services.netdata.configDir."stream.conf" = config.sops.templates."netdata-stream.conf".path; } ``` ## Common Secrets ### cryodev-main ```yaml mailserver: accounts: forgejo: "$2y$05$..." # bcrypt hash admin: "$2y$05$..." forgejo-runner: token: "..." headplane: cookie_secret: "..." # openssl rand -hex 16 agent_pre_authkey: "..." # headscale preauthkey tailscale: auth-key: "tskey-..." ``` ### cryodev-pi ```yaml tailscale: auth-key: "tskey-..." netdata: stream: child-uuid: "..." # uuidgen ``` ## Generating Secret Values | Secret | Command | |--------|---------| | Mailserver password | `nix-shell -p mkpasswd --run 'mkpasswd -sm bcrypt'` | | Random hex token | `nix-shell -p openssl --run 'openssl rand -hex 16'` | | UUID | `uuidgen` | | Tailscale preauth | `sudo headscale preauthkeys create --expiration 99y --reusable --user default` | ## Updating Keys After modifying `.sops.yaml`, update existing secrets files: ```bash sops --config .sops.yaml updatekeys hosts//secrets.yaml ``` ## Troubleshooting ### "No matching keys found" Ensure the host's age key is in `.sops.yaml` and you've run `updatekeys`. ### Secret not decrypting on host Check that `/etc/ssh/ssh_host_ed25519_key` exists and matches the public key in `.sops.yaml`.