Skip to content

Commit

Permalink
forwardauth: Skip copying missing response headers
Browse files Browse the repository at this point in the history
  • Loading branch information
francislavoie committed Oct 7, 2024
1 parent d7564d6 commit 8bce42e
Show file tree
Hide file tree
Showing 3 changed files with 267 additions and 34 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
app.example.com {
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
}

Expand Down Expand Up @@ -39,6 +39,13 @@ app.example.com {
]
},
"routes": [
{
"handle": [
{
"handler": "vars"
}
]
},
{
"handle": [
{
Expand All @@ -47,19 +54,104 @@ app.example.com {
"set": {
"Remote-Email": [
"{http.reverse_proxy.header.Remote-Email}"
],
]
}
}
}
],
"match": [
{
"not": [
{
"vars": {
"{http.reverse_proxy.header.Remote-Email}": [
""
]
}
}
]
}
]
},
{
"handle": [
{
"handler": "headers",
"request": {
"set": {
"Remote-Groups": [
"{http.reverse_proxy.header.Remote-Groups}"
],
]
}
}
}
],
"match": [
{
"not": [
{
"vars": {
"{http.reverse_proxy.header.Remote-Groups}": [
""
]
}
}
]
}
]
},
{
"handle": [
{
"handler": "headers",
"request": {
"set": {
"Remote-Name": [
"{http.reverse_proxy.header.Remote-Name}"
],
]
}
}
}
],
"match": [
{
"not": [
{
"vars": {
"{http.reverse_proxy.header.Remote-Name}": [
""
]
}
}
]
}
]
},
{
"handle": [
{
"handler": "headers",
"request": {
"set": {
"Remote-User": [
"{http.reverse_proxy.header.Remote-User}"
]
}
}
}
],
"match": [
{
"not": [
{
"vars": {
"{http.reverse_proxy.header.Remote-User}": [
""
]
}
}
]
}
]
}
]
Expand All @@ -80,7 +172,7 @@ app.example.com {
},
"rewrite": {
"method": "GET",
"uri": "/api/verify?rd=https://authelia.example.com"
"uri": "/api/authz/forward-auth"
},
"upstreams": [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ forward_auth localhost:9000 {
]
},
"routes": [
{
"handle": [
{
"handler": "vars"
}
]
},
{
"handle": [
{
Expand All @@ -36,22 +43,131 @@ forward_auth localhost:9000 {
"set": {
"1": [
"{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": [
"{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": [
"{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}": [
""
]
}
}
]
}
]
}
]
Expand Down
67 changes: 46 additions & 21 deletions modules/caddyhttp/reverseproxy/forwardauth/caddyfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package forwardauth
import (
"encoding/json"
"net/http"
"sort"
"strings"

"github.com/caddyserver/caddy/v2"
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 8bce42e

Please sign in to comment.