telemt/docs/XRAY-SINGBOX-ROUTING.ru.md

17 KiB
Raw Permalink Blame History

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 "Как это НЕ работает"

Telegram Client
   |
   |  (указан IP/домен telemt)
   v
telemt instance

Ожидание: "я указал telemt -> значит трафик попадёт в telemt" - нет!


Схема №2. "Как это реально работает с TLS/L7-роутером и SNI"

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 в открытом виде.

Упрощённо:

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-кеша/перерезолва/"умных" правил роутера

Рекомендуемая схема (целевое состояние)

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-роутера

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-сервис с Lets Encrypt сертификатом под этот домен,
  • (желательно) держать сайт-заглушку, чтобы 443 выглядел как обычный HTTPS