Merge pull request #192 from outis1one/claude/asterisk-digital-ocean-w22kk8
Claude/asterisk digital ocean w22kk8
This commit is contained in:
@@ -159,6 +159,51 @@ reloads Caddy. No-ops silently if Caddy isn't installed. The fourth argument
|
||||
is an optional string inserted verbatim inside the Caddy site block (use it
|
||||
for `import authelia` or custom matchers).
|
||||
|
||||
The function places that block **before** `reverse_proxy` in the generated
|
||||
site block — don't reorder this. `forward_auth` (what `import authelia`
|
||||
expands to) is the same directive family as `reverse_proxy` internally, and
|
||||
Caddy doesn't reorder repeats of the same directive within a block; it runs
|
||||
them in the order they're written. `reverse_proxy` written first would
|
||||
handle and terminate every request immediately, making an auth check
|
||||
written after it dead code that never runs — full bypass regardless of what
|
||||
the auth server's own access-control rules say. Confirmed live: this was
|
||||
the actual cause of a "Caddy proxies fine but Authelia never prompts for
|
||||
login" bug, on a site block that otherwise looked completely correct. If a
|
||||
service builds its own site block instead of using this helper (e.g.
|
||||
`services/asterisk-digital-ocean.sh` does, deliberately, see its own
|
||||
comment for why), put its auth block first there too.
|
||||
|
||||
**`forward_auth` to a remote Authelia over a scheme-qualified URL needs
|
||||
explicit `header_up` pins.** A bare `forward_auth authelia:9091` (Authelia on
|
||||
the same Docker network, one hop) is fine relying on Caddy's default
|
||||
`X-Forwarded-*` headers. But `forward_auth https://auth.example.com { ... }`
|
||||
(Authelia on a *different* machine, reached over its own public domain+TLS —
|
||||
see `services/asterisk-digital-ocean.sh`'s remote-Authelia prompt) is a
|
||||
second Caddy hop: Caddy rewrites the outgoing request's `Host` header to
|
||||
`auth.example.com` so the remote Caddy can route/SNI-match it, and without an
|
||||
override `X-Forwarded-Host` picks up that rewritten value instead of the
|
||||
original site's host. Confirmed live: Authelia evaluated *every* protected
|
||||
domain as if the request were for `auth.example.com` itself (which typically
|
||||
has `policy: bypass` in `access_control.rules` so its own login portal isn't
|
||||
gated behind itself) — so every domain behind the remote instance silently
|
||||
passed through with no 2FA prompt, regardless of that domain's own policy.
|
||||
Fix: pin the forwarded headers to the original request explicitly instead of
|
||||
trusting Caddy's default derivation:
|
||||
|
||||
```
|
||||
forward_auth https://auth.example.com {
|
||||
uri /api/authz/forward-auth
|
||||
copy_headers Remote-User Remote-Groups Remote-Name Remote-Email
|
||||
header_up X-Forwarded-Method {method}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-Host {host}
|
||||
header_up X-Forwarded-Uri {uri}
|
||||
}
|
||||
```
|
||||
|
||||
This only affects the remote-Authelia path — same-machine `authelia:9091`
|
||||
snippets (`services/authelia.sh`) are a single hop and don't need it.
|
||||
|
||||
Sets two out-params (not `local` — read them after the call returns) so the
|
||||
caller can tell whether Caddy actually ended up fronting the service:
|
||||
|
||||
|
||||
+8
-1
@@ -573,6 +573,14 @@ configure_caddy_for_service() {
|
||||
|
||||
# $SERVICE_NAME
|
||||
${SERVICE_DOMAIN} {
|
||||
# Auth (if any) must come before reverse_proxy — forward_auth is the
|
||||
# same directive family as reverse_proxy internally, and Caddy doesn't
|
||||
# reorder repeats of the same directive within a block; it runs them in
|
||||
# the order they're written. With reverse_proxy first, it would handle
|
||||
# and terminate every request immediately, so an auth check written
|
||||
# after it would be dead code that never runs — full bypass regardless
|
||||
# of what the auth server's own rules say.
|
||||
${EXTRA_CONFIG}
|
||||
reverse_proxy ${_BLOCK_UPSTREAM}
|
||||
|
||||
# Security headers
|
||||
@@ -588,7 +596,6 @@ ${SERVICE_DOMAIN} {
|
||||
output file /var/log/caddy/${SERVICE_DOMAIN}.log
|
||||
format json
|
||||
}
|
||||
${EXTRA_CONFIG}
|
||||
}
|
||||
CADDY_BLOCK
|
||||
)"
|
||||
|
||||
@@ -584,9 +584,40 @@ ENV
|
||||
local _remote_authelia=""
|
||||
prompt_text " Remote Authelia address — a bare host:port over a private network (e.g. a NetBird mesh IP:9091), or a full https:// URL if it's on its own public domain+TLS:" "" _remote_authelia
|
||||
if [[ -n "$_remote_authelia" ]]; then
|
||||
# header_up lines are required here (unlike the local
|
||||
# "authelia:9091" snippet in services/authelia.sh) because
|
||||
# this upstream is reached over a second Caddy hop when
|
||||
# given as a scheme-qualified URL (https://auth.example.com).
|
||||
# Caddy rewrites the outgoing request's Host header to that
|
||||
# upstream host so the remote Caddy can route/SNI-match it —
|
||||
# and without an explicit override, X-Forwarded-Host picks up
|
||||
# that rewritten value instead of the original site's host.
|
||||
# Confirmed live: Authelia was evaluating every request as
|
||||
# if it were for auth.example.com itself (which has
|
||||
# policy: bypass in access_control.rules), so every domain
|
||||
# silently passed through with no 2FA prompt regardless of
|
||||
# its own policy. Pinning these to the original request's
|
||||
# values fixes it regardless of hop count.
|
||||
#
|
||||
# X-Forwarded-Host uses a literal domain, NOT the {host}
|
||||
# placeholder. Confirmed live: {host} still evaluated to
|
||||
# the upstream's own hostname (auth.example.com) rather
|
||||
# than the original site's — Caddy appears to rewrite the
|
||||
# outgoing request's Host to the upstream target before
|
||||
# header_up placeholders are resolved for a scheme-
|
||||
# qualified upstream, so {host} echoes back the already-
|
||||
# rewritten value instead of the original client-facing
|
||||
# host. Since this site block only ever serves one domain
|
||||
# (DOMAIN_NAME), hardcoding it sidesteps the ambiguity
|
||||
# entirely instead of depending on Caddy's internal
|
||||
# header-mutation ordering.
|
||||
EXTRA_BLOCK=" forward_auth ${_remote_authelia} {
|
||||
uri /api/authz/forward-auth
|
||||
copy_headers Remote-User Remote-Groups Remote-Name Remote-Email
|
||||
header_up X-Forwarded-Method {method}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-Host ${DOMAIN_NAME}
|
||||
header_up X-Forwarded-Uri {uri}
|
||||
}"
|
||||
sed -i "s/^WEB_ADMIN_AUTH_DISABLED=.*/WEB_ADMIN_AUTH_DISABLED=true/" .env
|
||||
log_info "Using remote Authelia at ${_remote_authelia}."
|
||||
@@ -628,6 +659,14 @@ ENV
|
||||
|
||||
# Asterisk Web Admin
|
||||
${DOMAIN_NAME} {
|
||||
# Auth (if any) must come before reverse_proxy — forward_auth is the
|
||||
# same directive family as reverse_proxy internally, and Caddy doesn't
|
||||
# reorder repeats of the same directive within a block; it runs them in
|
||||
# the order they're written. With reverse_proxy first, it would handle
|
||||
# and terminate every request immediately, so an auth check written
|
||||
# after it would be dead code that never runs — full bypass regardless
|
||||
# of what the auth server's own rules say.
|
||||
${EXTRA_BLOCK}
|
||||
reverse_proxy ${_PROXY_TARGET}
|
||||
|
||||
header {
|
||||
@@ -641,7 +680,6 @@ ${DOMAIN_NAME} {
|
||||
output file /var/log/caddy/${DOMAIN_NAME}.log
|
||||
format json
|
||||
}
|
||||
${EXTRA_BLOCK}
|
||||
}
|
||||
CADDY_BLOCK
|
||||
)"
|
||||
|
||||
+14
-1
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user