Close the anonymous admin bypass - #376
Conversation
This comment has been minimized.
This comment has been minimized.
AUTH_ALLOW_ANONYMOUS_ADMIN made require_admin hand a superuser to any caller on every vhost, since one FastAPI app answers all of them. With the origin-side verifier in place (#372) and an Access application now in front of all three admin hostnames, this flag was the last thing holding the door open. All three deployed overlays lose it in a single commit because check-env-parity.py refuses to let them disagree about it, and that guard is right: closing the bypass in one environment and not the others is drift rather than a decision. docker-compose.local.yml keeps it deliberately, since a laptop has no Access assertion and no OAuth, and local sits outside the checker's scope. Staging and production gain the audience of the application created for each, so the verifier is configured everywhere the bypass has gone. An environment with neither would refuse everybody, including the people meant to get in, and failing closed should be reached on purpose rather than by omission. test_anonymous_admin_closed.py covers what parity cannot. Parity catches the flag returning to one environment; it cannot catch it returning to all three at once. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
These hit api.retina.fm, which carries no Access application and never can: it is the fleet's ingest hostname and a node cannot complete an interactive login. The 401 therefore comes from require_admin in this codebase rather than from the edge, which is the whole reason enforcement is backend-side. Response shape is no longer assertable from here, because nothing in CI can authenticate against that hostname; the backend suite still covers it. The admin surface-selection block would otherwise have stopped running altogether. It skipped itself once the server reported enforced auth, and with a login card in the way the sidebar that names the surface is never reached, so the assertion PR #335 added would have vanished quietly rather than failed. It now makes the strongest claim each auth mode allows: the surface name where the bypass is still on, and otherwise that the host resolved, Access admitted the run and nginx served this bundle. Which surface a hostname selects is resolveSurface's answer and stays covered in dashboard/src/test/surface.test.ts. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
fc4abc1 to
3543ccd
Compare
|
Claude finished @Babissimo's task in 21m 39s —— View job Review: Close the anonymous admin bypass (#376)Tasks
Findings1. Closing the bypass also removes the only working login path for the non-admin dashboard, on every deployed environment — not disclosed in the PR's "Known consequences" section. Verified directly in code, not just from the PR's own framing:
Net effect: before this PR, 2. (Altitude, plausible) The flag's removal only closes the bypass in the committed compose text, not necessarily on the live droplets.
3. Test/comment accuracy nits in the new coverage:
4. Frontend 401 handling, now exercised for the first time in production (medium confidence, follows from #1):
5. (Minor, reuse) Confirmed clean (per the PR's own claims)
I don't have deployment/network access in this sandbox to actually run |
Closes the anonymous-admin bypass, completing ClickUp
123zgec1gtf after #371 (CI's passage
through Access) and #372 (the origin-side verifier). Unlike those two, this one
changes behaviour:
/api/admin/*starts refusing anonymous callers on all sevenvhosts.
The Cloudflare side is already done. Applications now front all three admin
hostnames, each with the reusable allow policy for the
node-supportgroup andthe Service Auth policy for CI's token, and each verified returning a 302 to the
team login.
api.retina.fmdeliberately has none: it is the fleet's ingesthostname and a node cannot complete an interactive login, which is the whole
reason enforcement is backend-side.
The flag goes in one commit
deploy/check-env-parity.pyrefuses to let the three deployed overlays disagreeabout
AUTH_ALLOW_ANONYMOUS_ADMIN, and that guard is right: closing the bypassin one environment and not the others is drift rather than a decision. So all
three lose it together, and staging and production gain the audience of the
application created for each.
docker-compose.local.ymlkeeps the flag, since alaptop has no Access assertion and no OAuth and local sits outside the checker's
scope.
test_anonymous_admin_closed.pycovers what parity cannot. Parity catches theflag returning to one environment; it cannot catch it returning to all three at
once.
What was verified
offworldlab.cloudflareaccess.com, with the redirect'skidmatching theaudience pinned here.
api.retina.fm,dash,map,towersand the stagingand testmap surfaces confirmed unchanged.
deploy/check-env-parity.pyclean,pre-commit run --all-filesclean,typecheck:e2eclean, and the previously-red guard test now green./api/test/dashboard,/api/radar/analyticsand/api/radar/nodesareunauthenticated and stay so, and the
/api/auth/meprobe is a rate-limitburst that judges on 429 rather than on 200.
JWT_SECRETwas on the list of things to set on staging and test. It isalready set: all three droplets carry distinct 64-character secrets and the
running containers read them, so there was nothing to change.
Known consequences, not fixed here
nodes.spec.ts'safterAllretiresthe nodes it registers through
DELETE /api/admin/nodes/{id}/state, with nocredential and no way to get one on an ungated hostname. It will 401 and only
console.warn, so runs stay green while staging accumulatese2e-nodes.Raised separately; it wants a decision rather than a quick patch.
PUT /api/simulation/configisrequire_admin, and the map's physicspanel calls it from a bundle with no login path, so that panel goes read-only
on staging and test.
/api/admin/leaderboardand/api/admin/alertsareget_current_userrather than
require_admin: ordinary-user features under an admin prefix.They 401 too. Nothing regresses today, with no real accounts, but they want
re-homing before any external operator is onboarded.
Deploy
Staging first, then left a day as every change of this class here is, then
production. The staging run of this PR is also the first real test of CI's
service token against the new applications.
🤖 Generated with Claude Code