Почему приложение не может включить сохранённый профиль VPN на Android или iOS
Построить собственный туннель и включить сохранённый профиль — две разные задачи. Сравните, что Android, iOS и десктоп действительно разрешают приложению.
Две совершенно разные задачи попадают под название программного VPN, и только одна из них доступна обычному приложению на любой из двух мобильных платформ. Первая — включить профиль, который пользователь уже сохранил; вторая — построить туннель внутри собственного процесса. Android даёт вторую через VpnService, а iOS даёт вторую через право Network Extension, тогда как первая остаётся у системы, владельца устройства или сервера MDM. Выяснение, какая из них вам действительно нужна, обычно делает проект меньше.
Две задачи с одним названием
Активация сохранённого профиля означает сказать существующему туннелю, чтобы он поднялся: хост, учётные данные, протокол — всё уже настроено там, где пользователь это видит. Построение туннеля означает, что ваш код владеет интерфейсом, циклом пакетов и протоколом. В продуктовом бэклоге они выглядят похоже и почти не делят реализацию.
Ни одна из двух мобильных платформ не открывает первую для SDK-приложения. Соединение, появляющееся без каких-либо действий пользователя, неотличимо от вредоносного ПО, поэтому обе системы ставят шлюз согласия перед любым приложением, которое переносит трафик.
Android: шлюз согласия и никакого API профилей
VpnService существует с Android 4.0 и позволяет приложению создать интерфейс tun, а затем читать и записывать пакеты. Службу нужно объявить с BIND_VPN_SERVICE, а VpnService.prepare() создаёт системный диалог, который просит пользователя разрешить её. Пример платформы, ToyVpn, — самое ясное утверждение задуманной формы: ваш код владеет интерфейсом и всем, что через него проходит.
Чего она не делает — не добавляет запись на экран настроек VPN системы. Публичного API для этого нет, а приложение «Настройки» держит свои профили в хранилище, до которого обычное приложение не доберётся. Вызовы скрытых классов из обычного приложения создают профили, которые система никогда не показывает, поэтому код, который, казалось, работал в одной сборке, тихо перестаёт работать в следующей.
Управляемый путь отдельный. Владелец устройства или профиля может назначить всегда включённый пакет VPN через API политики устройства, а корпоративный инструмент может отправить профиль, который пользователь затем одобрит. Это полномочие управления устройством, а не разрешение, которое приложение запрашивает при установке. Если ваш клиент — организация, это путь, для которого нужно планировать; если ваш клиент — человек, устанавливающий из магазина, он вообще недоступен.
iOS: право — это граница
iOS делит ту же работу иначе. Клиент VPN реализует поставщика пакетного туннеля или прокси приложения в рамках Network Extension, и соответствующее право должно быть выдано учётной записи разработчика. Личная конфигурация VPN через NEVPNManager существует для более простого случая и по-прежнему работает в той же модели разрешений. Apple документирует всю поверхность в справочнике Network Extension, который стоит прочитать, прежде чем определять объём работы.
Нет поддерживаемого способа открыть экран настроек VPN, прочитать сохранённые конфигурации пользователя или запустить одну из них. Клиент может отправить профиль конфигурации для установки пользователем, а затем использовать полученный туннель через обычные сетевые вызовы. Туннелирование для каждого приложения назначает сервер MDM правилом по требованию, а не выбирает приложение во время выполнения.
Клиент VPN крупного поставщика не доказывает, что ваша учётная запись может отправить то же самое. Эти права запрашиваются для каждой учётной записи разработчика и проверяются, поэтому риск графика лежит на Apple, а не в вашем плане спринта.
Десктоп доходит дальше, но владение не меняется
Windows — разрешающий случай. Add-VpnConnection создаёт запись телефонной книги из сеанса PowerShell с повышенными правами, а rasdial подключает или отключает её из скрипта. Приложение также может управлять клиентом поставщика через его документированную командную строку. Создание профиля всё ещё административное действие, потому что запись живёт в хранилище всей машины.
macOS ведёт себя так же с другими инструментами. scutil --nc start и stop работают с уже существующими службами, тогда как создание одной — задача системного администрирования. Владение процессом на ноутбуке не делает вас владельцем конфигурации.
Что строить вместо этого
Если истинное требование — чтобы ваше приложение достигало внутреннего API, достаточно туннеля, который пользователь уже запускает. Отправляйте обычные HTTPS-запросы, позвольте системе выбрать путь, и перестаньте пытаться управлять туннелем. Если требование — чтобы туннель использовал только трафик вашего приложения, запросите VPN для каждого приложения через канал управления или завершите TLS внутри приложения перед службой, доступной без него.
Скрытые API — не короткий путь, который стоит выбирать. Они меняются между выпусками, проваливают проверку приложений, а сбои, которые они вызывают, кажутся тому, кто наследует код, обычными сетевыми сбоями. Сравнение не Android против iOS; это владение туннелем против просьбы к платформе направлять трафик, и только первое — ваше, чтобы строить.