nftables для продакшена: sets, maps, rate limiting, fail2ban и защита от DDoS на Linux-сервере

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

nftables 2026: rate limit, DDoS, fail2ban

Введение: почему nftables — стандарт файрвола Linux в 2026 году

Если вы до сих пор пишете правила 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-формате.

Об авторе Editorial Team

Our team of expert writers and editors.