fix(gateway): gate /metrics endpoint behind optional auth and rate limiting - #6756
Merged
Merged
Conversation
bug-ops
force-pushed
the
fix/6550-gateway-oauth-subagent-hardening
branch
from
September 5, 2026 19:40
ed2d6d0 to
d039210
Compare
bug-ops
enabled auto-merge (squash)
September 5, 2026 19:40
…miting Add opt-in `[metrics] require_auth` (default false, preserving existing behavior) so operators exposing the gateway beyond a trusted scrape network can require the same bearer token and rate limiting that /webhook already enforces. Previously /metrics was merged into the router after auth/rate-limit layers were applied to the protected sub-router, and axum's Router::merge does not propagate an outer layer onto a router merged in afterward, so no auth mode existed for /metrics at all. Also extracts the duplicated MCP OAuth vault-key derivation (loader.rs and bootstrap/oauth.rs) into a single zeph_config::oauth_vault_key helper, with new collision-validation test coverage. Two of the three findings originally batched in #6550 (MCP OAuth vault-key collision validation, GrantKind::Tool dispatch enforcement) were already fixed upstream since the issue was filed and required no changes here. Closes #6550
bug-ops
force-pushed
the
fix/6550-gateway-oauth-subagent-hardening
branch
from
September 5, 2026 19:42
d039210 to
b11682f
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #6550
Batch of three low-severity hardening observations from a prior security audit (CI-1429). Re-verified against current code before implementing — two of the three findings had already been fixed upstream since the issue was filed and needed no changes here:
/metricsbypassed both the gateway's bearer-token auth and rate limiting.GatewayServer::servemerged an unlayered metrics sub-router onto the router afterbuild_routerhad already appliedauth_middleware/rate_limit_middlewareto theprotectedsub-router —axum::Router::mergedoes not propagate a layer applied to the outer router onto a router merged in afterward, so no auth mode existed for/metricsat all.Config::validate_mcp_serversalready rejects this at config load time. This PR only extracts the duplicated key-derivation logic (loader.rsandbootstrap/oauth.rs) into a singlezeph_config::oauth_vault_keyhelper and adds collision-path test coverage that was missing.GrantKind::Toolinzeph-subagentis enforced at dispatch via closed issue research(subagent): Tool-kind capability grants are TTL-modeled but never enforced before dispatch #6567 (check_tool_grant/ToolGrantCheck). No changes needed; not touched by this PR.Changes
[metrics] require_auth(defaultfalse, no behavior change for existing deployments). Whentrue,/metricsrequires the sameAuthorization: Bearer <token>header as/webhook, and gets its own independent per-IP rate-limit counter with the same limit and middleware ordering (rate limit outside auth, so a failed-auth attempt still counts toward the limit) — closing a brute-force gap on the gateway token that an earlier revision of this fix left open (auth-only, no rate limiter).attach_metrics_routeincrates/zeph-gateway/src/server.rsso bothserve()and its tests exercise the identical code path — a regression that reintroduces the original unlayered-merge bug now fails the tests instead of only an audit.require_auththroughMetricsConfig→GatewayServer::with_metrics_registry→gateway_spawn.rs→runner.rs; added toconfig/default.toml, the--initwizard's Prometheus step,book/src/advanced/gateway.md, andspecs/019-gateway/spec.md's Key Invariants.zeph_config::oauth_vault_keyshared helper (F2 residual), used by bothConfig::validate_mcp_serversandVaultCredentialStore::new; added collision-rejection and negative-case tests.Test plan
cargo +nightly fmt --checkcargo clippy --profile ci --workspace --all-targets --features "desktop,ide,server,chat,pdf,scheduler,testing" -- -D warningscargo nextest run --config-file .github/nextest.toml --workspace --features "desktop,ide,server,chat,pdf,scheduler" --lib --bins(15406 passed, 0 failed)RUSTFLAGS="-D warnings" RUSTDOCFLAGS="--deny rustdoc::broken_intra_doc_links" cargo doc --no-deps --workspace ...)cargo test --doc -p zeph-gateway --features prometheusgitleaks protect --stagedrequire_auth = truerejects an unauthenticated request (401) and accepts a correctly authenticated one (200); a third failed-auth request against/metricsis rate-limited (429);require_auth = falseregression guard confirms the historical unauthenticated/unthrottled default is unchanged..local/testing/playbooks/gateway.mdScenario 12, 12a-12e) and coverage-status row added for a follow-up live session.