Безопасный Android-клиент для своих VPN-профилей

Tunguska — открытый Android-клиент для собственных VPN-профилей. Он умеет импортировать профили, выбирать sing-box или Xray + tun2socks, поднимать системный VPN-туннель Android, настраивать маршруты по приложениям и показывать состояние сессии.

Я начал писать его из-за российской практики вокруг VPN. Сетевые блокировки никуда не делись, но теперь часть проблем приходит уже с телефона: приложение видит активный VPN, отправляет этот признак на сервер, а при трафике через туннель ещё и светит выходной IP или адрес VPS. По данным РБК и Meduza, площадки должны делиться сведениями о новых выявленных VPN с регулятором; в такой схеме выходной IP может попасть в общие списки блокировки.

У меня это проявилось на телефонах родственников: часть российских приложений периодически переставала работать при активном VPN. Сторонние клиенты завязаны на чужой релизный цикл ядра, транспортов и Android-части. Быстрого решения под мои требования не нашлось, поэтому я начал писать Tunguska.

Репозиторий

На момент этой заметки актуальный публичный релиз — v0.7.2, опубликован 10 мая 2026 года. APK лежат в GitHub Releases, Google Play пока не участвует.

Что детектят приложения

Сетевые блокировки продолжаются. По данным «Коммерсанта» со ссылкой на Роскомнадзор, к концу февраля 2026 года в РФ ограничили доступ к 469 VPN-сервисам. В начале апреля 2026 года РБК, а затем Meduza писали, что Минцифры просило крупные российские платформы ограничивать доступ пользователям с активным VPN и передавать сведения о новых найденных VPN регулятору. В обновлении исследования RKS Global от 16 апреля 2026 года все 30 проверенных российских Android-приложений детектировали VPN, 20 из них активно блокировали или ограничивали функциональность.

Среди методов: NetworkCapabilities.TRANSPORT_VPN, интерфейсы вроде tun0, записи в /proc/net/tcp, системный прокси, список установленных VPN-клиентов, эвристика MTU и выходной IP при запросе через туннель.

Android официально помечает VPN-сеть как TRANSPORT_VPN. VPN-клиенты работают через системный API VpnService: приложение получает локальный TUN-интерфейс и отдаёт Android правила маршрутизации. Это нормальная часть платформы, но в российских приложениях она стала ещё одним источником сигнала об активном VPN.

Активный VPN Tunguska от Android не скрывает. На уровне системы иначе нельзя. Клиент занимается тем, что может контролировать: локальные поверхности, мосты между движками, раздельную маршрутизацию, поддержку форматов профилей и отображение плана трафика.

Почему отдельный клиент

Серверная часть у собственного VPN обычно под контролем: транспорт меняется на сервере, логи доступны, внешний доступ проверяется отдельными инструментами. На телефоне остаётся слой, который из серверных логов не виден: системный VPN API, TUN-интерфейс, DNS, маршрутизация по приложениям, жизненный цикл фоновой службы, смена сети, фоновые ограничения Android и ограничения конкретной прошивки. Tunguska закрывает свою часть этого слоя: быстрое обновление форматов профилей, явная совместимость с sing-box или Xray + tun2socks, маршруты по приложениям, DNS-режимы и статус сессии, который снимается после проваленной проверки маршрута.

Tunguska это клиент для людей, у которых уже есть свои профили, свои серверы или свой провайдер.

Что есть в 0.7.2

(скриншоты немного битые — в процессе обновления)

Tunguska хранит библиотеку профилей, даёт их импортировать и редактировать, показывает совместимость с движками, поднимает системный VPN-туннель Android и показывает, куда пойдёт трафик.

В приложении четыре основных раздела.

Главная — повседневный экран для активного профиля, подключения, IP до и после старта, краткой статистики и текущего состояния.

Профили — библиотека профилей с импортом из ссылки, JSON и QR. Перед сохранением приложение проверяет дубли и совместимость.

Маршруты — полный туннель, туннель для приложений, переиспользуемые политики, шаблоны, пользовательские правила и офлайн-проверка маршрута.

