Дозволити прохідне TLS-з’єднання в контексті stream NGINX
NGINX може відхилити ретрансляцію TCP TLS, не завершуючи TLS. Розмістіть правило дозволу в stream, а не в HTTP location, і перевірте, яка адреса партнера надходить.
Передавання зашифрованих байтів без володіння сертифікатом — це те, що робить релей TLS passthrough, і така схема зручна й водночас трохи заплутана. Оскільки нічого не розшифровується, немає нічого у формі HTTP для фільтрування, тому звичайний блок location та його правила доступу тут нічого не дають. Рішення, яке має NGINX, стосується самого з’єднання: який мережевий партнер постукав і чи слід його взагалі пропускати.
Рішення належить stream, а не HTTP
У контексті stream NGINX працює з TCP-сесіями. Він бачить адресу партнера з’єднання і може застосувати до неї впорядковані правила allow і deny, перш ніж будь-який байт досягне backend. Це повне рішення про допуск, і воно не потребує приватного ключа, сертифіката чи розбору HTTP.
| Mechanism | What it decides | Where it lives |
|---|---|---|
| stream allow/deny | Whether this TCP peer is relayed | stream server block |
| ssl_preread map | Which backend a named connection goes to | stream, with the preread module |
| HTTP location rule | Whether an HTTP request is served | http server block |
Ці три рядки ніколи не замінюють один одного. Копіювання HTTP-директиви доступу до будь-якого файлу, де згадується порт 443, нічого не змінює, бо сервер stream ніколи не читає контекст http.
Прийміть з’єднання, перш ніж пересилати його
Припустімо, є наявна інсталяція з доступними модулями stream і stream-access, клієнти VPN тримають адреси джерела в 10.90.0.0/24, а backend на 192.168.50.20:443 уже надає потрібну ідентичність HTTPS.
stream {
server {
listen 443;
allow 10.90.0.0/24;
deny all;
proxy_pass 192.168.50.20:443;
}
}
Читайте це як фрагмент нового listener, а не як пряму заміну робочої конфігурації. Спершу перегляньте наявні listener та include, бо другий listener на вже зайнятому порту падає під час reload, а не під час запиту.
Зверніть увагу, чого немає: немає listen ... ssl, немає директиви сертифіката. NGINX пересилає зашифрований потік, а backend завершує TLS і подає сертифікат, який перевіряє клієнт. Клієнт, що не пройшов allowlist, отримує відхилену TCP-сесію, а не сторінку HTTP 403, і будь-який інструмент, що очікує код стану, повідомить щось марне.
Що охоплює allowlist, а що ні
Якщо релей уже вибирає backend за іменем сервера, ssl_preread і map можуть продовжувати цю роботу, за умови що модуль доступний. Ім’я все ще надає клієнт, і ніщо в поданні імені не доводить, що клієнт приєднався до VPN.
Правило з прикладу охоплює весь listener stream. Це робить його непридатним для однієї точки входу, що обслуговує суміш публічних сайтів і сайтів лише для VPN, де знадобився б виняток для кожного імені; окремі listener зазвичай зрозуміліші за політику, залежну від імені, яку ніхто не може прочитати.
Варто назвати ще дві прогалини. Збіг префікса нічого не каже про те, через який інтерфейс надійшов пакет, а правило HTTP в іншому місці не може обмежити цей listener. Обидві причини тримати політику входу на рівні хоста в полі зору, а не вважати один рядок allow усією межею. Запишіть межу як два контролі, що мають збігатися, щоб зміна будь-якого з них проходила перевірку.
Чию адресу NGINX насправді бачить?
Балансувальник навантаження, інший proxy або source NAT можуть поставити посередника перед NGINX, тож спостережуваний партнер — не внутрішня адреса клієнта VPN. Перевірте реальний шлях, перш ніж вибирати префікс, бо allowlist, написаний для неправильної адреси, або пропускає всіх за транслятором, або нікого.
Протокол PROXY і обробка справжнього IP — законні відповіді, і їх слід налаштовувати лише для сумісних, явно довірених відправників. Обидва механізми вірять тому, що каже відправник, і саме тому список відправників має бути коротким. Довіра до заголовка ідентичності від будь-якого інтернет-клієнта не робить відомості про джерело знову надійними; вона перетворює правильну відмову на підроблений допуск.
Перевірте обидва результати перед reload
Перевірте конфігурацію, виконайте reload, потім перевірте двічі: нову дозволену сесію, яка має працювати, і відхилену, яка не повинна. Збережіть звичайну перевірку сертифіката backend у дозволеному тесті, бо релей, який підключається, але не може перевірити backend, вирішив одну проблему і створив іншу.
nginx -t
ss -tnp | grep ':443'
Перевірте також маршрут до backend і відповідь окремо. Проходження allowlist доводить лише те, що NGINX прийняв сесію; воно не може змусити недоступний backend відповісти і не може виправити зворотний шлях, якого не було до початку.