add(all): protect metrics with basic auth
This commit is contained in:
Generated
+9
@@ -9,6 +9,14 @@
|
||||
pruneopts = "UT"
|
||||
revision = "46ac2b31aa306875aa11f58794b0bb71d4828fa8"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:fa3f65e153117f7d341ace43a6198689237f8e1a21a845b53490323954c8988f"
|
||||
name = "github.com/99designs/basicauth-go"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "2a93ba0f464da2a77748a1dc2e9e5bea8bbdd4b9"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:8029ebc737ffdac4a8df494853595eb086e93b24be9b5e439087f2c8a8f24698"
|
||||
@@ -473,6 +481,7 @@
|
||||
analyzer-version = 1
|
||||
input-imports = [
|
||||
"github.com/766b/chi-prometheus",
|
||||
"github.com/99designs/basicauth-go",
|
||||
"github.com/SentimensRG/ctx",
|
||||
"github.com/SentimensRG/ctx/sigctx",
|
||||
"github.com/crusttech/go-oidc",
|
||||
|
||||
+7
-1
@@ -5,6 +5,7 @@ import (
|
||||
"reflect"
|
||||
"runtime"
|
||||
|
||||
"github.com/99designs/basicauth-go"
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/go-chi/chi/middleware"
|
||||
|
||||
@@ -28,7 +29,12 @@ func mountRoutes(r chi.Router, opts *config.HTTP, mounts ...func(r chi.Router))
|
||||
|
||||
func mountSystemRoutes(r chi.Router, opts *config.HTTP) {
|
||||
if opts.Metrics {
|
||||
r.Handle("/metrics", metrics.Handler())
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(basicauth.New("Metrics", map[string][]string{
|
||||
opts.MetricsUsername: {opts.MetricsPassword},
|
||||
}))
|
||||
r.Handle("/metrics", metrics.Handler())
|
||||
})
|
||||
}
|
||||
r.Mount("/debug", middleware.Profiler())
|
||||
r.Get("/version", version.HttpHandler)
|
||||
|
||||
+7
-1
@@ -5,6 +5,7 @@ import (
|
||||
"reflect"
|
||||
"runtime"
|
||||
|
||||
"github.com/99designs/basicauth-go"
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/go-chi/chi/middleware"
|
||||
|
||||
@@ -28,7 +29,12 @@ func mountRoutes(r chi.Router, opts *config.HTTP, mounts ...func(r chi.Router))
|
||||
|
||||
func mountSystemRoutes(r chi.Router, opts *config.HTTP) {
|
||||
if opts.Metrics {
|
||||
r.Handle("/metrics", metrics.Handler())
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(basicauth.New("Metrics", map[string][]string{
|
||||
opts.MetricsUsername: {opts.MetricsPassword},
|
||||
}))
|
||||
r.Handle("/metrics", metrics.Handler())
|
||||
})
|
||||
}
|
||||
r.Mount("/debug", middleware.Profiler())
|
||||
r.Get("/version", version.HttpHandler)
|
||||
|
||||
@@ -12,6 +12,8 @@ type (
|
||||
Pretty bool
|
||||
Tracing bool
|
||||
Metrics bool
|
||||
|
||||
MetricsUsername, MetricsPassword string
|
||||
}
|
||||
)
|
||||
|
||||
@@ -24,6 +26,9 @@ func (c *HTTP) Validate() error {
|
||||
if c.Addr == "" {
|
||||
return errors.New("No HTTP Addr is set, can't listen for HTTP")
|
||||
}
|
||||
if c.Metrics && (c.MetricsUsername == "" || c.MetricsPassword == "") {
|
||||
return errors.New("We can't have unprotected /metrics, please set METRICS_USERNAME/PASSWORD")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -41,6 +46,9 @@ func (*HTTP) Init(prefix ...string) *HTTP {
|
||||
flag.BoolVar(&http.Logging, p("http-log"), true, "Enable/disable HTTP request log")
|
||||
flag.BoolVar(&http.Pretty, p("http-pretty-json"), false, "Prettify returned JSON output")
|
||||
flag.BoolVar(&http.Tracing, p("http-error-tracing"), false, "Return error stack frame")
|
||||
flag.BoolVar(&http.Metrics, p("http-metrics"), false, "Provide metrics export for prometheus")
|
||||
|
||||
flag.BoolVar(&http.Metrics, "metrics", false, "Provide metrics export for prometheus")
|
||||
flag.StringVar(&http.MetricsUsername, "metrics-username", "metrics", "Provide metrics export for prometheus")
|
||||
flag.StringVar(&http.MetricsPassword, "metrics-password", "", "Provide metrics export for prometheus")
|
||||
return http
|
||||
}
|
||||
|
||||
+7
-1
@@ -5,6 +5,7 @@ import (
|
||||
"reflect"
|
||||
"runtime"
|
||||
|
||||
"github.com/99designs/basicauth-go"
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/go-chi/chi/middleware"
|
||||
|
||||
@@ -30,7 +31,12 @@ func mountRoutes(r chi.Router, opts *config.HTTP, mounts ...func(r chi.Router))
|
||||
|
||||
func mountSystemRoutes(r chi.Router, opts *config.HTTP) {
|
||||
if opts.Metrics {
|
||||
r.Handle("/metrics", metrics.Handler())
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(basicauth.New("Metrics", map[string][]string{
|
||||
opts.MetricsUsername: {opts.MetricsPassword},
|
||||
}))
|
||||
r.Handle("/metrics", metrics.Handler())
|
||||
})
|
||||
}
|
||||
r.Mount("/debug", middleware.Profiler())
|
||||
r.Get("/version", version.HttpHandler)
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.4
|
||||
- 1.5
|
||||
- 1.6
|
||||
|
||||
install:
|
||||
- go get github.com/stretchr/testify/assert
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 99designs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
basicauth-go
|
||||
=================
|
||||
[](https://godoc.org/github.com/99designs/basicauth-go)
|
||||
[](https://travis-ci.org/99designs/basicauth-go)
|
||||
|
||||
|
||||
golang middleware for HTTP basic auth.
|
||||
|
||||
```go
|
||||
// Chi
|
||||
|
||||
router.Use(basicauth.New("MyRealm", map[string][]string{
|
||||
"bob": {"password1", "password2"},
|
||||
}))
|
||||
|
||||
|
||||
// Manual wrapping
|
||||
|
||||
middleware := basicauth.New("MyRealm", map[string][]string{
|
||||
"bob": {"password1", "password2"},
|
||||
})
|
||||
|
||||
h := middlware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request)) {
|
||||
/// do stuff
|
||||
})
|
||||
|
||||
log.Fatal(http.ListenAndServe(":8080", h))
|
||||
```
|
||||
|
||||
### env loading
|
||||
If your environment looks like this:
|
||||
```bash
|
||||
SOME_PREFIX_BOB=password
|
||||
SOME_PREFIX_JANE=password1,password2
|
||||
```
|
||||
|
||||
you can load it like this:
|
||||
```go
|
||||
middleware := basicauth.NewFromEnv("MyRealm", "SOME_PREFIX")
|
||||
```
|
||||
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package basicauth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// New returns a piece of middleware that will allow access only
|
||||
// if the provided credentials match within the given service
|
||||
// otherwise it will return a 401 and not call the next handler.
|
||||
func New(realm string, credentials map[string][]string) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
username, password, ok := r.BasicAuth()
|
||||
if !ok {
|
||||
unauthorized(w, realm)
|
||||
return
|
||||
}
|
||||
|
||||
validPasswords, userFound := credentials[username]
|
||||
if !userFound {
|
||||
unauthorized(w, realm)
|
||||
return
|
||||
}
|
||||
|
||||
for _, validPassword := range validPasswords {
|
||||
if password == validPassword {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
unauthorized(w, realm)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func unauthorized(w http.ResponseWriter, realm string) {
|
||||
w.Header().Add("WWW-Authenticate", fmt.Sprintf(`Basic realm="%s"`, realm))
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package basicauth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// NewFromEnv reads a set of credentials in from environment variables in
|
||||
// the format {PREFIX}{USERNAME|tolower}=password1,password2 and returns
|
||||
// middleware that will validate incoming requests.
|
||||
func NewFromEnv(realm, prefix string) func(http.Handler) http.Handler {
|
||||
credentials := map[string][]string{}
|
||||
|
||||
re := regexp.MustCompile(fmt.Sprintf("^%s(?P<username>.*)$", strings.ToUpper(prefix)))
|
||||
for _, envVar := range os.Environ() {
|
||||
name, value := split2(envVar, "=")
|
||||
|
||||
if res := re.FindStringSubmatch(name); res != nil {
|
||||
username := strings.ToLower(res[1])
|
||||
credentials[username] = strings.Split(value, ",")
|
||||
}
|
||||
}
|
||||
|
||||
return New(realm, credentials)
|
||||
}
|
||||
|
||||
func split2(s, sep string) (string, string) {
|
||||
res := strings.SplitN(s, sep, 2)
|
||||
|
||||
return res[0], res[1]
|
||||
}
|
||||
Reference in New Issue
Block a user