mirror of
https://github.com/caddyserver/caddy.git
synced 2024-12-16 21:56:40 -05:00
forwardauth: Skip copying missing response headers (#6608)
This commit is contained in:
parent
00f948c605
commit
05cfb121ec
3 changed files with 267 additions and 34 deletions
|
@ -1,6 +1,6 @@
|
||||||
app.example.com {
|
app.example.com {
|
||||||
forward_auth authelia:9091 {
|
forward_auth authelia:9091 {
|
||||||
uri /api/verify?rd=https://authelia.example.com
|
uri /api/authz/forward-auth
|
||||||
copy_headers Remote-User Remote-Groups Remote-Name Remote-Email
|
copy_headers Remote-User Remote-Groups Remote-Name Remote-Email
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,6 +39,13 @@ app.example.com {
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"routes": [
|
"routes": [
|
||||||
|
{
|
||||||
|
"handle": [
|
||||||
|
{
|
||||||
|
"handler": "vars"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"handle": [
|
"handle": [
|
||||||
{
|
{
|
||||||
|
@ -47,19 +54,104 @@ app.example.com {
|
||||||
"set": {
|
"set": {
|
||||||
"Remote-Email": [
|
"Remote-Email": [
|
||||||
"{http.reverse_proxy.header.Remote-Email}"
|
"{http.reverse_proxy.header.Remote-Email}"
|
||||||
],
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"match": [
|
||||||
|
{
|
||||||
|
"not": [
|
||||||
|
{
|
||||||
|
"vars": {
|
||||||
|
"{http.reverse_proxy.header.Remote-Email}": [
|
||||||
|
""
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": [
|
||||||
|
{
|
||||||
|
"handler": "headers",
|
||||||
|
"request": {
|
||||||
|
"set": {
|
||||||
"Remote-Groups": [
|
"Remote-Groups": [
|
||||||
"{http.reverse_proxy.header.Remote-Groups}"
|
"{http.reverse_proxy.header.Remote-Groups}"
|
||||||
],
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"match": [
|
||||||
|
{
|
||||||
|
"not": [
|
||||||
|
{
|
||||||
|
"vars": {
|
||||||
|
"{http.reverse_proxy.header.Remote-Groups}": [
|
||||||
|
""
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": [
|
||||||
|
{
|
||||||
|
"handler": "headers",
|
||||||
|
"request": {
|
||||||
|
"set": {
|
||||||
"Remote-Name": [
|
"Remote-Name": [
|
||||||
"{http.reverse_proxy.header.Remote-Name}"
|
"{http.reverse_proxy.header.Remote-Name}"
|
||||||
],
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"match": [
|
||||||
|
{
|
||||||
|
"not": [
|
||||||
|
{
|
||||||
|
"vars": {
|
||||||
|
"{http.reverse_proxy.header.Remote-Name}": [
|
||||||
|
""
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": [
|
||||||
|
{
|
||||||
|
"handler": "headers",
|
||||||
|
"request": {
|
||||||
|
"set": {
|
||||||
"Remote-User": [
|
"Remote-User": [
|
||||||
"{http.reverse_proxy.header.Remote-User}"
|
"{http.reverse_proxy.header.Remote-User}"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"match": [
|
||||||
|
{
|
||||||
|
"not": [
|
||||||
|
{
|
||||||
|
"vars": {
|
||||||
|
"{http.reverse_proxy.header.Remote-User}": [
|
||||||
|
""
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -80,7 +172,7 @@ app.example.com {
|
||||||
},
|
},
|
||||||
"rewrite": {
|
"rewrite": {
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"uri": "/api/verify?rd=https://authelia.example.com"
|
"uri": "/api/authz/forward-auth"
|
||||||
},
|
},
|
||||||
"upstreams": [
|
"upstreams": [
|
||||||
{
|
{
|
||||||
|
|
|
@ -28,6 +28,13 @@ forward_auth localhost:9000 {
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"routes": [
|
"routes": [
|
||||||
|
{
|
||||||
|
"handle": [
|
||||||
|
{
|
||||||
|
"handler": "vars"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"handle": [
|
"handle": [
|
||||||
{
|
{
|
||||||
|
@ -36,22 +43,131 @@ forward_auth localhost:9000 {
|
||||||
"set": {
|
"set": {
|
||||||
"1": [
|
"1": [
|
||||||
"{http.reverse_proxy.header.A}"
|
"{http.reverse_proxy.header.A}"
|
||||||
],
|
]
|
||||||
"3": [
|
}
|
||||||
"{http.reverse_proxy.header.C}"
|
}
|
||||||
],
|
}
|
||||||
"5": [
|
],
|
||||||
"{http.reverse_proxy.header.E}"
|
"match": [
|
||||||
],
|
{
|
||||||
|
"not": [
|
||||||
|
{
|
||||||
|
"vars": {
|
||||||
|
"{http.reverse_proxy.header.A}": [
|
||||||
|
""
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": [
|
||||||
|
{
|
||||||
|
"handler": "headers",
|
||||||
|
"request": {
|
||||||
|
"set": {
|
||||||
"B": [
|
"B": [
|
||||||
"{http.reverse_proxy.header.B}"
|
"{http.reverse_proxy.header.B}"
|
||||||
],
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"match": [
|
||||||
|
{
|
||||||
|
"not": [
|
||||||
|
{
|
||||||
|
"vars": {
|
||||||
|
"{http.reverse_proxy.header.B}": [
|
||||||
|
""
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": [
|
||||||
|
{
|
||||||
|
"handler": "headers",
|
||||||
|
"request": {
|
||||||
|
"set": {
|
||||||
|
"3": [
|
||||||
|
"{http.reverse_proxy.header.C}"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"match": [
|
||||||
|
{
|
||||||
|
"not": [
|
||||||
|
{
|
||||||
|
"vars": {
|
||||||
|
"{http.reverse_proxy.header.C}": [
|
||||||
|
""
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": [
|
||||||
|
{
|
||||||
|
"handler": "headers",
|
||||||
|
"request": {
|
||||||
|
"set": {
|
||||||
"D": [
|
"D": [
|
||||||
"{http.reverse_proxy.header.D}"
|
"{http.reverse_proxy.header.D}"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"match": [
|
||||||
|
{
|
||||||
|
"not": [
|
||||||
|
{
|
||||||
|
"vars": {
|
||||||
|
"{http.reverse_proxy.header.D}": [
|
||||||
|
""
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": [
|
||||||
|
{
|
||||||
|
"handler": "headers",
|
||||||
|
"request": {
|
||||||
|
"set": {
|
||||||
|
"5": [
|
||||||
|
"{http.reverse_proxy.header.E}"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"match": [
|
||||||
|
{
|
||||||
|
"not": [
|
||||||
|
{
|
||||||
|
"vars": {
|
||||||
|
"{http.reverse_proxy.header.E}": [
|
||||||
|
""
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -17,6 +17,7 @@ package forwardauth
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/caddyserver/caddy/v2"
|
"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")
|
return nil, dispenser.Errf("the 'uri' subdirective is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
// set up handler for good responses; when a response
|
// Set up handler for good responses; when a response has 2xx status,
|
||||||
// has 2xx status, then we will copy some headers from
|
// then we will copy some headers from the response onto the original
|
||||||
// the response onto the original request, and allow
|
// request, and allow handling to continue down the middleware chain,
|
||||||
// handling to continue down the middleware chain,
|
// by _not_ executing a terminal handler. We must have at least one
|
||||||
// by _not_ executing a terminal handler.
|
// 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{
|
goodResponseHandler := caddyhttp.ResponseHandler{
|
||||||
Match: &caddyhttp.ResponseMatcher{
|
Match: &caddyhttp.ResponseMatcher{
|
||||||
StatusCode: []int{2},
|
StatusCode: []int{2},
|
||||||
},
|
},
|
||||||
Routes: []caddyhttp.Route{},
|
Routes: []caddyhttp.Route{
|
||||||
}
|
{
|
||||||
|
HandlersRaw: []json.RawMessage{caddyconfig.JSONModuleObject(
|
||||||
handler := &headers.Handler{
|
&caddyhttp.VarsMiddleware{},
|
||||||
Request: &headers.HeaderOps{
|
"handler",
|
||||||
Set: http.Header{},
|
"vars",
|
||||||
|
nil,
|
||||||
|
)},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// the list of headers to copy may be empty, but that's okay; we
|
// Sort the headers so that the order in the JSON output is deterministic.
|
||||||
// need at least one handler in the routes for the response handling
|
sortedHeadersToCopy := make([]string, 0, len(headersToCopy))
|
||||||
// logic in reverse_proxy to not skip this entry as empty.
|
for k := range headersToCopy {
|
||||||
for from, to := range headersToCopy {
|
sortedHeadersToCopy = append(sortedHeadersToCopy, k)
|
||||||
handler.Request.Set.Set(to, "{http.reverse_proxy.header."+http.CanonicalHeaderKey(from)+"}")
|
|
||||||
}
|
}
|
||||||
|
sort.Strings(sortedHeadersToCopy)
|
||||||
|
|
||||||
goodResponseHandler.Routes = append(
|
// Set up handlers to copy headers from the auth response onto the
|
||||||
goodResponseHandler.Routes,
|
// original request. We use vars matchers to test that the placeholder
|
||||||
caddyhttp.Route{
|
// 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(
|
HandlersRaw: []json.RawMessage{caddyconfig.JSONModuleObject(
|
||||||
handler,
|
handler,
|
||||||
"handler",
|
"handler",
|
||||||
"headers",
|
"headers",
|
||||||
nil,
|
nil,
|
||||||
)},
|
)},
|
||||||
},
|
})
|
||||||
)
|
}
|
||||||
|
|
||||||
|
goodResponseHandler.Routes = append(goodResponseHandler.Routes, copyHeaderRoutes...)
|
||||||
|
|
||||||
// note that when a response has any other status than 2xx, then we
|
// note that when a response has any other status than 2xx, then we
|
||||||
// use the reverse proxy's default behaviour of copying the response
|
// use the reverse proxy's default behaviour of copying the response
|
||||||
|
|
Loading…
Reference in a new issue