Туннель открывается, когда вы запрашиваете одно внутреннее имя, и остаётся закрытым, когда следующему приложению оно нужно так же сильно. Это не ошибка приложения; это форма доменного правила. Конфигурация on-demand от Apple описывает условия установления соединения, и доменная форма этих условий задаёт системному резолверу вопрос ещё до того, как сдвинется хотя бы один пакет. Два вопроса постоянно путают: когда туннель должен подняться и что туннель несёт, когда он уже существует. Первое — триггер, второе — политика маршрутов и резолвера, и один профиль обычно задаёт и то и другое в одном payload, поэтому правильное доменное правило всё равно может оставить вас в неправильной сети.

Триггер и выбор трафика находятся в разных слоях

Правило on-demand живёт в словаре on-demand VPN-payload: упорядоченный список правил, у каждого действие и набор условий совпадения. Система оценивает их при изменении подключения или когда что-то пытается достичь сети, а действие решает, поднимается ли туннель, оценивается, отключается или игнорируется. Всё это происходит в сетевом слое платформы, до того как у вашего приложения появится сокет.

Дальше идёт отдельная группа ключей: настройки IPv4 и IPv6 туннеля, его включённые и исключённые маршруты, адреса DNS-серверов и на iOS и iPadOS сопоставление по приложениям, которое решает, какие приложения вообще могут использовать туннель. Профиль может соединить широкий триггер с узким маршрутом или узкий триггер с маршрутом по умолчанию. Прочитайте только половину, и вторая покажется неисправностью.

У меня есть лабораторный профиль, который подключается всякий раз, когда запрашивается что-то под внутренним доменом, но направляет в туннель только одну /16. Он делает ровно то, что написано в его payload, и это также тип профиля, который порождает заявки: люди предполагают, что подключённый туннель означает, что их трафик внутри.

Что доменное правило на самом деле спрашивает у резолвера

Доменная форма правила, часто называемая connect-if-needed, не проверяет payload пакетов и не наблюдает за URL. Когда имя разрешается, система сравнивает его с доменами в правиле и при совпадении спрашивает, можно ли разрешить это имя через DNS-серверы, которые предоставляет туннель. Если резолвер туннеля отвечает, имя считается внутренним и соединение начинается. Если вместо этого отвечает локальный резолвер, имя считается внешним и туннель не появляется. Обзор развёртывания Apple описывает это как обнаружение сети с последующей оценкой соединения.

Отсюда следуют два вывода. Профиль должен указывать DNS-серверы внутри туннеля; доменному правилу без резолвера туннеля не с чем сравнивать, поэтому оценка не даст ожидаемого совпадения. И запрос должен доходить до системного резолвера. Приложение или браузер, который запускает собственный зашифрованный DNS, расширение, отвечающее локально, или подключение к буквальному адресу никогда не создают то разрешение имени, которого ждало правило. Один из самых длинных моих дней закончился, когда я обнаружил, что клиент перед туннелем использовал DNS over HTTPS.

Кэширование тоже убирает триггер. Имя, уже находящееся в кэше, может быть разрешено без проверки, которая запустила бы туннель, поэтому правило, работавшее вчера, отказывает после перезагрузки или после того, как туннель какое-то время был поднят. В macOS sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder сбрасывает состояние; в iOS переключение авиарежима или перезагрузка устройства дают сопоставимый чистый старт.

Область совпадения: имена, приложения и сети

Совпадение по домену — один тип из нескольких. Правило может также совпадать с типом интерфейса (Wi-Fi, сотовый, Ethernet) и конкретным SSID, а записи по приложению или домену Safari привязывают туннель к одному рабочему процессу. Эта привязка объясняет странный опыт, когда только одно приложение ведёт себя иначе, чем всё остальное: другие приложения находятся вне области туннеля, поэтому продолжают использовать локальный путь даже тогда, когда значок туннеля говорит «подключено».

Порядок важнее, чем люди ожидают. Правила оцениваются последовательно, и первое совпадение даёт действие, поэтому широкое правило «подключаться к любой Wi-Fi», стоящее выше узкого доменного правила, тихо побеждает и делает доменное правило непроверяемым. Прежде чем переписывать список доменов, убедитесь, что ни одно более раннее правило уже не совпало.

