Debian 13: правила nftables «не працюють» — порядок завантаження й конфлікти, виправлено назавжди

Було корисно?

Ви вносите зміну в nftables, можете nft list ruleset і бачите її, трафік поводиться очікувано п’ять хвилин… потім реальність повертається. Після перезавантаження все гірше: ваш «дозволити SSH» зник, Docker танцює інтерпретативний танець з вашим NAT, а бізнес питає, чому «проста правка файрвола» перетворилася на сафарі за зацікавленими сторонами.

Зазвичай це не «nftables капризний». Майже завжди проблема в порядку завантаження, конфліктуючих менеджерах або в тому, що правила технічно завантажені, але фактично обхідні. Виправимо це так, щоб більше не поверталося.

Що насправді означає «правила не працюють»

Коли хтось каже «nftables не працює», це зазвичай відповідає одному з цих реальних режимів відмови:

  • Правила зовсім не завантажені після завантаження системи або після перезапуску сервісу. Ви дивитесь на порожній ruleset або на замісник за замовчуванням.
  • Ruleset завантажений, але перезаписаний пізніше іншим актором (Docker, firewalld, cloud‑агент, юніт iptables-restore, ваша CI‑задача).
  • Ruleset завантажений, але не співпадає, бо він у неправильному хуку, невірній family (ip проти inet), неправильного типу ланцюга або має неправильний пріоритет.
  • Правила співпадають, але ваша очікуваність неправильна (стейти conntrack, шлях NAT, політика маршрутизації, міст проти маршрутизованого шляху, локальний проти форвард‑трафіку).
  • Шлях у ядрі обходить вас через offloads, XDP-програми, VRF/політику маршрутизації або інший netns, ніж ви думаєте, де фільтрація відбувається.

Підхід — перестати сперечатися зі своєю ментальною моделлю й опитати робочу систему. Debian 13 — чиста платформа для nftables — поки ви випадково не запустите два оркестратори файрвола одночасно або не покладетеся на «воно завантажилося в shell‑сесії, отже мусить зберегтися».

Жарт №1: Фаєрволи як наради — якщо ви не контролюєте, кого запрошено, хтось перепише порядок денний одразу після вашого виходу.

Факти та контекст, які варто знати (щоб припинити гадати)

  1. nftables замінив iptables як довгостроковий інтерфейс netfilter у Linux ще кілька років тому; iptables може бути сумісною обгорткою над nft (iptables-nft) або працювати з legacy‑бекендом (iptables-legacy).
  2. Debian історично підтримував обидва бекенди, бо продакшн‑флот не міг мігрувати за вихідні. Це означає, що ви можете випадково мати «працюючий» вигляд iptables, що не відображає того, що бачить nft.
  3. Сімейство inet — практичний дар: один ruleset може покривати IPv4 і IPv6. Підводне каміння — старі приклади досі використовують ip і ip6 окремо, і бездумне їх змішування може створити прогалини.
  4. Пріоритети ланцюгів мають значення, бо кілька базових ланцюгів можуть приєднуватися до одного й того самого хуку (input/forward/output/prerouting/postrouting) і виконуватися в порядку пріоритетів. «Моє правило є» не означає «моє правило виконується першим».
  5. Docker історично програмує правила файрвола/NAT автоматично і може робити це через інтерфейси iptables. З iptables-nft це все одно потрапляє в nftables — але не туди, де ви це очікуєте.
  6. firewalld — це не «лише інтерфейс»; це менеджер стану, який буде повторно накладати бажані правила. Якщо ви редагуєте nft вручну на хості з firewalld, рано чи пізно воно не погодиться з вами і переможе.
  7. Правила nftables завантажуються атомарно з файлу: синтаксичні помилки зазвичай призводять до «нічого не змінено», що добре — якщо ви не дивитесь невірний файл і думаєте, що воно завантажилось.
  8. Netfilter налаштований на кожен мережевий namespace. Контейнери і деякі менеджери сервісів можуть працювати у власних netns. Ви можете завантажувати правила в корінному namespace, тоді як трафік, який вас цікавить, живе в іншому.
  9. Boot на Debian під systemd — паралельний. Порядок має бути явним, а не магічним. Якщо ваш файрвол залежить від інтерфейсів, модулів або sysctl, ви повинні закодувати цей порядок, інакше отримаєте «працює на моєму перезавантаженні».

