fix(security): require authentication on /metrics endpoint#1288
fix(security): require authentication on /metrics endpoint#1288agh wants to merge 1 commit intojetkvm:devfrom
Conversation
The Prometheus metrics endpoint is the only route on the device with no authentication. Every other route is either public-by-design or behind protectedMiddleware (cookie) or basicAuthProtectedMiddleware (basic auth). Add metricsAuthMiddleware that accepts either the session cookie (browser access) or HTTP basic auth (Prometheus scrape configs). When localAuthMode is noPassword, all requests pass through — consistent with every other protected endpoint. No config toggle, no UI changes. The endpoint remains always available. Existing no-password setups are unaffected. Password-mode setups need to add basic_auth to their Prometheus scrape config. Alternative approach to jetkvm#299 that addresses the security concern without disabling the endpoint.
SuperQ
left a comment
There was a problem hiding this comment.
LGTM, thanks! I haven't looked at this code in a while to see how the config/auth is handled.
Sorry if this is obvious but is there a way or need to get the TLS status before enabling auth?
I worry about people enabling auth without TLS first. It's going to encourage users to leak their credentials over the wire without knowing that basic auth / token isn't protected.
agh
left a comment
There was a problem hiding this comment.
Good point — worth thinking about. This PR doesn't change the exposure surface though: the cookie auth (authToken) is already sent over plaintext HTTP with Secure: false, and the developer mode routes use basic auth over HTTP too. The metrics basic auth here sits alongside those existing paths.
You're right that it would be nice to nudge users toward TLS before relying on auth. Some pieces are already in the codebase that could help with that:
config.TLSModetracks whether TLS is enabled ("","self-signed","custom")getTLSState()inweb_tls.goexposes the current state- Issue #1092 is specifically about adding HTTP→HTTPS redirect when TLS is enabled, which would be the natural place to tackle credential protection on the wire
Longer term, setting Secure: true on the auth cookie when TLS is active and landing the HTTPS redirect from #1092 would cover this properly across all endpoints — not just metrics. Both feel like good follow-ups.
|
You admit that "disabling metrics does not improve security" but by forcing authentication anyway, now I need to put my password in plain text in a configuration file just to scrape statistics, which actively harms security by adding a new attack surface and potential place for that credential to be leaked. I think this should be configurable, or there should be the ability to configure an alternate auth token for this that is read-only. |
|
That quote is about PR #299's approach of adding a toggle to disable On the credential storage concern — that's a fair point, though it applies to any authenticated Prometheus target, not specifically to this change. Prometheus's own documentation recommends basic auth for scrape targets and supports A dedicated read-only metrics token is a reasonable idea and would address the concern about reusing the admin credential. That's a larger feature though (new config field, UI, token generation/storage), and I don't think it should gate closing the unauthenticated hole. Happy to open an issue to track it as a follow-up if that sounds useful. |
|
I think the main concern is the violation of the principle of least privilege due to the minimalist authentication system in place. The authentication itself isn't the problem, but the fact that you have to give it a full-access credential is the concern. I'm on the fence on which is worse, but opening an issue to add such a low-privilege token does at least start to address my concern! |
|
I'm going to wait for @adamshiervani to weigh in. It's ultimately down to what he's prepared to merge. |
Alternative approach to #299 that addresses the security concern without disabling the endpoint.
Problem
/metricsis the only endpoint on the device with no authentication. Every other route is either public-by-design (/device/status,/auth/login-local) or behindprotectedMiddleware(cookie auth) orbasicAuthProtectedMiddleware(basic auth). The metrics endpoint sits outside all route groups with no auth at all.Why #299 stalled
PR #299 proposed a config toggle to disable metrics entirely. This drew pushback from @SuperQ who correctly pointed out that disabling metrics does not improve security — the endpoint should simply require the same auth as everything else. @IDisposable raised a CPU overhead concern, but gathering only happens on scrape so a toggle does not help there either. The PR has been in draft with
needs-reworksince March 2025.What this PR does instead
Add a
metricsAuthMiddlewarethat accepts authentication via two methods:Session cookie (
authToken) — same asprotectedMiddleware. Browser access to/metricsworks if logged in.HTTP basic auth — same password verification as
basicAuthProtectedMiddleware. This is how Prometheus scrape configs authenticate:No-password mode passthrough — when
localAuthModeisnoPassword, the middleware passes all requests through. Consistent with every other protected endpoint on the device.What this does NOT do
How it addresses each concern from #299