Настройки — безопасность, резервные копии, экспорт аудита с вычищенными секретами, автоматизация, обновления и расширенная диагностика.

Интерфейс показывает технические детали до подключения. Для VLESS XHTTP, WireGuard-конфига или Shadowsocks prefix-ссылки сразу видно выбранный движок, ограничения и причину ошибки.

Есть два языка, обновление русского немного запаздывает.

Импорт профилей

При импорте Tunguska раскладывает профиль в типизированную модель и сохраняет параметры, которые влияют на запуск и совместимость.

В VLESS важны транспорт, поля REALITY, flow, packetEncoding, spx/spiderX, проверки ML-DSA-65 и полный снимок query-параметров. У Shadowsocks отдельно обрабатываются prefix-ссылки. WireGuard импортируется и из ссылки, и из обычного конфига с [Interface] и [Peer]. Поддержка этих полей зависит от выбранного движка, поэтому импорт хранит их без потери. Для части протоколов нет удобного публичного формата, который экспортируется и импортируется обратно без потери смысла.

Поддерживаются импорт и редактирование таких семейств:

  • VLESS + REALITY: TCP, HTTP, WebSocket, gRPC, HTTP Upgrade, QUIC, XHTTP и SplitHTTP с учётом выбранного движка;

  • VMess;

  • Trojan;

  • Shadowsocks, включая prefix-ссылки через sing-box;

  • SOCKS и HTTP-прокси;

  • Hysteria1 и Hysteria2;

  • TUIC;

  • WireGuard;

  • SSH;

  • AnyTLS, ShadowTLS и NaiveProxy через канонический JSON.

Публичные ссылки Tunguska экспортирует там, где формат действительно подходит. WireGuard, SSH, AnyTLS, ShadowTLS и NaiveProxy сохраняются через канонический JSON, потому что публичные форматы ссылок здесь не дают полного обратного импорта без потери параметров.

При импорте приложение проверяет профиль до записи в хранилище. Небезопасные TLS-флаги, отладочные входы и совместимость через открытый локальный прокси отбрасываются сразу. После импорта туннель не стартует сам. Сначала пользователь видит, что именно собирается сохранить.

Два движка под капотом

Основной путь запуска — sing-box через libbox: широкий набор протоколов, DNS, маршрутизация, TUN внутри встроенного движка, меньше промежуточных мостов. Xray + tun2socks остаётся веткой совместимости для профилей, которым нужен Xray. В первую очередь это VLESS + REALITY с XHTTP, SplitHTTP или ML-DSA-65. Через эту же ветку запускаются VMess, Trojan, обычный Shadowsocks, SOCKS и HTTP-прокси, но уже с ограничениями моста tun2socks.

Два пути нужны из-за несовпадающей поддержки. XHTTP, SplitHTTP и ML-DSA-65 сейчас логичнее вести через Xray; WireGuard, TUIC и большая часть sing-box-экосистемы живут в libbox. Поэтому выбор движка остаётся явной частью профиля.

Неподходящая комбинация блокируется до подключения. VLESS XHTTP не сваливается в «почти TCP», WireGuard не запускается через Xray + tun2socks, неподдерживаемый DNS-режим или правило маршрутизации показываются как проблема совместимости.

В интерфейсе для этого есть сводка по выбранному пути запуска и рекомендации по совместимости.

Маршрутизация

Маршрутизация в Tunguska хранится как видимая политика с фиксированным порядком применения правил. Поддерживаются полный туннель, режим «только выбранные приложения» и режим исключений. Android разрешает выбрать только один тип правил по приложениям на VPN-сессию, поэтому изменения сохраняются в профиле и применяются после переподключения. Конфиги туннеля по приложениям переиспользуются: их можно привязать к профилю или временно перекрыть глобальной политикой; один общий список приложений быстро мешает, когда у профилей разные задачи.

Шаблоны применяются как обычные правки политики. «Россия напрямую» добавляет российские доменные зоны и GeoIP RU, «Обход локальных/частных сетей» — локальные сети и домены, «Блокировка рекламы» — явные правила блокировки. Правила дорабатываются.

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

