Colmena keys via secrets decrypt; retire ansible-vault
closedReplace the ansible-vault files and the Python vault scripts (deployment/scripts/*, _vault_lib.py, vault-password-client) with Colmena keys fed by the secrets tool (~/projects/secrets).
Sketch:
- deployment.keys.<name>.keyCommand = [ "secrets" "decrypt" "sovrn/<scope>/<name>" ] with user, group and permissions per key.
- Naming scheme in the secrets store:
- sovrn/shared/*: OIDC key, OAuth attestation key, backup-domains token, relay API/SMTP, Resend key
- sovrn/<host>/*: Stalwart recovery admin, sovrnd service-account password, R2 keys, ZDS secrets
- Keys vanish on reboot: Colmena’s default destDir is /run/keys (tmpfs), so an unattended reboot leaves Stalwart, sovrnd and Litestream without secrets. Recommend a persistent root-only destDir (e.g. /var/lib/sovrn/keys) for the mail boxes. Decide.
- Order consumers on Colmena’s generated <name>-key.service units (After=/Wants=).
- Pre-generate the Stalwart recovery admin and the sovrnd service-account password with secrets encrypt, instead of generating them on the box and fetching them back. That deletes ensure-bootstrap-secrets, persist-bootstrap-secrets and the sentinel/slurp logic.
- One-off migration: decrypt the current vault files and re-encrypt each value into the store.
- Decide who deploys (laptop vs CI) and how CI gets an age identity.
Done when a scratch box receives its keys with the correct owner and mode, and they survive a reboot (if we choose a persistent destDir).
1 Comment
Progress: ansible-vault retired; Colmena keys come from
secretsDecisions (with the user) - Identity: one age identity. CI gets a copy of the operator’s
~/.config/age/identity.txt; there are no per-environment recipients. - Store location:SECRETS_DIR=~/projects/data. It becomes a git repo on the private server before CI is integrated. - CI scope: tests, plus automated deploys of staging (mx99.eu.sovrn.at) only. Production is deployed from the workstation after staging looks right. - Key directory: keys land in/var/lib/sovrn/keys(persistent), not/run/keys.Migration (done, verified by round-trip) - Store naming:
sovrn/shared/<file>andsovrn/hosts/<fqdn>/<file>. Each value is stored in its on-host form; the OAuth attestation key is base64-decoded to 32 raw bytes. -relay-api-secret,pds-resend-api-keyandmetrics-passwordare symlinks to the existingsmtp2go_api_key,resend_api_keyandsovrn_metrics_passwordentries. Those entries carry a trailing newline. sovrnd trims when it reads them; check vmagent and Alertmanager when their roles are ported (dd3c64a, a6edca3). - mx99: 8 per-host values migrated.sovrnd-stalwart-passwordandssh_host_ed25519_keyare not generated yet;new-host/gen-host-secretswill create them.Nix -
nix/modules/secrets.nixadds thesovrn.secrets.<name> = { scope = shared|host; user; group; mode; }option. It maps each entry todeployment.keyswithkeyCommand = secrets decrypt <storePath>. Root-owned keys upload pre-activation; keys owned by service users upload post-activation, because those users are created during activation. - The role modules declare their keys: cell 16, relay 3, metrics 2.Justfile.nix -
gen-host-secrets HOSTevaluates the host’s declared host-scoped secrets and generates the missing ones (SSH host key, 32-hex passwords, ZDS secrets, secp256k1-valid PLC key). It lists the R2 tokens for manual entry and never rotates existing values. -check-secrets [HOST]is a pre-deploy gate thatdeploydepends on. -new-hostnow runsgen-host-secrets. - Newtestandci-stagingrecipes (test→known-hosts→deploy mx99). - Tested against a copy of the store: an existing host (idempotent), a fresh cell, and the check failing and then passing.Removed - The vault files and
deployment/scripts/(Python vault tooling, test-deploy). -vault_password_filefrom ansible.cfg. - The Ansible run/provision recipes in the Justfile. -ansibleandapacheHttpdfrom devenv.Found:
deployment/playbooks/.local/stalwart-secret.sovrn.atwas committed in plaintext (in the history of origin/main). It is a Stalwart recovery credential for the oldsovrn.atbox and does not match mx99’s current value. Removed from the tree; the history still contains it.Open: a real deploy that verifies owners, modes and reboot survival (needs a NixOS host from bc4b527).