Skip to content

feat(auth): serve /metrics without auth by default (opt-out via metrics_require_auth)#4537

Open
mickgvirtu wants to merge 1 commit into
maximhq:devfrom
mickgvirtu:pr-metrics-public-default
Open

feat(auth): serve /metrics without auth by default (opt-out via metrics_require_auth)#4537
mickgvirtu wants to merge 1 commit into
maximhq:devfrom
mickgvirtu:pr-metrics-public-default

Conversation

@mickgvirtu

Copy link
Copy Markdown

Summary

Follow-up to #2695. That issue was resolved by letting operators add /metrics to the path whitelist — which works. This proposes making the conventional behavior the default: /metrics (like /health) bypasses auth out of the box, since Prometheus scrapers typically can't carry admin credentials, with an explicit opt-out for operators who consider the metric labels sensitive.

Changes

  • /metrics bypasses APIMiddleware auth by default, via an exact-match gate (no prefix widening: /metricsX, /metrics/foo stay authenticated).
  • New ClientConfig.metrics_require_auth (default false); set true to keep /metrics behind auth — for deployments where labels (provider/model/virtual-key/team/customer/cost) are sensitive.
  • Removed a duplicate /health entry in the whitelist.

Type of change

  • Feature

Affected areas

  • Transports (HTTP)
  • Core (Go)

How to test

go test ./transports/bifrost-http/handlers/ -run TestAuthMiddleware_Metrics

Covers public-by-default, gated-when-metrics_require_auth=true, and the exact-match negatives.

Follow-up: UI toggle (happy to send as a companion PR)

This adds a metrics_require_auth client-config field, which the dashboard should surface alongside the existing auth controls. The natural home is the Security settings view (ui/app/workspace/config/views/securityView.tsx) — next to the "Enable authentication" switch and the "Whitelisted routes" editor — plus the ClientConfig type in ui/lib/types/config.ts (beside whitelisted_routes): a single switch ("Require auth for /metrics", default off) wired through the existing setLocalConfig flow. Kept this PR backend-only to stay reviewable; glad to open the matching UI PR following your securityView patterns if you'd like it.


Deferential note: #2695 was closed with the position that the existing path-whitelist is the intended mechanism. We're proposing a default change on the principle that the conventional unauthenticated endpoint should require config to secure, not to expose — but we fully understand if you prefer to keep it whitelist-only. Happy to close this or rework it into docs for the whitelist path instead.

@CLAassistant

CLAassistant commented Jun 18, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: bae903c1-7260-44d6-8f82-52058ff3351a

📥 Commits

Reviewing files that changed from the base of the PR and between 48d8503 and 72cf118.

📒 Files selected for processing (5)
  • framework/configstore/clientconfig.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/bifrost-http/server/server.go
  • transports/config.schema.json
💤 Files with no reviewable changes (4)
  • transports/bifrost-http/server/server.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/config.schema.json
  • transports/bifrost-http/handlers/middlewares.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • framework/configstore/clientconfig.go

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Added a new configuration option to control whether the Prometheus /metrics endpoint requires authentication.
    • /metrics is public by default; enabling the option protects it behind auth.
    • The setting is applied at runtime during configuration reload.
  • Bug Fixes
    • Ensured only the exact /metrics route is affected; similar paths (e.g., /metricsX, /metrics/...) are not exempted.

Walkthrough

A new MetricsRequireAuth boolean field is added to ClientConfig with schema support. AuthMiddleware gains an atomic.Bool for this flag, initialized from config and updatable at runtime via UpdateMetricsRequireAuth. APIMiddleware now exact-matches /metrics and gates it on the flag. ReloadClientConfigFromConfigStore propagates the setting on reload. Tests cover the toggle and near-miss path exclusions.

Changes

Configurable /metrics Auth

