mirror of
https://github.com/oauth2-proxy/oauth2-proxy.git
synced 2026-10-21 10:17:22 +02:00
Integrate new header injectors with OAuth2 Proxy
This commit is contained in:
+68
-160
@@ -2,7 +2,6 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
b64 "encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -98,7 +97,6 @@ type OAuthProxy struct {
|
||||
PassAuthorization bool
|
||||
PreferEmailToUser bool
|
||||
skipAuthPreflight bool
|
||||
skipAuthStripHeaders bool
|
||||
skipJwtBearerTokens bool
|
||||
mainJwtBearerVerifier *oidc.IDTokenVerifier
|
||||
extraJwtBearerVerifiers []*oidc.IDTokenVerifier
|
||||
@@ -110,6 +108,8 @@ type OAuthProxy struct {
|
||||
AllowedGroups []string
|
||||
|
||||
sessionChain alice.Chain
|
||||
headersChain alice.Chain
|
||||
preAuthChain alice.Chain
|
||||
}
|
||||
|
||||
// NewOAuthProxy creates a new instance of OAuthProxy from the options provided
|
||||
@@ -169,7 +169,15 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr
|
||||
return nil, err
|
||||
}
|
||||
|
||||
preAuthChain, err := buildPreAuthChain(opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not build pre-auth chain: %v", err)
|
||||
}
|
||||
sessionChain := buildSessionChain(opts, sessionStore, basicAuthValidator)
|
||||
headersChain, err := buildHeadersChain(opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not build headers chain: %v", err)
|
||||
}
|
||||
|
||||
return &OAuthProxy{
|
||||
CookieName: opts.Cookie.Name,
|
||||
@@ -201,20 +209,10 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr
|
||||
allowedRoutes: allowedRoutes,
|
||||
whitelistDomains: opts.WhitelistDomains,
|
||||
skipAuthPreflight: opts.SkipAuthPreflight,
|
||||
skipAuthStripHeaders: opts.SkipAuthStripHeaders,
|
||||
skipJwtBearerTokens: opts.SkipJwtBearerTokens,
|
||||
mainJwtBearerVerifier: opts.GetOIDCVerifier(),
|
||||
extraJwtBearerVerifiers: opts.GetJWTBearerVerifiers(),
|
||||
realClientIPParser: opts.GetRealClientIPParser(),
|
||||
SetXAuthRequest: opts.SetXAuthRequest,
|
||||
PassBasicAuth: opts.PassBasicAuth,
|
||||
SetBasicAuth: opts.SetBasicAuth,
|
||||
PassUserHeaders: opts.PassUserHeaders,
|
||||
BasicAuthPassword: opts.BasicAuthPassword,
|
||||
PassAccessToken: opts.PassAccessToken,
|
||||
SetAuthorization: opts.SetAuthorization,
|
||||
PassAuthorization: opts.PassAuthorization,
|
||||
PreferEmailToUser: opts.PreferEmailToUser,
|
||||
SkipProviderButton: opts.SkipProviderButton,
|
||||
templates: templates,
|
||||
trustedIPs: trustedIPs,
|
||||
@@ -226,12 +224,46 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr
|
||||
basicAuthValidator: basicAuthValidator,
|
||||
displayHtpasswdForm: basicAuthValidator != nil,
|
||||
sessionChain: sessionChain,
|
||||
headersChain: headersChain,
|
||||
preAuthChain: preAuthChain,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func buildSessionChain(opts *options.Options, sessionStore sessionsapi.SessionStore, validator basic.Validator) alice.Chain {
|
||||
// buildPreAuthChain constructs a chain that should process every request before
|
||||
// the OAuth2 Proxy authentication logic kicks in.
|
||||
// For example forcing HTTPS or health checks.
|
||||
func buildPreAuthChain(opts *options.Options) (alice.Chain, error) {
|
||||
chain := alice.New(middleware.NewScope())
|
||||
|
||||
if opts.ForceHTTPS {
|
||||
_, httpsPort, err := net.SplitHostPort(opts.HTTPSAddress)
|
||||
if err != nil {
|
||||
return alice.Chain{}, fmt.Errorf("invalid HTTPS address %q: %v", opts.HTTPAddress, err)
|
||||
}
|
||||
chain = chain.Append(middleware.NewRedirectToHTTPS(httpsPort))
|
||||
}
|
||||
|
||||
healthCheckPaths := []string{opts.PingPath}
|
||||
healthCheckUserAgents := []string{opts.PingUserAgent}
|
||||
if opts.GCPHealthChecks {
|
||||
healthCheckPaths = append(healthCheckPaths, "/liveness_check", "/readiness_check")
|
||||
healthCheckUserAgents = append(healthCheckUserAgents, "GoogleHC/1.0")
|
||||
}
|
||||
|
||||
// To silence logging of health checks, register the health check handler before
|
||||
// the logging handler
|
||||
if opts.Logging.SilencePing {
|
||||
chain = chain.Append(middleware.NewHealthCheck(healthCheckPaths, healthCheckUserAgents), LoggingHandler)
|
||||
} else {
|
||||
chain = chain.Append(LoggingHandler, middleware.NewHealthCheck(healthCheckPaths, healthCheckUserAgents))
|
||||
}
|
||||
|
||||
return chain, nil
|
||||
}
|
||||
|
||||
func buildSessionChain(opts *options.Options, sessionStore sessionsapi.SessionStore, validator basic.Validator) alice.Chain {
|
||||
chain := alice.New()
|
||||
|
||||
if opts.SkipJwtBearerTokens {
|
||||
sessionLoaders := []middlewareapi.TokenToSessionLoader{}
|
||||
if opts.GetOIDCVerifier() != nil {
|
||||
@@ -264,6 +296,20 @@ func buildSessionChain(opts *options.Options, sessionStore sessionsapi.SessionSt
|
||||
return chain
|
||||
}
|
||||
|
||||
func buildHeadersChain(opts *options.Options) (alice.Chain, error) {
|
||||
requestInjector, err := middleware.NewRequestHeaderInjector(opts.InjectRequestHeaders)
|
||||
if err != nil {
|
||||
return alice.Chain{}, fmt.Errorf("error constructing request header injector: %v", err)
|
||||
}
|
||||
|
||||
responseInjector, err := middleware.NewResponseHeaderInjector(opts.InjectResponseHeaders)
|
||||
if err != nil {
|
||||
return alice.Chain{}, fmt.Errorf("error constructing request header injector: %v", err)
|
||||
}
|
||||
|
||||
return alice.New(requestInjector, responseInjector), nil
|
||||
}
|
||||
|
||||
func buildSignInMessage(opts *options.Options) string {
|
||||
var msg string
|
||||
if len(opts.Banner) >= 1 {
|
||||
@@ -685,6 +731,10 @@ func (p *OAuthProxy) IsTrustedIP(req *http.Request) bool {
|
||||
}
|
||||
|
||||
func (p *OAuthProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
p.preAuthChain.Then(http.HandlerFunc(p.serveHTTP)).ServeHTTP(rw, req)
|
||||
}
|
||||
|
||||
func (p *OAuthProxy) serveHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.URL.Path != p.AuthOnlyPath && strings.HasPrefix(req.URL.Path, p.ProxyPrefix) {
|
||||
prepareNoCache(rw)
|
||||
}
|
||||
@@ -884,15 +934,14 @@ func (p *OAuthProxy) AuthenticateOnly(rw http.ResponseWriter, req *http.Request)
|
||||
|
||||
// we are authenticated
|
||||
p.addHeadersForProxying(rw, req, session)
|
||||
rw.WriteHeader(http.StatusAccepted)
|
||||
p.headersChain.Then(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
rw.WriteHeader(http.StatusAccepted)
|
||||
})).ServeHTTP(rw, req)
|
||||
}
|
||||
|
||||
// SkipAuthProxy proxies allowlisted requests and skips authentication
|
||||
func (p *OAuthProxy) SkipAuthProxy(rw http.ResponseWriter, req *http.Request) {
|
||||
if p.skipAuthStripHeaders {
|
||||
p.stripAuthHeaders(req)
|
||||
}
|
||||
p.serveMux.ServeHTTP(rw, req)
|
||||
p.headersChain.Then(p.serveMux).ServeHTTP(rw, req)
|
||||
}
|
||||
|
||||
// Proxy proxies the user request if the user is authenticated else it prompts
|
||||
@@ -903,8 +952,7 @@ func (p *OAuthProxy) Proxy(rw http.ResponseWriter, req *http.Request) {
|
||||
case nil:
|
||||
// we are authenticated
|
||||
p.addHeadersForProxying(rw, req, session)
|
||||
p.serveMux.ServeHTTP(rw, req)
|
||||
|
||||
p.headersChain.Then(p.serveMux).ServeHTTP(rw, req)
|
||||
case ErrNeedsLogin:
|
||||
// we need to send the user to a login screen
|
||||
if isAjax(req) {
|
||||
@@ -961,120 +1009,6 @@ func (p *OAuthProxy) getAuthenticatedSession(rw http.ResponseWriter, req *http.R
|
||||
|
||||
// addHeadersForProxying adds the appropriate headers the request / response for proxying
|
||||
func (p *OAuthProxy) addHeadersForProxying(rw http.ResponseWriter, req *http.Request, session *sessionsapi.SessionState) {
|
||||
if p.PassBasicAuth {
|
||||
if p.PreferEmailToUser && session.Email != "" {
|
||||
req.SetBasicAuth(session.Email, p.BasicAuthPassword)
|
||||
req.Header["X-Forwarded-User"] = []string{session.Email}
|
||||
req.Header.Del("X-Forwarded-Email")
|
||||
} else {
|
||||
req.SetBasicAuth(session.User, p.BasicAuthPassword)
|
||||
req.Header["X-Forwarded-User"] = []string{session.User}
|
||||
if session.Email != "" {
|
||||
req.Header["X-Forwarded-Email"] = []string{session.Email}
|
||||
} else {
|
||||
req.Header.Del("X-Forwarded-Email")
|
||||
}
|
||||
}
|
||||
if session.PreferredUsername != "" {
|
||||
req.Header["X-Forwarded-Preferred-Username"] = []string{session.PreferredUsername}
|
||||
} else {
|
||||
req.Header.Del("X-Forwarded-Preferred-Username")
|
||||
}
|
||||
}
|
||||
|
||||
if p.PassUserHeaders {
|
||||
if p.PreferEmailToUser && session.Email != "" {
|
||||
req.Header["X-Forwarded-User"] = []string{session.Email}
|
||||
req.Header.Del("X-Forwarded-Email")
|
||||
} else {
|
||||
req.Header["X-Forwarded-User"] = []string{session.User}
|
||||
if session.Email != "" {
|
||||
req.Header["X-Forwarded-Email"] = []string{session.Email}
|
||||
} else {
|
||||
req.Header.Del("X-Forwarded-Email")
|
||||
}
|
||||
}
|
||||
|
||||
if session.PreferredUsername != "" {
|
||||
req.Header["X-Forwarded-Preferred-Username"] = []string{session.PreferredUsername}
|
||||
} else {
|
||||
req.Header.Del("X-Forwarded-Preferred-Username")
|
||||
}
|
||||
|
||||
if len(session.Groups) > 0 {
|
||||
for _, group := range session.Groups {
|
||||
req.Header.Add("X-Forwarded-Groups", group)
|
||||
}
|
||||
} else {
|
||||
req.Header.Del("X-Forwarded-Groups")
|
||||
}
|
||||
}
|
||||
|
||||
if p.SetXAuthRequest {
|
||||
rw.Header().Set("X-Auth-Request-User", session.User)
|
||||
if session.Email != "" {
|
||||
rw.Header().Set("X-Auth-Request-Email", session.Email)
|
||||
} else {
|
||||
rw.Header().Del("X-Auth-Request-Email")
|
||||
}
|
||||
if session.PreferredUsername != "" {
|
||||
rw.Header().Set("X-Auth-Request-Preferred-Username", session.PreferredUsername)
|
||||
} else {
|
||||
rw.Header().Del("X-Auth-Request-Preferred-Username")
|
||||
}
|
||||
|
||||
if p.PassAccessToken {
|
||||
if session.AccessToken != "" {
|
||||
rw.Header().Set("X-Auth-Request-Access-Token", session.AccessToken)
|
||||
} else {
|
||||
rw.Header().Del("X-Auth-Request-Access-Token")
|
||||
}
|
||||
}
|
||||
|
||||
if len(session.Groups) > 0 {
|
||||
for _, group := range session.Groups {
|
||||
rw.Header().Add("X-Auth-Request-Groups", group)
|
||||
}
|
||||
} else {
|
||||
rw.Header().Del("X-Auth-Request-Groups")
|
||||
}
|
||||
}
|
||||
|
||||
if p.PassAccessToken {
|
||||
if session.AccessToken != "" {
|
||||
req.Header["X-Forwarded-Access-Token"] = []string{session.AccessToken}
|
||||
} else {
|
||||
req.Header.Del("X-Forwarded-Access-Token")
|
||||
}
|
||||
}
|
||||
|
||||
if p.PassAuthorization {
|
||||
if session.IDToken != "" {
|
||||
req.Header["Authorization"] = []string{fmt.Sprintf("Bearer %s", session.IDToken)}
|
||||
} else {
|
||||
req.Header.Del("Authorization")
|
||||
}
|
||||
}
|
||||
if p.SetBasicAuth {
|
||||
switch {
|
||||
case p.PreferEmailToUser && session.Email != "":
|
||||
authVal := b64.StdEncoding.EncodeToString([]byte(session.Email + ":" + p.BasicAuthPassword))
|
||||
rw.Header().Set("Authorization", "Basic "+authVal)
|
||||
case session.User != "":
|
||||
authVal := b64.StdEncoding.EncodeToString([]byte(session.User + ":" + p.BasicAuthPassword))
|
||||
rw.Header().Set("Authorization", "Basic "+authVal)
|
||||
default:
|
||||
rw.Header().Del("Authorization")
|
||||
}
|
||||
}
|
||||
if p.SetAuthorization {
|
||||
if session.IDToken != "" {
|
||||
rw.Header().Set("Authorization", fmt.Sprintf("Bearer %s", session.IDToken))
|
||||
} else {
|
||||
rw.Header().Del("Authorization")
|
||||
}
|
||||
}
|
||||
|
||||
if session.Email == "" {
|
||||
rw.Header().Set("GAP-Auth", session.User)
|
||||
} else {
|
||||
@@ -1082,32 +1016,6 @@ func (p *OAuthProxy) addHeadersForProxying(rw http.ResponseWriter, req *http.Req
|
||||
}
|
||||
}
|
||||
|
||||
// stripAuthHeaders removes Auth headers for allowlisted routes from skipAuthRegex
|
||||
func (p *OAuthProxy) stripAuthHeaders(req *http.Request) {
|
||||
if p.PassBasicAuth {
|
||||
req.Header.Del("X-Forwarded-User")
|
||||
req.Header.Del("X-Forwarded-Groups")
|
||||
req.Header.Del("X-Forwarded-Email")
|
||||
req.Header.Del("X-Forwarded-Preferred-Username")
|
||||
req.Header.Del("Authorization")
|
||||
}
|
||||
|
||||
if p.PassUserHeaders {
|
||||
req.Header.Del("X-Forwarded-User")
|
||||
req.Header.Del("X-Forwarded-Groups")
|
||||
req.Header.Del("X-Forwarded-Email")
|
||||
req.Header.Del("X-Forwarded-Preferred-Username")
|
||||
}
|
||||
|
||||
if p.PassAccessToken {
|
||||
req.Header.Del("X-Forwarded-Access-Token")
|
||||
}
|
||||
|
||||
if p.PassAuthorization {
|
||||
req.Header.Del("Authorization")
|
||||
}
|
||||
}
|
||||
|
||||
// isAjax checks if a request is an ajax request
|
||||
func isAjax(req *http.Request) bool {
|
||||
acceptValues := req.Header.Values("Accept")
|
||||
|
||||
Reference in New Issue
Block a user