Безопасный 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, приватные ключи и токены.
ссылка на оригинал статьи https://habr.com/ru/articles/1039264/