From 44bd8c758f6db096dccd901378f418a14dac5a03 Mon Sep 17 00:00:00 2001 From: Mygod Date: Wed, 26 Apr 2023 16:35:55 -0400 Subject: [PATCH] Fix multiauth --- caddyfile.go | 14 ++++++++------ common_test.go | 30 +++++++++++++++++------------- forwardproxy.go | 22 +++++++--------------- 3 files changed, 32 insertions(+), 34 deletions(-) diff --git a/caddyfile.go b/caddyfile.go index 4124d11..057df5a 100644 --- a/caddyfile.go +++ b/caddyfile.go @@ -1,6 +1,7 @@ package forwardproxy import ( + "encoding/base64" "log" "strconv" "strings" @@ -45,13 +46,14 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { if strings.Contains(args[0], ":") { return d.Err("character ':' in usernames is not allowed") } - // TODO: Support multiple basicauths. - // TODO: Actually, just try to use Caddy 2's existing basicauth module. - if h.BasicauthUser != "" || h.BasicauthPass != "" { - return d.Err("Multi-user basicauth is not supported") + if h.AuthCredentials == nil { + h.AuthCredentials = [][]byte{} } - h.BasicauthUser = args[0] - h.BasicauthPass = args[1] + // base64-encode credentials + buf := make([]byte, base64.StdEncoding.EncodedLen(len(args[0])+1+len(args[1]))) + base64.StdEncoding.Encode(buf, []byte(args[0]+":"+args[1])) + h.AuthCredentials = append(h.AuthCredentials, buf) + h.AuthRequired = true case "hosts": if len(args) == 0 { return d.ArgErr() diff --git a/common_test.go b/common_test.go index b8bad5f..c9da928 100644 --- a/common_test.go +++ b/common_test.go @@ -3,6 +3,7 @@ package forwardproxy import ( "context" "crypto/tls" + "encoding/base64" "encoding/hex" "encoding/json" "fmt" @@ -190,15 +191,18 @@ func TestMain(m *testing.M) { }, } + buf := make([]byte, base64.StdEncoding.EncodedLen(9)) + base64.StdEncoding.Encode(buf, []byte("test:pass")) + caddyForwardProxyAuth = caddyTestServer{ addr: "127.0.0.1:4891", root: "./test/forwardproxy", tls: true, proxyHandler: &Handler{ - PACPath: defaultPACPath, - ACL: []ACLRule{{Subjects: []string{"all"}, Allow: true}}, - BasicauthUser: "test", - BasicauthPass: "pass", + PACPath: defaultPACPath, + ACL: []ACLRule{{Subjects: []string{"all"}, Allow: true}}, + AuthCredentials: [][]byte{buf}, + AuthRequired: true, }, } @@ -206,10 +210,10 @@ func TestMain(m *testing.M) { addr: "127.0.69.73:6973", root: "./test/forwardproxy", proxyHandler: &Handler{ - PACPath: defaultPACPath, - ACL: []ACLRule{{Subjects: []string{"all"}, Allow: true}}, - BasicauthUser: "test", - BasicauthPass: "pass", + PACPath: defaultPACPath, + ACL: []ACLRule{{Subjects: []string{"all"}, Allow: true}}, + AuthCredentials: [][]byte{buf}, + AuthRequired: true, }, } @@ -221,8 +225,8 @@ func TestMain(m *testing.M) { PACPath: "/superhiddenfile.pac", ACL: []ACLRule{{Subjects: []string{"all"}, Allow: true}}, ProbeResistance: &ProbeResistance{Domain: "test.localhost"}, - BasicauthUser: "test", - BasicauthPass: "pass", + AuthCredentials: [][]byte{buf}, + AuthRequired: true, }, httpRedirPort: "8880", } @@ -249,9 +253,9 @@ func TestMain(m *testing.M) { root: "./test/upstreamingproxy", tls: true, proxyHandler: &Handler{ - Upstream: "https://test:pass@127.0.0.1:4891", - BasicauthUser: "upstreamtest", - BasicauthPass: "upstreampass", + Upstream: "https://test:pass@127.0.0.1:4891", + AuthCredentials: [][]byte{buf}, + AuthRequired: true, }, } diff --git a/forwardproxy.go b/forwardproxy.go index 45ec450..4309cb8 100644 --- a/forwardproxy.go +++ b/forwardproxy.go @@ -91,10 +91,10 @@ type Handler struct { aclRules []aclRule // TODO: temporary/deprecated - we should try to reuse existing authentication modules instead! - BasicauthUser string `json:"auth_user_deprecated,omitempty"` - BasicauthPass string `json:"auth_pass_deprecated,omitempty"` - authRequired bool - authCredentials [][]byte // slice with base64-encoded credentials + BasicauthUser string `json:"auth_user_deprecated,omitempty"` + BasicauthPass string `json:"auth_pass_deprecated,omitempty"` + AuthRequired bool `json:"auth_required,omitempty"` + AuthCredentials [][]byte `json:"auth_credentials,omitempty"` // slice with base64-encoded credentials } // CaddyModule returns the Caddy module information. @@ -120,14 +120,6 @@ func (h *Handler) Provision(ctx caddy.Context) error { TLSHandshakeTimeout: 10 * time.Second, } - // TODO: temporary, in an effort to get the tests to pass - if h.BasicauthUser != "" && h.BasicauthPass != "" { - basicAuthBuf := make([]byte, base64.StdEncoding.EncodedLen(len(h.BasicauthUser)+1+len(h.BasicauthPass))) - base64.StdEncoding.Encode(basicAuthBuf, []byte(h.BasicauthUser+":"+h.BasicauthPass)) - h.authRequired = true - h.authCredentials = [][]byte{basicAuthBuf} - } - // access control lists for _, rule := range h.ACL { for _, subj := range rule.Subjects { @@ -155,7 +147,7 @@ func (h *Handler) Provision(ctx caddy.Context) error { h.aclRules = append(h.aclRules, &aclAllRule{allow: true}) if h.ProbeResistance != nil { - if !h.authRequired { + if !h.AuthRequired { return fmt.Errorf("probe resistance requires authentication") } if len(h.ProbeResistance.Domain) > 0 { @@ -237,7 +229,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyht } var authErr error - if h.authRequired { + if h.AuthRequired { authErr = h.checkCredentials(r) } if h.ProbeResistance != nil && len(h.ProbeResistance.Domain) > 0 && reqHost == h.ProbeResistance.Domain { @@ -418,7 +410,7 @@ func (h Handler) checkCredentials(r *http.Request) error { if strings.ToLower(pa[0]) != "basic" { return errors.New("Auth type is not supported") } - for _, creds := range h.authCredentials { + for _, creds := range h.AuthCredentials { if subtle.ConstantTimeCompare(creds, []byte(pa[1])) == 1 { // Please do not consider this to be timing-attack-safe code. Simple equality is almost // mindlessly substituted with constant time algo and there ARE known issues with this code,