Ограничение частоты соединений и временная блокировка 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 с запасом.
Заключение
Рабочий каркас защиты на динамических наборах собирается из пяти шагов:
- Набор blocked с типом ipv4_addr, флагами dynamic,timeout, явным timeout и size.
- Правило ct state established,related accept в начале цепочки input.
- Rate limiting через add @blocked { ip saddr limit rate over N/minute } drop для критичных портов.
- Загрузка конфига одной транзакцией: nft -c -f, затем nft -f /etc/nftables.conf.
- Сохранение снимка в /etc/nftables.conf и включение юнита nftables.
Проверка и откат остаются обязательной частью схемы: вторая SSH-сессия в tmux, резервная копия ruleset в /root и отложенный возврат через at. С таким набором подстраховок эксперименты с блокировками перестают быть риском потерять сервер.