A02/A07: harden the first-boot setup window #9

Closed
opened 2026-08-13 09:35:02 +00:00 by nalum · 0 comments
Owner

While zero users exist, UserService/Create and SystemService/ImportData accept anonymous callers. The X-Initial-Admin-Token gate only applies when EAG_INITIAL_ADMIN_TOKEN is set, and every shipped default leaves it empty (deploy/timoni/eagraiclainne/values.cue:48-51, TrueNAS compose has it commented out). SetupStatus advertises setup_token_required=false, so an attacker knows the window is open. The first caller to reach a fresh install becomes founding admin, or imports an archive that carries their own admin accounts and jwt_secret.

Three related defects:

  1. Token optional in shipped defaults. Make the token mandatory, or set it in every published deploy manifest (timoni values, TrueNAS compose, flux).
  2. TOCTOU in Create (internal/services/user/create.go:44-59): the user-count check runs outside a transaction, so two racing anonymous Creates can both become founding admin. ImportData re-checks inside its transaction (import.go:70-77) — give Create the same fix.
  3. Non-constant-time token compare (create.go:69, import.go:181): != on a secret. Use crypto/subtle.ConstantTimeCompare.

Context: ADR-0018 and ADR-0023 accept parts of this trade-off. The live install was recreated empty on 2026-08-12, so the window is a real exposure, not a theoretical one.


Source: OWASP Top 10 (2025) audit of v1.1.0, 2026-08-13. File references point at the v1.1.0 tree.

While zero users exist, `UserService/Create` and `SystemService/ImportData` accept anonymous callers. The `X-Initial-Admin-Token` gate only applies when `EAG_INITIAL_ADMIN_TOKEN` is set, and every shipped default leaves it empty (`deploy/timoni/eagraiclainne/values.cue:48-51`, TrueNAS compose has it commented out). `SetupStatus` advertises `setup_token_required=false`, so an attacker knows the window is open. The first caller to reach a fresh install becomes founding admin, or imports an archive that carries their own admin accounts and `jwt_secret`. Three related defects: 1. **Token optional in shipped defaults.** Make the token mandatory, or set it in every published deploy manifest (timoni values, TrueNAS compose, flux). 2. **TOCTOU in `Create`** (`internal/services/user/create.go:44-59`): the user-count check runs outside a transaction, so two racing anonymous Creates can both become founding admin. `ImportData` re-checks inside its transaction (`import.go:70-77`) — give `Create` the same fix. 3. **Non-constant-time token compare** (`create.go:69`, `import.go:181`): `!=` on a secret. Use `crypto/subtle.ConstantTimeCompare`. Context: ADR-0018 and ADR-0023 accept parts of this trade-off. The live install was recreated empty on 2026-08-12, so the window is a real exposure, not a theoretical one. --- Source: OWASP Top 10 (2025) audit of `v1.1.0`, 2026-08-13. File references point at the `v1.1.0` tree.
nalum added reference refs/tags/v1.1.0 2026-08-13 09:37:46 +00:00
nalum closed this issue 2026-08-13 11:40:41 +00:00
Sign in to join this conversation.
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
eagraiclainne/app#9
No description provided.