Тест маршрута работает офлайн: по пакету приложения, домену или IP он показывает итоговое действие правила без сетевого запроса. Порядок применения зафиксирован: loopback всегда остаётся локальным, затем применяется Android-правило по приложениям, потом явные правила профиля в сохранённом порядке, затем сгенерированные правила шаблонов и регионального обхода, после них — правило по умолчанию.

DNS

Жёсткая замена DNS ломает локальные имена, корпоративные зоны и часть провайдерских профилей. Постоянный публичный запасной DNS тоже не всегда подходит, если пользователь ожидал резолверы текущей сети. Поэтому DNS в Tunguska выбирается явно.

По умолчанию в модели профиля стоит Системный DNS. В Xray + tun2socks это совместимый режим с публичным запасным DNS; если нужны именно резолверы текущей сети Android, есть DNS сети Android: при старте приложение берёт DNS-серверы из активной сети Android, находящейся вне VPN. Если Android не отдаёт такие серверы, старт падает с ошибкой. Это особенно полезно для Xray + tun2socks, где хочется убрать неявный публичный запасной DNS.

Есть режимы DNS через VPN и Свой защищенный DNS. В sing-box они компилируются напрямую; в Xray + tun2socks поддержка более узкая, поэтому неподходящие формы режутся до старта.

Эта настройка помогает отделить проблемы профиля от проблем конкретной сети: Wi-Fi может отдавать нужные DNS, LTE — нет.

Безопасность без обещаний невидимости

Невидимость VPN на Android здесь не заявляется. Система показывает VPN-индикатор и отдаёт приложениям часть сетевых признаков; root и привилегированные наблюдатели видят ещё больше.

Клиент закрывает поверхности, которые контролирует сам.

У Tunguska нет неаутентифицированного локального прокси и включённых API управления для Xray или sing-box. В ветке Xray + tun2socks локальный SOCKS-мост слушает только 127.0.0.1; порт из диапазона 20000..49999 и учётные данные для него генерируются заново на каждый старт через SecureRandom, а tun2socks получает уже готовый socks5://user:pass@127.0.0.1:port.

Файлы движка лежат в закрытом хранилище приложения, временные конфиги удаляются после старта нативного движка, профили и резервные копии хранятся локально в шифрованном виде. Экспорт аудита вычищается: секреты заменяются хэшами и скрытыми полями.

Аналитики, рекламных идентификаторов и телеметрии по умолчанию нет. Проверка публичного IP нужна только для экрана состояния.

Автоматизация выключена по умолчанию. Если её включить, внешний запуск и остановка идут через токен и тот же контур управления, что интерфейс, уведомление и плитка быстрых настроек. Отдельного публичного API нет.

Усиленная защита включается вручную. В этом режиме уведомления меньше раскрывают детали, чувствительные экраны просят Android запретить скриншоты, скопированные ссылки профилей и токены автоматизации помечаются как чувствительные данные буфера обмена и очищаются через короткое время. Android VPN-индикатор остаётся на месте.

В sing-box-пути по умолчанию включена защита от трафика с неизвестным владельцем: если Android не смог сопоставить пакет на TUN с приложением-владельцем, такой трафик режется. Правило ограничено TUN-входом и не цепляет внутренние loopback-мосты Tunguska. Xray + tun2socks эту специфичную для sing-box защиту не применяет, там другая топология.

Единое состояние

После ошибки старта или смены сети экран и уведомление должны подтягивать свежий снимок службы. Старый статус вводит в заблуждение: пользователь видит рабочую сессию, хотя движок уже перезапускается. В Tunguska запуск, остановка и переподключение идут через общий контур управления движком. «Главная», уведомление, плитка быстрых настроек, автоматизация и диагностика читают один снимок состояния.

Рабочей считается только сессия, где движок поднят и маршрут проходит проверки; живой движок с падающим маршрутом получает «Сбой связи», а запрос ручного действия уходит в статус внимания.

