Linux-сети: от iptables к nftables
Если вы администрируете Linux-серверы, вы неизбежно столкнётесь с файрволом. Долгие годы стандартом де-факто был iptables, но ему на смену пришёл nftables. В этой статье разберём оба инструмента: как работает iptables, почему nftables лучше и как мигрировать без даунтайма.
iptables: фундамент, который нужно знать
Даже если вы планируете использовать только nftables, понимание iptables необходимо. Большинство существующих серверов, документации и Stack Overflow ответов всё ещё используют iptables. Кроме того, nftables наследует многие концепции из iptables.
Таблицы и цепочки
iptables организован в таблицы (tables), каждая из которых содержит цепочки (chains) с правилами.
Основные таблицы:
| Таблица | Назначение |
|---|---|
filter | Фильтрация пакетов (accept/drop). Таблица по умолчанию |
nat | Преобразование адресов (SNAT, DNAT, MASQUERADE) |
mangle | Модификация заголовков пакетов (TTL, TOS, MARK) |
raw | Обход connection tracking |
Основные цепочки в таблице filter:
- INPUT — входящие пакеты, адресованные серверу
- FORWARD — пакеты, проходящие через сервер (роутинг)
- OUTPUT — исходящие пакеты от сервера
Базовые команды
Посмотреть текущие правила:
# Все правила с номерами строк
sudo iptables -L -n -v --line-numbers
# Правила в таблице nat
sudo iptables -t nat -L -n -v Типичный вывод выглядит так:
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
num pkts bytes target prot opt in out source destination
1 1205 98K ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0
2 15.2M 2.1G ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
3 412 24K ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22
4 287 17K ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:80
5 193 11K ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:443
6 8431 504K DROP all -- * * 0.0.0.0/0 0.0.0.0/0 Типичная настройка файрвола
Вот минимальный набор правил для веб-сервера:
#!/bin/bash
# Сброс всех правил
sudo iptables -F
sudo iptables -X
# Политики по умолчанию
sudo iptables -P INPUT DROP
sudo iptables -P FORWARD DROP
sudo iptables -P OUTPUT ACCEPT
# Разрешить loopback
sudo iptables -A INPUT -i lo -j ACCEPT
# Разрешить установленные соединения
sudo iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
# SSH (порт 22)
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# HTTP и HTTPS
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# Разрешить ICMP (ping)
sudo iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
# Логировать отброшенные пакеты (опционально)
sudo iptables -A INPUT -j LOG --log-prefix "IPT_DROP: " --log-level 4 Проброс портов (DNAT)
Частая задача — пробросить порт с внешнего интерфейса на внутренний сервис. Например, перенаправить трафик с порта 8080 на порт 80:
# Включить IP forwarding
echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward
# DNAT: перенаправить внешний порт 8080 на локальный 80
sudo iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 127.0.0.1:80
# Разрешить форвардинг для этого трафика
sudo iptables -A FORWARD -p tcp --dport 80 -j ACCEPT Для перенаправления диапазона портов (полезно, например, для UDP-сервисов):
# Перенаправить UDP-порты 20000-40000 на порт 443
sudo iptables -t nat -A PREROUTING -p udp --dport 20000:40000 -j DNAT --to-destination :443 Rate limiting
Защита от брутфорса SSH:
# Не более 3 новых SSH-подключений за 60 секунд с одного IP
sudo iptables -A INPUT -p tcp --dport 22 -m state --state NEW
-m recent --set --name SSH
sudo iptables -A INPUT -p tcp --dport 22 -m state --state NEW
-m recent --update --seconds 60 --hitcount 4 --name SSH -j DROP Почему nftables?
iptables работает, но у него накопились проблемы:
Архитектура. Для разных протоколов нужны разные утилиты:
iptables(IPv4),ip6tables(IPv6),arptables(ARP),ebtables(Ethernet bridging). В nftables всё в одном инструменте.Производительность. iptables обрабатывает правила линейно — O(n). nftables использует множества (sets) и карты (maps), что даёт O(1) для поиска по большим спискам IP-адресов.
Синтаксис. Правила iptables — это длинные строки с кучей флагов. nftables использует более читаемый структурированный синтаксис.
Атомарные обновления. В iptables каждая команда
iptables -A— отдельный системный вызов. Между командами есть окно, когда правила в несогласованном состоянии. nftables позволяет применять набор правил атомарно.
Начиная с Debian 10 (Buster) и Ubuntu 20.04, nftables является файрволом по умолчанию. Утилита iptables во многих дистрибутивах — это уже обёртка iptables-nft, которая транслирует команды в nftables.
Проверить, что используется на вашей системе:
# Если вывод содержит "nf_tables" — используется nftables-бэкенд
sudo iptables -V
# iptables v1.8.9 (nf_tables)
# Или напрямую проверить
sudo nft list ruleset nftables: основы
Структура правил
В nftables иерархия такая: таблицы → цепочки → правила. Но в отличие от iptables, таблицы и цепочки нужно создавать явно.
Базовый конфиг файрвола:
#!/usr/sbin/nft -f
# Очистить все правила
flush ruleset
# Создать таблицу для IPv4 и IPv6
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
# Разрешить loopback
iif "lo" accept
# Разрешить установленные соединения
ct state established,related accept
# Разрешить ICMP и ICMPv6
ip protocol icmp accept
ip6 nexthdr icmpv6 accept
# SSH
tcp dport 22 accept
# HTTP и HTTPS
tcp dport { 80, 443 } accept
# Логировать и отбросить остальное
log prefix "NFT_DROP: " counter drop
}
chain forward {
type filter hook forward priority 0; policy drop;
}
chain output {
type filter hook output priority 0; policy accept;
}
} Обратите внимание: table inet обрабатывает и IPv4, и IPv6 одновременно. Больше не нужно дублировать правила.
Сравнение синтаксиса
Вот как одни и те же правила выглядят в iptables и nftables:
| Задача | iptables | nftables |
|---|---|---|
| Открыть порт 80 | iptables -A INPUT -p tcp --dport 80 -j ACCEPT | tcp dport 80 accept |
| Открыть несколько портов | iptables -A INPUT -p tcp -m multiport --dports 80,443,8080 -j ACCEPT | tcp dport { 80, 443, 8080 } accept |
| Блокировать IP | iptables -A INPUT -s 10.0.0.1 -j DROP | ip saddr 10.0.0.1 drop |
| Блокировать подсеть | iptables -A INPUT -s 10.0.0.0/24 -j DROP | ip saddr 10.0.0.0/24 drop |
| DNAT | iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to 10.0.0.2:80 | tcp dport 8080 dnat to 10.0.0.2:80 |
| Masquerade | iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE | oifname "eth0" masquerade |
| Rate limit | iptables -A INPUT -p tcp --dport 22 -m limit --limit 3/min -j ACCEPT | tcp dport 22 limit rate 3/minute accept |
NAT в nftables
Настройка NAT для сервера, выступающего шлюзом:
table inet nat {
chain prerouting {
type nat hook prerouting priority -100;
# Проброс порта 8080 -> внутренний сервер 10.0.0.2:80
tcp dport 8080 dnat to 10.0.0.2:80
# Проброс диапазона UDP-портов на один порт
udp dport 20000-40000 dnat to :443
}
chain postrouting {
type nat hook postrouting priority 100;
# Masquerade для исходящего трафика через eth0
oifname "eth0" masquerade
}
} Множества (sets) — киллер-фича nftables
В iptables для блокировки 1000 IP-адресов нужно 1000 правил (или ipset как отдельный инструмент). В nftables множества — встроенная функциональность:
table inet filter {
# Именованное множество IP-адресов
set blocked_ips {
type ipv4_addr
flags interval
elements = {
192.168.1.100,
10.0.0.0/8,
172.16.0.0/12
}
}
# Множество с автоматическим удалением элементов через таймаут
set bruteforce {
type ipv4_addr
flags dynamic,timeout
timeout 5m
}
chain input {
type filter hook input priority 0; policy drop;
# Блокировать IP из множества
ip saddr @blocked_ips drop
# Защита от брутфорса SSH
tcp dport 22 ct state new
limit rate over 3/minute
add @bruteforce { ip saddr } drop
ip saddr @bruteforce drop
# ... остальные правила
}
} Множество bruteforce динамически пополняется: если с одного IP приходит больше 3 новых SSH-подключений в минуту, адрес автоматически блокируется на 5 минут.
Добавлять и удалять элементы множества можно на лету:
# Добавить IP в множество
sudo nft add element inet filter blocked_ips { 203.0.113.50 }
# Удалить IP из множества
sudo nft delete element inet filter blocked_ips { 203.0.113.50 }
# Посмотреть содержимое множества
sudo nft list set inet filter blocked_ips Миграция с iptables на nftables
Автоматическая конвертация
В пакете iptables есть утилита iptables-translate, которая переводит правила:
$ iptables-translate -A INPUT -p tcp --dport 22 -m state --state NEW
-m recent --update --seconds 60 --hitcount 4 -j DROP
# nft add rule ip filter INPUT tcp dport 22 ct state new
# limit rate over 3/minute drop
$ iptables-translate -t nat -A PREROUTING -p udp --dport 20000:40000
-j DNAT --to-destination :443
# nft add rule ip nat PREROUTING udp dport 20000-40000 dnat to :443 Для экспорта всего набора правил:
# Экспорт текущих iptables-правил в формате nftables
sudo iptables-save | sudo iptables-restore-translate
# Сохранить в файл
sudo iptables-save | sudo iptables-restore-translate > /etc/nftables-migrated.conf Ручная миграция
Автоматическая конвертация не всегда работает идеально, особенно с нестандартными модулями. Рекомендуемый подход:
- Экспортировать текущие правила:
sudo iptables-save > /tmp/iptables-backup.rules - Написать эквивалентный nftables-конфиг
- Протестировать на staging-сервере
- Применить на продакшне с таймером безопасности
Таймер безопасности — критически важная штука. Если вы случайно заблокируете себе доступ к серверу, правила автоматически откатятся:
# Применить правила и автоматически откатить через 30 секунд,
# если не подтвердить
sudo nft -f /etc/nftables.conf &&
echo "Правила применены. Подтвердите в течение 30 секунд..." &&
sleep 30 &&
sudo nft -f /tmp/nftables-backup.conf Более элегантный вариант — использовать at:
# Запланировать откат через 5 минут
echo "nft -f /tmp/nftables-backup.conf" | sudo at now + 5 minutes
# Применить новые правила
sudo nft -f /etc/nftables.conf
# Если всё работает — отменить откат
sudo atrm $(atq | tail -1 | awk '{print $1}') Персистентность правил
Главная боль новичков: правила iptables/nftables не переживают перезагрузку. Вот как это решить.
iptables: iptables-persistent
# Установить пакет
sudo apt install iptables-persistent
# Сохранить текущие правила
sudo netfilter-persistent save
# Правила сохраняются в:
# /etc/iptables/rules.v4
# /etc/iptables/rules.v6 При перезагрузке systemd-сервис netfilter-persistent автоматически загрузит правила.
nftables: встроенный механизм
В nftables персистентность реализована проще. Правила хранятся в /etc/nftables.conf и загружаются systemd-сервисом:
# Сохранить текущие правила в конфиг
sudo nft list ruleset | sudo tee /etc/nftables.conf
# Включить автозагрузку
sudo systemctl enable nftables.service
# Проверить, что сервис активен
sudo systemctl status nftables.service Структура /etc/nftables.conf:
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
# ... ваши правила
}
}
table inet nat {
chain prerouting {
type nat hook prerouting priority -100;
# ... правила NAT
}
} Практический совет: отладка
Когда что-то не работает, а пакеты куда-то пропадают:
# nftables: включить счётчики для всех правил
sudo nft list ruleset -a
# Добавить трассировку для конкретного пакета
sudo nft add rule inet filter input
ip saddr 10.0.0.1 tcp dport 80
meta nftrace set 1
# Смотреть трассировку
sudo nft monitor trace
# tcpdump — всегда актуален
sudo tcpdump -i eth0 -n port 443
sudo tcpdump -i any -n udp port 20000-40000 Заключение
Если вы настраиваете новый сервер — используйте nftables. Синтаксис чище, множества работают из коробки, один инструмент для IPv4/IPv6, атомарные обновления правил.
Если у вас legacy-инфраструктура на iptables — не спешите мигрировать ради миграции. Но при следующей серьёзной переработке файрвола имеет смысл перейти на nftables. Утилита iptables-translate поможет с конвертацией, а iptables-nft обеспечит обратную совместимость на переходный период.
Главное — не забывайте сохранять правила и тестировать с таймером безопасности. Потерять SSH-доступ к продакшн-серверу из-за опечатки в правиле файрвола — опыт, который хочется пережить не более одного раза.