Просмотр исходного кода

Merge pull request #478 from dolonet/fix/sni-router-fronting-loop

sni-router: break domain-fronting loop
master
Sergei Arkhipov 2 дней назад
Родитель
Сommit
4c7d42fb0e
Аккаунт пользователя с таким Email не найден
2 измененных файлов: 51 добавлений и 3 удалений
  1. 40
    3
      contrib/sni-router/README.md
  2. 11
    0
      contrib/sni-router/mtg-config.toml

+ 40
- 3
contrib/sni-router/README.md Просмотреть файл

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

+ 11
- 0
contrib/sni-router/mtg-config.toml Просмотреть файл

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"

Загрузка…
Отмена
Сохранить