Плитка быстрых настроек показывает только старт, стоп и статус. Там нет имени профиля, адреса сервера, выходного IP или скорости.

Уведомление учитывает приватность: при выключенной усиленной защите показывает больше деталей, при включенной — только нейтральный статус.

Восстановление после сбоев сети

На Android VPN-сервис может оставаться живым, когда полезный трафик уже не проходит через ожидаемый путь.

Сервис делает две проверки: нативную проверку живости встроенного движка и низкочастотную проверку активного маршрута. После повторных неудач сессия получает состояние «Сбой связи» и перезапускается. При смене Wi-Fi на мобильную сеть или обратно активный путь тоже рестартится; старое сетевое состояние Android не используется как постоянное.

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

Если маршрут подозрительный, рабочий статус снимается.

Тесты

Приложение сильно покрыто тестами, иначе вести разработку было очень тяжело. Unit-тесты закрывают парсер и генерацию конфигов, UI-тесты — основные экраны. Отдельно проверяются офлайн-расчёт маршрута, traffic-probe в живом туннеле, UDP-сценарии и логика реакции на смену сети по умолчанию Android. Сквозные проверки закрывают жизненный цикл профиля на живых конфигурациях, матрицу sing-box/Xray + tun2socks, сверку маршрутизации, автоматизацию и вспомогательные проверки трафика.

Как поставить

Релизы лежат здесь:

https://github.com/Acionyx/tunguska/releases

В v0.7.2 опубликованы внутренние APK без отладки и файлы с контрольными суммами. Для обычного Android-устройства нужен arm64-v8a. Для локального Android Emulator есть x86_64. armeabi-v7a/32-bit сборок нет: обязательные Xray/tun2socks/vpnhelper артефакты в этой линейке есть только для arm64-v8a и x86_64.

Поддерживаемая платформа — Android 8.0+ (minSdk 26), сборка таргетит SDK 36.

Установка через adb обычная:

adb install tunguska-v0.7.2-arm64-v8a-internal.apk

Базовый путь: импортировать профиль в «Профилях», проверить предупреждения, сохранить, выбрать профиль активным и подключиться на «Главной». После старта стоит проверить выходной IP и отдельно прогнать «Проверку маршрута» для важных доменов и пакетов приложений.

В приложении есть проверка обновлений через GitHub Releases, она забирает только метаданные релиза. Проверка включена по умолчанию и срабатывает не чаще раза в день. APK автоматически не скачивается и не устанавливается; Tunguska только сообщает, что вышел новый релиз, и даёт перейти на страницу релизов. Нежелательную версию можно скрыть.

Если собираете из исходников, смотрите README. Для libbox-android может понадобиться доступ к GitHub Packages или локальный Maven override.

Ограничения текущего релиза

Проект молодой, по факту даже младенческий. Ставить его стоит, если готовы смотреть диагностику, проверять реальные сценарии и писать отчёты.

Совместимость с произвольными подписками провайдеров не заявлена.

iOS вне фокуса до доведения Android-версии.

Google Play нет. На текущем этапе релизы идут через GitHub, с файлами контрольных сумм рядом с APK.

Зачем я сюда пишу?

Сейчас больше всего нужны воспроизводимые проверки на реальных устройствах.

В багрепортах нужны условия воспроизведения: устройство и версия Android, сеть, тип профиля без секретов, выбранный движок и режим маршрутизации. Дальше — ожидаемый результат, фактический результат и проверка после переподключения, сна телефона или перезагрузки.

Особенно полезны баги на границе Android-сети: сон и пробуждение телефона, смена Wi-Fi и LTE, UDP, push-уведомления. Отдельно интересны расхождения между моделью Tunguska и фактическим поведением: туннель для приложений против «Проверки маршрута», разные результаты на sing-box и Xray + tun2socks, зависшее состояние интерфейса после ошибки. Проблемы на маленьких экранах и качество диагностики тоже лучше приносить в issues.

Не прикладывайте реальные ссылки профилей, ключи, UUID, приватные ключи и токены.

GitHub

Комментарии (0)