logowl
,___,
(O,O)
( )
--"-"--
LogOwl — страж nginx-логов: детект DDoS/флуда/сканеров в реальном времени, псевдографический TUI (curses) с совой, гибкая конфигурация, белый список, настраиваемые пороги и действия (бан / rate-limit / 444), уведомления (webhook, email), сохранение отчётов.
Только стандартная библиотека Python 3.8+. На целевую машину не нужно ставить ничего: логи читаются из файлов, journalctl или по обычному SSH (tail -F).
Возможности
-
Источники: локальные файлы (с обработкой ротации и truncate), systemd journal (
journalctl -f), удалённая машина по SSH (файлы и journal) — без агентов на цели. - Детекция: скользящие окна на IP, пороги запросов, доля 4xx/5xx, уникальные URL (сканеры), regex по URL, брутфорс конкретных путей, глобальный объёмный порог.
-
Действия:
ban(iptables/nftables — шаблоны команд),limit(динамическая nginx-map дляlimit_req),444(динамическая nginx-map сreturn 444), TTL с автобаном-разбаном, анти-дребезг, жёсткий лимит команд в минуту. -
Безопасность:
dry_runпо умолчанию (ничего не исполняется), whitelist никогда не трогается, IP строго валидируются перед подстановкой в команды, отчёты не содержат секретов. - Уведомления: webhook (JSON POST) и email (SMTP/STARTTLS/SSL), троттлинг и капы против спама.
-
Отчёты:
txt/json/html— вручную (клавишаSв TUI) и по расписанию. - TUI: сова (моргает), спарклайн RPS, таблица топ-обидчиков, события, ручные баны/лимиты прямо из интерфейса.
Быстрый старт
# сгенерировать конфиг с комментариями и отредактировать
python3 -m logowl --gen-config my.json
# live-режим с TUI
python3 -m logowl -c my.json
# без TUI (например, на сервере в tmux или для отладки)
python3 -m logowl -c my.json --no-tui
# офлайн-анализ файла с сохранением отчёта (без действий и уведомлений)
python3 -m logowl --once /var/log/nginx/access.log --report-dir reports
# демо на синтетической атаке
python3 examples/gen_attack_log.py attack.log 20000
python3 -m logowl --once attack.log
# наглядный стенд с живой эмуляцией атаки (см. demo/README.md)
python3 demo/attack_sim.py --log demo/access.log --loop
python3 -m logowl -c demo/demo_config.jsonc
TUI требует Unix-терминал (curses). На Windows доступен --no-tui и --once; для TUI на Windows поставьте pip install windows-curses.
Ключи командной строки
| Ключ | Значение |
|---|---|
-c, --config PATH |
путь к конфигу (JSON с //-комментариями и хвостовыми запятыми) |
--no-tui |
без curses, статусная строка раз в 10 с |
--once FILE |
офлайн-анализ файла(ов), отчёт, выход. Действия и уведомления отключены |
--report-dir DIR |
переопределить reports.dir
|
--dry-run / --live
|
принудительно включить/выключить dry-run (перекрывает конфиг) |
--gen-config PATH |
записать пример конфига |
--version |
версия |
Конфигурация
Полный пример с комментариями: config.example.jsonc (или --gen-config). Неизвестные поля и отсутствующие секции берутся из безопасных дефолтов. Ключевые поля:
general.log_files локальные файлы логов
general.journal journalctl-источники: "nginx.service" или {"unit": "...", "identifier": "...", "sudo":..., "stdbuf":..., "extra_args":[...]}
general.remote ssh-источник: {host, port, user, key, sudo, log_files[], journal[]}
general.from_end false = читать с начала (иначе tail с конца)
general.dry_run true (по умолчанию!) = только логировать действия
general.poll_interval / update_interval / max_lines_per_tick / queue_size / tracked_ip_cap
general.trust_xff брать IP из X-Forwarded-For (только за своим доверенным прокси)
log_format "combined" или строка формата nginx с $переменными
whitelist IP и CIDR — никогда не блокируются
rules[] правила детекции (см. ниже)
actions команды бана, map-файлы, reload, лимиты
notifications webhook / email
reports каталог, интервал, форматы
tui owl, blink, top_n, events_n
Правила детекции
{
"name": "http-flood", // имя (уникальное)
"window": 10, // окно, секунд
"requests_per_ip": 100, // порог запросов с одного IP за окно
"status": ["4xx"], // фильтр: "4xx","5xx",404,"any"; + "min_ratio": 0.7 — доля таких строк среди всех запросов IP
"methods": ["GET","POST"], // фильтр по методу
"url_regex": "wp-login\\.php", // фильтр по URL (брутфорс конкретного пути)
"unique_urls": 30, // IP тронул N разных URL — сканер
"min_total": 20, // минимум запросов за окно, прежде чем ratio-правило сработает
"requests_total": 2000, // глобальный порог (scope: "global") — объёмная атака с многих IP
"actions": ["ban"], // "ban" | "limit" | "444"
"ban_ttl": 3600, // срок действия бана/лимита, сек
"cooldown": 300 // анти-дребезг повторных срабатываний на тот же IP
}
Условия внутри одного правила объединяются по И. Правил может быть сколько угодно — каждое со своим окном, фильтрами и действием.
Действия
-
ban — шаблоны команд,
{ip}подставляется после строгой валидации:"ban": { "commands": ["iptables -I INPUT -s {ip} -j DROP"], "unban_commands": ["iptables -D INPUT -s {ip} -j DROP"], "ttl": 3600 }Вариант nftables:
nft add element inet logowl blacklist { {ip} }. -
limit / 444 — logowl ведёт динамические map-файлы и перезагружает nginx. Добавьте в конфиг nginx один раз:
# /etc/nginx/conf.d/logowl.conf geo $logowl_limit { default 0; include /etc/nginx/logowl/limit.map; } geo $logowl_block_444 { default 0; include /etc/nginx/logowl/444.map; } # ограничение скорости: обычные клиенты не считаются (пустой ключ), лимитируемые — считаются map $logowl_limit $logowl_limit_key { 0 ""; 1 $binary_remote_addr; } limit_req_zone $logowl_limit_key zone=logowl_limit:10m rate=10r/s; server { ... limit_req zone=logowl_limit burst=20 nodelay; if ($logowl_block_444) { return 444; } }В конфиге logowl:
"limit": { "map_path": "/etc/nginx/logowl/limit.map", "reload": ["nginx -t && nginx -s reload"], "min_reload_interval": 10, "ttl": 3600 }, "444": { "map_path": "/etc/nginx/logowl/444.map", "reload": ["nginx -t && nginx -s reload"], "min_reload_interval": 10, "ttl": 1800 }
Перезапись map-файла и reload выполняются только при реальных изменениях и не чаще min_reload_interval. При remote файл заливается через ssh "cat > ... && mv ...".
Все реальные команды ограничены actions.max_per_minute (анти-runaway), whitelist-IP отказываются всегда, дубликаты (уже забанен/уже в map) пропускаются.
Источники без установки ПО на цель
- файлы:
tail-подобное чтение с обработкой ротации; - journal: nginx должен писать в journald, например
access_log syslog:server=unix:/dev/log,facility=local1,tag=access,severity=info;затем"journal": ["nginx.service"](или"journal": [{"identifier": "access"}]); - ssh: пользователю на цели достаточно прав на чтение лога (группа
adm/systemd-journal) и, приsudo: true, passwordless sudo. Используются толькоssh,tail,journalctl.
TUI
,___, LOGOWL v1.0.0 [LIVE] host 2026-10-06 16:30:00
(O,O) RPS 57 ▂▃▅▇█▅▃▂▁...
( ) total 50 005 IPs 12 bans 13 lim 2 blk 7 ...
--"-"--
──────────────────────────────────────────────────────
TOP OFFENDERS ... ip, r10, r60, total, 4xx, 5xx, flags(B/L/4), UA
EVENTS ... детекты, действия, ошибки источников
──────────────────────────────────────────────────────
q:quit p:pause s:report v:bans b:ban l:limit 4:block444 u:unban x:un444 h:help
Клавиши: q выход, p пауза, s сохранить отчёт, v баны/map'ы, b/l/4 действие по выбранному IP (↑/↓ — выбор), u unban, x снять 444, h помощь. Бейдж DRY-RUN означает, что команды не исполняются.
Уведомления
-
webhook: POST JSON
{type, ts, ip, rule, ttl, dry_run, host, ...}на каждый детект/бан/лимит; заголовокX-LogOwl-Secretпри"secret". - email: SMTP с STARTTLS или SSL.
- Антиспам:
min_intervalна пару (тип события, IP) иmax_per_minuteглобально.
Отчёты
snapshots в reports.dir (report-YYYYmmdd-HHMMSS.txt|json|html): суммарная статистика, статусы, топ IP, все детекции, активные баны, обезличенный конфиг (пароли/секреты маскируются). Автосохранение по reports.interval (0 = только вручную).
Производительность и безопасность
- один процесс, thread на источник + главный цикл; ограничение батча
max_lines_per_tick, ограниченная очередь (queue_size, переполнение считается, а не ломает систему); - память ограничена:
tracked_ip_capс вытеснением старых IP, оконные деки с жёсткими капами, периодическая чисткаprune_interval; - CPU: слэбы по
update_interval; TUI перерисовывается 2–5 раз в секунду; - действия: dry-run по умолчанию, валидация IP (
ipaddress), капы, дедупликация, таймауты на subprocess, никакого shell-инжекта из данных лога.
systemd-юнит для самого logowl
[Unit]
Description=LogOwl nginx DDoS sentinel
After=network-online.target
[Service]
ExecStart=/usr/bin/python3 -m logowl -c /etc/logowl/logowl.json --no-tui
Restart=always
RestartSec=5
# для исполнения банов
AmbientCapabilities=CAP_NET_ADMIN
NoNewPrivileges=true
[Install]
WantedBy=multi-user.target
Разработка
python3 -m unittest discover -s tests
Структура: logowl/parser.py (разбор формата nginx), tailer.py (источники), analyzer.py (окна и правила), actions.py (ban/limit/444), notify.py, reports.py, engine.py (связка), tui.py (интерфейс с совой).