Зачем выбирать
В Linux в 2026 четыре актуальных способа управлять netfilter:
| Тулинг | Уровень | Где default |
|---|---|---|
| cmd-iptables | низкий, legacy | Ubuntu LTS, embedded, Docker (всё ещё) |
| cmd-nft | низкий, modern | Debian 11+, Ubuntu 22+, прямой kernel-API |
| firewalld | высокий | RHEL/CentOS/Fedora server |
| ufw | высокий | Ubuntu desktop |
iptables vs nftables, техническая дилемма (legacy vs modern, rules-replay vs atomic). firewalld vs чистый nft, архитектурная дилемма: оркестрируешь ты сам или daemon. ufw в этом сравнении упрощённый firewalld для Ubuntu.
firewalld, что это
Daemon, который читает XML-конфиги и применяет их к netfilter
через бэкенд (с RHEL 8, nftables, до того, iptables):
/usr/share/firewalld/ ← дистрибуция-default
/etc/firewalld/ ← кастомизация
├─ firewalld.conf ← глобальный конфиг
├─ zones/ ← XML, описывающие zones
│ ├─ public.xml
│ ├─ trusted.xml
│ └─ work.xml
├─ services/ ← XML, описывающие services
│ ├─ ssh.xml
│ └─ http.xml
└─ icmptypes/
Команды через firewall-cmd:
firewall-cmd --state # запущен ли
firewall-cmd --get-active-zones
firewall-cmd --get-default-zone
firewall-cmd --list-all # текущая зона детально
# Открыть HTTP постоянно
firewall-cmd --permanent --add-service=http
firewall-cmd --reload
# Произвольный порт
firewall-cmd --permanent --add-port=8080/tcp
# Удалить
firewall-cmd --permanent --remove-service=http
--permanent пишет в XML; без него, runtime-only (потеряется при
reload). Часто делают: сначала без --permanent для теста, потом
--runtime-to-permanent для commit.
Zones, концепция firewalld
Каждый сетевой интерфейс / source-IP принадлежит зоне. Зона это уровень доверия + список разрешённых services/ports.
Дефолтные зоны:
| Zone | По умолчанию |
|---|---|
| drop | всё drop, нет ответов |
| block | всё reject (с ICMP unreachable) |
| public | default; разрешён ssh, dhcpv6-client |
| external | для NAT, MASQUERADE включён |
| dmz | разрешён ssh; на внешний интерфейс к DMZ-машинам |
| work | разрешено ssh, dhcpv6-client, mdns |
| home | + samba, mdns; для дом-сети |
| internal | как home |
| trusted | всё разрешено |
Привязка интерфейса:
firewall-cmd --zone=trusted --change-interface=eth1 --permanent
Привязка source-IP:
firewall-cmd --zone=trusted --add-source=10.0.0.0/24 --permanent
Источники приоритетнее интерфейсов. Если src попадает в зону X трафик идёт по правилам X, неважно какой интерфейс.
Это сильная сторона firewalld: один и тот же сервер может говорить разные правила разным сетям без сложных rule-cascade.
Rich rules, кастомизация в firewalld
Когда services/ports мало, есть rich-rules, DSL для сложных правил:
firewall-cmd --permanent --add-rich-rule='
rule family="ipv4"
source address="10.0.0.0/24"
service name="http"
log prefix="http-from-internal: " level="info"
accept'
firewall-cmd --permanent --add-rich-rule='
rule family="ipv4"
source address="1.2.3.4"
drop'
firewall-cmd --permanent --add-rich-rule='
rule
service name="ssh"
accept
limit value="3/m"' # rate-limit
Удобно: ipset-style, rate-limit, log, accept/reject/drop. Под капотом всё-равно компилируется в nftables.
Чистый nft, Pro
Прямой контроль:
# /etc/nftables.conf
table inet filter { set blocked_ips {type ipv4_addr
flags interval
elements = { 1.2.3.0/24, 5.6.7.8 }}
chain input {type filter hook input priority filter; policy drop;
ct state established,related accept
iif lo accept
ip saddr @blocked_ips drop
tcp dport { 22, 80, 443 } acceptip protocol icmp accept
counter log prefix "DROPPED: " drop
}
chain forward {type filter hook forward priority filter; policy drop;
}
}
Применить:
nft -f /etc/nftables.conf
systemctl enable --now nftables
Преимущества по сравнению с firewalld:
- Atomic reload, весь файл за одну транзакцию, без race-window
- Sets и maps, нативно (vlan-id → action, ip → action)
- Один конфиг в git, версионируемый
- Меньше абстракций, что написал, то и в kernel
- Counters/log инлайн, без дополнительной обвязки
- Меньше overhead, нет daemon'а
Недостатки:
- Нет zones/services-абстракции, всё руками
- Нет API для динамической перерисовки (каждое изменение, file edit + reload)
- Сложнее для junior'ов
Когда что выбрать
firewalld:
- Desktop / laptop с разными сетями (home/work/coffee-shop)
- Multi-tenant сервер с зонами по доверию
- RHEL прод, где админы привычны к firewall-cmd
- Нужна интеграция с NetworkManager (NM zones)
- Динамические правила через API/D-Bus от приложений
Чистый nftables:
- Server fleet с config-management (Ansible/Puppet)
- Один конфиг для group of servers
- Performance-чувствительные нагрузки (gateway, k8s-nodes)
- Сложные rule-sets с sets/maps/vmaps
- Кастомные NAT/mangle правила
iptables-legacy:
- Только если что-то старое не работает с nft
- Docker до сих пор пишет правила через iptables, на нём оставляем
ufw:
- Простой случай "разрешить SSH, разрешить HTTPS, всё остальное drop" на одной машине; для большего, firewalld или nft
Миграция firewalld → nft
firewall-cmd --runtime-to-permanent # зафиксировать всё в XML
systemctl stop firewalld
systemctl disable firewalld
systemctl mask firewalld
nft list ruleset > /etc/nftables.conf # экспорт
# Подчистить, добавить header, пропустить .data {}, .options {}systemctl enable --now nftables
Проверь, что всё открыто как было: nft list ruleset.
firewalld backend toggle
Можно временно вернуть iptables-backend если nft что-то ломает:
# /etc/firewalld/firewalld.conf
FirewallBackend=iptables # default, nftables
systemctl restart firewalld
Использовать только если конкретный кейс ломается на nftables-backend.
Когда что-то пошло не так
firewall-cmd: not running, сервис не запущен.systemctl start firewalld. Проверь не masked ли (systemctl unmask firewalld).- Изменения исчезают после reload, забыл
--permanent. Service conflicts, другая firewall-утилита (iptables-services, ufw, nftables.service) уже живёт. Mask лишние.- NAT не работает в external zone, masquerade default включён только
в external. В public,
firewall-cmd --zone=public --add-masquerade. nft: Could not process rule: No such file or directorysyntax-error в конфиге; nftables-services не загружается. Проверьnft -c -f /etc/nftables.conf(dry-run).- Docker сломался после nft restart, Docker пишет свои правила
в iptables/nft при старте. Перезапусти
systemctl restart dockerпосле firewall'а. - Открыл порт, но не работает, забыл
firewall-cmd --reloadпосле--permanent. Либо неправильная zone (проверь--get-active-zones).
Сравнительная таблица
| Признак | firewalld | nftables (чистый) | cmd-iptables | ufw |
|---|---|---|---|---|
| Стиль | declarative XML + CLI | declarative file | imperative CLI | declarative CLI |
| Backend | nftables (default) или iptables | nftables (kernel) | iptables (kernel xt_) | iptables |
| Zones | да | нет | нет | нет |
| Atomic reload | через restart | да | нет | нет |
| Rich rules / DSL | rich-rules | nft DSL | shell-only | rules.before |
| API / D-Bus | да | нет | нет | нет |
| Runtime vs persistent | разделено | одно | руками iptables-save | одно |
| Default в | RHEL/Fedora | Debian/Ubuntu modern | embedded, Alpine, legacy | Ubuntu desktop |
| Сложность | средняя | низкая (если знаешь nft) | средняя | очень низкая |