From 393d7ec486dd1fe01da3b1ecf45bd6748c8fe969 Mon Sep 17 00:00:00 2001 From: Jens L Date: Sat, 30 Jul 2022 17:51:01 +0200 Subject: [PATCH] providers/proxy: no exposed urls (#3151) * test any callback Signed-off-by: Jens Langhammer * cleanup Signed-off-by: Jens Langhammer * dont detect callback in per-server handler Signed-off-by: Jens Langhammer * use full redirect uri with both path and query param Signed-off-by: Jens Langhammer * update tests Signed-off-by: Jens Langhammer * correctly route to embedded outpost for callback signature Signed-off-by: Jens Langhammer * fix allowed redirects Signed-off-by: Jens Langhammer --- authentik/providers/proxy/models.py | 9 +- .../proxyv2/application/application.go | 34 +++++--- .../proxyv2/application/mode_common.go | 9 +- .../proxyv2/application/mode_forward.go | 87 +++---------------- .../application/mode_forward_caddy_test.go | 24 +++-- .../application/mode_forward_envoy_test.go | 25 ++++-- .../application/mode_forward_nginx_test.go | 4 +- .../application/mode_forward_traefik_test.go | 24 +++-- internal/outpost/proxyv2/application/oauth.go | 7 +- internal/outpost/proxyv2/application/test.go | 5 ++ internal/web/proxy.go | 4 +- internal/web/web.go | 6 +- 12 files changed, 117 insertions(+), 121 deletions(-) diff --git a/authentik/providers/proxy/models.py b/authentik/providers/proxy/models.py index 8d1aa8b45..38fe5c171 100644 --- a/authentik/providers/proxy/models.py +++ b/authentik/providers/proxy/models.py @@ -14,6 +14,7 @@ from authentik.outposts.models import OutpostModel from authentik.providers.oauth2.models import ClientTypes, OAuth2Provider, ScopeMapping SCOPE_AK_PROXY = "ak_proxy" +OUTPOST_CALLBACK_SIGNATURE = "X-authentik-auth-callback" def get_cookie_secret(): @@ -22,7 +23,13 @@ def get_cookie_secret(): def _get_callback_url(uri: str) -> str: - return urljoin(uri, "outpost.goauthentik.io/callback") + return "\n".join( + [ + urljoin(uri, "outpost.goauthentik.io/callback") + + f"\\?{OUTPOST_CALLBACK_SIGNATURE}=true", + uri + f"\\?{OUTPOST_CALLBACK_SIGNATURE}=true", + ] + ) class ProxyMode(models.TextChoices): diff --git a/internal/outpost/proxyv2/application/application.go b/internal/outpost/proxyv2/application/application.go index 6b0052d6b..f3af62616 100644 --- a/internal/outpost/proxyv2/application/application.go +++ b/internal/outpost/proxyv2/application/application.go @@ -8,6 +8,7 @@ import ( "html/template" "net/http" "net/url" + "path" "regexp" "strings" "time" @@ -34,7 +35,7 @@ type Application struct { Cert *tls.Certificate UnauthenticatedRegex []*regexp.Regexp - endpint OIDCEndpoint + endpoint OIDCEndpoint oauthConfig oauth2.Config tokenVerifier *oidc.IDTokenVerifier outpostName string @@ -72,12 +73,18 @@ func NewApplication(p api.ProxyOutpostConfig, c *http.Client, cs *ak.CryptoStore SupportedSigningAlgs: []string{"RS256", "HS256"}, }) + redirectUri, _ := url.Parse(p.ExternalHost) + redirectUri.Path = path.Join(redirectUri.Path, "/outpost.goauthentik.io/callback") + redirectUri.RawQuery = url.Values{ + CallbackSignature: []string{"true"}, + }.Encode() + // Configure an OpenID Connect aware OAuth2 client. endpoint := GetOIDCEndpoint(p, ak.Outpost.Config["authentik_host"].(string)) oauth2Config := oauth2.Config{ ClientID: *p.ClientId, ClientSecret: *p.ClientSecret, - RedirectURL: urlJoin(p.ExternalHost, "/outpost.goauthentik.io/callback"), + RedirectURL: redirectUri.String(), Endpoint: endpoint.Endpoint, Scopes: p.ScopesToRequest, } @@ -86,7 +93,7 @@ func NewApplication(p api.ProxyOutpostConfig, c *http.Client, cs *ak.CryptoStore Host: externalHost.Host, log: muxLogger, outpostName: ak.Outpost.Name, - endpint: endpoint, + endpoint: endpoint, oauthConfig: oauth2Config, tokenVerifier: verifier, proxyConfig: p, @@ -139,11 +146,18 @@ func NewApplication(p api.ProxyOutpostConfig, c *http.Client, cs *ak.CryptoStore }) }) mux.Use(sentryhttp.New(sentryhttp.Options{}).Handle) + mux.Use(func(inner http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if _, set := r.URL.Query()[CallbackSignature]; set { + a.handleAuthCallback(w, r) + } else { + inner.ServeHTTP(w, r) + } + }) + }) - // Support /start and /sign_in for backwards compatibility - mux.HandleFunc("/outpost.goauthentik.io/start", a.handleRedirect) - mux.HandleFunc("/outpost.goauthentik.io/sign_in", a.handleRedirect) - mux.HandleFunc("/outpost.goauthentik.io/callback", a.handleCallback) + mux.HandleFunc("/outpost.goauthentik.io/start", a.handleAuthStart) + mux.HandleFunc("/outpost.goauthentik.io/callback", a.handleAuthCallback) mux.HandleFunc("/outpost.goauthentik.io/sign_out", a.handleSignOut) switch *p.Mode.Get() { case api.PROXYMODE_PROXY: @@ -197,14 +211,14 @@ func (a *Application) handleSignOut(rw http.ResponseWriter, r *http.Request) { //TODO: Token revocation s, err := a.sessions.Get(r, constants.SessionName) if err != nil { - http.Redirect(rw, r, a.endpint.EndSessionEndpoint, http.StatusFound) + http.Redirect(rw, r, a.endpoint.EndSessionEndpoint, http.StatusFound) return } s.Options.MaxAge = -1 err = s.Save(r, rw) if err != nil { - http.Redirect(rw, r, a.endpint.EndSessionEndpoint, http.StatusFound) + http.Redirect(rw, r, a.endpoint.EndSessionEndpoint, http.StatusFound) return } - http.Redirect(rw, r, a.endpint.EndSessionEndpoint, http.StatusFound) + http.Redirect(rw, r, a.endpoint.EndSessionEndpoint, http.StatusFound) } diff --git a/internal/outpost/proxyv2/application/mode_common.go b/internal/outpost/proxyv2/application/mode_common.go index c5a95c524..8a0a61d21 100644 --- a/internal/outpost/proxyv2/application/mode_common.go +++ b/internal/outpost/proxyv2/application/mode_common.go @@ -13,8 +13,6 @@ import ( "goauthentik.io/internal/constants" ) -var hasReportedMisconfiguration = false - func (a *Application) addHeaders(headers http.Header, c *Claims) { // https://goauthentik.io/docs/providers/proxy/proxy @@ -26,7 +24,7 @@ func (a *Application) addHeaders(headers http.Header, c *Claims) { headers.Set("X-authentik-jwt", c.RawToken) // System headers - headers.Set("X-authentik-meta-jwks", a.endpint.JwksUri) + headers.Set("X-authentik-meta-jwks", a.endpoint.JwksUri) headers.Set("X-authentik-meta-outpost", a.outpostName) headers.Set("X-authentik-meta-provider", a.proxyConfig.Name) headers.Set("X-authentik-meta-app", a.proxyConfig.AssignedApplicationSlug) @@ -105,9 +103,6 @@ func (a *Application) getNginxForwardUrl(r *http.Request) (*url.URL, error) { func (a *Application) ReportMisconfiguration(r *http.Request, msg string, fields map[string]interface{}) { fields["message"] = msg a.log.WithFields(fields).Error("Reporting configuration error") - if hasReportedMisconfiguration { - return - } req := api.EventRequest{ Action: api.EVENTACTIONS_CONFIGURATION_ERROR, App: "authentik.providers.proxy", // must match python apps.py name @@ -117,8 +112,6 @@ func (a *Application) ReportMisconfiguration(r *http.Request, msg string, fields _, _, err := a.ak.Client.EventsApi.EventsEventsCreate(context.Background()).EventRequest(req).Execute() if err != nil { a.log.WithError(err).Warning("failed to report configuration error") - } else { - hasReportedMisconfiguration = true } } diff --git a/internal/outpost/proxyv2/application/mode_forward.go b/internal/outpost/proxyv2/application/mode_forward.go index 3f4d7dbc2..6a1761738 100644 --- a/internal/outpost/proxyv2/application/mode_forward.go +++ b/internal/outpost/proxyv2/application/mode_forward.go @@ -3,12 +3,9 @@ package application import ( "fmt" "net/http" - "net/url" "strings" - "goauthentik.io/api/v3" "goauthentik.io/internal/outpost/proxyv2/constants" - "goauthentik.io/internal/utils/web" ) const ( @@ -40,7 +37,7 @@ func (a *Application) forwardHandleTraefik(rw http.ResponseWriter, r *http.Reque http.Error(rw, "configuration error", http.StatusInternalServerError) return } - + // Check if we're authenticated, or the request path is on the allowlist claims, err := a.getClaims(r) if claims != nil && err == nil { a.addHeaders(rw.Header(), claims) @@ -51,22 +48,9 @@ func (a *Application) forwardHandleTraefik(rw http.ResponseWriter, r *http.Reque a.log.Trace("path can be accessed without authentication") return } - if strings.HasPrefix(r.Header.Get("X-Forwarded-Uri"), "/outpost.goauthentik.io") { - a.log.WithField("url", r.URL.String()).Trace("path begins with /outpost.goauthentik.io, allowing access") - return - } - host := "" - // Optional suffix, which is appended to the URL - if *a.proxyConfig.Mode.Get() == api.PROXYMODE_FORWARD_SINGLE { - host = web.GetHost(r) - } else if *a.proxyConfig.Mode.Get() == api.PROXYMODE_FORWARD_DOMAIN { - eh, err := url.Parse(a.proxyConfig.ExternalHost) - if err != nil { - a.log.WithField("host", a.proxyConfig.ExternalHost).WithError(err).Warning("invalid external_host") - } else { - host = eh.Host - } - } + tr := r.Clone(r.Context()) + tr.URL = fwd + a.handleAuthStart(rw, r) // set the redirect flag to the current URL we have, since we redirect // to a (possibly) different domain, but we want to be redirected back // to the application @@ -76,17 +60,9 @@ func (a *Application) forwardHandleTraefik(rw http.ResponseWriter, r *http.Reque s.Values[constants.SessionRedirect] = fwd.String() err = s.Save(r, rw) if err != nil { - a.log.WithError(err).Warning("failed to save session before redirect") + a.log.WithError(err).Warning("failed to save session") } } - - proto := r.Header.Get("X-Forwarded-Proto") - if proto != "" { - proto = proto + ":" - } - rdFinal := fmt.Sprintf("%s//%s%s", proto, host, "/outpost.goauthentik.io/start") - a.log.WithField("url", rdFinal).Debug("Redirecting to login") - http.Redirect(rw, r, rdFinal, http.StatusTemporaryRedirect) } func (a *Application) forwardHandleCaddy(rw http.ResponseWriter, r *http.Request) { @@ -103,7 +79,7 @@ func (a *Application) forwardHandleCaddy(rw http.ResponseWriter, r *http.Request http.Error(rw, "configuration error", http.StatusInternalServerError) return } - + // Check if we're authenticated, or the request path is on the allowlist claims, err := a.getClaims(r) if claims != nil && err == nil { a.addHeaders(rw.Header(), claims) @@ -114,22 +90,9 @@ func (a *Application) forwardHandleCaddy(rw http.ResponseWriter, r *http.Request a.log.Trace("path can be accessed without authentication") return } - if strings.HasPrefix(r.Header.Get("X-Forwarded-Uri"), "/outpost.goauthentik.io") { - a.log.WithField("url", r.URL.String()).Trace("path begins with /outpost.goauthentik.io, allowing access") - return - } - host := "" - // Optional suffix, which is appended to the URL - if *a.proxyConfig.Mode.Get() == api.PROXYMODE_FORWARD_SINGLE { - host = web.GetHost(r) - } else if *a.proxyConfig.Mode.Get() == api.PROXYMODE_FORWARD_DOMAIN { - eh, err := url.Parse(a.proxyConfig.ExternalHost) - if err != nil { - a.log.WithField("host", a.proxyConfig.ExternalHost).WithError(err).Warning("invalid external_host") - } else { - host = eh.Host - } - } + tr := r.Clone(r.Context()) + tr.URL = fwd + a.handleAuthStart(rw, r) // set the redirect flag to the current URL we have, since we redirect // to a (possibly) different domain, but we want to be redirected back // to the application @@ -139,17 +102,9 @@ func (a *Application) forwardHandleCaddy(rw http.ResponseWriter, r *http.Request s.Values[constants.SessionRedirect] = fwd.String() err = s.Save(r, rw) if err != nil { - a.log.WithError(err).Warning("failed to save session before redirect") + a.log.WithError(err).Warning("failed to save session") } } - - proto := r.Header.Get("X-Forwarded-Proto") - if proto != "" { - proto = proto + ":" - } - rdFinal := fmt.Sprintf("%s//%s%s", proto, host, "/outpost.goauthentik.io/start") - a.log.WithField("url", rdFinal).Debug("Redirecting to login") - http.Redirect(rw, r, rdFinal, http.StatusTemporaryRedirect) } func (a *Application) forwardHandleNginx(rw http.ResponseWriter, r *http.Request) { @@ -200,7 +155,7 @@ func (a *Application) forwardHandleEnvoy(rw http.ResponseWriter, r *http.Request a.log.WithField("header", r.Header).Trace("tracing headers for debug") r.URL.Path = strings.TrimPrefix(r.URL.Path, envoyPrefix) fwd := r.URL - + // Check if we're authenticated, or the request path is on the allowlist claims, err := a.getClaims(r) if claims != nil && err == nil { a.addHeaders(rw.Header(), claims) @@ -211,22 +166,7 @@ func (a *Application) forwardHandleEnvoy(rw http.ResponseWriter, r *http.Request a.log.Trace("path can be accessed without authentication") return } - if strings.HasPrefix(r.URL.Path, "/outpost.goauthentik.io") { - a.log.WithField("url", r.URL.String()).Trace("path begins with /outpost.goauthentik.io, allowing access") - return - } - host := "" - // Optional suffix, which is appended to the URL - if *a.proxyConfig.Mode.Get() == api.PROXYMODE_FORWARD_SINGLE { - host = web.GetHost(r) - } else if *a.proxyConfig.Mode.Get() == api.PROXYMODE_FORWARD_DOMAIN { - eh, err := url.Parse(a.proxyConfig.ExternalHost) - if err != nil { - a.log.WithField("host", a.proxyConfig.ExternalHost).WithError(err).Warning("invalid external_host") - } else { - host = eh.Host - } - } + a.handleAuthStart(rw, r) // set the redirect flag to the current URL we have, since we redirect // to a (possibly) different domain, but we want to be redirected back // to the application @@ -239,7 +179,4 @@ func (a *Application) forwardHandleEnvoy(rw http.ResponseWriter, r *http.Request a.log.WithError(err).Warning("failed to save session before redirect") } } - rdFinal := fmt.Sprintf("//%s%s", host, "/outpost.goauthentik.io/start") - a.log.WithField("url", rdFinal).Debug("Redirecting to login") - http.Redirect(rw, r, rdFinal, http.StatusTemporaryRedirect) } diff --git a/internal/outpost/proxyv2/application/mode_forward_caddy_test.go b/internal/outpost/proxyv2/application/mode_forward_caddy_test.go index f8c3778af..9942eec0b 100644 --- a/internal/outpost/proxyv2/application/mode_forward_caddy_test.go +++ b/internal/outpost/proxyv2/application/mode_forward_caddy_test.go @@ -1,8 +1,10 @@ package application import ( + "fmt" "net/http" "net/http/httptest" + "net/url" "testing" "github.com/stretchr/testify/assert" @@ -43,11 +45,16 @@ func TestForwardHandleCaddy_Single_Headers(t *testing.T) { rr := httptest.NewRecorder() a.forwardHandleCaddy(rr, req) - assert.Equal(t, rr.Code, http.StatusTemporaryRedirect) + assert.Equal(t, http.StatusFound, rr.Code) loc, _ := rr.Result().Location() - assert.Equal(t, loc.String(), "http://test.goauthentik.io/outpost.goauthentik.io/start") - s, _ := a.sessions.Get(req, constants.SessionName) + shouldUrl := url.Values{ + "client_id": []string{*a.proxyConfig.ClientId}, + "redirect_uri": []string{"https://ext.t.goauthentik.io/outpost.goauthentik.io/callback?X-authentik-auth-callback=true"}, + "response_type": []string{"code"}, + "state": []string{s.Values[constants.SessionOAuthState].([]string)[0]}, + } + assert.Equal(t, fmt.Sprintf("http://fake-auth.t.goauthentik.io/auth?%s", shouldUrl.Encode()), loc.String()) assert.Equal(t, "http://test.goauthentik.io/app", s.Values[constants.SessionRedirect]) } @@ -123,10 +130,15 @@ func TestForwardHandleCaddy_Domain_Header(t *testing.T) { rr := httptest.NewRecorder() a.forwardHandleCaddy(rr, req) - assert.Equal(t, http.StatusTemporaryRedirect, rr.Code) + assert.Equal(t, http.StatusFound, rr.Code) loc, _ := rr.Result().Location() - assert.Equal(t, "http://auth.test.goauthentik.io/outpost.goauthentik.io/start", loc.String()) - s, _ := a.sessions.Get(req, constants.SessionName) + shouldUrl := url.Values{ + "client_id": []string{*a.proxyConfig.ClientId}, + "redirect_uri": []string{"https://ext.t.goauthentik.io/outpost.goauthentik.io/callback?X-authentik-auth-callback=true"}, + "response_type": []string{"code"}, + "state": []string{s.Values[constants.SessionOAuthState].([]string)[0]}, + } + assert.Equal(t, fmt.Sprintf("http://fake-auth.t.goauthentik.io/auth?%s", shouldUrl.Encode()), loc.String()) assert.Equal(t, "http://test.goauthentik.io/app", s.Values[constants.SessionRedirect]) } diff --git a/internal/outpost/proxyv2/application/mode_forward_envoy_test.go b/internal/outpost/proxyv2/application/mode_forward_envoy_test.go index 3f5c3a488..30d75f3da 100644 --- a/internal/outpost/proxyv2/application/mode_forward_envoy_test.go +++ b/internal/outpost/proxyv2/application/mode_forward_envoy_test.go @@ -1,8 +1,10 @@ package application import ( + "fmt" "net/http" "net/http/httptest" + "net/url" "testing" "github.com/stretchr/testify/assert" @@ -27,11 +29,16 @@ func TestForwardHandleEnvoy_Single_Headers(t *testing.T) { rr := httptest.NewRecorder() a.forwardHandleEnvoy(rr, req) - assert.Equal(t, rr.Code, http.StatusTemporaryRedirect) + assert.Equal(t, http.StatusFound, rr.Code) loc, _ := rr.Result().Location() - assert.Equal(t, loc.String(), "//test.goauthentik.io/outpost.goauthentik.io/start") - s, _ := a.sessions.Get(req, constants.SessionName) + shouldUrl := url.Values{ + "client_id": []string{*a.proxyConfig.ClientId}, + "redirect_uri": []string{"https://ext.t.goauthentik.io/outpost.goauthentik.io/callback?X-authentik-auth-callback=true"}, + "response_type": []string{"code"}, + "state": []string{s.Values[constants.SessionOAuthState].([]string)[0]}, + } + assert.Equal(t, fmt.Sprintf("http://fake-auth.t.goauthentik.io/auth?%s", shouldUrl.Encode()), loc.String()) assert.Equal(t, "http://test.goauthentik.io/app", s.Values[constants.SessionRedirect]) } @@ -89,10 +96,16 @@ func TestForwardHandleEnvoy_Domain_Header(t *testing.T) { rr := httptest.NewRecorder() a.forwardHandleEnvoy(rr, req) - assert.Equal(t, http.StatusTemporaryRedirect, rr.Code) + assert.Equal(t, http.StatusFound, rr.Code) loc, _ := rr.Result().Location() - assert.Equal(t, "//auth.test.goauthentik.io/outpost.goauthentik.io/start", loc.String()) - s, _ := a.sessions.Get(req, constants.SessionName) + + shouldUrl := url.Values{ + "client_id": []string{*a.proxyConfig.ClientId}, + "redirect_uri": []string{"https://ext.t.goauthentik.io/outpost.goauthentik.io/callback?X-authentik-auth-callback=true"}, + "response_type": []string{"code"}, + "state": []string{s.Values[constants.SessionOAuthState].([]string)[0]}, + } + assert.Equal(t, fmt.Sprintf("http://fake-auth.t.goauthentik.io/auth?%s", shouldUrl.Encode()), loc.String()) assert.Equal(t, "http://test.goauthentik.io/app", s.Values[constants.SessionRedirect]) } diff --git a/internal/outpost/proxyv2/application/mode_forward_nginx_test.go b/internal/outpost/proxyv2/application/mode_forward_nginx_test.go index a23a0be71..434a18ee0 100644 --- a/internal/outpost/proxyv2/application/mode_forward_nginx_test.go +++ b/internal/outpost/proxyv2/application/mode_forward_nginx_test.go @@ -39,7 +39,7 @@ func TestForwardHandleNginx_Single_Headers(t *testing.T) { rr := httptest.NewRecorder() a.forwardHandleNginx(rr, req) - assert.Equal(t, rr.Code, http.StatusUnauthorized) + assert.Equal(t, http.StatusUnauthorized, rr.Code) s, _ := a.sessions.Get(req, constants.SessionName) assert.Equal(t, "http://test.goauthentik.io/app", s.Values[constants.SessionRedirect]) @@ -53,7 +53,7 @@ func TestForwardHandleNginx_Single_URI(t *testing.T) { rr := httptest.NewRecorder() a.forwardHandleNginx(rr, req) - assert.Equal(t, rr.Code, http.StatusUnauthorized) + assert.Equal(t, http.StatusUnauthorized, rr.Code) s, _ := a.sessions.Get(req, constants.SessionName) assert.Equal(t, "/app", s.Values[constants.SessionRedirect]) diff --git a/internal/outpost/proxyv2/application/mode_forward_traefik_test.go b/internal/outpost/proxyv2/application/mode_forward_traefik_test.go index 8d22b12ed..87598b6c4 100644 --- a/internal/outpost/proxyv2/application/mode_forward_traefik_test.go +++ b/internal/outpost/proxyv2/application/mode_forward_traefik_test.go @@ -1,8 +1,10 @@ package application import ( + "fmt" "net/http" "net/http/httptest" + "net/url" "testing" "github.com/stretchr/testify/assert" @@ -43,11 +45,16 @@ func TestForwardHandleTraefik_Single_Headers(t *testing.T) { rr := httptest.NewRecorder() a.forwardHandleTraefik(rr, req) - assert.Equal(t, rr.Code, http.StatusTemporaryRedirect) + assert.Equal(t, http.StatusFound, rr.Code) loc, _ := rr.Result().Location() - assert.Equal(t, loc.String(), "http://test.goauthentik.io/outpost.goauthentik.io/start") - s, _ := a.sessions.Get(req, constants.SessionName) + shouldUrl := url.Values{ + "client_id": []string{*a.proxyConfig.ClientId}, + "redirect_uri": []string{"https://ext.t.goauthentik.io/outpost.goauthentik.io/callback?X-authentik-auth-callback=true"}, + "response_type": []string{"code"}, + "state": []string{s.Values[constants.SessionOAuthState].([]string)[0]}, + } + assert.Equal(t, fmt.Sprintf("http://fake-auth.t.goauthentik.io/auth?%s", shouldUrl.Encode()), loc.String()) assert.Equal(t, "http://test.goauthentik.io/app", s.Values[constants.SessionRedirect]) } @@ -123,10 +130,15 @@ func TestForwardHandleTraefik_Domain_Header(t *testing.T) { rr := httptest.NewRecorder() a.forwardHandleTraefik(rr, req) - assert.Equal(t, http.StatusTemporaryRedirect, rr.Code) + assert.Equal(t, http.StatusFound, rr.Code) loc, _ := rr.Result().Location() - assert.Equal(t, "http://auth.test.goauthentik.io/outpost.goauthentik.io/start", loc.String()) - s, _ := a.sessions.Get(req, constants.SessionName) + shouldUrl := url.Values{ + "client_id": []string{*a.proxyConfig.ClientId}, + "redirect_uri": []string{"https://ext.t.goauthentik.io/outpost.goauthentik.io/callback?X-authentik-auth-callback=true"}, + "response_type": []string{"code"}, + "state": []string{s.Values[constants.SessionOAuthState].([]string)[0]}, + } + assert.Equal(t, fmt.Sprintf("http://fake-auth.t.goauthentik.io/auth?%s", shouldUrl.Encode()), loc.String()) assert.Equal(t, "http://test.goauthentik.io/app", s.Values[constants.SessionRedirect]) } diff --git a/internal/outpost/proxyv2/application/oauth.go b/internal/outpost/proxyv2/application/oauth.go index cf87826de..81928781d 100644 --- a/internal/outpost/proxyv2/application/oauth.go +++ b/internal/outpost/proxyv2/application/oauth.go @@ -13,7 +13,8 @@ import ( ) const ( - redirectParam = "rd" + redirectParam = "rd" + CallbackSignature = "X-authentik-auth-callback" ) func (a *Application) checkRedirectParam(r *http.Request) (string, bool) { @@ -41,7 +42,7 @@ func (a *Application) checkRedirectParam(r *http.Request) (string, bool) { return u.String(), true } -func (a *Application) handleRedirect(rw http.ResponseWriter, r *http.Request) { +func (a *Application) handleAuthStart(rw http.ResponseWriter, r *http.Request) { newState := base64.RawURLEncoding.EncodeToString(securecookie.GenerateRandomKey(32)) s, err := a.sessions.Get(r, constants.SessionName) if err != nil { @@ -65,7 +66,7 @@ func (a *Application) handleRedirect(rw http.ResponseWriter, r *http.Request) { http.Redirect(rw, r, a.oauthConfig.AuthCodeURL(newState), http.StatusFound) } -func (a *Application) handleCallback(rw http.ResponseWriter, r *http.Request) { +func (a *Application) handleAuthCallback(rw http.ResponseWriter, r *http.Request) { s, err := a.sessions.Get(r, constants.SessionName) if err != nil { a.log.WithError(err).Trace("failed to get session") diff --git a/internal/outpost/proxyv2/application/test.go b/internal/outpost/proxyv2/application/test.go index 74787114a..3bb93591c 100644 --- a/internal/outpost/proxyv2/application/test.go +++ b/internal/outpost/proxyv2/application/test.go @@ -22,6 +22,11 @@ func newTestApplication() *Application { BasicAuthEnabled: api.PtrBool(true), BasicAuthUserAttribute: api.PtrString("username"), BasicAuthPasswordAttribute: api.PtrString("password"), + OidcConfiguration: api.ProxyOutpostConfigOidcConfiguration{ + AuthorizationEndpoint: "http://fake-auth.t.goauthentik.io/auth", + TokenEndpoint: "http://fake-auth.t.goauthentik.io/token", + UserinfoEndpoint: "http://fake-auth.t.goauthentik.io/userinfo", + }, }, http.DefaultClient, nil, diff --git a/internal/web/proxy.go b/internal/web/proxy.go index 24364e2e4..b7ebce92a 100644 --- a/internal/web/proxy.go +++ b/internal/web/proxy.go @@ -9,6 +9,7 @@ import ( "time" "github.com/prometheus/client_golang/prometheus" + "goauthentik.io/internal/outpost/proxyv2/application" "goauthentik.io/internal/utils/sentry" "goauthentik.io/internal/utils/web" ) @@ -52,7 +53,8 @@ func (ws *WebServer) configureProxy() { } before := time.Now() if ws.ProxyServer != nil { - if ws.ProxyServer.HandleHost(rw, r) { + _, oauthCallbackSet := r.URL.Query()[application.CallbackSignature] + if ws.ProxyServer.HandleHost(rw, r) || oauthCallbackSet { Requests.With(prometheus.Labels{ "dest": "embedded_outpost", }).Observe(float64(time.Since(before))) diff --git a/internal/web/web.go b/internal/web/web.go index 50198dd6e..86a5d7f8e 100644 --- a/internal/web/web.go +++ b/internal/web/web.go @@ -39,12 +39,12 @@ func NewWebServer(g *gounicorn.GoUnicorn) *WebServer { mainHandler.Use(sentryhttp.New(sentryhttp.Options{}).Handle) mainHandler.Use(handlers.ProxyHeaders) mainHandler.Use(handlers.CompressHandler) - logginRouter := mainHandler.NewRoute().Subrouter() - logginRouter.Use(web.NewLoggingHandler(l, nil)) + loggingHandler := mainHandler.NewRoute().Subrouter() + loggingHandler.Use(web.NewLoggingHandler(l, nil)) ws := &WebServer{ m: mainHandler, - lh: logginRouter, + lh: loggingHandler, log: l, p: g, }