iptables или nftables в Ubuntu: как проверить настройки
Узнайте, почему команда iptables в Ubuntu 20.04 и новее записывает правила в nftables. Проверьте бэкенд через iptables --version и найдите конфликты между ufw и Docker.
iptables против nftables в Ubuntu: что использует ваш сервер?
В Ubuntu 20.04 и более поздних версиях команда iptables является интерфейсом, который записывает правила для nftables. В ядре работает один фильтр пакетов — nftables, а две команды в пространстве пользователя управляют им. Строка 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). Имя в скобках — это бэкенд. (nf_tables) означает, что команда взаимодействует с nftables. (legacy) означает старый бэкенд x_tables, который Ubuntu по-прежнему поставляет как iptables-legacy и который ядро хранит как полностью отдельный набор правил. update-alternatives выводит символическую ссылку, стоящую за этим выбором: link currently points to /usr/sbin/iptables-nft.
На чистом VPS без настроенного межсетевого экрана 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 (network address translation) требуют iptables -t nat -L, а правила mangle — -t mangle. IPv6 использует отдельную команду, ip6tables, со своим собственным набором правил. Таким образом, список может выглядеть пустым в одном представлении, в то время как пакеты отбрасываются или перезаписываются правилами из таблицы, которую вы не проверяли.
sudo nft list ruleset выводит все семейства, таблицы, цепочки и правила в одном списке. На сервере, который настраивали не вы, эта команда — самый быстрый способ увидеть реальное состояние конфигурации. Добавьте -a для вывода дескрипторов (handles) правил; они необходимы, если нужно удалить одно конкретное правило, а не всю цепочку целиком.
Пока вы здесь, стоит выработать две полезные привычки. iptables -L пытается преобразовать адреса и порты в имена, поэтому на сервере с неисправным DNS-резолвером команда может выглядеть зависшей: используйте iptables -nvL. Также убедитесь, что устаревший бэкенд пуст с помощью sudo iptables-legacy -nvL, так как при наличии правил в обоих бэкендах ядро обрабатывает их все, и ни один список не даст вам полной картины происходящего.
Таблицы и цепочки, которые вы создаете, а не наследуете
nftables начинает работу с пустого состояния. Таблицы filter не существует, пока вы её не создадите, а слово filter — это лишь выбранное вами имя. Цепочка начинает видеть пакеты только после того, как вы зададите ей тип, хук (hook) и приоритет, что делает её базовой цепочкой (base chain). Цепочка без этих параметров доступна только через явный вызов 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;
}
}Прочитайте вторую строку дважды. flush ruleset удаляет все таблицы на сервере, включая те, что были созданы ufw и Docker. Продолжайте чтение, прежде чем запускать это на рабочем сервере.
Первое правило во входной цепочке (input chain) выполняет основную часть работы. 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 анализирует файл и сообщает об ошибках, не загружая правила. При успешном анализе вывод отсутствует.
Наборы (sets) заменяют длинные списки правил
tcp dport { 80, 443 } — это анонимный набор: одно правило и один поиск вместо одного правила на каждый порт. Именованный набор, такой как admin_ips, еще эффективнее, так как его можно изменять во время работы межсетевого экрана.
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 во все правила, которые вы планируете отлаживать в будущем.
Как хуки и приоритеты определяют порядок
Базовая цепочка (base chain) определяет хук — точку в пути прохождения пакета, где она выполняется. prerouting выполняется до принятия решения о маршрутизации. input выполняется для пакетов, адресованных данной машине. forward выполняется для пакетов, проходящих через неё транзитом. output выполняется для пакетов от локальных процессов. postrouting выполняется последним, непосредственно перед выходом пакета из системы.
Приоритет определяет порядок цепочек внутри одного хука: сначала выполняется цепочка с меньшим числом. В nftables классическим значениям присвоены имена: raw соответствует -300, mangle — -150, dstnat — -100, filter — 0, srcnat — 100. Запись priority filter; эквивалентна записи priority 0;.
Теперь о том, как это влияет на совместную работу различных инструментов. Каждая базовая цепочка, зарегистрированная на хуке, выполняется в порядке приоритета. Пакет, принятый в вашей цепочке, не завершает свой путь: accept прерывает только текущую цепочку, и пакет продолжает движение к следующей базовой цепочке на том же хуке. drop является окончательным вердиктом везде и немедленно отбрасывает пакет. Таким образом, разрешающее правило в вашей таблице не может отменить действие accept в таблице ufw, независимо от того, какая из них выполняется первой, и ваше правило accept не обеспечит защиты от цепочки, которая выполняется позже.
Две базовые цепочки на одном хуке с одинаковым приоритетом выполняются в порядке регистрации, который зависит от того, какой сервис запустился первым. Этот порядок может меняться после перезагрузки. Если вам необходимо использовать собственную таблицу параллельно с ufw, назначьте ей уникальный приоритет, чтобы порядок выполнения был зафиксирован, а не зависел от состояния гонки при загрузке.
Почему не нужно создавать правило обратного NAT?
Это вопрос, в котором чаще всего ошибаются, поэтому вот прямой ответ. Механизм отслеживания соединений (connection tracking) создает обратную трансляцию за вас. Второе правило добавлять не нужно.
Таблица 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. Если бы оно каким-то образом сработало, вы бы повторно транслировали пакет, который ядро уже исправило.
Место, где должен происходить перезапись, определяется тем же механизмом. Трансляция назначения (destination translation) должна выполняться в prerouting, до принятия решения о маршрутизации, так как маршрутизация должна видеть новый адрес назначения, иначе пакет уйдет не туда. Трафик, который генерирует сама машина, обрабатывается в хуке output по той же причине. Трансляция источника (source translation), включая перезапись исходного порта, должна выполняться в 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 и правило accept в путь пересылки (forward path), причем оба срабатывают до пользовательских цепочек 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Не используйте оба инструмента одновременно. Два файла, каждый из которых претендует на роль основного хранилища правил, неизбежно разойдутся, и результат загрузки будет непредсказуемым.
Существует также риск при выгрузке текущего набора правил. Команда sudo nft -s list ruleset > /etc/nftables.conf сохраняет всё, что загружено в данный момент, включая таблицы ufw и Docker. Если восстановить такой дамп при загрузке, вы получите «замороженную» копию правил, которые эти инструменты должны создавать динамически, а затем — вторую копию, когда они запустятся самостоятельно. Выгружайте только свою таблицу с помощью sudo nft -s list table inet filter. Флаг -s позволяет исключить счетчики, которым не место в конфигурационном файле.
Стоит ли включать ufw на вашем VPS?
Не трогайте ufw, если вам не требуется функциональность, которую этот инструмент не поддерживает. ufw справляется с типичными задачами VPS: политика запрета по умолчанию и несколько открытых портов. Замена этого решения на написанный вручную набор правил ради самого процесса лишь добавит вам лишний объект для обслуживания.
Переходите на нативные средства, когда ваши задачи выходят за рамки модели ufw: NAT и проброс портов, наборы правил, обновляемые во время выполнения, одно правило для обоих семейств адресов или самостоятельное определение приоритетов цепочек. Это веские причины, и ufw не позволяет реализовать ничего из перечисленного.
Если вы переходите на нативные средства, делайте это полностью. Выполните 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, а также отсутствие наборов (sets), ради которых и стоило переходить на новую систему. Перепишите конфигурацию вручную в виде одной таблицы 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, которая выводит iptables v1.8.10 (nf_tables) в 24.04. Старый бэкенд x_tables по-прежнему поставляется как iptables-legacy и содержит полностью независимый набор правил, поэтому используйте только один бэкенд, а не оба сразу.
Нужно ли создавать второе правило для отмены NAT при обратном трафике?
Нет. Механизм отслеживания соединений (connection tracking) сохраняет параметры трансляции, когда первый пакет соединения попадает под правило nat, и каждый последующий пакет в обоих направлениях переписывается на основе этой записи. sudo conntrack -L отображает это как два кортежа на соединение: исходное направление и уже обратный ответ. Правило, написанное для обратного направления, не поможет, так как обратные пакеты никогда не попадают в цепочку nat.
Можно ли одновременно использовать ufw и собственные правила nftables?
Это технически возможно, но создаст проблемы. Каждая базовая цепочка (base chain) на хуке выполняется, поэтому итоговая политика представляет собой комбинацию обоих наборов правил, упорядоченных по приоритету, а при равном приоритете — по времени запуска сервиса. Команда 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.