From 14c7d8c4f4e72ce304aa3cdba515a06c03ef8a4c Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Tue, 18 Jan 2022 23:19:43 +0100 Subject: [PATCH] internal: route traffic to proxy providers based on cookie domain when multiple domain-level providers exist Signed-off-by: Jens Langhammer #2079 --- .../proxyv2/application/application.go | 4 ++ internal/outpost/proxyv2/handlers.go | 42 +++++++++++++++++-- internal/outpost/proxyv2/proxyv2.go | 9 ++-- internal/web/proxy.go | 5 +-- 4 files changed, 50 insertions(+), 10 deletions(-) diff --git a/internal/outpost/proxyv2/application/application.go b/internal/outpost/proxyv2/application/application.go index c1d18c700c..57a4a37d31 100644 --- a/internal/outpost/proxyv2/application/application.go +++ b/internal/outpost/proxyv2/application/application.go @@ -187,6 +187,10 @@ func (a *Application) Mode() api.ProxyMode { return *a.proxyConfig.Mode } +func (a *Application) ProxyConfig() api.ProxyOutpostConfig { + return a.proxyConfig +} + func (a *Application) ServeHTTP(rw http.ResponseWriter, r *http.Request) { a.mux.ServeHTTP(rw, r) } diff --git a/internal/outpost/proxyv2/handlers.go b/internal/outpost/proxyv2/handlers.go index 103d6cf6ec..83568fb357 100644 --- a/internal/outpost/proxyv2/handlers.go +++ b/internal/outpost/proxyv2/handlers.go @@ -8,6 +8,7 @@ import ( "time" "github.com/prometheus/client_golang/prometheus" + "goauthentik.io/internal/outpost/proxyv2/application" "goauthentik.io/internal/outpost/proxyv2/metrics" "goauthentik.io/internal/utils/web" staticWeb "goauthentik.io/web" @@ -43,6 +44,42 @@ func (ps *ProxyServer) HandleStatic(rw http.ResponseWriter, r *http.Request) { }).Observe(float64(after)) } +func (ps *ProxyServer) lookupApp(r *http.Request) (*application.Application, string) { + host := web.GetHost(r) + // Try to find application by directly looking up host first (proxy, forward_auth_single) + a, ok := ps.apps[host] + if ok { + ps.log.WithField("host", host).WithField("app", a).Debug("Found app based direct host match") + return a, host + } + // For forward_auth_domain, we don't have a direct app to domain relationship + // Check through all apps, and check how much of their cookie domain matches the host + // Return the application that has the longest match + var longestMatch *application.Application + longestMatchLength := 0 + for _, app := range ps.apps { + // Check if the cookie domain has a leading period for a wildcard + // This will decrease the weight of a wildcard domain, but a request to example.com + // with the cookie domain set to example.com will still be routed correctly. + cd := strings.TrimPrefix(*app.ProxyConfig().CookieDomain, ".") + if !strings.HasSuffix(host, cd) { + continue + } + if len(cd) < longestMatchLength { + continue + } + longestMatch = app + longestMatchLength = len(cd) + } + // Check if our longes match is 0, in which case we didn't match, so we + // manually return no app + if longestMatchLength == 0 { + return nil, host + } + ps.log.WithField("host", host).WithField("app", longestMatch).Debug("Found app based on cookie domain") + return longestMatch, host +} + func (ps *ProxyServer) Handle(rw http.ResponseWriter, r *http.Request) { if strings.HasPrefix(r.URL.Path, "/akprox/static") { ps.HandleStatic(rw, r) @@ -52,9 +89,8 @@ func (ps *ProxyServer) Handle(rw http.ResponseWriter, r *http.Request) { ps.HandlePing(rw, r) return } - host := web.GetHost(r) - a, ok := ps.apps[host] - if !ok { + a, host := ps.lookupApp(r) + if a == nil { // If we only have one handler, host name switching doesn't matter if len(ps.apps) == 1 { ps.log.WithField("host", host).Trace("passing to single app mux") diff --git a/internal/outpost/proxyv2/proxyv2.go b/internal/outpost/proxyv2/proxyv2.go index a91e464cc2..c890077cea 100644 --- a/internal/outpost/proxyv2/proxyv2.go +++ b/internal/outpost/proxyv2/proxyv2.go @@ -70,11 +70,12 @@ func NewProxyServer(ac *ak.APIController, portOffset int) *ProxyServer { return s } -func (ps *ProxyServer) HandleHost(host string, rw http.ResponseWriter, r *http.Request) bool { - if app, ok := ps.apps[host]; ok { - if app.Mode() == api.PROXYMODE_PROXY { +func (ps *ProxyServer) HandleHost(rw http.ResponseWriter, r *http.Request) bool { + a, host := ps.lookupApp(r) + if a != nil { + if a.Mode() == api.PROXYMODE_PROXY { ps.log.WithField("host", host).Trace("routing to proxy outpost") - app.ServeHTTP(rw, r) + a.ServeHTTP(rw, r) return true } } diff --git a/internal/web/proxy.go b/internal/web/proxy.go index 41f3c5173a..36234724c8 100644 --- a/internal/web/proxy.go +++ b/internal/web/proxy.go @@ -47,10 +47,9 @@ func (ws *WebServer) configureProxy() { ws.proxyErrorHandler(rw, r, fmt.Errorf("authentik core not running yet")) return } - host := web.GetHost(r) before := time.Now() if ws.ProxyServer != nil { - if ws.ProxyServer.HandleHost(host, rw, r) { + if ws.ProxyServer.HandleHost(rw, r) { Requests.With(prometheus.Labels{ "dest": "embedded_outpost", }).Observe(float64(time.Since(before))) @@ -60,7 +59,7 @@ func (ws *WebServer) configureProxy() { Requests.With(prometheus.Labels{ "dest": "py", }).Observe(float64(time.Since(before))) - ws.log.WithField("host", host).Trace("routing to application server") + ws.log.WithField("host", web.GetHost(r)).Trace("routing to application server") rp.ServeHTTP(rw, r) }) }