Promote to production #158

Merged
jfraeys merged 4 commits from main into prod 2026-10-14 15:30:11 -04:00
Owner

Promotion: staging passed. Run scripts/plan-prod.sh locally before merging

Merging this PR is the approval record; it deploys nothing. Then check out the validated commit and run scripts/promote-prod.sh (or scripts/deploy-services.sh --tags ... --limit services for an Ansible-only change); both refuse any other commit.

Staging validation: passed on commit e456921651 (this run)
Production drift check: clean

Ansible preview (staging clone, synthetic secrets — approximate, not exact prod values)


PLAY [Deploy the Docker-based services stack] **********************************
skipping: no hosts matched

PLAY RECAP *********************************************************************
### Promotion: staging passed. Run `scripts/plan-prod.sh` locally before merging Merging this PR is the approval record; it deploys nothing. Then check out the validated commit and run `scripts/promote-prod.sh` (or `scripts/deploy-services.sh --tags ... --limit services` for an Ansible-only change); both refuse any other commit. **Staging validation:** passed on commit e4569216512c906656e04148718e86eae7451af1 (this run) **Production drift check:** clean ### Ansible preview (staging clone, synthetic secrets — approximate, not exact prod values) ``` PLAY [Deploy the Docker-based services stack] ********************************** skipping: no hosts matched PLAY RECAP ********************************************************************* ```
feat(infra): one front door: day-2 ops, every rotation, checks and keys through ./infra
All checks were successful
Lint / lint (pull_request) Successful in 1m50s
Secret scan / scan (pull_request) Successful in 14s
db6748d882
./infra now also forwards health/backup/user/firewall/vpn/node/service/demo to
./manage (its global options work anywhere on the line), and covers the
credentials outside rotate-secret.sh's service list (rotate ovh-s3, ci-ssh-key,
headscale-api-key, ci-state-key), the read-only checks (check exposure,
versions) and SOPS age key creation (keys operator|recovery [project]).

./manage stays a separate, self-contained script: roles/manage_cli installs it on
the hosts as infra-manage, where git, tea and the vault are not available. Its
`secret` command now points at ./infra rotate. ./setup stays the engine; staging,
DR and bootstrap still call it directly.

daily-commands.md documents the commands and how the three entry points fit;
test-infra-cli.sh gains 13 dispatch cases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ge5tovcwdEUBtPwUktsZZZ
fix(vaultwarden): the admin token reached the container mangled; escape $ for compose
All checks were successful
Lint / lint (pull_request) Successful in 2m59s
Secret scan / scan (pull_request) Successful in 15s
469d771e68
The vault holds VAULTWARDEN_ADMIN_TOKEN as an Argon2 PHC string
($argon2id$v=19$m=65536,t=2,p=4$<salt>$<hash>). The compose template injected it
unescaped, so Docker Compose expanded every $... as an unset variable and the
container received "=19=65536,t=2,p=4" (17 characters, confirmed on services).
Vaultwarden treats a non-PHC ADMIN_TOKEN as a plaintext password, so /admin
accepted that predictable string and no real token ever worked. /admin is behind
Authelia, so this broke the second layer, not the only one.

- Template: replace('$', '$$'). Checked with a real container on services and a
  sample hash: the old line yields "=19=65536,t=2,p=4", the fixed one the hash
  byte-for-byte.
- validate-services.yml (staging): assert the running ADMIN_TOKEN equals the
  vault's, no_log, so this cannot come back silently.
- rotate-secret.sh: hash with the argon2 CLI only (the `docker run
  vaultwarden/server hash` path never worked: the image's entrypoint is not the
  binary) and fail instead of silently storing the plaintext; the Bitwarden note
  body is now the token alone (also LLDAP), without the *_PLAIN= prefix.
- gen-auth-secrets.sh: drop the same dead docker branch (it would have left the
  token empty).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ge5tovcwdEUBtPwUktsZZZ
Merge pull request 'feat(infra): one front door: day-2 ops, every rotation, checks and keys through ./infra' (#152) from jfraeys/feat/infra-one-front-door into main
All checks were successful
Staging Validation / validate (push) Successful in 12m19s
Staging Validation / promote (push) Successful in 11s
ee63e91452
Merge pull request 'fix(vaultwarden): the admin token reached the container mangled; escape $ for compose' (#157) from jfraeys/fix/vaultwarden-admin-token-dollar into main
All checks were successful
Staging Validation / validate (push) Successful in 13m10s
Lint / lint (pull_request) Successful in 2m32s
Secret scan / scan (pull_request) Successful in 17s
Staging Validation / promote (push) Successful in 9s
e456921651
Sign in to join this conversation.
No reviewers
No labels
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
jfraeys/infra!158
No description provided.