feat: mark proxy-originated errors and pin container DNS
Build Admin / Build image (push) Successful in 28s
Build Admin / Build image (push) Successful in 28s
- handle_errors sets X-Proxy-Error (dial|upstream) and X-Proxy-Error-Detail so clients with several proxies can fail over when the upstream was never reached, instead of treating the proxy's 502 as the upstream's - explicit dns in compose: the container no longer inherits a host resolver that may disappear (systemd-resolved was switched off on the VM and every lookup failed until a restart) - strip X-Proxy-Token from Caddy logs (it was logged in clear text) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e84b79dda0
commit
4fccfb9089
3 files changed
+50
-1
No files matched your search
@@ -5,6 +5,18 @@
|
|||||||
{
|
{
|
||||||
email {$ACME_EMAIL}
|
email {$ACME_EMAIL}
|
||||||
admin off
|
admin off
|
||||||
|
|
||||||
|
# The error log dumps request headers, and Caddy redacts only
|
||||||
|
# Authorization/Cookie on its own: X-Proxy-Token used to land in
|
||||||
|
# `docker logs` in clear text on every failed upstream call.
|
||||||
|
log default {
|
||||||
|
format filter {
|
||||||
|
wrap json
|
||||||
|
fields {
|
||||||
|
request>headers>X-Proxy-Token delete
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# --- Access control (generated by docker-entrypoint.sh from env) ---
|
# --- Access control (generated by docker-entrypoint.sh from env) ---
|
||||||
@@ -44,8 +56,38 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# --- Proxy-originated errors ---
|
||||||
|
# Errors produced by the proxy itself (the upstream answered nothing) are
|
||||||
|
# marked with X-Proxy-Error so that a client with several proxies can tell
|
||||||
|
# them apart from an upstream's own 5xx and fail over:
|
||||||
|
# dial — the upstream was never reached (DNS, connect, no route): the
|
||||||
|
# request did not leave this host, retrying elsewhere is safe
|
||||||
|
# even for non-idempotent calls
|
||||||
|
# upstream — anything else (timeouts, broken responses): the upstream may
|
||||||
|
# have processed the request
|
||||||
|
# X-Proxy-Error-Detail carries the reason. Only clients that passed the
|
||||||
|
# guards ever get here (the guards respond directly, without an error).
|
||||||
|
# Note: handled errors are logged by Caddy at DEBUG level only — the reason
|
||||||
|
# is visible to the client via X-Proxy-Error-Detail.
|
||||||
|
(proxy_errors) {
|
||||||
|
handle_errors {
|
||||||
|
@dial `{err.message}.startsWith("dial ")`
|
||||||
|
handle @dial {
|
||||||
|
header X-Proxy-Error dial
|
||||||
|
header X-Proxy-Error-Detail "{err.message}"
|
||||||
|
respond `{"error":"proxy_dial_failed"}` {err.status_code}
|
||||||
|
}
|
||||||
|
handle {
|
||||||
|
header X-Proxy-Error upstream
|
||||||
|
header X-Proxy-Error-Detail "{err.message}"
|
||||||
|
respond `{"error":"proxy_upstream_failed"}` {err.status_code}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
(routes) {
|
(routes) {
|
||||||
import /etc/caddy/matchers.caddy
|
import /etc/caddy/matchers.caddy
|
||||||
|
import proxy_errors
|
||||||
|
|
||||||
route {
|
route {
|
||||||
# Health check — no auth, evaluated before the guards
|
# Health check — no auth, evaluated before the guards
|
||||||
|
|||||||
@@ -12,6 +12,13 @@ services:
|
|||||||
# Legacy plain-HTTP endpoint (unchanged for existing clients)
|
# Legacy plain-HTTP endpoint (unchanged for existing clients)
|
||||||
- "8082:8080"
|
- "8082:8080"
|
||||||
env_file: .env
|
env_file: .env
|
||||||
|
# Explicit resolvers instead of inheriting the host's: Docker snapshots
|
||||||
|
# the host resolv.conf at container start, so switching the host off
|
||||||
|
# systemd-resolved (127.0.0.53) left Caddy with a dead resolver and every
|
||||||
|
# upstream lookup failing until the container was restarted.
|
||||||
|
dns:
|
||||||
|
- 1.1.1.1
|
||||||
|
- 8.8.8.8
|
||||||
volumes:
|
volumes:
|
||||||
# Let's Encrypt certificates and ACME account keys — MUST persist,
|
# Let's Encrypt certificates and ACME account keys — MUST persist,
|
||||||
# otherwise every recreate re-issues certs and burns LE rate limits
|
# otherwise every recreate re-issues certs and burns LE rate limits
|
||||||
|
|||||||
Reference in new issue
Block a user