From ab5c42f4a0b62c33216446c0ba9059b15c3bcb15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20WENSKE?= Date: Sun, 13 Apr 2025 09:35:57 +0200 Subject: [PATCH] feat(api, gui): allow authentication bypass for metrics (#10045) ### Purpose Give the ability to skip authentication for prometheus metrics ("/metrics"). ### Testing When authentication is enabled and "Metrics Without Auth" is checked (not the default), the "/metrics" path remains accessible even when disconnected. ### Screenshots ![image](https://github.com/user-attachments/assets/144b696b-dd72-46f4-94d5-cd21848e4a4c) ### Documentation https://github.com/syncthing/docs/pull/906 --- lib/api/api_auth.go | 8 ++++++-- lib/api/api_csrf.go | 2 +- lib/config/guiconfiguration.go | 1 + 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/api/api_auth.go b/lib/api/api_auth.go index 3be4705f3..791888056 100644 --- a/lib/api/api_auth.go +++ b/lib/api/api_auth.go @@ -51,7 +51,7 @@ func forbidden(w http.ResponseWriter) { http.Error(w, "Forbidden", http.StatusForbidden) } -func isNoAuthPath(path string) bool { +func isNoAuthPath(path string, metricsWithoutAuth bool) bool { // Local variable instead of module var to prevent accidental mutation noAuthPaths := []string{ "/", @@ -60,6 +60,10 @@ func isNoAuthPath(path string) bool { "/rest/svc/lang", // Required to load language settings on login page } + if metricsWithoutAuth { + noAuthPaths = append(noAuthPaths, "/metrics") + } + // Local variable instead of module var to prevent accidental mutation noAuthPrefixes := []string{ // Static assets @@ -115,7 +119,7 @@ func (m *basicAuthAndSessionMiddleware) ServeHTTP(w http.ResponseWriter, r *http } // Exception for static assets and REST calls that don't require authentication. - if isNoAuthPath(r.URL.Path) { + if isNoAuthPath(r.URL.Path, m.guiCfg.MetricsWithoutAuth) { m.next.ServeHTTP(w, r) return } diff --git a/lib/api/api_csrf.go b/lib/api/api_csrf.go index e8f03418d..50c6749d8 100644 --- a/lib/api/api_csrf.go +++ b/lib/api/api_csrf.go @@ -78,7 +78,7 @@ func (m *csrfManager) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } - if isNoAuthPath(r.URL.Path) { + if isNoAuthPath(r.URL.Path, false) { // REST calls that don't require authentication also do not // need a CSRF token. m.next.ServeHTTP(w, r) diff --git a/lib/config/guiconfiguration.go b/lib/config/guiconfiguration.go index 749f6f349..a38c7652c 100644 --- a/lib/config/guiconfiguration.go +++ b/lib/config/guiconfiguration.go @@ -25,6 +25,7 @@ type GUIConfiguration struct { User string `json:"user" xml:"user,omitempty"` Password string `json:"password" xml:"password,omitempty"` AuthMode AuthMode `json:"authMode" xml:"authMode,omitempty"` + MetricsWithoutAuth bool `json:"metricsWithoutAuth" xml:"metricsWithoutAuth" default:"false"` RawUseTLS bool `json:"useTLS" xml:"tls,attr"` APIKey string `json:"apiKey" xml:"apikey,omitempty"` InsecureAdminAccess bool `json:"insecureAdminAccess" xml:"insecureAdminAccess,omitempty"`