Динамические наборы nftables: rate limiting и временная блокировка IP | AdminWiki

Динамические наборы nftables: rate limiting и временная блокировка IP

11 сентября 2026 12 мин. чтения
Содержание статьи

Ограничение частоты соединений и временная блокировка IP в nftables держатся на трёх механизмах: динамический set с параметром timeout, meter или набор с limit rate для подсчёта частоты, атомарная загрузка всего ruleset командой nft -f. Ядро само добавляет адрес нарушителя в чёрный список и само удаляет его через заданный интервал. Внешние скрипты, cron и fail2ban для этого не нужны.

Минимальный рабочий пример для SSH: адрес, превысивший три новых соединения в минуту, отправляется в набор blocked на час и дальше дропается.

nft add table ip filter
nft add set ip filter blocked { type ipv4_addr\; flags dynamic,timeout\; timeout 1h\; size 100000\; }
nft add rule ip filter input ct state established,related accept
nft add rule ip filter input tcp dport 22 ct state new add @blocked { ip saddr limit rate over 3/minute } drop
nft add rule ip filter input ip saddr @blocked drop
nft add rule ip filter input tcp dport 22 accept

Порядок правил критичен. Установленные соединения принимаются первыми, поэтому уже открытая сессия SSH не рвётся, когда в набор добавляется новый адрес. Правила из файла применяются одной транзакцией, а набор сохраняется в /etc/nftables.conf, чтобы пережить перезагрузку.

Дальше разберём каждый блок отдельно: синтаксис набора, счётчики и метры, время жизни элемента, атомарное обновление, автозагрузку и диагностику с подстраховкой. Базовые вещи по защите самого хоста, включая настройку SSH и sudo, разобраны в материале про политику безопасности Linux-сервера.

Введение в динамические наборы nftables

Что такое set в nftables и его возможности

Set в nftables - именованный контейнер для элементов одного типа: IPv4-адресов, IPv6-адресов, портов (inet_service), MAC-адресов (ether_addr). Правило сверяется с содержимым набора через символ @, сам набор живёт в ядре и меняется по netlink без перезагрузки правил. Один набор может использоваться десятком правил одновременно.

ПараметрЧто делает
typeтип элементов: ipv4_addr, ipv6_addr, inet_service, ether_addr
timeoutвремя жизни элемента, после которого адрес удаляется автоматически
flags dynamicразрешает добавлять элементы прямо из правил, а не только вручную
countersсчётчики пакетов и байтов по каждому элементу набора
sizeмаксимум элементов, по умолчанию 65536
gc-intervalинтервал, с которым ядро вычищает истёкшие элементы

Создание набора для блокировки на час. В bash фигурные скобки и точки с запятой экранируются обратным слешем, иначе оболочка попробует раскрыть их сама и команда развалится на части.

nft add set ip filter blacklist { type ipv4_addr\; flags dynamic\; timeout 1h\; }
nft list set ip filter blacklist

Второй командой проверяем результат: в выводе видно тип, флаги, timeout и текущее число элементов. Счётчики в этом выводе появятся, только если набор создавали с параметром counters.

Зачем нужны динамические наборы для rate limiting и блокировки

Статический набор адресов требует ручной правки: увидел в логах атакующий IP, добавил, через сутки вспомнил, что пора убрать. Динамический набор переворачивает схему. Адрес попадает в него по событию (превышение лимита соединений, срабатывание правила с limit rate) и исчезает сам по таймауту.

Что это даёт на практике:

  • Брутфорс SSH или FTP гасится на уровне ядра, без запуска внешних демонов и без задержки на разбор логов.
  • Сканеры, долбящие диапазон портов, теряют доступ на заданный интервал после первых же попыток.
  • Нагрузка на CPU падает: одно правило с meter заменяет регулярный парсинг access.log скриптом раз в минуту.
  • Состояние видно командой nft list set, то есть его легко отдать в мониторинг.

Размер одного элемента набора измеряется десятками байт, поэтому даже список из ста тысяч адресов не создаёт заметной нагрузки на память. Ограничение задаётся параметром size, и его стоит выставлять заранее: при переполнении набора новые адреса просто не добавляются.

