From 27219355c28b8dd8caa4047c8d0620353b82d9ac Mon Sep 17 00:00:00 2001 From: ssh-monitor Date: Tue, 14 Apr 2026 10:50:38 +1000 Subject: [PATCH] =?UTF-8?q?fix(notify):=20=D0=BE=D1=82=D0=BF=D1=80=D0=B0?= =?UTF-8?q?=D0=B2=D0=BA=D0=B0=20=D0=B2=D0=BE=20=D0=B2=D1=81=D0=B5=20=D0=BA?= =?UTF-8?q?=D0=B0=D0=BD=D0=B0=D0=BB=D1=8B=20NOTIFY=5FORDER,=20=D0=BD=D0=B5?= =?UTF-8?q?=20=D1=82=D0=BE=D0=BB=D1=8C=D0=BA=D0=BE=20=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D0=B2=D1=8B=D0=B9=20=D1=83=D1=81=D0=BF=D0=B5=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - BACKUP_WEBHOOK только если все каналы неуспешны - README/conf.example; версия 0.9.5.3 --- README.md | 8 ++++---- ssh-monitor | 33 ++++++++++++++++++++++----------- ssh-monitor.conf.example | 4 +++- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 0b4226d..b44b490 100644 --- a/README.md +++ b/README.md @@ -14,14 +14,14 @@ Bash-скрипт мониторинга **SSH**, **SUDO** и событий **` - для **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`** в эту проверку **не входит** — он используется только когда основные каналы уже есть, но доставка через них не удалась. +**Обязательное условие:** после загрузки конфигурации в цепочке **`NOTIFY_CHAIN`** должен быть **минимум один** канал (`telegram`, `zabbix` или `email`). Если каналов **нет** (пустой `NOTIFY_ORDER` и ни Telegram, ни Zabbix, ни SMTP не удовлетворяют критериям «настроен», либо в `NOTIFY_ORDER` остались только неизвестные имена), скрипт **не входит** в основной цикл и завершается с кодом **1**, в stderr: **`Не настроен ни один канал отправки оповещений`**. Это же правило действует для **`--check-config`** и **`--dry-run`**. Резервный **`BACKUP_WEBHOOK_URL`** в эту проверку **не входит** — он используется только если **ни один** канал из **`NOTIFY_CHAIN`** не смог доставить сообщение. Поддерживаемые параметры: - `TELEGRAM_BOT_TOKEN`, `TELEGRAM_CHAT_ID` -- `NOTIFY_ORDER` — явная очередь каналов оповещений (`telegram`, `zabbix`, `email` и сокращения); пусто = в цепочку попадают только настроенные каналы (порядок по умолчанию см. в `ssh-monitor.conf.example`). Если итоговая цепочка пуста — скрипт не стартует (см. раздел **«Каналы оповещений и пустой NOTIFY_CHAIN»**). +- `NOTIFY_ORDER` — список каналов (`telegram`, `zabbix`, `email` и сокращения **tg**, **zbx**, **mail**); при каждом оповещении скрипт **пытается отправить во все перечисленные** каналы по порядку (не «первый успешный — и стоп»). Пусто = в цепочку попадают только настроенные каналы (порядок по умолчанию см. в `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) +- `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_FILE` - `ENABLE_LOGIND_MONITOR`, `LOGIND_NOTIFY_NEW`, `LOGIND_NOTIFY_REMOVED`, `LOGIND_NOTIFY_FAILED`, `LOGIND_SKIP_REMOTE` — см. раздел «Мониторинг systemd-logind» ниже - `DAILY_REPORT_HOUR` (0..23), `DAILY_REPORT_TZ` (опционально), `NOTIFY_TZ` (опционально), `SSH_ACCEPT_NOTIFY_DEDUP_SEC`, `DAILY_REPORT_TOP_IPS` @@ -38,7 +38,7 @@ Bash-скрипт мониторинга **SSH**, **SUDO** и событий **` - `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`). +- `NOTIFY_ORDER` — CSV имён каналов (`telegram`, `zabbix`, `email` или `tg`, `zbx`, `mail`). Для **каждого** оповещения выполняется попытка доставки **во все** перечисленные каналы (при ошибке одного остальные всё равно пробуются; неудачи пишутся в **`LOG_FILE`**). Пустая строка: автоматически собирается цепочка только из реально настроенных каналов (по умолчанию порядок 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-элемента: в него уходит **текст** оповещения (одна строка, переводы строк заменены пробелами). diff --git a/ssh-monitor b/ssh-monitor index 73b52d4..90cc952 100644 --- a/ssh-monitor +++ b/ssh-monitor @@ -7,7 +7,7 @@ IFS=$'\n\t' # ============================================ CONFIG_FILE="/etc/ssh-monitor.conf" -SSH_MONITOR_VERSION="0.9.5.2" +SSH_MONITOR_VERSION="0.9.5.3" TELEGRAM_BOT_TOKEN="" TELEGRAM_CHAT_ID="" @@ -291,7 +291,7 @@ is_email_configured() { } # Заполняет NOTIFY_CHAIN: при пустом NOTIFY_ORDER — только реально настроенные каналы (telegram → zabbix → email). -# При непустом NOTIFY_ORDER — ровно указанная очередь (имена каналов), без добавления «лишних». +# При непустом NOTIFY_ORDER — ровно указанный список (имена каналов), без добавления «лишних»; notify_send шлёт во ВСЕ каналы из списка. refresh_notify_chain() { NOTIFY_CHAIN=() local order_raw @@ -470,7 +470,7 @@ write_log() { } # ============================================ -# УВЕДОМЛЕНИЯ: telegram / zabbix / email (каскад по NOTIFY_CHAIN) +# УВЕДОМЛЕНИЯ: telegram / zabbix / email — по NOTIFY_CHAIN во все перечисленные каналы; BACKUP_WEBHOOK только если все неуспешны. # ============================================ send_email_raw() { @@ -638,33 +638,44 @@ notify_send() { return 0 fi + local any_ok=0 local ch for ch in "${NOTIFY_CHAIN[@]}"; do case "$ch" in telegram) if send_telegram_raw "$message"; then - return 0 + any_ok=1 + else + write_log "WARN: канал telegram: не удалось отправить уведомление" fi ;; zabbix) if send_zabbix_trapper "$message"; then - return 0 + any_ok=1 + else + write_log "WARN: канал zabbix: не удалось отправить (zabbix_sender, сеть, имя хоста/ключи в Zabbix)" fi ;; email) if send_email_raw "$message"; then - return 0 + any_ok=1 + else + write_log "WARN: канал email: не удалось отправить уведомление" fi ;; esac done - if send_backup_webhook_raw "$message"; then - write_log "WARN: основные каналы недоступны, сообщение ушло в BACKUP_WEBHOOK_URL" + if [ "$any_ok" -eq 1 ]; then return 0 fi - write_log "WARN: не удалось доставить уведомление (каналы: $(notify_chain_human); BACKUP_WEBHOOK не задан или недоступен)" + if send_backup_webhook_raw "$message"; then + write_log "WARN: все каналы из NOTIFY_CHAIN недоступны, сообщение ушло в BACKUP_WEBHOOK_URL" + return 0 + fi + + write_log "WARN: не удалось доставить уведомление ни в один канал ($(notify_chain_human); BACKUP_WEBHOOK не задан или недоступен)" return 1 } @@ -1620,7 +1631,7 @@ send_startup_notification() { message="${message}🚫 Автоблокировка IP после ${MAX_ATTEMPTS} попыток на ${BAN_TIME} секунд"$'\n' message="${message}⚪ Белый список: ${whitelist_count} IP/подсетей"$'\n' message="${message}❤️ Heartbeat будет отправляться каждые 12 часов"$'\n' - message="${message}📢 Каналы уведомлений (каскад): $(notify_chain_human)" + message="${message}📢 Каналы уведомлений (все по списку): $(notify_chain_human)" notify_send "$message" write_log "Скрипт мониторинга запущен (белый список: $whitelist_count записей)" @@ -1678,7 +1689,7 @@ run_check_config() { printf 'Проверка конфигурации ssh-monitor\n' printf 'SSH_MONITOR_VERSION=%s\n' "${SSH_MONITOR_VERSION:-}" printf 'CONFIG_FILE=%s\n' "$CONFIG_FILE" - printf 'NOTIFY_ORDER=%s (пусто = авто: только настроенные, порядок telegram,zabbix,email)\n' "${NOTIFY_ORDER:-}" + printf 'NOTIFY_ORDER=%s (пусто = авто: только настроенные; при событии — отправка во все каналы списка)\n' "${NOTIFY_ORDER:-}" printf 'NOTIFY_CHAIN (эффективная очередь)=%s\n' "$(notify_chain_human)" printf 'TELEGRAM: %s\n' "$([ -n "$TELEGRAM_BOT_TOKEN" ] && echo настроен || echo НЕ настроен)" printf 'BACKUP_WEBHOOK_URL: %s\n' "$([ -n "${BACKUP_WEBHOOK_URL:-}" ] && echo настроен || echo не задан)" diff --git a/ssh-monitor.conf.example b/ssh-monitor.conf.example index 3591a1c..6af8875 100644 --- a/ssh-monitor.conf.example +++ b/ssh-monitor.conf.example @@ -1,7 +1,7 @@ TELEGRAM_BOT_TOKEN="replace_with_telegram_bot_token" TELEGRAM_CHAT_ID="replace_with_telegram_chat_id" -# Резервный webhook, если все каналы из NOTIFY_CHAIN недоступны (JSON {"text":"..."}). +# Резервный webhook, если ВСЕ каналы из NOTIFY_CHAIN не смогли доставить сообщение (JSON {"text":"..."}). # ВАЖНО: BACKUP_WEBHOOK_URL НЕ заменяет основной канал — без хотя бы одного из telegram/zabbix/email # скрипт сразу завершится с ошибкой в stderr: «Не настроен ни один канал отправки оповещений» (код 1), # в том числе при --check-config и --dry-run. Сначала настройте Telegram, Zabbix или SMTP ниже. @@ -9,6 +9,8 @@ BACKUP_WEBHOOK_URL="" # --- Очередь оповещений (NOTIFY_ORDER) --- # CSV: telegram, zabbix, email (или сокращения tg, zbx, mail). +# При каждом событии скрипт ПЫТАЕТСЯ ОТПРАВИТЬ ВО ВСЕ перечисленные каналы по порядку (не только в первый успешный). +# Неудача одного канала не отменяет отправку в остальные; подробности — в LOG_FILE (WARN по каналам). # # Пустая строка NOTIFY_ORDER="" = авто: в цепочку NOTIFY_CHAIN попадают ТОЛЬКО реально настроенные каналы, # порядок по умолчанию: telegram → zabbix → email (см. критерии «настроен» в README, раздел про каналы).