Layer / File(s) Summary
Config contract and schema
framework/configstore/clientconfig.go, transports/config.schema.json
MetricsRequireAuth bool added to ClientConfig with JSON tag. Schema field metrics_require_auth defined with default false.
Middleware flag, initialization, and auth decision
transports/bifrost-http/handlers/middlewares.go
AuthMiddleware gains atomic.Bool field initialized from config in InitAuthMiddleware, exposed via new UpdateMetricsRequireAuth method. APIMiddleware special-cases url == "/metrics" to return !m.metricsRequireAuth.Load() as the auth-skip decision. Removes redundant "/health" from systemWhitelistedRoutes.
Config reload propagation and tests
transports/bifrost-http/server/server.go, transports/bifrost-http/handlers/middlewares_test.go
ReloadClientConfigFromConfigStore now calls UpdateMetricsRequireAuth with the reloaded config value. Existing whitelist test gains "/metrics" entry; new TestAuthMiddleware_Metrics validates public/gated toggle and exact-match exclusion of near-miss paths.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐇 A metrics path, once wide open and free,
Now wears a lock if the config decree!
atomic.Bool keeps the flag in its place,
Near-miss paths find no exempt embrace.
Reload the config, the change flows right through —
The rabbit ensures your /metrics is true! 🔐

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: /metrics endpoint is now public by default with an opt-out configuration option.
Description check ✅ Passed The description covers Summary, Changes, Type of change, Affected areas, How to test, and Security considerations with sufficient detail and follows the template structure.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps

greptile-apps Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Confidence Score: 5/5

Safe to merge — the change narrows the auth surface of a single endpoint using an exact-match gate, follows all existing concurrency and hot-reload patterns, updates the schema, and is covered by targeted tests.

The /metrics exemption uses an atomic.Bool for race-safe hot reloads, identical to how tempTokensEnabled is handled. The exact-match check (url == "/metrics") prevents prefix widening. The schema is updated. The duplicate /health removal is confirmed correct — one occurrence remains in systemWhitelistedRoutes. Tests cover all three defined scenarios. No correctness, concurrency, or API-compatibility issues were found.

No files require special attention.

Important Files Changed

Filename Overview
transports/bifrost-http/handlers/middlewares.go Adds an exact-match early-return gate for /metrics before the whitelist checks, backed by atomic.Bool; removes a genuine duplicate /health entry. Logic and concurrency model match existing patterns.
framework/configstore/clientconfig.go Adds MetricsRequireAuth bool with json:"metrics_require_auth" to ClientConfig; default zero-value of false matches the intended public-by-default behavior.
transports/config.schema.json Adds metrics_require_auth boolean entry with "default": false and full description between whitelisted_routes and hide_deleted_virtual_keys_in_filters, keeping the schema in sync.
transports/bifrost-http/server/server.go Wires UpdateMetricsRequireAuth into ReloadClientConfigFromConfigStore alongside the other hot-reload calls; no issues.
transports/bifrost-http/handlers/middlewares_test.go Adds TestAuthMiddleware_Metrics covering public-by-default, auth-gated, and near-miss paths; also extends TestAuthMiddleware_WhitelistedRoutes to cover the default /metrics exemption.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Incoming Request] --> B{isAPIKeyAuth?}
    B -- yes --> Z[next handler]
    B -- no --> C{authConfig nil\nor disabled?}
    C -- yes --> Z
    C -- no --> D{url == /metrics?}
    D -- yes --> E{metricsRequireAuth\n= true?}
    E -- no --> Z
    E -- yes --> G{Check auth\nheader / session}
    D -- no --> F{systemWhitelistedRoutes\nor whitelistedPrefixes?}
    F -- yes --> Z
    F -- no --> H{user whitelistedRoutes?}
    H -- yes --> Z
    H -- no --> G
    G -- valid --> Z
    G -- invalid --> Y[401 Unauthorized]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[Incoming Request] --> B{isAPIKeyAuth?}
    B -- yes --> Z[next handler]
    B -- no --> C{authConfig nil\nor disabled?}
    C -- yes --> Z
    C -- no --> D{url == /metrics?}
    D -- yes --> E{metricsRequireAuth\n= true?}
    E -- no --> Z
    E -- yes --> G{Check auth\nheader / session}
    D -- no --> F{systemWhitelistedRoutes\nor whitelistedPrefixes?}
    F -- yes --> Z
    F -- no --> H{user whitelistedRoutes?}
    H -- yes --> Z
    H -- no --> G
    G -- valid --> Z
    G -- invalid --> Y[401 Unauthorized]
