Ви розгортаєте чистий хост Debian 13, відкриваєте єдиний потрібний порт і все працює. Потім через тиждень
відбувається невинне оновлення пакета, оновлюється runtime контейнерів або хтось «просто додає швидке правило iptables»,
і раптом ваш брандмауер перетворюється на будинок з привидами. Трафік падає. Логи брешуть. Частина команд нічого не показує.
Це не надприродне явище. Це два інтерфейси управління (iptables та nftables), що намагаються контролювати ті самі
Netfilter-хуки, часто через сумісні шари, іноді безпосередньо, і іноді третя сторона
(Docker, Kubernetes, UFW, firewalld) випадково обирає одну зі сторін. Результат — тиха війна брандмауерів: правила існують,
але не там, де ви їх шукаєте.
Справжня проблема: одне ядро, кілька площин керування
На Debian 13 у вас може бути:
- nftables що керує правилами безпосередньо через
nftіnftables.service - iptables команди, які можуть бути:
- iptables-nft: CLI iptables, що транслює в правила nft
- iptables-legacy: старий бекенд iptables, що використовує застарілі інтерфейси ядра
- Один або кілька вищих менеджерів: UFW, firewalld, Docker, Kubernetes, Podman, fail2ban
Жоден із цих компонентів сам по собі не є «поганим». Режим відмови виникає, коли ви вважаєте, що вони всі дивляться в ту саму
базу правил. Вони цього не роблять. Іноді вони працюють з різними бекендами. Іноді вони пишуть у той самий
бекенд, але в різні таблиці/ланцюги з пріоритетами, які ви не передбачали. Іноді ваші правила в порядку, але пакети ніколи до них не доходять через offload, bridging, policy routing або зміни в conntrack.
Операційний гріх — дозволяти двом авторитетам керувати політикою брандмауера. Брандмауери схожі на контроль змін: у вас повинен бути лише
один керівник.
Факти та історія: чому ми опинилися тут
Кілька конкретних фактів допоможуть швидше розібратися, бо вони пояснюють, чому «iptables каже X» може бути беззмістовним на сучасній системі.
- Netfilter — це фреймворк у ядрі; iptables і nftables — фронтенди в просторах користувача. Боротьба переважно в просторах користувача, але біль відчутний у шляху пакета.
- nftables було введено як заміна iptables. Воно уніфікує IPv4/IPv6, краще підтримує множини/мапи і зменшує розростання правил в типових випадках.
- iptables-nft існує, щоб старі інструменти продовжили працювати. Воно перетворює команди iptables у правила nftables, але відображення не завжди «прозоре» при інспекції.
- iptables-legacy все ще можна інсталювати та вибрати. На хості, де деякі компоненти використовують legacy, а інші — nft, ви гарантовано отримаєте плутанину і час від часу розбіжності правил.
- Історично UFW говорив iptables. Декотрі середовища підключають UFW до системи з nftables і вважають, що він стає «брандмауером». Іноді так буває. Іноді це другий брандмауер.
- firewalld рухався в бік бекендів nftables. Але не всі версії і не всі дистрибутиви за замовчуванням роблять це однаково, і «який бекенд активний» має значення.
- Docker популяризував автоматичне програмування iptables. Він встановлює правила NAT і filter, якщо його не проінструктувати інакше, і очікує семантики iptables навіть коли бекенд — nft.
- Kubernetes традиційно передбачає доступність iptables. Багато CNI-плагінів усе ще маніпулюють правилами iptables; на nft-бекенді це зазвичай працює — допоки не перестає.
- У nftables пріоритет ланцюга — першокласна концепція. В iptables порядок вбудованих ланцюгів переважно фіксований. У nftables ви можете створювати кілька ланцюгів на той самий хук з різними пріоритетами; це потужний інструмент і чудовий спосіб нашкодити собі.
Ось одна надійна цитата, яку варто тримати на стікері:
«Надія — це не стратегія.» — Gene Kranz
Швидкий план діагностики (робіть це спочатку)
Коли пакети втрачаються або порти виглядають «відкритими, але недоступними», не починайте з редагування правил. Почніть з доведення, кому належить брандмауер і де пакети оцінюються.
1) Визначте активну площину керування брандмауером
- Перевірте, чи завантажено nftables і чи служба ввімкнена/активна.
- Перевірте, який бекенд iptables використовується на системі (nft чи legacy).
- Перевірте, чи встановлені Docker/Kubernetes/UFW/firewalld і чи вони маніпулюють правилами.
2) Інспектуйте ефективні правила ядра, а не свої припущення
- Здампіть повний ruleset через
nft list ruleset. - Перелічіть правила iptables з лічильниками.
- Шукайте дублікатні або конфліктні ланцюги (особливо NAT) і несподівані політики за замовчуванням.
3) Перевірте потік пакетів за допомогою лічильників і цільових захоплень
- Використовуйте лічильники (iptables
-v, nftcounter). - Використовуйте
conntrackдля перевірки стану. - Використовуйте
tcpdumpна вході та виході, щоб побачити, де воно помирає.
4) Лише потім змінюйте політику
- Оберіть одного менеджера і один бекенд.
- Видаліть або вимкніть інших менеджерів.
- Зробіть політику постійною та протестуйте поведінку після перезавантаження.
Жарт №1: Брандмауери схожі на офісні кухні — всі думають, що вони «просто допомагають», а потім нічого не працює і смердить дивно.
Практична модель: iptables, nftables і ядро
У ядрі є шлях пакета з чітко визначеними хуками: ingress, prerouting, input, forward, output, postrouting (спрощуючи трішки).
Netfilter забезпечує ці хуки. Програми в просторі користувача створюють правила, що прив’язуються до цих хуків. Важлива частина: може існувати кілька
наборів правил і кілька ланцюгів, прив’язаних до того самого хука, і вони виконуються в порядку.
iptables (класичний) організований у таблиці (filter, nat, mangle, raw, security) і вбудовані ланцюги (INPUT, OUTPUT, FORWARD, PREROUTING, POSTROUTING).
nftables більш гнучкий: ви створюєте таблиці і ланцюги, потім вказуєте, до якого хука і з яким пріоритетом кожен ланцюг належить.
На Debian 13 найпоширеніша операційна реальність така:
- Ваша команда
iptablesфактично за замовчуванням — iptables-nft, яка пише в nftables через сумісний шар. nft list ruleset— це істина для правил, що діють (якщо ви на nft бекенді).- Якщо ви випадково переключитесь на iptables-legacy для якогось компоненту, ви можете отримати «два різні брандмауери», обидва які звітують успіх.
Шаблон конфлікту виглядає так:
- Ви запускаєте
iptables -Sі бачите «нічого страшного». - Ви запускаєте
nft list rulesetі бачите політику завдовжки, яку ви не писали. - Або гірше:
iptablesпоказує одну політику,nftпоказує іншу, і пакети підкоряються тому бекенду, який фактично приєднано.
Практичні завдання: команди, виводи, рішення
Наведені нижче завдання призначені для виконання на хостах Debian 13 під час інциденту, під час міграції або для перевірки.
Кожне містить: команду, що типовий вивід означає, і яке рішення прийняти далі. Не ігноруйте рішення; саме там зупиняються відмови.
Task 1 — Identify which iptables backend is selected
cr0x@server:~$ 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
slave iptables-restore is /usr/sbin/iptables-restore
slave iptables-save is /usr/sbin/iptables-save
/usr/sbin/iptables-legacy - priority 10
/usr/sbin/iptables-nft - priority 20
Що це означає: Якщо «currently points to iptables-nft», то команди iptables транслюються в nftables.
Якщо вказано iptables-legacy — ви використовуєте legacy бекенд.
Рішення: Оберіть один бекенд для всього хоста. Для Debian 13 віддавайте перевагу iptables-nft, якщо немає жорсткої вимоги до legacy.
Змішані бекенди — шлях до фантомних правил.
Task 2 — Check if nftables service is enabled and active
cr0x@server:~$ systemctl status nftables --no-pager
● nftables.service - nftables
Loaded: loaded (/lib/systemd/system/nftables.service; enabled; preset: enabled)
Active: active (exited) since Sun 2025-12-28 10:41:12 UTC; 2h 3min ago
Docs: man:nft(8)
man:nftables(8)
Process: 512 ExecStart=/usr/sbin/nft -f /etc/nftables.conf (code=exited, status=0/SUCCESS)
Main PID: 512 (code=exited, status=0/SUCCESS)
Що це означає: «active (exited)» — нормально; служба завантажує правила й виходить. «enabled» означає, що правила будуть завантажені при завантаженні з /etc/nftables.conf.
Рішення: Якщо nftables увімкнено, розглядайте /etc/nftables.conf як бажане джерело правди — або вимкніть nftables, якщо ви делегуєте іншому менеджеру.
Task 3 — Dump the effective nft ruleset (ground truth)
cr0x@server:~$ nft list ruleset
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
ct state established,related accept
iif "lo" accept
tcp dport { 22, 443 } accept
counter packets 10234 bytes 811234 drop
}
}
Що це означає: Ефективна політика для inet filter input — drop, з явними правилами accept.
Лічильник на правилi drop показує, що пакети потрапляють під нього.
Рішення: Якщо очікуваний порт відсутній тут, перестаньте редагувати iptables-скрипти і виправте nft ruleset або менеджера, який його генерує.
Task 4 — See iptables rules with counters (even on nft backend)
cr0x@server:~$ iptables -L -n -v
Chain INPUT (policy DROP 120 packets, 9600 bytes)
pkts bytes target prot opt in out source destination
8123 640K ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0
553 42012 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22
102 6120 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:443
Що це означає: Якщо цей вивід збігається з nft ruleset, ймовірно ви на iptables-nft і бачите трансляцію.
Якщо він не збігається з виводом nft — можлива ситуація «split-brain» бекендів.
Рішення: Якщо nft і iptables не погоджуються, негайно перевірте update-alternatives для iptables і ip6tables, і проаудіть, хто встановлює правила.
Task 5 — Verify ip6tables backend too (IPv6 is where surprises hide)
cr0x@server:~$ update-alternatives --display ip6tables
ip6tables - auto mode
link best version is /usr/sbin/ip6tables-nft
link currently points to /usr/sbin/ip6tables-nft
link ip6tables is /usr/sbin/ip6tables
slave ip6tables-restore is /usr/sbin/ip6tables-restore
slave ip6tables-save is /usr/sbin/ip6tables-save
/usr/sbin/ip6tables-legacy - priority 10
/usr/sbin/ip6tables-nft - priority 20
Що це означає: Ви можете бути на nft для IPv4 і на legacy для IPv6, якщо хтось «полагодив одну річ». Це не вирішення; це бомба уповільненої дії.
Рішення: Узгодьте бекенди IPv4 і IPv6. Якщо ви не готові до політики IPv6, явно блокувати або явно дозволяти — не «забути, що вона існує».
Task 6 — Find who is managing firewall rules (package-level)
cr0x@server:~$ dpkg -l | egrep 'nftables|iptables|ufw|firewalld|fail2ban|docker|kube|podman' | awk '{print $1,$2,$3}'
ii nftables 1.0.9-1
ii iptables 1.8.10-2
ii ufw 0.36.2-2
ii docker.io 26.0.2+dfsg1-1
ii fail2ban 1.1.0-1
Що це означає: На хості є кілька потенційних записувачів: UFW, Docker, fail2ban. Будь-хто з них може додавати або перезавантажувати правила.
Рішення: Оберіть одного «автора політики». Якщо ви хочете рідну nftables-політику, налаштуйте Docker/fail2ban відповідно або обмежте їх. Інакше прийміть, що вони володіють частинами ruleset.
Task 7 — Check if Docker is programming iptables
cr0x@server:~$ grep -R "iptables" /etc/docker/daemon.json 2>/dev/null || echo "no daemon.json iptables setting"
no daemon.json iptables setting
Що це означає: За замовчуванням Docker зазвичай маніпулює iptables. Чи прийнятно це — залежить від вашої моделі.
Рішення: Якщо вам потрібна сувора контроль nftables, розгляньте налаштування Docker "iptables": false і реалізуйте свої NAT/forward правила. Якщо не маєте часу робити це безпечно — не робіть напівзаходів.
Task 8 — Inspect nftables for Docker-created tables/chains
cr0x@server:~$ nft list tables
table inet filter
table ip nat
table ip filter
Що це означає: Побачити table ip nat і table ip filter — нормально, але якщо ви їх не створювали, їх створив хтось інший.
Docker часто інжектує NAT-правила.
Рішення: Якщо зовнішній інструмент інжектує правила, документуйте це і моніторте. Найгірший стан — «невідома автоматизація».
Task 9 — Check chain priorities and hooks (nftables ordering issues)
cr0x@server:~$ nft -a list chain inet filter input
table inet filter {
chain input { # handle 5
type filter hook input priority 0; policy drop;
ct state established,related accept # handle 12
iif "lo" accept # handle 13
tcp dport 22 accept # handle 14
counter packets 10234 bytes 811234 drop # handle 20
}
}
Що це означає: Hook input priority 0. Якщо у вас є кілька ланцюгів, приєднаних до того самого хука з різними пріоритетами,
потрібно знати порядок, бо drop у ланцюзі вищого пріоритету може завадити виконанню accept у пізніших ланцюгах.
Рішення: Якщо кілька ланцюгів ділять хук, консолідуйте або документуйте пріоритети. «Два ланцюги на input hook» не обов’язково неправильно; це автоматично викликає підозру.
Task 10 — Prove packets hit the expected rule (counters)
cr0x@server:~$ nft list ruleset | sed -n '1,120p'
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
ct state established,related accept
iif "lo" accept
tcp dport 443 counter packets 0 bytes 0 accept
counter packets 10234 bytes 811234 drop
}
}
Що це означає: Якщо лічильник для 443 accept залишається 0, поки клієнти не підключаються, або трафік не надходить,
або він надходить на інший інтерфейс/стек, або його відкидають раніше (priority, raw table, ingress), або проблема в маршрутизації.
Рішення: Перейдіть до захоплення пакетів і перевірки маршрутизації; перестаньте механічно редагувати правила дозволу.
Task 11 — Confirm routing and local listening (not a firewall problem)
cr0x@server:~$ ss -lntp | egrep ':(22|443)\s'
LISTEN 0 4096 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=710,fd=3))
LISTEN 0 4096 0.0.0.0:443 0.0.0.0:* users:(("nginx",pid=1188,fd=7))
Що це означає: Сервіси дійсно слухають. Якщо це порожнo, брандмауер ні до чого й ви полюєте на не ту тварину.
Рішення: Якщо сервіс не слухає — виправляйте сервіс. Якщо слухає — продовжуйте перевірки брандмауера/маршрутизації.
Task 12 — Packet capture: does traffic reach the interface?
cr0x@server:~$ sudo tcpdump -ni eth0 tcp port 443 -c 5
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
10:55:12.112233 IP 203.0.113.10.52344 > 192.0.2.20.443: Flags [S], seq 123456789, win 64240, options [mss 1460,sackOK,TS val 1 ecr 0,nop,wscale 7], length 0
10:55:13.112244 IP 203.0.113.10.52344 > 192.0.2.20.443: Flags [S], seq 123456789, win 64240, options [mss 1460,sackOK,TS val 2 ecr 0,nop,wscale 7], length 0
Що це означає: SYN-и надходять. Якщо ви не бачите SYN-ACKів вихідних, ймовірно локальний drop, локальна маршрутизація або сервіс не відповідає.
Рішення: Якщо SYN надходить, перевірте лічильники nft/iptables і політики ще раз, а також conntrack. Якщо SYN не надходить — ви в мережевій зоні (upstream ACLs, routing, security groups тощо).
Task 13 — Inspect conntrack state for a stuck flow
cr0x@server:~$ sudo conntrack -L | head
tcp 6 431999 ESTABLISHED src=203.0.113.10 dst=192.0.2.20 sport=52344 dport=443 src=192.0.2.20 dst=203.0.113.10 sport=443 dport=52344 [ASSURED] mark=0 use=1
Що це означає: Conntrack бачить встановлені потоки. Якщо ви бачите багато станів SYN_SENT або TIME_WAIT,
це натякає на дропи, асиметричну маршрутизацію або відмову застосунку.
Рішення: Якщо conntrack показує established, але застосунок все ще не працює — підозрюйте застосунок або проблеми з MTU. Якщо conntrack показує SYN-и без завершення — підозрюйте брандмауер або upstream.
Task 14 — Check for a policy drop you didn’t mean (nftables default policy)
cr0x@server:~$ nft list chain inet filter input | sed -n '1,60p'
chain input {
type filter hook input priority 0; policy drop;
ct state established,related accept
iif "lo" accept
tcp dport { 22 } accept
}
Що це означає: Політика за замовчуванням — drop і немає правила accept для вашого порту. Це не баг; це саме те, що ви попросили,
або те, що хтось зробив від вашого імені.
Рішення: Додайте порт у правильній площині керування. Якщо файл генерується менеджером, не редагуйте його вручну, якщо вам не подобаються сюрпризи після перезавантаження.
Task 15 — Detect legacy rules still present (split-brain suspicion)
cr0x@server:~$ iptables-legacy -L -n -v 2>/dev/null | head
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
Що це означає: Якщо legacy показує іншу політику, ніж ваша активна система на основі nft, ймовірно у вас є інструменти, що пишуть у різні бекенди,
або залишкова конфігурація, яка укусе під час відмови або перезавантаження.
Рішення: Якщо ви стандартизуєтесь на nft, приберіть або нейтралізуйте використання legacy (альтернативи і служби). Якщо мусите залишитись на legacy — припиніть використовувати nftables service.
Task 16 — Audit who last touched firewall state (logs)
cr0x@server:~$ journalctl -u nftables -u docker -u ufw --since "today" --no-pager | tail -n 30
Dec 28 09:01:10 server systemd[1]: Starting nftables...
Dec 28 09:01:10 server nft[512]: /etc/nftables.conf:23:3-45: Warning: deprecated syntax
Dec 28 09:01:10 server systemd[1]: Finished nftables.
Dec 28 09:14:22 server dockerd[844]: time="2025-12-28T09:14:22.012345678Z" level=info msg="Loading containers: done."
Dec 28 09:14:22 server dockerd[844]: time="2025-12-28T09:14:22.112345678Z" level=info msg="Default bridge (docker0) is assigned with an IP address 172.17.0.0/16."
Що це означає: nftables завантажився під час завантаження; Docker ініціалізувався пізніше і, ймовірно, також встановив правила. Порядок має значення.
Рішення: Якщо пост-бутова служба перезаписує або додає правила, вам потрібно або контролювати її поведінку, або вбудувати її очікування в вашу політику.
Три корпоративні міні-історії з «передової»
Міні-історія №1: Інцидент, спричинений хибним припущенням
Середня компанія стандартизувала Debian для крайових реверс-проксі. У них було просте правило: «Тепер все — nftables».
Хтось написав чисту nft-полтику, помістив її в /etc/nftables.conf і увімкнув службу. Це пройшло в staging.
Розгортання було настільки спокійним, що люди почали називати це «нудним» як комплімент.
Через кілька місяців нова команда додала внутрішній агент сканування, який «потребує iptables». Вони встановили пакет, що потягнув інструменти iptables
і допоміжний скрипт, що запускав iptables -I INPUT -p tcp --dport 9100 -j ACCEPT при завантаженні, бо так завжди робили на старих образах.
Ніхто не задумався. Вони побачили правило в iptables -L. Вони навіть задокументували це в runbook.
Потім стався перший реальний інцидент: деякі вузли випадково відмовляли в підключенні до порту експортера, але лише після перезавантажень.
Під час інцидент-колу люди повторювали: «Але iptables показує правило accept». Це було правдою — на тих вузлах iptables перехопився на legacy через зміну alternatives.
Тим часом nftables завантажив default-drop input chain і ніколи не бачив правило iptables.
Аутейдж не був драматичним. Ніяких помилок для клієнтів. Просто відсутні метрики, потім помилкові рішення автоскейлінгу, потім спрацьовування алармів.
Висновок був організаційний: команда платформи думала «iptables — це застарілий вигляд у nftables». Команда застосунків думала «iptables — це брандмауер».
Обидві були неправі, але по-різному.
Виправлення було жорстким: заборонили ad-hoc використання iptables на тих хостах, зафіксували alternatives на iptables-nft і написали єдиний документований спосіб відкривати порти:
зміна в репозиторії nftables, що проходить рев’ю як код. Також додали CI-перевірку, яка порівнює nft list ruleset з очікуваними ланцюгами/хуками та політиками за замовчуванням.
Міні-історія №2: Оптимізація, яка відкотилась назад
Інша компанія експлуатувала завантажену платформу контейнерів на Debian-хостах. Вони гналися за латентністю, і один інженер запропонував «спростити шари брандмауера»
шляхом вимкнення програмування iptables Docker і керування всім у nftables напряму. На папері це виглядало чисто: один файл політики, без сюрпризів,
без випадкових ланцюгів.
Вони вимкнули в Docker "iptables": false і поступово розкатували зміни. Це працювало в dev. Працювало в staging.
Потім у production почали надходити періодичні помилки «не можу дістатися зовнішнього сервісу» для деяких контейнерів, особливо після ротації вузлів.
Першою підозрою був DNS. Другою — маршрутизація. Третьою — вина застосунку.
Проблема була в NAT і форвардингу. Docker тихо виконував багато необхідного: правила MASQUERADE, правила FORWARD accept для встановлених зв’язків,
і деяку логіку ізоляції бриджу. Їхнє ручне nft-політику покривала щасливий шлях, але пропустила кілька крайових випадків, включно з трафіком з певних бриджів
і hairpin-з’єднаннями. Conntrack ускладнив ситуацію, роблячи відмови непостійними залежно від стану.
Вони відновили роботу, ввімкнувши Docker iptables в production, поки не побудували nft-політику з явними вимогами для мереж контейнерів.
Саме «оптимізація» не була неправильною; план розгортання був. Вони ставили мережеву політику як конфіг-тумблер, а не як системну зміну.
Урок: якщо ви вимикаєте автоматизацію брандмауера інструмента, ви переймаєте його обов’язки. Це не завжди погано, але ніколи не безкоштовно.
Міні-історія №3: Нудна, але правильна практика, що врятувала день
Регульована компанія експлуатувала bastion-хости на Debian для адміністративного доступу. Правила були строгі: default drop, явні allow-листи і вимога, що
кожна зміна брандмауера має бути відстежуваною. Їхня команда безпеки наполягла, щоб політика брандмауера була декларативною, зберігалася в репозиторії і розгорталася автоматизацією.
Усі скаржились. Потім звикли.
Одного дня після оновлення ядра стався шторм перезавантажень. Декілька команд повідомили «SSH недоступний» на підмножестві bastion-хостів.
Запахло проблемою брандмауера, бо все інше було здорове, і мережа гіпервізора була в порядку.
On-call підключився до консолі хоста, запустив nft list ruleset і порівняв з останньою відомо доброю політикою з репозиторію.
Вони співпали. Лічильники показували, що вхідні SYN-и приймалися. Брандмауер не був винуватцем.
Проблема виявилася в неправильно впорядкованій зміні імені мережевого інтерфейсу, що змінило, який інтерфейс потрапляв під allow-правила.
Оскільки політика була декларативною, версіонованою і ідентичною на всіх хостах, вони виключили «довільний дріфт» за кілька хвилин.
Це скоротило інцидент і завадило класичній помилці: люди додають аварійні allow-правила, які потім стають постійними й недокументованими дірками.
Нудна практика була не лише в репозиторії. Головне — дисципліна мати одне джерело правди і тестувати його при перезавантаженні.
Ось як не дати брандмауеру перетворитись на фольклор.
Припиніть війну: оберіть площину керування і мігруйте чисто
У вас є три життєздатні кінцеві стани на Debian 13. Два — розумні. Один — пастка.
- Розумний стан A: nftables-native політика, керована через
/etc/nftables.conf(або згенерований файл), зі службою nftables увімкненою. iptables використовується лише як перегляд/сумісність (iptables-nft), а не як автор. - Розумний стан B: один вищий менеджер (firewalld або UFW) — єдиний авторитет, і все інше налаштовано на співпрацю. Важливо знати, який бекенд він використовує.
- Пастка: «трошки nftables, трохи iptables-правил, Docker робить своє, fail2ban додає присипку». Це не defense-in-depth. Це операційне рулетка.
Що я рекомендую для більшості серверів Debian 13
Якщо це сервер, яким ви керуєте як інфраструктурою (VM, bare metal, edge, БД, load balancers), віддавайте перевагу nftables-native.
Це послідовно, придатно для інспекції та автоматизації. Далі вирішіть, що робити з контейнерними інструментами:
- Якщо хост запускає контейнери і вам потрібна швидка безпека: дозвольте Docker керувати своїми правилами (з iptables-nft), і вважайте Docker частиною системи брандмауера. Документуйте, що він володіє.
- Якщо хост критичний з погляду безпеки і вам потрібна сувора політика: вимикайте iptables Docker тільки якщо маєте час і експертизу безпечно відтворити потрібні NAT/forward правила.
Як «обрати один бекенд» без драми
Ваша мета: один бекенд (nft) і один автор (служба nftables або одиний менеджер).
Найпоширеніша причина конфлікту — не сама наявність nftables, а те, що щось все ще пише legacy-правила або перезавантажує їх у незручний час.
Switch iptables alternatives to nft (if you want nft backend)
cr0x@server:~$ sudo update-alternatives --set iptables /usr/sbin/iptables-nft
update-alternatives: using /usr/sbin/iptables-nft to provide /usr/sbin/iptables (iptables) in manual mode
cr0x@server:~$ sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-nft
update-alternatives: using /usr/sbin/ip6tables-nft to provide /usr/sbin/ip6tables (ip6tables) in manual mode
Рішення: Після цього будь-які інструменти, що працюють через iptables, мають записувати правила в nft через шар сумісності, а не legacy.
Якщо ви все ще бачите розбіжності — хтось явно викликає iptables-legacy.
Make nftables policy persistent (and explicit)
Мінімальний, явний nftables-файл кращий за «хитрий». Хитрі брандмауери — причина того, що люди мають «тимчасові» правила на два роки.
Debian зазвичай завантажує /etc/nftables.conf, якщо служба nftables увімкнено.
Також: якщо ви мігруєте з iptables-скриптів, не намагайтесь перекладати все 1:1 у перший день. Почніть з:
established/related accept, loopback accept, потрібні вхідні порти, і потрібна вихідна політика. Потім розширюйте.
Жарт №2
Змішувати iptables-legacy і nftables — як мати дві зарплатні системи: ви всіх заплатите, просто не завжди правильну суму.
Типові помилки: симптом → корінь проблеми → виправлення
1) «iptables -L виглядає нормально, але трафік заблоковано»
Симптоми: Порт здається дозволеним у виводі iptables; клієнти все одно таймаутять; лічильники nft показують дропи або правила, які ви не очікували.
Корінь проблеми: iptables використовує один бекенд (часто legacy), тоді як ефективні правила в nftables (або навпаки). Або ви дивитесь на неправильну родину (ip vs inet).
Виправлення: Перевірте alternatives для iptables/ip6tables. Дампіть nft list ruleset. Стандартизуйте на iptables-nft або лише на nft. Приберіть legacy і зупиніть дублювання менеджерів.
2) «nft list ruleset порожній після перезавантаження»
Симптоми: Правила існують одразу після застосування, але зникають після перезавантаження; служба nftables неактивна/відключена.
Корінь проблеми: Ви завантажили правила вручну (інтерактивно nft) і не зберегли їх, або nftables.service відключено, або менеджер перезаписує правила після бота.
Виправлення: Увімкніть nftables service і переконайтесь, що /etc/nftables.conf коректний. Проаудьте пост-бутові служби типу Docker/UFW/firewalld на предмет перезавантажень.
3) «Docker-мереження зламалося, коли ми «спростили» брандмауер»
Симптоми: Контейнери не можуть дістатися зовнішніх мереж або не приймають вхідні з’єднання; випадкова доступність залежно від життєвого циклу вузла.
Корінь проблеми: Вимкнули інтеграцію iptables у Docker, не відтворивши еквівалентні NAT/forward/bridge правила в nftables.
Виправлення: Увімкніть Docker iptables поки не отримаєте перевірену nftables-політику для контейнерної мережі. Перевірте поведінку FORWARD, NAT masquerade і conntrack.
4) «IPv4 працює, IPv6 повністю відкритий (або мертвий)»
Симптоми: Сканери безпеки показують несподіване IPv6-експонування; або IPv6-клієнти не працюють, тоді як IPv4 працює.
Корінь проблеми: Налаштували лише iptables (v4) і забули ip6tables/inet або бекенди відрізняються між IPv4 і IPv6.
Виправлення: Використовуйте nft table inet для уніфікованої політики. Узгодьте alternatives для обох iptables і ip6tables. Визначте явну політику щодо IPv6.
5) «Правила існують, але ніколи не збігаються»
Симптоми: Правила accept показують нуль лічильників; tcpdump бачить трафік; клієнти все одно не проходять.
Корінь проблеми: Несумісність пріоритетів/хуків; трафік обробляється в іншому хуці (ingress vs input), або відкидається раніше в ланцюзі з вищим пріоритетом, або ви фільтруєте по неправильному інтерфейсу (переіменування, bond, VLAN).
Виправлення: Перелічіть ланцюги з хуками і пріоритетами. Консолідуйте ланцюги для одного хука. Підтвердіть імена інтерфейсів і розгляньте фільтрацію за адресою/підмережею, коли це доречно.
6) «Усе ламається тільки після старту fail2ban»
Симптоми: Захист від брутфорсу SSH працює, але легітимні користувачі блокуються; правила брандмауера ростуть до величезних розмірів з часом.
Корінь проблеми: fail2ban пише в iptables, поки ви керуєте nftables (або навпаки); баннати зберігаються непослідовно; невідповідність бекендів дає застарілі або неефективні бан-правила.
Виправлення: Налаштуйте fail2ban на використання дій для nftables, коли nfttables — вибрана площина керування, або ізолюйте fail2ban в той самий бекенд послідовно.
Чеклісти / покроковий план
Чекліст A: Стандартизація Debian 13 хоста на nftables без сюрпризів
- Інвентаризуйте авторів: Docker, Kubernetes, UFW, firewalld, fail2ban.
- Оберіть власника: nftables native (рекомендовано) або один менеджер.
- Узгодьте alternatives iptables/ip6tables до nft:
update-alternatives --set iptables /usr/sbin/iptables-nftupdate-alternatives --set ip6tables /usr/sbin/ip6tables-nft
- Увімкніть nftables service і забезпечте, щоб конфігураційний файл був єдиним джерелом правди.
- Застосуйте політику у вікні обслуговування; тримайте доступ до консолі поза смугою готовим.
- Перевірте: вхідні порти, вихідну пов’язаність, потоки контейнерів (за потреби), ставлення до IPv6.
- Тест перезавантаження. Завжди. Якщо ви не тестуєте перезавантаження — ви граєтесь у вдачу.
- Моніторте лічильники і логи дропів (помірно) кілька днів.
Чекліст B: Якщо потрібно зберегти інструменти в стилі iptables
- Все одно стандартизуйтеся на iptables-nft, якщо немає доведеної перепони.
- Не запускайте nftables.service з окремим файлом політики, що конфліктує з правилами, якими керує iptables.
- Переконайтеся, що команди інспекції відповідають бекенду:
- iptables-nft бекенд: інспектуйте і
iptables -L -v, іnft list ruleset. - legacy бекенд: інспектуйте явно через
iptables-legacy, щоб уникнути самообману.
- iptables-nft бекенд: інспектуйте і
- Документуйте, яка служба завантажує правила при старті (
iptables-persistent, скрипти, systemd unit-и). - Доведіть сумісність з Docker/Kubernetes перед розгортанням.
Чекліст C: Інцидент-реакція, коли власник брандмауера невідомий
- Підтвердіть, що сервіс слухає (
ss -lntp). - Підтвердіть, що трафік приходить (
tcpdumpна інтерфейсі). - Дампіть nft ruleset і правила iptables з лічильниками.
- Перевірте alternatives і шукайте розбіжності legacy.
- Перевірте логи на предмет сервісів, що перезавантажують правила після буту.
- Зупиніть кровотечу: тимчасово дозвольте лише те, що потрібно в активному бекенді, потім заплануйте прибирання.
- Після інциденту: приберіть дублюючі менеджери і закріпіть власність політики.
FAQ (реальні питання з реальних інцидентів)
1) Чи nftables «кращий» за iptables на Debian 13?
Для більшості серверних випадків — так: nftables — сучасніший інтерфейс, підтримує уніфіковані inet-правила і це напрямок розвитку екосистеми.
Але «кращий» не має значення, якщо ваші інструменти очікують семантику iptables; тоді найкращий вибір — той, яким ви можете послідовно керувати.
2) Чому iptables -L показує правила, яких я не бачу у своєму nft-файлі?
Бо ці правила можуть бути згенеровані іншою службою (Docker, UFW, fail2ban), або це може бути трансляція iptables-nft виду на стан nftables.
Конфігураційний файл не завжди є джерелом; це лише одне можливе джерело.
3) Чи можуть iptables і nftables співіснувати безпечно?
Вони можуть співіснувати, якщо iptables — це nft бекенд (iptables-nft) і ви ставитесь до нього як до інтерфейсу сумісності, а не як до другого автора.
Вони не можуть безпечно співіснувати, якщо ви змішуєте iptables-legacy з nftables політикою на одному хості. Це split-brain за визначенням.
4) Який найшвидший спосіб визначити, що я в режимі split-brain?
Порівняйте nft list ruleset з iptables -L -v, потім явно перевірте iptables-legacy -L.
Якщо legacy показує інший світ, ніж nft — ви знайшли свого привида.
5) Чому в моїх accept-правил нуль лічильників, хоча клієнти підключаються?
Або ви дивитесь не в тому місці (неправильна таблиця/родина/хук), або трафік обробляється в іншому місці раніше,
або він ніколи не досягає того хука (наприклад, local output, forwarded, або обробка на ingress).
Лічильники — це правда, але лише для правила, яке ви реально матчите.
6) Чи використовувати table inet чи окремі ip/ip6 таблиці в nftables?
Використовуйте table inet для більшості політик, щоб випадково не розділити поведінку IPv4 і IPv6.
Окремі таблиці підходять для складних випадків, але вони збільшують шанс, що IPv6 стане «чужою» проблемою.
7) А що з UFW на Debian 13?
UFW може бути операційно прийнятним, якщо він є єдиним авторитетом і ви погоджуєтесь із його моделлю.
Проблема виникає, якщо нашаровувати його на nftables-політику або на контейнерні інструменти, які також пишуть правила.
Оберіть одне: UFW-керований брандмауер або nftables-керований, але не обидва.
8) Як мігрувати з iptables правил на nftables?
Не робіть 1:1 переклад одразу. Почніть з мінімальної nft-політики, що відтворює вашу безпекову ідею,
потім додавайте складність. Перевіряйте лічильниками і тестами трафіку. Якщо тимчасово потрібно зберегти iptables-інструменти,
стандартизуйтеся на iptables-nft і трактуйте nft-вивід як канонічний стан.
9) Чому перезавантаження «змінило» брандмауер?
Бо інша служба завантажила правила при старті, або менеджер застосував свою політику після ваших правил,
або імена інтерфейсів змінились і ваші умови матчення більше не працюють. Перезавантаження — великий тест правди конфігів.
10) Чи варто логувати дропи під час налагодження?
Так, вибірково. Логи всього і відразу вичерпають диск і ваш мозок. Додайте тимчасові правила логування з rate limits,
зловіть достатньо для визначення шляху, потім приберіть їх. Тримайте лічильники довше; вони дешевші й частіше корисніші.
Наступні кроки, які можна зробити сьогодні
Якщо ви експлуатуєте Debian 13 у production і хочете, щоб ця проблема більше не з’являлась о 3:00 ранку, зробіть ці три речі:
- Визначте власність. nftables-native або один менеджер. Ніякої спільної опіки.
- Доведіть консистентність бекендів. Узгодьте alternatives iptables і ip6tables; підтвердьте, що випадково не використовуєте legacy на одній стороні.
- Операціоналізуйте інспекцію. Додайте стандартний набір «правди брандмауера» у ваші runbook-и:
nft list ruleset,iptables -L -v,update-alternatives --display iptablesі цільовийtcpdump.
А потім зробіть нудну справу: перезавантажте канар-локальний хост і підтвердіть, що правила, які ви вважаєте наявними, дійсно присутні. Оце і є вся гра.