iptables чи nftables в Ubuntu: що працює на сервері
В Ubuntu 20.04+ iptables зазвичай записує правила nftables. Перевірте backend, побачте native ruleset і зрозумійте конфлікти ufw та Docker.
iptables проти nftables в Ubuntu: який із них працює на вашому сервері?
В Ubuntu 20.04 і новіших версіях команда iptables є frontend, який записує правила nftables. У ядрі працює один фільтр пакетів — nftables, а дві команди user space програмують його. Рядок iptables -A INPUT і далі працює так само, як раніше, а створене ним правило є правилом nftables, яке може вивести nft.
Перш ніж робити висновки, перевірте це на власному сервері.
iptables -V
sudo update-alternatives --display iptables
sudo nft list rulesetВ Ubuntu 24.04 (iptables 1.8.10 станом на серпень 2026 року) команда iptables -V виводить iptables v1.8.10 (nf_tables). Назва в дужках — це backend. (nf_tables) означає, що команда працює з nftables. (legacy) означає старий backend x_tables, який Ubuntu досі постачає як iptables-legacy і який ядро підтримує як повністю окремий набір правил. Команда update-alternatives виводить symlink, що відповідає цьому вибору: link currently points to /usr/sbin/iptables-nft.
На новому VPS без налаштованого firewall команда sudo nft list ruleset нічого не виводить. Цей порожній результат є базовим станом. Додайте одне правило старим способом і перевірте результат ще раз.
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
sudo nft list ruleset# Warning: table ip filter is managed by iptables-nft, do not touch!
table ip filter {
chain INPUT {
type filter hook input priority filter; policy accept;
tcp dport 22 counter packets 0 bytes 0 accept
}
chain FORWARD {
type filter hook forward priority filter; policy accept;
}
chain OUTPUT {
type filter hook output priority filter; policy accept;
}
}Ваше правило iptables є правилом nftables. iptables-nft позначає таблиці, які воно створює, а nft виводить це попередження, коли бачить таку позначку, оскільки редагування цієї таблиці за допомогою nft передає керування одним набором правил двом інструментам. Подивіться, що створила одна команда: таблицю, яку ви не називали, і ланцюжки, яких ви не запитували. Це стара модель, і саме це змінюється в першу чергу, коли ви записуєте правила nftables безпосередньо.
Що від вас приховує iptables -L
iptables -L показує лише таблицю filter. Для правил NAT (перетворення мережевих адрес) потрібен iptables -t nat -L, а для правил mangle — -t mangle. IPv6 використовує окрему команду — ip6tables, у якій є власна копія кожного правила. Тому сервер може виглядати чистим в одному списку, хоча правила з таблиці, яку ви не перевірили, блокують або змінюють ваші пакети.
sudo nft list ruleset виводить усі сімейства, усі таблиці, усі ланцюжки та всі правила в одному результаті. На сервері, який ви налаштовували не самі, ця команда є найшвидшим способом побачити, що фактично завантажено. Додайте -a, щоб вивести ідентифікатори правил. Вони потрібні, щоб видалити одне правило, а не весь ланцюжок.
Поки ви працюєте з цим, варто виправити дві звички. iptables -L перетворює адреси та порти на імена, тому на сервері з несправним resolver може здаватися, що команда зависла: використовуйте iptables -nvL. Також переконайтеся, що legacy back end порожній, за допомогою sudo iptables-legacy -nvL. Якщо правила існують в обох back end, ядро перевіряє їх усі, а жоден окремий список не показує повної картини.
Таблиці та ланцюжки, які ви створюєте, а не успадковуєте
nftables починає з порожньої конфігурації. Таблиці filter не існує, доки ви її не створите, а слово filter — лише вибране вами ім’я. Ланцюжок обробляє пакети лише після призначення йому типу, hook і пріоритету. Так він стає базовим ланцюжком. Ланцюжок без цих параметрів досяжний лише через явний jump або goto, тому він не споживає ресурси, доки до нього не буде виконано перехід.
Інша важлива зміна — сімейство inet. Одна таблиця inet обробляє IPv4 та IPv6 за однаковими правилами. Це усуває цілий клас помилок, коли порт закритий у iptables, але повністю відкритий у ip6tables. Така невідповідність трапляється досить часто, щоб мати окремий сценарій відмови на серверах з ufw.
Нижче наведено повний набір правил для сервера. Його потрібно зберегти у /etc/nftables.conf.
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
set admin_ips {
type ipv4_addr
flags interval
elements = { 203.0.113.5, 198.51.100.0/24 }
}
chain input {
type filter hook input priority filter; policy drop;
ct state established,related accept
ct state invalid drop
iif lo accept
ip protocol icmp accept
meta l4proto ipv6-icmp accept
tcp dport 22 ip saddr @admin_ips counter accept
tcp dport { 80, 443 } counter accept
}
chain forward {
type filter hook forward priority filter; policy drop;
}
chain output {
type filter hook output priority filter; policy accept;
}
}Перечитайте рядок 2 двічі. flush ruleset видаляє всі таблиці на сервері, зокрема таблиці, які ufw і Docker створили для власних потреб. Перед виконанням цієї команди на робочому сервері дочитайте інструкцію до кінця.
Перше правило в ланцюжку input виконує основну роботу. ct state established,related accept пропускає відповіді на з’єднання, які ви ініціювали, тому решті ланцюжка потрібно лише вирішувати, що робити з новими з’єднаннями. ct state invalid drop відкидає пакети, які не відповідають жодному відомому з’єднанню і не є коректним початком нового з’єднання. Усе, що залишається після цього, є явно дозволеним винятком, а policy drop обробляє решту пакетів.
Перевірте файл перед завантаженням і під час цього залиште відкритим другий сеанс SSH. policy drop разом із однією помилкою в правилі SSH може заблокувати вам доступ до власного сервера.
sudo nft -c -f /etc/nftables.conf
sudo nft -f /etc/nftables.conf
sudo nft list rulesetnft -c -f розбирає файл і повідомляє про помилки, не завантажуючи його. Якщо синтаксис правильний, команда не виводить нічого.
Набори замінюють довгі списки правил
tcp dport { 80, 443 } — це анонімний набір: одне правило й одна перевірка замість окремого правила для кожного порту. Іменований набір, наприклад admin_ips, дає більше можливостей, оскільки його можна змінювати під час роботи firewall.
sudo nft add element inet filter admin_ips { 203.0.113.9 }
sudo nft list set inet filter admin_ips
sudo nft delete element inet filter admin_ips { 203.0.113.9 }Перезавантаження не потрібне, нумерація правил не змінюється, а перевірка залишається однією операцією незалежно від того, містить набір п’ять адрес чи п’ятдесят тисяч. flags interval дає змогу зберігати в наборі діапазони та префікси CIDR (classless inter-domain routing), наприклад 198.51.100.0/24. Без цього прапорця набір приймає лише окремі адреси, і завантаження префікса завершується помилкою.
Елементи наборів також можуть автоматично видалятися після завершення терміну дії.
set banned {
type ipv4_addr
flags timeout
timeout 1h
}Якщо правило містить ip saddr @banned drop, кожен елемент видаляється через годину після додавання. Саме так дія nftables у fail2ban на Ubuntu 24.04 блокує адресу: вона додає елемент до набору, а не нове правило. Якщо робота з портами для вас нова, почніть зі статті що таке порт у Linux насправді.
Під час міграції часто виникає одна відмінність. nftables не підраховує пакети, якщо явно не попросити його про це. iptables -nvL завжди показує лічильники для кожного правила. У nftables номери мають лише правила з ключовим словом counter, тому додайте counter до кожного правила, яке плануєте налагоджувати пізніше.
Як хуки та пріоритети визначають порядок
Базовий ланцюжок визначає hook — точку в шляху проходження пакета, де він виконується. prerouting виконується до прийняття рішення щодо маршрутизації. input обробляє пакети, адресовані цьому комп’ютеру. forward обробляє пакети, що маршрутизуються через нього. output обробляє пакети від локальних процесів. postrouting виконується останнім, безпосередньо перед виходом пакета.
Пріоритет визначає порядок ланцюжків у межах одного hook: спочатку виконується ланцюжок із найменшим числом. nftables використовує назви для класичних значень: raw — це -300, mangle — -150, dstnat — -100, filter — 0, srcnat — 100. Запис priority filter; еквівалентний запису priority 0;.
Тепер розглянемо умову, яка визначає, чи можна поєднувати ці інструменти. Кожен базовий ланцюжок, зареєстрований у hook, виконується відповідно до пріоритету. Пакет, прийнятий у вашому ланцюжку, не вважається остаточно обробленим: accept завершує лише цей ланцюжок, після чого пакет переходить до наступного базового ланцюжка в тому самому hook. drop є остаточною дією всюди й одразу зупиняє пакет. Тому дозвільне правило у вашій таблиці не може скасувати drop у таблиці ufw незалежно від того, який ланцюжок виконується першим, а ваш accept не захищає від ланцюжка, який виконується пізніше.
Два базові ланцюжки в одному hook з однаковим пріоритетом виконуються в порядку реєстрації. Він залежить від того, який сервіс запустився першим. Після перезавантаження цей порядок може змінитися. Якщо потрібно запускати власну таблицю поруч з ufw, призначте їй окремий пріоритет. Тоді порядок буде визначено явно, а не залежатиме від результату змагання під час запуску.
Чому немає правила зворотної NAT-трансляції, яке потрібно додати?
Це питання найчастіше розуміють неправильно, тому відповідь проста. Відстеження з’єднань самостійно записує зворотну трансляцію. Друге правило додавати не потрібно.
Таблиця nat, яка виконує обидві типові функції VPS, має такий вигляд.
table inet nat {
chain prerouting {
type nat hook prerouting priority dstnat; policy accept;
iifname "enp1s0" tcp dport 8080 dnat ip to 10.0.0.5:80
}
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
ip saddr 10.0.0.0/24 oifname "enp1s0" masquerade
}
}Лише перший пакет з’єднання перевіряється в ланцюжку nat. Коли правило збігається, ядро зберігає цю трансляцію в таблиці відстеження з’єднань разом із записом про з’єднання. Усі наступні пакети в обох напрямках переписуються на основі збереженого запису, і правила повторно не перевіряються. Встановіть інструмент conntrack і перегляньте активний запис.
sudo apt install -y conntrack
sudo conntrack -L -p tcptcp 6 431999 ESTABLISHED src=198.51.100.20 dst=203.0.113.10 sport=54321 dport=8080 src=10.0.0.5 dst=198.51.100.20 sport=80 dport=54321 [ASSURED] mark=0 use=1Інтерпретуйте його як два кортежі. Перші чотири поля описують з’єднання в тому вигляді, у якому клієнт його надіслав, із призначенням на 203.0.113.10:8080, вашу публічну адресу. Другі чотири поля описують відповідь, яку очікує ядро: вона вже розвернута й трансформована та надходить від 10.0.0.5:80, фактичного бекенду. Цей другий кортеж і є зворотним правилом. Ядро записало його, коли перший пакет збігся з правилом.
Тому не створюйте правило для зворотного напрямку. Воно не може спрацювати, оскільки пакети відповіді належать до вже встановленого з’єднання й ніколи не потрапляють до ланцюжка nat. Навіть якби це сталося, ви повторно трансформували б пакет, який ядро вже обробило.
Розташування трансляції визначається тим самим механізмом. Трансляція адреси призначення має виконуватися в prerouting до вибору маршруту, оскільки маршрутизація повинна бачити нове призначення. Інакше пакет буде надіслано не туди. Трафік, який сам генерує цей сервер, обробляється в hook output з тієї самої причини. Трансляція адреси джерела, зокрема переписування порту джерела, має виконуватися в postrouting після вибору маршрутом вихідного інтерфейсу. masquerade отримує адресу цього інтерфейсу, а сам інтерфейс стає відомим лише після виконання маршрутизації.
Тому правило такого типу має розташовуватися наприкінці шляху й більше ніде.
ip saddr 10.0.0.0/24 oifname "enp1s0" snat ip to 203.0.113.10:20000-30000Діапазон портів переписує порт джерела разом з адресою джерела. Це потрібно, коли багато внутрішніх клієнтів спільно використовують одну публічну адресу, а їхні порти джерела збігаються. Відповідь надходить на порт із цього діапазону, conntrack зіставляє її із записом, а початковий порт джерела відновлюється перед доставленням пакета. Знову ж таки, друге правило не потрібне.
Практичний наслідок такий: зміна правила NAT не впливає на вже наявні з’єднання, оскільки їхні трансляції вже збережені. Вони продовжують працювати за старою схемою, доки їхні записи не завершать дію. sudo conntrack -D -p tcp --dport 8080 видаляє відповідні записи, а sudo conntrack -F видаляє всі записи. Обережно використовуйте другий варіант на NAT-сервері, оскільки саме збережені трансляції підтримують поточні з’єднання. Їх очищення одночасно розриває всі з’єднання через цей сервер.
ufw і Docker самостійно додають власні правила
ufw є інтерфейсом для iptables, який в Ubuntu є інтерфейсом для nftables. Тому система з ufw має таблицю ip filter, заповнену ланцюжками з назвами ufw-before-input, ufw-user-input тощо, а також копію цієї структури ip6 filter. Перегляньте їх за допомогою sudo nft list ruleset | grep ufw. Ці ланцюжки генеруються з файлів у /etc/ufw, а ufw reload щоразу повністю перезаписує їх. Тому вручну додане зверху правило iptables зникає під час наступного перезавантаження конфігурації. У матеріалі Основи ufw для VPS описано структуру цих файлів.
Docker самостійно налаштовує брандмауер і не враховує правила ufw. Публікація порту за допомогою -p 80:80 додає правило DNAT до таблиці nat і правило дозволу до шляху пересилання. Обидва правила виконуються перед користувацькими ланцюжками ufw. Через це виникає типова проблема: запускається ufw deny 80, але контейнер усе одно доступний з інтернету. Правильне місце для власних правил — ланцюжок DOCKER-USER, який Docker залишає для них. У матеріалі Чому контейнери Docker ігнорують ufw це пояснено детальніше. Перегляньте правила у своїй системі за допомогою sudo nft list ruleset | grep -i docker.
Тепер знову перегляньте рядок flush ruleset з наведеної вище конфігурації. Він видаляє всі таблиці, зокрема таблиці, якими керують ці два інструменти. На хості Docker опубліковані порти перестають працювати, доки sudo systemctl restart docker не відновить ланцюжки. Саме цей рядок найчастіше призводить до вимкнення власних сервісів під час упорядкування конфігурації брандмауера.
Правила, які зберігаються після перезавантаження
Жоден із двох наборів правил не зберігається автоматично. Після завершення роботи ядро забуває всі правила, а кожна система вирішує цю проблему окремим пакетом.
Для nftables файл /etc/nftables.conf читає nftables.service. В Ubuntu цей сервіс вимкнений, тому перевірте його стан, перш ніж покладатися на нього.
systemctl is-enabled nftables
sudo nft -c -f /etc/nftables.conf
sudo systemctl enable --now nftablesДля iptables потрібен пакет iptables-persistent. Він встановлює netfilter-persistent і зберігає правила у /etc/iptables/rules.v4 та /etc/iptables/rules.v6.
sudo apt install -y iptables-persistent
sudo netfilter-persistent saveНе запускайте обидва механізми. Два файли, кожен з яких вважає себе джерелом правил firewall, поступово розійдуться. Правила, завантажені останніми, матимуть перевагу, але це неможливо передбачити, читаючи лише один із файлів.
Існує ще одна проблема зі збереженням активного набору правил. sudo nft -s list ruleset > /etc/nftables.conf зберігає все, що завантажено в цей момент, зокрема таблиці ufw і Docker. Якщо відновити цей файл під час запуску, ви отримаєте зафіксовану копію правил, які ці інструменти мають створювати самостійно, а після їх запуску — ще одну копію. Зберігайте лише власну таблицю за допомогою sudo nft -s list table inet filter. Прапорець -s виключає лічильники, оскільки їм не місце у файлі конфігурації.
Чи варто перейти на native firewall на VPS?
Не змінюйте ufw, якщо вам не потрібні функції, яких він не підтримує. ufw покриває типове завдання для VPS: заборона всього вхідного трафіку за замовчуванням і відкриття кількох портів. Заміна цієї конфігурації власним набором правил без практичної причини дає той самий firewall і ще один компонент для обслуговування.
Переходьте на native firewall, якщо вам потрібні функції поза моделлю ufw: NAT і перенаправлення портів, набори, які можна оновлювати під час роботи, одне правило для обох сімейств адрес або власні пріоритети ланцюжків. Це обґрунтовані причини, і ufw не може їх реалізувати.
Якщо переходите на native firewall, зробіть це повністю. Виконайте sudo ufw disable і sudo systemctl disable --now ufw, за допомогою sudo nft list ruleset переконайтеся, що його таблиці видалено, а потім завантажте власний файл. Сервер, на якому одночасно працюють ufw і власна таблиця правил, усе ще пропускає трафік, але активна політика тепер є об’єднанням двох наборів правил, які перевіряються в порядку, визначеному запуском сервісів. За жодним із цих файлів неможливо встановити, як саме працює сервер.
Перенесення наявного набору правил iptables
iptables-translate перетворює одне правило та виводить його форму для nftables. Команда нічого не змінює на сервері.
iptables-translate -A INPUT -p tcp --dport 22 -j ACCEPTnft add rule ip filter INPUT tcp dport 22 counter acceptiptables-restore-translate -f /etc/iptables/rules.v4 виконує те саме для всього збереженого набору правил. Розглядайте її вивід як перший варіант. Перетворення виконується механічно, правило за правилом. У результаті ви отримуєте старі назви таблиць і ланцюжків, два окремі набори правил для IPv4 та IPv6 і жодного з наборів, заради яких перехід був доцільним. Вручну перепишіть його в одну таблицю inet, а потім перевірте її за допомогою nft -c -f, перш ніж застосовувати на робочому сервері.
Адреси в цих прикладах взято з діапазонів документації 203.0.113.0/24 і 198.51.100.0/24, а enp1s0 — це назва інтерфейсу. Замість копіювання моїх значень візьміть свої з ip route show default і ip -br addr, оскільки в актуальних образах Ubuntu інтерфейс рідко називається eth0.
FAQ
Чи вважається iptables застарілим в Ubuntu?
Команда не зникає й досі працює в Ubuntu 24.04. Змінилося те, що відбувається під капотом: iptables — це інтерфейс, який записує правила nftables через бекенд iptables-nft. Перевірте свій варіант командою iptables -V. В Ubuntu 24.04 вона виводить iptables v1.8.10 (nf_tables). Старий бекенд x_tables досі постачається як iptables-legacy і має повністю окремий набір правил. Тому додавайте правила лише в один бекенд, а не в обидва.
Чи потрібне друге правило, щоб скасувати NAT у зворотному напрямку?
Ні. Відстеження з’єднань зберігає трансляцію, коли перший пакет з’єднання відповідає правилу nat. Кожен наступний пакет в обох напрямках переписується на основі цього збереженого запису. sudo conntrack -L показує це як два кортежі для кожного з’єднання: початковий напрямок, а потім уже розвернуту відповідь. Правило для зворотного напрямку не допоможе, оскільки пакети відповіді ніколи не проходять через ланцюжок nat.
Чи можна одночасно використовувати ufw і власні правила nftables?
Це працює, але створює проблему. Виконуються всі базові ланцюжки на одному hook. Тому фактична політика є поєднанням обох наборів правил. Їхній порядок визначається пріоритетом, а за однакового пріоритету — тим, який сервіс запустився першим. drop у будь-якому з них завершує обробку, а accept у вашому наборі правил не завадить іншому набору відкинути той самий пакет. Оберіть один інструмент. Якщо це nftables, спочатку вимкніть ufw і переконайтеся, що його таблиці зникли з sudo nft list ruleset.
Як зробити так, щоб правила nftables зберігалися після перезавантаження Ubuntu?
Збережіть набір правил у /etc/nftables.conf, перевірте його командою sudo nft -c -f /etc/nftables.conf, а потім виконайте sudo systemctl enable --now nftables. Сервіс не ввімкнений за замовчуванням, тому варто один раз виконати systemctl is-enabled nftables. Під час створення цього файла експортуйте лише власну таблицю за допомогою sudo nft -s list table inet filter, оскільки повний дамп list ruleset також містить таблиці, якими самостійно керують ufw і Docker.