Файрвол VPS: відкрийте лише порт VPN, а потім це доведіть
Один порт має залишатися публічним, інакше жоден клієнт не підключиться. Відхиліть решту, тримайте перенаправлення окремо від входу й залиште заздалегідь перевірений шлях відновлення.
Файрвол, який пропускає лише порт VPN, — це не одне правило. Це три різні рішення, які випадково стосуються того самого номера порту: ланцюг входу вирішує, що може дійти до локального процесу, ланцюг перенаправлення вирішує, що може пройти через хост від імені клієнтів тунелю, а таблиця NAT переписує адресу джерела на виході. Помилитеся в одному з них, і результат рідко буває чистим збоєм. Ви отримаєте хост, який приймає клієнтів, але нічого не завантажує, або хост, який виглядає закритим, доки хтось не прочитає порядок ланцюгів.
Єдиний порт, який не можна закрити
Слухач VPN має приймати з’єднання з адрес, які ви не можете передбачити, тому за визначенням це публічний сервіс. Це єдиний виняток, на якому побудовано дизайн: один UDP-порт для OpenVPN, один UDP-порт для WireGuard, один TCP-порт для SSL VPN. Усе інше належить за тунелем, отже, прикладні сервіси мають прив’язуватися до адреси тунелю або до loopback, а не до всіх інтерфейсів.
Однак прив’язка — не примус. Процес, прив’язаний до 0.0.0.0, доступний лише тоді, коли правило його пропускає, а політика за замовчуванням — це запасний варіант для трафіку, що не збігся з жодним правилом, а не правило, яке керує портом. Дозвіл, поставлений вище вашого блокування, залишається дозволом, і перенесення сервісу з публічної адреси — самостійне покращення, а не заміна ланцюга.
Вхід, перенаправлення і NAT відповідають на різні питання
Вхідний трафік адресований самому хосту, тому там живуть слухач VPN і адміністративний шлях. Трафік перенаправлення проходить через хост, і клієнтам тунелю він потрібен разом з net.ipv4.ip_forward=1. Класична напівзламана конфігурація дозволяє вихідний потік і забуває зворотний напрямок, тому клієнт завершує рукостискання і далі нічого не завантажується; правило conntrack для встановленого й пов’язаного трафіку покриває цей напрямок.
NAT — третє питання. Без перезапису в postrouting перенаправлені пакети виходять із приватною адресою тунелю й відкидаються вище, бо жодна мережа за межами хоста не знає, як маршрутизувати пул назад. Префікс джерела в цьому правилі має збігатися з пулом VPN, а не з власною адресою хоста, і те саме стосується правила перенаправлення, яке називає підмережу клієнтів.
IPv6 — це друга політика, а не пізня думка. Хост із суворим ланцюгом IPv4 і відкритою політикою на ip6tables нічого не закрив; сервіси досі доступні через іншу родину. Або віддзеркальте набір правил за допомогою ip6tables, або навмисно вимкніть IPv6 на публічному інтерфейсі, і запишіть, що ви обрали.
Основа, яку читають за один прохід
Почніть із заборони, а потім додайте лише те, що потрібно хосту. Спочатку loopback і відповіді, далі адміністративний шлях, далі слухач тунелю, далі політика перенаправлення для клієнтів:
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT
iptables -A INPUT -p udp --dport 1194 -m conntrack --ctstate NEW -j ACCEPT
sysctl -w net.ipv4.ip_forward=1
iptables -A FORWARD -i tun0 -o eth0 -s 10.8.0.0/24 -j ACCEPT
iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADEНазви інтерфейсів, порт і пул — це заповнювачі; читайте їх із сервера, а не копіюйте. Зберегти результат так само важливо, як написати його, бо набір правил, що живе лише в пам’яті, зникає під час наступного перезавантаження і тихо знову відкриває хост. Зробіть його постійним за допомогою механізму дистрибутива і тримайте копію поза машиною на той день, коли машина не завантажиться.
Закрити останній порт, не втративши хост
У дизайні є пастка. Якщо SSH існує лише через тунель, збій служби VPN забирає SSH із собою, і єдиним шляхом назад може бути консоль, яку ви ніколи не відкривали. Перш ніж закрити останній публічний порт, переконайтеся, що ця консоль існує і що ви справді можете нею користуватися, або залиште один вузький адміністративний шлях, що не залежить від тунелю: SSH із фіксованої адреси, лише ключі, автентифікація паролем вимкнена.
Застосовуйте зміну з уже встановленої сесії, а не через з’єднання, яке збираєтеся фільтрувати. Заплануйте відкат, щоб помилка спливла, а не стала постійною, наприклад завдання systemd-run --on-active=10min, яке відновлює попередній набір правил, і тримайте другу сесію SSH відкритою, поки перевіряєте першу. Потім переконайтеся, що завдання відкату справді спрацьовує, бо неперевірене відновлення — це те саме, що його відсутність.
Перевіряйте шлях, а не скрипт
Читайте завантажені правила, а не файл, який мав їх завантажити. З авторизованого зовнішнього хоста переконайтеся, що відповідають лише потрібні порти; зсередини тунелю переконайтеся, що сервіси, які ви хотіли опублікувати, доступні. Політика заборони за замовчуванням, яка блокує й трафік тунелю, виглядає так само, як робоче закриття, доки хтось не спробує нею скористатися.
iptables -S
iptables -t nat -S
ss -lntup
ip -4 addr showЗапишіть дозволений порт, пул клієнтів, інтерфейс тунелю, адміністративне джерело й процедуру з консоллю поруч із конфігурацією. Наступна зміна пулу або назви інтерфейсу має бути узгоджена і у фільтрі, і в правилі NAT, інакше хост повернеться в напіввідкритий стан, і ніхто цього не помітить.
Одна звичка цінніша за набір правил: після кожної зміни доведіть шлях перенаправлення запитом клієнта, а шлях відновлення — другою сесією. Закриття, яким ви не користувалися, — лише гіпотеза.