Свой прокси против DPI: клиент для Android
В прошлой статье мы подняли сервер Xray с VLESS, XTLS Vision и REALITY и настроили клиент v2rayN на Windows. Здесь — то же для Android. Телефон чаще остальных устройств ходит по чужим сетям: мобильный интернет, Wi-Fi в кафе, роуминг, — и именно на нём прокси нужнее всего.
В статье: как устроен VPN в Android и почему он в системе один; что находится внутри приложения v2rayNG; как установить его с проверкой подписи; как выдать телефону отдельный ключ и ввести его QR-кодом; какие настройки стоит поменять и почему — IPv6, routeOnly, DNS, обход локальной сети; как устроена маршрутизация и что делать с правилами, подписанными по-китайски; как проверить результат по журналу сервера и какие подводные камни ждут при переключении между VPN и между сетями.
Сервер и протоколы здесь заново не разбираются: они описаны в предыдущей статье, а про DNS, TCP, UDP и TLS есть части серии «Сети для самых маленьких». Нужное для понимания повторяется в одну-две фразы.
Адреса в статье документационные: сервер 203.0.113.10 и 2001:db8::10, сайт-маска www.example.com, домашняя сеть 192.168.1.0/24 и 2001:db8:1::/64, телефон выходит в интернет с адреса 198.51.100.23. Ключи — заглушки в угловых скобках. Версия клиента — v2rayNG 2.3.11 (внутри Xray-core 26.10.10), Android 7.0 и новее; всё описанное проверено на Xiaomi 13T Pro с Android 16. Названия пунктов меню даны по английскому интерфейсу, в скобках — как они называются в русском.
Как устроен VPN в Android
Приложение само не может перехватить чужой трафик. Для этого в Android есть системный интерфейс VpnService. Порядок такой:
- Приложение просит у системы разрешение. Система показывает окно «Запрос на подключение»: приложение хочет настроить VPN, весь сетевой трафик может быть перехвачен. Без вашего согласия дальше ничего не происходит.
- После согласия приложение описывает сетевой интерфейс через
VpnService.Builder: его адрес (addAddress), маршруты (addRoute), адреса DNS-серверов (addDnsServer), MTU (setMtu) и, если нужно, список приложений, которые в туннель попадают или не попадают (addAllowedApplication,addDisallowedApplication). - Метод
establish()создаёт в системе виртуальный интерфейс TUN и отдаёт приложению файловый дескриптор. TUN — интерфейс, который вместо провода отдаёт программе IP-пакеты: всё, что система отправляет по маршрутам туннеля, программа читает из дескриптора как сырые пакеты, а пакеты, которые она в него записывает, система принимает как пришедшие из сети. - Приложение делает с пакетами что хочет: шифрует и отправляет на свой сервер, фильтрует, считает. Для этого его собственное соединение с сервером должно идти мимо туннеля, иначе пакеты, везущие пакеты, ходили бы по кругу. Поэтому приложение либо помечает сокет как «вне VPN» (
protect), либо исключает само себя из туннеля, и v2rayNG делает второе: в его коде при запуске стоитaddDisallowedApplicationсо своим именем пакета.
Отсюда два следствия, которые важны на практике.
VPN в системе один. В Android одновременно работает только один VpnService: когда другое приложение вызывает establish(), предыдущее отзывается. В строке состояния виден значок ключа. Если на телефоне стоят домашний IKEv2, WireGuard и v2rayNG, включить можно любой один, а запуск следующего отключит предыдущий. Это не ошибка, так задумано.
Маршруты решают, что попадает в туннель. Всё, что не попало ни в один маршрут туннеля, уходит в сеть как обычно. Это нам пригодится ниже: от того, какие маршруты добавил v2rayNG, зависит, попадёт ли в туннель, например, IPv6.
Что внутри v2rayNG
v2rayNG — приложение с открытым кодом от автора v2rayN (того клиента, что мы ставили на Windows). Оболочка написана на Kotlin, а сети занимаются две программы внутри неё:
- Xray-core — то же ядро, что на сервере (в приложении оно подключено как библиотека на Go). Принимает соединения на локальном порту, решает по правилам маршрутизации, куда их отправить, и умеет выходить через VLESS с REALITY.
- hev-socks5-tunnel — программа на C, которая читает пакеты из TUN. Такие программы называют tun2socks. У неё есть собственная реализация стека TCP/IP в пользовательском пространстве: она собирает из IP-пакетов TCP-соединения и UDP-датаграммы и для каждого открывает запрос к обычному SOCKS5-прокси. В v2rayNG это включено по умолчанию — пункт Use Hev TUN («Использовать Hev TUN»). Если его выключить, пакеты из TUN читает само ядро Xray.
SOCKS5 (RFC 1928) — простой протокол прокси: клиент сообщает прокси адрес назначения, а дальше идёт поток. Для TCP это команда CONNECT. Для UDP есть команда UDP ASSOCIATE: клиент получает у прокси адрес, на который можно отправлять датаграммы, каждая из которых несёт в заголовке адрес назначения. Конфигурация hev-socks5-tunnel в приложении это и показывает: прокси 127.0.0.1:10808, режим UDP udp, то есть через UDP ASSOCIATE. На порту 10808 слушает Xray-core, у которого входом служит как раз SOCKS5.
приложения Android
│ IP-пакеты (выбирает система по маршрутам VPN)
▼
интерфейс TUN ────► hev-socks5-tunnel ──► SOCKS5 127.0.0.1:10808
│
Xray-core: sniffing + правила
┌───────────────┼───────────────┐
▼ ▼ ▼
proxy direct block
VLESS + Vision + REALITY мимо прокси отбросить
│
▼
сервер Xray ────► интернет
На выходе из ядра три варианта, те же, что в v2rayN: proxy — отправить на сервер по VLESS и REALITY, direct — отправить напрямую с телефона (ядро само исключено из туннеля, так что петли нет), block — отбросить.
С UDP на сервере то же, что и в прошлой статье: каждая датаграмма едет внутри того же TCP-соединения с сервером (мультиплексор XUDP), на сервере её распаковывают, и она уходит в интернет настоящим UDP. В журнале сервера это видно по записи: у UDP-вызовов источник помечен tcp: — UDP приехал по TCP. Образец ниже, в разделе «Смотрим руками».
Установка
Проект раздаёт приложение на странице релизов GitHub: github.com/2dust/v2rayNG/releases. Других официальных источников в описании проекта нет. В магазинах приложений могут встречаться клоны с похожими названиями: ставьте только отсюда. Берите последний стабильный релиз (на странице он без пометки Pre-release; на 23 октября 2026 это 2.3.11 от 11 октября, версии 2.3.12 и 2.3.13 помечены как предварительные).
Файлов в релизе много. Для большинства современных телефонов нужен v2rayNG_2.3.11_arm64-v8a.apk:
| Файл | Для чего |
|---|---|
…_arm64-v8a.apk |
64-разрядные ARM-процессоры — почти все телефоны последних лет |
…_armeabi-v7a.apk |
старые и бюджетные 32-разрядные телефоны |
…_universal.apk |
любой процессор, файл примерно вдвое больше |
…_x86.apk, …_x86_64.apk |
эмуляторы и редкие планшеты |
…-fdroid_….apk |
то же приложение с другим именем пакета (требование каталога F-Droid); обычному пользователю не нужно |
Минимальная версия Android — 7.0. Если не знаете архитектуру телефона, берите universal.
Проверка файла
Рядом с APK лежат его подпись ….apk.sig и открытый ключ автора v2rayN-public-key.asc, а страница релиза показывает SHA-256 каждого файла. SHA-256 — контрольная сумма: изменился хоть один байт файла, и она совсем другая. Подпись PGP — другая защита: автор подписывает файл закрытым ключом, а любой, у кого есть открытый ключ, проверяет, что файл подписан именно им и не менялся. Проверять надёжнее на компьютере, пока файл не попал на телефон:
sha256sum v2rayNG_2.3.11_arm64-v8a.apk
b9aed943bdace5531b2268eb57479db769fffa5463adecb1559c8c62d5864b6f v2rayNG_2.3.11_arm64-v8a.apk
gpg --import v2rayN-public-key.asc
gpg --verify v2rayNG_2.3.11_arm64-v8a.apk.sig v2rayNG_2.3.11_arm64-v8a.apk
В ответе gpg должна быть строка Good signature from "2dust <2dust-noreply@github.com>" и отпечаток ключа:
Primary key fingerprint: 76945 E9F3E 9A168 F8070 F1958 05D66 1C134 DFAF6 8903C 19946
Предупреждение This key is not certified with a trusted signature — нормально: gpg не знает, что ключ принадлежит этому автору, пока вы сами не заверите ключ. Отпечаток стоит сравнить с тем, что вы видите на странице проекта или у другого надёжного источника. В PowerShell тот же SHA-256 считается так:
Get-FileHash .\v2rayNG_2.3.11_arm64-v8a.apk -Algorithm SHA256
Если проверять на компьютере неудобно, скачайте файл прямо на телефоне со страницы релиза: GitHub отдаёт его по HTTPS, а при обновлениях Android всё равно откажется ставить поверх приложения файл, подписанный чужим ключом.
Установка на телефоне
- Скачайте APK в браузере телефона (или перенесите с компьютера).
- Android спросит разрешение «Установка неизвестных приложений» для браузера или файлового менеджера, через который вы открываете файл. Разрешите. После установки это разрешение можно отозвать в настройках.
- Play Protect может предложить проверить приложение: это нормально для файлов вне магазина.
- Первый запуск: приложение может попросить разрешить уведомления. Разрешите: уведомление держит службу VPN в рабочем состоянии, фоновые службы без него система останавливает охотнее.
Обновляется приложение вручную: боковое меню → Check for update («Проверить обновление»); есть переключатель, разрешающий предварительные версии. Не ставьте их без нужды.
Ключ для телефона
В прошлой статье мы завели на сервере скрипт xray-user. Телефону — свой пользователь, как и ноутбуку: потеряли телефон — отозвали только его ключ, остальные устройства не заметят.
xray-user add alice-phone
Скрипт печатает две ссылки vless://…: на IPv4-адрес сервера и на IPv6. Ссылка — секрет, в ней UUID. Её устройство должно получить, не оставив копий в чатах и почте. Удобнее всего QR-код, в котором закодирована та же строка:
apt install qrencode
xray-user link alice-phone | head -n1 | tr -d '\n' | qrencode -t PNG -s 9 -o alice-phone.png
xray-user link печатает ссылки существующего пользователя; head -n1 берёт первую, на IPv4, а tr -d '\n' убирает перевод строки, чтобы в код не попал лишний символ. Скопируйте файл на компьютер, откройте на экране, отсканируйте телефоном и удалите: это ключ в виде картинки. Не отправляйте её в мессенджеры и не фотографируйте экран с кодом. Вместо файла можно вывести код прямо в терминал: qrencode -t ansiutf8; на тёмной теме терминала иногда нужен инвертированный вариант -t ansiutf8i, а если телефон код не читает, увеличьте шрифт терминала.
Первое подключение
- В v2rayNG нажмите + («Добавить»), выберите Import from QR code («Импорт из QR-кода») и разрешите доступ к камере. Если кода нет, скопируйте ссылку и выберите Import from Clipboard («Импорт из буфера обмена»).
- Появится сообщение «Success» и профиль в списке. Его название приложение берёт из конца ссылки после знака
#, у нас этоalice-phone-ipv4. - Нажмите на профиль, чтобы выбрать его, и на кнопку запуска. Android покажет окно запроса VPN — подтвердите. В строке состояния появится ключ, а в уведомлении — статус.
- Статус внизу экрана: «Connected. Tap to check connection» («Подключено. Нажмите, чтобы проверить соединение»). Нажмите: приложение отправит через туннель запрос на
https://www.gstatic.com/generate_204и покажет время ответа, «Connection succeeded in …» («Соединение установлено за …»). Адрес проверки меняется в Settings → Advanced Settings → Real delay test URL.
Параметры профиля видны, если выбрать пункт редактирования: адрес и порт сервера, id (UUID), flow (xtls-rprx-vision), encryption (none), TLS = reality, SNI, Fingerprint (chrome), PublicKey, ShortId. Это те же параметры, что описаны в таблице про ссылку vless:// в прошлой статье, и менять их вручную не нужно.
После первого подключения приложение уже работает, но с настройками «из коробки», которые для нашей схемы не оптимальны. Дальше — что именно поменять и почему.
Настройки: что поменять и почему
Все настройки — на одной прокручиваемой странице Settings («Настройки»), разделённой на сворачиваемые группы: UI settings, VPN Settings, Core Settings, Mux Settings, Fragment Settings, Observatory Settings, Advanced Settings и Mode Settings. Ниже — только то, что влияет на нашу схему.
Режим работы
В Mode Settings → Mode («Настройки режима → Режим») два варианта:
- VPN (по умолчанию) — приложение создаёт TUN и пропускает через него весь трафик, как TUN-режим v2rayN на Windows. Работают все приложения, включая те, что про прокси не знают, и UDP.
- Proxy only («Прокси») — TUN не создаётся, VPN-слот не занят. Приложение только слушает локальный прокси
127.0.0.1:10808(на этом порту ядро принимает и SOCKS5, и HTTP), а трафик в него отправляют те программы, которым его адрес указан вручную: некоторые браузеры, Telegram. Остальные ходят напрямую. Удобно, когда VPN-слот нужен другому приложению, например домашнему IKEv2.
Дальше всё про режим VPN.
IPv6
VPN Settings → Enable IPv6 («Использовать IPv6») по умолчанию выключено. Что это значит, видно из того, как v2rayNG настраивает VpnService.Builder:
- IPv4: интерфейсу выдаётся адрес, и добавляются маршруты на публичные диапазоны IPv4 (об обходе локальной сети ниже).
- IPv6: только если пункт включён, интерфейс получает IPv6-адрес и маршруты
2000::/3(весь выделяемый сейчас глобальный IPv6) иfc00::/18. Если выключено, в туннеле IPv6 нет вовсе.
Система выбирает маршрут по самому точному совпадению. Без IPv6-маршрутов в туннеле пакет к IPv6-адресу сайта туннель не получает и уходит в сеть как обычно, мимо прокси. На домашнем Wi-Fi роутер раздаёт телефону настоящий глобальный IPv6-адрес по SLAAC — автонастройке адреса по анонсам роутера, без DHCP-сервера (пятнадцатая часть), и все сайты, доступные по IPv6, видят ваш домашний адрес, а не адрес сервера. Классическая «утечка IPv6». На мобильных сетях с IPv6 то же. Включите пункт.
Рядом стоит Prefer IPv6 («Предпочитать IPv6»): при разрешении имён приложение предпочитает IPv6-адреса. Включать не обязательно: приложения с IPv6 справляются сами.
Sniffing и routeOnly
Core Settings → Enable Sniffing («Анализировать трафик») по умолчанию включено, а Enable routeOnly («Домен только для маршрутизации (routeOnly)») — выключено. Та же ловушка, что на Windows. Ядро достаёт из первых байтов соединения имя сайта (из SNI в TLS ClientHello или из заголовка Host) и по умолчанию подменяет им адрес назначения: на сервер уходит не «соединись с таким-то IPv6-адресом», а «соединись с example.com», и сервер сам резолвит имя и выбирает IPv4. Включение IPv6 в туннеле от этого толку не даёт: адреса, которые выбрало приложение, превращаются в имена.
С routeOnly имя нужно только правилам маршрутизации, а соединение идёт на тот адрес, что выбрало приложение. Включите routeOnly и оставьте Enable Sniffing включённым.
Эффект виден по журналу сервера. В моём прогоне до включения обоих пунктов из 206 записей телефона имя назначения было в 109, IPv6-адресов — ни одного. После включения из 111 записей имён осталось 2, а IPv6-адресов стало 33.
Обход локальной сети
VPN Settings → Bypass LAN in VPN mode («VPN обходит LAN») принимает значения «Follow config» («Как в профиле»), «Bypass» («Обходит») и «Not Bypass» («Не обходит»). По умолчанию стоит Bypass, и это то, что нужно. Что оно делает: вместо маршрута 0.0.0.0/0 приложение добавляет в туннель список публичных диапазонов IPv4, из которого исключены три частные сети — 10.0.0.0/8, 172.16.0.0/12 и 192.168.0.0/16. Пакеты к принтеру, роутеру, NAS в домашней сети в туннель не попадают и идут как обычно. Без этого они отправлялись бы на сервер, а он (как мы настроили в прошлой статье) отправляет частные адреса в blackhole.
Про IPv6 в этом режиме есть оговорка, о ней в разделе про маршрутизацию.
DNS
DNS в приложении задаётся тремя настройками, которые легко перепутать.
| Пункт | Что это | Значение по умолчанию |
|---|---|---|
| VPN DNS (в VPN Settings) | адреса DNS-серверов, которые приложение сообщает системе через VpnService.Builder; Android отправляет запросы именно им |
1.1.1.1 |
| Remote DNS («Удалённый сервер DNS», в Core Settings) | резолвер внутри ядра Xray для имён, идущих через прокси; работает, только если включён Enable local DNS; адрес вида https://…/dns-query — это DoH, DNS внутри HTTPS (RFC 8484) |
https://cloudflare-dns.com/dns-query |
| Direct DNS («DNS для прямых подключений») | резолвер ядра для имён, идущих напрямую; тоже только при Enable local DNS | 223.5.5.5 |
При настройках по умолчанию Enable local DNS выключено, и работает только VPN DNS. Запрос DNS от приложения Android отправляет на 1.1.1.1 по UDP на порт 53, пакет попадает в туннель и едет на сервер как обычная UDP-датаграмма. Сервер отправляет её на 1.1.1.1 уже со своего адреса. Провайдер телефона видит только TLS к вашему серверу. Если оставить как есть, DNS не утекает.
Два замечания. Direct DNS по умолчанию — китайский публичный резолвер 223.5.5.5, как и в v2rayN. Он нужен, только если вы включили local DNS и настроили прямые маршруты; тогда замените его на 1.1.1.1 или резолвер своего оператора. И Enable FakeDNS оставьте выключенным: приложения получают вместо настоящих адресов фиктивные, а это лишняя сложность.
Ещё одно место, где в DNS вмешивается сама система: Private DNS («Частный DNS-сервер», в настройках Android: сеть и интернет). В режиме «Автоматически» Android пробует DNS over TLS (RFC 7858), то есть обычный DNS внутри TLS на порт 853, к тому DNS-серверу, который выдан сети. В нашем случае это VPN DNS 1.1.1.1, и в журнале сервера видны соединения tcp:1.1.1.1:853 с телефона: DoT тоже идёт в туннель и дальше с сервера на Cloudflare. Ничего менять не надо.
Остальные пункты VPN Settings
- Add HTTP proxy to VPN («Дополнительный HTTP-прокси»): на Android 10 и новее добавляет в конфигурацию VPN системный HTTP-прокси
127.0.0.1. Браузеры и приложения, которые его читают, идут в прокси напрямую, минуя TUN. Включать не обязательно: он влияет только на приложения, которые читают системный прокси, а UDP и остальные программы не затрагивает. - VPN MTU (1500) и VPN Interface Address: оставьте.
- Enable Mux («Использовать мультиплексирование»): оставьте выключенным. Мультиплексирование — упаковка нескольких соединений в одно. Для профиля с
flow(у нас Vision) приложение всё равно отключает его для TCP и оставляет только XUDP для UDP, так что пункт ничего не даёт.
Маршрутизация
Меню → Routing Settings («Маршрутизация»). Список правил в том же виде, что и в v2rayN: условия «такие адреса или имена → такой выход», сверху вниз, первое совпавшее решает. Поля правила: domain, ip, port, protocol, network и выход outboundTag (proxy, direct, block). Все заполненные условия правила должны выполниться одновременно. Если ни одно правило не подошло, трафик идёт в proxy.
Набор по умолчанию
При первом запуске приложение загружает набор «China Whitelist»: блок UDP 443, geosite:google через прокси, частные адреса и домены напрямую, затем напрямую публичные DNS-серверы Китая, адреса и домены Китая (geoip:cn, geosite:cn). Всё остальное — в прокси. Для нашей задачи он работает, но китайские правила нам не нужны.
Другие наборы выбираются в меню: Routing Settings → Import predefined rulesets («Импорт набора правил»): «China Blacklist», «Global» («Общие»), «Russia Whitelist», «Iran Whitelist». Наборы заменяют текущий список целиком. Нам подходит Global.
Что в «Global» и как это прочитать
Правила в «Global» записаны так, что названия на китайском:
| Название | Перевод | Условие | Выход |
|---|---|---|---|
| 阻断udp443 | Блокировать UDP 443 | порт 443, сеть UDP | block |
| 绕过局域网IP | Мимо прокси: локальные IP | geoip:private |
direct |
| 绕过局域网域名 | Мимо прокси: локальные имена | geosite:private |
direct |
| 最终代理 | Всё остальное через прокси | порт 0-65535 |
proxy |
Названия (remarks) на работу не влияют, это просто подпись. Правило можно открыть и переименовать: поле «Remarks» («Название») в окне Routing Rule Settings, оно обязательное. В списке правил под названием показано краткое условие (udp443, geoip, geosite, 0-65535), а выход видно, если открыть правило. Первое правило нужно потому же, что и на Windows: UDP на порт 443 — это QUIC (HTTP/3), и браузеру выгоднее сразу перейти на HTTPS по TCP, где работает Vision.
Свои правила можно вводить вручную: кнопка + в списке («Add rule»), либо импортировать из буфера обмена (меню списка → Import ruleset from clipboard) тот же JSON, что и в v2rayN: массив объектов с теми же полями.
Локальная сеть по IPv6
Вот что обнаружилось при проверке. Правило geoip:private отправляет напрямую адреса 192.168.… и им подобные. Но дома IPv6-адреса у устройств публичные: провайдер выдаёт роутеру префикс, например 2001:db8:1::/64, и компьютер, принтер и телефон получают адреса из него. Для geoip:private это обычные интернет-адреса. А при включённом IPv6 в туннеле маршрут 2000::/3 перехватывает и их.
В журнале сервера это выглядело так: телефон обратился по UDP к домашнему компьютеру по его IPv6-адресу 2001:db8:1::10, запрос ушёл через туннель на сервер, а сервер пытается достучаться до вашего дома снаружи. Фоновая связь Windows с телефоном при этом не работает, а адреса и порты ваших домашних устройств видит сервер.
Лечится правилом: направить свой префикс напрямую. В Routing Settings нажмите + и заполните:
| Поле | Значение |
|---|---|
| Remarks | Home IPv6 direct |
| ip | 2001:db8:1::/64 (ваш префикс, а если провайдер делегировал вам блок побольше — весь блок, например /56) |
| outboundTag | direct |
Новое правило встаёт в начало списка, а правила проверяются сверху вниз, так что оно сработает раньше последнего «всё остальное — в прокси». Пакет по-прежнему входит в TUN, но ядро отправляет его напрямую с телефона, мимо сервера. Само правило на своём телефоне я не вводил: проблему увидел по журналу сервера, а решение следует из порядка проверки правил.
Заодно ещё один эффект, который не лечится. Пока DNS идёт через прокси, внутренние имена домашней сети разрешаются публичным DNS: имя вашего сервиса nas.example.net, у которого дома адрес 192.168.1.12, снаружи может вести на внешний адрес роутера. Запрос пойдёт на внешний адрес через сервер, а не по локальной сети. Если сервис опубликован наружу, он откроется, но кружным путём; если нет, не откроется совсем. Открывайте внутренние ресурсы по IP-адресу или выключайте на время туннель.
Приложения: кто идёт в туннель
Меню → Per-app settings («Выбор приложений»). Пункт Enable per-app («Использовать выбор») включает список, а переключатель Bypass Mode («Обход») выбирает смысл списка:
- выключен — в туннель идут только отмеченные приложения, остальные ходят напрямую;
- включён — отмеченные ходят напрямую, а в туннель идёт всё остальное.
Для чего это нужно: банковские приложения, госсервисы и сервисы, которые отказываются работать с VPN или режут иностранные адреса; приложения, которым нужна домашняя сеть. Реализует это система: приложения из списка «напрямую» исключаются из VPN-сети на уровне VpnService.Builder, их пакеты в TUN вообще не попадают. Само v2rayNG исключено всегда.
Остальное на стороне Android
- Другие VPN. При запуске v2rayNG домашний IKEv2 и WireGuard будут отключены. Переключайтесь вручную: остановите одно, запустите другое. Если вам нужны одновременно и туннель в домашнюю сеть, и обход DPI, используйте режим Proxy only и укажите адрес прокси в тех приложениях, которым нужен обход.
- Always-on VPN и «Блокировать соединения без VPN»: в настройках Android (VPN → значок шестерёнки у профиля), в v2rayNG есть ярлык: Settings → Advanced Settings → System VPN settings («Системные настройки VPN»). С блокировкой телефон не выходит в сеть, пока туннель не поднят, чтобы ничто не ушло мимо. Я это не включал и в статье не проверял: включайте, только убедившись, что подключение стабильно, иначе потеряете интернет вовсе.
- Автозапуск. Advanced Settings → Connect after device boot («Автоподключение при загрузке устройства»): после перезагрузки телефона приложение само поднимет туннель. Android ограничивает фоновый запуск, на некоторых телефонах это работает ненадёжно.
- Батарея. Отключите для v2rayNG оптимизацию расхода заряда (Настройки → Приложения → v2rayNG → Батарея → «Без ограничений»), иначе система может остановить службу в фоне. Многие производители (Xiaomi, Huawei, Samsung и другие) добавляют к стандартному Android свои ограничения фоновой работы: в настройках приложения ищите пункты вроде «Автозапуск» и «Экономия заряда». Уведомление приложения должно оставаться.
- Быстрое включение. У приложения есть плитка быстрых настроек и виджет «Switch»: они включают и выключают выбранный профиль, не открывая приложение.
Смотрим руками
Что видит сервер
Журнал доступа Xray на сервере (включается временно, см. раздел «Смотрим руками» прошлой статьи) пишет каждое соединение с меткой пользователя. Первая минута работы телефона:
2026/10/23 18:51:24 from tcp:198.51.100.23:46706 accepted udp:1.1.1.1:53 [vless-reality >> direct] email: alice-phone
2026/10/23 18:51:55 from 198.51.100.23:57908 accepted tcp:siteicon.operacdn.com:443 [vless-reality >> direct] email: alice-phone
2026/10/23 18:51:57 from 198.51.100.23:45802 accepted tcp:ip6.me:443 [vless-reality >> direct] email: alice-phone
Что читать в строке: from — адрес, с которого пришёл телефон (у нас это внешний адрес домашнего роутера; в мобильной сети будет адрес оператора); accepted tcp:… или accepted udp:… — куда соединение отправлено; [vless-reality >> direct] — вход и выход ядра на сервере; email — метка пользователя. У UDP-записей источник помечен tcp:: датаграмма приехала внутри TCP-соединения, как говорилось выше. Записи с именем назначения (siteicon.operacdn.com) — это ещё до включения routeOnly; после него преобладают адреса:
2026/10/23 19:04:54 from 198.51.100.23:55010 accepted tcp:1.1.1.1:853 [vless-reality >> direct] email: alice-phone
2026/10/23 19:04:56 from 198.51.100.23:56714 accepted tcp:[2a00:1450:4001:c17::66]:443 [vless-reality >> direct] email: alice-phone
Первая строка — Private DNS (DNS over TLS на порт 853), вторая — первое соединение с IPv6-адресом сайта через туннель.
Журнал доступа записывает адреса, куда ходит каждый пользователь. Включайте его только для отладки и в нужное время, а потом возвращайте "access": "none".
Статистика без журнала
Узнать, подключён ли пользователь и сколько он передал, можно, вообще не записывая адреса назначения. Для этого в конфиг сервера добавляются три раздела (рядом с log, inbounds и остальными):
"stats": {},
"api": { "tag": "api", "listen": "127.0.0.1:10085", "services": [ "StatsService" ] },
"policy": {
"levels": { "0": { "statsUserUplink": true, "statsUserDownlink": true, "statsUserOnline": true } }
}
stats включает счётчики, policy говорит, какие считать (уровень 0 — уровень по умолчанию у пользователей), а api открывает управляющий интерфейс gRPC на локальном адресе сервера, не доступном снаружи. Проверка и перезапуск — как всегда:
xray run -test -config /usr/local/etc/xray/config.json
systemctl restart xray
Теперь можно спросить, кто и откуда подключён и сколько трафика прошло:
xray api statsonlineiplist --server=127.0.0.1:10085 -email alice-phone
xray api statsquery --server=127.0.0.1:10085 -pattern 'user>>>alice-phone'
{ "ips": { "198.51.100.23": 1792781523 }, "name": "user>>>alice-phone>>>online" }
"name": "user>>>alice-phone>>>traffic>>>uplink", "value": 123604
"name": "user>>>alice-phone>>>traffic>>>downlink", "value": 132163
В первой команде — адреса, с которых пользователь подключён сейчас, и время последней активности (число — метка времени в секундах). Счётчики живут до перезапуска Xray. Если пользователь с момента запуска не подключался, ответ not found.
Адрес выхода
На телефоне при включённом туннеле откройте в браузере ifconfig.co и ip6.me: должны показаться адреса сервера, 203.0.113.10 и 2001:db8::10, а не домашние. У меня после настройки оба сайта показали адреса сервера, и IPv4, и IPv6. Если IPv6 показывает домашний адрес — не включён пункт Enable IPv6.
Смена сети
Телефон переходит между Wi-Fi и мобильной сетью постоянно, и туннель должен это пережить. Соединение с сервером — обычный TCP, а TCP привязан к паре адресов и на другой адрес «переехать» не умеет (в отличие от IKEv2 с расширением MOBIKE, которое позволяет VPN-сессии переехать на новый адрес). При смене сети соединения с сервером рвутся, и приложение открывает новые. Проверка: я выключил Wi-Fi на включённом туннеле, оставив мобильные данные, и открыл сайт — всё работало. По журналу видно, как это выглядит на сервере:
последняя запись с домашнего адреса 19:12:47
первая запись с адреса оператора 19:12:56
Около девяти секунд, и после этого statsonlineiplist показывал оба адреса, домашний и оператора (как долго старый остаётся в списке, я не проверял). Соединения, открытые в момент смены сети, обрываются, и приложениям приходится повторять запросы. В журнале ошибок в эти секунды только записи об обрыве соединений сервера с сайтами (connection ends, readfrom tcp) — старые соединения закрываются, это штатно.
Если мобильная сеть оператора выдаёт только IPv6 (такие встречаются), используйте вторую ссылку, с IPv6-адресом сервера: xray-user link alice-phone печатает её второй строкой.
Если не подключается
Приложение само отвечает «Connection test failed» с причиной, а подробности — в Logcat («Logcat», пункт меню; уровень подробности задаётся в Core Settings → Log Level). Сторона сервера видна в журнале ошибок Xray:
На сервере (error.log) |
Причина |
|---|---|
REALITY: processed invalid connection from …: failed to read client hello |
пришёл не TLS: сканер или клиент с другим протоколом |
REALITY: processed invalid connection from …: server name mismatch |
SNI клиента не из списка serverNames: в профиле не то имя сайта-маски |
| соединение принято, но нет записей в журнале доступа | неверный UUID: ключ отозван или скопирован с ошибкой |
Из первых двух строк видно, что REALITY отвечает чужим: такие записи постоянно приходят от сканеров интернета, и им отдаётся настоящий сайт-маска. Это штатная работа, а не поломка.
Ограничения
- Ссылка — это ключ. Кто получил
vless://…, тот подключается от вашего имени, и сервер отличает его от вас только по адресу. Не слайте ссылки и QR-коды в мессенджеры и не оставляйте их в истории. Утекла ссылка телефона — отозвать её командойxray-user del alice-phone, остальные пользователи не пострадают. - Одно устройство — один пользователь. Тогда видно, что делает каждое устройство, и отзыв точечный.
- Приложения, которые не любят VPN. Некоторые банки и сервисы определяют VPN-сеть по системному признаку и отказываются работать. Для них — список Per-app settings.
- Версии. Сервер и клиент лучше держать на версиях одного времени: Xray и v2rayNG часто обновляются, а формат и значения по умолчанию иногда меняются. Клиент 2.3.11 с ядром 26.10.10 нормально работает с сервером 26.3.27, но обновляйте оба.
- Адрес. Как и раньше, маскировка не защищает от блокировки сервера по IP.
Итог
Телефон получил то же, что и компьютер: весь трафик, включая UDP и IPv6, уходит на сервер внутри обычного на вид HTTPS к www.example.com. По умолчанию приложение при этом кое-что оставляет мимо туннеля или подменяет: IPv6, адреса назначения, домашние IPv6-адреса, китайские наборы правил. Всё это видно по журналу сервера и поправляется несколькими переключателями. Главное, что нужно запомнить: в Android один VPN, ключ у каждого устройства свой, а проверять результат нужно по тому, что видит сервер, а не по значку ключа в строке состояния.
Источники
- v2rayNG — приложение, страница релизов и описание файлов.
- hev-socks5-tunnel — tun2socks, который читает пакеты из TUN.
- VpnService и VpnService.Builder — как приложение создаёт VPN в Android; обзор VPN.
- Xray-core, REALITY; справочник Xray: маршрутизация, статистика, policy, API.
- RFC 1928 — SOCKS5; RFC 7858 — DNS over TLS; RFC 8484 — DNS over HTTPS.
- qrencode — утилита для QR-кодов.
Оставить комментарий могут только зарегистрированные пользователи.
Войдите на сайт или зарегистрируйтесь, чтобы оставить комментарий.