Предел числа правил в firewall: с какого количества пакет начинает тормозить
Сервер обрастает правилами firewall годами: сюда добавили блокировку абузеров, туда — доступ для нового сервиса, где-то накопился список IP из fail2ban. В какой-то момент правил становится тысячи, и возникает вопрос — а не они ли теперь тормозят каждый пакет? Ответ зависит не от общего числа правил, а от того, *как* они организованы: линейный список условий или структура с хэш-поиском. Разберём, почему это различие существует, чем nftables отличается от iptables на практике и как измерить накладные расходы firewall у себя, а не поверить цифре из чужой статьи.
Содержание
- Почему число правил вообще влияет на скорость пакета
- iptables: почему линейный проход правил становится узким местом
- nftables: хэш-множества и verdict-таблицы вместо цепочки if-else
- ipset: как решить проблему длинных списков в iptables без миграции
- Как измерить накладные расходы firewall на латентность
- Как организовать правила: порядок, группировка, chains
Почему число правил вообще влияет на скорость пакета
Каждый пакет, проходящий через netfilter, движется по цепочке хуков (PREROUTING, INPUT, FORWARD, OUTPUT, POSTROUTING), и внутри каждого хука — по цепочке правил в том порядке, в котором они записаны. Ядро проверяет правило за правилом, пока не встретит терминирующий вердикт (ACCEPT, DROP, REJECT) или не дойдёт до конца цепочки и не применит политику по умолчанию.
Каждое правило — это как минимум одна операция сравнения (адрес, порт, состояние соединения), а если в правило добавлены расширения вроде string, connlimit или recent — операций больше и они дороже. Для пакета, которому приходится пройти через много правил прежде чем найдётся совпадение (или который вообще не совпадает ни с одним и падает на политику по умолчанию), суммарное время обработки растёт примерно пропорционально числу проверенных правил — то есть в худшем случае это O(N) от размера цепочки.
На малом числе правил (десятки) и невысоком pps это незаметно: одна проверка простого правила — операция масштаба наносекунд, но она не бесплатна и не одна. Эффект накапливается: больше правил → больше сравнений на пакет → больше циклов CPU в контексте softirq → меньше запаса на реальный трафик. Заметнее всего это проявляется не в среднем отклике, а в хвостовых задержках и джиттере под нагрузкой — на серверах с высоким pps (антиDDoS-фильтрация, NAT для множества клиентов, шлюзы) и на установлении новых соединений, если проверка состояния не стоит в начале цепочки.
Точного порога «после скольки правил начинаются проблемы» не существует — он зависит от pps, CPU и того, как правила организованы. Плохо организованные 200 правил могут стоить дороже, чем хорошо организованные 5000.
iptables: почему линейный проход правил становится узким местом
Классический iptables (движок xtables, функция ipt_do_table в ядре) обходит список правил цепочки строго последовательно. Каждый iptables -A CHAIN ... добавляет правило в конец, и пакет проверяется по правилам от первого к последнему, пока не встретит совпадение с терминирующей целью.
Посмотреть текущую нагрузку по правилам можно счётчиками:
iptables -nvL --line-numbers
Колонки pkts и bytes показывают, сколько раз правило сработало — это первый инструмент диагностики: правила с большим числом попаданий стоит подвинуть ближе к началу цепочки, а правила с нулём попаданий за долгий период — кандидаты на удаление.
Классическая ошибка — генерировать правила программно по одному значению за раз: список из нескольких тысяч заблокированных адресов превращается в несколько тысяч правил -s a.b.c.d -j DROP. Пакет, которому нужно дойти до конца такого списка (например, легитимный трафик, не входящий в блок-лист), пройдёт через все правила по очереди — с ростом списка стоимость проверки растёт линейно.
Отдельный нюанс: на современных дистрибутивах команда iptables часто является совместимым фронтендом к nftables (iptables-nft) — синтаксис остаётся привычным, но правила транслируются в nftables-представление. Это не отменяет линейность: правила в стиле «одно правило — один адрес» остаются линейным списком независимо от бэкенда. Ускоряет не смена команды, а смена структуры данных — о ней ниже.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверnftables: хэш-множества и verdict-таблицы вместо цепочки if-else
nftables заменил xtables как основной фреймворк фильтрации в ядре Linux, и в основе — компактная байт-код-машина (nft_do_chain), которая, как и iptables, по умолчанию обходит правила цепочки линейно. Сам по себе переход на nftables не ускоряет длинную цепочку из отдельных правил — если переписать те же тысячи правил «один адрес — одно правило» в синтаксисе nft, поведение останется линейным.
Реальное архитектурное преимущество в другом: множества (set) и карты (map) в nftables — не надстройка, а объекты первого класса, доступные из коробки. Множество реализуется как хэш-таблица (для точных значений) или как дерево интервалов (для диапазонов и CIDR-подсетей), и поиск в нём не зависит линейно от числа элементов — вместо O(N) сравнений получаем O(1)/O(log n).
Практическая разница:
# было: N отдельных правил iptables
iptables -A INPUT -s 1.2.3.4 -j DROP
iptables -A INPUT -s 1.2.3.5 -j DROP
# ... ещё тысячи строк
# стало: одно множество + одно правило nft
nft add set inet filter blocklist { type ipv4_addr\; flags interval\; }
nft add element inet filter blocklist { 1.2.3.4, 1.2.3.5, 10.0.0.0/24 }
nft add rule inet filter input ip saddr @blocklist drop
Verdict-карты идут дальше: значение из пакета сразу маппится на действие или переход в конкретную цепочку одной операцией, вместо цепочки из «если порт 80, перейти сюда; если порт 443, перейти туда». Это заменяет линейный перебор диспетчеризацией по значению.
nftables не «магически быстрее» iptables по своей природе — выигрыш даёт структура данных (множество), а не название инструмента. Если писать nft-правила по-старому, по одному на значение, выигрыша не будет. Разница проявляется на списках — блок-листах, гео-IP, диапазонах портов, — а не на обычной ручной цепочке из полутора десятков правил для сервиса.
ipset: как решить проблему длинных списков в iptables без миграции
Если менять iptables на nftables пока не хочется (рабочий ruleset, привычные скрипты, интеграции), проблему длинных списков решает ipset — модуль ядра и утилита, появившиеся раньше нативных множеств nftables и дающие iptables доступ к тем же принципам: хэш-таблицам и деревьям вместо перебора.
Создание множества и добавление адресов:
ipset create blocklist hash:ip
ipset add blocklist 1.2.3.4
ipset add blocklist 5.6.7.8
Для массовой загрузки удобнее ipset restore с файлом в формате add blocklist <ip> построчно — это быстрее, чем добавлять адреса по одному через отдельные вызовы.
Подключение к iptables — одно правило вместо тысяч:
iptables -A INPUT -m set --match-set blocklist src -j DROP
Полезные типы множеств: hash:ip (адреса), hash:net (подсети), hash:ip,port (пара адрес+порт), bitmap:ip (компактно для небольшого непрерывного диапазона). Для временных банов удобен параметр timeout при создании множества — запись сама исчезает по истечении срока, не нужен отдельный cron на очистку:
ipset create tempban hash:ip timeout 3600
ipset add tempban 9.9.9.9 timeout 600
Типичные источники таких списков — динамические баны от fail2ban или crowdsec (сравнение подходов — в статье fail2ban или crowdsec: что выбрать для сервера), гео-IP блок-листы, белые списки адресов для доступа к панели управления. Важно: множества нужно явно сохранять (ipset save > /etc/ipset.conf) и восстанавливать при старте — про это легко забыть отдельно от правил iptables, которые обычно уже входят в persistence-механизм дистрибутива.
Как измерить накладные расходы firewall на латентность
Не гадайте — измеряйте, и в идеале на тестовом сервере, максимально похожем на боевой, а не сразу на проде.
CPU и softirq. Обработка сетевых пакетов, включая прогон через netfilter, идёт в контексте программных прерываний. Смотрите долю %soft в mpstat -P ALL 1 и рост счётчика NET_RX в /proc/softirqs. Если под сопоставимым pps softirq-нагрузка заметно выше на боевом ruleset, чем на минимальном тестовом, — это сигнал, что цена именно в правилах, а не в приложении.
Счётчики по правилам. iptables -nvL и nft list ruleset -a показывают число пакетов и байт на каждое правило. Это не прямое измерение задержки, но даёт две практические вещи: какие правила стоит поднять выше (часто срабатывают — держите их ближе к началу), и какие можно удалить (ноль срабатываний за разумный период — мёртвый груз, через который всё равно проходит каждый непопавший пакет).
Профилирование ядра. perf record -a -g -- sleep 10 с последующим perf report покажет долю времени, проведённого внутри ipt_do_table (legacy iptables) или nft_do_chain (nftables). Растущая доля этих функций под нагрузкой — прямое указание на стоимость ruleset, а не приложения.
A/B на тестовом стенде. Самый надёжный способ — сравнить одинаковый трафик (iperf3, hping3 или другой генератор pps) на трёх вариантах одного и того же стенда: (а) минимальный ruleset, (б) реальный боевой ruleset «как есть», (в) тот же список правил, переписанный через ipset/nft-set. Сравните достижимый pps и загрузку CPU. Это даёт честную цифру именно для вашего железа, ядра и трафика — любое число из статьи, включая эту, зависит от версии ядра, драйвера сетевой карты и профиля трафика настолько сильно, что переносить его на свой сервер бессмысленно.
Задержка на уровне приложения. Если можете сгенерировать стабильный синтетический трафик, сравнение p50/p99 отклика с обходом firewall (только на тестовом окружении!) и с активным ruleset изолирует вклад сетевого стека. На практике для обычного веб-трафика этот вклад обычно теряется на фоне задержек диска и базы — накладные расходы firewall заметны прежде всего под давлением по pps, а не в обычной latency одного запроса. Общую методику замера задержки без привязки к firewall смотрите в статье как измерить реальный пинг до сервера: методика.
Не отключайте firewall на проде «чтобы проверить» — даже на минуту. Тестируйте на изолированном стенде или в окне обслуживания с готовым откатом.
Как организовать правила: порядок, группировка, chains
Частое — в начало. Правило приёма установленных соединений должно стоять как можно раньше в INPUT:
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
Большая часть трафика на работающем сервере — уже установленные соединения. Если это правило стоит первым, такие пакеты выходят из цепочки за одну-две проверки вместо прохода через весь список.
Явный мусор — как можно раньше. Заведомо некорректные пакеты (ctstate INVALID) и трафик с приватных адресов на публичном интерфейсе разумно отсекать в самом начале обработки, до тяжёлых посервисных правил — это избавляет остальную цепочку от лишней работы над трафиком, который и так будет отброшен.
Группировка через chains. Вместо одной длинной плоской цепочки создавайте пользовательские chains по назначению (по сервису, по интерфейсу, по типу трафика) и переходите в них через -j, предварительно отсеяв заведомо нерелевантные пакеты:
iptables -N WEB_RULES
iptables -A INPUT -p tcp -m multiport --dports 80,443 -j WEB_RULES
iptables -A WEB_RULES -m set --match-set web_allow src -j ACCEPT
iptables -A WEB_RULES -j DROP
Пакет, не идущий на 80/443, вообще не проверяется против правил WEB_RULES — общее число правил в системе то же, но эффективное число проверок на конкретный пакет меньше.
Списки — только через множества. Любой набор дискретных значений (IP, подсети, порты) — это ipset для iptables или set/map для nftables, а не серия однотипных правил. Это правило без исключений: как только список превышает десяток записей, линейный перебор становится хуже читаемым и медленнее хэш-таблицы без каких-либо компенсирующих плюсов.
Логирование вне горячего пути. -j LOG в цепочке с высоким pps стоит дорого и само по себе, и потенциально ограничивает пропускную способность сильнее, чем логика, которую вы пытаетесь отладить. Если логирование нужно — ограничивайте частоту (-m limit --limit 5/min) и держите вне самых нагруженных участков.
Регулярный аудит. Раз в квартал (или после крупных изменений инфраструктуры) сверяйте счётчики iptables -nvL: правила с нулём срабатываний за долгий период и правила, которые физически не могут сработать (перекрыты более ранним терминирующим правилом того же условия), — чистый балласт, который проверяется на каждом пакете без всякой пользы.
Когда netfilter — не единственный уровень. На очень большом масштабе (десятки тысяч динамических записей, экстремальный pps, DDoS-класс трафика) стоит смотреть выше самого firewall: фильтрация на уровне XDP/eBPF отбрасывает трафик до входа в стек netfilter вообще, а вынесенная защита от DDoS снимает объём ещё до сервера — это тема отдельного разговора, но иметь её в виду стоит прежде чем бесконечно тюнинговать сам ruleset. Разбор частой ошибки на другом конце спектра — когда firewall вовсе оставляют открытым «на всякий случай» — в статье антипаттерн: firewall «разрешить всё», а история о случайной потере доступа из-за неудачного правила — в статье ошиблись в правиле firewall и потеряли доступ.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
После скольки правил начинать беспокоиться?
Фиксированного порога нет — зависит от pps сервера и структуры правил. Практический ориентир: как только у вас появляется список из десятка и более однотипных значений (адреса, порты), это повод перейти на множество независимо от общего числа правил в системе.
iptables или nftables — что выбрать для нового сервера в 2026 году?
Для нового сервера без унаследованных скриптов разумный выбор по умолчанию — nftables (или iptables-nft с сознательным использованием множеств вместо построчных правил) — он спроектирован под текущие практики. Если у вас уже есть рабочий iptables-ruleset с дисциплинированным использованием ipset, срочной причины мигрировать ради производительности нет — миграцию стоит планировать исходя из удобства сопровождения, а не только скорости.
ipset делает firewall быстрее сам по себе?
Нет, ipset — это структура данных для *списков*, а не замена или ускоритель firewall в целом. Он убирает конкретную проблему — линейный перебор одинаковых по смыслу правил. Обычные разнородные правила (по портам, состояниям, интерфейсам) ipset не трогает и не ускоряет.
Число правил в FORWARD влияет иначе, чем в INPUT?
Да, если сервер занимается NAT или маршрутизацией (шлюз, VPN-концентратор), через FORWARD может идти в разы больше пакетов, чем через INPUT самого хоста, — цена одного лишнего правила там ощутимее просто из-за большего объёма трафика через эту цепочку.
Conntrack и число правил firewall — это одна и та же проблема?
Нет, это разные оси. Число правил определяет, сколько сравнений проходит каждый пакет в цепочке. Размер таблицы conntrack (nf_conntrack_max) и её накладные расходы — это отдельная стоимость отслеживания состояния соединений, растущая с числом одновременных соединений, а не с числом правил. Обе проблемы стоит проверять отдельно.
Нужно ли переписывать рабочий iptables на nftables только ради скорости?
Как правило нет, если списки уже вынесены в ipset, а правил разумное количество (сотни, не многие тысячи построчных записей). Миграция оправдана, если вы и так планируете обновление инфраструктуры, а не как отдельный проект под лозунгом «nftables быстрее».
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →