todo/mtproxy/mtproto-zig-setup.md
loop-uh 0ad0bcc8dd
Some checks failed
Published content check / validate (push) Failing after 5s
seo+зеркало: [!mirror]-плашка со ссылкой на вики и description во frontmatter всех заметок
Каждая публикуемая заметка получила callout-шапку со ссылкой на свою страницу
wiki.zapret.moe (на самой вики она вырезается транформером RemoveMirrorCallout,
видна только на зеркале Obsidian Publish и в Forgejo) и SEO-поле description —
1–2 предложения для meta description обоих сайтов.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-26 22:05:57 +03:00

18 KiB
Raw Permalink Blame History

date tags aliases link description
2026-06-07
mtproto
mtproxy
mtproto-zig
setup
dpi
tspu
Настройка mtproto.zig
mtproto.zig setup
MTProxy runbook
https://github.com/sleep3r/mtproto.zig Пошаговая настройка MTProxy на mtproto.zig: установка mtbuddy, config.toml, TCPMSS-дробление, nfqws-desync, диагностика блокировок ТСПУ по метрикам.

[!mirror] Резервное зеркало Актуальная версия этой страницы — на основной вики: wiki.zapret.moe/mtproxy/mtproto-zig-setup

🛠️ Настройка MTProxy на mtproto.zig — пошагово и «что происходит на проводе»

