nftables для продакшена: sets, maps, rate limiting, fail2ban и защита от DDoS на Linux-сервере
Полное руководство по настройке nftables для продакшен-серверов: sets и maps для фильтрации, динамические denylist-ы, rate limiting через meters, интеграция с fail2ban, DDoS-защита, geo-blocking и миграция с iptables — с рабочими примерами.

Если вы до сих пор пишете правила iptables вручную — ну, как бы помягче это сказать — вы работаете с устаревшим инструментом. Начиная с 2022 года все основные дистрибутивы — Ubuntu 22.04+, Debian 11+, RHEL 9+, AlmaLinux 9+, Fedora 35+ — перешли на nftables как стандартную подсистему фильтрации пакетов. Та самая команда iptables, которую вы запускаете на современных системах, — это на самом деле iptables-nft, обёртка совместимости, транслирующая старый синтаксис в правила nftables.
Но переход на nftables — это не просто смена синтаксиса. Это качественно другой подход к управлению сетевой безопасностью.
Sets, maps, meters, конкатенированные совпадения, атомарные обновления правил — всё это делает nftables не просто заменой iptables, а полноценной платформой для построения продакшен-файрвола. Честно говоря, после того как я настроил пару серверов через нативный nftables, возвращаться к цепочкам iptables уже совершенно не хочется.
В этой статье мы не будем повторять базовые команды nft add rule. Вместо этого соберём полную конфигурацию для боевого сервера — с динамическими чёрными списками, rate limiting по source IP, интеграцией с fail2ban и защитой от DDoS-атак. Все примеры рабочие — можно скопировать и адаптировать под себя.
Архитектура nftables: что нужно знать перед настройкой
Прежде чем писать правила, стоит разобраться в фундаментальных отличиях архитектуры nftables от iptables. Это не косметические изменения — они определяют, как вы будете проектировать файрвол.
Иерархия: семейство → таблица → цепочка → правило
В iptables таблицы filter, nat, mangle были предопределены ядром. В nftables — ничего не существует по умолчанию. Вы сами создаёте таблицы, даёте им произвольные имена и назначаете семейство адресации.
Семейство inet — самый практичный выбор для серверов. Оно объединяет IPv4 и IPv6, позволяя описать правила один раз для обоих протоколов. Не нужно дублировать конфигурацию между iptables и ip6tables — а это, между прочим, одна из самых частых ошибок в продакшене, когда IPv4 защищён, а IPv6 остаётся полностью открытым.
# Основные семейства nftables:
# inet — IPv4 + IPv6 (рекомендуется для серверов)
# ip — только IPv4
# ip6 — только IPv6
# netdev — обработка на уровне устройства (ingress/egress)
# bridge — мостовые кадры
Цепочки: базовые и обычные
Базовые цепочки подключаются к хукам netfilter (input, forward, output, prerouting, postrouting). Обычные — вызываются из правил, примерно как пользовательские цепочки в iptables, только гибче.
Каждая базовая цепочка требует три параметра: тип (filter, nat, route), хук (точка перехвата в стеке netfilter) и приоритет (порядок обработки — чем меньше число, тем раньше выполняется).
Атомарные обновления
В iptables каждое изменение правила заставляло ядро пересоздавать весь набор. В nftables правила компилируются в байткод и выполняются виртуальной машиной ядра. Команда nft -f загружает конфигурацию атомарно — либо все изменения применяются успешно, либо остаётся предыдущая конфигурация. Никаких промежуточных состояний, когда половина правил на месте, а вторая ещё нет. Это, пожалуй, одно из самых важных улучшений с точки зрения надёжности.
Базовый каркас продакшен-конфигурации
Итак, начнём со скелета конфигурации /etc/nftables.conf, который будем постепенно наращивать. Политика по умолчанию — drop для входящего и транзитного трафика, accept для исходящего.
#!/usr/sbin/nft -f
# Очистка текущих правил для атомарной перезагрузки
flush ruleset
table inet firewall {
# === SETS ===
# Доверенные сети для управления
set trusted_networks {
type ipv4_addr
flags interval
elements = {
10.0.0.0/8,
172.16.0.0/12,
192.168.0.0/16
}
}
# Разрешённые TCP-порты
set allowed_tcp_ports {
type inet_service
elements = { 22, 80, 443 }
}
# === ЦЕПОЧКА INPUT ===
chain input {
type filter hook input priority filter; policy drop;
# Loopback — всегда разрешаем
iif "lo" accept
# Установленные и связанные соединения
ct state established,related accept
# Невалидные пакеты — сразу сбрасываем
ct state invalid counter drop
# ICMP — разрешаем с ограничением
ip protocol icmp icmp type {
echo-request,
destination-unreachable,
time-exceeded
} limit rate 10/second accept
# ICMPv6 — необходим для работы IPv6
ip6 nexthdr icmpv6 icmpv6 type {
echo-request,
destination-unreachable,
nd-neighbor-solicit,
nd-neighbor-advert,
nd-router-solicit,
nd-router-advert
} accept
# Разрешённые TCP-порты
tcp dport @allowed_tcp_ports accept
# Логирование отброшенных пакетов (с ограничением)
limit rate 5/minute burst 10 packets \
log prefix "[nft-drop] " level warn
}
# === ЦЕПОЧКА FORWARD ===
chain forward {
type filter hook forward priority filter; policy drop;
# По умолчанию запрещаем весь транзит
# Раскомментируйте для Docker/контейнеров:
# ct state established,related accept
# iifname "docker0" accept
}
# === ЦЕПОЧКА OUTPUT ===
chain output {
type filter hook output priority filter; policy accept;
# Исходящий трафик разрешён
}
}
Этот каркас уже вполне рабочий: сервер принимает SSH, HTTP и HTTPS, отвечает на пинг с ограничением частоты, сбрасывает невалидные пакеты и логирует отброшенный трафик. Но это только основа — дальше будет интереснее.
Sets и Maps: эффективная фильтрация без дублирования правил
Sets — одна из ключевых возможностей, ради которых стоит переходить на nftables. Вместо написания отдельного правила для каждого IP-адреса или порта, вы группируете их в set и ссылаетесь одним правилом. Внутри sets используются хэш-таблицы, поэтому поиск занимает O(1) вне зависимости от количества элементов.
Set из 10 000 IP-адресов работает так же быстро, как set из 10. Это не преувеличение.
Именованные sets
Мы уже видели базовые sets в каркасе выше. Вот более продвинутые примеры.
# Set с интервалами — для блокировки подсетей
set blocked_ranges {
type ipv4_addr
flags interval
elements = {
45.155.205.0/24, # Известные ботнеты
185.220.101.0/24, # Tor exit nodes (если нужно)
193.142.146.0/24 # Сканеры
}
}
# Set портов для мониторинга (только из доверенных сетей)
set monitoring_ports {
type inet_service
elements = { 9090, 9100, 3000 } # Prometheus, Node Exporter, Grafana
}
# Правила использования sets
chain input {
# ...
ip saddr @blocked_ranges counter drop
ip saddr @trusted_networks tcp dport @monitoring_ports accept
}
Maps для маршрутизации решений
Maps идут дальше sets — они сопоставляют ключ со значением. Это позволяет реализовать довольно сложную логику буквально одним правилом.
# Map: порт → действие (verdict map)
map port_policy {
type inet_service : verdict
elements = {
22 : accept,
80 : accept,
443 : accept,
8080 : drop,
3306 : drop
}
}
# Map: IP-адрес → VLAN-маркировка (data map)
map client_mark {
type ipv4_addr : mark
flags interval
elements = {
10.0.1.0/24 : 100,
10.0.2.0/24 : 200,
10.0.3.0/24 : 300
}
}
# Применение verdict map — одно правило заменяет десять
chain input {
tcp dport vmap @port_policy
}
Verdict maps особенно полезны, когда на одном сервере крутится множество сервисов. Вместо десятка правил «если порт X — accept, если порт Y — drop» получается одна таблица, которую легко читать и модифицировать.
Конкатенированные совпадения
А вот это — одна из моих любимых фич. nftables позволяет объединять несколько полей в одно совпадение, что значительно мощнее отдельных фильтров по IP и порту.
# Set с конкатенацией: IP + порт
set service_access {
type ipv4_addr . inet_service
elements = {
10.0.1.5 . 3306, # MySQL только с этого IP
10.0.1.10 . 6379, # Redis только с этого IP
10.0.2.0/24 . 5432 # PostgreSQL только из этой подсети
}
}
chain input {
ip saddr . tcp dport @service_access accept
}
Одно правило — и каждый сервис доступен строго с разрешённых адресов. Попробуйте реализовать это в iptables без ipset — получите с десяток правил, и это ещё в лучшем случае.
Динамические sets и meters: автоматическая защита без внешних утилит
Динамические sets — пожалуй, самый мощный инструмент для автоматической реакции на аномалии. В отличие от статических sets, их элементы добавляются и удаляются самими правилами nftables в реальном времени. Никаких внешних скриптов — всё на уровне ядра.
Автоматический denylist с таймаутом
Давайте создадим set, который автоматически блокирует IP-адреса, устанавливающие слишком много соединений:
# Динамический set для временных банов
set denylist {
type ipv4_addr
flags dynamic, timeout
timeout 15m # Бан на 15 минут по умолчанию
}
# Set для отслеживания скорости подключений
set conn_rate {
type ipv4_addr
flags dynamic, timeout
timeout 2m
}
chain input {
# Проверка denylist — первым правилом!
ip saddr @denylist counter drop
# Автоматический бан при >20 новых соединений в минуту
ct state new update @conn_rate {
ip saddr limit rate over 20/minute
} add @denylist { ip saddr } counter drop
}
Как это работает: каждое новое соединение проверяется через meter conn_rate. Если с одного IP поступает более 20 новых соединений в минуту, этот IP автоматически попадает в denylist и блокируется на 15 минут. По истечении таймаута — автоматическое удаление. Никаких скриптов, никаких cron-задач.
Двойной denylist для эскалации банов
Более продвинутый паттерн — эскалация наказания для повторных нарушителей. На практике это оказывается невероятно эффективным:
# Первый уровень — быстрый бан на 10 минут
set denylist_temp {
type ipv4_addr
flags dynamic, timeout
timeout 10m
size 65536
}
# Второй уровень — длительный бан на 4 часа для рецидивистов
set denylist_persist {
type ipv4_addr
flags dynamic, timeout
timeout 4h
size 65536
}
# Счётчики для мониторинга эффективности
counter cnt_temp_blocked { }
counter cnt_persist_blocked { }
chain input {
# Сначала проверяем длительный бан
ip saddr @denylist_persist counter name cnt_persist_blocked drop
# Затем — временный бан
ip saddr @denylist_temp counter name cnt_temp_blocked drop
# Если IP попал во временный бан повторно — переводим в persist
ct state new ip saddr @denylist_temp \
add @denylist_persist { ip saddr } drop
# Rate limiting с автоматическим добавлением во временный бан
ct state new update @conn_rate {
ip saddr limit rate over 15/minute
} add @denylist_temp { ip saddr } drop
}
Итого: первое нарушение — бан на 10 минут. Попался снова — на 4 часа. Проверить, насколько хорошо это работает, можно простой командой:
# Просмотр счётчиков
nft list counters table inet firewall
# Пример вывода:
# counter cnt_temp_blocked { packets 12847 bytes 770820 }
# counter cnt_persist_blocked { packets 368198 bytes 21603012 }
# Просмотр содержимого denylist
nft list set inet firewall denylist_temp
nft list set inet firewall denylist_persist
Rate limiting SSH по source IP с meters
Meters — это специализированная форма динамических sets, заточенная под per-IP rate limiting:
# Ограничение SSH: максимум 3 новых подключения в минуту с одного IP
chain input {
tcp dport 22 ct state new meter ssh_meter {
ip saddr limit rate 3/minute burst 5 packets
} accept
# Всё, что превысило лимит — блокируем и логируем
tcp dport 22 ct state new log prefix "[ssh-brute] " \
add @denylist_temp { ip saddr } drop
}
# Ограничение HTTP/HTTPS: максимум 50 запросов в секунду с одного IP
chain input {
tcp dport { 80, 443 } ct state new meter http_meter {
ip saddr limit rate 50/second burst 100 packets
} accept
}
Обратите внимание на параметр burst: он задаёт размер «ведра токенов» (token bucket) и определяет допустимый кратковременный всплеск. Для SSH разумно выставить 5 — этого хватит, чтобы открыть несколько сессий подряд, но перебор уже будет заблокирован. Для HTTP — 100, чтобы не мешать нормальной загрузке страниц с кучей ресурсов.
Интеграция nftables с fail2ban
Динамические sets отлично справляются с flood-атаками, но для более сложных паттернов — неудачных авторизаций SSH, ошибок аутентификации веб-приложений, подбора URL — нужен анализ логов. И тут на сцену выходит fail2ban.
Настройка fail2ban для работы с nftables
Fail2ban начиная с версии 0.11.2 нативно поддерживает nftables. Настройка довольно прямолинейная — редактируем /etc/fail2ban/jail.local:
# /etc/fail2ban/jail.local
[DEFAULT]
# Используем nftables вместо iptables
banaction = nftables-multiport
banaction_allports = nftables-allports
chain = input
# Глобальные параметры
bantime = 1h
findtime = 10m
maxretry = 5
ignoreip = 127.0.0.1/8 ::1 10.0.0.0/8
# Инкрементальные баны — наказание растёт с каждым нарушением
bantime.increment = true
bantime.factor = 2
bantime.maxtime = 1w
bantime.overalljails = true
[sshd]
enabled = true
port = 22
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
findtime = 5m
bantime = 2h
[sshd-aggressive]
enabled = true
port = 22
filter = sshd[mode=aggressive]
logpath = /var/log/auth.log
maxretry = 1
findtime = 1d
bantime = 1w
# Рецидивисты — бан на всех портах
[recidive]
enabled = true
banaction = nftables-allports
filter = recidive
logpath = /var/log/fail2ban.log
maxretry = 3
findtime = 1d
bantime = 4w
Создание выделенной таблицы для fail2ban
По умолчанию fail2ban создаёт собственную таблицу nftables. Для лучшего контроля можно задать приоритет, чтобы правила fail2ban обрабатывались до основных правил файрвола:
# /etc/fail2ban/action.d/nftables-common.local
[Init]
# Приоритет -10 — обработка до основной таблицы (priority 0)
nftables_family = inet
nftables_table = f2b-table
nftables_chain = f2b-chain
chain = input
blocktype = drop
Зависимость systemd: автоматический перезапуск
Есть нюанс, о котором часто забывают: если файрвол перезагружается, правила fail2ban исчезают. Решается привязкой fail2ban к nftables через systemd:
# /etc/systemd/system/fail2ban.service.d/override.conf
[Unit]
Requires=nftables.service
After=nftables.service
PartOf=nftables.service
# Перечитать конфигурацию systemd
# systemctl daemon-reload
# systemctl restart fail2ban
Теперь при рестарте nftables fail2ban автоматически перезапустится и пересоздаст свои правила. Мелочь, а без неё рано или поздно столкнётесь с неприятным сюрпризом.
Проверка работы связки
# Статус fail2ban
fail2ban-client status sshd
# Пример вывода:
# Status for the jail: sshd
# |- Filter
# | |- Currently failed: 2
# | |- Total failed: 847
# | \`- File list: /var/log/auth.log
# \`- Actions
# |- Currently banned: 5
# |- Total banned: 312
# \`- Banned IP list: 203.0.113.50 198.51.100.23 ...
# Просмотр правил nftables, созданных fail2ban
nft list table inet f2b-table
# Ручное управление банами
fail2ban-client set sshd banip 203.0.113.100
fail2ban-client set sshd unbanip 203.0.113.100
Защита от DDoS: нативные возможности nftables
Сразу оговорюсь: nftables не заменит полноценное DDoS-митигирование на уровне провайдера или CDN. Но для защиты от низкоуровневых volumetric-атак и SYN-флудов нативные механизмы nftables работают на удивление хорошо — и при этом практически не нагружают CPU.
SYN flood protection
# Выделенная цепочка для DDoS-митигирования
chain ddos_protection {
# SYN flood — ограничиваем новые SYN-пакеты
tcp flags syn limit rate over 50/second burst 100 packets \
counter drop
# SYN-пакеты без ct state new — аномалия
tcp flags syn ct state != new counter drop
# Фрагментированные пакеты — часто используются в атаках
ip frag-off & 0x1fff != 0 counter drop
# XMAS-пакеты (все флаги TCP установлены)
tcp flags & (fin|syn|rst|psh|ack|urg) == fin|syn|rst|psh|ack|urg \
counter drop
# NULL-пакеты (ни одного флага)
tcp flags & (fin|syn|rst|psh|ack|urg) == 0x0 counter drop
# Невалидные комбинации флагов
tcp flags syn,fin counter drop
tcp flags syn,rst counter drop
}
chain input {
type filter hook input priority filter; policy drop;
iif "lo" accept
ct state established,related accept
ct state invalid counter drop
# Вызов цепочки DDoS-защиты
jump ddos_protection
# ... остальные правила
}
Conntrack tuning для высокой нагрузки
При серьёзном трафике таблица conntrack может переполниться — и тогда начнут отбрасываться вообще все новые соединения. Вот оптимизация параметров ядра, которая спасала меня не раз:
# /etc/sysctl.d/99-nftables-hardening.conf
# Максимальное число отслеживаемых соединений
net.netfilter.nf_conntrack_max = 1048576
# Уменьшение таймаутов для быстрого освобождения ресурсов
net.netfilter.nf_conntrack_tcp_timeout_established = 3600
net.netfilter.nf_conntrack_tcp_timeout_time_wait = 30
net.netfilter.nf_conntrack_tcp_timeout_close_wait = 15
net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 30
# SYN cookies — защита от SYN-флуда
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 65536
# Размер хэш-таблицы conntrack
net.netfilter.nf_conntrack_buckets = 262144
# Применить: sysctl -p /etc/sysctl.d/99-nftables-hardening.conf
Per-IP connection limiting
# Ограничение одновременных соединений с одного IP
chain input {
# SSH — максимум 3 одновременных соединения
tcp dport 22 meter ssh_connlimit {
ip saddr ct count over 3
} counter reject with tcp reset
# HTTP/HTTPS — максимум 100 одновременных соединений
tcp dport { 80, 443 } meter http_connlimit {
ip saddr ct count over 100
} counter drop
}
Geo-blocking: блокировка по странам через sets
nftables поддерживает sets с флагом interval, что позволяет загружать тысячи IP-диапазонов без потери производительности. На этом строится geo-blocking.
Подготовка geo-данных
#!/bin/bash
# geo-update.sh — скрипт загрузки geo-IP списков
GEOIP_DIR="/etc/nftables/geoip"
mkdir -p "$GEOIP_DIR"
# Загрузка актуальных данных (пример для ipdeny.com)
for country in cn kp ir; do
curl -sS "https://www.ipdeny.com/ipblocks/data/aggregated/${country}-aggregated.zone" \
-o "${GEOIP_DIR}/${country}.zone"
done
# Формирование set-файла для nftables
echo "define geo_blocked = {" > "${GEOIP_DIR}/blocked.nft"
for zone_file in "${GEOIP_DIR}"/*.zone; do
while IFS= read -r cidr; do
[ -n "$cidr" ] && echo " ${cidr},"
done < "$zone_file"
done >> "${GEOIP_DIR}/blocked.nft"
echo "}" >> "${GEOIP_DIR}/blocked.nft"
Использование в конфигурации nftables
# В основном файле /etc/nftables.conf
include "/etc/nftables/geoip/blocked.nft"
table inet firewall {
set geo_blocked_ips {
type ipv4_addr
flags interval
elements = $geo_blocked
}
chain input {
# Блокировка geo-IP — одним правилом
ip saddr @geo_blocked_ips counter drop
# ...
}
}
Автоматическое обновление через cron
# /etc/cron.weekly/geoip-update
#!/bin/bash
/usr/local/bin/geo-update.sh && \
nft -f /etc/nftables.conf && \
logger "nftables: geo-IP lists updated successfully"
Благодаря флагу interval и хэш-таблицам, set из 50 000 CIDR-диапазонов добавляет менее 1 мкс задержки на пакет. Это пренебрежимо мало — можно не беспокоиться о производительности.
Миграция с iptables: пошаговый план
Если у вас работающий продакшен с iptables, миграция требует аккуратности. Вот проверенный план, который позволяет перейти без простоя.
Шаг 1: экспорт и трансляция существующих правил
# Сохранение текущих правил iptables
iptables-save > /root/iptables-backup.rules
ip6tables-save > /root/ip6tables-backup.rules
# Трансляция в синтаксис nftables
iptables-restore-translate -f /root/iptables-backup.rules \
> /root/nftables-translated.nft
# Просмотр результата
cat /root/nftables-translated.nft
Шаг 2: валидация трансляции
# Проверка синтаксиса без применения
nft -c -f /root/nftables-translated.nft
# Если есть ошибки — правим вручную
# Трансляция не идеальна: сложные правила могут
# потребовать ручной доработки
Шаг 3: параллельный запуск
# Применяем nftables-правила параллельно с iptables
# (они работают на разных уровнях)
nft -f /root/nftables-translated.nft
# Проверяем, что всё работает
nft list ruleset
ss -tlnp # проверяем доступность сервисов
curl -I http://localhost # тест HTTP
Шаг 4: переключение
# Убираем iptables
iptables -F
iptables -X
ip6tables -F
ip6tables -X
# Отключаем сервис iptables
systemctl disable iptables 2>/dev/null
systemctl disable ip6tables 2>/dev/null
# Включаем nftables
cp /root/nftables-translated.nft /etc/nftables.conf
systemctl enable nftables
systemctl start nftables
# Финальная проверка
nft list ruleset
systemctl status nftables
Важно: не удаляйте бэкап iptables-backup.rules до тех пор, пока не убедитесь, что nftables стабильно работает в продакшене хотя бы неделю. Серьёзно, не удаляйте.
Отладка и мониторинг nftables
Написать правила — полдела. Нужно ещё убедиться, что они работают как задумано, и уметь оперативно находить проблемы.
Трассировка пакетов
# Включение трассировки для конкретного IP
nft add rule inet firewall input ip saddr 203.0.113.50 \
meta nftrace set 1
# Просмотр трассировки в реальном времени
nft monitor trace
# Пример вывода:
# trace id abc123 inet firewall input packet: iif "eth0"
# ether saddr aa:bb:cc:dd:ee:ff ip saddr 203.0.113.50
# ip daddr 10.0.1.5 tcp dport 22
# trace id abc123 inet firewall input rule tcp dport @allowed_tcp_ports accept
# trace id abc123 inet firewall input verdict accept
# Не забудьте отключить трассировку после отладки!
nft delete rule inet firewall input handle
Счётчики и статистика
# Просмотр правил со счётчиками
nft list chain inet firewall input
# Именованные счётчики
nft list counters table inet firewall
# Сброс счётчиков
nft reset counters table inet firewall
# Список активных элементов в динамических sets
nft list set inet firewall denylist_temp
# Количество элементов
nft list set inet firewall denylist_temp | grep -c "timeout"
Интеграция с системным журналом
# Правило логирования с rate limiting
chain input {
# Логирование перед drop
limit rate 15/minute burst 30 packets \
log prefix "[nft-input-drop] " level warn \
counter
}
# Фильтрация логов
# journalctl -k --grep="nft-drop"
# Настройка rsyslog для выделения nftables-логов
# /etc/rsyslog.d/10-nftables.conf:
# :msg, contains, "[nft-" /var/log/nftables.log
# & stop
Полная продакшен-конфигурация
Ну что ж, давайте соберём всё вместе. Вот финальный файл конфигурации, объединяющий все рассмотренные техники:
#!/usr/sbin/nft -f
# /etc/nftables.conf — Production Firewall Configuration
# Дата: 2026 | Семейство: inet (IPv4+IPv6)
flush ruleset
table inet firewall {
# ===== STATIC SETS =====
set trusted_mgmt {
type ipv4_addr
flags interval
comment "Сети управления — полный доступ к SSH и мониторингу"
elements = { 10.0.0.0/8, 172.16.0.0/12 }
}
set allowed_tcp {
type inet_service
comment "Публичные TCP-порты"
elements = { 80, 443 }
}
set blocked_ranges {
type ipv4_addr
flags interval
comment "Статический чёрный список"
}
# ===== DYNAMIC SETS =====
set denylist_short {
type ipv4_addr
flags dynamic, timeout
timeout 15m
size 65536
comment "Автоматический бан — 15 минут"
}
set denylist_long {
type ipv4_addr
flags dynamic, timeout
timeout 4h
size 65536
comment "Рецидивисты — бан на 4 часа"
}
# ===== COUNTERS =====
counter cnt_blocked_static { comment "Статический чёрный список" }
counter cnt_blocked_short { comment "Временные баны" }
counter cnt_blocked_long { comment "Длительные баны" }
counter cnt_syn_flood { comment "SYN flood drops" }
counter cnt_invalid { comment "Invalid state drops" }
counter cnt_total_dropped { comment "Всего отброшено" }
# ===== DDoS MITIGATION CHAIN =====
chain ddos_filter {
tcp flags syn limit rate over 50/second burst 100 packets \
counter name cnt_syn_flood drop
tcp flags syn ct state != new drop
ip frag-off & 0x1fff != 0 drop
tcp flags & (fin|syn|rst|psh|ack|urg) == fin|syn|rst|psh|ack|urg drop
tcp flags & (fin|syn|rst|psh|ack|urg) == 0x0 drop
tcp flags syn,fin drop
tcp flags syn,rst drop
}
# ===== INPUT CHAIN =====
chain input {
type filter hook input priority filter; policy drop;
# --- Loopback ---
iif "lo" accept
# --- Connection tracking ---
ct state established,related accept
ct state invalid counter name cnt_invalid drop
# --- Denylist checks (раньше всего) ---
ip saddr @blocked_ranges counter name cnt_blocked_static drop
ip saddr @denylist_long counter name cnt_blocked_long drop
ip saddr @denylist_short counter name cnt_blocked_short drop
# --- DDoS mitigation ---
jump ddos_filter
# --- ICMP ---
ip protocol icmp icmp type {
echo-request,
destination-unreachable,
time-exceeded
} limit rate 10/second accept
ip6 nexthdr icmpv6 icmpv6 type {
echo-request,
destination-unreachable,
nd-neighbor-solicit,
nd-neighbor-advert,
nd-router-solicit,
nd-router-advert
} accept
# --- SSH: только из доверенных сетей + rate limiting ---
ip saddr @trusted_mgmt tcp dport 22 ct state new \
meter ssh_rate { ip saddr limit rate 5/minute burst 10 packets } \
accept
# SSH brute-force — автоматический бан
tcp dport 22 ct state new \
add @denylist_short { ip saddr } \
log prefix "[ssh-blocked] " counter drop
# --- Публичные сервисы ---
tcp dport @allowed_tcp ct state new \
meter http_rate { ip saddr limit rate 60/second burst 150 packets } \
accept
# HTTP flood — автоматический бан
tcp dport { 80, 443 } ct state new \
add @denylist_short { ip saddr } counter drop
# --- Мониторинг (только из доверенных) ---
ip saddr @trusted_mgmt tcp dport { 9090, 9100, 3000 } accept
# --- Логирование отброшенных ---
limit rate 10/minute burst 20 packets \
log prefix "[nft-drop] " level warn \
counter name cnt_total_dropped
}
# ===== FORWARD CHAIN =====
chain forward {
type filter hook forward priority filter; policy drop;
}
# ===== OUTPUT CHAIN =====
chain output {
type filter hook output priority filter; policy accept;
}
}
Автоматизация: проверка конфигурации в CI/CD
Файрвол — критическая инфраструктура. Одна ошибка в правилах может заблокировать доступ к серверу (и хорошо, если у вас есть IPMI или консоль). Автоматизированная проверка — не роскошь, а необходимость.
Скрипт валидации
#!/bin/bash
# validate-nftables.sh — проверка конфигурации перед деплоем
CONFIG_FILE="${1:-/etc/nftables.conf}"
echo "[*] Проверка синтаксиса..."
if ! nft -c -f "$CONFIG_FILE" 2>&1; then
echo "[FAIL] Синтаксическая ошибка в $CONFIG_FILE"
exit 1
fi
echo "[OK] Синтаксис корректен"
echo "[*] Проверка наличия базовых правил..."
REQUIRED_PATTERNS=(
"policy drop"
"ct state established"
"ct state invalid"
"iif.*lo.*accept"
)
for pattern in "${REQUIRED_PATTERNS[@]}"; do
if ! grep -qE "$pattern" "$CONFIG_FILE"; then
echo "[FAIL] Отсутствует обязательное правило: $pattern"
exit 1
fi
done
echo "[OK] Все обязательные правила на месте"
echo "[*] Проверка: SSH-доступ не заблокирован полностью..."
if ! grep -qE "dport 22.*accept|dport ssh.*accept" "$CONFIG_FILE"; then
echo "[WARN] Не найдено правило, разрешающее SSH!"
echo " Убедитесь, что доступ обеспечивается другим способом."
fi
echo "[*] Все проверки пройдены"
Интеграция в GitLab CI / GitHub Actions
# .gitlab-ci.yml (фрагмент)
validate_firewall:
stage: test
image: debian:bookworm
before_script:
- apt-get update && apt-get install -y nftables
script:
- nft -c -f nftables.conf
- bash validate-nftables.sh nftables.conf
rules:
- changes:
- nftables.conf
- nftables/**/*
FAQ: часто задаваемые вопросы
Чем nftables лучше iptables и стоит ли мигрировать?
Коротко — да, стоит. nftables — официальная замена iptables, принятая во всех основных дистрибутивах. Ключевые преимущества: единый инструмент nft для IPv4/IPv6 (вместо отдельных iptables/ip6tables), нативные sets и maps для O(1)-поиска, атомарные обновления, более читаемый синтаксис. Если ваши серверы на Ubuntu 22.04+, Debian 11+, RHEL 9+ — под капотом уже nftables через слой совместимости. Прямая настройка даёт полный контроль и доступ ко всем фишкам.
Как не заблокировать себя при настройке файрвола?
Три золотых правила: (1) всегда держите открытую SSH-сессию при изменении правил — она не будет прервана благодаря ct state established; (2) добавьте в cron временную задачу восстановления: */5 * * * * nft -f /etc/nftables.conf.backup — если потеряете доступ, через 5 минут вернётся рабочая конфигурация; (3) всегда проверяйте синтаксис через nft -c -f перед применением. Поверьте, лучше потратить 30 секунд на проверку, чем час на поездку в дата-центр.
Можно ли использовать nftables вместе с Docker?
Можно, но с оговорками. Docker манипулирует правилами nftables напрямую для проброса портов контейнеров. Его NAT-правила обрабатываются до ваших фильтров, что может привести к обходу файрвола. Рекомендации: (1) задайте DOCKER_IPTABLES=false в конфигурации Docker и управляйте NAT вручную; (2) используйте цепочку forward для контроля трафика контейнеров; (3) рассмотрите Docker-прокси или reverse proxy вместо прямого проброса портов.
Как защитить сервер от DDoS с помощью nftables?
nftables эффективно справляется с низкоуровневыми DDoS-атаками: SYN-флуд блокируется rate limiting'ом SYN-пакетов, невалидные TCP-флаги отсекаются выделенной цепочкой, per-IP connection limiting ограничивает одновременные соединения. Плюс обязательно настройте sysctl-параметры: tcp_syncookies, nf_conntrack_max. Но для серьёзных volumetric-атак (десятки Гбит/с) без защиты на уровне провайдера или CDN не обойтись — файрвол хоста физически не отфильтрует трафик, превышающий ширину канала.
Как мониторить работу nftables в реальном времени?
Несколько инструментов на выбор. Именованные счётчики (nft list counters) показывают статистику по категориям. Команда nft monitor trace позволяет трассировать путь конкретных пакетов через цепочки — незаменимо при отладке. Логирование через log prefix с rate limiting отправляет события в systemd journal и syslog. Для автоматизации можно экспортировать счётчики в Prometheus через nftables_exporter или парсить вывод nft -j list counters в JSON-формате.
