2021-09-08 18:04:56 +00:00
package application
import (
"fmt"
"net/http"
2022-01-21 12:43:16 +00:00
"strings"
2021-09-08 18:04:56 +00:00
2021-09-09 08:56:20 +00:00
"goauthentik.io/internal/outpost/proxyv2/constants"
2021-09-08 18:04:56 +00:00
)
2022-06-03 08:32:52 +00:00
const (
2022-06-04 21:25:47 +00:00
envoyPrefix = "/outpost.goauthentik.io/auth/envoy"
2022-07-29 08:58:53 +00:00
caddyPrefix = "/outpost.goauthentik.io/auth/caddy"
2022-06-04 21:25:47 +00:00
traefikPrefix = "/outpost.goauthentik.io/auth/traefik"
nginxPrefix = "/outpost.goauthentik.io/auth/nginx"
2022-06-03 08:32:52 +00:00
)
2021-09-08 18:04:56 +00:00
func ( a * Application ) configureForward ( ) error {
2022-06-04 21:25:47 +00:00
a . mux . HandleFunc ( traefikPrefix , a . forwardHandleTraefik )
2022-07-29 08:58:53 +00:00
a . mux . HandleFunc ( caddyPrefix , a . forwardHandleCaddy )
2022-06-04 21:25:47 +00:00
a . mux . HandleFunc ( nginxPrefix , a . forwardHandleNginx )
2022-06-03 08:32:52 +00:00
a . mux . PathPrefix ( envoyPrefix ) . HandlerFunc ( a . forwardHandleEnvoy )
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
}
2022-09-02 20:03:08 +00:00
if strings . EqualFold ( fwd . Query ( ) . Get ( CallbackSignature ) , "true" ) {
a . log . Debug ( "handling OAuth Callback from querystring signature" )
a . handleAuthCallback ( rw , r )
return
} else if strings . EqualFold ( fwd . Query ( ) . Get ( LogoutSignature ) , "true" ) {
a . log . Debug ( "handling OAuth Logout from querystring signature" )
a . handleSignOut ( rw , r )
return
}
2022-07-30 15:51:01 +00:00
// Check if we're authenticated, or the request path is on the allowlist
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 ) {
2022-07-29 08:58:53 +00:00
a . log . Trace ( "path can be accessed without authentication" )
return
}
2022-07-30 15:51:01 +00:00
tr := r . Clone ( r . Context ( ) )
tr . URL = fwd
a . handleAuthStart ( rw , r )
2022-07-29 08:58:53 +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
s , _ := a . sessions . Get ( r , constants . SessionName )
if _ , redirectSet := s . Values [ constants . SessionRedirect ] ; ! redirectSet {
s . Values [ constants . SessionRedirect ] = fwd . String ( )
err = s . Save ( r , rw )
if err != nil {
2022-07-30 15:51:01 +00:00
a . log . WithError ( err ) . Warning ( "failed to save session" )
2022-07-29 08:58:53 +00:00
}
}
}
func ( a * Application ) forwardHandleCaddy ( rw http . ResponseWriter , r * http . Request ) {
a . log . WithField ( "header" , r . Header ) . Trace ( "tracing headers for debug" )
// 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 Caddy" , 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
}
2022-09-02 20:03:08 +00:00
if strings . EqualFold ( fwd . Query ( ) . Get ( CallbackSignature ) , "true" ) {
a . log . Debug ( "handling OAuth Callback from querystring signature" )
a . handleAuthCallback ( rw , r )
return
} else if strings . EqualFold ( fwd . Query ( ) . Get ( LogoutSignature ) , "true" ) {
a . log . Debug ( "handling OAuth Logout from querystring signature" )
a . handleSignOut ( rw , r )
return
}
2022-07-30 15:51:01 +00:00
// Check if we're authenticated, or the request path is on the allowlist
2022-07-29 08:58:53 +00:00
claims , err := a . getClaims ( r )
if claims != nil && err == nil {
a . addHeaders ( rw . Header ( ) , claims )
rw . Header ( ) . Set ( "User-Agent" , r . Header . Get ( "User-Agent" ) )
a . log . WithField ( "headers" , rw . Header ( ) ) . Trace ( "headers written to forward_auth" )
return
} 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-07-30 15:51:01 +00:00
tr := r . Clone ( r . Context ( ) )
tr . URL = fwd
a . handleAuthStart ( rw , r )
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-06-04 21:25:47 +00:00
s , _ := a . sessions . Get ( r , constants . SessionName )
if _ , redirectSet := s . Values [ constants . SessionRedirect ] ; ! redirectSet {
s . Values [ constants . SessionRedirect ] = fwd . String ( )
err = s . Save ( r , rw )
if err != nil {
2022-07-30 15:51:01 +00:00
a . log . WithError ( err ) . Warning ( "failed to save session" )
2022-06-04 21:25:47 +00:00
}
2021-09-08 18:04:56 +00:00
}
}
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-06-04 21:25:47 +00:00
if _ , redirectSet := s . Values [ constants . SessionRedirect ] ; ! redirectSet {
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" )
}
2022-01-24 19:50:13 +00:00
}
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 )
}
2022-06-02 22:06:09 +00:00
func ( a * Application ) forwardHandleEnvoy ( rw http . ResponseWriter , r * http . Request ) {
a . log . WithField ( "header" , r . Header ) . Trace ( "tracing headers for debug" )
2022-06-03 08:32:52 +00:00
r . URL . Path = strings . TrimPrefix ( r . URL . Path , envoyPrefix )
2022-08-18 21:42:22 +00:00
r . URL . Host = r . Host
2022-06-02 22:06:09 +00:00
fwd := r . URL
2022-07-30 15:51:01 +00:00
// Check if we're authenticated, or the request path is on the allowlist
2022-06-02 22:06:09 +00:00
claims , err := a . getClaims ( r )
if claims != nil && err == nil {
a . addHeaders ( rw . Header ( ) , claims )
rw . Header ( ) . Set ( "User-Agent" , r . Header . Get ( "User-Agent" ) )
a . log . WithField ( "headers" , rw . Header ( ) ) . Trace ( "headers written to forward_auth" )
return
} else if claims == nil && a . IsAllowlisted ( fwd ) {
a . log . Trace ( "path can be accessed without authentication" )
return
}
2022-07-30 15:51:01 +00:00
a . handleAuthStart ( rw , r )
2022-06-02 22:06:09 +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-06-04 21:25:47 +00:00
s , _ := a . sessions . Get ( r , constants . SessionName )
2022-06-03 08:32:52 +00:00
if _ , redirectSet := s . Values [ constants . SessionRedirect ] ; ! redirectSet {
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" )
}
2022-06-02 22:06:09 +00:00
}
}