From 362b3740ed891166f6b2be94c32f6e3545b02d20 Mon Sep 17 00:00:00 2001 From: Bob Shannon Date: Wed, 14 Nov 2018 15:42:47 -0500 Subject: [PATCH] Add basic authentication support to metrics endpoint --- docs/sources/installation/configuration.md | 9 +++++++++ pkg/api/http_server.go | 8 ++++++++ pkg/setting/setting.go | 6 ++++++ pkg/util/auth.go | 19 +++++++++++++++++++ 4 files changed, 42 insertions(+) create mode 100644 pkg/util/auth.go diff --git a/docs/sources/installation/configuration.md b/docs/sources/installation/configuration.md index 8d156e739bf..08bf461f0b5 100644 --- a/docs/sources/installation/configuration.md +++ b/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. diff --git a/pkg/api/http_server.go b/pkg/api/http_server.go index ce28e4716ee..ba7cb2c425b 100644 --- a/pkg/api/http_server.go +++ b/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) } diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index afae642f5b3..33c7fd965ce 100644 --- a/pkg/setting/setting.go +++ b/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) diff --git a/pkg/util/auth.go b/pkg/util/auth.go new file mode 100644 index 00000000000..41165e42927 --- /dev/null +++ b/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 +}