HAProxy 302 на приватний застосунок: хто відкриває другий перехід?
Перенаправлення віддає друге з'єднання браузеру. Надішліть запит до бекенду, а потім виправте власні абсолютні перенаправлення та cookie застосунку.
Хтось спрямовує публічний хост HAProxy на локальний застосунок і питає, чому браузер не може піти далі. Проксі дістається застосунку через свій VPN, перенаправлення повертає заголовок Location, і браузер потім пробує адресу, яка існує лише всередині тунелю. Ззовні сторінка просто зависає. Рішення стосується того, хто відкриває друге з'єднання, і саме це відрізняє перенаправлення від бекенду.
Два шляхи, які в конфігурації виглядають схоже
Перенаправлення завершує обмін відповіддю. Клієнт читає Location і починає новий запит звідти, де він перебуває, тобто з мережі, яку ви ніколи не контролювали. Проксі більше не задіяний. Усе подальше залежить від власних DNS, маршруту й довіри клієнта, тому результат залежить від того, де користувач відкрив браузер:
GET /app client -> proxy
302 Location proxy -> client (http://10.40.0.20:8080/)
GET /app client -> 10.40.0.20:8080 client network, no route
Бекенд проксі залишає другий етап на серверному боці. Проксі відкриває його наявним шляхом, а клієнт далі працює з одним публічним ім'ям. Якщо тунель падає, клієнт отримує помилку замість URL, за яким не може піти. Ця різниця ще й робить збій діагностованим, бо помилка з'являється там, де лежать логи:
GET /app client -> proxy
GET /app proxy -> 10.40.0.20:8080 over the VPN
response proxy -> client
Документація HAProxy щодо перенаправлень окреслює саме цю межу: відповісти клієнту перенаправленням — це інша дія, ніж переслати запит до бекенду. Обидві директиви стоять у файлі конфігурації за кілька рядків одна від одної й поводяться геть не схоже. Одна завершує запит, а інша починає другий, яким володіє проксі.
Що потрібно браузеру і чого не дає VPN сервера
Власний запит браузера потребує чотирьох речей, яких у нього може не бути: відповіді DNS для приватного імені, маршруту до цієї адреси, дозволу дістатися служби, що слухає, і довіри до сертифіката, який ця служба надасть. Хост проксі має всі чотири завдяки своєму тунелю. Жодна з них не переноситься на клієнта, який ніколи не був у цьому тунелі, бо тунель — властивість хоста, а не URL.
Ця прогалина пояснює симптом «майже працює». Перший запит проходить, адресний рядок змінюється, а потім з'єднання зависає або обривається, хоча проксі весь час здоровий. У конфігурації проксі нічого не зламано, і її правка не допоможе клієнту, в якого немає шляху до 10.40.0.20. Або клієнт отримує маршрут, або проксі має встановити з'єднання за нього.
Коли до застосунку може дістатися лише проксі
Приберіть дію перенаправлення й надішліть ці запити до бекенду, що вказує на приватну адресу. Фронтенд зберігає публічне ім'я, а бекенд вирішує, куди насправді йде запит. У шляху клієнта нічого не змінюється, бо проксі перестає описувати пункт призначення й сам стає ним. Для застосунку, який навмисно слухає внутрішній порт 8080, блок сервера короткий:
backend private_app
mode http
option httpchk GET /health
server app1 10.40.0.20:8080 check
Читайте цей блок як твердження про таблицю маршрутизації проксі: адреса 10.40.0.20 має бути доступною з самого хоста проксі, що тут означає через інтерфейс VPN, а не через маршрут за замовчуванням. Ключове слово check лише повідомляє, що робить цей маршрут, а шлях перевірки здоров'я має існувати в застосунку, інакше кожна проба позначає сервер недоступним. Успішна перевірка нічого не доводить про клієнта й не замінює політику, яка вирішує, кому можна користуватися бекендом.
Цей фрагмент — не повний фронтенд. Збережіть слухач, автентифікацію, вибір дозволеного хоста й шляху, а також правило, яке вирішує, хто взагалі може дістатися застосунку. Узгодьте й протокол бекенду, бо HTTPS-бекенду потрібні перевірка сертифіката й налаштування ідентичності сервера, а пропуск цієї перевірки перетворює приватне з'єднання на неперевірене.
Перш ніж оголошувати перемогу, підтвердьте зворотний шлях в обох напрямках. Тепер застосунок бачить з'єднання від проксі, тому його логи й будь-які метадані про адресу клієнта описують проксі, якщо не влаштувати інакше. Вирішіть, як обробляються ідентичність і переслані адреси, і перевірте це рішення, а не припускайте його.
Власні перенаправлення застосунку все ще ведуть усередину
Пересилання першого запиту — лише половина інтеграції. Застосунок може видавати абсолютні перенаправлення, жорстко задані URL ресурсів або cookie, прив'язані до імені, яке він знає зсередини, і кожне з цього відправляє браузер назад на адресу, до якої той не дістанеться. Заголовок Location, що вказує на внутрішнє ім'я, виглядає точно як початкова проблема, тому його хибно діагностують як збій проксі.
Задайте задокументований зовнішній URL застосунку та його параметри проксі, потім приведіть заголовок Host у відповідність до віртуального хоста й очікувань сертифіката. Cookie, прив'язаний до внутрішнього імені, не повернеться через публічне ім'я, і наступний цикл входу читається як помилка автентифікації. Спершу змініть зовнішній URL, повторіть тест із чистого профілю браузера й очікуйте, що наполовину перенесені налаштування дадуть вхід, який проходить, і сесію, яка одразу вмирає.
Довіряйте пересланим метаданим лише від призначеного проксі. Заголовок, який може виставити будь-який клієнт, ніколи не повинен ставати значенням авторизації, інакше проксі перетворюється на спосіб для того, хто викликає, привласнити чужу ідентичність. Це правило не залежить від питання про перенаправлення й переживає будь-який подальший редизайн.
Вирішіть, який дизайн у вас насправді
Якщо в усіх користувачів уже є авторизований доступ через VPN, перенаправлення на приватний URL може бути свідомим рішенням. Підтвердьте цю вимогу явно, а не припускайте її, бо ці два дизайни обслуговують цілком різні групи. Один із них зовсім не працює з готельної мережі, а другий відкриває застосунок будь-кому, хто може завантажити публічний URL. Ця відмінність має бути в проєктній записці.
Запустіть перевірку з машини в мережі користувачів і простежте кожен перехід, який робить браузер, а не лише першу відповідь. Стежте за адресним рядком і за кожним наступним запитом, бо одного абсолютного URL у тегу ресурсу достатньо, щоб зламати потік, який пройшов перший етап. Вхід має працювати через публічне ім'я, а ресурси, які мають бути заборонені, повинні залишитися забороненими.
Публікація зворотного проксі — окреме рішення про доступ, а не прозоре розширення власного VPN проксі на будь-кого, хто може завантажити його публічний URL. На діаграмі ці речі виглядають однаково, а різняться вони тим, кому дозволено вхід. Вирішення цього питання після розгортання — це і є спосіб випадково перетворити внутрішній застосунок на публічний.
Запишіть, який варіант ви збудували і чому. Наступна людина, яка візьметься за конфігурацію, припускатиме інший. Одного перенаправлення, доданого для зручності, достатньо, щоб відтворити початковий збій, і симптом виглядатиме точно як той, який ви щойно виправили.