/api/auth/setup reaches the KDF unguarded before first-run completes #162
Labels
No labels
bug
duplicate
enhancement
help wanted
invalid
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
lz/agent-nexus#162
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Surfaced by PR #160 (rate-limiting
/api/auth/unlock) and deliberately scoped out of it. Belongs to the #110 auth-hardening family rather than to the MCP milestone — it blocks nothing there.The gap
/api/auth/setupis unauthenticated by necessity and does reach the KDF, deriving a key with no lock aroundisSetupComplete. PR #160 added an attempt limiter to/api/auth/unlockbut not here, so on an instance that has not completed first-run,/api/auth/setupis an unmetered Argon2id trigger — the same CPU-exhaustion shape #149 was filed for, on a different route.It also corrected a claim that had been written on that branch: "the only unauthenticated route that reaches the KDF" is false for a pre-setup instance. Both statements now say "once setup is complete", which is accurate and which is what leaves this gap visible.
Why it is lower priority than #149 was
An instance that has not been set up holds no secret. Anyone who can reach it can simply claim it outright by completing setup themselves — so CPU burn is not the marginal risk in that window, and a rate limiter would not be the control that matters.
The more interesting question is arguably the race:
isSetupCompleteis checked without a lock, and/api/auth/setupis unauthenticated. PR #146 already hardened the vault side of this by splittingwritePrincipalintoinsertPrincipal/upsertPrincipal, so a concurrent setup now fails loudly rather than silently replacing the winner's wrapper. Worth confirming that the route-level behaviour is equally sane, and that the loser of the race gets a comprehensible error rather than a 500.Work
UnlockLimiterrather than growing a second mechanism; it is a single global counter with no attacker-controlled key.