@@ -48,6 +48,12 @@ var (
48 invalidRedirectRegex = regexp.MustCompile(`[/\\](?:[\s\v]*|\.{1,2})[/\\]`)
48 invalidRedirectRegex = regexp.MustCompile(`[/\\](?:[\s\v]*|\.{1,2})[/\\]`)
51+// allowedRoute manages method + path based allowlists
52+type allowedRoute struct {
54+ pathRegex *regexp.Regexp
51// OAuthProxy is the main authentication proxy
57// OAuthProxy is the main authentication proxy
52type OAuthProxy struct {
58type OAuthProxy struct {
53 CookieSeed string
59 CookieSeed string
@@ -70,6 +76,7 @@ type OAuthProxy struct {
70 AuthOnlyPath string
76 AuthOnlyPath string
71 UserInfoPath string
77 UserInfoPath string
79+ allowedRoutes []*allowedRoute
73 redirectURL *url.URL // the url to receive requests at
80 redirectURL *url.URL // the url to receive requests at
74 whitelistDomains []string
81 whitelistDomains []string
75 provider providers.Provider
82 provider providers.Provider
@@ -90,13 +97,11 @@ type OAuthProxy struct {
90 SetAuthorization bool
97 SetAuthorization bool
91 PassAuthorization bool
98 PassAuthorization bool
92 PreferEmailToUser bool
99 PreferEmailToUser bool
93− skipAuthRegex []string
94 skipAuthPreflight bool
100 skipAuthPreflight bool
95 skipAuthStripHeaders bool
101 skipAuthStripHeaders bool
96 skipJwtBearerTokens bool
102 skipJwtBearerTokens bool
97 mainJwtBearerVerifier *oidc.IDTokenVerifier
103 mainJwtBearerVerifier *oidc.IDTokenVerifier
98 extraJwtBearerVerifiers []*oidc.IDTokenVerifier
104 extraJwtBearerVerifiers []*oidc.IDTokenVerifier
99− compiledRegex []*regexp.Regexp
100 templates *template.Template
105 templates *template.Template
101 realClientIPParser ipapi.RealClientIPParser
106 realClientIPParser ipapi.RealClientIPParser
102 trustedIPs *ip.NetSet
107 trustedIPs *ip.NetSet
@@ -121,10 +126,6 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr
121 return nil, fmt.Errorf("error initialising upstream proxy: %v", err)
126 return nil, fmt.Errorf("error initialising upstream proxy: %v", err)
124− for _, u := range opts.GetCompiledRegex() {
125− logger.Printf("compiled skip-auth-regex => %q", u)
128 if opts.SkipJwtBearerTokens {
129 if opts.SkipJwtBearerTokens {
129 logger.Printf("Skipping JWT tokens from configured OIDC issuer: %q", opts.OIDCIssuerURL)
130 logger.Printf("Skipping JWT tokens from configured OIDC issuer: %q", opts.OIDCIssuerURL)
130 for _, issuer := range opts.ExtraJwtIssuers {
131 for _, issuer := range opts.ExtraJwtIssuers {
@@ -163,6 +164,11 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr
167+ allowedRoutes, err := buildRoutesAllowlist(opts)
166 sessionChain := buildSessionChain(opts, sessionStore, basicAuthValidator)
172 sessionChain := buildSessionChain(opts, sessionStore, basicAuthValidator)
168 return &OAuthProxy{
174 return &OAuthProxy{
@@ -192,14 +198,13 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr
192 sessionStore: sessionStore,
198 sessionStore: sessionStore,
193 serveMux: upstreamProxy,
199 serveMux: upstreamProxy,
194 redirectURL: redirectURL,
200 redirectURL: redirectURL,
201+ allowedRoutes: allowedRoutes,
195 whitelistDomains: opts.WhitelistDomains,
202 whitelistDomains: opts.WhitelistDomains,
196− skipAuthRegex: opts.SkipAuthRegex,
197 skipAuthPreflight: opts.SkipAuthPreflight,
203 skipAuthPreflight: opts.SkipAuthPreflight,
198 skipAuthStripHeaders: opts.SkipAuthStripHeaders,
204 skipAuthStripHeaders: opts.SkipAuthStripHeaders,
199 skipJwtBearerTokens: opts.SkipJwtBearerTokens,
205 skipJwtBearerTokens: opts.SkipJwtBearerTokens,
200 mainJwtBearerVerifier: opts.GetOIDCVerifier(),
206 mainJwtBearerVerifier: opts.GetOIDCVerifier(),
201 extraJwtBearerVerifiers: opts.GetJWTBearerVerifiers(),
207 extraJwtBearerVerifiers: opts.GetJWTBearerVerifiers(),
202− compiledRegex: opts.GetCompiledRegex(),
203 realClientIPParser: opts.GetRealClientIPParser(),
208 realClientIPParser: opts.GetRealClientIPParser(),
204 SetXAuthRequest: opts.SetXAuthRequest,
209 SetXAuthRequest: opts.SetXAuthRequest,
205 PassBasicAuth: opts.PassBasicAuth,
210 PassBasicAuth: opts.PassBasicAuth,
@@ -277,6 +282,51 @@ func buildSignInMessage(opts *options.Options) string {
277 return msg
282 return msg
285+// buildRoutesAllowlist builds an []allowedRoute list from either the legacy
286+// SkipAuthRegex option (paths only support) or newer SkipAuthRoutes option
287+// (method=path support)
288+func buildRoutesAllowlist(opts *options.Options) ([]*allowedRoute, error) {
289+ var routes []*allowedRoute
291+ for _, path := range opts.SkipAuthRegex {
292+ compiledRegex, err := regexp.Compile(path)
296+ routes = append(routes, &allowedRoute{
298+ pathRegex: compiledRegex,
302+ for _, methodPath := range opts.SkipAuthRoutes {
308+ parts := strings.Split(methodPath, "=")
309+ if len(parts) == 1 {
313+ method = strings.ToUpper(parts[0])
314+ path = strings.Join(parts[1:], "=")
317+ compiledRegex, err := regexp.Compile(path)
321+ routes = append(routes, &allowedRoute{
323+ pathRegex: compiledRegex,
280// GetRedirectURI returns the redirectURL that the upstream OAuth Provider will
330// GetRedirectURI returns the redirectURL that the upstream OAuth Provider will
281// redirect clients to once authenticated
331// redirect clients to once authenticated
282func (p *OAuthProxy) GetRedirectURI(host string) string {
332func (p *OAuthProxy) GetRedirectURI(host string) string {
@@ -584,16 +634,16 @@ func (p *OAuthProxy) IsValidRedirect(redirect string) bool {
587−// IsWhitelistedRequest is used to check if auth should be skipped for this request
637+// IsAllowedRequest is used to check if auth should be skipped for this request
588−func (p *OAuthProxy) IsWhitelistedRequest(req *http.Request) bool {
638+func (p *OAuthProxy) IsAllowedRequest(req *http.Request) bool {
589 isPreflightRequestAllowed := p.skipAuthPreflight && req.Method == "OPTIONS"
639 isPreflightRequestAllowed := p.skipAuthPreflight && req.Method == "OPTIONS"
590− return isPreflightRequestAllowed || p.IsWhitelistedPath(req.URL.Path) || p.IsTrustedIP(req)
640+ return isPreflightRequestAllowed || p.isAllowedRoute(req) || p.IsTrustedIP(req)
593−// IsWhitelistedPath is used to check if the request path is allowed without auth
643+// IsAllowedRoute is used to check if the request method & path is allowed without auth
594−func (p *OAuthProxy) IsWhitelistedPath(path string) bool {
644+func (p *OAuthProxy) isAllowedRoute(req *http.Request) bool {
595− for _, u := range p.compiledRegex {
645+ for _, route := range p.allowedRoutes {
596− if u.MatchString(path) {
646+ if (route.method == "" || req.Method == route.method) && route.pathRegex.MatchString(req.URL.Path) {
597 return true
647 return true
@@ -643,7 +693,7 @@ func (p *OAuthProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
643 switch path := req.URL.Path; {
693 switch path := req.URL.Path; {
644 case path == p.RobotsPath:
694 case path == p.RobotsPath:
645 p.RobotsTxt(rw)
695 p.RobotsTxt(rw)
646− case p.IsWhitelistedRequest(req):
696+ case p.IsAllowedRequest(req):
647 p.SkipAuthProxy(rw, req)
697 p.SkipAuthProxy(rw, req)
648 case path == p.SignInPath:
698 case path == p.SignInPath:
649 p.SignIn(rw, req)
699 p.SignIn(rw, req)
@@ -831,7 +881,7 @@ func (p *OAuthProxy) AuthenticateOnly(rw http.ResponseWriter, req *http.Request)
831 rw.WriteHeader(http.StatusAccepted)
881 rw.WriteHeader(http.StatusAccepted)
834−// SkipAuthProxy proxies whitelisted requests and skips authentication
884+// SkipAuthProxy proxies allowlisted requests and skips authentication
835func (p *OAuthProxy) SkipAuthProxy(rw http.ResponseWriter, req *http.Request) {
885func (p *OAuthProxy) SkipAuthProxy(rw http.ResponseWriter, req *http.Request) {
836 if p.skipAuthStripHeaders {
886 if p.skipAuthStripHeaders {
837 p.stripAuthHeaders(req)
887 p.stripAuthHeaders(req)
@@ -1026,7 +1076,7 @@ func (p *OAuthProxy) addHeadersForProxying(rw http.ResponseWriter, req *http.Req
1029−// stripAuthHeaders removes Auth headers for whitelisted routes from skipAuthRegex
1079+// stripAuthHeaders removes Auth headers for allowlisted routes from skipAuthRegex
1030func (p *OAuthProxy) stripAuthHeaders(req *http.Request) {
1080func (p *OAuthProxy) stripAuthHeaders(req *http.Request) {
1031 if p.PassBasicAuth {
1081 if p.PassBasicAuth {
1032 req.Header.Del("X-Forwarded-User")
1082 req.Header.Del("X-Forwarded-User")