From 06c9c1e64a14d31d971339217d0940199b46d81f Mon Sep 17 00:00:00 2001 From: crazygolem Date: Sun, 29 Sep 2024 19:28:44 +0200 Subject: [PATCH] feat(server): require explicitly enabling reverse proxy auth for unix sockets (#3062) --- server/auth.go | 16 +++++++++------- server/auth_test.go | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/server/auth.go b/server/auth.go index 784c17645..dabc452f7 100644 --- a/server/auth.go +++ b/server/auth.go @@ -10,6 +10,7 @@ import ( "fmt" "net" "net/http" + "slices" "strings" "time" @@ -196,7 +197,7 @@ func UsernameFromToken(r *http.Request) string { } func UsernameFromReverseProxyHeader(r *http.Request) string { - if conf.Server.ReverseProxyWhitelist == "" && !strings.HasPrefix(conf.Server.Address, "unix:") { + if conf.Server.ReverseProxyWhitelist == "" { return "" } reverseProxyIp, ok := request.ReverseProxyIpFrom(r.Context()) @@ -324,14 +325,16 @@ func handleLoginFromHeaders(ds model.DataStore, r *http.Request) map[string]inte } func validateIPAgainstList(ip string, comaSeparatedList string) bool { + if comaSeparatedList == "" || ip == "" { + return false + } + + cidrs := strings.Split(comaSeparatedList, ",") + // Per https://github.com/golang/go/issues/49825, the remote address // on a unix socket is '@' if ip == "@" && strings.HasPrefix(conf.Server.Address, "unix:") { - return true - } - - if comaSeparatedList == "" || ip == "" { - return false + return slices.Contains(cidrs, "@") } if net.ParseIP(ip) == nil { @@ -342,7 +345,6 @@ func validateIPAgainstList(ip string, comaSeparatedList string) bool { return false } - cidrs := strings.Split(comaSeparatedList, ",") testedIP, _, err := net.ParseCIDR(fmt.Sprintf("%s/32", ip)) if err != nil { diff --git a/server/auth_test.go b/server/auth_test.go index 6b2686541..864fb7436 100644 --- a/server/auth_test.go +++ b/server/auth_test.go @@ -154,6 +154,40 @@ var _ = Describe("Auth", func() { // Request Header authentication should not generate a JWT token Expect(parsed).ToNot(HaveKey("token")) }) + + It("does not set auth data when listening on unix socket without whitelist", func() { + conf.Server.Address = "unix:/tmp/navidrome-test" + conf.Server.ReverseProxyWhitelist = "" + + // No ReverseProxyIp in request context + serveIndex(ds, fs, nil)(resp, req) + + config := extractAppConfig(resp.Body.String()) + Expect(config["auth"]).To(BeNil()) + }) + + It("does not set auth data when listening on unix socket with incorrect whitelist", func() { + conf.Server.Address = "unix:/tmp/navidrome-test" + + req = req.WithContext(request.WithReverseProxyIp(req.Context(), "@")) + serveIndex(ds, fs, nil)(resp, req) + + config := extractAppConfig(resp.Body.String()) + Expect(config["auth"]).To(BeNil()) + }) + + It("sets auth data when listening on unix socket with correct whitelist", func() { + conf.Server.Address = "unix:/tmp/navidrome-test" + conf.Server.ReverseProxyWhitelist = conf.Server.ReverseProxyWhitelist + ",@" + + req = req.WithContext(request.WithReverseProxyIp(req.Context(), "@")) + serveIndex(ds, fs, nil)(resp, req) + + config := extractAppConfig(resp.Body.String()) + parsed := config["auth"].(map[string]interface{}) + + Expect(parsed["id"]).To(Equal("111")) + }) }) Describe("login", func() {