Швидкий план діагностики

Це послідовність «зупинити кровотечу», яку я використовую, коли хтось пише «зміна файрвола не застосувалася», і годинник голосно тікає.

По‑перше: доведіть, які правила дійсно активні

  • Запустіть nft list ruleset і збережіть результат. Якщо він порожній або відсутні ваші таблиці — ви розбираєтесь не з тим.
  • Перевірте, чи ви дивитесь у кореневий namespace. Якщо у вас є контейнери або мережеві namespace, підтвердіть, де живе трафік.
  • Підтвердьте, який фреймворк керує правилами: сервіс nftables, firewalld, Docker, юніт iptables-restore чи агенти конфігураційного менеджменту.

По‑друге: знайдіть, хто останній писав правила

  • Використайте журнали systemd: шукайте перезавантаження nftables, старт firewalld, рестарт docker та юніти iptables-restore.
  • Перевірте часові мітки файлів ruleset. Якщо вони вірні, але живі правила відрізняються — хтось перезаписав їх після завантаження.

По‑третє: валідируйте шлях пакета й hook/пріоритет

  • Це input чи forward? Локальний трафік проти маршрутизованого плутає всіх.
  • Чи NAT відбувається раніше, ніж ви очікуєте? Чи ви матчуєтеся по оригінальних чи перекладених адресах?
  • Чи є кілька базових ланцюгів на одному хуку? Проінспектуйте пріоритети та політики.

По‑четверте: вирішіть, хто є єдиним джерелом істини

  • Або: plain nftables з одним файлом ruleset і systemd‑юнітом.
  • Або: firewalld як єдиний менеджер.
  • Або: вищий рівень оркестратора (Kubernetes CNI тощо).

Змішування їх — якраз рецепт «правила не працюють», тільки правила працюють — просто не ваші.

Порядок завантаження: systemd, мережа і чому час має значення

На Debian 13 найпоширеніша причина «працює вручну, але не при завантаженні» — простий гонитва: сервіс nftables завантажується до того, як існує середовище, на яке він спирається.

Що означає «середовище» тут?

  • Інтерфейси: якщо ваші правила посилаються на iifname "ens192", але інтерфейс перейменовується udev пізніше, на завантаженні може виникнути невідповідність.
  • Модулі ядра: деяка логіка матчів/conntrack/NAT залежить від наявності модулів. Зазвичай вони автоматично підвантажуються, але інколи — ні, особливо для нестандартних протоколів.
  • Sysctl: forwarding, rp_filter, bridge-nf-call-iptables тощо. Вони можуть змінити те, який трафік потрапляє в хуки netfilter.
  • Менеджери мережі: ifupdown, systemd-networkd, NetworkManager — кожен має свій час і точки кріплення.

Systemd порядок явний. Якщо ви його не вказуєте, отримаєте «best effort». На продакшні best effort — це як сиве волосся.

Об’єктивні поради

  • Тримайте nftables якомога раніше для політик default‑drop у input, але не пишіть правила, які залежать від інтерфейсних імен, які будуть перейменовані пізніше.
  • Якщо залежите від інтерфейсів, розташуйте nftables після повного підняття мережі (або принаймні після settle udev) і прийміть, що ранній трафік в завантаженні менш контрольований.
  • Надавайте перевагу матчу по адресах/підмережах замість імен інтерфейсів, коли це можливо, особливо на хостах з передбачуваними адресами.

Конфлікти: iptables, firewalld, Docker і партнери

Конфлікти — не філософія. Це буквально кілька процесів, які пишуть у той самий netfilter ruleset, іноді через різні API, із різними припущеннями та поведінкою при перезавантаженні.

iptables проти nftables: дволике дзеркало

На Debian iptables може працювати у двох режимах:

  • iptables-nft: команди iptables програмують правила nftables під капотом.
  • iptables-legacy: команди iptables програмують legacy xtables бекенд, окремий від nftables.

Якщо ви не знаєте, який режим активний, ви легко «полагодите» файрвол за допомогою невірного інструменту і потім дивуєтесь, чому nftables не змінилося. Або навпаки: nft виглядає правильно, але насправді фільтрацію виконує iptables-legacy.

firewalld: ввічливий диктатор

