2021-09-08 18:04:56 +00:00
package application
import (
"fmt"
"net/http"
2021-09-27 18:49:00 +00:00
"net/url"
2022-01-21 12:43:16 +00:00
"strings"
2021-09-08 18:04:56 +00:00
2022-03-03 09:40:07 +00:00
"goauthentik.io/api/v3"
2021-09-09 08:56:20 +00:00
"goauthentik.io/internal/outpost/proxyv2/constants"
2021-09-08 18:04:56 +00:00
"goauthentik.io/internal/utils/web"
)
func ( a * Application ) configureForward ( ) error {
2022-02-08 19:25:38 +00:00
a . mux . HandleFunc ( "/outpost.goauthentik.io/auth" , func ( rw http . ResponseWriter , r * http . Request ) {
2021-09-08 18:04:56 +00:00
if _ , ok := r . URL . Query ( ) [ "traefik" ] ; ok {
a . forwardHandleTraefik ( rw , r )
return
}
a . forwardHandleNginx ( rw , r )
} )
2022-02-08 19:25:38 +00:00
a . mux . HandleFunc ( "/outpost.goauthentik.io/auth/traefik" , a . forwardHandleTraefik )
a . mux . HandleFunc ( "/outpost.goauthentik.io/auth/nginx" , a . forwardHandleNginx )
2021-09-08 18:04:56 +00:00
return nil
}
func ( a * Application ) forwardHandleTraefik ( rw http . ResponseWriter , r * http . Request ) {
2022-01-24 21:08:31 +00:00
a . log . WithField ( "header" , r . Header ) . Trace ( "tracing headers for debug" )
2022-01-27 17:14:02 +00:00
// First check if we've got everything we need
fwd , err := a . getTraefikForwardUrl ( r )
if err != nil {
a . ReportMisconfiguration ( r , fmt . Sprintf ( "Outpost %s (Provider %s) failed to detect a forward URL from Traefik" , a . outpostName , a . proxyConfig . Name ) , map [ string ] interface { } {
"provider" : a . proxyConfig . Name ,
"outpost" : a . outpostName ,
"url" : r . URL . String ( ) ,
"headers" : cleanseHeaders ( r . Header ) ,
} )
http . Error ( rw , "configuration error" , http . StatusInternalServerError )
return
}
2021-09-08 18:04:56 +00:00
claims , err := a . getClaims ( r )
if claims != nil && err == nil {
2021-12-01 19:05:56 +00:00
a . addHeaders ( rw . Header ( ) , claims )
2021-12-02 09:01:54 +00:00
rw . Header ( ) . Set ( "User-Agent" , r . Header . Get ( "User-Agent" ) )
2021-12-01 19:05:56 +00:00
a . log . WithField ( "headers" , rw . Header ( ) ) . Trace ( "headers written to forward_auth" )
2021-09-08 18:04:56 +00:00
return
2022-01-24 19:50:13 +00:00
} else if claims == nil && a . IsAllowlisted ( fwd ) {
2021-09-08 18:04:56 +00:00
a . log . Trace ( "path can be accessed without authentication" )
return
}
2022-02-08 19:25:38 +00:00
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" )
2022-01-21 12:43:16 +00:00
return
}
2021-09-08 18:04:56 +00:00
host := ""
2022-05-20 08:10:26 +00:00
s , _ := a . sessions . Get ( r , constants . SessionName )
2021-09-08 18:04:56 +00:00
// Optional suffix, which is appended to the URL
2022-05-26 13:15:30 +00:00
if * a . proxyConfig . Mode . Get ( ) == api . PROXYMODE_FORWARD_SINGLE {
2021-09-08 18:04:56 +00:00
host = web . GetHost ( r )
2022-05-26 13:15:30 +00:00
} else if * a . proxyConfig . Mode . Get ( ) == api . PROXYMODE_FORWARD_DOMAIN {
2022-01-24 20:41:15 +00:00
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
}
2021-09-10 10:43:57 +00:00
}
// 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
// X-Forwarded-Uri is only the path, so we need to build the entire URL
2022-01-24 19:50:13 +00:00
s . Values [ constants . SessionRedirect ] = fwd . String ( )
2021-09-10 10:43:57 +00:00
err = s . Save ( r , rw )
if err != nil {
a . log . WithError ( err ) . Warning ( "failed to save session before redirect" )
2021-09-08 18:04:56 +00:00
}
2021-12-20 21:21:53 +00:00
2021-09-08 18:04:56 +00:00
proto := r . Header . Get ( "X-Forwarded-Proto" )
if proto != "" {
proto = proto + ":"
}
2022-02-08 19:25:38 +00:00
rdFinal := fmt . Sprintf ( "%s//%s%s" , proto , host , "/outpost.goauthentik.io/start" )
2021-09-08 18:04:56 +00:00
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 ) {
2022-01-24 21:08:31 +00:00
a . log . WithField ( "header" , r . Header ) . Trace ( "tracing headers for debug" )
2022-01-27 17:14:02 +00:00
fwd , err := a . getNginxForwardUrl ( r )
if err != nil {
a . ReportMisconfiguration ( r , fmt . Sprintf ( "Outpost %s (Provider %s) failed to detect a forward URL from nginx" , a . outpostName , a . proxyConfig . Name ) , map [ string ] interface { } {
"provider" : a . proxyConfig . Name ,
"outpost" : a . outpostName ,
"url" : r . URL . String ( ) ,
"headers" : cleanseHeaders ( r . Header ) ,
} )
http . Error ( rw , "configuration error" , http . StatusInternalServerError )
return
}
2021-09-08 18:04:56 +00:00
claims , err := a . getClaims ( r )
if claims != nil && err == nil {
2021-12-01 19:05:56 +00:00
a . addHeaders ( rw . Header ( ) , claims )
2021-12-02 09:01:54 +00:00
rw . Header ( ) . Set ( "User-Agent" , r . Header . Get ( "User-Agent" ) )
2021-09-09 08:56:20 +00:00
rw . WriteHeader ( 200 )
2021-12-01 19:05:56 +00:00
a . log . WithField ( "headers" , rw . Header ( ) ) . Trace ( "headers written to forward_auth" )
2021-09-08 18:04:56 +00:00
return
2022-01-24 19:50:13 +00:00
} else if claims == nil && a . IsAllowlisted ( fwd ) {
2021-09-08 18:04:56 +00:00
a . log . Trace ( "path can be accessed without authentication" )
return
}
2022-01-24 19:50:13 +00:00
2022-05-20 08:10:26 +00:00
s , _ := a . sessions . Get ( r , constants . SessionName )
2022-01-24 19:50:13 +00:00
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" )
}
if fwd . String ( ) != r . URL . String ( ) {
2022-02-08 19:25:38 +00:00
if strings . HasPrefix ( fwd . Path , "/outpost.goauthentik.io" ) {
a . log . WithField ( "url" , r . URL . String ( ) ) . Trace ( "path begins with /outpost.goauthentik.io, allowing access" )
2022-01-24 08:30:33 +00:00
return
}
2022-01-21 12:43:16 +00:00
}
2021-09-08 18:04:56 +00:00
http . Error ( rw , "unauthorized request" , http . StatusUnauthorized )
}