Доступ до служби на клієнті VPN: зворотний тунель або DNAT
Доступ до служби на клієнті OpenVPN без переспрямування порту: порівняйте зворотний тунель SSH з DNAT на сервері та три умови, що визначають вибір.
Навіть ноутбук за NAT оператора може обслуговувати колегу, якщо сервер VPN слугує точкою зустрічі. Туди ведуть два механізми: клієнт підключається назовні й просить сервер тримати слухаючий порт, або сервер публікує порт і транслює його в тунель. Обидва розв'язують ту саму проблему доступності й поводяться по-різному під час збою, нагляду та журналювання. Три умови визначають, який із них пасує вашій конфігурації.
Дві форми, одна точка зустрічі
У першій формі клієнт відкриває сеанс SSH назовні й просить сервер слухати вибраний порт, повертаючи кожне з'єднання через цей сеанс. На сервері нічого не пересилається й трансляції пакетів не відбувається, тож єдині вимоги — обліковий запис SSH, дозволене пересилання й правило брандмауера для порту. Служба залишається прив'язаною до loopback на клієнті, що зазвичай і потрібно.
ssh -N -R 8081:127.0.0.1:8080 -o ExitOnForwardFailure=yes -o ServerAliveInterval=30 [email protected]
У другій формі сервер володіє публічним портом і переписує кожне з'єднання в бік адреси тунелю клієнта. Для цього потрібні ввімкнене пересилання IP, трансляція призначення на вході й трансляція джерела на виході, і це продовжує працювати, коли сеансу SSH немає. Порт належить серверу, а не сеансу, і саме ця властивість вирішує більшість реальних випадків.
Жодна з форм не безпечніша за своєю конструкцією. Обидві зрештою відкривають один порт служби всім, хто може дістатися публічної адреси сервера, і обидві досі залежать від власної автентифікації служби.
Умова перша: чи має порт жити довше за сеанс
Зворотне пересилання живе рівно стільки, скільки сеанс SSH, що його створив. Коли зв'язок падає, слухач зникає, і колега бачить відмову в з'єднанні замість повільного шляху. Нагляд за командою через юніт systemd і політику перезапуску це вирішує, а ExitOnForwardFailure=yes робить невдале прив'язування одразу видимим, замість того щоб лишити сеанс без пересилання.
Трансльований порт переживає перезавантаження, якщо правила збережено, але залежить від того, чи збереже клієнт ту саму адресу тунелю. Динамічний пул, який після повторного підключення дає ноутбуку іншу адресу, мовчки спрямовує правило в нікуди. Спершу зарезервуйте адресу тунелю, потім напишіть правило й вважайте це однією зміною.
Якщо служба має відповідати о третій ночі без нагляду, у форми з опублікованим сервером портом менше рухомих частин, бо єдиний процес, що мусить залишатися запущеним, — сам тунель.
Умова друга: кому належать брандмауер і журнали
Зворотна форма тримає всю політику в двох місцях. sshd через AllowTcpForwarding вирішує, чи взагалі дозволені віддалені пересилання, а GatewayPorts вирішує, чи прив'язується слухач до loopback чи до кожної адреси. Зі значенням за замовчуванням no колега поза сервером не може дістатися порту, хоча пересилання вдалося, і це виглядає як зламаний тунель, коли насправді це адреса прив'язування.
sudo ss -ltn '( sport = :8081 )'
sudo grep -E '^(GatewayPorts|AllowTcpForwarding)' /etc/ssh/sshd_config
Трансльованій формі потрібні ввімкнене пересилання й два правила, і саме на другому правилі помиляється більшість рецептів. Обмеження трансляції джерела станом conntrack DNAT означає, що переписуються лише з'єднання, створені правилом призначення, а не кожен пакет, який сервер надсилає в тунель.
sudo sysctl -w net.ipv4.ip_forward=1
sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 8081 -j DNAT --to-destination 10.88.0.6:8080
sudo iptables -t nat -A POSTROUTING -o tun0 -m conntrack --ctstate DNAT -j SNAT --to-source 10.88.0.1
Цей перепис також руйнує атрибуцію. Після нього клієнт бачить кожного викликача як адресу тунелю сервера, що згортає будь-яке обмеження швидкості на адресу в одне відро й приховує, хто що надіслав. Якщо атрибуція важлива, завершуйте з'єднання на сервері або залиште там рішення про дозвіл і передавайте ідентичність у заголовку, якому служба довіряє. Вибір між цими двома виправленнями залежить від того, чи читає служба заголовок проксі взагалі.
Умова третя: один порт чи багато, і кому належить ім'я
Одна служба на одному порту — це випадок, коли зворотне пересилання виграє за витратами праці. Потрібні одна команда, один обліковий запис і одне правило брандмауера, а прибирання — це завершення сеансу. Кілька служб на одній публічній адресі штовхають вас до форми з публікацією на сервері, бо кожному зворотному пересиланню потрібен свій порт сервера, і набір зростає з кожною службою.
Імена ще більше все ускладнюють. Зворотне пересилання може стояти за проксі TCP, який маршрутизує за SNI, тоді як трансльований порт зазвичай один порт на службу. Якщо служба сама завершує TLS і потребує початкового імені хоста, підхід із проксі зберігає його, а підхід із DNAT зберігає лише тоді, коли служба приймає це ім'я на цьому порту.
Запишіть, яка з трьох умов визначила форму. Інакше наступна людина, яка додасть другу службу, знову відкриє те саме порівняння з меншим контекстом.
Вирішіть, потім перевірте ззовні
Перевірте шлях із мережі, яка не має маршруту до ноутбука, і спостерігайте за лічильниками на сервері, поки запит виконується. Перевірка із сервера на його власну публічну адресу тут нічого не доводить, бо пакет створюється локально й ніколи не доходить до вхідного гачка трансляції, тож тест може пройти, поки реальний шлях зламано.
sudo iptables -t nat -L PREROUTING -n -v
sudo tcpdump -ni any 'tcp port 8081' -c 20
Лічильники, що стоять на місці, означають, що пакет так і не дійшов до правила, що вказує на провайдера або брандмауер хоста. Лічильники, що рухаються, поки служба відмовляє, вказують на власний брандмауер клієнта або адресу, на якій служба слухає. Тунель, який несе пакет, але нічого не повертає, — це проблема маршруту або зворотного шляху, і жоден із цих трьох результатів не слід налагоджувати редагуванням адаптера клієнта. Запишіть, який лічильник зрушив, перш ніж щось змінити, бо наступному тесту потрібна ця базова лінія.
Коли форма запрацює, запишіть порт, власника й метод нагляду поруч із конфігурацією тунелю. Виправлення доступності, що існує лише в чиїйсь історії оболонки, перебуває за одне перезавантаження від повторного інциденту.