Иногда кажется, что задача «показать в дашборде, какой IP заблокирован и за что» должна решаться за пять минут. Потом открываешь CrowdSec, Nginx, systemd, архивы логов — и понимаешь, что пять минут уже прошли, а приключение только начинается.
Моя задача была конкретной: связать решения CrowdSec с реальными запросами Nginx и показать в BAN LOG IP, срок блокировки, страну, TARGET, HTTP-код и REQUEST. Причём без гаданий. Если реальный запрос отсутствует — так и показывать пустой REQUEST. Никаких «ну, наверное, это был следующий GET».
Для этого я сделал отдельный sidecar с данными HTTP-событий CrowdSec. При появлении нового ban collector получает alert CrowdSec, извлекает события, а затем сопоставляет их с security_dashboard.log Nginx по IP, времени, HTTP-методу, пути и статусу. Окно корреляции — 5 секунд: достаточно, чтобы учесть небольшую разницу во времени, но недостаточно, чтобы случайно поженить два совершенно разных запроса.
Отдельно мне пришлось разобраться с историческими банами. Часть нужных записей уже успела уехать в сжатые архивы Nginx. Поэтому я сделал одноразовый backfill активных банов, проверил 141 активное решение и восстановил для них sidecar-данные. После этого архивы больше не используются для обычной работы — collector читает только текущий security_dashboard.log.
Особенно забавными оказались HTTP 400 без REQUEST. Сначала они выглядели как проблема корреляции. Но проверка архивных логов показала: нет, это действительно реальные malformed HTTP-запросы, которые Nginx записал с пустым $request. Поэтому я оставил всё как есть. Пустой запрос — значит пустой запрос. Даже если рядом лежит десяток красивых GET.
TARGET для виртуального _ я также привёл к публичному IP сервера, а не к IP атакующего. Потому что атакующий — это IP источника, а TARGET должен показывать, куда прилетел запрос.
И финальная мелочь: окно поиска CrowdSec я увеличил с 5 до 6 минут. Timer запускает collector каждые две минуты, поэтому дополнительная минута — небольшой технический запас на случай задержек, а не повод будить администратора ночью.
В итоге BAN LOG теперь показывает не предположение о том, что произошло, а максимально близкую к исходным данным картину. А сервер продолжает молча делать свою работу. Именно так и должно быть.