Отчёт о ходе разработки f2b
Sep 9 23:06:55 f2b[10753]: jail 'asterisk': new ip found -- 89.163.146.93
Sep 9 23:09:50 f2b[10753]: jail 'asterisk': new ip match -- 89.163.146.93 (2/3)
Sep 9 23:12:43 f2b[10753]: jail 'asterisk': new ip match -- 89.163.146.93 (2/3)
Sep 9 23:15:35 f2b[10753]: jail 'asterisk': new ip match -- 89.163.146.93 (2/3)
Sep 9 23:18:40 f2b[10753]: jail 'asterisk': banned ip 89.163.146.93 for 1.0hrs
# ... попался, ублюдок!
Продолжаем неспешно пилить чудо-программу.
С момента анонса темпы пиления упали, т.к. сказывается "эффект УМВР", но тем не менее сделано следующее:
Control socket для демона
Над реализацией пришлось изрядно посидеть и поэкспериментировать.
Изначально я хотел использовать SOCK_STREAM
и обычный текстовый протокол.
Посидел, написал свою реализацию getline для сокета,
простенький аналог bison'а для разбора команд
и стало мне грустно от осознания объёма кода, который будет это всё обслуживать.
Плюсы:
- с этим может работать любая утилита с помощью netcat/telnet
Минусы, навскидку:
- разбор текстовых команд в виде нескольких токенов - достаточно сложное занятие. Нужно вычленить строку (в экстремальном случае она может приходить по байту за раз), побить её на токены, проверить синтаксис, количество аргументов. Нужно достаточно сложное управление входным буфером: если не распарсилось полностью - дождаться недостающего, осталось лишнее - хранить.
- перечисленное выше - работает с недоверенными данными из сети -> всё это надо выписывать с упором на безопасность.
- открывается дорога всем нашим любимым сетевым шалостям: типа открытия 500 соединенений разом и заливки в каждое из них мегабайтной строки, без малейших признаков "\r" или "\n" -> скажите "здравствуй" исчерпанию памяти или fd.
- ну и такой мелкий штрих: если мы хотим встроенный хелп - из-за тупости клиента, будьте любезны тащить описание команд на серверную сторону.
...ну и не стоит забывать, что ломание кривонаписанного парсера текстового протокола - это старая-добрая традиция, ещё начиная со времён sendmail'а и irc. А уж как народ с самой почтой развлекается, это просто песня.
Поэтому плюнул я на это дело и взял SOCK_DGRAM
.
Минусы:
- нужен спецклиент
- негарантированная доставка
Но плюсов в моём случае - много:
- разом снимется проблема с разбором сообщений: readv + проверка длинны пакета.
- никаких висящих соединений: пришёл пакет - обработал, опционально ответил.
- бесплатно получаем управление несколькими инстансами в пределах локальной сети (udp+igmp)1.
- возможность использовать тот же код в качестве source/backend: если один хост в сети засёк пидараса - делается рассылка через тот же igmp и все остальные его дружно банят ещё до того, как он полезет сканить порты на следующий узел.
Простенький клиент
Появился в силу предыдущего пункта.
Сейчас он умеет все базовые операции: показать статистику, забанить/разбанить, перечитать конфиг, завершиться.
Есть задел и на больше - точная настройка конкретного jail'а, добавление регэкспов "на лету".
redis-backend
Написан черновик, но пока не тестировался: чтобы его использовать, нужен парный ему source, а source у нас сейчас прибит гвоздями к файлам.
Растущее время поиска/бана для конкретного ip
Думаю, кто пробовал настроить fail2ban видел в логах "умных" ботов, после первого бана подстраивающих частоту запросов так, чтоб не попасть под раздачу.
Это работает, но только до той поры, пока период обнаружения фиксирован. Если он уменьшается по мере того, как растёт число запросов - к ним приходит песец.
На примере: допустим у нас настроено ограничение 5 неправильных запросов в 5 минут. При фиксированном времени обнаружения бот может сделать 4 запроса, и пойти покурить, передав эстафету другому боту.
При динамическом времени обнаружения, на эти 4 запроса в первый раз боту придётся так же подождать 5 минут, а вот в следующий раз - уже 5,5 минут, и т.д.
Вторая идея, логически продолжающая первую - увеличение времени бана при рецидивах. Это тоже сделано, потому что важно не просто забанить бота, но и порвать ему шаблон.
Третья идея в этом плане - смотреть на "соседей" забаненного ip, и расширять бан до подсети при необходимости. Этого у меня пока не реализовано.
CMake
- добавлена поддержка GNUInstallDirs
- наведён порядок с путями конфигов, постараюсь больше не менять.
Багфиксы
Без них - никуда. Исправлена пара сегфолтов в коде фильтров. После этого были сделаны оргвыводы, и притащен strlcpy() на замену snprintf()/strncpy() из директории уровнем выше.
Обеспечена сборка и работа под бзд. Там же был пойман забавный баг:
fgets(buf, bufsize, file->fd);
...это не работает, если у file->fd
выставлен EOF, даже если файл после этого прибавил пару гигабайт.
Решение - сбрасывать руками перед каждым вызовом через clearerr().
Допускаю, что это implementation-defined
поведение.
Ну и кое-что по мелочи, типа выключения буферизации у логфайла.
Это планируется, пока играюсь с point-to-point через unix-сокет. ↩