Именно поэтому always-on — не просто более строгое правило on-demand. Принудительное применение always-on — отдельный режим развёртывания со своими требованиями к надзору и управлению; он держит туннель открытым, вместо того чтобы реагировать на имя. Если вам нужен туннель, который никогда не падает, доменное правило — неподходящий инструмент.

Режимы отказа, имитирующие сломанное правило

Большинство сообщений о том, что доменное правило не работает, попадают в одно из пяти мест, и только последнее из них — текст правила.

What you seeLayerFirst check
Tunnel comes up, internal site still failsroute or resolver after connectnetstat -rn, scutil --dns
Tunnel never comes up for that nametrigger pathfresh lookup, cache flush, private DNS
Tunnel comes up for every appscope, or an earlier rulerule order, per-app mapping
Tunnel drops and will not return on the same Wi-Fiinterface or SSID match, admissioncaptive portal, SSID string, auth prompt
Tunnel returns after you disconnect itprofile ownershipprofiles show

Captive-порталы заслуживают отдельной строки, потому что Wi-Fi в отелях и аэропортах перехватывает именно тот запрос, от которого зависит правило. Пока портал не пропустит устройство, DNS-сервер туннеля недоступен, проверка не удаётся, и сбой выглядит как неправильно написанный домен. Аутентификация — другой тихий случай: правило может сработать, туннель может начать подключаться, а соединение умирает на просроченном сертификате или на запросе, появляющемся на другом экране.

IPv6 создаёт вариант, который легко диагностировать неправильно. Имени с обоими семействами адресов могут сначала ответить из записи AAAA, тогда как список маршрутов туннеля несёт только IPv4. Один браузер справляется, другой нет, и список доменов совершенно невиновен.

Проверяйте реальным запросом, в таком порядке

Прочитайте политику, прежде чем гадать. В macOS sudo profiles show -type configuration печатает установленные профили, включая VPN-payload, scutil --nc list показывает состояние службы, а scutil --nc show <service> печатает словарь on-demand, который использует система. На управляемом iPhone или iPad тот же payload приходит из консоли управления или отчёта MDM. Сетевые журналы доступны с log stream --predicate 'subsystem == "com.apple.networkextension"' в macOS и через консоль устройства, когда телефон подключён.

Затем проверьте с именем хоста, которое не запрашивалось ранее в сеансе. Выберите имя в DNS-зоне туннеля, о котором вы знаете, что оно разрешается внутренне, запросите его один раз и посмотрите, появится ли туннель. Выполните тот же запрос дважды: вторая попытка задействует путь кэша, и разница между ними скажет, триггер — это разрешение или маршрутизация.

После подъёма туннеля проверьте два слоя отдельно. netstat -rn -f inet и netstat -rn -f inet6 показывают, куда ведёт каждый префикс, scutil --dns показывает порядок резолверов и какой интерфейс владеет каждым резолвером, а nettop -m route показывает, какой интерфейс перенёс байты. Если туннель поднят и резолвер верен, но ресурс всё равно недоступен, прекратите редактировать правило; проверьте авторизацию на стороне сервера и обратный маршрут.

Когда правило принадлежит кому-то другому

Если туннель появляется снова после отключения, правило обычно принадлежит профилю, а не приложению: конфигурация, выдвинутая через MDM, оставшийся личный профиль или профиль, установленный другим клиентом. sudo profiles show -type configuration плюс поиск по ключам on-demand сузят круг, и путь удаления — это профиль, а не переключатель в строке меню. Удаление записи службы или приложения оставляет правило, и следующая установка снова его наследует.

Один тест решает вопрос владения: отключите, подождите, запросите внутреннее имя снова. Если туннель возвращается, триггер работает как задумано, и открытый вопрос — кто им управляет и та ли это область, которая вам нужна.

Прежде чем сообщать об этом как об ошибке, пройдите те же пять проверок: адреса резолвера туннеля присутствуют в payload, имя не было в кэше, ни одно более раннее правило уже не совпало, путь AAAA покрыт списком маршрутов, и приложение находится в области туннеля. Когда все пять выполняются, правило делает свою работу, а неисправность находится дальше по пути.

Похожие статьи