sing-box-lx: как, почему и зачем я завёл fork сетевого ядра — XHTTP + AWG2 для всех

от автора

Если вы пользовались моим LxBox или десктопным лаунчером, то могли натыкаться на оранжевый баннер: «с этим узлом, скорее всего, не соединится». Так клиент честно сознаётся, что наткнулся на узел с транспортом XHTTP, а ядро sing-box, на котором всё крутится, его не умеет. В этот момент лаунчер тихо даунгрейдит конфиг до HTTPUpgrade.

А еще сразу как я выпустил приложение пришли запросы с AWG/AWG2 и такие: сделай! а я сюда

В какой-то момент я устал ждать и собрал свой форк ядра — sing-box-lx. Сегодня расскажу, зачем он понадобился, что внутри, и почему главное в нём — не сами фичи, а то, как он сделан.

Сначала — кто на ком стоит

Чтобы было понятно, напомню конструкцию. Лаунчер (и десктопный, и LxBox) — это GUI-оркестратор: он собирает конфиг и рулит маршрутизацией. А всю сетевую работу делает ядро sing-box — по сути швейцарский нож сетевой маршрутизации. Я всегда делал к этому ножу удобную ручку. Ну лааадно, еще и вилку приделал и ложку… ну особенно на Андройде там..

singbox-launcher / LxBox  (GUI, ручка)        ↓  генерация конфига  sing-box  (ядро, нож)   ← вот тут не хватало пары лезвий        ↓   TUN / трафик

Проблема в том, что у ножа не хватало ровно двух лезвий. Причём именно тех, что нужны, когда обычный туннель уже не проходит. Я уже и sing-box связал в одну связку с ByeByeDPI, но все равно не хватало..

Чего не хватало — и почему именно этого

1. XHTTP — клиентский транспорт из мира Xray. Если совсем просто: он заворачивает трафик в обычные на вид HTTP-запросы, и со стороны DPI это выглядит как банальный веб-сёрфинг, а не «подозрительный туннель». У upstream sing-box клиентской поддержки XHTTP нет — поэтому лаунчер и вынужден был даунгрейдить такие узлы.

2. AmneziaWG 2.0 — обфусцированный WireGuard. Объясняю максимально просто: у обычного WireGuard рукопожатие имеет узнаваемую сигнатуру, и DPI её прекрасно режет. AmneziaWG подмешивает «мусорные» пакеты и магические заголовки (параметры JcS1–S4H1–H4I1–I5), и сигнатура ломается — туннель перестаёт выглядеть как WireGuard. В sing-box есть обычный wireguard, но обфускации AmneziaWG как клиентского endpoint — нет.

Обе фичи про одно: пройти там, где обычный туннель уже завернули. Это вся их суть.

Ну не с нуля же делать фичи?

Конечно нет. Писать сетевое ядро с нуля это просто не реально, да и фичи без рефренсов — это тонны работы и грабли на каждом шагу, а sing-box уже мощный, живой и активно развивается. Как мы обычно делаем? — форкнуть и допилить.

Вот только форки обычно гниют. Знакомая история: кто-то форкает крупный проект, добавляет пару своих хотелок, а через полгода апстрим уехал вперёд, смержиться уже невозможно, и форк превращается в заброшенную поделку с дырами в безопасности. Так вот и получается что все ядра что вы можете найти или перегнили или опухли или просто уродливы…

Я не хотел использовать форк. И вот тут начинается главная мысль, ради которой и затевалась статья.

Кардинальное отличие: форк — это набор патчей, а не новый проект

Я с самого начала строил sing-box-lx так, чтобы он не мог сгнить. Три правила:

Минимальное касание апстрима. Почти весь мой код — это новые файлы: Makefile.lx, воркфлоу .github/workflows/lx-*.yml, тестовые конфиги в lx-test/. Единственная правка апстрим-файла — один помеченный блок // lx в сборщике Android-библиотеки. Всё. Если завтра выйдет новый sing-box, мне почти нечего переносить руками.

