From 4cae3da108264d83ea26f9f9ef696978ab4803b6 Mon Sep 17 00:00:00 2001 From: PTah Date: Mon, 13 Apr 2026 10:06:57 +1000 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=B5=D0=B4=D0=B8=D0=BD=D0=B0=D1=8F=20?= =?UTF-8?q?=D0=B8=D0=BD=D0=B8=D1=86=D0=B8=D0=B0=D0=BB=D0=B8=D0=B7=D0=B0?= =?UTF-8?q?=D1=86=D0=B8=D1=8F=20=D0=BC=D0=B5=D1=82=D0=BE=D0=BA=20LAST=5F*?= =?UTF-8?q?=20=D0=B4=D0=BB=D1=8F=20SSH/sudo/logind?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - checkpoint_stamp_init_if_bad: нет файла / битое значение → только метка «сейчас», без бэклога - sudo/logind: убрано окно -30m при «первом» запуске (как у SSH) - README: раздел про файлы-метки и отличие LAST_SECURITY_EVENTS_FILE --- README.md | 20 ++++++++++++--- ssh-monitor | 74 +++++++++++++++++++++++++++++++---------------------- 2 files changed, 59 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index ca386b9..87b77d6 100644 --- a/README.md +++ b/README.md @@ -38,10 +38,10 @@ Bash-скрипт мониторинга **SSH**, **SUDO** и событий **` - `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 минут истории. +- `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 …*). @@ -76,6 +76,18 @@ Bash-скрипт мониторинга **SSH**, **SUDO** и событий **` Команда **`ssh-monitor --check-config`** выводит актуальные значения, в том числе параметры logind. +### Файлы-метки (`LAST_SSH_CHECK_FILE`, `LAST_SUDO_CHECK_FILE`, `LAST_LOGIND_CHECK_FILE`) + +Для **SSH**, **sudo** и **logind** используется одна и та же политика: + +1. **Файла ещё нет** — при первом же проходе создаётся файл с **текущим** unix-временем, в **`LOG_FILE`** пишется пояснение, **журнал за прошлое не разбирается** (нет лавины уведомлений). +2. **Файл есть, но пустой, не число, ноль или отрицательное значение** — содержимое считается ошибочным, файл **перезаписывается** текущим временем, бэклог **не** обрабатывается (та же защита, что и при первом запуске). +3. **В файле положительное unix-время** — с него строится окно `journalctl --since=@…` (или чтение лог-файлов для sudo при отсутствии journal), обрабатываются только события **после** этой метки. + +Таким образом, «унифицирована» одна и та же модель: **либо валидная метка продолжения, либо безопасная инициализация без ретрансляции истории.** + +Для **`LAST_SECURITY_EVENTS_FILE`** поведение другое: при отсутствии или невалидном значении окно начинается примерно с **часа назад** от текущего момента (чтобы не терять свежие тревоги и не тянуть весь журнал). Это отдельный блок (`monitor_security_events`). + ## Режимы запуска Обычный запуск (root): diff --git a/ssh-monitor b/ssh-monitor index f7436a9..354310e 100644 --- a/ssh-monitor +++ b/ssh-monitor @@ -691,18 +691,45 @@ extract_ssh_client_ip() { printf '%s' "$ip" } +# Единая логика меток для SSH / sudo / logind: +# нет файла или в нём не положительное unix-время → записать «сейчас», залогировать, +# вернуть 0 (вызывающий пропускает разбор бэклога). Иначе: CHECKPOINT_STAMP=метка, вернуть 1. +CHECKPOINT_STAMP="" +checkpoint_stamp_init_if_bad() { + local path="$1" + local log_label="$2" + CHECKPOINT_STAMP="" + local ts + ts=$(date '+%s') + if [ ! -f "$path" ]; then + echo "$ts" >"$path" || true + write_log "${log_label}: первый запуск — создан ${path}; бэклог не обрабатывается." + return 0 + fi + local raw + raw="$(tr -d ' \t\r\n' <"$path" 2>/dev/null || true)" + if ! [[ "$raw" =~ ^[0-9]+$ ]] || [ "${raw:-0}" -le 0 ]; then + echo "$ts" >"$path" || true + write_log "${log_label}: в ${path} ожидается положительное unix-время; значение пересоздано, бэклог не обрабатывается." + return 0 + fi + CHECKPOINT_STAMP="$raw" + return 1 +} + # ============================================ # Мониторинг SSH # ============================================ monitor_ssh() { - local last_check="0" - if [ -f "$LAST_SSH_CHECK_FILE" ]; then - last_check="$(<"$LAST_SSH_CHECK_FILE")" - fi local current_time current_time=$(date '+%s') + if checkpoint_stamp_init_if_bad "$LAST_SSH_CHECK_FILE" "SSH-мониторинг"; then + return 0 + fi + local last_check="$CHECKPOINT_STAMP" + local ssh_logs="" if command -v journalctl >/dev/null 2>&1; then ssh_logs=$(journalctl -u ssh -u sshd --since="@${last_check}" --until="now" 2>/dev/null | grep -E "Accepted|Failed|Disconnected" || true) @@ -816,22 +843,15 @@ parse_sudo_audit_line() { } monitor_sudo() { - local last_check="0" - if [ -f "$LAST_SUDO_CHECK_FILE" ]; then - last_check="$(<"$LAST_SUDO_CHECK_FILE")" - fi - if ! [[ "$last_check" =~ ^[0-9]+$ ]]; then - last_check=0 - fi local current_time current_time=$(date '+%s') - local since_journal - if [ "$last_check" -eq 0 ]; then - since_journal="--since=-30m" - else - since_journal="--since=@${last_check}" + if checkpoint_stamp_init_if_bad "$LAST_SUDO_CHECK_FILE" "SUDO-мониторинг"; then + return 0 fi + local last_check="$CHECKPOINT_STAMP" + + local since_journal="--since=@${last_check}" local sudo_logs="" if command -v journalctl >/dev/null 2>&1; then @@ -986,24 +1006,16 @@ monitor_logind() { return 0 fi - local last_check="0" - if [ -f "$LAST_LOGIND_CHECK_FILE" ]; then - last_check="$(<"$LAST_LOGIND_CHECK_FILE")" - fi - if ! [[ "$last_check" =~ ^[0-9]+$ ]]; then - last_check=0 - fi - - local since_journal - if [ "$last_check" -eq 0 ]; then - since_journal="--since=-30m" - else - since_journal="--since=@${last_check}" - fi - local current_time current_time=$(date '+%s') + if checkpoint_stamp_init_if_bad "$LAST_LOGIND_CHECK_FILE" "LOGIND-мониторинг"; then + return 0 + fi + local last_check="$CHECKPOINT_STAMP" + + local since_journal="--since=@${last_check}" + local logind_lines logind_lines=$(journalctl -u systemd-logind $since_journal --until="now" -o cat --no-pager 2>/dev/null || true)