Loading

Reviews (3): Last reviewed commit: "auth: /metrics public by default, gated ..." | Re-trigger Greptile

RequiredHeaders []string `json:"required_headers,omitempty"` // Headers that must be present on every request (case-insensitive)
LoggingHeaders []string `json:"logging_headers,omitempty"` // Headers to capture in log metadata
WhitelistedRoutes []string `json:"whitelisted_routes,omitempty"` // Routes that bypass auth middleware
MetricsRequireAuth bool `json:"metrics_require_auth"` // Require auth on the Prometheus /metrics endpoint (default false: /metrics is public, like /health — scrapers can't carry admin auth). Set true to keep /metrics behind the auth middleware.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 metrics_require_auth missing from config schema

transports/config.schema.json is the declared source of truth for all config fields in this repo. metrics_require_auth is added to ClientConfig but is absent from the schema, so tools that validate or document the config against the schema (e.g., JSON Schema validators, the dashboard config editor, docs generation) will not recognise the field. The entry should appear between whitelisted_routes and hide_deleted_virtual_keys_in_filters, as a boolean with "default": false and a short description matching the Go doc comment.

Rule Used: transports/config.schema.json is the source of tru... (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@framework/configstore/clientconfig.go`:
- Line 98: The `metrics_require_auth` boolean field is defined in the
clientconfig structure but is missing from the transports/config.schema.json
schema file, which creates a mismatch between the code configuration and its
schema definition. Add the `metrics_require_auth` field entry to the schema file
with the appropriate type (boolean) and description, placing it alongside the
other auth-related fields like `required_headers`, `logging_headers`,
`whitelisted_routes`, and `hide_deleted_virtual_keys_in_filters` to maintain
consistency and parity with the actual code configuration.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 607fd56a-c5cf-4e13-995d-97f1bf32bd27

📥 Commits

Reviewing files that changed from the base of the PR and between 96bb2bd and 489e5dd.

📒 Files selected for processing (4)
  • framework/configstore/clientconfig.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/bifrost-http/server/server.go

Comment thread framework/configstore/clientconfig.go
@mickgvirtu

Copy link
Copy Markdown
Author

Thanks — addressed:

  • metrics_require_auth missing from transports/config.schema.json (greptile P1 / coderabbit): added the field to the schema (boolean, default false) next to whitelisted_routes, with a description noting it gates the public-by-default /metrics. go test ./transports/schema_test/ passes, and the schema↔struct sync check no longer reports metrics_require_auth as drift.

On the design itself (per the PR description): this remains a proposal to flip the default to public — happy to defer to the whitelist-only approach from #2695 if you'd prefer. I also offered a companion UI toggle for the Security settings view; glad to send it if this lands.

@mickgvirtu mickgvirtu force-pushed the pr-metrics-public-default branch from 489e5dd to 48d8503 Compare June 19, 2026 17:16
The Prometheus scrape endpoint is operational telemetry and scrapers can't carry admin auth,
so /metrics bypasses the auth middleware by default (like /health). Because the metric labels
can include provider/model/virtual-key/team/customer/cost, operators who consider that
sensitive can set client config metrics_require_auth=true to keep /metrics behind auth.

Implemented as an exact-match gate in APIMiddleware reading an atomic flag (default false),
fed from ClientConfig.MetricsRequireAuth via UpdateMetricsRequireAuth — not a hardcoded
unconditional whitelist. Also drops a duplicate /health entry. Tests cover public-by-default,
gated-when-set, and the exact-match guard (/metricsX and /metrics/foo stay authenticated).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@mickgvirtu mickgvirtu force-pushed the pr-metrics-public-default branch from 48d8503 to 72cf118 Compare June 21, 2026 18:23
@mickgvirtu

Copy link
Copy Markdown
Author

Rebased onto transports/v1.5.16 to keep current with dev. Re-applied the metrics_require_auth entry against this release's restructured config.schema.json; TestConfigSchemaSync and TestAuthMiddleware_Metrics pass. Still mergeable into dev.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants