Guard admin endpoints behind auth by default#443
Guard admin endpoints behind auth by default#443ChristianPavilonis wants to merge 3 commits intomainfrom
Conversation
Admin paths (/admin/*) were only auth-gated when a configured handler's path regex happened to match them. The default config (^/secure) left /admin/keys/rotate and /admin/keys/deactivate publicly accessible. Now enforce_basic_auth always denies admin paths that no handler covers, and a startup warning alerts operators when no handler matches /admin/. Closes #400
P2: Check all admin endpoints (rotate + deactivate) in coverage
warning instead of only checking rotate.
P2: Move admin coverage warning from per-request main() to build
time via cargo:warning in build.rs, avoiding log noise in
Fastly Compute where every request is a fresh WASM instance.
P3: Guard exact /admin path in addition to /admin/ prefix so
future endpoints at that path are also protected.
There was a problem hiding this comment.
Summary
This PR correctly identifies and fixes the security gap where admin endpoints could be accessed without authentication when no handler is configured. The fail-closed approach is sound. However, the implementation adds complexity that may not be necessary given the existing handler-based auth system.
♻️ refactor
- Simplify: use config validation instead of runtime guard: The handler-based auth already enforces credentials correctly. Instead of adding
is_admin_path()and special-case logic inenforce_basic_auth, makeSettings::validate()return an error when no handler covers admin endpoints. This keepsenforce_basic_authunchanged, eliminates the dual-mechanism (is_admin_pathprefix check vsADMIN_ENDPOINTSlist), and catches misconfiguration at build time as an error rather than silently returning 401s at runtime. The existinguncovered_admin_endpoints()method is the right building block — call it fromvalidate()instead ofbuild.rs.
|
To clarify the refactor suggestion above — the required config is just a standard [[handlers]]
path = "^/admin"
username = "admin"
password = "a-strong-password"With the validation approach, |
Move admin endpoint protection from a runtime check in enforce_basic_auth to a build-time validation error in Settings::from_toml_and_env. This eliminates the dual-mechanism (is_admin_path prefix check vs handler-based auth) and catches misconfiguration at build time rather than silently returning 401s at runtime.
|
Addressed the review feedback — refactored from runtime guard to build-time config validation:
All 472 tests pass, clippy clean, fmt clean. |
Summary
/admin/keys/rotateand/admin/keys/deactivatewere publicly accessible when no handler regex covered/admin/*pathsSettings::from_toml_and_env()now returns a hard error when no handler covers admin endpoints, catching misconfiguration at build timeenforce_basic_authremains a single, simple mechanism — admin protection is enforced by requiring handler coverage in the configurationChanges
crates/common/src/auth.rsenforce_basic_auth— removeis_admin_path()and runtime admin guard; admin paths use standard handler-based authcrates/common/src/settings.rsfrom_toml_and_env()errors whenuncovered_admin_endpoints()finds unprotected admin paths; update doc comments and testscrates/common/build.rscargo:warningloop (redundant —from_toml_and_envnow errors on uncovered admin endpoints)crates/common/src/test_support.rs^/adminhandler to base test settingstrusted-server.toml^/adminhandler to dev configCloses
Closes #400
Test plan
cargo test --workspace(472 tests pass)cargo clippy --all-targets --all-features -- -D warningscargo fmt --all -- --checkChecklist
unwrap()in production code — useexpect("should ...")tracingmacros (notprintln!)