Завдання firewalld — підтримувати бажаний стан. Якщо ви редагуєте nft правила вручну на системі під керуванням firewalld, ви пишете графіті на дошці, яке буде стерто при наступному оновленні.

Docker: корисний, доки не стає проблемою

Docker зазвичай забезпечує підключення контейнерів, вставляючи правила NAT та filter. В залежності від налаштувань і версій, він може:

  • створювати власні ланцюги і робити на них jump
  • змінювати політику forwarding
  • повторно застосовувати правила при рестарті демона (що може трапитися під час апгрейдів, ротації логів або при тиску на ресурси)

Якщо ви запускаєте строгі політики nft на хості з Docker, потрібно планувати поведінку Docker щодо програмування правил. Зазвичай правильний підхід — інтегруватися з ним (дозволяти його ланцюги, але обмежувати), а не робити вигляд, що його немає.

Жарт №2: Docker і файрволи мають статус стосунків: «Це складно», і ваше вікно змін — це сімейний терапевт.

Підключення в хуки, пріоритет ланцюгів і ілюзія «моє правило є»

nftables — не «один великий список». Це таблиці, ланцюги, хуки і пріоритети. Якщо ви звикли до м’язової пам’яті iptables, структура може здатися, ніби хтось переставив ваш набір інструментів у пронумеровані шухлядки. Це добре — поки ви не покладете гайковий ключ у шухляду «ложки» і не дивуєтесь, чому нічого не знімається.

Три підводні камені, що виглядають як зламані правила

  • Неправильний хук: ви блокуєте в input, але трафік форвардиться через хост і потрапляє в forward замість цього.
  • Неправильна family: ви написали table ip filter, але трафік — IPv6, тому він лишається неторкнутим. table inet filter часто є прагматичним за замовчуванням.
  • Інверсія пріоритету: базовий ланцюг з «кращим» (чисельно меншим) пріоритетом виконується перед вашим і ACCEPT‑ить трафік, тому ваш пізніший DROP ніколи не спрацьовує.

Пріоритет ланцюгів на практиці

Пріоритети — це цілі числа. Менше означає виконання раніше. NAT має звичні пріоритети (наприклад, dstnat у prerouting, srcnat у postrouting), але ви все одно можете створювати конкуруючі базові ланцюги. Якщо агент вендора встановлює базовий ланцюг на тому самому хуку з раннішим пріоритетом, ваша продумана політика може стати декоративною.

Під час діагностики не просто читайте ваш конфіг. Проінспектуйте live ruleset і знайдіть усі базові ланцюги, приєднані до кожного хуку. Потім розмірковуйте про порядок.

Цитата, яку варто тримати на моніторі

переказана думка від John Allspaw: надійність — це властивість системи, а не поведінки окремих компонентів відповідно до дизайну.

Практичні завдання: команди, виходи, рішення (чеклист для продакшна)

Ви хотіли команди. Ось вони. Кожне завдання включає, що означає вивід і яке рішення прийняти далі.

Завдання 1: Підтвердити статус сервісу nftables і останню дію

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 09:11:08 UTC; 2h 3min ago
       Docs: man:nft(8)
    Process: 412 ExecStart=/usr/sbin/nft -f /etc/nftables.conf (code=exited, status=0/SUCCESS)
   Main PID: 412 (code=exited, status=0/SUCCESS)

Значення: Сервіс виконався і вийшов успішно (нормально для oneshot). Якщо він inactive/failed — ваші правила можуть ніколи не завантажитись.

Рішення: Якщо failed — проінспектуйте логи і зробіть ручне завантаження в режимі перевірки (Завдання 4). Якщо active — рухайтесь далі: щось інше може перезаписувати правила.

Завдання 2: Переглянути журнал на предмет перезавантажень і конкуруючих сервісів

cr0x@server:~$ journalctl -u nftables -u firewalld -u docker --since "today" --no-pager
Dec 28 09:11:08 server systemd[1]: Starting nftables...
Dec 28 09:11:08 server nft[412]: /etc/nftables.conf:52:16-16: Error: Could not process rule: No such file or directory
Dec 28 09:11:08 server systemd[1]: nftables.service: Main process exited, code=exited, status=1/FAILURE
Dec 28 09:11:08 server systemd[1]: Failed to start nftables.
Dec 28 09:19:43 server systemd[1]: Starting Docker Application Container Engine...
Dec 28 09:19:44 server dockerd[733]: time="2025-12-28T09:19:44.101" level=info msg="Loading containers: done."

