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

sni-router: break domain-fronting loop

When the secret's domain resolves back to this server (the SNI-router
default), mtg's fallback fronting dial lands on HAProxy, the SNI
matches the secret, HAProxy routes the connection back to mtg -> loop.

Set [domain-fronting].host = "web" in mtg-config.toml so mtg dials
Caddy directly via compose-network DNS, bypassing HAProxy.  Requires
mtg >= 2.4 (#480 added hostname acceptance for the fronting target).

README gains a "Fronting loop" section explaining the cause.
pull/478/head
Alexey Dolotov 5 дней назад
Родитель
Сommit
0fdf6cbab7
2 измененных файлов: 45 добавлений и 0 удалений
  1. 34
    0
      contrib/sni-router/README.md
  2. 11
    0
      contrib/sni-router/mtg-config.toml

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

56
 If you disable one, disable all three, otherwise the backend will fail
56
 If you disable one, disable all three, otherwise the backend will fail
57
 to parse the connection.
57
 to parse the connection.
58
 
58
 
59
+## Fronting loop (why `[domain-fronting]` is set explicitly)
60
+
61
+When mtg sees TLS that isn't valid Telegram (a probe or a browser
62
+hitting the domain on `:443`), it forwards that connection to a real
63
+web server — "domain fronting".  By default mtg uses the secret's
64
+hostname as the fronting target and resolves it via DNS, which in
65
+this setup points back to this server: the fronting dial lands on
66
+HAProxy, SNI matches the secret, HAProxy routes the connection back
67
+to mtg → loop.
68
+
69
+The trigger is DNS, not name equality: any time the secret's hostname
70
+resolves to this host, the loop reproduces.  In an SNI-router
71
+deployment the secret's hostname has to point here for clients to
72
+reach mtg in the first place, so the loop is the default state unless
73
+mtg is steered away from HAProxy.
74
+
75
+`mtg-config.toml` therefore pins the fronting target to the Caddy
76
+container directly:
77
+
78
+```toml
79
+[domain-fronting]
80
+host = "web"
81
+port = 8443
82
+proxy-protocol = true
83
+```
84
+
85
+`host = "web"` resolves through compose-network DNS to the `web`
86
+service (Caddy), bypassing HAProxy.  `proxy-protocol = true` matches
87
+Caddy's `:8443` listener wrapper so the real client IP still
88
+propagates to Caddy's logs.
89
+
90
+Requires mtg ≥ 2.4 — hostname acceptance for the fronting target was
91
+added in #480.
92
+
59
 ## ACME (Let's Encrypt) notes
93
 ## ACME (Let's Encrypt) notes
60
 
94
 
61
 HAProxy passes `/.well-known/acme-challenge/` requests on `:80` to
95
 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"

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