Refuse to start with insecure initial-admin password (M-9) #113

Merged
qwc merged 1 commit from fix/reject-default-admin-password into main 2026-05-18 22:59:15 +02:00
Owner

Summary

Workstream item #18. Audit finding M-9 (Medium): config.yaml.example ships with password: "changeme" and config.Defaults uses "admin". A deployment copying either unchanged ended up with working admin/changeme (or admin/admin) credentials.

Changes

  • ensureInitialAdmin refuses to create the initial admin user when the password is one of the obvious defaults (changeme, admin, password) or shorter than 12 characters. Logs a clear error and os.Exit(1) so the server can't accidentally boot with insecure defaults.
  • Check extracted into isInsecureInitialAdminPassword (pure func) for testing.
  • config.yaml.example placeholder updated to REPLACE_ME_with_a_strong_password with a comment explaining the constraint and pointing at the ASIAKIRJAT_ADMIN_PASSWORD env override.

⚠️ Behavior change for fresh deployments: if you boot the server against an empty database without setting a strong password, it will refuse to start. Existing deployments are unaffected — the check only runs when users.Count() == 0.

Test plan

  • go test ./... passes; 9-case TestIsInsecureInitialAdminPassword covers banned defaults, empty, just-too-short, and acceptable cases
  • Manual: fresh DB + default config (password: "changeme") → server logs the refusal and exits 1
  • Manual: fresh DB + ASIAKIRJAT_ADMIN_PASSWORD=a-strong-passphrase → starts normally and creates the admin
  • Manual: existing DB (users already present) → no change, server starts as usual

🤖 Generated with Claude Code

## Summary Workstream item #18. Audit finding **M-9 (Medium)**: `config.yaml.example` ships with `password: "changeme"` and `config.Defaults` uses `"admin"`. A deployment copying either unchanged ended up with working `admin/changeme` (or `admin/admin`) credentials. ## Changes - `ensureInitialAdmin` refuses to create the initial admin user when the password is one of the obvious defaults (`changeme`, `admin`, `password`) or shorter than 12 characters. Logs a clear error and `os.Exit(1)` so the server can't accidentally boot with insecure defaults. - Check extracted into `isInsecureInitialAdminPassword` (pure func) for testing. - `config.yaml.example` placeholder updated to `REPLACE_ME_with_a_strong_password` with a comment explaining the constraint and pointing at the `ASIAKIRJAT_ADMIN_PASSWORD` env override. ⚠️ Behavior change for fresh deployments: if you boot the server against an empty database without setting a strong password, it will refuse to start. Existing deployments are unaffected — the check only runs when `users.Count() == 0`. ## Test plan - [x] `go test ./...` passes; 9-case `TestIsInsecureInitialAdminPassword` covers banned defaults, empty, just-too-short, and acceptable cases - [ ] Manual: fresh DB + default config (`password: "changeme"`) → server logs the refusal and exits 1 - [ ] Manual: fresh DB + `ASIAKIRJAT_ADMIN_PASSWORD=a-strong-passphrase` → starts normally and creates the admin - [ ] Manual: existing DB (users already present) → no change, server starts as usual 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Refuse to start with insecure initial-admin password (M-9)
All checks were successful
CI / test (pull_request) Successful in 1m22s
CI / build (pull_request) Successful in 58s
CI / docker (pull_request) Has been skipped
9887d75275
Audit finding M-9: config.yaml.example ships with password: "changeme"
and config.Defaults uses "admin". A deployment copying either unchanged
ended up with working admin/changeme (or admin/admin) credentials, which
has happened in real setups.

ensureInitialAdmin now refuses to create the initial admin user when
the configured password is:
  - one of the obvious defaults: "changeme", "admin", "password"
  - shorter than 12 characters

On refusal the process logs a clear error and exits with status 1, so
the operator can't accidentally boot the server with insecure defaults.
Once an admin exists, this check is irrelevant — the password rotation
flow inside the running app has its own validation.

config.yaml.example placeholder updated to REPLACE_ME_with_a_strong_password
with a comment block explaining the constraint and pointing at the
ASIAKIRJAT_ADMIN_PASSWORD env override.

The check is extracted to isInsecureInitialAdminPassword for testability;
main_test.go covers the banned defaults, empty, just-too-short, and
acceptable cases.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
qwc merged commit b1cb2d86a6 into main 2026-05-18 22:59:15 +02:00
qwc deleted branch fix/reject-default-admin-password 2026-05-18 22:59:16 +02: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
qwc-open/asiakirjat!113
No description provided.