Значення: Тут nftables фактично зазнав помилки через помилку конфігу (відсутній include‑файл — поширений випадок). Docker стартував пізніше і буде програмувати правила незалежно.

Рішення: Виправте помилку парсингу nftables перед тим, як шукати «конфлікти». Якщо nft падає при завантаженні — ви боретесь із незавантаженою політикою.

Завдання 3: Перевірити, який бекенд iptables вибрано

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

Значення: Команди iptables будуть маніпулювати nftables. Це добре для єдиного уніфікованого ruleset, але також означає, що Docker/інші користувачі iptables пишуть у nft.

Рішення: Якщо ви бачите legacy на хості, яким керуєте через nftables — зупиніться і узгодьте інструменти. Змішування бекендів = театр дебагу.

Завдання 4: Валідируйте конфіг без застосування

cr0x@server:~$ nft -c -f /etc/nftables.conf
/etc/nftables.conf:12:1-14: Warning: table ip filter is managed by iptables-nft, do not touch!

Значення: Конфіг парситься, але ви пишете в таблицю, якою керує інший інструмент (iptables-nft). Текст попередження варіюється, але ситуація реальна.

Рішення: Не боріться за одні й ті самі імена таблиць. Використовуйте власні таблиці/ланцюги й інтегруйтесь через jump, або припиніть використовувати iptables‑інструменти на цьому хості.

Завдання 5: Дамп живого ruleset і пошук ваших слідів

cr0x@server:~$ nft list ruleset
table inet filter {
	chain input {
		type filter hook input priority 0; policy drop;
		ct state established,related accept
		iifname "lo" accept
		tcp dport 22 accept
		counter drop
	}
}
table ip nat {
	chain PREROUTING {
		type nat hook prerouting priority -100; policy accept;
	}
	chain POSTROUTING {
		type nat hook postrouting priority 100; policy accept;
	}
}

Значення: Ваша inet filter таблиця існує, політика input — drop, SSH дозволено. NAT‑таблиця теж є.

Рішення: Якщо ваших правил немає — ви не завантажуєте правильний файл або вас перезаписують. Якщо вони присутні, але трафік все одно йде неправильно — переходьте до валідації hook/шляху й трасування.

Завдання 6: Визначте всі базові ланцюги та пріоритети на кожному хуку

cr0x@server:~$ nft -a list chains
table inet filter {
	chain input { type filter hook input priority 0; policy drop; }
	chain forward { type filter hook forward priority 0; policy drop; }
}
table inet docker {
	chain input { type filter hook input priority -10; policy accept; }
}

Значення: Є два базові ланцюги на hook input. Ланцюг Docker виконується раніше (priority -10) і за замовчуванням приймає. Ваша політика drop виконується пізніше і може ніколи не бачити пакети.

Рішення: Або змініть пріоритети, або видаліть конкуруючий базовий ланцюг, або інтегруйтеся, зробивши Docker jump у ваш ланцюг (або навпаки). Залишати два базові ланцюги з конфліктними політиками — це запрошення до сюрпризів.

Завдання 7: Підтвердити, чи firewalld керує nftables

cr0x@server:~$ systemctl is-enabled firewalld
enabled

Значення: firewalld, ймовірно, буде наполягати на своєму стані при завантаженні та перезавантаженні, потенційно перезаписуючи частини ruleset.

Рішення: Оберіть: firewalld‑керований хост або nftables‑керований. Якщо firewalld залишаєте — припиніть редагувати nft напряму й використовуйте примітиви firewalld. Якщо nfttables залишається — відключіть firewalld.

Завдання 8: Перевірити, чи Docker програмує iptables правила

cr0x@server:~$ grep -nE 'iptables|ip6tables' /etc/docker/daemon.json
12:  "iptables": true,
13:  "ip6tables": true,

Значення: Docker буде програмувати правила (через інтерфейс iptables), які потраплять у nft, якщо обрано iptables-nft.

Рішення: Якщо вам потрібен суворий контроль, розгляньте встановлення "iptables": false лише якщо ви готові самостійно керувати підключенням контейнерів. Більшість команд — ні, і вони дізнаються це о 2‑й годині ночі.