Настройка rate limiting с помощью nftables counters и meter

Использование meter для ограничения частоты соединений

Meter создаёт анонимный динамический набор, ключом которого служит выражение из правила (например, ip saddr), а значением - счётчик вместе с порогом. Конструкция { ip saddr limit rate 3/minute } означает: считать события отдельно по каждому адресу источника и сравнить с порогом три в минуту.

nft add rule ip filter input tcp dport 22 ct state new meter ssh_flood { ip saddr limit rate 3/minute } accept

Правило пропускает первые три соединения и перестаёт срабатывать для четвёртого и последующих. Пакет сверх лимита не отбрасывается автоматически, он просто уходит к следующим правилам цепочки, поэтому meter обычно ставят рядом с правилом drop или с логированием.

Ключевое слово over меняет логику на противоположную: правило срабатывает именно тогда, когда порог превышен. Современный синтаксис вместо meter использует именованный динамический набор, что удобнее для отладки, потому что содержимое видно в nft list set.

nft add set ip filter ssh_flood { type ipv4_addr\; flags dynamic\; size 65535\; }
nft add rule ip filter input tcp dport 22 ct state new add @ssh_flood { ip saddr limit rate over 3/minute } drop
nft list set ip filter ssh_flood

Ключ ct state new отсекает уже установленные сессии, поэтому в счётчик попадают только попытки открыть новое соединение. Без него rate limiting начнёт считать и служебный трафик внутри активной сессии, а порог сработает раньше времени.

Добавление counters для мониторинга трафика

Счётчик добавляется к любому правилу словом counter. Это самый дешёвый способ понять, доходит ли трафик до конкретной строки правил.

nft add rule ip filter input tcp dport 22 counter accept
nft -a list chain ip filter input

Флаг -a показывает handles: идентификаторы правил, по которым их можно точечно удалить, не переписывая всю цепочку.

nft delete rule ip filter input handle 12
nft list ruleset

Счётчик растёт - трафик доходит. Счётчик стоит на нуле - пакеты режет правило выше по цепочке, и искать причину нужно там, а не в конфигурации сервиса. У набора с флагом counters статистика ведётся отдельно по каждому адресу, что сразу показывает самых активных нарушителей.

Временная блокировка IP с помощью set и timeout

Создание set с параметром timeout

Набор с таймаутом по умолчанию создаётся так: тип, флаги dynamic и timeout, само значение времени жизни, размер.

nft add set ip filter blocked { type ipv4_addr\; flags dynamic,timeout\; timeout 24h\; size 100000\; }
nft add rule ip filter input ip saddr @blocked drop

Добавить адрес вручную можно в любой момент, и он получит таймаут набора. Либо свой, отдельный для конкретного элемента.

nft add element ip filter blocked { 203.0.113.10 }
nft add element ip filter blocked { 203.0.113.11 timeout 30m }
nft list set ip filter blocked

Второй вариант удобен для ручных работ: адрес снимается через полчаса без напоминаний. Персональный таймаут элемента работает только в наборе, созданном с флагом timeout, иначе nft вернёт ошибку синтаксиса.

Автоматическое добавление IP в блокировку по событию

Rate limiting и блокировка объединяются в одну строку. Как только адрес перебирает пароли быстрее трёх новых соединений в минуту, он попадает в blocked.

nft add rule ip filter input tcp dport 22 ct state new add @blocked { ip saddr limit rate over 3/minute } drop

Дальше все пакеты с этого адреса ловит правило ip saddr @blocked drop, размещённое выше разрешающих правил. Если положить его в начало цепочки, нарушитель теряет доступ ко всем портам, а не только к 22. Это сознательный выбор: агрессивный сканер обычно перебирает и веб, и почту, и панели управления.

Для веб-сервиса схема та же, меняются порог и время жизни. Двадцать новых соединений в секунду с одного адреса на 443 - типичный признак флуда, десять минут блокировки хватает, чтобы автоматика атакующего переключилась на другую цель.

nft add set ip filter web_flood { type ipv4_addr\; flags dynamic,timeout\; timeout 10m\; size 200000\; }
nft add rule ip filter input tcp dport 443 ct state new add @web_flood { ip saddr limit rate over 20/second } drop

