aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKegsay <kegan@matrix.org>2020-04-14 15:54:35 +0100
committerGitHub <noreply@github.com>2020-04-14 15:54:35 +0100
commit609f034bfb241934dc0653a7af0df08417688640 (patch)
tree40c5e487e540659e202f696ce94187e4d23ad1f1
parent2c43e222bd57cbb1e278ff776f7a2aa2221ac1e8 (diff)
S7evinK: basicauth metrics (#961)
* Add setting to enable/disable metrics (#461) Add basic auth to /metric handlers Signed-off-by: Till Faelligen <tfaelligen@gmail.com> * Add warning message if metrics are exposed without protection * Remove redundant type conversion Signed-off-by: Till Faelligen <tfaelligen@gmail.com> * SetBasicAuth per test case * Update warning message and change loglevel to warn * Update common/config/config.go * Update dendrite-config.yaml Co-authored-by: Till Faelligen <tfaelligen@gmail.com> Co-authored-by: Neil Alexander <neilalexander@users.noreply.github.com>
-rw-r--r--cmd/dendrite-monolith-server/main.go6
-rw-r--r--common/basecomponent/base.go2
-rw-r--r--common/config/config.go13
-rw-r--r--common/httpapi.go35
-rw-r--r--common/httpapi_test.go95
-rw-r--r--dendrite-config.yaml9
6 files changed, 155 insertions, 5 deletions
diff --git a/cmd/dendrite-monolith-server/main.go b/cmd/dendrite-monolith-server/main.go
index 9f6531ed..c71d956b 100644
--- a/cmd/dendrite-monolith-server/main.go
+++ b/cmd/dendrite-monolith-server/main.go
@@ -33,8 +33,8 @@ import (
"github.com/matrix-org/dendrite/publicroomsapi"
"github.com/matrix-org/dendrite/roomserver"
"github.com/matrix-org/dendrite/syncapi"
-
"github.com/prometheus/client_golang/prometheus/promhttp"
+
"github.com/sirupsen/logrus"
)
@@ -78,7 +78,9 @@ func main() {
// Set up the API endpoints we handle. /metrics is for prometheus, and is
// not wrapped by CORS, while everything else is
- http.Handle("/metrics", promhttp.Handler())
+ if cfg.Metrics.Enabled {
+ http.Handle("/metrics", common.WrapHandlerInBasicAuth(promhttp.Handler(), cfg.Metrics.BasicAuth))
+ }
http.Handle("/", httpHandler)
// Expose the matrix APIs directly rather than putting them under a /api path.
diff --git a/common/basecomponent/base.go b/common/basecomponent/base.go
index 432819a2..dc27f540 100644
--- a/common/basecomponent/base.go
+++ b/common/basecomponent/base.go
@@ -208,7 +208,7 @@ func (b *BaseDendrite) SetupAndServeHTTP(bindaddr string, listenaddr string) {
addr = listenaddr
}
- common.SetupHTTPAPI(http.DefaultServeMux, common.WrapHandlerInCORS(b.APIMux))
+ common.SetupHTTPAPI(http.DefaultServeMux, common.WrapHandlerInCORS(b.APIMux), b.Cfg)
logrus.Infof("Starting %s server on %s", b.componentName, addr)
err := http.ListenAndServe(addr, nil)
diff --git a/common/config/config.go b/common/config/config.go
index e2f5e663..a1a84425 100644
--- a/common/config/config.go
+++ b/common/config/config.go
@@ -119,6 +119,19 @@ type Dendrite struct {
ThumbnailSizes []ThumbnailSize `yaml:"thumbnail_sizes"`
} `yaml:"media"`
+ // The configuration to use for Prometheus metrics
+ Metrics struct {
+ // Whether or not the metrics are enabled
+ Enabled bool `yaml:"enabled"`
+ // Use BasicAuth for Authorization
+ BasicAuth struct {
+ // Authorization via Static Username & Password
+ // Hardcoded Username and Password
+ Username string `yaml:"username"`
+ Password string `yaml:"password"`
+ } `yaml:"basic_auth"`
+ } `yaml:"metrics"`
+
// The configuration for talking to kafka.
Kafka struct {
// A list of kafka addresses to connect to.
diff --git a/common/httpapi.go b/common/httpapi.go
index 22c77447..843336f5 100644
--- a/common/httpapi.go
+++ b/common/httpapi.go
@@ -6,6 +6,7 @@ import (
"github.com/matrix-org/dendrite/clientapi/auth"
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
+ "github.com/matrix-org/dendrite/common/config"
"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/util"
opentracing "github.com/opentracing/opentracing-go"
@@ -13,8 +14,15 @@ import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prometheus/client_golang/prometheus/promhttp"
+ "github.com/sirupsen/logrus"
)
+// BasicAuth is used for authorization on /metrics handlers
+type BasicAuth struct {
+ Username string `yaml:"username"`
+ Password string `yaml:"password"`
+}
+
// MakeAuthAPI turns a util.JSONRequestHandler function into an http.Handler which authenticates the request.
func MakeAuthAPI(
metricsName string, data auth.Data,
@@ -123,11 +131,34 @@ func MakeFedAPI(
// SetupHTTPAPI registers an HTTP API mux under /api and sets up a metrics
// listener.
-func SetupHTTPAPI(servMux *http.ServeMux, apiMux http.Handler) {
- servMux.Handle("/metrics", promhttp.Handler())
+func SetupHTTPAPI(servMux *http.ServeMux, apiMux http.Handler, cfg *config.Dendrite) {
+ if cfg.Metrics.Enabled {
+ servMux.Handle("/metrics", WrapHandlerInBasicAuth(promhttp.Handler(), cfg.Metrics.BasicAuth))
+ }
servMux.Handle("/api/", http.StripPrefix("/api", apiMux))
}
+// WrapHandlerInBasicAuth adds basic auth to a handler. Only used for /metrics
+func WrapHandlerInBasicAuth(h http.Handler, b BasicAuth) http.HandlerFunc {
+ if b.Username == "" || b.Password == "" {
+ logrus.Warn("Metrics are exposed without protection. Make sure you set up protection at proxy level.")
+ }
+ return func(w http.ResponseWriter, r *http.Request) {
+ // Serve without authorization if either Username or Password is unset
+ if b.Username == "" || b.Password == "" {
+ h.ServeHTTP(w, r)
+ return
+ }
+ user, pass, ok := r.BasicAuth()
+
+ if !ok || user != b.Username || pass != b.Password {
+ http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
+ return
+ }
+ h.ServeHTTP(w, r)
+ }
+}
+
// WrapHandlerInCORS adds CORS headers to all responses, including all error
// responses.
// Handles OPTIONS requests directly.
diff --git a/common/httpapi_test.go b/common/httpapi_test.go
new file mode 100644
index 00000000..7de7ce33
--- /dev/null
+++ b/common/httpapi_test.go
@@ -0,0 +1,95 @@
+package common
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "testing"
+)
+
+func TestWrapHandlerInBasicAuth(t *testing.T) {
+ type args struct {
+ h http.Handler
+ b BasicAuth
+ }
+
+ dummyHandler := http.HandlerFunc(func(h http.ResponseWriter, r *http.Request) {
+ h.WriteHeader(http.StatusOK)
+ })
+
+ tests := []struct {
+ name string
+ args args
+ want int
+ reqAuth bool
+ }{
+ {
+ name: "no user or password setup",
+ args: args{h: dummyHandler},
+ want: http.StatusOK,
+ reqAuth: false,
+ },
+ {
+ name: "only user set",
+ args: args{
+ h: dummyHandler,
+ b: BasicAuth{Username: "test"}, // no basic auth
+ },
+ want: http.StatusOK,
+ reqAuth: false,
+ },
+ {
+ name: "only pass set",
+ args: args{
+ h: dummyHandler,
+ b: BasicAuth{Password: "test"}, // no basic auth
+ },
+ want: http.StatusOK,
+ reqAuth: false,
+ },
+ {
+ name: "credentials correct",
+ args: args{
+ h: dummyHandler,
+ b: BasicAuth{Username: "test", Password: "test"}, // basic auth enabled
+ },
+ want: http.StatusOK,
+ reqAuth: true,
+ },
+ {
+ name: "credentials wrong",
+ args: args{
+ h: dummyHandler,
+ b: BasicAuth{Username: "test1", Password: "test"}, // basic auth enabled
+ },
+ want: http.StatusForbidden,
+ reqAuth: true,
+ },
+ {
+ name: "no basic auth in request",
+ args: args{
+ h: dummyHandler,
+ b: BasicAuth{Username: "test", Password: "test"}, // basic auth enabled
+ },
+ want: http.StatusForbidden,
+ reqAuth: false,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ baHandler := WrapHandlerInBasicAuth(tt.args.h, tt.args.b)
+
+ req := httptest.NewRequest("GET", "http://localhost/metrics", nil)
+ if tt.reqAuth {
+ req.SetBasicAuth("test", "test")
+ }
+
+ w := httptest.NewRecorder()
+ baHandler(w, req)
+ resp := w.Result()
+
+ if resp.StatusCode != tt.want {
+ t.Errorf("Expected status code %d, got %d", resp.StatusCode, tt.want)
+ }
+ })
+ }
+}
diff --git a/dendrite-config.yaml b/dendrite-config.yaml
index 7436af7a..86a208d7 100644
--- a/dendrite-config.yaml
+++ b/dendrite-config.yaml
@@ -53,6 +53,15 @@ media:
height: 600
method: scale
+# Metrics config for Prometheus
+metrics:
+ # Whether or not metrics are enabled
+ enabled: false
+ # Use basic auth to protect the metrics. Uncomment to the complete block to enable.
+ #basic_auth:
+ # username: prometheusUser
+ # password: y0ursecr3tPa$$w0rd
+
# The config for the TURN server
turn:
# Whether or not guests can request TURN credentials