Завдання 9: Підтвердити sysctl для форвардингу і bridge netfilter

cr0x@server:~$ sysctl net.ipv4.ip_forward net.ipv6.conf.all.forwarding net.bridge.bridge-nf-call-iptables
net.ipv4.ip_forward = 0
net.ipv6.conf.all.forwarding = 0
net.bridge.bridge-nf-call-iptables = 0

Значення: Форвардинг вимкнено; мостовий трафік може обходити iptables‑подібні хуки. Якщо ви очікували, що хост маршрутизує або фільтрує містовий трафік контейнерів, ваше очікування хибне.

Рішення: Якщо цей хост — маршрутизатор або виконує форвардинг контейнерів, увімкніть відповідні sysctl і закріпіть їх персистентно. Якщо не має форвардити — тримайте їх виключеними і скорегуйте дизайн.

Завдання 10: Підтвердити, що ви фільтруєте правильний namespace

cr0x@server:~$ ip netns list
cni-3f2a9c7b-1

Значення: Існує принаймні один не‑кореневий netns. Трафік може оброблятись там із власними правилами.

Рішення: Якщо проблемний трафік знаходиться в тому namespace — інспектуйте його за допомогою ip netns exec і застосуйте правила там (або скоригуйте політику хоста).

Завдання 11: Трасування оцінки пакета, щоб побачити, яке правило збігається

cr0x@server:~$ nft monitor trace
trace id 9b7e7f05 inet filter input packet: iif "ens192" ip saddr 203.0.113.50 ip daddr 192.0.2.10 tcp sport 51544 tcp dport 22
trace id 9b7e7f05 rule inet filter input handle 7: tcp dport 22 accept

Значення: Пакет потрапив у ваш input chain і співпав з правилом дозволу SSH (handle 7). Трасування прибирає здогадки.

Рішення: Якщо пакет співпав з несподіваним accept/drop — виправте порядок і предикати. Якщо він взагалі не з’являється — ви трасуєте неправильний хук/шлях або трафік обходить хост.

Завдання 12: Перевірити на вузькі перезаписи періодичними задачами чи CM

cr0x@server:~$ systemctl list-timers --all --no-pager | grep -E 'nft|iptables|firewall'
Sun 2025-12-28 10:00:00 UTC  1h ago   Sun 2025-12-28 11:00:00 UTC  1min ago  firewall-sync.timer  firewall-sync.service

Значення: Існує таймер, який, ймовірно, періодично повторно застосовує стан файрвола. Ваша ручна зміна буде скасована по розкладу.

Рішення: Або інтегруйте вашу зміну в цю керовану лінію, або відключіть конкуруючу задачу. «Я просто редагую наживо» — не стратегія, це жарт над Майбутнім Вами.

Завдання 13: Підтвердити, що включені файли існують і порядок їх завантаження в конфігу

cr0x@server:~$ sed -n '1,120p' /etc/nftables.conf
#!/usr/sbin/nft -f

flush ruleset

include "/etc/nftables.d/base.nft"
include "/etc/nftables.d/nat.nft"
include "/etc/nftables.d/docker-overrides.nft"

Значення: У вас модульний ruleset з жорстким flush на початку. Це розумно, якщо ніхто інший не очікує зберігати свої власні правила.

Рішення: Якщо Docker/firewalld мають співіснувати, не робіть flush ruleset безумовно. Замість цього скидайте лише ваші таблиці або визначте чіткий контракт інтеграції.

Завдання 14: Перевірити, що перезавантаження не ріже існуючі SSH‑сесії

cr0x@server:~$ sudo nft -f /etc/nftables.conf && echo "reload ok"
reload ok

Значення: Ruleset успішно завантажився. Якщо ваш SSH залишився підключеним, правило established/related робить свою роботу.

Рішення: Якщо перезавантаження вбило сесії — у вас ймовірно відсутнє правило stateful accept або ви очистили очікування conntrack. Виправте це перед виконанням віддалено ще раз.

Три корпоративні міні‑історії (і що з них запозичити)

Міні‑історія №1: Інцидент через неправильне припущення

