From 58b3d07395a85c6c240a7d3de7999f6c624aa106 Mon Sep 17 00:00:00 2001 From: CodeShell <122738806+CodeShellDev@users.noreply.github.com> Date: Wed, 7 Jan 2026 13:15:38 +0100 Subject: [PATCH 1/3] add hostname middleware for realm restriction --- internals/config/structure/structure.go | 1 + internals/proxy/middlewares/hostname.go | 43 +++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 internals/proxy/middlewares/hostname.go diff --git a/internals/config/structure/structure.go b/internals/config/structure/structure.go index d572d3ec..aeed07c0 100644 --- a/internals/config/structure/structure.go +++ b/internals/config/structure/structure.go @@ -18,6 +18,7 @@ type CONFIG struct { } type SERVICE struct { + HOSTNAMES []string `koanf:"hostnames" env>aliases:".hostnames"` PORT string `koanf:"port" env>aliases:".port"` LOG_LEVEL string `koanf:"loglevel" env>aliases:".loglevel"` } diff --git a/internals/proxy/middlewares/hostname.go b/internals/proxy/middlewares/hostname.go new file mode 100644 index 00000000..63df8b04 --- /dev/null +++ b/internals/proxy/middlewares/hostname.go @@ -0,0 +1,43 @@ +package middlewares + +import ( + "net/http" + "slices" +) + +var Hostname Middleware = Middleware{ + Name: "Hostname", + Use: hostnameHandler, +} + +func hostnameHandler(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + logger := getLogger(req) + + conf := getConfigByReq(req) + + hostnames := conf.SERVICE.HOSTNAMES + + if hostnames == nil { + hostnames = getConfig("").SERVICE.HOSTNAMES + } + + if len(hostnames) > 0 { + hostname := req.URL.Hostname() + + if hostname == "" { + logger.Error("Encountered empty hostname") + http.Error(w, "Bad Request: invalid hostname", http.StatusBadRequest) + return + } + + if !slices.Contains(hostnames, hostname) { + logger.Warn("Client tried using Token with wrong hostname") + onUnauthorized(w) + return + } + } + + next.ServeHTTP(w, req) + }) +} \ No newline at end of file From 3620159eea3d1baf620c874e212b55347e611847 Mon Sep 17 00:00:00 2001 From: CodeShell <122738806+CodeShellDev@users.noreply.github.com> Date: Wed, 7 Jan 2026 13:22:52 +0100 Subject: [PATCH 2/3] add hostname to middlewares --- internals/proxy/proxy.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internals/proxy/proxy.go b/internals/proxy/proxy.go index d532707c..b16335b3 100644 --- a/internals/proxy/proxy.go +++ b/internals/proxy/proxy.go @@ -38,8 +38,9 @@ func (proxy Proxy) Init() http.Handler { Use(m.InternalClientIP). Use(m.RequestLogger). Use(m.InternalAuthRequirement). - Use(m.IPFilter). Use(m.Port). + Use(m.Hostname). + Use(m.IPFilter). Use(m.RateLimit). Use(m.Template). Use(m.Endpoints). From 1f132ca229cdd351f657a8bea03117c120e88dfb Mon Sep 17 00:00:00 2001 From: CodeShell <122738806+CodeShellDev@users.noreply.github.com> Date: Wed, 7 Jan 2026 13:56:42 +0100 Subject: [PATCH 3/3] parse URL into context (also using XF-headers) --- internals/proxy/middlewares/hostname.go | 5 ++++- internals/proxy/middlewares/proxy.go | 30 ++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/internals/proxy/middlewares/hostname.go b/internals/proxy/middlewares/hostname.go index 63df8b04..1701b1c9 100644 --- a/internals/proxy/middlewares/hostname.go +++ b/internals/proxy/middlewares/hostname.go @@ -2,6 +2,7 @@ package middlewares import ( "net/http" + "net/url" "slices" ) @@ -23,7 +24,9 @@ func hostnameHandler(next http.Handler) http.Handler { } if len(hostnames) > 0 { - hostname := req.URL.Hostname() + URL := getContext[*url.URL](req, originURLKey) + + hostname := URL.Hostname() if hostname == "" { logger.Error("Encountered empty hostname") diff --git a/internals/proxy/middlewares/proxy.go b/internals/proxy/middlewares/proxy.go index 6762bfe2..599d403c 100644 --- a/internals/proxy/middlewares/proxy.go +++ b/internals/proxy/middlewares/proxy.go @@ -4,6 +4,7 @@ import ( "errors" "net" "net/http" + "net/url" "strings" ) @@ -14,6 +15,7 @@ var InternalProxy Middleware = Middleware{ const trustedProxyKey contextKey = "isProxyTrusted" const clientIPKey contextKey = "clientIP" +const originURLKey contextKey = "originURL" func proxyHandler(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { @@ -32,6 +34,8 @@ func proxyHandler(next http.Handler) http.Handler { host, _, _ := net.SplitHostPort(req.RemoteAddr) + originUrl := req.Proto + "://" + req.URL.Host + ip = net.ParseIP(host) if len(rawTrustedProxies) != 0 { @@ -50,10 +54,30 @@ func proxyHandler(next http.Handler) http.Handler { if realIP != nil { ip = realIP } + + XFHost := req.Header.Get("X-Forwarded-Host") + XFProto := req.Header.Get("X-Forwarded-Proto") + XFPort := req.Header.Get("X-Forwarded-Port") + + if XFHost == "" || XFProto == "" || XFPort == "" { + logger.Warn("Missing X-Forwarded-* headers") + } + + originUrl = XFProto + "://" + XFHost + ":" + XFPort + } + + originURL, err := url.Parse(originUrl) + + if err != nil { + logger.Error("Could not parse Url: ", originUrl) + http.Error(w, "Bad Request: invalid Url", http.StatusBadRequest) + return } - req = setContext(req, clientIPKey, ip) req = setContext(req, trustedProxyKey, trusted) + req = setContext(req, originURLKey, originURL) + + req = setContext(req, clientIPKey, ip) next.ServeHTTP(w, req) }) @@ -123,13 +147,13 @@ func getRealIP(req *http.Request) (net.IP, error) { realIP := net.ParseIP(strings.TrimSpace(ips[0])) if realIP == nil { - return nil, errors.New("malformed x-forwarded-for header") + return nil, errors.New("malformed X-Forwarded-For header") } return realIP, nil } - return nil, errors.New("no x-forwarded-for header present") + return nil, errors.New("no X-Forwarded-For header present") } func isIPInList(ip net.IP, list []*net.IPNet) bool {