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

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,15 +46,52 @@ docker compose exec mtg mtg access /config/config.toml
46 46
 
47 47
 HAProxy forwards TCP connections to mtg and Caddy with a PROXY protocol
48 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 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 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 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 95
 ## ACME (Let's Encrypt) notes
59 96
 
60 97
 HAProxy passes `/.well-known/acme-challenge/` requests on `:80` to

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

@@ -11,6 +11,17 @@ bind-to = "[::]:3128"
11 11
 # real client IP.  Keep this in sync with haproxy.cfg (`send-proxy-v2`).
12 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 25
 [defense.anti-replay]
15 26
 enabled = true
16 27
 max-size = "1mib"

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