Одна команда отримала у спадок Debian‑хост, який виконував роль jump box і легкого маршрутизатора для лабораторного сегмента. Вони захотіли «посилити вхідний доступ» і написали nftables input ланцюг з політикою default drop і акуратним allowlist. Виглядало ідеально при рев’ю. Тести з їхньої робочої станції пройшли.

Через дві години інша група повідомила, що лабораторний сегмент не може дістатися до кеша артефактів. Кеш був по інший бік jump box. Ніхто не пов’язав це з правкою файрвола, бо «ми торкались лише input».

Реальність: майже весь релевантний трафік форвардився через коробку і ніколи не потрапляв у input. Політика за замовчуванням у forward залишалась такою, якою її лишили Docker і історичний iptables‑багаж. Іншими словами — вони захистили невірні двері.

Виправлення не було героїчним. Вони додали явний базовий forward ланцюг у table inet filter з дозволами між сегментами і політикою default drop, потім перевірили за допомогою nft monitor trace з обох сторін. Також вони занотували припущення щодо шляху пакета в change request, що звучить нудно, поки це не запобіжить повторенню тієї ж помилки наступним.

Міні‑історія №2: Оптимізація, що відбилась боком

Платформна команда вирішила «стандартизувати і прискорити завантаження», змусивши nftables завантажуватись дуже рано, до ініціалізації мережі, по флоту. Їхня мотивація була зрозуміла: раніший файрвол — менше вікно ризику. Тож вони встановили агресивний порядок і прибрали мережеві залежності з юніту.

В стагінгу це працювало, здебільшого. У продакшні ж частина машин мала передбачувані перейменування інтерфейсів, які завершувалися пізніше в процесі завантаження. Правила посилались на iifname матчі для management і storage інтерфейсів. Раннє завантаження означало, що ці імена ще не були стабільні. Правила компілювались, але нічого не матчило. Хости завантажувались у дозволяючій реальності, якої вони не хотіли.

Зловили вони це лише тому, що інженер помітив, що nft list ruleset показує очікувані правила, але трафік явно не фільтрується. Трасування виявило пакети, що потрапляли у default accept у іншому ланцюзі, який мав бути недосяжним.

Урок: «завантажити раніше» не завжди означає «безпечніше». Це безпечно лише якщо об’єкти, по яких ви матчуєтеся, стабільні з ранньої стадії завантаження. Вони врешті перенесли правила, що залежать від інтерфейсів, у набори, ключовані підмережами, використовували inet таблиці і впорядкували nftables після udev‑settle на цій класі обладнання. Трохи пізніше — і фактично правильно.

Міні‑історія №3: Нудна, але правильна практика, що врятувала день

В іншій компанії SRE мала нудну звичку: кожна зміна файрвола включала знімок before/after nft list ruleset й простий скрипт перевірки з двох візуальних точок. Вони зберігали ці артефакти з записом деплойменту. Ніхто не вихвалявся цим на вечірках.

Під час рутинного оновлення ОС на Debian‑хостах новий пакет підтягнув firewalld як залежність для «зручного» інструмента. Це не було зловмисним; це була настройка за замовчуванням. firewalld самовключився при завантаженні на невеликій частці флоту через пост‑інсталяційну поведінку та автоматизаційну особливість.

За годину хтось помітив, що live ruleset не відповідає очікуваному знімку після перезавантаження. Не тому, що трафік вже впав — а тому, що вони мали звичку порівнювати стан після обслуговування. Вони відключили firewalld на тих нодах, відновили nftables і додали явний pin пакета плюс CI‑перевірку, яка падає, якщо firewalld увімкнено на цій ролі.

Урок‑врятування дня: надійні операції — це в основному звички та запобіжні заходи. Розумний дебаг потрібен лише коли ці запобіжники підводять.

Типові помилки: симптом → корінна причина → виправлення

1) «Правила присутні, але пакети ігнорують їх»

Симптом: nft list ruleset показує ваші drop‑правила, але з’єднання все одно вдаються.

Коренева причина: Ваше правило в неправильному хуку (input vs forward) або в неправильній family (ip vs inet), або інший базовий ланцюг виконується раніше і ACCEPT‑ить.

Виправлення: Використайте nft -a list chains для переліку базових ланцюгів і пріоритетів; nft monitor trace для перевірки оцінки; підкоригуйте hook/priority і видаліть конкуруючі базові ланцюги.

