From 9aacb17a4db504c53f6ba97eb17497e731b899ed Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 20 Jul 2026 22:31:07 +0000 Subject: [PATCH] Pin X-Forwarded-Host on the Authelia portal's own Caddy block The generated auth. block's bare "reverse_proxy authelia:9091" let Caddy recompute X-Forwarded-Host from its own incoming request (always auth. itself) on every hop through it, overwriting whatever a forward_auth caller elsewhere had already set for its own domain. Confirmed live: a remote site's forward_auth check always evaluated as if it were for the Authelia portal itself (bypass policy), so 2FA silently never triggered for any domain going through it. --- services/authelia.sh | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/services/authelia.sh b/services/authelia.sh index 602cbf3..974668b 100644 --- a/services/authelia.sh +++ b/services/authelia.sh @@ -414,7 +414,20 @@ SNIPPET_EOF # ── Authelia login portal ────────────────────────────────────────────────────── auth.${AUTHELIA_DOMAIN} { - reverse_proxy authelia:9091 + # header_up pins X-Forwarded-Host to whatever the client actually sent. + # Without it, Caddy's reverse_proxy recomputes X-Forwarded-Host from its + # own incoming request (always auth.${AUTHELIA_DOMAIN} itself) and + # overwrites the value a forward_auth caller (e.g. a remote site's + # "forward_auth https://auth.${AUTHELIA_DOMAIN}" block, see + # services/asterisk-digital-ocean.sh) set for its own domain. Confirmed + # live: every forward-auth check evaluated as if it were for + # auth.${AUTHELIA_DOMAIN} itself (which has policy: bypass in + # access_control.rules so its own login portal isn't gated behind + # itself), so every domain behind it silently passed through with no + # 2FA prompt regardless of that domain's own policy. + reverse_proxy authelia:9091 { + header_up X-Forwarded-Host {http.request.header.X-Forwarded-Host} + } log { output file /var/log/caddy/auth.log }