Телефон перезавантажується, always-on увімкнено, «блокувати з'єднання без VPN» увімкнено, а тунель так і не піднімається. Нічого не налаштовано неправильно: блокування робить свою роботу, і спроба самого клієнта дістатися своєї кінцевої точки теж йому підпорядкована. Ім'я кінцевої точки треба розв'язати до того, як тунель існує, клієнтом, якому дозволено говорити з мережею, тоді як усім іншим застосункам ні. Розуміння того, який компонент це робить і в якому порядку, перетворює цю загадку на питання конфігурації.

Що насправді запитує конструктор VPN

Застосунок Android VPN не передає платформі адресу сервера. Він будує локальний інтерфейс: адреси й маршрути для тунелю, резолвери, які застосунки мають використовувати всередині нього, а потім establish() повертає файловий дескриптор, який застосунок читає й записує як необроблені IP-пакети. Віддалена кінцева точка в цьому конструкторі взагалі не з'являється.

Це ключовий структурний факт. Платформа створює інтерфейс tun і застосовує до нього політику; застосунок реалізує транспорт у просторі користувача, тобто сам розв'язує ім'я своєї кінцевої точки й відкриває зашифроване з'єднання. Тому кінцева точка у вигляді імені хоста можлива — не через те, що платформа підтримує імена, а через те, що платформа ніколи не бачить кінцеву точку.

Одне поле конструктора все ж приймає адреси: DNS-сервери. Вони мають бути числовими адресами резолверів, зазвичай тими, які оператор хоче опитувати через тунель. Це поле описує, куди застосунки надсилають запити після підняття тунелю. Воно нічого не каже про те, як клієнт знаходить кінцеву точку, і саме змішування цих двох налаштувань породжує плутанину.

Два питання DNS, а не одне

Перше питання: куди застосунки надсилають запити, коли тунель уже піднято? Відповідь — адреса резолвера всередині тунелю, доступна через інтерфейс tun і за побудовою числова.

Друге питання: як клієнт знаходить свою власну кінцеву точку до того, як цей тунель існує? Цей пошук відбувається поза тунелем, у нижележній мережі, у процесі клієнта. Він може використовувати ім'я і мусить використовувати резолвер, доступний пристрою без VPN.

StageWho resolvesPathFailure looks like
Endpoint lookupThe VPN appUnderlying network, protected socketNever connects; no tunnel interface traffic
Peer authenticationClient and serverTunnel transportConnects then drops; certificate or key error
Private name lookupApps via the tunnelResolver inside the tunTunnel up, internal sites do not resolve

Три етапи, три різні симптоми. Одне повідомлення про помилку «не вдається підключитися» зливає їх воєдино, і саме тому люди починають редагувати поле DNS-сервера, щоб виправити проблему, яка виникає ще до того, як це поле взагалі буде прочитане.

Чому bootstrap може зайти в глухий кут

Always-on з увімкненим блокуванням означає, що трафік, який не перебуває всередині VPN, відкидається, зокрема трафік застосунку, якому належить VPN. Вихід задано явно: застосунок викликає protect() на сокеті, що прив'язує його до нижележної мережі, тож він обходить тунель і отримує дозвіл вийти. Кожен VPN-клієнт мусить робити це для свого транспорту, інакше він ніколи не дістанеться сервера.

Тепер розгляньмо розгортання, у якому єдиний резолвер, що знає ім'я кінцевої точки, сам розташований усередині тунелю. Клієнту потрібен тунель, щоб розв'язати ім'я, і ім'я, щоб побудувати тунель. Це справжня циклічна залежність, і жодна конфігурація клієнта її не розв'язує. Це помилка проєктування, а не баг.

Час викриває ту саму залежність у м'якших формах. Під час завантаження до першого розблокування пристрій може ще не мати придатної мережі; на captive portal мережа є, але блокує все, крім порталу; під час переходу з Wi-Fi на мобільну мережу нижележна мережа захищеного сокета змінюється, і клієнт мусить це помітити й розв'язати ім'я заново. Кожне з цього є подія bootstrap, і кожне варто перевіряти навмисно, а не виявляти о 3 годині ночі.

Чому фіксація відповіді не є рішенням

Спокусливий обхідний шлях — розв'язати ім'я один раз і вставити отриману адресу в числове поле або жорстко закодувати її у власній збірці. Це виглядає робочим і перетворює ім'я на знімок. Коли оператор переміщує кінцеву точку — міграція, зміна anycast, нова регіональна адреса — клієнт продовжує набирати стару, і ніщо в інтерфейсі не пояснює чому.

Записи DNS мають час життя саме для того, щоб відповіді могли змінюватися; кешування визначається цим строком, а не назавжди. Фіксація відповіді відкидає механізм, який робить ім'я кращим за адресу, зберігаючи при цьому експлуатаційні витрати використання імені.

Фіксація зазвичай іде пліч-о-пліч з другою поганою звичкою: послабленням перевірки автентичності, бо з'єднання тепер падає інакше. Перевірка автентичності сервера не є частиною проблеми bootstrap, і її послаблення обмінює незручність DNS на ризик перехоплення.

Рішення, що переживають перезавантаження

Довговічне рішення неголосне. Використовуйте клієнт, який коректно реалізує захищені сокети й розв'язує свою кінцеву точку в нижележній мережі, і дайте йому ім'я кінцевої точки, що розв'язується публічно або через резолвер, доступний до тунелю. Залиште блокування ввімкненим; повне його вимкнення заради роботи DNS прибирає властивість, заради якої існує розгортання.

Там, де кінцева точка має залишатися приватною, забезпечте явний шлях bootstrap, яким платформа може скористатися — резолвер, доступний без тунелю, або керівне з'єднання, яке підтримує оператор, — і тримайте цей виняток вузьким і належним політиці, а не користувацькому перемикачу. Документація Android VpnService — це довідка про те, що платформа гарантує щодо захисту та життєвого циклу, а довідка з VpnService.Builder підтверджує, що поле DNS приймає числові адреси.

Потім перевірте три етапи по порядку на новому з'єднанні після перезавантаження: ім'я кінцевої точки розв'язується, тунель встановлюється і партнер автентифікується, а приватне ім'я розв'язується через резолвер тунелю. Якщо перший етап не проходить, ніщо наступне не може працювати, і виправлення належить шляху bootstrap — ніколи полю резолвера.

  • Розв'язання кінцевої точки відбувається в застосунку, поза тунелем, на захищеному сокеті.
  • Поле DNS у конструкторі призначене для застосунків усередині тунелю, а не для власної кінцевої точки клієнта.
  • Резолвер, доступний лише через тунель, — це глухий кут, а не повільний старт.
  • Перевіряйте кінцеву точку, автентифікацію та приватний пошук окремо після перезавантаження.

Схожі статті