Файрвол на хосте - это не дополнительная опция, а базовый рубеж защиты в глубину. Даже если служба слушает только localhost, а SELinux в enforcing, грамотный набор правил фильтрации отсекает целые классы атак до того, как пакет дойдёт до приложения. В этом уроке мы разбираем nftables - единый фреймворк фильтрации, который в 2026 году стоит бэкендом по умолчанию во всех актуальных дистрибутивах (Debian 13, Ubuntu 24.04 LTS, RHEL 10, Fedora 41+). Поймём анатомию: таблицы, цепочки, хуки, приоритеты, наборы (sets), stateful-фильтрацию, NAT и логирование. iptables рассмотрим как наследие и слой совместимости, ebtables - для фильтрации на мосту.

Как это работает
В ядре Linux весь сетевой трафик проходит через подсистему Netfilter. У неё есть фиксированные точки - хуки - в которых пакет можно перехватить: prerouting (сразу после приёма), input (пакет адресован хосту), forward (пакет идёт транзитом), output (рождён на хосте), postrouting (перед отправкой). nftables - это пользовательский язык и движок поверх этих хуков. Старые iptables/ip6tables/arptables/ebtables дублировали логику в четырёх отдельных движках; nftables объединил их в один.
Иерархия в nftables строгая. На верхнем уровне - таблица (table), у неё есть семейство адресов: ip, ip6, inet (оба сразу, рекомендуется), arp, bridge, netdev. Внутри таблицы живут цепочки (chain). Цепочка бывает базовой - тогда у неё указан тип (filter, nat, route), хук и приоритет, и она реально цепляется к Netfilter. Или регулярной - тогда это просто контейнер правил, в который прыгают через jump/goto.
Приоритет цепочки - число со знаком; чем меньше, тем раньше отработает цепочка на одном хуке. Это позволяет на одном хуке prerouting сначала сделать DNAT (priority -100, тип nat), а потом фильтровать (priority 0, тип filter). Стандартные имена приоритетов: raw (-300), mangle (-150), dstnat (-100), filter (0), srcnat (100). Понимание порядка критично: если фильтрующая цепочка стоит до DNAT, вы фильтруете по старому адресу назначения.
Stateful-фильтрация - сердце современного файрвола. Модуль conntrack отслеживает состояние соединений. Вместо того чтобы открывать порты в обе стороны, вы один раз разрешаете established,related, и ответный трафик на любые исходящие соединения проходит автоматически. Это и безопаснее (не надо держать порты открытыми), и быстрее (решение по соединению кешируется).
Наборы (sets) - киллер-фича nftables. Вместо сотни однотипных правил вы держите один набор адресов или портов и проверяете вхождение за O(1). Наборы бывают анонимные (инлайн в правиле) и именованные. Именованные могут быть динамическими (flag dynamic) - в них правило само добавляет элементы с таймаутом, что даёт встроенный rate-limit и защиту от брутфорса без fail2ban.
Команды и примеры
Установка инструментов и службы. На Debian/Ubuntu правила хранит служба nftables, на RHEL/Fedora обычно поверх стоит firewalld, но можно работать и с чистым nftables.
Код: Выделить всё
# Debian 13 / Ubuntu 24.04
apt install nftables
systemctl enable --now nftables
# RHEL 10 / Fedora 41
dnf install nftables
systemctl disable --now firewalld # если нужен чистый nftables
systemctl enable --now nftablesКод: Выделить всё
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
set ssh_bf {
type ipv4_addr
flags dynamic, timeout
timeout 1h
}
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
ip6 nexthdr icmpv6 accept
# защита SSH от брутфорса через динамический набор
tcp dport 22 ct state new \
add @ssh_bf { ip saddr limit rate 6/minute } accept
tcp dport { 80, 443 } accept
limit rate 5/minute log prefix "nft-drop: " counter
}
chain forward { type filter hook forward priority filter; policy drop; }
chain output { type filter hook output priority filter; policy accept; }
}Код: Выделить всё
nft -c -f /etc/nftables.conf # -c только проверить
nft -f /etc/nftables.conf # применить (flush + загрузка атомарно)
nft list ruleset # показать активный набор
nft -a list table inet filter # с handle для точечного удаления
nft delete rule inet filter input handle 14Код: Выделить всё
table inet nat {
chain prerouting { type nat hook prerouting priority dstnat; }
chain postrouting { type nat hook postrouting priority srcnat;
oifname "eth0" masquerade
}
}
# проброс внешнего 8443 на внутренний веб
nft add rule inet nat prerouting iif "eth0" tcp dport 8443 \
dnat ip to 10.0.0.5:443Код: Выделить всё
# дамп текущих iptables-правил в nftables-синтаксис
iptables-save > old.rules
iptables-restore-translate -f old.rules > /etc/nftables-migrated.confКод: Выделить всё
iptables -V # должно показать (nf_tables), а не (legacy)
update-alternatives --config iptables # Debian: переключить вариантФильтрация на мосту (L2). Когда хост работает мостом (виртуализация, контейнеры), фильтровать надо по MAC и до маршрутизации. Наследие - ebtables; в 2026 это семейство bridge в nftables:
Код: Выделить всё
table bridge filter {
chain forward { type filter hook forward priority filter;
ether saddr aa:bb:cc:dd:ee:ff drop
}
}- Политика цепочки input стоит drop, а правило для established,related забыли поставить первым - в итоге обрываются ответы на собственные исходящие соединения, в том числе apt и dnf.
- Заблокировали loopback (iif lo accept отсутствует) - ломаются локальные сокеты, журналы, иногда systemd-resolved.
- Перепутали приоритеты: фильтрующая цепочка отработала до DNAT, и пакет режется по внешнему адресу назначения вместо внутреннего.
- Параллельно работают firewalld и своя служба nftables - правила перетирают друг друга при перезагрузке. Выберите один фронтенд.
- Меняли правила вживую через nft add без сохранения в файл - после reboot всё пропало, потому что persistency обеспечивает именно служба, читающая конфиг.
- Смешали legacy iptables и iptables-nft на одной машине: пакеты видны в одном движке, невидимы в другом, отладка превращается в ад. Держите один вариант (проверка iptables -V).
- Логирование без limit rate - один скан забивает журнал гигабайтами и сам становится DoS.
- На тестовой ВМ сохраните текущие правила: nft list ruleset > /root/fw.bak.
- Напишите /etc/nftables.conf с таблицей inet filter, политикой drop на input и разрешением established,related, lo, icmp и SSH - проверьте сначала через nft -c -f.
- Примените конфиг и убедитесь, что SSH-сессия не оборвалась, а ping наружу работает (established).
- Добавьте именованный динамический набор ssh_bf с rate-limit 6/minute на новые SSH-соединения; с другой машины сделайте 10 быстрых подключений и найдите свой адрес в nft list set inet filter ssh_bf.
- Поднимите второй интерфейс, настройте masquerade в таблице nat и проверьте выход в сеть с внутреннего узла.
- Включите логирование дропов с limit rate 5/minute и посмотрите записи через journalctl -k -g nft-drop.
- Сравните: выполните iptables -V и убедитесь, что это nf_tables, а не legacy.
- Чем базовая цепочка отличается от регулярной и какие три параметра обязательны для базовой?
- Зачем нужен приоритет цепочки и в каком порядке отработают цепочки nat (dstnat) и filter на хуке prerouting?
- Какое одно правило заменяет десятки правил разрешения ответного трафика и за счёт какого механизма ядра?
- В чём преимущество семейства inet перед раздельными ip и ip6 при написании правил?
- Что на самом деле выполняется, когда вы вводите команду iptables на RHEL 10, и почему важно проверить вывод iptables -V?
- Как с помощью динамического набора (set) реализовать защиту от брутфорса SSH без внешних утилит вроде fail2ban?