2) «Працює до перезавантаження, потім зникає»

Симптом: Після перезавантаження ruleset порожній або за замовчуванням.

Коренева причина: Сервіс nftables відключений, конфіг файл не посилається, помилка парсингу на завантаженні або інший менеджер перезаписав стан після завантаження nftables.

Виправлення: Переконайтесь у systemctl enable nftables; валідируйте з nft -c -f; перевірте журнал на помилки; проведіть аудит firewalld/Docker/iptables restore юнітів і таймерів.

3) «Мій include‑файл завантажується вручну, але падає в сервісі»

Симптом: Ручний nft -f працює; завантаження при старті падає.

Коренева причина: Відносні include‑шляхи, відсутній файл на завантаженні, інший робочий каталог або порядок, де файл генерується пізніше.

Виправлення: Використовуйте абсолютні шляхи в include; переконайтесь, що юніт, який генерує файл, виконується до nftables; додайте Requires= та After= залежності у systemd при потребі.

4) «Docker‑мережування ламається, коли я вмикаю default drop»

Симптом: Контейнери втрачають зовнішній доступ або опубліковані порти не працюють.

Коренева причина: Docker очікує певну поведінку forward/NAT; ваші правила дропають форвард або блокують між‑містові потоки; Docker‑ланцюги можуть працювати раніше за ваші.

Виправлення: Явно дозволяйте established/related у forward; дозволяйте потрібні bridge‑інтерфейси і Docker‑ланцюги; уникайте повного flush ruleset якщо Docker має керувати власними шматками.

5) «iptables показує одне, nft — інше»

Симптом: iptables -S не відповідає nft list ruleset.

Коренева причина: Активний бекенд iptables‑legacy, або ви змішуєте інструменти в різних namespace.

Виправлення: Узгодьте alternatives на iptables-nft якщо хочете уніфікований стан; припиніть використовувати legacy‑інструменти; перевірте контекст namespace.

6) «Агент вендора постійно повертає мої правила»

Симптом: Ваші правила застосовуються, потім незрозуміло чому повертаються назад через інтервали часу.

Коренева причина: Періодичне примусове застосування (systemd timer, конфігураційний менеджмент, cloud security agent).

Виправлення: Знайдіть таймер/сервіс; змініть джерело істини конфігурації; додайте моніторинг дрейфу правил (хешуйте вивід nft list ruleset).

Чеклисти / покроковий план

Покроково: зробити nftables єдиним джерелом істини (рекомендовано для серверів)

  1. Виберіть менеджера. Якщо хочете plain nftables — відключіть firewalld і припиніть використовувати iptables‑скрипти як канонічну конфігурацію.
  2. Узгодьте бекенд iptables. Використовуйте iptables-nft, щоб Docker та інші споживачі iptables потрапляли у світ nft, якщо немає вагомої причини інакше.
  3. Спроєктуйте структуру ruleset. Надавайте перевагу table inet filter для input/forward/output. Зберігайте NAT у table ip natip6 nat лише якщо справді робите IPv6 NAT).
  4. Вирішіть, чи можна безпечно робити flush. Якщо ніхто інший не має керувати правилами — flush ruleset чистий. Якщо Docker мусить керувати — скидайте лише ваші таблиці.
  5. Закодуйте порядок. Якщо залежите від sysctl або згенерованих файлів — systemd має про це знати.
  6. Перевіряйте трасуванням і лічильниками. Додавайте counters до ключових правил і спостерігайте інкременти під час тестів.
  7. Зробіть це персистентним. Увімкніть nftables‑юнит і тримайте конфіг у керованому файлопотоці.
  8. Додайте детектування дрейфу. Алертуйте, якщо хеш живого ruleset змінюється поза вікнами змін.

Чеклист: безпечна віддалена процедура змін

  • Переконайтесь, що маєте консольний доступ або OOB (BMC, консоль гіпервізора).
  • Переконайтесь, що ct state established,related accept існує перед тим, як встановлювати default‑drop input.
  • Використайте nft -c -f перед nft -f.
  • Застосовуйте зміни під час сесії з другим підключенням як канаркою.
  • Негайно запустіть nft monitor trace під час тестування репрезентативного потоку.
  • Задокументуйте nft list ruleset до/після для подальшої безвинної діагностики.

