На iOS и macOS сторонние приложения не имеют прямого доступа к TUN/TAP-интерфейсам. Единственный легальный способ реализовать VPN-туннель — фреймворк Network Extension, который Apple предоставляет с жёсткими рамками: лимиты памяти, требования к MDM для always-on и per-app режимов, изолированный процесс расширения. Эти ограничения определяют, что может и чего не может делать VPN-клиент на платформах Apple.
Network Extension — это набор классов, позволяющих расширять сетевую функциональность системы без модификации ядра. Для VPN релевантны два провайдера:
На iOS расширение работает как отдельный процесс, изолированный от основного приложения. Пользователь видит только приложение-оболочку, а всю работу выполняет extension. На macOS начиная с Catalina Network Extension реализуется через System Extension (замена kernel extensions), что также означает отдельный процесс с собственным sandbox.
Система управляет процессом VPN-расширения по собственным правилам:
Это означает, что VPN-клиент на iOS не может «держать соединение» в фоне произвольно долго — он живёт ровно столько, сколько система считает нужным.
Процесс Network Extension работает в sandbox с ограниченной памятью. На iOS лимит составляет порядка 50 МБ для процесса расширения (точное значение зависит от версии ОС и устройства). При превышении лимита система завершает процесс без предупреждения.
Практические следствия:
На macOS лимиты мягче, но sandbox всё равно ограничивает доступ к файловой системе и системным вызовам. Расширение не может читать произвольные файлы, запускать дочерние процессы или открывать сокеты вне туннеля.
Always-on — режим, при котором VPN-соединение устанавливается автоматически при загрузке устройства и не может быть отключено пользователем. На iOS и macOS он реализуется по-разному.
На iOS always-on VPN требует выполнения двух условий:
Пользователь не может отключить такой VPN через настройки — переключатель становится неактивным. Без Supervised-режима и MDM always-on на iOS невозможен. Это ограничение на уровне ОС, обойти его средствами приложения нельзя.
На macOS always-on VPN можно настроить двумя способами:
В обоих случаях система будет автоматически переподключать VPN при разрыве соединения и не позволит пользователю отключить его через системные настройки без удаления профиля.
Если always-on слишком жёсткий, Network Extension поддерживает On-Demand Rules — набор условий, при которых туннель активируется или деактивируется автоматически. Правила могут учитывать:
On-Demand Rules настраиваются в конфигурации провайдера и работают без MDM на обеих платформах. Это компромисс: VPN не активен постоянно, но включается автоматически при соблюдении условий.
Per-app VPN позволяет направить через туннель трафик только определённых приложений, а остальной трафик пустить напрямую. Механизм зависит от платформы.
На iOS per-app VPN реализуется исключительно через MDM-профиль. В payload указываются bundle identifier приложений, трафик которых должен идти через туннель. Без MDM приложение не может программно ограничить туннель определёнными процессами.
Типичный сценарий: корпоративное устройство, где через VPN должен работать только почтовый клиент и внутренний мессенджер, а браузер и личные приложения используют прямое соединение.
Ограничения per-app на iOS:
На macOS per-app туннелирование возможно без MDM через
Приложение-оболочка задаёт правила маршрутизации (по bundle identifier, домену, порту), а провайдер применяет их в рантайме. Это более гибкий механизм, чем MDM-профиль, но он требует, чтобы пользователь явно разрешил расширение в системных настройках.
WireGuard на iOS и macOS реализован как NEPacketTunnelProvider. Официальный клиент использует минимальную конфигурацию: приватный ключ, список пиров с публичными ключами и AllowedIPs. Модель Cryptokey Routing — ассоциация публичных ключей с разрешёнными IP-адресами — позволяет обходиться без сложных таблиц состояний и TLS-сессий.
При отправке пакета WireGuard определяет пира по адресу назначения из таблицы AllowedIPs, шифрует пакет его публичным ключом и отправляет на последний известный endpoint. При получении — расшифровывает, проверяет источник и сверяет с AllowedIPs. Эта простота напрямую влияет на потребление памяти и стабильность в условиях sandbox.
Однако даже WireGuard сталкивается с платформенными ограничениями:
Когда VPN-расширение нестабильно или не запускается, полезны следующие шаги.
Ряд ограничений Network Extension является фундаментальным и не обходится средствами приложения:
Эти рамки определяют, что любой VPN-клиент на платформах Apple — это компромисс между гибкостью и требованиями sandbox. Для корпоративного развёртывания с always-on и per-app необходим MDM; для индивидуального использования доступны on-demand rules и ручное управление туннелем.
Архитектура Network Extension
Network Extension — это набор классов, позволяющих расширять сетевую функциональность системы без модификации ядра. Для VPN релевантны два провайдера:
- NEPacketTunnelProvider — создаёт виртуальный сетевой интерфейс и перехватывает IP-пакеты на уровне L3. Именно его используют WireGuard, OpenVPN Connect, Outline и большинство коммерческих VPN-клиентов.
- NEAppProxyProvider — работает на уровне L4 (TCP/UDP-соединения) и позволяет проксировать трафик отдельных приложений. Используется реже, но именно он лежит в основе per-app туннелирования без MDM на macOS.
На iOS расширение работает как отдельный процесс, изолированный от основного приложения. Пользователь видит только приложение-оболочку, а всю работу выполняет extension. На macOS начиная с Catalina Network Extension реализуется через System Extension (замена kernel extensions), что также означает отдельный процесс с собственным sandbox.
Жизненный цикл процесса расширения
Система управляет процессом VPN-расширения по собственным правилам:
- Расширение запускается при активации туннеля (пользователем, MDM-профилем или on-demand правилом).
- При переходе устройства в сон или при нехватке памяти система может приостановить или завершить процесс.
- После завершения процесса туннель разрывается. Если включён always-on, система попытается перезапустить расширение.
- Расширение не может выполнять фоновую работу, когда туннель неактивен.
Это означает, что VPN-клиент на iOS не может «держать соединение» в фоне произвольно долго — он живёт ровно столько, сколько система считает нужным.
Лимиты ресурсов
Процесс Network Extension работает в sandbox с ограниченной памятью. На iOS лимит составляет порядка 50 МБ для процесса расширения (точное значение зависит от версии ОС и устройства). При превышении лимита система завершает процесс без предупреждения.
Практические следствия:
- WireGuard на iOS хранит в памяти только криптографическое состояние и таблицу пиров, что укладывается в лимит благодаря минималистичному дизайну протокола. Протокол оперирует ассоциацией публичных ключей с AllowedIPs и не требует TLS-рукопожатий или сложных состояний сессии.
- OpenVPN-клиенты, использующие TLS с цепочками сертификатов и буферами, ближе к границе лимита.
- Клиенты с агрессивным логированием или кэшированием DNS-запросов могут получить неожиданное завершение процесса.
На macOS лимиты мягче, но sandbox всё равно ограничивает доступ к файловой системе и системным вызовам. Расширение не может читать произвольные файлы, запускать дочерние процессы или открывать сокеты вне туннеля.
Always-on VPN
Always-on — режим, при котором VPN-соединение устанавливается автоматически при загрузке устройства и не может быть отключено пользователем. На iOS и macOS он реализуется по-разному.
Требования к always-on на iOS
На iOS always-on VPN требует выполнения двух условий:
- Устройство должно находиться в режиме Supervised (активируется через Apple Business Manager или Apple Configurator при первоначальной настройке).
- MDM-сервер устанавливает конфигурационный профиль с VPN-payload, в котором ключ
AlwaysOnустановлен вtrue.
Пользователь не может отключить такой VPN через настройки — переключатель становится неактивным. Без Supervised-режима и MDM always-on на iOS невозможен. Это ограничение на уровне ОС, обойти его средствами приложения нельзя.
Always-on на macOS через профиль
На macOS always-on VPN можно настроить двумя способами:
- Через MDM-профиль (аналогично iOS, но Supervised-режим не требуется).
- Через конфигурационный профиль, установленный вручную (
.mobileconfigфайл), с ключомAlwaysOn.
В обоих случаях система будет автоматически переподключать VPN при разрыве соединения и не позволит пользователю отключить его через системные настройки без удаления профиля.
On-Demand Rules как альтернатива
Если always-on слишком жёсткий, Network Extension поддерживает On-Demand Rules — набор условий, при которых туннель активируется или деактивируется автоматически. Правила могут учитывать:
- SSID текущей Wi-Fi сети.
- Наличие или отсутствие определённых DNS-серверов.
- URL-адрес, к которому приложение пытается подключиться (probe URL).
- Тип интерфейса (Wi-Fi, сотовая сеть, Ethernet).
On-Demand Rules настраиваются в конфигурации провайдера и работают без MDM на обеих платформах. Это компромисс: VPN не активен постоянно, но включается автоматически при соблюдении условий.
Per-app туннелирование
Per-app VPN позволяет направить через туннель трафик только определённых приложений, а остальной трафик пустить напрямую. Механизм зависит от платформы.
Привязка per-app к MDM на iOS
На iOS per-app VPN реализуется исключительно через MDM-профиль. В payload указываются bundle identifier приложений, трафик которых должен идти через туннель. Без MDM приложение не может программно ограничить туннель определёнными процессами.
Типичный сценарий: корпоративное устройство, где через VPN должен работать только почтовый клиент и внутренний мессенджер, а браузер и личные приложения используют прямое соединение.
Ограничения per-app на iOS:
- Нельзя указать per-app правила из самого приложения — только через MDM.
- Если приложение не указано в списке, его трафик идёт напрямую (или блокируется, в зависимости от политики).
- Системные сервисы (iCloud, App Store, push-уведомления) не подчиняются per-app правилам и всегда используют прямое соединение.
NEAppProxyProvider на macOS
На macOS per-app туннелирование возможно без MDM через
NEAppProxyProvider. Этот провайдер перехватывает TCP- и UDP-соединения на уровне сокетов и может принимать решение для каждого соединения отдельно: проксировать через туннель или отклонить.Приложение-оболочка задаёт правила маршрутизации (по bundle identifier, домену, порту), а провайдер применяет их в рантайме. Это более гибкий механизм, чем MDM-профиль, но он требует, чтобы пользователь явно разрешил расширение в системных настройках.
WireGuard и Network Extension
WireGuard на iOS и macOS реализован как NEPacketTunnelProvider. Официальный клиент использует минимальную конфигурацию: приватный ключ, список пиров с публичными ключами и AllowedIPs. Модель Cryptokey Routing — ассоциация публичных ключей с разрешёнными IP-адресами — позволяет обходиться без сложных таблиц состояний и TLS-сессий.
При отправке пакета WireGuard определяет пира по адресу назначения из таблицы AllowedIPs, шифрует пакет его публичным ключом и отправляет на последний известный endpoint. При получении — расшифровывает, проверяет источник и сверяет с AllowedIPs. Эта простота напрямую влияет на потребление памяти и стабильность в условиях sandbox.
Однако даже WireGuard сталкивается с платформенными ограничениями:
- На iOS без MDM невозможно включить always-on или per-app режим.
- При завершении процесса расширения (нехватка памяти, сон устройства) туннель разрывается. WireGuard автоматически переподключается при возобновлении работы, но разрыв неизбежен.
- На iOS расширение не может выполнять keepalive-пакеты, когда процесс приостановлен системой.
Диагностика проблем с Network Extension
Когда VPN-расширение нестабильно или не запускается, полезны следующие шаги.
Диагностика на iOS
- Проверка статуса в настройках: Настройки → VPN. Если отображается ошибка, система обычно указывает код (например,
NEVPNErrorConfigurationInvalid).
- Системный лог через Console.app: подключите устройство к Mac, откройте Console.app и фильтруйте по имени процесса расширения. Ищите записи с
nesessionmanagerи именем вашего провайдера.
- Проверка MDM-профиля: Настройки → Основные → Управление устройством. Убедитесь, что профиль установлен и не повреждён.
Диагностика на macOS
- systemextensionsctl: команда
systemextensionsctl listпоказывает установленные системные расширения и их статус.
- Log Stream:
log stream --predicate 'subsystem == "com.apple.networkextension"'выводит события Network Extension в реальном времени.
- Проверка sandbox-нарушений: в логах ищите записи
Sandbox: denyс указанием расширения — это означает, что процесс пытается выполнить запрещённую операцию.
Типичные ошибки и их причины
| Симптом | Вероятная причина |
|---|---|
| Расширение не запускается | Не выдано разрешение пользователем (macOS) или не установлен MDM-профиль (iOS) |
| Туннель обрывается через 30–60 секунд | Превышен лимит памяти, система завершила процесс |
| Always-on не работает | Устройство не в Supervised-режиме (iOS) или профиль не содержит ключ AlwaysOn |
| Per-app не применяется | Профиль установлен без MDM (iOS) или bundle identifier указан с ошибкой |
| Трафик идёт мимо туннеля | AllowedIPs не покрывает нужные подсети или on-demand правила конфликтуют с маршрутами |
Архитектурные ограничения, которые нельзя обойти
Ряд ограничений Network Extension является фундаментальным и не обходится средствами приложения:
- Нет доступа к raw sockets из расширения.
- Нельзя создать более одного активного packet tunnel одновременно (система позволяет только один NEPacketTunnelProvider в активном состоянии).
- Расширение не может модифицировать системную таблицу маршрутов напрямую — только через параметры туннеля, переданные системе.
- На iOS приложение-оболочка и расширение не разделяют память; обмен данными возможен только через App Group (UserDefaults, файлы в общем контейнере).
- Расширение не может инициировать сетевые соединения вне туннеля для собственных нужд (например, для обновления конфигурации) — весь исходящий трафик проходит через туннель или блокируется.
Эти рамки определяют, что любой VPN-клиент на платформах Apple — это компромисс между гибкостью и требованиями sandbox. Для корпоративного развёртывания с always-on и per-app необходим MDM; для индивидуального использования доступны on-demand rules и ручное управление туннелем.
