secrets: rotate hermes@ mailbox password, wire gateway + dovecot restarts #351

Merged
hermes merged 1 commit from task/hermes-cred-rot into main 2026-09-17 18:32:12 -06:00
Collaborator

Rotates the operator's mailbox credential end to end, and fixes the reason the previous value would have survived the rotation.

What changed (one commit):

  • secrets/secrets.yaml: new hermes-mail-password and the EMAIL_PASSWORD= line inside vpn's hermes-env — the same plaintext in both places, so they are edited together. Only those two of 43 top-level keys differ; the other 8 env key names are unchanged and the recipients are byte-identical.
  • hosts/mail/default.nix: hermes-mail-password.restartUnits = [ "dovecot.service" ]. The passwd-file is rebuilt at activation, but the running dovecot keeps serving the cached credential for its TTL — including a revoked one.
  • hosts/vpn/default.nix: hermes-env.restartUnits = [ "hermes-agent.service" "hermes-backend.service" ], plus a comment listing what the file actually holds (it had gone stale) and the measurement that motivated it.

Evidence for the restart wiring. $HERMES_HOME/.env is composed at activation, so the file on disk is fresh after every switch — measured .env at 2026-09-17 18:16:56 while hermes-agent.service had been up since 2026-09-16 23:02:17. A rotated EMAIL_PASSWORD would therefore have sat inert in a file the long-lived gateway had already read: the same reader-drift class as the AutoKuma and coturn findings, and the reason both readers now declare their restart. sops-nix restarts only when the rendered content changes, so ordinary deploys do not bounce the gateway.

Verification plan (post-deploy):

  1. doveadm auth test on mail with the new credential → expects auth succeeded; the old credential → expects rejection. The same test with the current value was run pre-change as a control (it succeeded), so the method is known-good before it is used as evidence.
  2. IMAP login from vpn with the new credential — the exact path the adapter uses.
  3. hermes-agent.service / hermes-backend.service ActiveEnterTimestamp after the secret's mtime; .env content matching the rendered secret.
  4. Gateway journal: adapter alive, no auth failures.
Rotates the operator's mailbox credential end to end, and fixes the reason the previous value would have survived the rotation. **What changed (one commit):** - `secrets/secrets.yaml`: new `hermes-mail-password` **and** the `EMAIL_PASSWORD=` line inside vpn's `hermes-env` — the same plaintext in both places, so they are edited together. Only those two of 43 top-level keys differ; the other 8 env key names are unchanged and the recipients are byte-identical. - `hosts/mail/default.nix`: `hermes-mail-password.restartUnits = [ "dovecot.service" ]`. The passwd-file is rebuilt at activation, but the running dovecot keeps serving the cached credential for its TTL — including a revoked one. - `hosts/vpn/default.nix`: `hermes-env.restartUnits = [ "hermes-agent.service" "hermes-backend.service" ]`, plus a comment listing what the file actually holds (it had gone stale) and the measurement that motivated it. **Evidence for the restart wiring.** `$HERMES_HOME/.env` is composed at activation, so the file on disk is fresh after every switch — measured `.env` at `2026-09-17 18:16:56` while `hermes-agent.service` had been up since `2026-09-16 23:02:17`. A rotated `EMAIL_PASSWORD` would therefore have sat inert in a file the long-lived gateway had already read: the same reader-drift class as the AutoKuma and coturn findings, and the reason both readers now declare their restart. sops-nix restarts only when the rendered content changes, so ordinary deploys do not bounce the gateway. **Verification plan (post-deploy):** 1. `doveadm auth test` on mail with the new credential → expects `auth succeeded`; the old credential → expects rejection. The same test with the current value was run pre-change as a control (it succeeded), so the method is known-good before it is used as evidence. 2. IMAP login from vpn with the new credential — the exact path the adapter uses. 3. `hermes-agent.service` / `hermes-backend.service` `ActiveEnterTimestamp` after the secret's mtime; `.env` content matching the rendered secret. 4. Gateway journal: adapter alive, no auth failures.
secrets: rotate hermes@ mailbox password and wire the gateway/dovecot restarts
All checks were successful
NixOS Build Check / Flake input regression guard (pull_request) Successful in 2s
NixOS Build Check / Scope changed deployable hosts (pull_request) Successful in 3s
NixOS Build Check / Eval giga (scope + liveness + offline fallback) (pull_request) Successful in 3s
PR base guard / Base drift / conflict check (pull_request) Successful in 2s
Secret Scan / gitleaks scan (pull_request) Successful in 4s
NixOS Build Check / Build mail closure (on mail) (pull_request) Successful in 12s
NixOS Build Check / Build vpn closure (on vpn) (pull_request) Successful in 21s
NixOS Build Check / Eval giga natively (authoritative, on giga) (pull_request) Successful in 34s
NixOS Build Check / Build public closure (on public) (pull_request) Successful in 1m11s
ddac1f6594
Rotates hermes-mail-password (mail side) together with the EMAIL_PASSWORD line
inside vpn's hermes-env in a single commit: nixos-mailserver builds dovecot's
passwd-file from the secret at activation and the gateway authenticates with
the same plaintext, so the two can never be edited apart.

Both readers were start-only: hermes-agent.service had been up since the
previous day while .env was recomposed on every switch (observed 2026-09-17
18:16:56), and dovecot's auth cache outlives a passwd-file rebuild. Declare
restartUnits on both secrets (sops-nix restarts only when the rendered content
changes, so ordinary deploys do not bounce anything). Recipients unchanged.
hermes merged commit f74af12401 into main 2026-09-17 18:32:12 -06:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
ymrtech/nix-config!351
No description provided.