contrib/manifest/generate.py + pre-commit hook; install-git-hooks.sh creates .local/build-release-manifest.sh (gitignored). Remove scripts/ from remote.
27 KiB
ssh-monitor
Версия: 2.2.2-SAC
Bash-мониторинг SSH, sudo, systemd-logind: Telegram/email, бан IP (ipset), ежедневный отчёт, heartbeat.
Security Alert Center: модуль sac-client.sh, режим UseSAC (off | exclusive | dual | fallback) — контракт SAC. Основной способ обновления на Linux — кнопка «Обновить ssh-monitor (SSH)» в SAC: сервер передаёт REPO_URL / GIT_BRANCH, updater сам подтягивает git, выставляет права на конфиг/spool/state и перезапускает сервис. Cron/systemd timer — опционально.
Безопасность (2.1.7+): агент работает от root; доверяйте только своему git-зеркалу (REPO_URL) и /etc/ssh-monitor.conf (chmod 600). Updater дописывает обязательные ключи (CONFIG_STRICT_PERMS, SAC_TLS_INSECURE, SAC-*) и на каждом прогоне применяет chmod/chown к конфигу, SAC-spool и state-файлам. Подробнее: docs/security-roadmap.ru.md.
Состав репозитория
| Файл | Назначение |
|---|---|
ssh-monitor |
Основной цикл: разбор журналов (journalctl: sshd, sudo, «тяжёлые» события, systemd-logind), цепочка уведомлений (Telegram, SMTP через python3, резервный webhook), при ENABLE_IP_BAN=1 — учёт неудачных попыток, бан в ipset и согласование с iptables/ip6tables, ежедневный отчёт, heartbeat, опционально Prometheus textfile и JSON health. Режимы: --check-config, --dry-run. |
ssh-monitor-watchdog |
Отдельный скрипт (обычно по systemd timer): проверяет, что сервис монитора активен и файл heartbeat не старше порога; при сбое выполняет systemctl restart и шлёт уведомление в Telegram или в BACKUP_WEBHOOK_URL. Читает те же TELEGRAM_* / webhook из /etc/ssh-monitor.conf. |
update_ssh_monitor.sh |
Автообновление: git + manifest verify, sac-client.sh, перезапуск ssh-monitor.service. См. docs/auto-update.ru.md. |
Системные пакеты и зависимости (что поставить на сервер до запуска): пошагово в docs/install-prerequisites.ru.md.
Конфигурация
Закрытый инстанс Gitea: в доверенном закрытом репозитории допустимо хранить
ssh-monitor.confс боевыми токенами (файл не в.gitignore). В публичном GitHub такой файл игнорируется — используйте толькоssh-monitor.conf.example.
Скрипт читает параметры из /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; - или настройте почту (SMTP) по переменным ниже при
NOTIFY_ORDER=""(автовыбор каналов).
Обязательное условие: после загрузки конфигурации в цепочке NOTIFY_CHAIN должен быть минимум один канал (telegram или email). Если каналов нет (пустой NOTIFY_ORDER и ни Telegram, ни SMTP не удовлетворяют критериям «настроен», либо в NOTIFY_ORDER остались только неизвестные имена), скрипт не входит в основной цикл и завершается с кодом 1, в stderr: Не настроен ни один канал отправки оповещений. Это же правило действует для --check-config и --dry-run. Резервный BACKUP_WEBHOOK_URL в эту проверку не входит — он используется только если ни один канал из NOTIFY_CHAIN не смог доставить сообщение.
Поддерживаемые параметры:
TELEGRAM_BOT_TOKEN,TELEGRAM_CHAT_IDNOTIFY_ORDER— список каналов (telegram,emailи сокращения tg, mail); при каждом оповещении скрипт пытается отправить во все перечисленные каналы по порядку (не «первый успешный — и стоп»). Пусто = в цепочку попадают только настроенные каналы (порядок по умолчанию см. вssh-monitor.conf.example). Если итоговая цепочка пуста — скрипт не стартует (см. раздел «Каналы оповещений и пустой NOTIFY_CHAIN»).MAIL_SMTP_HOST,MAIL_SMTP_PORT,MAIL_SMTP_USER,MAIL_SMTP_PASSWORD,MAIL_FROM,MAIL_TO,MAIL_SMTP_STARTTLS,MAIL_SMTP_SSL— отправка почты через python3 (см. таблицу каналов ниже)BACKUP_WEBHOOK_URL— (deprecated) резервная доставка JSON, если все каналы изNOTIFY_CHAINне смогли доставить; удаление в 2.3.xLOG_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_ENABLED(1/0 — отчёт с агента; 0 = только SAC),DAILY_REPORT_TZ(опционально),NOTIFY_TZ(опционально),SERVER_DISPLAY_NAME(опционально),SSH_ACCEPT_NOTIFY_DEDUP_SEC,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-файлу с меткой последней итерации циклаENABLE_IP_BAN—1(по умолчанию): мониторинг и автобан через ipset;0: только мониторинг и уведомления (без ipset/бана)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,emailилиtg,mail). Для каждого оповещения выполняется попытка доставки во все перечисленные каналы (при ошибке одного остальные всё равно пробуются; неудачи пишутся вLOG_FILE). Пустая строка: автоматически собирается цепочка только из реально настроенных каналов (по умолчанию порядок telegram → email; критерии «настроен» — таблица в разделе «Каналы оповещений и пустой NOTIFY_CHAIN»). Если после сборки цепочки нет ни одного канала, скрипт сразу завершается с ошибкой: «Не настроен ни один канал отправки оповещений» (в том числе режим--check-configи--dry-run).MAIL_SMTP_HOST,MAIL_SMTP_PORT,MAIL_SMTP_USER,MAIL_SMTP_PASSWORD,MAIL_FROM,MAIL_TO,MAIL_SMTP_STARTTLS,MAIL_SMTP_SSL— параметры SMTP для каналаemail(отправка через python3; см. таблицу «настроен»).BACKUP_WEBHOOK_URL— (deprecated) URL резервной отправки; только fallback; не засчитывается как основной канал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(по умолчанию): для новой сессии logind, если доступенloginctl, не отправляется второе Telegram, если сессия уже учтена вmonitor_ssh: приType=sshили еслиServiceуказывает на sshd (частоType=tty+Service=sshd); при пустом ответеloginctlделаются короткие повторы. Поставьте0, если нужны отдельные алерты logind и для SSH-сессий.DAILY_REPORT_HOUR— час (0..23), после наступления которого в текущих календарных сутках (в выбранной ниже зоне) отправляется не более одного ежедневного отчёта.DAILY_REPORT_ENABLED—1(по умолчанию) отправлятьreport.daily.sshс агента;0— не слать (суточный отчёт формирует только SAC, см.SAC_DAILY_REPORT_*).DAILY_REPORT_TZ— необязательная IANA-зона (Europe/Moscow,Asia/Yekaterinburg, …). Если пусто, для отчёта используется та же зона, что и у командыdateу процесса монитора (как правило, совпадает сtimedatectl//etc/localtimeна сервере). Если сервис запускается сTZ=UTCв unit-файле, безDAILY_REPORT_TZотчёт ориентируется на UTC — тогда задайте явную зону в конфиге.NOTIFY_TZ— IANA-зона для строк «🕐 Время» в Telegram/email (и аналогичных текстах). Пусто — используетсяDAILY_REPORT_TZ, если она задана, иначе зона процесса. Частая причина «время в теле сообщения UTC, а в заголовке чата локальное»: вssh-monitor.serviceзаданоEnvironment=TZ=UTC— тогда задайтеNOTIFY_TZ(илиDAILY_REPORT_TZ) на вашу локаль, напримерAsia/Vladivostok.SERVER_DISPLAY_NAME— подпись в строке «🖥️ Сервер» во всех уведомлениях. Пусто —hostnameи при возможности основной IPv4 (hostname (10.x.x.x)). Задайте явное имя, если несколько хостов с одинаковым hostname шлют в один чат. Подробнее: docs/notifications.ru.md.SERVER_IPV4— опциональный override для SAChost.ipv4. Если пусто, IPv4 определяется автоматически.SSH_ACCEPT_NOTIFY_DEDUP_SEC— не чаще одного Telegram по успешному SSH (Accepted) на одну пару пользователь + IP за указанное число секунд (по умолчанию 5). Снимает дубли, когда в journal две строки с разными портами клиента за один вход.0— отключить этот антидубль (останется только дедупликация внутри одного проходаmonitor_sshпоuser|ip|port).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" }.ENABLE_IP_BAN— включить автобан IP (1) или работать только как монитор с уведомлениями (0; ipset не требуется).BAN_TIME— длительность бана IP в секундах (используется приENABLE_IP_BAN=1).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 |
email |
непустые MAIL_SMTP_HOST, MAIL_FROM, MAIL_TO, в PATH есть python3 |
Если итоговый NOTIFY_CHAIN пуст, скрипт завершается с сообщением Не настроен ни один канал отправки оповещений (см. шаг 3 в разделе «Конфигурация» выше).
Мониторинг systemd-logind
- Собираются сообщения юнита
systemd-logindчерезjournalctl(-o cat): новые и завершённые сессии, а также строки с подстрокой failed (если включено). - Без
journalctlна хосте этот блок не работает (как и часть других функций, завязанных на journal). - Для разбора «новой сессии» используются типичные англоязычные форматы (
New session … of user …). При несовпадении формата строка всё равно попадёт в лог с пометкой о неудачном разборе. loginctlнужен дляLOGIND_SKIP_REMOTE: поTypeиServiceрешается, не дублировать ли Telegram сmonitor_ssh(см. описаниеLOGIND_SKIP_REMOTEвыше).monitor_ssh: события sshd читаются из journal преимущественно какjournalctl _COMM=sshd(запасные варианты — юнитыsshd/ssh), строкиsort -u, дляAcceptedв одном проходе — дедуп по пользователь|IP|порт; между проходами цикла — паузаSSH_ACCEPT_NOTIFY_DEDUP_SECдля той же пары пользователь|IP (см. переменную в конфиге).
Команда ssh-monitor --check-config выводит актуальные значения, в том числе параметры logind.
Ограничения на хостах без journald и нюансы /var/log/auth.log: см. раздел 4 в docs/install-prerequisites.ru.md.
Идентификация сервера в уведомлениях
Во всех оповещениях (SSH, sudo, бан IP, logind, heartbeat, ежедневный отчёт и т.д.) после заголовка автоматически добавляется строка «🖥️ Сервер: …» — формат hostname (IPv4) или только hostname, если IPv4 определить не удалось. Логика в notify_send(); дублирование в тексте сообщения не требуется.
- Утилиты
hostnameиip(см. docs/install-prerequisites.ru.md). - Опционально
SERVER_DISPLAY_NAMEв конфиге — фиксированная подпись вместо hostname (удобно при нескольких серверах в одном Telegram-чате). - Типы сообщений и примеры: docs/notifications.ru.md.
Обновление агента
Рекомендуемый путь — SAC: в карточке Linux-хоста кнопка «Обновить ssh-monitor (SSH)». Участие админа на сервере не требуется: SAC передаёт REPO_URL/GIT_BRANCH, updater выполняет git pull, синхронизирует sac-client.sh, дописывает ключи конфига, выставляет права на конфиг/spool/state и перезапускает ssh-monitor.service.
Скрипт update_ssh_monitor.sh также можно запускать вручную или по расписанию (cron/systemd timer) с тем же REPO_URL — поведение идентично.
Пошаговая настройка: docs/auto-update.ru.md.
Режимы запуска
Обычный запуск (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
Релизный архив
Версия задаётся в SSH_MONITOR_VERSION в файле ssh-monitor и дублируется в version.txt (та же строка, что в шапке README). Модуль sac-client.sh использует ту же версию для SAC product_version. Release manifest (release/manifest-*.json): docs/release-manifest.ru.md — после clone выполните ./contrib/install-git-hooks.sh (автогенерация при bump версии).
Сборка tarball из текущего git-дерева:
make dist
Появится файл ssh-monitor-<версия>.tar.gz (через git archive). Готовые архивы для установки без клона репозитория прикладываются к релизам на GitHub.
Установка скрипта, конфигурации и systemd
Автоматическая установка (рекомендуется)
sudo ./first_deploy.sh
# или: sudo ./update_ssh_monitor.sh --deploy
См. docs/auto-update.ru.md — клон из git, копирование в /usr/local/bin/, sac-client.sh, пример /etc/ssh-monitor.conf, unit systemd.
Ручная установка
- Скопируйте скрипт в постоянное место:
sudo install -m 750 ./ssh-monitor /usr/local/bin/ssh-monitorsudo install -m 644 ./sac-client.sh /usr/local/bin/sac-client.sh
- Убедитесь, что конфиг создан (см. раздел «Конфигурация» выше):
sudo cp ./ssh-monitor.conf.example /etc/ssh-monitor.confsudo chmod 600 /etc/ssh-monitor.conf
- Создайте unit-файл
/etc/systemd/system/ssh-monitor.service(илиsudo cp ssh-monitor.service.example ...):
[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_*, если решите их ротировать отдельно).
Watchdog (автоматическое восстановление)
Watchdog проверяет активность ssh-monitor.service и актуальность heartbeat (LAST_HEARTBEAT_FILE). При сбое выполняет systemctl restart и отправляет уведомление (Telegram и при сбое — BACKUP_WEBHOOK_URL, если задан).
Пошаговая установка скрипта ssh-monitor-watchdog и timer в systemd: раздел 6 в docs/install-prerequisites.ru.md.
Ключевые темы (для поиска): мониторинг SSH и sshd, bash-скрипт для Linux-сервера, уведомления в Telegram и по SMTP / электронной почте, systemd-logind, sudo, journalctl, iptables / ip6tables, автоматический бан IP и whitelist, systemd unit, ежедневный отчёт, heartbeat, OpenSSH, безопасность сервера, Prometheus textfile, опционально watchdog для сервиса.
Лицензия
Проект распространяется по лицензии MIT — см. файл LICENSE.