Hairpin NAT: чому кінцева точка VPN не працює зсередини
Локальний клієнт, що використовує публічну адресу, залишає LAN і повертається всередину. Коли маршрутизатор не робить hairpin, зовні працює, а зсередини ні.
VPN-сервер, який відповідає з мобільної мережі, але не працює з дивана, зазвичай не є проблемою сертифіката. Клієнт усередині LAN просить маршрутизатор перекласти пакет, призначенням якого є власна публічна адреса маршрутизатора, тобто пакет має вийти назовні й одразу повернутися всередину. Цей маневр називається hairpinning, і багато маршрутизаторів або взагалі його не реалізують, або роблять це лише за встановленого прапорця. Саме успішний тест зовні й заплутує, бо доводить, що слухач і правило переспрямування гаразд.
Пакет виходить назовні й повертається всередину
Простежте пакет. Клієнт розв'язує публічне ім'я, з'єднується з публічною адресою, і NAT призначення на маршрутизаторі переписує призначення на внутрішній сервер. Відповідь сервера адресована LAN-адресі клієнта, тому вона повертається через LAN-інтерфейс маршрутизатора, жодного разу не проходячи через те правило NAT, яке повернуло б джерело.
Тепер клієнт отримує пакет з адреси, куди нічого не надсилав, і відкидає його. Ця асиметрія і є вся поломка.
RFC 4787 описує поведінку, яку очікують від NAT у цьому випадку, і коротко кажучи, на практиці вона необов'язкова навіть там, де задана теоретично. Одні маршрутизатори називають це налаштування NAT loopback, інші — hairpin NAT або NAT reflection, а деякі взагалі нічого не показують. Маршрутизатор без цього налаштований не неправильно; він робить звичайну річ, і виправлення належить іншому місцю. Не робіть з невдалого локального тесту висновку, що публічне правило переспрямування хибне.
Перевірка 1: перевірте два шляхи окремо
Ззовні LAN підключіться з тим самим профілем, який використав би реальний користувач, і підтвердьте, що сеанс встановлюється. Зсередини підключіться до публічної адреси з тим самим профілем і запишіть точну помилку та час.
Точний локальний тест зберігає ім'я хоста й замінює лише адресу: curl -v --resolve vpn.example.com:443:203.0.113.10 https://vpn.example.com/ надсилає запит на публічну адресу, все ще подаючи правильне ім'я сервера. Це відокремлює проблему розв'язання імен від проблеми перетворення, які легко переплутати, коли те саме ім'я працює з кав'ярні. Зверніть увагу, чи клієнт падає на TCP-з'єднанні, чи пізніше на TLS, бо перше вказує на перетворення, а друге на ідентичність.
Перевірка 2: прочитайте, що бачить сервер
Найкорисніше спостереження — це погляд сервера. Запустіть захоплення на VPN-хості з фільтром за LAN-адресою клієнта і за LAN-адресою маршрутизатора, потім повторіть локальну спробу. Якщо нічого не надходить, пакет не пережив перетворення.
Якщо з'єднання надходить з LAN-адреси маршрутизатора, а не клієнта, hairpinning відбувається і джерело переписано. Це робоча конфігурація, а не поломка.
Перевірка 3: виправте, не торкаючись зовнішнього шляху
Існують три підтримувані форми за зростанням радіуса впливу. Найвужча — розділений DNS: опублікуйте внутрішній запис, щоб те саме ім'я сервера розв'язувалося на внутрішню адресу для клієнтів у LAN, що повністю оминає публічний шлях. Наступна — увімкнути документовану опцію loopback або reflection на маршрутизаторі, якщо вона є, і застосувати її лише до цього сервісу. Найширша — правило source-NAT на маршрутизаторі, яке переписує трафік з LAN на сервер, щоб відповідь поверталася через маршрутизатор; на Linux-маршрутизаторі це правило MASQUERADE у POSTROUTING, що відповідає адресі та порту сервера. Усі три залишають зовнішній шлях недоторканим, і саме цю властивість треба зберегти.
Не розширюйте не пов'язане із завданням переспрямування портів, щоб локальний шлях став схожим на зовнішній, і не переносьте сервіс на інший порт, щоб обійти проблему. Обидві зміни змінюють те, чого може сягнути зовнішній світ, аби виправити те, що бачить лише внутрішній.
Розділений DNS має одну пастку, яку варто назвати. Заміна імені хоста довільним IP змінює те, що перевіряє клієнт, тому внутрішній запис усе одно має відповідати імені в сертифікаті.
Перевірка 4: після цього перевірте обидва шляхи
Повторіть зовнішню спробу ззовні LAN, повторіть внутрішню спробу з тим самим профілем і підтвердьте, що журнал сервера показує два окремі успішні сеанси, а не один успіх і одну тишу. Потім перевірте, що інші клієнти досі доходять до слухача і що маршрути до приватних ресурсів не змінилися.
Тримайте запис коротким: яку адресу використав кожен клієнт, що побачив сервер і яке з трьох виправлень застосовано. Вимоги hairpinning з RFC 4787 тут є орієнтиром того, що очікують від NAT, і пояснюють, чому маршрутизатор без цієї функції поводиться нормально. Локальний збій, який зовнішній світ не може відтворити, є питанням перетворення, доки не доведено протилежне, тож перевіряйте його саме так.