Чеклист: правила співіснування (коли уникнути Docker/firewalld не можна)

  • Якщо firewalld увімкнено — розглядайте його як джерело істини і налаштовуйте його безпосередньо.
  • Якщо Docker увімкнений і ви тримаєте "iptables": true — не робіть flush усього ruleset при перезавантаженні.
  • Явно інспектуйте пріоритети ланцюгів, щоб переконатись, що ваша політика не обходиться ранніми базовими ланцюгами.
  • Надавайте перевагу інтеграції з Docker‑ланцюгами через jump, а не їх перезапису.

FAQ

Чому мої правила працюють, коли я запускаю nft -f вручну, але не після перезавантаження?

Тому що завантаження — це гонитва. Ваш сервіс може падати через include‑шлях, завантажуватись до того, як файли існують, або бути перезаписаним після завантаження Docker/firewalld/таймерами. Спершу перевірте systemctl status nftables і журнал.

Використовувати table inet чи окремі table ip/table ip6?

Використовуйте inet для фільтр‑правил, якщо немає конкретної причини не робити цього. Це зменшує дрейф між v4/v6 політиками і запобігає «випадковому відкриттю IPv6».

Чи безпечно поміщати flush ruleset на початок /etc/nftables.conf?

Безпечно лише якщо nftables — єдиний менеджер. Якщо Docker або firewalld програмують правила, повний flush видалить їхні ланцюги і ви проведете вечір, пояснюючи, чому контейнери не можуть досягти DNS.

Як визначити, чи Docker змінює мій файрвол?

Шукайте Docker‑створені таблиці/ланцюги в nft list ruleset і зіставляйте з journalctl -u docker. Також перевірте /etc/docker/daemon.json на наявність "iptables": true.

Чи можна запускати firewalld і ручний nftfiles ruleset разом?

Можна, але не слід. firewalld стверджуватиме свій стан; ваші ручні зміни стануть недетермінованими. Обирайте один менеджер для ролі хоста.

Чому iptables -S не відповідає nft list ruleset?

Можливо, ви використовуєте iptables‑legacy. Перевірте update-alternatives --display iptables. Також перевірте мережеві namespace — iptables в іншому namespace покаже інший стан.

Мій SSH був обірваний під час перезавантаження. Що я зробив не так?

Ймовірно, ви встановили default‑drop input без раннього дозволу ct state established,related, або замінили ruleset без збереження stateful‑дозволів. Виправте порядок: established/related першим, потім loopback, потім дозволені сервіси, потім drop.

Як довести, яке правило дропає пакет?

Використайте nft monitor trace під час тестового потоку. Воно показує ланцюг і handle правила, що співпав. Лічильники теж допомагають, але trace — найчіткіше.

Який найчистіший спосіб запобігти дрейфу правил?

Зробіть один механізм джерелом істини (файл nftables, firewalld або CM), потім додайте моніторинг, який хешує nft list ruleset і алертить при несподіваних змінах.

Висновок: наступні кроки, які запобігають повторенню

Якщо nftables «не працює» на Debian 13, це майже ніколи не таємниця ядра. Це невизначеність контрольної площини: порядок завантаження, кілька менеджерів або ланцюги, що виконуються в порядку, якого ви не планували.

Зробіть наступне:

  1. Пройдіть швидкий план діагностики і зафіксуйте live ruleset, пріоритети ланцюгів і хронологію журналу.
  2. Виберіть один менеджер файрвола для ролі хоста. Вимкніть інші або інтегруйте явно.
  3. Усуньте випадкові гонитви: валідируйте конфіг, використовуйте абсолютні include‑шляхи і задайте ordering у systemd там, де є залежності.
  4. Використовуйте trace хоча б раз під час інциденту. Це найшвидший спосіб припинити дебати про теорії.
  5. Додайте детектування дрейфу, щоб ви дізнавалися про перезаписи до того, як користувачі помітять.

Кінцевий стан, який вам потрібен, — нудний: детерміноване поведінка при завантаженні, одне джерело істини і зміни файрвола, що поводяться однаково в вівторок, як і під час тесту в п’ятницю.

← Попередня
Майнерські версії: найкращі та найгірші ідеї, які постачала індустрія
Наступна →
AMD і трасування променів: чому наздоганяти було розумно

Залишити коментар