Android chroot VPN: почему туннель работает, а приложения его не замечают
Chroot меняет только поиск путей, а не ядро, сетевое пространство имён или регистрацию VPN в Android. Отсюда два исхода, и оба удивляют людей.
Вы запускаете на телефоне дистрибутив Linux под chroot, ставите в нём VPN-клиент, и оболочка сообщает, что туннель поднят. Потом вы открываете браузер, и трафик уходит через мобильные данные, будто ничего не произошло. Или наоборот: каждое приложение на телефоне теряет связь, потому что клиент добавил маршрут по умолчанию в то пространство имён, которое Android действительно использует. Оба симптома происходят из одного недостающего элемента, и ни один не является ошибкой VPN-клиента.
Оболочка говорит, что подключено, приложения не согласны
Первый исход — обычный. Туннель в пользовательском пространстве, открытый из chroot, может завершить рукопожатие, вывести статистику и передавать трафик, который создаёт та же оболочка, тогда как приложения Android продолжают пользоваться сетью телефона по умолчанию. Ничто их не перехватывает, потому что никто не сообщил Android о существовании VPN.
Второй исход — опасный. Если chroot делит сетевое пространство имён Android, что он делает по умолчанию, процесс с достаточными привилегиями может добавить маршруты и интерфейс TUN, влияющие на весь телефон. Тогда туннель работает для каждого приложения, включая те, которые вы не собирались маршрутизировать, и связь телефона теперь зависит от демона без управления жизненным циклом, без индикатора подключения и без политики постоянного включения.
Какой из двух вариантов вам достался, можно выяснить одной командой. Сравните ip route внутри chroot с той же командой из оболочки Android снаружи. Одинаковый вывод означает, что chroot находится в пространстве имён хоста: любое изменение сети, которое он делает, действует на весь телефон. Разный вывод означает, что загрузчик создал для него отдельное пространство имён, и его туннель для Android невидим по самой конструкции.
Что chroot изменил, а что нет
| Object | Effect of a chroot |
|---|---|
| Pathname resolution | Changed: lookups start at the new root |
| Kernel and drivers | Unchanged: the phone's kernel, its TUN driver, its limits |
| Network namespace | Unchanged unless the launcher creates one |
| Android VPN registration | Unchanged: no app consented, no VPN is selected |
Системный вызов chroot делает одну вещь: он меняет каталог, используемый для разрешения имён путей. Страница man прямо говорит, что это не граница безопасности, и ничего не сообщает об интерфейсах, маршрутах или состоянии VPN. Всё, чего люди ждут от контейнера Linux — частный сетевой стек, отфильтрованная файловая система, ограничения ресурсов — происходит из других механизмов, которые загрузчик мог применять, а мог и нет.
Поэтому утверждения, что chroot изолирует VPN, обычно отвечают не на тот вопрос. Уместный вопрос — в каком пространстве имён работает процесс и в каком домене SELinux он ограничен, и оба являются свойствами того, как он был запущен, а не слова chroot.
TUN нужны три вещи, и узел устройства — только одна
Традиционному туннелю в пользовательском пространстве нужны устройство TUN, которое он может открыть, поддержка этого устройства в ядре и разрешение настраивать интерфейс и маршруты. Это отдельные требования, и создание имени пути в /dev само по себе не выполняет ни одного из них:
ls -l /dev/net/tun
ip tuntap add dev tun0 mode tun
В Android поддержка TUN обычно встроена в ядро, потому что её использует собственный VPN-сервис платформы, но это не значит, что ваш процесс может ею пользоваться. Настройка интерфейса требует полномочия администрирования сети, а SELinux решает, может ли процесс вообще добраться до узла устройства. Отказ выглядит как ошибка прав без очевидной причины, и ответ находится в журнале аудита:
dmesg | grep avc
logcat -d | grep "avc: denied"
Root это не решает. SELinux ограничивает процессы, работающие от uid 0, точно так же, как остальные, поэтому chroot, запущенный из root-оболочки, всё равно может получить отказ к устройству, которое видит. Отключение принудительного режима, чтобы заработал старый установщик, — это решение для всего телефона с последствиями далеко за пределами туннеля, и оно не является исправлением устаревшего двоичного файла.
Почему приложения Android игнорируют ваш туннель
Поддерживаемая модель Android — это VPN-приложение, которое регистрируется через VPN-сервис платформы, один раз запрашивает согласие пользователя и получает файловый дескриптор для своего туннеля. Затем платформа направляет трафик в этот дескриптор и применяет область приложений, выбранную пользователем или администратором, поэтому один и тот же механизм может быть для каждого приложения, для каждого профиля и сочетаться с настройками постоянного включения и блокировки без VPN. Руководство по разработке VPN для Android описывает это рукопожатие, и главное в том, что это регистрация, а не трюк с маршрутизацией.
Демон внутри chroot никогда не регистрируется. Нет диалога согласия, нет значка ключа в строке состояния, нет списка включения или исключения для приложений и нет участия в политике постоянного включения. Поэтому приложения продолжают пользоваться сетью по умолчанию, а туннель передаёт только то, что отправляет сама оболочка.
Обратный путь тоже не помогает. Проталкивание маршрутов в пространство имён хоста перехватывает всё, включая трафик, который платформа рассчитывала оставить локальным, и делает это без управления для каждого приложения и без возможности для пользователя увидеть или отозвать это. Нет промежуточной настройки, где демон chroot маршрутизирует трафик только одного приложения.
Двоичная совместимость — отдельная стена
Даже когда сетевая сторона интересна, у исполняемого файла свои требования: архитектура процессора, динамический загрузчик, библиотека C и те возможности ядра, на которые он рассчитывает. Android использует bionic, а не glibc, и старый двоичный файл, собранный для настольного дистрибутива, может требовать библиотек, которых на устройстве больше нет.
Символическая ссылка с именем библиотеки на более новую версию не создаёт совместимость ABI, а архивная криптографическая библиотека из пакета десятилетней давности не является поддерживаемым компонентом. Если план на этом держится, план состоит в том, чтобы запускать неподдерживаемый код с сетевыми привилегиями на телефоне.
Докажите это в контексте Android и приберите за собой
Проверяйте то, что вам действительно важно, с той стороны, которая имеет значение: откройте конкретное приложение, достигните конкретного приватного адреса, убедитесь, какой резолвер ответил, затем отключите туннель и подтвердите задокументированное поведение. Результат из терминала внутри chroot — свидетельство только о контексте выполнения этого терминала и ни о чём больше.
Если во время экспериментов вы меняли маршруты хоста, верните их и запишите, что изменили. Затем проверьте, настроен ли на телефоне VPN с постоянным включением, потому что старый эксперимент, оставивший правило, через месяцы может выглядеть как сбой платформы.
Для реальной потребности в удалённом доступе используйте поддерживаемый клиент сервиса или одобренный проект шлюза. Стоит спрашивать не о том, можно ли заставить работать установщик с root, а о том, переносит ли поддерживаемая реализация нужные вам приложения через задуманную границу, не оставляя на устройстве ничьих привилегированных изменений.