Атомарное обновление правил nftables

Загрузка ruleset из файла одной командой

Файл /etc/nftables.conf с полным набором таблиц, цепочек и правил применяется целиком: nft -f передаёт ядру одну транзакцию netlink. Либо применяется всё, либо ничего. Промежуточного состояния, когда старые правила уже удалены, а новые ещё не загружены, не существует. Для удалённого сервера это разница между секундной паузой и потерей доступа.

Перед правкой снимите копию рабочего состояния. Вывод nft list ruleset - валидный синтаксис для nft -f, поэтому файл сразу годится и как конфиг, и как точка отката.

nft list ruleset
nft list ruleset

Сохраните вывод в /root/nftables.backup. Типовая структура рабочего конфига выглядит так:

flush ruleset

table ip filter {
    set blocked {
        type ipv4_addr
        flags dynamic,timeout
        timeout 1h
        size 100000
    }

    chain input {
        type filter hook input priority filter\; policy drop\;
        ct state established,related accept
        iif lo accept
        ip saddr @blocked drop
        tcp dport 22 ct state new add @blocked { ip saddr limit rate over 3/minute } drop
        tcp dport 22 accept
    }
}

Строка flush ruleset в начале сбрасывает прежний набор целиком, поэтому транзакция не оставляет за собой старых правил. Если на хосте работает Docker, его таблицы ip nat и цепочки DOCKER живут отдельно, и обновление основного ruleset их не затрагивает. Нюансы совместной работы с контейнерами, включая DNAT для published ports и порядок хуков, разобраны в статье Docker и nftables: настройка firewall без потери SSH-доступа.

Проверка синтаксиса перед применением

Флаг -c выполняет разбор файла без загрузки в ядро. Опечатка в имени цепочки, забытая точка с запятой или неверный тип элемента выявятся до того, как правило попадёт в работу.

nft -c -f /etc/nftables.conf
nft -f /etc/nftables.conf
nft list ruleset

Проверка синтаксиса не гарантирует логическую корректность: файл может быть валидным и при этом рубить SSH. Смысловые ошибки ловятся только на живом трафике, поэтому держите вторую сессию и заранее подготовленный откат.

Сохранение конфигурации nftables после перезагрузки

Сохранение текущих правил в файл

Правила, добавленные командами nft add, живут в памяти ядра и исчезнут после перезагрузки. Экспорт состояния в файл решает вопрос.

nft list ruleset

systemctl enable --now nftables
systemctl status nftables

Первая команда с перенаправлением в /etc/nftables.conf записывает снимок. Вторая включает юнит и запускает его сразу. Дистрибутивы на Debian и Ubuntu поставляют сервис nftables в комплекте с пакетом, в RHEL-семействе проверьте, что тем же набором не управляет firewalld.

Включение и проверка сервиса nftables

Юнит читает /etc/nftables.conf при каждой загрузке. Ошибка в файле означает, что сервис упадёт и правила не применятся вообще: сервер окажется без фильтрации. Полезно один раз отрепетировать перезагрузку на тестовом стенде.

systemctl restart nftables
systemctl is-enabled nftables
nft list ruleset

Команда systemctl restart nftables применяет файл на живом сервере через ту же атомарную транзакцию. is-enabled показывает, включён ли автозапуск. После рестарта обязательно сверьте вывод nft list ruleset с ожидаемым: лишние правила чаще всего приходят из конфигов firewalld или из скриптов, которые дописывают свою цепочку при старте сервиса.

Безопасная диагностика nftables без риска потерять SSH

Использование отдельной SSH-сессии для тестирования

Порядок, снимающий риск потерять доступ. Откройте вторую SSH-сессию и оставьте её висеть, лучше внутри tmux или screen. Все эксперименты проводите в первой. Вторая остаётся страховкой: даже если новое правило рубит новые подключения, уже установленное соединение продолжает работать, а правило ct state established,related accept в начале цепочки защищает и его.

Второй уровень страховки - отложенный откат. Перед применением рискованного файла поставьте задачу в at.

nft list ruleset

