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: 2419d313-10a8-4391-b7ae-911f91a16c80

📥 Commits

Reviewing files that changed from the base of the PR and between 489e5dd and 48d8503.

📒 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 skipped from review due to trivial changes (1)
  • framework/configstore/clientconfig.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/bifrost-http/server/server.go
  • transports/bifrost-http/handlers/middlewares.go

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Added a new configuration option to control whether the Prometheus /metrics endpoint requires authentication.
    • By default, /metrics is public; enabling auth protection restricts access accordingly.
    • Changes can be applied at runtime via configuration reload.
  • Bug Fixes
    • Improved route handling to ensure only the exact /metrics path is affected (near-miss paths 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 specifically summarizes the main change: /metrics endpoint now serves without auth by default with explicit opt-out via metrics_require_auth config field.
Description check ✅ Passed The description covers all key sections: Summary explains the problem and solution, Changes detail implementation specifics, Type of change is marked, Affected areas are checked, How to test provides test commands, and Security considerations discuss label sensitivity.
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 is additive, backward-compatible, and correctly defaults to public /metrics without touching any authenticated paths.

The /metrics gate is an exact-match early-return backed by an atomic.Bool, consistent with how tempTokensEnabled is handled elsewhere. InitAuthMiddleware, ReloadClientConfigFromConfigStore, and the hot-reload path all propagate the new flag. The config schema was updated in this same PR. The duplicate /health removal is correct. Tests cover all three meaningful states. No pre-existing auth paths are weakened.

No files require special attention.

Important Files Changed

Filename Overview
transports/bifrost-http/handlers/middlewares.go Adds metricsRequireAuth atomic.Bool; exact-match /metrics gate returns early before systemWhitelistedRoutes check; atomics, init, and update path are all consistent with tempTokensEnabled pattern.
framework/configstore/clientconfig.go Adds MetricsRequireAuth bool field with json tag and doc comment; no omitempty so zero-value false round-trips correctly.
transports/bifrost-http/server/server.go ReloadClientConfigFromConfigStore now propagates MetricsRequireAuth alongside whitelisted routes and temp-token flag; no issues.
transports/config.schema.json metrics_require_auth added as boolean with default false and description; correctly placed between whitelisted_routes and hide_deleted_virtual_keys_in_filters.
transports/bifrost-http/handlers/middlewares_test.go New TestAuthMiddleware_Metrics covers all three cases: public-by-default, gated-when-true, and exact-match negatives; /metrics added to existing whitelisted-routes smoke test.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Prometheus as Prometheus Scraper
    participant MW as APIMiddleware
    participant Auth as Auth Check
    participant H as /metrics Handler

    Note over MW: metricsRequireAuth = false (default)
    Prometheus->>MW: GET /metrics (no credentials)
    MW->>MW: "url == "/metrics" → !metricsRequireAuth = true"
    MW->>H: next(ctx) — bypasses auth
    H-->>Prometheus: 200 OK (metrics)

    Note over MW: metricsRequireAuth = true (opt-in)
    Prometheus->>MW: GET /metrics (no credentials)
    MW->>MW: "url == "/metrics" → !metricsRequireAuth = false"
    MW->>Auth: enforce auth
    Auth-->>Prometheus: 401 Unauthorized

    Note over MW: Near-miss paths always auth-gated
    Prometheus->>MW: GET /metricsX or /metrics/foo
    MW->>MW: "url != "/metrics" → falls through to systemWhitelist check"
    MW->>Auth: enforce auth
    Auth-->>Prometheus: 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"}}}%%
sequenceDiagram
    participant Prometheus as Prometheus Scraper
    participant MW as APIMiddleware
    participant Auth as Auth Check
    participant H as /metrics Handler

    Note over MW: metricsRequireAuth = false (default)
    Prometheus->>MW: GET /metrics (no credentials)
    MW->>MW: "url == "/metrics" → !metricsRequireAuth = true"
    MW->>H: next(ctx) — bypasses auth
    H-->>Prometheus: 200 OK (metrics)

    Note over MW: metricsRequireAuth = true (opt-in)
    Prometheus->>MW: GET /metrics (no credentials)
    MW->>MW: "url == "/metrics" → !metricsRequireAuth = false"
    MW->>Auth: enforce auth
    Auth-->>Prometheus: 401 Unauthorized

    Note over MW: Near-miss paths always auth-gated
    Prometheus->>MW: GET /metricsX or /metrics/foo
    MW->>MW: "url != "/metrics" → falls through to systemWhitelist check"
    MW->>Auth: enforce auth
    Auth-->>Prometheus: 401 Unauthorized
Loading

Reviews (2): 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
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

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
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