Свой прокси против 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. Порядок такой:

  1. Приложение просит у системы разрешение. Система показывает окно «Запрос на подключение»: приложение хочет настроить VPN, весь сетевой трафик может быть перехвачен. Без вашего согласия дальше ничего не происходит.
  2. После согласия приложение описывает сетевой интерфейс через VpnService.Builder: его адрес (addAddress), маршруты (addRoute), адреса DNS-серверов (addDnsServer), MTU (setMtu) и, если нужно, список приложений, которые в туннель попадают или не попадают (addAllowedApplication, addDisallowedApplication).
  3. Метод establish() создаёт в системе виртуальный интерфейс TUN и отдаёт приложению файловый дескриптор. TUN — интерфейс, который вместо провода отдаёт программе IP-пакеты: всё, что система отправляет по маршрутам туннеля, программа читает из дескриптора как сырые пакеты, а пакеты, которые она в него записывает, система принимает как пришедшие из сети.
  4. Приложение делает с пакетами что хочет: шифрует и отправляет на свой сервер, фильтрует, считает. Для этого его собственное соединение с сервером должно идти мимо туннеля, иначе пакеты, везущие пакеты, ходили бы по кругу. Поэтому приложение либо помечает сокет как «вне 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 всё равно откажется ставить поверх приложения файл, подписанный чужим ключом.

Установка на телефоне

  1. Скачайте APK в браузере телефона (или перенесите с компьютера).
  2. Android спросит разрешение «Установка неизвестных приложений» для браузера или файлового менеджера, через который вы открываете файл. Разрешите. После установки это разрешение можно отозвать в настройках.
  3. Play Protect может предложить проверить приложение: это нормально для файлов вне магазина.
  4. Первый запуск: приложение может попросить разрешить уведомления. Разрешите: уведомление держит службу 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, а если телефон код не читает, увеличьте шрифт терминала.

Первое подключение

  1. В v2rayNG нажмите + («Добавить»), выберите Import from QR code («Импорт из QR-кода») и разрешите доступ к камере. Если кода нет, скопируйте ссылку и выберите Import from Clipboard («Импорт из буфера обмена»).
  2. Появится сообщение «Success» и профиль в списке. Его название приложение берёт из конца ссылки после знака #, у нас это alice-phone-ipv4.
  3. Нажмите на профиль, чтобы выбрать его, и на кнопку запуска. Android покажет окно запроса VPN — подтвердите. В строке состояния появится ключ, а в уведомлении — статус.
  4. Статус внизу экрана: «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, ключ у каждого устройства свой, а проверять результат нужно по тому, что видит сервер, а не по значку ключа в строке состояния.


Источники

Оставить комментарий могут только зарегистрированные пользователи.

Войдите на сайт или зарегистрируйтесь, чтобы оставить комментарий.