Фаєрволи зазвичай не відмовляють голосно. Вони працюють як чемний саботажник: деплой «успішний», моніторинг сідає в сірий, а команда з рефлекторної звички звинувачує DNS.
Debian 13 дає все необхідне для чистої, мінімальної й нудної політики фаєрвола. Цей посібник про те, щоб пропускати правильний трафік, відкидати інше і швидко діагностувати збої — без перетворення сервера на музейний експонат, яким ніхто не зможе керувати.
Принципи: мінімально, зі збереженням стану та спостережуваністю
«Мінімальний профіль фаєрвола» — це не «заблокувати все й помолитися». Це явна політика, що відповідає способу використання машини, реалізована зі стейтфул-фаєрволом, щоб не писати вручну кожен відповідний пакет, ніби це 1999 рік.
1) За замовчуванням відкидати вхідні, за замовчуванням дозволяти встановлені
Вхідний трафік слід відкидати, якщо немає причини його приймати. Але коли ви приймаєте з’єднання, ви повинні пропускати пов’язані відповіді. Для цього служить відстеження стану. Якщо спробувати бути хитрим і блокувати половину TCP-хендшейку, ви тільки знайдете нові способи мучитися.
2) Вихідний трафік — там «без параної» перетворюється на «без сюрпризів»
Більшість мінімальних профілів дозволяють вихідний трафік за замовчуванням. Це підходить для багатьох серверів — поки ви не чистите скомпрометований процес, що без зусиль вивозить дані по 443 куди завгодно. Здоровий компроміс: дозволяти вихід широко, але логувати і за потреби обмежувати «дивні» місця призначення чи порти, коли зрозумієте роль боксу.
3) Логи повинні відповідати на питання, а не створювати мистецтво
Якщо логувати кожен відкинутий пакет, логи стануть дорогим сховищем ботнет-погоди. Логуйте з обмеженням швидкості і тільки на межах, які ви реально розслідуєте: нові вхідні відмови, несподіваний вихідний трафік і заблоковані типи ICMP, що ламають PMTUD.
4) Якщо це не можна протестувати, це не політика
Набір правил, що існує лише в чиїйсь голові, — це казка на ніч. Ваш фаєрвол має бути тестованим із хоста та з мережі. Потрібні команди, що перевіряють «SSH працює з адмін-підмережі», «моніторинг може опитувати» і «DNS-резолюція не ламається мовчки».
Цитата, яка й досі актуальна: «Сподівання — це не стратегія.»
— John C. Maxwell. Операційні люди повторюють це, бо альтернатива — писати інцидентні звіти про «ми припустили».
Короткий жарт №1: Фаєрвол — як швейцар: якщо він не знає, хто в списку гостей, рано чи пізно не допустить діджея і впустить хлопця з маршрутизатором.
Цікаві факти та історичний контекст (коротко, корисно)
- Netfilter з’явився в Linux 2.4 (початок 2001 року), замінив старий код фільтрації і дав сучасну модель conntrack, що робить «established/related» корисним.
- iptables — це інтерфейс, а не рушій. Під капотом — netfilter; інструменти змінювалися, бо інтерфейс став складним і важко розширюваним.
- nftables введено, щоб уніфікувати варіанти iptables (iptables, ip6tables, arptables, ebtables) в одну систему з кращою оцінкою правил і структурами даних.
- conntrack — це спільний стан, а не магія. Він споживає пам’ять; під навантаженням або під атакою погане налаштування може спричинити відкидання з’єднань, що виглядає як «випадкові мережеві збої».
- ICMP — не опціональний для здорового TCP. Блокування всього ICMP ламає PMTUD і викликає затори на деяких маршрутах та MTU. Про це люди згадують кожні кілька років.
- Стейтфул-фаєрволи — не лише для входу. Політики виходу часто покладаються на стан, щоб дозволити зворотний трафік без відкриття вхідних портів.
- Debian довго відстоює «роби одну річ добре». Він не нав’язує менеджер фаєрвола; ви отримуєте nftables, сумісність iptables-nft і самі обираєте рівень опінійності обгортки.
- Хмарні security groups змінили очікування. Багато команд тепер вважають хост-фаєрволи другорядними. Це працює, поки хтось не перемістить VM в інший мережевий кордон або не неправильно налаштує security group.
Модель загроз, якою реально можна користуватися
Мінімальний профіль фаєрвола — це не список відповідності. Це мапа того, який трафік має існувати. Почніть з того, від чого ви захищаєтесь:
- Інтернет-сканування та опортуністичні експлойти. Якщо ви відкриваєте SSH або веб-порт, очікуйте постійний фоновий шум і потік сумнівних спроб.
- Латеральний рух всередині вашої мережі. Внутрішні мережі не є магічно безпечними. Вони просто там, де атаки тихіші.
- Випадкове експонування. Розробник прив’язав дебаг-сервер до 0.0.0.0 або пакет відкрив порт, про який ви забули.
- Експорт даних і канал керування. Вихідні правила — єдине місце, де ви реально можете уповільнити це на рівні хоста.
- Власноруч спричинені відмови. Більшість інцидентів з фаєрволом — це не хакери. Це ви о 2 ранку, що вносите «одну маленьку зміну».
Мінімальний не означає крихкий. Мета — набір правил, достатньо малий, щоб його можна було осмислити, і достатньо суворий, щоб уникнути сюрпризів.
Що дозволяти проти чого відкидати (типовий сервер Debian 13)
Вхідні: відкидання за замовчуванням, потім додавати лише те, що ви можете назвати
У вхідних ви виграєте, будучи нудними. Почніть із відмови за замовчуванням, потім явно дозвольте:
- SSH (TCP/22) з адмін-підмережі, бастіону або діапазону VPN. Якщо потрібно дозволити звідусіль, додайте обмеження частоти і надійну автентифікацію.
- HTTP/HTTPS (TCP/80, TCP/443) тільки якщо хост дійсно є веб-ендпоінтом. Багатьом машинам ці порти взагалі не потрібні відкритими.
- Вхід моніторингу (варіюється): наприклад, node exporter (9100), SNMP (161/udp), кастомні агенти. Віддавайте перевагу «pull з мережі моніторингу», а не глобальному доступу.
- Порти специфічні для сервісів баз даних і брокерів лише до підмереж застосунків, що їх потребують. Бази даних майже ніколи не повинні приймати з’єднання з «будь-де».
- ICMP селективно: дозволяйте echo-request з довірених мереж і дозволяйте помилки ICMP, необхідні для PMTUD (принаймні повідомлення про необхідність фрагментації).
Вихідні: почніть помірковано, потім звужуйте відповідно до реальності
Вихідні — там, де люди або нічого не роблять, або йдуть у повний бункер. Практичний підхід:
- Дозволити DNS (UDP/TCP 53) тільки до ваших резолверів або дозволити до локального stub-резолвера, якщо він у вас є. Випадковий вихід на DNS часто свідчить про проблему.
- Дозволити NTP (UDP/123) до ваших джерел часу. Зсув часу викликає проблеми з автентифікацією і плутанину в логах.
- Дозволити оновлення пакетів (TCP/80/443) у світ, якщо потрібно, або до корпоративного проксі/дзеркала, якщо він є.
- Дозволити бізнес-ехід: бази даних, message bus, об’єктні сховища, API. Документуйте їх; інакше ви «оптимізуєте» себе до відмови пізніше.
Що відкидати без докорів сумління
- Усе вхідне, що не дозволене явно.
- Весь вхідний UDP за замовчуванням, якщо ви не запускаєте UDP-сервіс (DNS-сервер, NTP-сервер, VPN-точка, syslog-рісівер тощо). Відкритий UDP запрошує дивні ситуації.
- Весь вхідний SMB/NFS/RPC якщо хост не є файловим сервером і ви його не звузили сильно. Ці протоколи чудові — в межах правильної зони довіри.
- Усі «портали управління» (Docker API, kubelet, довільні дашборди) якщо ви не можете сказати, хто і звідки їх потребує.
Короткий жарт №2: Якщо ви «тимчасово відкрили всі порти», ваш фаєрвол стає мотиваційним плакатом: виглядає захисно, але в основному надихає оптимізм.
Мінімальний профіль nftables (з обґрунтуванням)
Debian 13 з радістю запускає nftables як основний фаєрвол. Тримайте набір правил читабельним, коментуйте його і не піддавайтеся спокусі побудувати всередині фаєрвола другий стек мережі.
Вибори дизайну
- Єдина таблиця inet для симетрії IPv4+IPv6. Якщо писати тільки v4-правила, v6 стане боковим входом, який ви не мали наміру відкривати.
- Вхід за замовчуванням drop, forward за замовчуванням drop. Вихід за замовчуванням accept (спершу), з опцією звузити пізніше.
- Завжди дозволяти loopback. Розбиття трафіку localhost — чудовий спосіб вигадати нові режими відмов.
- Раннє дозволення established/related задля продуктивності й здорового глузду.
- Дозволяти основні ICMP/ICMPv6, а не «все» або «нічого».
- Логувати відмови з обмеженням швидкості. Вам потрібен сигнал, а не акваріум пакетів.
Приклад мінімального набору правил
cr0x@server:~$ sudo tee /etc/nftables.conf > /dev/null <<'EOF'
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
set admin_v4 {
type ipv4_addr
flags interval
elements = { 192.0.2.0/24, 198.51.100.10 }
}
chain input {
type filter hook input priority 0; policy drop;
# Always allow loopback
iif "lo" accept
# Allow established/related traffic
ct state established,related accept
# Drop invalid packets early
ct state invalid drop
# ICMP/ICMPv6: keep the network healthy
ip protocol icmp icmp type { echo-request, echo-reply, destination-unreachable, time-exceeded, parameter-problem } accept
ip6 nexthdr icmpv6 icmpv6 type { echo-request, echo-reply, destination-unreachable, time-exceeded, packet-too-big, parameter-problem } accept
# SSH from admin networks only
ip saddr @admin_v4 tcp dport 22 ct state new accept
# Example: HTTPS if this host is a web endpoint
tcp dport 443 ct state new accept
# Log & drop everything else (rate-limited)
limit rate 10/second burst 20 packets log prefix "nft-in-drop " level info
drop
}
chain forward {
type filter hook forward priority 0; policy drop;
}
chain output {
type filter hook output priority 0; policy accept;
# Optional: tighten later once you know your egress
# Example patterns shown in the tasks section.
}
}
EOF
Це навмисно просто. Воно не намагається вирішити DDoS. Воно не мікрокерує кожним вихідним з’єднанням. Воно зупиняє випадкове експонування, зменшує площу ураження «ой, я встановив демон» і робить вхідний доступ аудитованим.
Дві операційні нотатки, що рятують від болю:
- IPv6 — це реальність. Якщо хост має IPv6 (поширено), а фаєрвол покриває тільки IPv4, у вас з’являється ненавмисний обхід.
- Не забувайте про збереження правил. Набір правил, застосований вручну о 15:00 і зниклий після перезавантаження, — це не фаєрвол; це перформанс-арт.
Практичні завдання (команди, вивід, рішення)
Це речі, які ви реально робите на Debian 13, коли хочете мінімальний фаєрвол без зайвих заявок. Кожне завдання містить: команду, що означає вивід, та ухвалене рішення.
Завдання 1: Підтвердіть, який бекенд фаєрвола ви реально використовуєте
cr0x@server:~$ sudo update-alternatives --display iptables
iptables - auto mode
link best version is /usr/sbin/iptables-nft
link currently points to /usr/sbin/iptables-nft
link iptables is /usr/sbin/iptables
/usr/sbin/iptables-legacy - priority 10
/usr/sbin/iptables-nft - priority 20
Що це означає: Ви використовуєте бекенд nftables через сумісність iptables. Це нормально, але змішування сирих правил iptables і наборів nftables може швидко заплутати.
Рішення: Віддавайте перевагу нативним правилам nftables у /etc/nftables.conf. Якщо використовується legacy iptables, заплануйте вікно міграції.
Завдання 2: Перевірте, чи nftables активний і зберігається
cr0x@server:~$ systemctl status nftables
● nftables.service - nftables
Loaded: loaded (/lib/systemd/system/nftables.service; enabled)
Active: active (exited) since Mon 2025-12-30 09:11:10 UTC; 2min ago
Docs: man:nft(8)
Що це означає: Сервіс увімкнений і завантажив ваш набір правил. «active (exited)» — нормальний стан; він завантажує правила і виходить.
Рішення: Якщо він вимкнений/неактивний, увімкніть після валідації правил: sudo systemctl enable --now nftables.
Завдання 3: Перегляньте активний набір правил (довіряй, але перевіряй)
cr0x@server:~$ sudo nft list ruleset
table inet filter {
set admin_v4 {
type ipv4_addr
flags interval
elements = { 192.0.2.0/24, 198.51.100.10 }
}
chain input {
type filter hook input priority filter; policy drop;
iif "lo" accept
ct state established,related accept
ct state invalid drop
ip protocol icmp icmp type { echo-request, echo-reply, destination-unreachable, time-exceeded, parameter-problem } accept
ip6 nexthdr icmpv6 icmpv6 type { echo-request, echo-reply, destination-unreachable, time-exceeded, packet-too-big, parameter-problem } accept
ip saddr @admin_v4 tcp dport 22 ct state new accept
tcp dport 443 ct state new accept
limit rate 10/second burst 20 packets log prefix "nft-in-drop " level info
drop
}
}
Що це означає: Ядро має саме те, що ви вважаєте завантаженим. Це істина на місці виконання.
Рішення: Якщо ви бачите несподівані таблиці/цепочки, інші інструменти пишуть правила (агент хмари, платформа контейнерів, старі скрипти). Визначте, хто володіє фаєрволом.
Завдання 4: Перевірте синтаксис конфігу перед застосуванням (щоб не викинути себе з доступу)
cr0x@server:~$ sudo nft -c -f /etc/nftables.conf
Що це означає: Відсутність виводу — добре; синтаксис пройшов перевірку. Якщо є помилка, вона вкаже номер рядка і токен.
Рішення: Ніколи не застосовуйте неперевірені конфіги фаєрвола по SSH, якщо у вас немає консолі поза смугою або сітки безпечного відкату.
Завдання 5: Знайдіть сервіси, що слухають (перестаньте гадати)
cr0x@server:~$ sudo ss -lntu
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
tcp LISTEN 0 4096 0.0.0.0:22 0.0.0.0:*
tcp LISTEN 0 4096 0.0.0.0:443 0.0.0.0:*
udp UNCONN 0 0 127.0.0.53:53 0.0.0.0:*
Що це означає: Лише SSH і HTTPS слухають на всіх інтерфейсах; DNS прив’язаний тільки до локального stub-резолвера. Хороша відправна точка.
Рішення: Якщо ви бачите несподіваний порт на 0.0.0.0 або :::, або закрийте його на рівні сервісу (переважно), або тимчасово зафайрволіть його, доки не розберетеся.
Завдання 6: Визначте процес-власник порту
cr0x@server:~$ sudo ss -lntup 'sport = :443'
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
tcp LISTEN 0 4096 0.0.0.0:443 0.0.0.0:* users:(("nginx",pid=1187,fd=6))
Що це означає: nginx — слухач. Це корисно, коли вирішуєте, чи відкривати 443 взагалі.
Рішення: Якщо це не потрібний інтернет-фейсинг сервіс, змініть адресу прив’язки сервісу або відключіть його.
Завдання 7: Перевірте маршрут за замовчуванням і інтерфейси (правила залежать від інтерфейсу)
cr0x@server:~$ ip -br addr
lo UNKNOWN 127.0.0.1/8 ::1/128
ens3 UP 203.0.113.20/24 2001:db8:1234::20/64
cr0x@server:~$ ip route
default via 203.0.113.1 dev ens3
203.0.113.0/24 dev ens3 proto kernel scope link src 203.0.113.20
Що це означає: ens3 — реальний інтерфейс; у вас є IPv6 також. Якщо фаєрвол не покриває inet, ви пропускаєте половину історії.
Рішення: Використовуйте table inet, якщо немає свідомої причини інакше.
Завдання 8: Підтвердіть, що ви випадково не форвардите пакети
cr0x@server:~$ sysctl net.ipv4.ip_forward net.ipv6.conf.all.forwarding
net.ipv4.ip_forward = 0
net.ipv6.conf.all.forwarding = 0
Що це означає: Цей хост не виступає маршрутизатором. Для типової серверної ролі — добре.
Рішення: Якщо форвардинг увімкнено ненавмисно, вимкніть його або переконайтеся, що політика forward чітка й безпечна.
Завдання 9: Слідкуйте в реальному часі за відмовами (доведіть, що фаєрвол — причина)
cr0x@server:~$ sudo journalctl -f -k | grep -E 'nft-in-drop|IN='
Dec 30 09:15:22 server kernel: nft-in-drop IN=ens3 OUT= MAC=... SRC=198.51.100.77 DST=203.0.113.20 LEN=60 TOS=0x00 PREC=0x00 TTL=49 ID=4242 DF PROTO=TCP SPT=51234 DPT=22 WINDOW=64240 SYN
Що це означає: Хтось з 198.51.100.77 намагався SYN до SSH і був відкинутий вашими правилами (не входить у множину admin_v4).
Рішення: Якщо це легітимний адміністратор, додайте IP/діапазон в allowlist. Якщо ні — ігноруйте та тримайте логи з обмеженням швидкості.
Завдання 10: Перевірте, що conntrack працює (стейтфул-правила залежать від цього)
cr0x@server:~$ sudo conntrack -S
cpu=0 found=412 new=37 invalid=2 ignore=0 delete=12 delete_list=12 insert=37 insert_failed=0 drop=0 early_drop=0 error=0 search_restart=0
Що це означає: conntrack функціонує; низький рівень drop/early_drop — ознака здоров’я.
Рішення: Якщо ви бачите багато drop або early_drop, можливо, ви вичерпуєте розмір таблиці conntrack під навантаженням або атакою. Розгляньте налаштування і зменшення експозиції.
Завдання 11: Перевірте місткість таблиці conntrack (класичний винуватець «випадкових збоїв»)
cr0x@server:~$ sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_max
net.netfilter.nf_conntrack_count = 1832
net.netfilter.nf_conntrack_max = 262144
Що це означає: Ви далеко від ліміту. Якщо count наближається до max, відстеження стану стає вузьким місцем.
Рішення: Якщо це відбувається при нормальному навантаженні, підвищте nf_conntrack_max і переконайтеся в наявності пам’яті; також зменшіть непотрібну вхідну експозицію.
Завдання 12: Підтвердіть, що DNS працює з вашою політикою фаєрвола
cr0x@server:~$ resolvectl status
Global
Protocols: -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
resolv.conf mode: stub
Current DNS Server: 192.0.2.53
DNS Servers: 192.0.2.53 192.0.2.54
cr0x@server:~$ dig +short debian.org
151.101.2.132
Що це означає: Системний резолвер використовує ваш внутрішній DNS, і запити успішні.
Рішення: Якщо DNS ламається після звуження виходу, дозвольте UDP/TCP 53 до перелічених резолверів (не «всюди»).
Завдання 13: Перевірте доступність з хоста самого себе (саніті чек)
cr0x@server:~$ nc -vz 127.0.0.1 22
Connection to 127.0.0.1 22 port [tcp/ssh] succeeded!
cr0x@server:~$ nc -vz 127.0.0.1 443
Connection to 127.0.0.1 443 port [tcp/https] succeeded!
Що це означає: Сервіси підняті локально. Якщо віддалений доступ не працює, це питання фаєрвола/маршруту/security group, а не демона.
Рішення: Не «фіксуйте» nginx, коли проблема — відкинутий SYN.
Завдання 14: Застосуйте правила безпечно з автоматичним відкатом
cr0x@server:~$ sudo bash -c 'nft -c -f /etc/nftables.conf && (sleep 20; nft flush ruleset) & nft -f /etc/nftables.conf'
Що це означає: Це застосовує правила, але планує очищення через 20 секунд. Якщо ви втратите доступ, правила самознищаться.
Рішення: Використовуйте такий захист при редагуванні по SSH. Якщо все працює, скасуйте відкат, вбивши фоновий процес sleep (або повторно застосуйте і перезапустіть nftables).
Завдання 15: Зробіть правила стійкими і керованими systemd
cr0x@server:~$ sudo systemctl enable --now nftables
Synchronizing state of nftables.service with SysV service script with /lib/systemd/systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install enable nftables
Що це означає: Правила завантажуватимуться при старті; ви перейшли від «ручного стану в пам’яті» до керованого сервісу.
Рішення: Відтепер зміни мають іти через управління конфігом або принаймні версіонування.
Завдання 16: Звузьте вихідні правила без порушення оновлень (приклад)
Саме тут команди лякаються і нічого не роблять. Ось робочий патерн: тримайте політику output accept, але явно дозвольте «відомі необхідні» порти і протоколы та логируйте решту деякий час перед тим, як переключитися на drop.
cr0x@server:~$ sudo nft add rule inet filter output ct state established,related accept
cr0x@server:~$ sudo nft add rule inet filter output oif "lo" accept
cr0x@server:~$ sudo nft add rule inet filter output udp dport 53 ip daddr { 192.0.2.53, 192.0.2.54 } accept
cr0x@server:~$ sudo nft add rule inet filter output tcp dport 53 ip daddr { 192.0.2.53, 192.0.2.54 } accept
cr0x@server:~$ sudo nft add rule inet filter output udp dport 123 ip daddr 192.0.2.123 accept
cr0x@server:~$ sudo nft add rule inet filter output tcp dport { 80, 443 } accept
Що це означає: Ви перелічили DNS, NTP і веб-ехід. Якщо пізніше зміните політику output на drop, ці правила збережуть функціональність боксу.
Рішення: Зберіть, що ще потрібно (бекапи метрик, кінцеві точки сховищ, поштові реле), перед тим як встановлювати drop.
Три корпоративні міні-історії з бойових дій
Інцидент: хибне припущення (IPv6 «не використовується»)
Команда мала флот Debian-за-загрузчиком за хмарним лоад-балансером. Вони написали чистий IPv4-фаєрвол: відкидання за замовчуванням, дозвіл 443, SSH з VPN-діапазону. Вони похвалилися і почали інші справи. Вікно змін закінчилося раніше — що завжди підозріло.
Через тиждень безпека знайшла вхідний трафік, який не мав бути можливим. Причина не в «зламаному» фаєрволі, а в його неактуальності. Хости мали глобальні IPv6-адреси, присвоєні автоматично, а правила покривали лише IPv4. Сервіс був доступний через IPv6 напряму, оминаючи шляхи лоад-балансера і бажану політику периметра.
Це не була екзотична атака — інтернет сканує все, що має маршрутизовану адресу. Логи були мізерні, бо логували лише IPv4-відмови. Команда додатку ганялася за привидами в конфігурі лоад-баланcера.
Виправлення не було героїчним. Вони перемістили набір правил у таблицю inet, явно дозволили те, що мали на увазі для v6, і відкинули решту. Потім пройшли аудит адресації: або залишити IPv6 і контролювати його, або навмисно відключити. Реальний урок — не «IPv6 страшний», а «якщо ви не написали правило для нього, ви ним не керуєте».
Оптимізація, що дала відкат (drop ICMP щоб «зменшити шум»)
Команда платформи втомилася від «штормів ping» і випадкових логів ICMP. Хтось запропонував політику: дропати весь ICMP вхідний і вихідний. Менше шуму, менше аргументів про атаку, більше часу на важливі речі — наприклад, зустрічі.
Декілька днів було тихо. Потім клієнти почали повідомляти про переривчасті таймаути при великих відповідях. Не всі клієнти. Не на всіх шляхах. Достатньо, щоб зробити це циклічною головним болем. Спочатку звинувачували додаток. Потім мережу. Потім підвищували таймаути — класичний спосіб приховати проблему до незручного моменту.
Справжня проблема: PMTUD. Деякі маршрути вимагали обробки фрагментації, і сервери мали отримувати ICMP «packet too big» (IPv6) або відповідні повідомлення (IPv4). При блокуванні ICMP з’єднання зависали, великі пакети зникали в чорну діру. Маленькі запити працювали, великі повільно помирали.
Вони відкотили blanket-drop ICMP і замінили його на «дозволяти необхідні типи, обмежувати echo». Інцидент не обійшовся дорого у вигляді обладнання чи підтримки; він обійшовся увагою. Повернення назад показало, що помилкова оптимізація була не проблемою сама по собі, а оптимізацією не того.
Нудна, але правильна практика, що врятувала день (поетапне розгортання + логовані відмови)
Компанія зі стільки-стільки sane SRE захотіла хост-фаєрволи на сотнях Debian-серверів. Ключова фраза — «помірно адекватні»: вони не намагалися зробити ідеально одразу. Вони написали мінімальну inbound default-drop політику, потім розгорнули її в режимі спостереження з логами відмов з обмеженням швидкості і дашбордом, що підсумовував топ заблокованих портів по підмережах.
Перший тиждень був нудним. Боти били SSH, фаєрвол їх відкидав. Декілька легітимних адміністраторських IP відсутні у allowlist; їх додали. Декілька застарілих перевірок моніторингу все ще опитували зі старих мереж; їх мігрували. Ніхто не панікував, бо розгортання було поетапним і був спосіб бачити, що відкидається.
Другий тиждень — де нудна робота окупилася. Під час окремого інциденту — неправильна конфігурація додатка, що спричинила потік вихідних з’єднань — команда швидко довела, що це не вхідна подія. Логи фаєрволу були спокійні. Таблиця conntrack стабільна. Мережа в порядку. Вони сфокусувалися на правильному шарі й вирішили проблему швидше.
Операційний чит-код: мінімальний фаєрвол плюс спостережуваність, розгорнутий поступово, нудніший за грандіозний редизайн — але працює.
Швидкий план діагностики
Коли «мережа зламана», потрібна послідовність, що звузить область пошуку за кілька хвилин, а не годинний тур по чужих думках.
Перше: чи сервіс насправді слухає?
- Запустіть
ss -lntu. Якщо нічого не слухає на очікуваному порті, перестаньте звинувачувати фаєрвол. - Запустіть
systemctl status <service>. Якщо сервіс впав або прив’язаний до localhost, фаєрвол — лишній глядач.
Друге: чи трафік доходить до хоста?
- Перевірте інтерфейс/адресу:
ip -br addr. - З віддаленого клієнта спробуйте TCP-з’єднання. На сервері дивіться
tcpdumpна інтерфейсі. Якщо SYN-и ніколи не приходять, справа в маршрутизації/security group/NACL вище по ланцюжку.
Третє: фаєрвол відкидає?
- Перевірте логи:
journalctl -k | grep nft-in-drop. - Перегляньте правила й лічильники:
nft list ruleset. Якщо ви не рахуєте, почніть; лічильники перетворюють гадання на математику.
Четверте: якщо «переривчасто», перевірте conntrack і ICMP
conntrack -Sіsysctl net.netfilter.nf_conntrack_countна предмет симптомів ємності.- Перевірте політику ICMP. Якщо ви блокували packet-too-big/time-exceeded, ви отримали дивний аутідж.
П’яте: перевірте вихідні залежності
- DNS:
resolvectl statusіdig. - Час:
timedatectlі статус вашого NTP-клієнта. - Оновлення/проксі: запустіть
apt-get updateі слідкуйте за відмовами.
Типові помилки: симптом → корінь → виправлення
1) «Раніше SSH працював, тепер я відрізаний»
Симптом: SSH таймаут після застосування правил.
Корінь: Ви дозволили SSH лише з підмережі, що не включає вашу поточну IP, або забули про шляхи доступу через IPv6.
Виправлення: Використовуйте консоль поза смугою або rollback guard. Додайте ваш адмін-діапазон до множини nft; застосуйте через nft -c спочатку. Переконайтеся, що правила в table inet, щоб покрити v6.
2) «HTTPS відкритий, але клієнти зависають на великих відповідях»
Симптом: Малі запити проходять; великі завантаження зависають або скидаються непередбачувано.
Корінь: Блоковано ICMP «packet too big» або пов’язані PMTUD-повідомлення; проблеми з фрагментацією призводять до чорних ділянок.
Виправлення: Дозвольте суттєві типи ICMP/ICMPv6, а не тільки echo. Тримайте логи з обмеженням швидкості.
3) «Моніторинг раптово нічого не бачить»
Симптом: Метрики не збираються або агенти не можуть підключитися після змін фаєрвола.
Корінь: IP-адреси джерел моніторингу змінилися (нові колектори, NAT, нова підмережа) і не були в allowlist.
Виправлення: Використовуйте nft множини для підмереж моніторингу і тримайте їх в актуальному стані. Краще дозволяти з мереж моніторингу, а не «будь-де».
4) «DNS випадково ламається тільки на цьому хості»
Симптом: Деякі резолви таймаутяться; інші виконуються.
Корінь: Вихідний UDP/53 дозволено, але TCP/53 заблоковано; великі DNS-відповіді або DNSSEC потребують TCP.
Виправлення: Дозвольте і UDP, і TCP 53 до ваших резолверів.
5) «З’єднання хиткі при навантаженні»
Симптом: Випадкові збої з’єднань при піках.
Корінь: Вичерпання таблиці conntrack або сильний її оборот; стейтфул-фаєрвол не в змозі виділити записи.
Виправлення: Налаштуйте nf_conntrack_max і розгляньте зменшення експозиції або використання SYN-proxy вище по ланцюжку. Також перевірте, чи ви не відстежуєте трафік там, де це не потрібно (наприклад, для локальних інтерфейсів).
6) «Я дозволив порт 443, але він все одно заблокований»
Симптом: Віддалені клієнти не підключаються; локальні підключення працюють.
Корінь: Верхній security group/NACL блокує його, або сервіс прив’язаний до localhost, або ви тестуєте IPv6, а дозволили лише IPv4.
Виправлення: Перевірте bind-адресу через ss -lntup. Підтвердіть, що пакети доходять через tcpdump. Використовуйте inet-правила для паритету v4/v6.
7) «Docker/Kubernetes змінює мій фаєрвол поза моїми спиною»
Симптом: Набір правил відрізняється від того, що в /etc/nftables.conf; з’являються несподівані accept-правила.
Корінь: Платформи контейнерів маніпулюють netfilter для реалізації NAT/форвардингу.
Виправлення: Визначте власника: або платформа керує певними таблицями/цепочками, або ізолюйте політику хоста в окремій таблиці і уникайте перезаписування платформних ланцюгів. Перевіряйте після розгортання.
Чеклісти / покроковий план
Чекліст A: Мінімальна вхідна політика для типового сервера
- Перелік слухачів: запустіть
ss -lntu. Якщо сервіс не повинен бути публічним — виправте сервіс спочатку (прив’яжіть до localhost або відключіть). - Визначте шлях доступу адміністрування: VPN/бастіон/підмережа. Запишіть це. Серйозно.
- Напишіть правила nftables в inet-таблиці: accept для loopback, accept для established/related, drop для invalid.
- Дозвольте SSH лише з адмін-мереж: використовуйте множину, щоб зміни не вимагали переписування правил.
- Дозвольте тільки необхідні сервіси: 443 для вебу, можливо нічого іншого.
- Дозвольте суттєві типи ICMP/ICMPv6: зберігайте роботу PMTUD.
- Обмежте логи відмов: інакше ви врешті решт відключите логування зовсім.
- Перевірте синтаксис:
nft -c -f /etc/nftables.conf. - Застосуйте з rollback guard, якщо віддалено: заплануйте очищення правил, якщо ви хвилюєтесь.
- Увімкніть сервіс nftables: щоб правила зберігалися при реґбуті.
Чекліст B: Розумна вихідна позиція без самосаботажу
- Спочатку виміряйте: логируйте несподіваний вихід деякий час перед жорстким блоком.
- Дозвольте DNS до резолверів: UDP/TCP 53 до відомих серверів.
- Дозвольте NTP до джерел часу: не допускайте дрейфу часу, що створює проблеми з автентифікацією.
- Дозвольте оновлення: TCP 80/443 до проксі або дзеркала; інакше ви «тимчасово відкриєте» назавжди.
- Дозвольте бізнес-залежності: бази даних, черги повідомлень, кінцеві точки сховищ.
- Тільки потім розгляньте output policy drop: і тримайте аварійний шлях «break-glass».
Чекліст C: Зміни, що не створять інцидент
- Поетапне розгортання: зробіть канарку на невеликому відсотку хостів.
- Майте шлях поза основним каналом: консольний доступ, серійний порт або принаймні таймер відкату.
- Використовуйте лічильники й логи: інакше ви відлагоджуєте на ореолі.
- Зафіксуйте намір: коментар до правила дешевший за постмортем.
- Тестуйте з потрібних місць: мережа адмінів, мережа моніторингу і ворожа зовнішня мережа (або принаймні інша підмережа).
Питання та відповіді
1) Використовувати nftables безпосередньо чи обгортку?
Якщо ви керуєте продакшн-серверами і хочете передбачуваної поведінки — використовуйте nftables безпосередньо. Обгортки можуть бути корисні, але часто ховають реальні правила і ускладнюють налагодження.
2) Чи вистачає «default deny inbound» як захисту?
Це сильна базова лінія, але не повна програма безпеки. Вона зменшує випадкове експонування і обмежує опортуністичні сканування. Вам усе ще потрібні патчі, надійна автентифікація, принцип найменших привілеїв і моніторинг.
3) Чи потрібно блокувати вихідний трафік?
Не завжди з першого дня. Але ви принаймні маєте розуміти вихідні залежності і логувати незвичний еґрес. Жорсткі вихідні правила найкорисніші, коли роль хоста стабільна.
4) Чому дозволяти будь-який ICMP взагалі?
Бо мережі використовують ICMP для коректності, не лише для «ping». PMTUD і сигнали помилок на ньому базуються. Якщо хочете блокувати echo — можна, але не ламайте packet-too-big і подібні повідомлення.
5) Який мінімальний вхідний набір для headless-сервера?
Loopback, established/related, суттєві ICMP/ICMPv6 і SSH з адмін-мережі. Це все, якщо хост не обслуговує трафік.
6) Як не відрізати себе від SSH?
Перевіряйте через nft -c, застосовуйте з таймером відкату і майте консоль поза основним каналом. Також не тестуйте нові правила вперше на єдиному хості, який не можна перезапустити.
7) IPv6 — вимкнути чи фаєрволити?
Фаєрволити. Вимкнення IPv6 може бути прийнятним в деяких середовищах, але це часто гра в whack-a-mole з інструментами й дефолтами. Якщо хост має IPv6, політика має його охоплювати.
8) Чи відкривати порти баз даних у світ, якщо пароль сильний?
Ні. Надійна автентифікація — добре; зайве експонування усе ще погано. Обмежуйте порти БД підмережами застосунків або приватними мережами. Якщо потрібен віддалений доступ — використовуйте бастіон або VPN.
9) Чи можна покладатися на cloud security groups замість хост-фаєрволів?
Можна, поки не настане момент, коли не можна. Хост-фаєрволи — локальна подушка безпеки й запобіжник від випадкових слухачів. Використовуйте обидва, коли можливо; принаймні використовуйте хост-фаєрволи на цінних системах.
10) Скільки логування включати для відкинутих пакетів?
Достатньо, щоб діагностувати: лімітуйте швидкість логів для нових вхідних відмов і, за бажанням, для несподіваного вихідного трафіку. Якщо логи перетворяться на щоденник ботнету, ви перестанете їх читати — і тоді вони марні.
Наступні кроки, що не зіпсують вихідні
Зробіть три речі в цьому порядку, і ви отримаєте 80% вартості з 20% ризику:
- Перелічіть слухачів за допомогою
ss -lntuі зупиніть все, що не повинно бути доступним. Це зменшить обсяг політики фаєрвола, яку вам потрібно підтримувати. - Розгорніть мінімальний inbound nftables-профіль (inet-таблиця, loopback, established/related, суттєві ICMP/ICMPv6, SSH з адмін-підмережі, порти сервісів, які ви дійсно обслуговуєте). Увімкніть збереження через systemd.
- Додайте спостережуваність: логування відмов з обмеженням швидкості і звичку швидкої діагностики (стан слухача → надходження пакетів → рішення фаєрвола → conntrack/ICMP).
Коли це буде стабільно, вирішіть, наскільки звужувати вихідні правила. Почніть з логування і allowlist для DNS/NTP/оновлень. Звужуйте лише після спостереження реального трафіку. «Без параної» не означає «без політики». Це означає, що ваша політика ґрунтується на тому, що роблять ваші сервери, а не на тому, чого боїться ваша тривога.