diff options
Diffstat (limited to 'modules/caddyhttp/reverseproxy/forwardauth/caddyfile.go')
-rw-r--r-- | modules/caddyhttp/reverseproxy/forwardauth/caddyfile.go | 67 |
1 files changed, 46 insertions, 21 deletions
diff --git a/modules/caddyhttp/reverseproxy/forwardauth/caddyfile.go b/modules/caddyhttp/reverseproxy/forwardauth/caddyfile.go index 8350096ae..347f6dfbf 100644 --- a/modules/caddyhttp/reverseproxy/forwardauth/caddyfile.go +++ b/modules/caddyhttp/reverseproxy/forwardauth/caddyfile.go @@ -17,6 +17,7 @@ package forwardauth import ( "encoding/json" "net/http" + "sort" "strings" "github.com/caddyserver/caddy/v2" @@ -170,42 +171,66 @@ func parseCaddyfile(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error) return nil, dispenser.Errf("the 'uri' subdirective is required") } - // set up handler for good responses; when a response - // has 2xx status, then we will copy some headers from - // the response onto the original request, and allow - // handling to continue down the middleware chain, - // by _not_ executing a terminal handler. + // Set up handler for good responses; when a response has 2xx status, + // then we will copy some headers from the response onto the original + // request, and allow handling to continue down the middleware chain, + // by _not_ executing a terminal handler. We must have at least one + // route in the response handler, even if it's no-op, so that the + // response handling logic in reverse_proxy doesn't skip this entry. goodResponseHandler := caddyhttp.ResponseHandler{ Match: &caddyhttp.ResponseMatcher{ StatusCode: []int{2}, }, - Routes: []caddyhttp.Route{}, - } - - handler := &headers.Handler{ - Request: &headers.HeaderOps{ - Set: http.Header{}, + Routes: []caddyhttp.Route{ + { + HandlersRaw: []json.RawMessage{caddyconfig.JSONModuleObject( + &caddyhttp.VarsMiddleware{}, + "handler", + "vars", + nil, + )}, + }, }, } - // the list of headers to copy may be empty, but that's okay; we - // need at least one handler in the routes for the response handling - // logic in reverse_proxy to not skip this entry as empty. - for from, to := range headersToCopy { - handler.Request.Set.Set(to, "{http.reverse_proxy.header."+http.CanonicalHeaderKey(from)+"}") + // Sort the headers so that the order in the JSON output is deterministic. + sortedHeadersToCopy := make([]string, 0, len(headersToCopy)) + for k := range headersToCopy { + sortedHeadersToCopy = append(sortedHeadersToCopy, k) } + sort.Strings(sortedHeadersToCopy) - goodResponseHandler.Routes = append( - goodResponseHandler.Routes, - caddyhttp.Route{ + // Set up handlers to copy headers from the auth response onto the + // original request. We use vars matchers to test that the placeholder + // values aren't empty, because the header handler would not replace + // placeholders which have no value. + copyHeaderRoutes := []caddyhttp.Route{} + for _, from := range sortedHeadersToCopy { + to := http.CanonicalHeaderKey(headersToCopy[from]) + placeholderName := "http.reverse_proxy.header." + http.CanonicalHeaderKey(from) + handler := &headers.Handler{ + Request: &headers.HeaderOps{ + Set: http.Header{ + to: []string{"{" + placeholderName + "}"}, + }, + }, + } + copyHeaderRoutes = append(copyHeaderRoutes, caddyhttp.Route{ + MatcherSetsRaw: []caddy.ModuleMap{{ + "not": h.JSON(caddyhttp.MatchNot{MatcherSetsRaw: []caddy.ModuleMap{{ + "vars": h.JSON(caddyhttp.VarsMatcher{"{" + placeholderName + "}": []string{""}}), + }}}), + }}, HandlersRaw: []json.RawMessage{caddyconfig.JSONModuleObject( handler, "handler", "headers", nil, )}, - }, - ) + }) + } + + goodResponseHandler.Routes = append(goodResponseHandler.Routes, copyHeaderRoutes...) // note that when a response has any other status than 2xx, then we // use the reverse proxy's default behaviour of copying the response |