v1.2.0
🇷🇺 Что нового (RU)
Что добавляет этот релиз
v1.2.0 — релиз про расширение апстрима: новый провайдер egress превращает обычную VPN-ссылку в чистый, трудноблокируемый выход для хопа прокси→Telegram. Плюс — фикс автопереключения в туннель-пуле (по репорту #323) и рефактор проверки fronting-домена.
[!NOTE]
Новая фича включается только явной командой — обновление безопасно, существующие деплои ничего не меняют. Egress ложится в ту же абстракциюtype = tunnel, что и AmneziaWG.
Egress из VPN-ссылки — mtbuddy setup egress <ссылка>
Одна команда превращает share-ссылку в туннельный egress (прозрачный L3 + policy-routing — ровно как AmneziaWG), на двух бэкендах:
vless:// vmess:// trojan:// ss://→ локальный sing-box в TUN-режиме (интерфейсsbx0). DC-трафик прокси (SO_MARK=200) направляется через него (fwmark 200 → table 200 → sbx0) — тот же механизм, что у WG-туннеля. VLESS-Reality маскирует хоп выхода под настоящий TLS к реальному сайту — заметно труднее блокировать, чем фингерпринт WireGuard.wireguard://→ нативный kernel-WG/AmneziaWG-туннель (переиспользует существующий флоу; AmneziaWG-ручкиjc/s1/h1..и presharedkey проносятся).
Несколько ссылок → sing-box urltest-пул с failover. Ядро прокси не меняется — оно просто метит сокеты, как для любого туннеля; вся работа в mtbuddy.
Автопереключение в туннель-пуле теперь работает (#323)
Проблема: при двух WG-туннелях в пуле (priority auto) выключение активного туннеля не приводило к переключению на второй рабочий.
Причина: контроллер пула (setup_tunnel.sh, раз в 30с) считал «здоровым» любой поднятый интерфейс — результат проверки достижимости Telegram логировался, но игнорировался (всегда return 0). Поэтому всегда выбирался первый поднятый интерфейс, а мёртвый-но-поднятый активный туннель никогда не пропускался.
Фикс: двухфазный выбор — сперва туннель, который реально достаёт до Telegram (настоящий failover), иначе fallback на первый просто-рабочий (чтобы single-tunnel-деплой не ушёл в чёрную дыру — помечается degraded). Выбор стал «липким»: предпочитает текущий активный туннель, если он ещё здоров, чтобы не флапать каждые 30с. Покрыто новым installer-e2e-тестом.
Проверка fronting-домена — общий хелпер
x25519-проверка пригодности tls_domain вынесена из инсталлера в fronting_domain.zig и теперь шарится тремя точками: mtbuddy install, mtbuddy setup masking --domain … и mtbuddy config doctor --network (где рассинхрон теперь ещё и плюсует к счётчику предупреждений в summary). Плюс тесты на классификацию вывода openssl (single-round x25519 / доступен-без-x25519 / недоступен).
Закалка (из самоаудита)
Перед мержем — два независимых код-ревью + ручной проход; найденное закрыто:
- Эксклюзивность
table 200: sing-box-egress и WG-пул владеют одной таблицей маршрутизации/fwmark 200— теперь взаимоисключающие (поднятие одного снимает другой), иначе таймер пула каждые 30с молча уводил бы маршрут сsbx0. - Валидация ссылок: транспорты кроме
tcp/ws/grpcи неизвестные sing-box'у SS-шифры отклоняются с понятной ошибкой на этапе разбора, а не молча вырождаются в plain-TCP / роняют весь конфиг sing-box. - Порядок старта:
mtproto-proxyупорядоченAfter=/Wants=egress-юнита (drop-in) — на ребутеsbx0и его маршрут поднимаются раньше, чем прокси метит DC-сокеты. - Тихий и проверенный uninstall: удаление больше не сыпет ожидаемыми ошибками (
stop/flush/netns delотсутствующих компонентов) поверх спиннера; в installer-e2e добавлена проверка удаления — и она сразу поймала реальный баг: юнитmtproto-mask-health.serviceникогда не удалялся (путь с двойным.service).
Проверено
- Сквозная проверка egress (systemd-контейнер ubuntu:20.04, arm64):
setup egressс реальной VLESS-Reality-ссылкой →sbx0поднят,table 200 default dev sbx0, и запрос сfwmark 200(тот самыйSO_MARKпрокси) выходит с IP сервера сквозь Reality-туннель. - Failover-e2e (
verify_tunnel_pool_failover):awg0(Telegram-проба заблокирована) +awg1(проба ок) → контроллер выбираетawg1. zig build test, кросс-сборкаx86_64-linux+aarch64-linux,zig fmt— зелёные; installer-e2e на 5 ОС (debian 11/12, ubuntu 20.04/22.04/24.04), включая новую проверку удаления.
🇬🇧 Release notes (EN)
What this release adds
v1.2.0 is an upstream-egress release: a new egress provider turns an ordinary VPN share-link into a clean, hard-to-block exit for the proxy→Telegram hop. Plus a tunnel-pool failover fix (reported in #323) and a fronting-domain-check refactor.
[!NOTE]
The new feature is opt-in via an explicit command — upgrading is safe and existing deployments change nothing. The egress slots into the sametype = tunnelabstraction as AmneziaWG.
VPN-link egress — mtbuddy setup egress <link>
One command turns a share-link into a tunnel egress (transparent L3 + policy routing — exactly like AmneziaWG), on two backends:
vless:// vmess:// trojan:// ss://→ a local sing-box in TUN mode (interfacesbx0). The proxy'sSO_MARK=200DC traffic is policy-routed through it (fwmark 200 → table 200 → sbx0) — the same mechanism the WG tunnel uses. VLESS-Reality camouflages the egress hop as real TLS to a real site — much harder to block than WireGuard's fingerprint.wireguard://→ the native kernel WG/AmneziaWG tunnel (reuses the existing flow; AmneziaWG knobsjc/s1/h1..and presharedkey are carried through).
More than one link → a sing-box urltest failover pool. The proxy relay is unchanged — it just SO_MARKs, as for any tunnel; all the work lives in mtbuddy.
Tunnel-pool failover actually switches now (#323)
The problem: with two WG tunnels in the pool (priority auto), disabling the active tunnel did not fail over to the second working one.
Root cause: the pool controller (setup_tunnel.sh, every 30s) treated any up interface as healthy — the Telegram-reachability probe result was logged but ignored (always return 0). So the first up interface always won, and a dead-but-up active tunnel was never skipped.
The fix: two-pass selection — first a tunnel that actually reaches Telegram (real failover), else fall back to the first merely-usable one (so a single-tunnel deploy doesn't go dark — marked degraded). Selection is now sticky: it prefers the currently-active tunnel while it's still healthy, to avoid flapping every 30s. Covered by a new installer-e2e test.
Fronting-domain check — a shared helper
The x25519 tls_domain suitability check moves out of the installer into fronting_domain.zig, now shared by mtbuddy install, mtbuddy setup masking --domain …, and mtbuddy config doctor --network (where a mismatch now also bumps the summary warning count). Plus tests for the openssl-output classification (single-round x25519 / reachable-without-x25519 / unreachable).
Hardening (from a self-audit)
Before merge: two independent code reviews + a manual pass; findings closed:
table 200exclusivity: the sing-box egress and the WG pool both own the same routing table /fwmark 200— they're now mutually exclusive (setting one up retires the other), otherwise the pool timer would silently steal the route fromsbx0every 30s.- Link validation: transports other than
tcp/ws/grpcand shadowsocks ciphers sing-box doesn't accept are rejected with a clear error at parse time, instead of silently degrading to plain TCP / making sing-box refuse the whole config. - Startup ordering:
mtproto-proxyis orderedAfter=/Wants=the egress unit (drop-in) sosbx0and its route exist before the proxy marks DC sockets on reboot. - Quiet, verified uninstall: uninstall no longer spews expected-failure noise (
stop/flush/netns delof absent components) over the spinner; installer-e2e gained an uninstall check — which immediately caught a real bug: themtproto-mask-health.serviceunit was never deleted (a double-.servicepath).
Verified
- End-to-end egress (ubuntu:20.04 systemd container, arm64):
setup egresswith a real VLESS-Reality link →sbx0up,table 200 default dev sbx0, and afwmark 200request (the proxy'sSO_MARK) egresses with the server's IP through the Reality tunnel. - Failover e2e (
verify_tunnel_pool_failover):awg0(Telegram probe blocked) +awg1(probe ok) → the controller selectsawg1. zig build test, cross-builds forx86_64-linux+aarch64-linux,zig fmt— green; installer-e2e across 5 OSes (debian 11/12, ubuntu 20.04/22.04/24.04), including the new uninstall check.