echo 'nft -f /root/nftables.backup' | at now + 5 minutes
atq

Если через пять минут доступ работает и правила устраивают, задание снимается командой atrm с номером из atq. Если доступ потерян, откат сработает сам, без вашего участия. На системах без at подойдёт фоновый вариант: sh -c 'sleep 300; nft -f /root/nftables.backup' запустить в отдельной сессии.

Для тренировок берите отдельный VDS, а не боевой хост: облачный сервер с почасовой оплатой обходится дешевле, чем разбор последствий на продакшене. Подойдёт, например, Timeweb Cloud с готовыми образами Debian и Ubuntu.

Применение временных правил и счётчиков для отладки

Диагностика начинается со счётчиков и логирования. Сначала убедитесь, что трафик доходит до нужного правила, и только потом добавляйте блокировки.

nft add rule ip filter input ct state established,related accept
nft add rule ip filter input tcp dport 22 counter accept
nft add rule ip filter input tcp dport 22 counter log prefix "ssh-test: "
nft monitor trace

Для трассировки отметьте правило флагом nftrace и смотрите поток в отдельной сессии.

nft add rule ip filter input tcp dport 22 meta nftrace set 1
nft monitor trace

Отладочное правило снимается по handle, найденному через nft -a list chain, чтобы не трогать остальную цепочку. Полностью безопасный вариант проверки - сетевой namespace: правила применяются внутри него и не влияют на трафик хоста.

ip netns add nfttest
ip netns exec nfttest nft list ruleset

Отладочные префиксы и большие выборки логов удобно разбирать через агрегатор API: AiTunnel даёт единый доступ к моделям GPT, Gemini и Claude с оплатой в рублях, что экономит время на ручном просмотре строк. Методику проверки периметра целиком, с анализом уже существующих правил firewall, стоит пройти по отдельной методике аудита сетевой инфраструктуры.

Возможные проблемы и ограничения

Конфликты с другими системами управления firewall

firewalld, ufw и Docker пишут в тот же netfilter, что и nftables, и легко перетирают ваши правила при собственном перезапуске. Проверьте, кто ещё активен на хосте.

systemctl status firewalld
systemctl status ufw
nft list tables

Если firewalld активен, выберите один путь: отключить его (systemctl disable --now firewalld и systemctl mask firewalld) либо перенести правила в его конфигурацию. Смешивать два менеджера правил на одном хосте не стоит, отладка превращается в поиск виновника по времени в логах. Дополнительные сложности возникают при маршрутизации и NAT: правила FORWARD и SNAT живут в своих цепочках, и их порядок разобран в руководстве по политикам маршрутизации в Linux.

Различия в версиях ядра и nftables

Первым делом посмотрите версии: nft --version и uname -r. Ключевое слово meter сохраняется для совместимости, но новые сборки предпочитают динамический набор с limit rate, и часть примеров из старых руководств может не разобраться парсером. Персональный таймаут элемента требует флага timeout при создании набора.

Сборщик мусора удаляет истёкшие элементы не мгновенно, а по своему интервалу (gc-interval). Блокировка с точностью до секунды невозможна: адрес может провисеть в наборе лишние десятки секунд. Размер набора по умолчанию 65536 элементов, и при наплыве сканеров он переполняется, а новые адреса молча не добавляются. Задавайте size с запасом.

Заключение

Рабочий каркас защиты на динамических наборах собирается из пяти шагов:

  1. Набор blocked с типом ipv4_addr, флагами dynamic,timeout, явным timeout и size.
  2. Правило ct state established,related accept в начале цепочки input.
  3. Rate limiting через add @blocked { ip saddr limit rate over N/minute } drop для критичных портов.
  4. Загрузка конфига одной транзакцией: nft -c -f, затем nft -f /etc/nftables.conf.
  5. Сохранение снимка в /etc/nftables.conf и включение юнита nftables.

Проверка и откат остаются обязательной частью схемы: вторая SSH-сессия в tmux, резервная копия ruleset в /root и отложенный возврат через at. С таким набором подстраховок эксперименты с блокировками перестают быть риском потерять сервер.

Поделиться:
Сохранить гайд? В закладки браузера