providers/proxy: no exposed urls (#3151)
* test any callback Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * cleanup Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * dont detect callback in per-server handler Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * use full redirect uri with both path and query param Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * update tests Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * correctly route to embedded outpost for callback signature Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * fix allowed redirects Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
b7b5168910
commit
393d7ec486
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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])
|
||||
}
|
||||
|
|
|
@ -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])
|
||||
}
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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])
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
Reference in New Issue