[!info] Что это и для кого Практический runbook: как поднять MTProxy на mtproxy/mtproto-zig и понимать, что делает каждый слой защитыс учётом свежих наблюдений (протухший фингерпринт клиента из tdesktop#30733, детект по expected_64_got_0, приёмы TCPMSS и SYN-ACK).

Теория и «почему» — в обзорной статье mtproxy/mtproto-zig. Здесь — команды, конфиг и диагностика.

[!tip] Почему mtproto.zig, а не telemt Оба — хорошие FakeTLS-прокси. mtproto.zig берут, когда нужен обход DPI «под ключ»: он сам ставит TCPMSS-дробление + nfqws-desync + nginx-маскировку одной командой, без ручного iptables. telemt — когда нужен REST API для бота (Zapret/mtproto/02-implementations).


Шаг 0. Подготовка

Что Как и почему
VPS / подсеть Не «народный» хостинг (Selectel/Я.Облако — Сигнал 1 [[VLESS/dpi-tls-june-2026
Домен маскировки Популярный, с одним раундом x25519: rutube.ru, ozon.ru, vk.com, yandex.ru. НЕ wb.ru и др. HRR/secp521r1.
Порт 443. Любой другой подозрителен.
Доступ root/sudo на сервере.

⚠️ Домен вшивается в ссылку tg:// и неизменен после раздачи. Выбирай один раз.


Шаг 1. Установка

# 1. bootstrap mtbuddy (проверяет minisign-подпись + SHA-256)
curl -fsSL https://raw.githubusercontent.com/sleep3r/mtproto.zig/v1.10.3/deploy/bootstrap.sh | sudo bash

# 2. установка прокси со всеми DPI-модулями
sudo mtbuddy install --port 443 --domain rutube.ru --yes

Что делает install (--no-dpi отключает п.56):

  1. Качает готовый бинарь (определяет CPU: x86_64_v3x86_64aarch64)
  2. Генерит секрет (или --secret <32hex>)
  3. systemd-сервис mtproto-proxy
  4. Открывает порт в ufw
  5. TCPMSS=88 iptables (дробит ClientHello) ← см. Шаг 4
  6. nginx-маскировка + nfqws-desync ← см. Шаг 5
  7. Печатает tg://-ссылку

Полезные флаги: --secret, --user, --tcpmss <n> (дефолт 88), --no-tcpmss, --no-nfqws, --no-masking, --ipv6-hop.


Шаг 2. Конфиг config.toml

Большинство — уже дефолты; фиксируем ключевое явно (/opt/mtproto-proxy/config.toml):

[general]
use_middle_proxy = true        # медиа на не-Premium + promo-теги

[server]
port = 443
# rate_limit_per_subnet = 0    # ОСТАВЬ 0 для мобильных юзеров РФ (carrier-NAT)

[censorship]
tls_domain = "rutube.ru"       # single-round x25519, неизменен после раздачи
mask = true                    # форвард зондов на реальный домен (анти-probing)
fake_tls_only = true           # реджектить палевный dd-транспорт
# desync = true                # дефолт on — дробит ServerHello (1 байт + 3мс)
drs = true                     # мимикрия размеров TLS-записей под браузер
fast_mode = true

[metrics]
enabled = true                 # Prometheus /metrics (см. Шаг 6 — диагностика)
host = "127.0.0.1"
port = 9400

[access.users]
user1 = "00112233445566778899aabbccddeeff"   # openssl rand -hex 16

После правки: sudo systemctl restart mtproto-proxy (SIGHUP-reload тоже есть, но при workers>1 запрещён).


Шаг 3. Что происходит на проводе (карта защит)

КЛИЕНТ (Telegram)                    ТСПУ                    СЕРВЕР (mtproto.zig)
   │  ── ClientHello (почерк!) ──────► 👁 фингерпринт ──────►  снимает fp в лог
   │                                   │ (тут и блок #30733)
   │  ◄──────────── ServerHello (дроблён desync) ────────────  mask/desync/drs
   │  ── 64-байт MTProto-хендшейк ───► (если жив) ───────────►  proxy → DC
Слой Что делает Против чего
TCPMSS=88 клиент режет ClientHello на ~6 кусков фингерпринт (DPI не пересобирает)
nfqws desync fake-пакеты + TTL-split (S→C) stateful DPI
desync ServerHello 1 байт + 3мс + хвост пассивные сигнатуры
mask зонды → реальный tls_domain active probing
drs размеры записей как у браузера статистика трафика

[!danger] Слабое звено, которое сервер НЕ чинит — фингерпринт клиента Почерк ClientHello (JA3/JA4) генерирует приложение Telegram, не сервер. По tdesktop#30733: Desktop мимикрирует под Chrome 134/macOS (t13d1516h2_8daaf6152771_d8a2da3f94cd), а живой Chrome — 148 → пресет протух, и это маркер. Это тот самый блок, дающий expected_64_got_0 (Zapret/mtproto/10-telemt-logs-dpi).

Лечится только в самом Telegram. Всё, что может сервер — спрятать/сломать опознание этого почерка (TCPMSS, desync), а не поправить его. Поэтому Шаги 46 важны.


Шаг 4. TCPMSS — дробление ClientHello

Как работает

Сервер анонсирует в SYN-ACK малый MSS, и клиент вынужден резать всё исходящее (включая ClientHello ~517 байт) на мелкие сегменты. DPI без потоковой пересборки не складывает почерк целиком → не матчит сигнатуру.

В mtproto.zig включено по умолчанию (TCPMSS=88). Сменить значение: mtbuddy install --tcpmss 96.

Вариант «через балансировщик» — помогает?

Да, помогает. Это тот же механизм. Если перед прокси стоит балансировщик (он терминирует клиентский TCP), MSS-clamp надо вешать именно на балансировщик — там рождается SYN-ACK к клиенту:

iptables -t mangle -A OUTPUT -p tcp --sport 443 \
  --tcp-flags SYN,ACK SYN,ACK -j TCPMSS --set-mss 96

Конфиг telemt/mtproto.zig при этом трогать не надо — правило работает на уровне ядра.

Почему «на балансировщике»: clamp на бэкенде (где сидит сам прокси) не дойдёт до клиента, если балансировщик пере-устанавливает TCP. Правило должно быть на той коробке, что шлёт SYN-ACK клиенту.

[!note] 88 или 96 — разница невелика Оба дают ~6 сегментов на ClientHello. mtproto.zig по умолчанию 88; совет из интернета — 96. Бери любое; если ставишь mtproto.zig напрямую (без отдельного балансировщика) — TCPMSS уже стоит, дублировать руками не нужно.

[!tip] Это и есть «решение JA4» из teleproxy Когда говорят «в teleproxy решён JA4» — речь именно об этой фрагментации: teleproxy ставит TCP_MAXSEG=256, чтобы DPI не извлёк JA4 из первого пакета. JA4 при этом не меняется (его задаёт клиент). mtproto.zig делает то же самое и агрессивнее (MSS=88), так что этот приём у тебя уже включён. Почему это не «смена почерка» и где лежит настоящий фикс — mtproxy/ja4-sni-client-side.

[!warning] Дробление ≠ панацея MSS-clamp бьёт по DPI, который не пересобирает поток. Если ТСПУ делает реассемблинг — одного дробления мало, нужен desync (nfqws, Шаг 5), который активно ломает пересборку fake-пакетами. Поэтому их ставят вместе.


Шаг 5. nfqws TCP desync + тюнинг TTL

Ставится при install. Стратегия: --dpi-desync=fake,split2 --dpi-desync-ttl=6 --dpi-desync-fooling=md5sig — fake-пакет с заниженным TTL (долетает до ТСПУ, умирает до клиента) + битая MD5-опция, чтобы сбить state-машину DPI.

TTL надо подтюнить под маршрут (дефолт 6 не универсален, «48 для росс. ISP»):

traceroute <ip_клиента_или_DC>     # прикинуть хоп, где сидит ТСПУ
sudo mtbuddy nfqws --ttl 7         # переставить
systemctl status nfqws-mtproto     # проверить, что запущен

TTL должен быть больше расстояния до ТСПУ, но меньше расстояния до клиента.


Шаг 5.5 (опционально). Egress через Xray/SOCKS5 или туннель

Это аналог SOCKS5_PROXY + DIRECT_MODE из конфигов teleproxy: маршрут исходящего трафика прокси к дата-центрам Telegram через Xray/VLESS (SOCKS5) или WireGuard/AmneziaWG-туннель.

[upstream]
type = "socks5"            # "direct" | "tunnel" | "socks5" | "http"

[upstream.socks5]
host = "127.0.0.1"
port = 1080               # порт локального Xray/VLESS
# username = ""
# password = ""

Для туннеля вместо SOCKS5:

[upstream]
type = "tunnel"
[upstream.tunnel]
interfaces = ["awg0", "awg1"]   # WireGuard/AmneziaWG, с авто-фолбэком

[!important] Что это лечит, а что нет Egress-маршрут помогает, когда дата-центры Telegram недоступны с твоего VPS (заблокированы/режутся на пути proxy→DC), или нужен лишний хоп. Это путь сервер→DC — на входящий ClientHello (где JA4, который видит ТСПУ у клиента) он не влияет. Не путай: это про доступность DC, а не про обход детекта почерка. См. mtproxy/ja4-sni-client-side.


Шаг 6 (опционально). Лимит SYN-ACK — десинхрон + анти-залп

На пальцах: сервер иногда «роняет» свой ответ при установке соединения (SYN-ACK), клиент переспрашивает через секунду. От этого DPI сбивается со счёта и не опознаёт почерк клиента, а соединения идут не пачкой, а по одному в секунду. Спорный, но у людей рабочий приём.

Подробная механика (с аналогией), цена и per-port вариант (бюджет 1/сек на каждый порт, чтобы не калечить всех юзеров) — в Zapret/mtproto/10-telemt-logs-dpi#Лимит SYN-ACK — помогает или нет (для mtproto.zig всё идентично, только порт 443).

Коротко: ставить стоит, если блок держится после Шагов 45; брать сразу per-port; мониторить логи (Шаг 7), чтобы поймать адаптацию ТСПУ.


Шаг 7. Диагностика — mtproto.zig сам показывает атаку DPI

Фингерпринт клиента в логах

mtproto.zig логирует почерк первых 16 ClientHello (диагностический бюджет):

journalctl -u mtproto-proxy | grep "client ClientHello"
# client ClientHello [ciphers=... groups=... key_share=...] (we serve: ...)

[!tip] Как связать с #30733 Смотри key_share в логе. Свежий браузер шлёт X25519MLKEM768 (post-quantum). Если твой клиент его не шлёт — он на старом пресете и попадает под детект из #30733. Это прямой способ увидеть «протух ли почерк» на своём трафике.

Метрики close-reason — детектор начала блокировок

mtproto.zig отдаёт Prometheus-метрику с причинами закрытия — её всплеск = ТСПУ начал резать:

curl -s 127.0.0.1:9400/metrics | grep -E "close_reason|handshake_timeouts"
# mtproto_connection_close_reason_total{reason="tls_validation_failed"} ...
# mtproto_connection_close_reason_total{reason="replay_detected"} ...      ← зонды Revisor
# mtproto_connection_close_reason_total{reason="bad_handshake"} ...
# mtproto_handshake_timeouts_total ...                                     ← аналог expected_64_got_0

Рост tls_validation_failed / replay_detected / handshake_timeouts над фоном — это и есть сигнал, что цензор начал работать по тебе (так и задумано разработчиком). replay_detected отдельно ловит active-probe зонды ТСПУ (Revisor).

Плюс есть веб-дашборд (порт 61208, Basic-auth, токен в /opt/mtproto-proxy/monitor/dashboard.token) — открывать только через SSH-тоннель.


Когда всё-таки заблокировали — порядок действий

  1. Проверь логи/метрики (Шаг 7): растёт ли handshake_timeouts / tls_validation_failed, и какой key_share у падающих клиентов.
  2. Подтюнь TTL nfqws (Шаг 5) — частая причина, что desync «не достаёт» до ТСПУ.
  3. Снизь MSS (--tcpmss 80) или добавь clamp на балансировщик (Шаг 4).
  4. Включи SYN-ACK per-port (Шаг 6).
  5. Если рвётся путь до DC (а не вход) — egress через Xray/SOCKS5 или туннель (Шаг 5.5).
  6. Смени узел/подсеть (Сигнал 1) — если IP/диапазон попал под раздачу.
  7. Не дёргай настройки рефлекторно под блоком — сам паттерн адаптации может усугубить.
  8. Запасной каналVLESS/dpi-tls-june-2026 на отдельном узле.

Чек-лист

  • VPS на «чистой» подсети, домен single-round x25519, порт 443
  • mask = true, fake_tls_only = true, drs = true
  • TCPMSS активен (iptables -t mangle -S OUTPUT | grep TCPMSS) — или clamp на балансировщике
  • nfqws запущен, TTL подтюнен (systemctl status nfqws-mtproto)
  • [metrics] enabled = true + мониторинг close_reason/handshake_timeouts
  • Проверил key_share клиента в логах (свежесть почерка, #30733)
  • rate_limit_per_subnet = 0 для мобильных юзеров РФ
  • Готов запасной VLESS/XHTTP

📚 См. также