From 45227059da6b85623a852a804151e0439f2366f6 Mon Sep 17 00:00:00 2001 From: Daniel Hrabovcak Date: Thu, 14 Sep 2023 09:48:55 -0400 Subject: [PATCH] Add Authorization header support into the example app --- .../instrumentation/go-synthetic/README.md | 7 ++ examples/instrumentation/go-synthetic/auth.go | 79 ++++++++++++++++++- examples/instrumentation/go-synthetic/main.go | 9 ++- 3 files changed, 89 insertions(+), 6 deletions(-) diff --git a/examples/instrumentation/go-synthetic/README.md b/examples/instrumentation/go-synthetic/README.md index edc7bb086f..d93fac02a1 100644 --- a/examples/instrumentation/go-synthetic/README.md +++ b/examples/instrumentation/go-synthetic/README.md @@ -34,6 +34,13 @@ go run ./examples/instrumentation/go-synthetic/ --basic-auth-username=admin --ba curl localhost:8080/metrics -u "admin:pw" ``` +#### Authorization + +```bash +go run ./examples/instrumentation/go-synthetic/ --auth-scheme=Bearer --auth-parameters=xyz +curl -H "Authorization: Bearer xyz" localhost:8080/metrics +``` + ## Running on Kubernetes If running managed-collection on a Kubernetes cluster, the `go-synthetic` can be diff --git a/examples/instrumentation/go-synthetic/auth.go b/examples/instrumentation/go-synthetic/auth.go index 97c8c7381a..578715db7f 100644 --- a/examples/instrumentation/go-synthetic/auth.go +++ b/examples/instrumentation/go-synthetic/auth.go @@ -1,6 +1,7 @@ package main import ( + "errors" "flag" "net/http" ) @@ -22,10 +23,6 @@ func (c *basicAuthConfig) isEnabled() bool { } func (c *basicAuthConfig) handle(handler http.Handler) http.Handler { - if !c.isEnabled() { - return handler - } - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { username, password, ok := r.BasicAuth() if ok && username == c.username && password == c.password { @@ -37,3 +34,77 @@ func (c *basicAuthConfig) handle(handler http.Handler) http.Handler { http.Error(w, "Unauthorized", http.StatusUnauthorized) }) } + +type authorizationConfig struct { + scheme string + parameters string +} + +func newAuthorizationConfigFromFlags() *authorizationConfig { + c := &authorizationConfig{} + flag.StringVar(&c.scheme, "auth-scheme", "", "Authorization header scheme") + flag.StringVar(&c.parameters, "auth-parameters", "", "Data to require in the Authorization header") + return c +} + +func (c *authorizationConfig) isEnabled() bool { + return c.scheme != "" || c.parameters != "" +} + +func (c *authorizationConfig) validate() error { + var errs []error + if c.scheme == "" && c.parameters != "" { + errs = append(errs, errors.New("must specify --auth-scheme when using --auth-parameters")) + } + if c.scheme == "Basic" { + errs = append(errs, errors.New("use --basic-auth flags to specify BasicAuth")) + } + return errors.Join(errs...) +} + +func (c *authorizationConfig) handle(handler http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + auth := r.Header.Get("Authorization") + expected := c.scheme + " " + c.parameters + if auth == expected { + handler.ServeHTTP(w, r) + return + } + + w.Header().Set("WWW-Authenticate", c.scheme+` realm="restricted", charset="UTF-8"`) + http.Error(w, "Unauthorized", http.StatusUnauthorized) + }) +} + +type httpClientConfig struct { + basicAuth *basicAuthConfig + auth *authorizationConfig +} + +func newHttpClientConfigFromFlags() *httpClientConfig { + return &httpClientConfig{ + basicAuth: newBasicAuthConfigFromFlags(), + auth: newAuthorizationConfigFromFlags(), + } +} + +func (c *httpClientConfig) validate() error { + var errs []error + if c.basicAuth.isEnabled() && c.auth.isEnabled() { + errs = append(errs, errors.New("cannot specify both --basic-auth and --auth flags")) + } + if err := c.auth.validate(); err != nil { + errs = append(errs, err) + } + return errors.Join(errs...) +} + +func (c *httpClientConfig) handle(handler http.Handler) http.Handler { + if c.auth.isEnabled() { + return c.auth.handle(handler) + } + if c.basicAuth.isEnabled() { + return c.basicAuth.handle(handler) + } + return handler +} diff --git a/examples/instrumentation/go-synthetic/main.go b/examples/instrumentation/go-synthetic/main.go index b8434604bd..be8d36fac2 100644 --- a/examples/instrumentation/go-synthetic/main.go +++ b/examples/instrumentation/go-synthetic/main.go @@ -172,9 +172,14 @@ var ( ) func main() { - basicAuthConfig := newBasicAuthConfigFromFlags() + httpClientConfig := newHttpClientConfigFromFlags() flag.Parse() + if err := httpClientConfig.validate(); err != nil { + log.Println("Invalid HTTP client config flags:", err) + os.Exit(1) + } + metrics := prometheus.NewRegistry() metrics.MustRegister( collectors.NewGoCollector(collectors.WithGoCollectorRuntimeMetrics(collectors.MetricsAll)), @@ -219,7 +224,7 @@ func main() { } { mux := http.NewServeMux() - mux.Handle("/metrics", basicAuthConfig.handle(promhttp.HandlerFor(metrics, promhttp.HandlerOpts{ + mux.Handle("/metrics", httpClientConfig.handle(promhttp.HandlerFor(metrics, promhttp.HandlerOpts{ Registry: metrics, EnableOpenMetrics: true, })))