mirror of https://github.com/telemt/telemt.git
Create XRAY-SINGBOX-ROUTING.ru.md
This commit is contained in:
parent
ac453638b8
commit
265b9a5f11
|
|
@ -0,0 +1,321 @@
|
||||||
|
# SNI-маршрутизация в xray-core / sing-box + TLS-fronting
|
||||||
|
|
||||||
|
## Термины (в контексте этого кейса)
|
||||||
|
|
||||||
|
- **TLS-fronting домен** — домен, который фигурирует в TLS ClientHello как **SNI** (например, `petrovich.ru`): он используется как "маска" на L7 и как ключ маршрутизации в прокси-роутере.
|
||||||
|
- **xray-core / sing-box** — локальный или удалённый L7/TLS-роутер (прокси), который:
|
||||||
|
1) принимает входящее TCP/TLS-соединение,
|
||||||
|
2) читает TLS ClientHello,
|
||||||
|
3) извлекает SNI,
|
||||||
|
4) по SNI выбирает outbound/апстрим,
|
||||||
|
5) устанавливает новое TCP-соединение к целевому хосту уже **от себя**.
|
||||||
|
- **SNI (Server Name Indication)** — поле в TLS ClientHello, где клиент Telegram сообщает доменное имя для "маскировки"
|
||||||
|
- **DNS-resolve на стороне L7-роутера** — если выходной адрес задан доменом (или роутер решил "всё равно идти по SNI"), то DNS резолвится **на стороне xray/sing-box**, а не на стороне Telegram-клиента
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Ключевая идея: куда на самом деле идёт соединение решает не то, что вы указали клиенту, а то как L7-роутер трактует SNI
|
||||||
|
|
||||||
|
Механика:
|
||||||
|
|
||||||
|
1) Telegram-клиенту вы можете указать **IP/домен telemt**,как "сервер".
|
||||||
|
2) Между клиентом и telemt стоит xray-core/sing-box, который принимает TCP, читает TLS ClientHello и видит **SNI=petrovich.ru**
|
||||||
|
3) Дальше роутер говорит: "Вижу SNI - направить на апстрим/маршрут N"
|
||||||
|
4) И устанавливает исходящее соединение не "по тому IP, который пользователь подразумевал", а **по домену из SNI** (или по сопоставлению SNI→outbound), используя для определния его IP собственный DNS-кеш или резолвер
|
||||||
|
5) `petrovich.ru` по A-записи указывает **не на IP telemt**, а значит при L7-маршрутизации трафик уйдёт на "оригинальный" сайт за этим доменом, а не в telemt: Telegram-клиент, естественно, не сможет получить ожидаемое поведение, потому что ответить с handshake на той стороне некому
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Схема №1 "Как это НЕ работает"
|
||||||
|
|
||||||
|
```text
|
||||||
|
Telegram Client
|
||||||
|
|
|
||||||
|
| (указан IP/домен telemt)
|
||||||
|
v
|
||||||
|
telemt instance
|
||||||
|
````
|
||||||
|
|
||||||
|
Ожидание: "я указал telemt -> значит трафик попадёт в telemt" - **нет!**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Схема №2. "Как это реально работает с TLS/L7-роутером и SNI"
|
||||||
|
|
||||||
|
```text
|
||||||
|
Telegram Client
|
||||||
|
|
|
||||||
|
| 1) TCP/TLS connection:
|
||||||
|
| - ClientHello:
|
||||||
|
| - SNI=petrovich.ru
|
||||||
|
v
|
||||||
|
xray-core / sing-box / любой L7 router
|
||||||
|
|
|
||||||
|
| 2) читает ClientHello -> вытаскивает SNI
|
||||||
|
| 3) выбирает маршрут по SNI
|
||||||
|
| 4) делает DNS для petrovich.ru
|
||||||
|
| 5) подключается к полученному IP по TLS с этим SNI
|
||||||
|
v
|
||||||
|
"Оригинальный" сайт, A-запись которого не на telemt
|
||||||
|
|
|
||||||
|
X не telemt -> Telegram-клиент не коннектится как ожидалось
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Почему указанный в клиенте IP/домен telemt "не спасает"
|
||||||
|
|
||||||
|
Потому что в таком режиме xray/sing-box выступает как **точка терминации TCP/TLS**, можно сказать - TLS-инспектор на уровне ClientHello, это означает:
|
||||||
|
|
||||||
|
* TCP-сессия от Telegram-клиента заканчивается на xray/sing-box
|
||||||
|
* Дальше создаётся **новая** TCP-сессия "от имени" xray/sing-box к апстриму
|
||||||
|
* Выбор апстрима делается правилами роутинга, а в TLS-сценариях самый удобный и распространённый ключ — **SNI**
|
||||||
|
|
||||||
|
То есть, "куда идти дальше" определяется логикой L7-роутера:
|
||||||
|
|
||||||
|
* либо правилами вида `if SNI == petrovich.ru -> outbound X`,
|
||||||
|
* либо более "автоматическим" поведением: `подключаться к тому хосту, который указан в SNI`,
|
||||||
|
* плюс кэш DNS и собственные резолверы роутера
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Что именно извлекается из TLS ClientHello и почему этого достаточно
|
||||||
|
|
||||||
|
TLS ClientHello отправляется **в начале** TLS-сессии и, в классическом TLS без ECH, содержит SNI в открытом виде.
|
||||||
|
|
||||||
|
Упрощённо:
|
||||||
|
|
||||||
|
```text
|
||||||
|
ClientHello:
|
||||||
|
- supported_versions
|
||||||
|
- cipher_suites
|
||||||
|
- extensions:
|
||||||
|
- server_name: petrovich.ru <-- SNI
|
||||||
|
- alpn: h2/http1.1/...
|
||||||
|
- ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Роутеру не нужно расшифровывать трафик и завершать TLS "как сервер" — часто достаточно просто прочитать первые пакеты и распарсить ClientHello, чтобы получить SNI и принять решение
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Типовой алгоритм SNI-роутинга
|
||||||
|
|
||||||
|
1. Принять входящий TCP.
|
||||||
|
2. Подождать первые байты.
|
||||||
|
3. Определить протокол:
|
||||||
|
|
||||||
|
* если видим TLS ClientHello → парсим SNI/ALPN
|
||||||
|
4. Применить route rules:
|
||||||
|
|
||||||
|
* match по `server_name` / `domain` / `tls.sni`
|
||||||
|
5. Выбрать outbound:
|
||||||
|
|
||||||
|
* direct / proxy / specific upstream / detour
|
||||||
|
6. Установить исходящее соединение:
|
||||||
|
|
||||||
|
* либо на фиксированный IP:порт,
|
||||||
|
* либо на домен через DNS-resolve на стороне роутера
|
||||||
|
7. Начать проксирование данных между входом и выходом
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Почему "A-запись фронтинг-домена не на telemt" ломает кейс
|
||||||
|
|
||||||
|
### Ситуация
|
||||||
|
|
||||||
|
* В ClientHello: `SNI = petrovich.ru`
|
||||||
|
* DNS: `petrovich.ru -> 203.0.113.77` - "оригинальный" сайт
|
||||||
|
* telemt живёт на: `198.51.100.10`
|
||||||
|
|
||||||
|
### Что делает роутер
|
||||||
|
|
||||||
|
* Видит SNI `petrovich.ru`
|
||||||
|
* Либо:
|
||||||
|
|
||||||
|
* (а) напрямую коннектится к `petrovich.ru:443`, резолвя A-запись в `203.0.113.77`,
|
||||||
|
* либо:
|
||||||
|
* (б) выбирает outbound, который указывает на `petrovich.ru` как destination,
|
||||||
|
* либо:
|
||||||
|
* (в) делает sniffing/override destination по SNI
|
||||||
|
|
||||||
|
В итоге исходящий коннект идёт на `203.0.113.77:443`, а не на telemt!
|
||||||
|
Другой сервер, другой протокол, другая логика, где telemt не участвует
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## "Где именно происходит подмена destination на SNI"
|
||||||
|
|
||||||
|
Это зависит от конфигурации, но типовые варианты:
|
||||||
|
|
||||||
|
### Вариант A: outbound задан доменом (и он совпадает с SNI)
|
||||||
|
|
||||||
|
Правило по SNI выбирает outbound, у которого destination задан доменом фронтинга,
|
||||||
|
тогда DNS резолвится на стороне роутера и вы уходите на "оригинальный" хост
|
||||||
|
|
||||||
|
### Вариант B: destination override / sniffing
|
||||||
|
|
||||||
|
Роутер "снифает" SNI и **перезаписывает** destination на домен из SNI (даже если вход изначально был на IP telemt),
|
||||||
|
это особенно коварно: пользователь видит "я подключаюсь к IP telemt", но роутер после sniffing решает иначе
|
||||||
|
|
||||||
|
### Вариант C: split DNS / кеш / независимый резолвер
|
||||||
|
|
||||||
|
Даже если клиент "где-то" резолвит иначе, это не важно: конечный DNS для исходящего коннекта — на стороне xray/sing-box,
|
||||||
|
который может иметь:
|
||||||
|
|
||||||
|
* свой DoH/DoT,
|
||||||
|
* свой кеш,
|
||||||
|
* свои правила fake-ip / system resolver,
|
||||||
|
* и, как следствие, своя "карта" **домен/SNI -> IP**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Признаки того, что трафик "утёк на оригинал", а не попал в telemt
|
||||||
|
|
||||||
|
* На стороне telemt отсутствуют входящие соединения/логи
|
||||||
|
* На стороне роутера видно, что destination — домен фронтинга, а IP соответствует публичному сайту
|
||||||
|
* TLS-метрики/сертификат на выходе соответствует "оригинальному" сайту в записах трафика
|
||||||
|
* Telegram-клиент получает неожиданный тип ответов/ошибку handshaking/timeout в debug-режиме
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Best-practice решение для этого кейса: свой домен фронтинга + заглушка на telemt + Let's Encrypt
|
||||||
|
|
||||||
|
### Цель
|
||||||
|
|
||||||
|
Сделать так, чтобы:
|
||||||
|
|
||||||
|
* SNI (фронтинг-домен) **резолвился в IP telemt**,
|
||||||
|
* на IP telemt реально был TLS-сервис с валидным сертификатом под этот домен,
|
||||||
|
* даже если кто-то "попробует открыть домен как сайт", он увидит нормальную заглушку, а не "пустоту"
|
||||||
|
|
||||||
|
### Что это даёт
|
||||||
|
|
||||||
|
* xray/sing-box, маршрутизируя по SNI, будет неизбежно приходить на telemt, потому что DNS(SNI-домен) → IP telemt
|
||||||
|
* Внешний вид будет правдоподобным: обычный домен с обычным сертификатом
|
||||||
|
* Устойчивость: меньше сюрпризов от DNS-кеша/перерезолва/"умных" правил роутера
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Рекомендуемая схема (целевое состояние)
|
||||||
|
|
||||||
|
```text
|
||||||
|
Telegram Client
|
||||||
|
|
|
||||||
|
| TLS ClientHello: SNI = hello.example.com
|
||||||
|
v
|
||||||
|
xray-core / sing-box
|
||||||
|
|
|
||||||
|
| Route by SNI -> outbound -> connect to hello.example.com:443
|
||||||
|
| DNS(hello.example.com) = IP telemt
|
||||||
|
v
|
||||||
|
telemt instance (IP telemt)
|
||||||
|
|
|
||||||
|
| TLS cert for hello.example.com (Let's Encrypt)
|
||||||
|
| + сайт-заглушка / health endpoint
|
||||||
|
v
|
||||||
|
OK
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Практический чеклист (минимальный)
|
||||||
|
|
||||||
|
1. Купить/иметь домен: `hello.example.com`
|
||||||
|
2. В DNS:
|
||||||
|
|
||||||
|
* `A hello.example.com -> <IP telemt>`
|
||||||
|
* (опционально) AAAA, если используете IPv6 и он стабилен
|
||||||
|
3. На telemt-хосте:
|
||||||
|
|
||||||
|
* поднять TLS endpoint на 443 с валидным сертификатом LE под `hello.example.com`
|
||||||
|
* отдать "заглушку" (например, статический сайт), чтобы домен выглядел как обычный веб-сервис
|
||||||
|
4. В xray/sing-box правилах:
|
||||||
|
|
||||||
|
* маршрутизировать нужный трафик по SNI = `hello.example.com` в "правильный" outbound (к telemt)
|
||||||
|
* избегать конфигураций, где destination override уводит на чужой домен
|
||||||
|
5. Важно:
|
||||||
|
|
||||||
|
* если вы используете кеш DNS на роутере — сбросить/обновить его после смены A-записи
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Пояснение про сайт-заглушку
|
||||||
|
|
||||||
|
Для эмуляции TLS, telemt имеет подсистему TLS-F в `src/tls_front`:
|
||||||
|
- её модуль - fetcher, собирает TLS-профили, чтоб максимально поведенчески корректно повторять TLS конкретно указанного сайта
|
||||||
|
|
||||||
|
Когда вы указываете сайт, который не отвечает по TLS:
|
||||||
|
- fetcher не может собрать TLS-профиль и происходит fallback на `fake_cert_len` - примитивный алгоритм,
|
||||||
|
- он забивает служебную информацию TLS рандомными байтами,
|
||||||
|
- простые системы DPI не распознают это
|
||||||
|
- однако, продвинутые системы, такие как nEdge или Fraud Control в сетях мобильной связи легко заблокируют или замедлят такой трафик
|
||||||
|
|
||||||
|
Создав сайт-заглушку с Let's Encrypt сертификатом, вы даёте TLS-F возможность получить данные сертификата и корректно его "повторять" в дальнейшем
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Вариант конфиг-подхода: "SNI строго привязываем к telemt - фиксированный IP"
|
||||||
|
|
||||||
|
Чтобы полностью исключить зависимость от DNS если вам это нужно, можно сделать outbound, который ходит на **фиксированный IP telemt**, но при этом выставляет SNI/Host как `hello.example.com`.
|
||||||
|
|
||||||
|
Идея:
|
||||||
|
|
||||||
|
* destination: `IP:443`
|
||||||
|
* SNI: `hello.example.com`
|
||||||
|
* сертификат на telemt именно под `hello.example.com`
|
||||||
|
|
||||||
|
Так вы получаете:
|
||||||
|
|
||||||
|
* TLS выглядит корректно, ведь SNI совпадает с сертификатом,
|
||||||
|
* а routing никогда не уйдёт на "оригинал", потому что A-запись указывает на telemt и контроллируется вами!
|
||||||
|
|
||||||
|
Но в вашем описании проблема как раз в том, что роутер "сам решает по SNI и резолвит домен", поэтому самый универсальный вариант — сделать так, чтобы DNS всегда приводил в telemt
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Пример логики правил на псевдоконфиге L7-роутера
|
||||||
|
|
||||||
|
```text
|
||||||
|
if inbound is TLS and sni == "hello.example.com":
|
||||||
|
route -> outbound "telemt"
|
||||||
|
else:
|
||||||
|
route -> outbound "default"
|
||||||
|
```
|
||||||
|
|
||||||
|
Outbound `telemt`:
|
||||||
|
|
||||||
|
* destination: `hello.example.com:443`
|
||||||
|
* TLS enabled
|
||||||
|
* SNI: `hello.example.com`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Отдельно: что может неожиданно сломать даже "правильный" DNS
|
||||||
|
|
||||||
|
* **Кеширование DNS** на xray/sing-box или на системном резолвере, особенно при смене A-записи
|
||||||
|
* **Split-horizon DNS**: разные ответы внутри/снаружи, попытки подмены/терминирования в других точках
|
||||||
|
* **IPv6**: если есть AAAA и он указывает не туда, роутер может предпочесть IPv6: помните, что поддержка v6 нестабильна и не рекомендуется в prod
|
||||||
|
* **DoH/DoT** на роутере: он может резолвить не тем резолвером, которым вы проверяли
|
||||||
|
|
||||||
|
Минимальная гигиена:
|
||||||
|
|
||||||
|
* контролировать A/AAAA,
|
||||||
|
* держать TTL разумным,
|
||||||
|
* проверять, каким резолвером пользуется именно роутер,
|
||||||
|
* при необходимости отключить/ограничить destination override
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Итог
|
||||||
|
|
||||||
|
В режиме TLS-fronting с xray-core/sing-box как L7/TLS-роутером **SNI становится приоритетным "source-of-truth" для маршрутизации**
|
||||||
|
|
||||||
|
Если фронтинг-домен по DNS указывает не на IP telemt, роутер честно уводит трафик на "оригинальный" сайт, потому что он строит исходящее соединение "по SNI"
|
||||||
|
|
||||||
|
Надёжное решение для этого кейса:
|
||||||
|
|
||||||
|
* использовать **свой домен** для фронтинга,
|
||||||
|
* направить его **A/AAAA** на IP telemt,
|
||||||
|
* поднять на telemt **TLS-сервис с Let’s Encrypt сертификатом** под этот домен,
|
||||||
|
* (желательно) держать **сайт-заглушку**, чтобы 443 выглядел как обычный HTTPS
|
||||||
Loading…
Reference in New Issue