Jelajahi Sumber

Add basic authentication support to metrics endpoint

Bob Shannon 7 tahun lalu
induk
melakukan
362b3740ed

+ 9 - 0
docs/sources/installation/configuration.md

@@ -454,6 +454,15 @@ Ex `filters = sqlstore:debug`
 ### enabled
 Enable metrics reporting. defaults true. Available via HTTP API `/metrics`.
 
+### basic_auth_enabled
+Enables basic authentication on the metrics endpoint. Defaults to false.
+
+### basic_auth_username
+Username to use for basic authentication on the metrics endpoint.
+
+### basic_auth_password
+Password to use for basic authentication on the metrics endpoint.
+
 ### interval_seconds
 
 Flush/Write interval when sending metrics to external TSDB. Defaults to 10s.

+ 8 - 0
pkg/api/http_server.go

@@ -32,6 +32,7 @@ import (
 	"github.com/grafana/grafana/pkg/services/hooks"
 	"github.com/grafana/grafana/pkg/services/rendering"
 	"github.com/grafana/grafana/pkg/setting"
+	"github.com/grafana/grafana/pkg/util"
 )
 
 func init() {
@@ -245,6 +246,13 @@ func (hs *HTTPServer) metricsEndpoint(ctx *macaron.Context) {
 		return
 	}
 
+	if hs.Cfg.MetricsEndpointBasicAuthEnabled {
+		if !util.BasicAuthenticatedRequest(ctx.Req, hs.Cfg.MetricsEndpointBasicAuthUsername, hs.Cfg.MetricsEndpointBasicAuthPassword) {
+			ctx.Resp.WriteHeader(http.StatusUnauthorized)
+			return
+		}
+	}
+
 	promhttp.HandlerFor(prometheus.DefaultGatherer, promhttp.HandlerOpts{}).
 		ServeHTTP(ctx.Resp, ctx.Req.Request)
 }

+ 6 - 0
pkg/setting/setting.go

@@ -215,6 +215,9 @@ type Cfg struct {
 	DisableBruteForceLoginProtection bool
 	TempDataLifetime                 time.Duration
 	MetricsEndpointEnabled           bool
+	MetricsEndpointBasicAuthEnabled  bool
+	MetricsEndpointBasicAuthUsername string
+	MetricsEndpointBasicAuthPassword string
 	EnableAlphaPanels                bool
 	EnterpriseLicensePath            string
 }
@@ -676,6 +679,9 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error {
 	cfg.PhantomDir = filepath.Join(HomePath, "tools/phantomjs")
 	cfg.TempDataLifetime = iniFile.Section("paths").Key("temp_data_lifetime").MustDuration(time.Second * 3600 * 24)
 	cfg.MetricsEndpointEnabled = iniFile.Section("metrics").Key("enabled").MustBool(true)
+	cfg.MetricsEndpointBasicAuthEnabled = iniFile.Section("metrics").Key("basic_auth_enabled").MustBool(true)
+	cfg.MetricsEndpointBasicAuthUsername = iniFile.Section("metrics").Key("basic_auth_username").String()
+	cfg.MetricsEndpointBasicAuthPassword = iniFile.Section("metrics").Key("basic_auth_password").String()
 
 	analytics := iniFile.Section("analytics")
 	ReportingEnabled = analytics.Key("reporting_enabled").MustBool(true)

+ 19 - 0
pkg/util/auth.go

@@ -0,0 +1,19 @@
+package util
+
+import (
+	"crypto/subtle"
+	macaron "gopkg.in/macaron.v1"
+)
+
+// BasicAuthenticated parses the provided HTTP request for basic authentication credentials
+// and returns true if the provided credentials match the expected username and password.
+// Returns false if the request is unauthenticated.
+// Uses constant-time comparison in order to mitigate timing attacks.
+func BasicAuthenticatedRequest(req macaron.Request, expectedUser, expectedPass string) bool {
+	user, pass, ok := req.BasicAuth()
+	if !ok ||  subtle.ConstantTimeCompare([]byte(user), []byte(expectedUser)) != 1 || subtle.ConstantTimeCompare([]byte(pass), []byte(expectedPass)) != 1 {
+		return false
+	}
+
+	return true
+}