- checkpoint_stamp_init_if_bad: нет файла / битое значение → только метка «сейчас», без бэклога - sudo/logind: убрано окно -30m при «первом» запуске (как у SSH) - README: раздел про файлы-метки и отличие LAST_SECURITY_EVENTS_FILE Made-with: Cursor
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
- Заполните минимум:
TELEGRAM_BOT_TOKENTELEGRAM_CHAT_ID
Поддерживаемые параметры:
TELEGRAM_BOT_TOKEN,TELEGRAM_CHAT_IDBACKUP_WEBHOOK_URL— резервная доставка JSON{"text":"..."}(например Slack Incoming Webhook), если Telegram недоступен или вернул ошибку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 чата/пользователя, куда отправляются уведомления.BACKUP_WEBHOOK_URL— URL для резервной отправки (тело JSON{"text":"..."}).LOG_FILE— путь к основному лог-файлу скрипта.LAST_HEARTBEAT_FILE— файл с timestamp последнего heartbeat-сообщения.LAST_REPORT_FILE— дата последнего ежедневного отчёта в форматеYYYY-MM-DD(старый формат Unix-времени при первом запуске будет автоматически интерпретирован).LAST_SSH_CHECK_FILE— файл с меткой времени последней проверки SSH-событий (unix-время, одна строка); см. раздел «Файлы-метки» ниже.LAST_SUDO_CHECK_FILE— метка последней проверки sudo-событий; см. «Файлы-метки».LAST_SECURITY_EVENTS_FILE— метка последней проверки «тяжёлых» событий безопасности в журнале (логика первого запуска отличается от SSH/sudo/logind — см. там же).LAST_LOGIND_CHECK_FILE— метка последней обработки журналаsystemd-logind; см. «Файлы-метки».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отключает.
Мониторинг 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.
Файлы-метки (LAST_SSH_CHECK_FILE, LAST_SUDO_CHECK_FILE, LAST_LOGIND_CHECK_FILE)
Для SSH, sudo и logind используется одна и та же политика:
- Файла ещё нет — при первом же проходе создаётся файл с текущим unix-временем, в
LOG_FILEпишется пояснение, журнал за прошлое не разбирается (нет лавины уведомлений). - Файл есть, но пустой, не число, ноль или отрицательное значение — содержимое считается ошибочным, файл перезаписывается текущим временем, бэклог не обрабатывается (та же защита, что и при первом запуске).
- В файле положительное unix-время — с него строится окно
journalctl --since=@…(или чтение лог-файлов для sudo при отсутствии journal), обрабатываются только события после этой метки.
Таким образом, «унифицирована» одна и та же модель: либо валидная метка продолжения, либо безопасная инициализация без ретрансляции истории.
Для LAST_SECURITY_EVENTS_FILE поведение другое: при отсутствии или невалидном значении окно начинается примерно с часа назад от текущего момента (чтобы не терять свежие тревоги и не тянуть весь журнал). Это отдельный блок (monitor_security_events).
Режимы запуска
Обычный запуск (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