Автоматический ребейз на апстрим. Раз в неделю CI сам берёт новейший стабильный тег upstream и пытается перенести на него мои коммиты:

свежий стабильный тег sing-box        ↓  CI ребейзит мои патчи        ↓ ┌─ чисто + собралось + прошло check → открыть PR ├─ конфликт → завести issue с точным списком // lx-швов └─ уже актуально → ничего не делаем

Важная деталь: воркфлоу никогда не делает force-push в мою основную ветку. Он лишь готовит ветку и зовёт меня глазами посмотреть. То есть машина делает скучную работу, а решение остаётся за человеком.

Воспроизводимые сборки. Как и в лаунчере, мой принцип: опубликованный релиз обязан соответствовать открытому коду. Все бинарники собираются только через GitHub CI/CD — никаких «я тут локально собрал, доверяйте».

Что я выкинул — это тоже фича

sing-box умеет кучу всего, в том числе серверные и совсем экзотические вещи. Мне в клиенте они не нужны, поэтому набор тегов сборки я осознанно урезал до клиентского: убрал серверную выдачу сертификатов (acme), tailscale и AI-прокси-модули. А добавил ровно три вещи — with_xhttpwith_awg и with_purego (последний — чтобы naive/cronet кросс-собирались без CGO).

Чтобы ничего не разъезжалось, строка тегов живёт в одном месте — Makefile.lx, и CI с релизом берут её оттуда же. Никакого копипаста тегов по десяти местам:

with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_clash_api,with_naive_outbound,with_purego,badlinkname,tfogo_checklinkname0,with_xhttp,with_awg

Собрал — проверил живьём

Я не верю в «скомпилировалось — значит работает», поэтому обе фичи гонял против реальных серверов, ой и спасибо тестерам, которые это гоняют со мной, господи, благослави энтузиастов OS:

  • XHTTP — против Xray (3x-ui). Режимы packet-up/auto работают: рукопожатие, DNS, HTTPS, скачивание файлов. Режим stream-one пока с известным багом по фреймингу — чиню, но это не блокер.

  • AmneziaWG 2.0 — против живого AWG2-сервера: рукопожатие, keepalive, реальный трафик. Обфускация на месте.

Итог собран в релиз v1.13.13-lx.3: 6 десктопных бинарников (linux/macOS/windows × amd64/arm64), две AAR-библиотеки (libbox для Android — да, та самая, что внутри LxBox) и SHA256SUMS. Всё зелёное в CI.

sing-box-lx ├─ desktop ×6  → singbox-launcher └─ libbox.aar  → LxBox (Android)

То есть одно ядро готово кормить сразу оба моих клиента — и десктоп, и мобилку. Осталось подружить их по-настоящему: научить лаунчер собирать честный XHTTP вместо даунгрейда — и тот самый оранжевый баннер можно будет наконец убрать. Но это уже следующая история.

Чуть-чуть про дисциплину

Как и в лаунчере, тут всё ведётся через спеки: что и зачем меняется, какая зона касается апстрима, как закрывается задача. Для тонкого форка это критично — когда придёт время ребейза, я по маркерам // lx за минуту вижу все свои швы, а не вспоминаю «где я там что трогал полгода назад».

Забрать и потрогать

Репозиторий: https://github.com/Leadaxe/sing-box-lx — там релизы, теги сборки, воркфлоу CI/ребейза и тестовые конфиги XHTTP/AWG2.

Отдельная уважуха апстриму — SagerNet/sing-box за прекрасное ядро, команде Xray за XHTTP и Amnezia за идею обфускации WireGuard, ну понятно из этих небожителей никто не прочитает.
Ну а мне приятно их поблагодарить.

Если у вас есть сценарии, где XHTTP или AmneziaWG ведут себя не так, как ожидалось, — заводите issue, это ценнее любой новой фичи. Ну а если зашло — вы знаете: звёздочка ⭐, форк ↑↓ и, если совсем хорошо, pull request </>.

Всем, кто дочитал, — спасибо. Мне правда приятно делать штуки, которые вам заходят.

ссылка на оригинал статьи https://habr.com/ru/articles/1045706/