Alexey Dolotov 6 days ago
parent
commit
4441fe5288
No account linked to committer's email address
2 changed files with 51 additions and 3 deletions
  1. 40
    3
      contrib/sni-router/README.md
  2. 11
    0
      contrib/sni-router/mtg-config.toml

+ 40
- 3
contrib/sni-router/README.md View File

47
 
47
 
48
 HAProxy forwards TCP connections to mtg and Caddy with a PROXY protocol
48
 HAProxy forwards TCP connections to mtg and Caddy with a PROXY protocol
49
 v2 header so both backends see the real client IP instead of HAProxy's
49
 v2 header so both backends see the real client IP instead of HAProxy's
50
-container address.  The three pieces must stay in sync:
50
+container address.  Caddy also receives PROXY v2 from mtg on the
51
+fronting path (see "Fronting loop" below), so all four pieces below
52
+must stay in sync:
51
 
53
 
52
 - `haproxy.cfg` — `send-proxy-v2` on the `mtg` and `web` backend `server` lines
54
 - `haproxy.cfg` — `send-proxy-v2` on the `mtg` and `web` backend `server` lines
53
-- `mtg-config.toml` — `proxy-protocol-listener = true`
55
+- `mtg-config.toml` — `proxy-protocol-listener = true` (HAProxy → mtg)
56
+- `mtg-config.toml` — `[domain-fronting].proxy-protocol = true` (mtg → Caddy on fronting)
54
 - `Caddyfile` — `listener_wrappers { proxy_protocol { ... } tls }` on `:8443`
57
 - `Caddyfile` — `listener_wrappers { proxy_protocol { ... } tls }` on `:8443`
55
 
58
 
56
-If you disable one, disable all three, otherwise the backend will fail
59
+If you disable one, disable all four, otherwise the backend will fail
57
 to parse the connection.
60
 to parse the connection.
58
 
61
 
62
+## Fronting loop (why `[domain-fronting]` is set explicitly)
63
+
64
+When mtg sees TLS that isn't valid Telegram (a probe or a browser
65
+hitting the domain on `:443`), it forwards that connection to a real
66
+web server — "domain fronting".  By default mtg uses the secret's
67
+hostname as the fronting target and resolves it via DNS, which in
68
+this setup points back to this server: the fronting dial lands on
69
+HAProxy, SNI matches the secret, HAProxy routes the connection back
70
+to mtg → loop.
71
+
72
+The trigger is DNS, not name equality: any time the secret's hostname
73
+resolves to this host, the loop reproduces.  In an SNI-router
74
+deployment the secret's hostname has to point here for clients to
75
+reach mtg in the first place, so the loop is the default state unless
76
+mtg is steered away from HAProxy.
77
+
78
+`mtg-config.toml` therefore pins the fronting target to the Caddy
79
+container directly:
80
+
81
+```toml
82
+[domain-fronting]
83
+host = "web"
84
+port = 8443
85
+proxy-protocol = true
86
+```
87
+
88
+`host = "web"` resolves through compose-network DNS to the `web`
89
+service (Caddy), bypassing HAProxy.  `proxy-protocol = true` matches
90
+Caddy's `:8443` listener wrapper so the real client IP still
91
+propagates to Caddy's logs.
92
+
93
+Requires mtg ≥ 2.4 — hostname acceptance for the fronting target was
94
+added in #480.
95
+
59
 ## ACME (Let's Encrypt) notes
96
 ## ACME (Let's Encrypt) notes
60
 
97
 
61
 HAProxy passes `/.well-known/acme-challenge/` requests on `:80` to
98
 HAProxy passes `/.well-known/acme-challenge/` requests on `:80` to

+ 11
- 0
contrib/sni-router/mtg-config.toml View File

11
 # real client IP.  Keep this in sync with haproxy.cfg (`send-proxy-v2`).
11
 # real client IP.  Keep this in sync with haproxy.cfg (`send-proxy-v2`).
12
 proxy-protocol-listener = true
12
 proxy-protocol-listener = true
13
 
13
 
14
+# Fronting target: point mtg at the Caddy container directly so its
15
+# fallback dial (for non-Telegram TLS) bypasses HAProxy and doesn't
16
+# loop back here.  Without this, mtg resolves the secret's hostname
17
+# via DNS, which in this setup resolves to this server -> HAProxy ->
18
+# mtg again.  See README's "Fronting loop" section for the long form.
19
+# Requires mtg >= 2.4 (#480 added hostname acceptance for the target).
20
+[domain-fronting]
21
+host = "web"
22
+port = 8443
23
+proxy-protocol = true
24
+
14
 [defense.anti-replay]
25
 [defense.anti-replay]
15
 enabled = true
26
 enabled = true
16
 max-size = "1mib"
27
 max-size = "1mib"

Loading…
Cancel
Save