sni-router: break domain-fronting loop with pinned Caddy IP
When the secret's domain points at this server (the recommended
deployment), mtg's default fronting behavior dials that domain on :443
and the connection lands on HAProxy. HAProxy sees the SNI matching the
secret and routes back to mtg, looping until something gives.
Pin Caddy's container address via a static `sni` network and point
mtg's `[domain-fronting]` at it directly with `proxy-protocol = true`,
matching Caddy's :8443 PROXY listener wrapper. mtg's
`domain-fronting.ip` only accepts a literal IP (not a hostname), so the
network needs a fixed subnet.
README documents the loop, the fix, and the requirement to keep the
pinned IP in sync between docker-compose.yml and mtg-config.toml.
Reported by @gaudima in #462.
Pass real client IPs through with PROXY protocol v2
Without this, mtg and Caddy see HAProxy's container IP for every
connection, which breaks meaningful logging, abuse handling, and any
IP-based blocklist logic. HAProxy sends a PROXY protocol v2 header on
its TCP backends; mtg enables proxy-protocol-listener, and Caddy wraps
:8443 with a proxy_protocol listener before tls.
The :80 path (ACME HTTP-01 passthrough) is unchanged — client IP there
is not useful and HAProxy's http mode already adds X-Forwarded-For if
anyone wants it.
Requested in https://github.com/9seconds/mtg/pull/462 review.
Add docker-compose example with HAProxy SNI router
Turnkey deployment: HAProxy on :443 peeks at the TLS SNI and routes
Telegram clients to mtg while forwarding everything else (including DPI
probes) to a real Caddy web server with automatic HTTPS.
This is the setup recommended in BEST_PRACTICES.md, packaged so that
operators can clone and run it with minimal configuration.
Refs: #458