- Caddy allow: 127.0.0.0/8 → 127.0.0.1/32 (only loopback peer is HAProxy).
- haproxy.cfg: rewrite v6only comment to describe what it actually does
(suppresses v4-mapped accept, preventing conflict with the v4 bind),
not the symptom.
- docker-compose.yml: trim the 8-line haproxy comment to 3 lines and
defer the rationale to README. Add one-line note explaining why web
uses host port 8080 (HAProxy owns :80).
- README: condense the "Why network_mode: host" subsection. Spell out
trade-offs as a list: own-the-host-ports, Linux-only (Docker Desktop
doesn't make this layout reachable), userns-remap incompatibility.
Note that mtg-config.toml stays as-is because mtg/web remain on the
compose bridge.
sni-router: switch HAProxy to host networking for real client IPs
Bridge ingress (Docker's docker-proxy userland forwarder, Podman's
slirp4netns/pasta) rewrites the source IP of inbound connections on a
published port to the bridge gateway address. HAProxy then stamps that
gateway address into the PROXY v2 header it forwards to mtg and Caddy,
so neither backend ever sees a real client IP.
Move HAProxy into the host netns (network_mode: host) so it binds
:443/:80 directly with no NAT in the path. mtg and Caddy stay on the
compose bridge and are published on 127.0.0.1 only; HAProxy reaches
them via host loopback and PROXY v2 carries the real client IP (v4 or
v6) end-to-end.
Also accept IPv6 clients explicitly on the HAProxy frontends — `bind
*:443` is IPv4-only and missed v6 clients on hosts where the previous
example happened to "work" only because of dual-stack quirks.
Add 127.0.0.0/8 to Caddy's PROXY allow-list to cover the new loopback
hop from HAProxy. README gains a short subsection explaining the
host-mode choice and its trade-off (HAProxy occupies host :443/:80).
Diagnosed and tested by @bam80 on Fedora + Docker 29. Fixes #498.
contrib/sni-router: split MTG_SECRET assignment from envsubst in examples
`MTG_SECRET=<placeholder> envsubst < ...` was shell-broken on literal
copy-paste — bash parses `<placeholder>` as redirection from a
non-existent file. Two-line `export MTG_SECRET=...` + plain envsubst
form removes the ambiguity. Applies to README, docker-compose.yml,
and the .example header.
contrib/sni-router: render mtg-config.toml from a tracked .example
Track `mtg-config.toml.example` with `secret = "${MTG_SECRET}"`; the
rendered `mtg-config.toml` and local `.env` are gitignored, so the
secret never lands in a tracked file.
Quick start switches from "paste the secret into mtg-config.toml" to
either `envsubst < mtg-config.toml.example > mtg-config.toml` or
`cp` + hand-edit `${MTG_SECRET}` for users without envsubst.
After #502 made DOMAIN env-driven, the secret was the last hand-edit
of a tracked file in the example. Follow-up to #506.
contrib/sni-router: read $DOMAIN from env in haproxy.cfg
Closes #501.
Before: the SNI hostname had to be edited in haproxy.cfg, plus
DOMAIN had to be set in .env for Caddy. Two places to keep in sync.
After: DOMAIN is the single source — set it once in .env (or export),
docker-compose forwards it into both haproxy and caddy containers,
and HAProxy interpolates ${DOMAIN} into the SNI ACL at startup.
mtg-config.toml's secret still embeds the domain in its bytes
(generated once with `mtg generate-secret <domain>`), so that one
remains a one-time edit at install — the README is updated to reflect
this.
HAProxy has supported environment-variable substitution in config
strings since 1.6.
Fix SELinux-related permission denied error for containerized apps
reading configs exposed via volumes.
Also make it possible to use port 80 in the fronted.
Add an ACL that routes /.well-known/acme-challenge/ requests on :80
to Caddy instead of redirecting to HTTPS, so Let's Encrypt certificate
issuance works out of the box.
Also simplify Caddyfile to use Caddy's http_port/https_port directives.
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