From 826b2124d14f63e57ecc7a24758ebab54c9bef32 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Thu, 4 Jul 2024 11:44:45 +0200 Subject: [PATCH 1/2] feat: add bypass key --- pkg/httphandler/root.go | 7 +++++++ pkg/options/options.go | 1 + 2 files changed, 8 insertions(+) diff --git a/pkg/httphandler/root.go b/pkg/httphandler/root.go index 7e153c3..8d1c5da 100644 --- a/pkg/httphandler/root.go +++ b/pkg/httphandler/root.go @@ -21,6 +21,13 @@ func (root *HttpHandler) rootHandler(w http.ResponseWriter, r *http.Request, for "Path": forwardedURI.Path, }) + bypassKey := r.Header.Get("X-Bypass-Key") + if bypassKey != "" && bypassKey == root.options.BypassKey { + w.Header().Set("X-Forwarded-User", "bypass@example.com") + w.WriteHeader(200) + return + } + claims, err := root.forwardAuth.IsAuthenticated(r.Context(), logger, w, r, root.options) if err != nil { logger = logger.WithField("FunctionSource", "RootHandler") diff --git a/pkg/options/options.go b/pkg/options/options.go index 0dd15d8..04963de 100644 --- a/pkg/options/options.go +++ b/pkg/options/options.go @@ -19,6 +19,7 @@ type Options struct { Port int `env:"PORT" envDefault:"4181"` RedirectURL string `env:"REDIRECT_URL" envDefault:"/auth/resp"` Scopes string `env:"SCOPES"` + BypassKey string `env:"BYPASS_KEY"` } // LoadOptions parses the environment vars and the options From 523baf9c8a52c60094ebb75dcba28dc684c1ba2d Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Fri, 10 Jan 2025 23:55:25 +0100 Subject: [PATCH 2/2] feat: use basic auth for bypass --- Dockerfile | 2 +- go.mod | 13 ++++++++----- go.sum | 32 ++++++++++++++++++-------------- pkg/httphandler/root.go | 15 ++++++++++----- pkg/options/options.go | 21 ++++++++++++++++++++- 5 files changed, 57 insertions(+), 26 deletions(-) diff --git a/Dockerfile b/Dockerfile index 1c1dc20..53f2648 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Builder -FROM golang:alpine as builder +FROM golang:alpine AS builder WORKDIR /app # Install git + SSL ca certificates. diff --git a/go.mod b/go.mod index f3d11d8..2f7301b 100644 --- a/go.mod +++ b/go.mod @@ -6,14 +6,17 @@ toolchain go1.22.2 require ( github.com/caarlos0/env v3.5.0+incompatible - github.com/coreos/go-oidc/v3 v3.10.0 + github.com/coreos/go-oidc/v3 v3.12.0 github.com/google/uuid v1.6.0 github.com/sirupsen/logrus v1.9.3 - golang.org/x/oauth2 v0.19.0 + golang.org/x/oauth2 v0.25.0 ) +require github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962 // indirect + require ( - github.com/go-jose/go-jose/v4 v4.0.1 // indirect - golang.org/x/crypto v0.22.0 // indirect - golang.org/x/sys v0.19.0 // indirect + github.com/go-jose/go-jose/v4 v4.0.4 // indirect + github.com/tg123/go-htpasswd v1.2.3 + golang.org/x/crypto v0.32.0 // indirect + golang.org/x/sys v0.29.0 // indirect ) diff --git a/go.sum b/go.sum index 56f6b86..98a24d3 100644 --- a/go.sum +++ b/go.sum @@ -1,14 +1,16 @@ +github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962 h1:KeNholpO2xKjgaaSyd+DyQRrsQjhbSeS7qe4nEw8aQw= +github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962/go.mod h1:kC29dT1vFpj7py2OvG1khBdQpo3kInWP+6QipLbdngo= github.com/caarlos0/env v3.5.0+incompatible h1:Yy0UN8o9Wtr/jGHZDpCBLpNrzcFLLM2yixi/rBrKyJs= github.com/caarlos0/env v3.5.0+incompatible/go.mod h1:tdCsowwCzMLdkqRYDlHpZCp2UooDD3MspDBjZ2AD02Y= -github.com/coreos/go-oidc/v3 v3.10.0 h1:tDnXHnLyiTVyT/2zLDGj09pFPkhND8Gl8lnTRhoEaJU= -github.com/coreos/go-oidc/v3 v3.10.0/go.mod h1:5j11xcw0D3+SGxn6Z/WFADsgcWVMyNAlSQupk0KK3ac= +github.com/coreos/go-oidc/v3 v3.12.0 h1:sJk+8G2qq94rDI6ehZ71Bol3oUHy63qNYmkiSjrc/Jo= +github.com/coreos/go-oidc/v3 v3.12.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U= -github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E= +github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -17,15 +19,17 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= -golang.org/x/oauth2 v0.19.0 h1:9+E/EZBCbTLNrbN35fHv/a/d/mOBatymz1zbtQrXpIg= -golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tg123/go-htpasswd v1.2.3 h1:ALR6ZBIc2m9u70m+eAWUFt5p43ISbIvAvRFYzZPTOY8= +github.com/tg123/go-htpasswd v1.2.3/go.mod h1:FcIrK0J+6zptgVwK1JDlqyajW/1B4PtuJ/FLWl7nx8A= +golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= +golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/pkg/httphandler/root.go b/pkg/httphandler/root.go index 8d1c5da..f3d4767 100644 --- a/pkg/httphandler/root.go +++ b/pkg/httphandler/root.go @@ -21,11 +21,16 @@ func (root *HttpHandler) rootHandler(w http.ResponseWriter, r *http.Request, for "Path": forwardedURI.Path, }) - bypassKey := r.Header.Get("X-Bypass-Key") - if bypassKey != "" && bypassKey == root.options.BypassKey { - w.Header().Set("X-Forwarded-User", "bypass@example.com") - w.WriteHeader(200) - return + user, pass, usesBasicAuth := r.BasicAuth() + if usesBasicAuth && root.options.BypassPwd != nil { + if root.options.BypassPwd.Match(user, pass) { + logger.Infof("Basic auth successful: %s", user) + w.Header().Set("X-Forwarded-User", "bypass@example.com") + w.WriteHeader(200) + return + } else { + logger.Errorf("Basic auth failed: %s", user) + } } claims, err := root.forwardAuth.IsAuthenticated(r.Context(), logger, w, r, root.options) diff --git a/pkg/options/options.go b/pkg/options/options.go index 04963de..3a1477d 100644 --- a/pkg/options/options.go +++ b/pkg/options/options.go @@ -6,8 +6,10 @@ package options import ( "fmt" + "strings" "github.com/caarlos0/env" + "github.com/tg123/go-htpasswd" ) type Options struct { @@ -19,7 +21,9 @@ type Options struct { Port int `env:"PORT" envDefault:"4181"` RedirectURL string `env:"REDIRECT_URL" envDefault:"/auth/resp"` Scopes string `env:"SCOPES"` - BypassKey string `env:"BYPASS_KEY"` + BypassUser string `env:"BYPASS_USER"` + BypassFile string `env:"BYPASS_FILE"` + BypassPwd *htpasswd.File } // LoadOptions parses the environment vars and the options @@ -29,5 +33,20 @@ func LoadOptions() (*Options, error) { return nil, fmt.Errorf("failed to parse options: %s", err) } + if options.BypassFile != "" { + parsed, err := htpasswd.New(options.BypassFile, htpasswd.DefaultSystems, func(err error) {}) + if err != nil { + return nil, err + } + options.BypassPwd = parsed + } else if options.BypassUser != "" { + prep := strings.ReplaceAll(options.BypassUser, ";", "\n") + parsed, err := htpasswd.NewFromReader(strings.NewReader(prep), htpasswd.DefaultSystems, func(err error) {}) + if err != nil { + return nil, err + } + options.BypassPwd = parsed + } + return &options, nil }