zapret/docs/nftables_notes.md
2024-09-17 17:12:49 +03:00

143 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

- [Вступление](#вступление)
- [Минусы](#минусы)
- [Главная боль №1](#главная-боль-1)
- [Боль №2](#боль-2)
- [Боль №3](#боль-3)
- [Боль №4](#боль-4)
- [Плюсы](#плюсы)
- [Плюс №1, главный](#плюс-1-главный)
- [Плюс №2](#плюс-2)
- [Плюс №3](#плюс-3)
- [Плюс №4](#плюс-4)
- [Плюс №5](#плюс-5)
- [Выводы](#выводы)
# Вступление
`nftables` - это технология, пришедшая на замену `iptables`.
В ней собрали все, что относилось к различным `iptables`. А их немало. `iptables`, `ip6tables`, `ebtables`, `arptables`, `ipset`.
Весь код из разрозненных, но похожих компонент, собрали в одно целое с единым синтаксисом.
Добавили различные конструкции языка, позволяющие писать правила более лаконично, не повторяя одни и те же команды с небольшими различиями.
На `nftables` можно сделать почти все, что можно было сделать на `iptables`. Есть то, что можно сделать на `nftables`, но нельзя на `iptables`.
Есть и наоборот.
# Минусы
К сожалению, не обошлось и без боли.
## Главная боль №1
Очень серьезная, актуальная для OpenWrt, и решения не видно.
ipset-ы позволяли загонять пересекающиеся интервалы IP адресов или подсетей.
nftables sets это не позволяют. Любое пересечение вызывает ошибку.
Есть auto-merge, но это работает только в user mode в процессе `nft`, при условии, что весь блок адресов загоняется одной командой
и нет пересечений с уже имеющимся контентом в set.
Это не было бы критической проблемой, поскольку скрипты `zapret` и так загоняют ipset целиком.
Проблема в катастрофическом расходе памяти при операции загона больших интервальных листов, то есть с подсетями и диапазонами.
Чтобы загнать 100000 IPv4 записей, едва хватает 300 Mb памяти устройства.
При успехе операции в ядре список столько не занимает, но суть дела это не меняет.
Для традиционных Linux систем это не проблема, но почти все роутеры загнутся.
Приемлемого решения не просматривается.
Сделать записи непересекающимися в листах - задача непростая.
Потребуется переписать алгоритм auto-merge из `nft`, но с пониженным расходом памяти.
Загонять записи по одному отдельными вызовами `nft`, игнорируя ошибки, займет вечность.
Загонять блоком отдельных команд, игнорируя ошибки, - `nft` такого не умеет. Похоже, при любой ошибке происходит откат всего скрипта.
К тому же при таком подходе будут неточности в итоговом результате.
Swap позволяет немного сгладить проблему, но лишь незначительно.
Скажем, если вдруг list загоняется без ошибок с 300 Mb памяти и с падением на 256 MB, то swap спасает.
Если памяти становится 200 MB, то swap уже не спасет.
Все равно вызывается OOM killer, заодно убивая и другие процессы, кроме `nft`, а это уже совсем плохо. Может быть убито что-то важное.
## Боль №2
Не смертельная, но тоже не айс.
Какие-то нерациональные алгоритмы разбора таблиц в `nft`.
Например, есть 1 большой set на 100000 элементов и 1 маленький на 2 элемента.
Чтобы просто пролистать мелкий set или добавить туда еще что-то `nft` будет мусолить несколько секунд.
Что он делает за это время? Тащит из ядра огромный блоб, в котором все в куче, и разбирает его, чтобы выделить искомую мелочь?
В какой-то мере удается это сгладить, объединяя несколько команд в единый скрипт.
## Боль №3
Система `nftables` построена на виртуальной машине. Правила, которые вы пишите, переводятся в псевдокод VM.
Чтобы потом их показать, `nft` декомпилирует код и переводит в читаемый язык.
Это довольно сложно, и регулярно случаются баги, связанные с неверным отображением.
Кроме этого, часто встречаются и баги парсера.
Например, все версии `nft` вплоть до 1.0.1 имеют баг, который не разрешает названия интерфейсов в кавычках в определении flowtable.
Без кавычек нельзя вставить интерфейсы, имя которых начинается с цифры.
OpenWrt решает эту проблему отдельным патчем в snapshot версии, но на традиционных системах и в OpenWrt 21.x- его нет.
Почему бы не наплевать на интерфейсы, начинающиеся с цифры? Потому что для OpenWrt 6to4-6to4, 6in4-he-net - обычное явление.
На текущий момент этой проблемы в OpenWrt уже нет, если использовать актуальную версию.
Но тем не менее, хоть `nft` и давно перешел отметку 1.0, всякая мелочь, особенно на не совсем стандартных правилах, регулярно всплывает.
Потому чем новее у вас будет версия `nft`, тем больше там выловлено проблем.
Здесь обновления важны, чтобы потом не мучиться из-за давно исправленного велосипеда.
## Боль №4
Невозможно, не копаясь в других таблицах и хуках, ничего не зная об их содержании, предотвратить DROP или REJECT.
Нельзя написать такое правило, которое что-то важное ACCEPT нет, игнорируя остальные хуки во всех таблицах.
Если у вас есть какой-то фаервол, и он что-то дропает, то как от этого отказаться, если надо временно что-то принять?
Это особенность `netfilter`, он так работает, но в `iptables` есть лишь стандартные таблицы с их хуками, куда можно вставить ACCEPT.
Здесь хуков может быть сколько угодно в каких угодно таблицах.
Эта проблема частично ломает кайф от независимого управления таблицами.
# Плюсы
## Плюс №1, главный
`iptables` хороши, когда ими пользуется кто-то один. Иначе это проходной двор.
Когда есть система управления фаерволом, то приходится как-то к ней прикручиваться, чтобы не нарушить ее работу
и управлять правилами синхронно. Нужно уметь внести и удалить отдельные правила когда это нужно, не трогая все остальное.
Некоторые системы управления фаерволом вообще не предполагают, чтобы кто-то еще лез в `iptables`, и это очень сильно портит жизнь.
У `iptables` есть предопределенный набор хуков `netfilter` с фиксированным приоритетом.
В `nftables` хуков можно создать неограниченное количество с выбранным приоритетом, управляя ими независимо в отдельных таблицах.
Система управления фаерволом может работать в одной таблице (`fw4` в случае OpenWrt) и не трогать все остальное.
`zapret` может работать в другой таблице и не трогать систему управления фаерволом. Они друг другу не мешают.
Это снимает множество боли.
Но есть и исключение. `nfset`-ы - аналог `ipset`-ов - нельзя использовать из другой таблицы.
Потому если вам нужен `ipset`, создаваемый `zapret` скриптами, вам понадобится писать правила в той же таблице.
Но нет никакой необходимости влезать в цепочки `zapret`.
Создаете свои цепочки и хуки и делаете в них что угодно.
## Плюс №2
Возможность выбора приоритета хука позволяет легко решить проблему хаотической и принудительной дефрагментацией L3 IPv6, без танцев с загрузкой модулей ядра со специальными параметрами или перекомпиляцией `nftables-nft`.
Это же позволяет перехватить трафик после SNAT/MASQUERADE, что на `iptables` невозможно.
Атаки на проходящий трафик, ломающие NAT, крайне затруднены на `iptables`.
## Плюс №3
Наличие множеств (anonymous/named sets) позволяет не писать кучу однообразных правил там, где в `iptables` их пришлось бы написать.
## Плюс №4
Если у вас есть `nftables`, то там наверняка есть уже все или почти все.
Нет кучи разных модулей ядра и .so плагинов для `iptables` user-mode процесса.
Отдельные модули ядра есть, но их меньше, чем в `iptables`, и OpenWrt их делит на меньшее число пакетов, большинство из которых
и так ставятся по умолчанию. user-mode процесс `nft` и вовсе неделим. EXE-шник + lib.
## Плюс №5
Пишут, что `nftables` работают быстрее. Но это не точно и зависит от много чего.
В целом - чем меньше правил, тем выше скорость. Но в `nftables` правил можно писать меньше, значит скорость тоже может быть выше.
У разработчиков есть идея перевести backend `nftables` на BPF, а это наверняка будет существенно быстрее.
# Выводы
Без больших листов все почти прекрасно. Но большие ip листы убивают все. Не для домашних это роутеров.
А ipset-ы к `nftables` не прикрутить.
Зато есть возможность задействовать более продвинутые атаки, конфликтующие с NAT, которые на `iptables` могут быть невозможны.
Делать нечего. Openwrt отошел от `iptables`. С этим придется как-то жить.
Поэтому пришлось сделать для OpenWrt поддержку и `iptables`, и `nftables` (только с версии OpenWrt 21.xx, в более старых будут проблемы).
`iptables` можно задействовать на любой OpenWrt версии.
Если используется `fw3`, применяется старый механизм интеграции в `fw3`.
Если он не используется, то правилами `iptables` управляем как в традиционных Linux системах - то есть с возможностью
запуска и остановки, а скрипт запуска вносит в том числе и правила `iptables`.
На новых OpenWrt возможно снести `nftables` и `firewall4` и установить `firewall3` и `iptables`.
Если вам никак без больших IP листов на слабой системе, это может быть единственным спасением.