Made-with: Cursor
20 KiB
ssh-monitor
Bash-скрипт мониторинга SSH, SUDO и событий systemd-logind (локальные/графические сессии и др.) с уведомлениями в Telegram, опциональным резервным webhook, авто-блокировкой IP (IPv4 через iptables, IPv6 через ip6tables при наличии) и ежедневным отчётом.
Конфигурация
Скрипт читает параметры из /etc/ssh-monitor.conf в формате KEY="value".
- Скопируйте пример:
sudo cp ./ssh-monitor.conf.example /etc/ssh-monitor.conf
- Ограничьте доступ:
sudo chmod 600 /etc/ssh-monitor.conf
- Заполните минимум — хотя бы один канал доставки оповещений (иначе скрипт сразу завершится с ошибкой; см. абзац «Обязательное условие» ниже и раздел «Каналы оповещений и пустой NOTIFY_CHAIN»):
- для Telegram:
TELEGRAM_BOT_TOKENиTELEGRAM_CHAT_ID; - или настройте Zabbix / почту по переменным ниже при
NOTIFY_ORDER=""(автовыбор каналов).
Обязательное условие: после загрузки конфигурации в цепочке NOTIFY_CHAIN должен быть минимум один канал (telegram, zabbix или email). Если каналов нет (пустой NOTIFY_ORDER и ни Telegram, ни Zabbix, ни SMTP не удовлетворяют критериям «настроен», либо в NOTIFY_ORDER остались только неизвестные имена), скрипт не входит в основной цикл и завершается с кодом 1, в stderr: Не настроен ни один канал отправки оповещений. Это же правило действует для --check-config и --dry-run. Резервный BACKUP_WEBHOOK_URL в эту проверку не входит — он используется только когда основные каналы уже есть, но доставка через них не удалась.
Поддерживаемые параметры:
TELEGRAM_BOT_TOKEN,TELEGRAM_CHAT_IDNOTIFY_ORDER— явная очередь каналов оповещений (telegram,zabbix,emailи сокращения); пусто = в цепочку попадают только настроенные каналы (порядок по умолчанию см. вssh-monitor.conf.example). Если итоговая цепочка пуста — скрипт не стартует (см. раздел «Каналы оповещений и пустой NOTIFY_CHAIN»).ZABBIX_SERVER,ZABBIX_HOST_NAME,ZABBIX_ALERT_KEY,ZABBIX_SEQ_KEY— отправка в Zabbix черезzabbix_sender; подробнее — раздел «Zabbix (zabbix_sender)» нижеBACKUP_WEBHOOK_URL— резервная доставка JSON{"text":"..."}(например Slack Incoming Webhook), если каналы изNOTIFY_CHAINнедоступны или вернули ошибку; не заменяет обязательность хотя бы одного основного канала (см. шаг 3)LOG_FILE,LAST_HEARTBEAT_FILE,LAST_REPORT_FILE,LAST_SSH_CHECK_FILE,LAST_SUDO_CHECK_FILE,LAST_SECURITY_EVENTS_FILE,LAST_LOGIND_CHECK_FILE,BAN_LIST_FILEENABLE_LOGIND_MONITOR,LOGIND_NOTIFY_NEW,LOGIND_NOTIFY_REMOVED,LOGIND_NOTIFY_FAILED,LOGIND_SKIP_REMOTE— см. раздел «Мониторинг systemd-logind» нижеDAILY_REPORT_HOUR(0..23),DAILY_REPORT_TZ(опционально),DAILY_REPORT_TOP_IPSBRUTE_WINDOW_SEC,BRUTE_MIN_FAILS,BRUTE_NOTIFY_COOLDOWN_SECPROMETHEUS_TEXTFILE_DIR— каталог дляssh_monitor.prom(совместимость с node_exporter textfile collector)HEALTHCHECK_STATUS_FILE— путь к JSON-файлу с меткой последней итерации циклаBAN_TIME,MAX_ATTEMPTS,BAN_CHECK_INTERVAL,MONITOR_INTERVALWHITELIST_IPS,WHITELIST_SUBNETS(CSV, напримерip1,ip2)WATCHDOG_MAX_HEARTBEAT_AGE,WATCHDOG_LOG_FILE,WATCHDOG_SERVICE_NAME,WATCHDOG_NOTIFY_ON_RECOVERY
Если файл /etc/ssh-monitor.conf отсутствует или часть значений пустая, используются значения по умолчанию из скрипта.
Описание переменных в /etc/ssh-monitor.conf
TELEGRAM_BOT_TOKEN— токен Telegram-бота для отправки уведомлений.TELEGRAM_CHAT_ID— ID чата/пользователя, куда отправляются уведомления.NOTIFY_ORDER— CSV имён каналов (telegram,zabbix,emailилиtg,zbx,mail). Пустая строка: автоматически собирается цепочка только из реально настроенных каналов (по умолчанию порядок telegram → zabbix → email; критерии «настроен» — таблица в разделе «Каналы оповещений и пустой NOTIFY_CHAIN»). Если после сборки цепочки нет ни одного канала, скрипт сразу завершается с ошибкой: «Не настроен ни один канал отправки оповещений» (в том числе режим--check-configи--dry-run).ZABBIX_SERVER— имя или IP сервера Zabbix дляzabbix_sender -z(пусто = канал Zabbix отключён).ZABBIX_HOST_NAME— имя хоста в Zabbix, как в конфигурации агента/шаблона (-sуzabbix_sender); должно совпадать с тем, для какого хоста созданы trapper-элементы.ZABBIX_ALERT_KEY— ключ первого trapper-элемента: в него уходит текст оповещения (одна строка, переводы строк заменены пробелами).ZABBIX_SEQ_KEY— ключ второго trapper-элемента: в него при каждой отправке пишется монотонно растущий счётчик (1, 2, 3, … за время работы процесса скрипта). Значение по умолчанию —ssh.monitor.seq. Назначение: чтобы Zabbix и триггеры видели новое значение даже при одинаковом тексте алерта (иначе повтор с тем же текстом может плохо отражаться на логике «изменилось ли значение»); также по счётчику удобнее отслеживать порядок событий. В шаблоне Zabbix нужны два элемента типа Zabbix trapper с ключами, совпадающими сZABBIX_ALERT_KEYиZABBIX_SEQ_KEY(второй — обычно числовой).BACKUP_WEBHOOK_URL— URL для резервной отправки (тело JSON{"text":"..."}); только fallback при сбое основных каналов, не засчитывается как «настроенный канал» (см. шаг 3).LOG_FILE— путь к основному лог-файлу скрипта.LAST_HEARTBEAT_FILE— файл с timestamp последнего heartbeat-сообщения.LAST_REPORT_FILE— дата последнего ежедневного отчёта в форматеYYYY-MM-DD(старый формат Unix-времени при первом запуске будет автоматически интерпретирован).LAST_SSH_CHECK_FILE— файл с меткой времени последней проверки SSH-событий.LAST_SUDO_CHECK_FILE— файл с меткой времени последней проверки sudo-событий.LAST_SECURITY_EVENTS_FILE— метка последней проверки «тяжёлых» событий безопасности в журнале.LAST_LOGIND_CHECK_FILE— метка последней обработки журналаsystemd-logind(unix-время); при первом запуске подтягивается около 30 минут истории.BAN_LIST_FILE— файл состояния банов (IP, время окончания бана и метаданные).ENABLE_LOGIND_MONITOR—1включает опросjournalctl -u systemd-logind,0полностью отключает этот блок.LOGIND_NOTIFY_NEW—1отправлять Telegram при появлении новой сессии в logind (строки вида New session … of user …).LOGIND_NOTIFY_REMOVED—1отправлять Telegram при завершении сессии (Removed session …); по умолчанию0(только запись вLOG_FILE, чтобы не заспамить канал).LOGIND_NOTIFY_FAILED—1отправлять Telegram по строкам logind, содержащим failed (широкий фильтр; при необходимости отключите).LOGIND_SKIP_REMOTE—1(по умолчанию): для новой сессии, если доступенloginctl, запрашивается тип сессии; приType=sshвторое уведомление в Telegram не отправляется (успешный SSH уже покрываетmonitor_ssh), в лог пишется пояснение. Поставьте0, если нужны отдельные алерты logind и для SSH-сессий.DAILY_REPORT_HOUR— час (0..23), после наступления которого в текущих календарных сутках (в выбранной ниже зоне) отправляется не более одного ежедневного отчёта.DAILY_REPORT_TZ— необязательная IANA-зона (Europe/Moscow,Asia/Yekaterinburg, …). Если пусто, для отчёта используется та же зона, что и у командыdateу процесса монитора (как правило, совпадает сtimedatectl//etc/localtimeна сервере). Если сервис запускается сTZ=UTCв unit-файле, безDAILY_REPORT_TZотчёт ориентируется на UTC — тогда задайте явную зону в конфиге.DAILY_REPORT_TOP_IPS— сколько IP показывать в топе неудачных попыток за 24 часа.BRUTE_WINDOW_SEC— окно (секунды) для оценки «массового» брутфорса поjournalctl.BRUTE_MIN_FAILS— минимум неудачных попыток за окно для тревоги.BRUTE_NOTIFY_COOLDOWN_SEC— пауза между повторными уведомлениями по одному и тому же IP.PROMETHEUS_TEXTFILE_DIR— если задан существующий каталог, на каждой итерации пишетсяssh_monitor.promс метрикойssh_monitor_last_loop_unixtime.HEALTHCHECK_STATUS_FILE— если задан, на каждой итерации обновляется JSON{ "ts", "hostname", "dry_run" }.BAN_TIME— длительность бана IP в секундах.MAX_ATTEMPTS— число неудачных SSH-попыток до автоматического бана.BAN_CHECK_INTERVAL— интервал проверки просроченных банов и аудита согласованности бан-листа с firewall (секунды).MONITOR_INTERVAL— пауза между итерациями основного цикла мониторинга (секунды).WHITELIST_IPS— белый список IP через запятую (IPv4 и IPv6 — точное совпадение).WHITELIST_SUBNETS— белый список подсетей только IPv4 CIDR через запятую (например10.0.0.0/24).WATCHDOG_MAX_HEARTBEAT_AGE— через сколько секунд heartbeat считается устаревшим.WATCHDOG_LOG_FILE— лог-файл watchdog-скрипта.WATCHDOG_SERVICE_NAME— имя systemd-сервиса, который контролирует watchdog (по умолчаниюssh-monitor.service).WATCHDOG_NOTIFY_ON_RECOVERY—1включает служебные сообщения watchdog при штатном состоянии,0отключает.
Каналы оповещений и пустой NOTIFY_CHAIN
Кратко, что считается настроенным каналом (только такие попадают в автоматическую цепочку при NOTIFY_ORDER=""):
| Канал | Условие «настроен» |
|---|---|
telegram |
заданы TELEGRAM_BOT_TOKEN и TELEGRAM_CHAT_ID |
zabbix |
непустые ZABBIX_SERVER, ZABBIX_HOST_NAME, в PATH есть zabbix_sender |
email |
непустые MAIL_SMTP_HOST, MAIL_FROM, MAIL_TO, в PATH есть python3 |
Если итоговый NOTIFY_CHAIN пуст, скрипт завершается с сообщением Не настроен ни один канал отправки оповещений (см. шаг 3 в разделе «Конфигурация» выше).
Zabbix (zabbix_sender)
Канал активен, если в NOTIFY_CHAIN есть zabbix, заданы ZABBIX_SERVER и ZABBIX_HOST_NAME, в PATH есть zabbix_sender.
При отправке формируется временный файл с двумя строками в формате ключ<TAB>значение:
ZABBIX_ALERT_KEY(по умолчаниюssh.monitor.alert) — текст сообщения.ZABBIX_SEQ_KEY(по умолчаниюssh.monitor.seq) — целое число-счётчик, увеличивается на 1 при каждой отправке в Zabbix.
Имеет смысл завести в Zabbix на соответствующем хосте два trapper-элемента с этими ключами. Ключ ZABBIX_SEQ_KEY можно переименовать в конфиге, если в вашем шаблоне приняты другие имена; смысл остаётся тем же: отдельный item под последовательность, а не под человекочитаемый текст.
Мониторинг systemd-logind
- Собираются сообщения юнита
systemd-logindчерезjournalctl(-o cat): новые и завершённые сессии, а также строки с подстрокой failed (если включено). - Без
journalctlна хосте этот блок не работает (как и часть других функций, завязанных на journal). - Для разбора «новой сессии» используются типичные англоязычные форматы (
New session … of user …). При несовпадении формата строка всё равно попадёт в лог с пометкой о неудачном разборе. loginctlнужен только дляLOGIND_SKIP_REMOTE: определяетсяTypeсессии; для удалённого SSH обычноssh, чтобы не дублировать уведомление с блоком мониторинга SSH.
Команда ssh-monitor --check-config выводит актуальные значения, в том числе параметры logind.
Режимы запуска
Обычный запуск (root):
sudo bash ./ssh-monitor
Проверка конфигурации и синтаксиса без запуска цикла, без изменений iptables и без обязательного Telegram:
sudo bash ./ssh-monitor --check-config
Режим dry-run (правила firewall не меняются; уведомления выводятся в stderr вместо отправки):
sudo bash ./ssh-monitor --dry-run
Проверка синтаксиса
bash -n ./ssh-monitor
Автозапуск через systemd
- Скопируйте скрипт в постоянное место:
sudo install -m 750 ./ssh-monitor /usr/local/bin/ssh-monitor
- Убедитесь, что конфиг есть:
sudo cp ./ssh-monitor.conf.example /etc/ssh-monitor.conf(если еще не создан)sudo chmod 600 /etc/ssh-monitor.conf
- Создайте unit-файл
/etc/systemd/system/ssh-monitor.service:
[Unit]
Description=SSH Monitor with Telegram alerts
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/local/bin/ssh-monitor
Restart=always
RestartSec=5
User=root
[Install]
WantedBy=multi-user.target
- Включите и запустите сервис:
sudo systemctl daemon-reload
sudo systemctl enable ssh-monitor
sudo systemctl start ssh-monitor
- Проверка состояния и логов:
sudo systemctl status ssh-monitor
sudo journalctl -u ssh-monitor -f
Ротация логов
Пример для logrotate лежит в репозитории: contrib/logrotate.d/ssh-monitor. Скопируйте файл в /etc/logrotate.d/ и при необходимости поправьте пути под ваши LOG_FILE / WATCHDOG_LOG_FILE и другие файлы состояния в /var/log/ (в т.ч. LAST_*, если решите их ротировать отдельно).
Статистика без systemd (journalctl)
Для корректного подсчёта событий за последние 24 часа по файлам /var/log/auth.log* рекомендуется наличие Python 3 на сервере. При его отсутствии счётчики в ежедневном отчёте для «классического» syslog могут быть занижены (используется безопасный fallback).
Мониторинг systemd-logind и другие части, использующие journalctl, на таких системах не выполняются или дают неполные данные — ориентируйтесь на окружение с systemd и доступным журналом.
Watchdog (автоматическое восстановление)
Watchdog проверяет:
- активен ли
ssh-monitor.service; - не устарел ли heartbeat (
LAST_HEARTBEAT_FILE).
Если сервис неактивен или heartbeat старше WATCHDOG_MAX_HEARTBEAT_AGE, watchdog выполняет systemctl restart и отправляет уведомление (Telegram и при сбое — BACKUP_WEBHOOK_URL, если задан).
Установка watchdog
- Установите watchdog-скрипт:
sudo install -m 750 ./ssh-monitor-watchdog /usr/local/bin/ssh-monitor-watchdog
- Создайте unit и timer из примеров:
sudo cp ./ssh-monitor-watchdog.service.example /etc/systemd/system/ssh-monitor-watchdog.servicesudo cp ./ssh-monitor-watchdog.timer.example /etc/systemd/system/ssh-monitor-watchdog.timer
- Примените и запустите timer:
sudo systemctl daemon-reload
sudo systemctl enable ssh-monitor-watchdog.timer
sudo systemctl start ssh-monitor-watchdog.timer
- Проверка:
sudo systemctl status ssh-monitor-watchdog.timer
sudo systemctl list-timers | grep ssh-monitor-watchdog
sudo journalctl -u ssh-monitor-watchdog.service -f