feat: единая инициализация меток LAST_* для SSH/sudo/logind
- checkpoint_stamp_init_if_bad: нет файла / битое значение → только метка «сейчас», без бэклога - sudo/logind: убрано окно -30m при «первом» запуске (как у SSH) - README: раздел про файлы-метки и отличие LAST_SECURITY_EVENTS_FILE
This commit is contained in:
@@ -38,10 +38,10 @@ Bash-скрипт мониторинга **SSH**, **SUDO** и событий **`
|
|||||||
- `LOG_FILE` — путь к основному лог-файлу скрипта.
|
- `LOG_FILE` — путь к основному лог-файлу скрипта.
|
||||||
- `LAST_HEARTBEAT_FILE` — файл с timestamp последнего heartbeat-сообщения.
|
- `LAST_HEARTBEAT_FILE` — файл с timestamp последнего heartbeat-сообщения.
|
||||||
- `LAST_REPORT_FILE` — дата последнего ежедневного отчёта в формате `YYYY-MM-DD` (старый формат Unix-времени при первом запуске будет автоматически интерпретирован).
|
- `LAST_REPORT_FILE` — дата последнего ежедневного отчёта в формате `YYYY-MM-DD` (старый формат Unix-времени при первом запуске будет автоматически интерпретирован).
|
||||||
- `LAST_SSH_CHECK_FILE` — файл с меткой времени последней проверки SSH-событий.
|
- `LAST_SSH_CHECK_FILE` — файл с меткой времени последней проверки SSH-событий (unix-время, одна строка); см. раздел «Файлы-метки» ниже.
|
||||||
- `LAST_SUDO_CHECK_FILE` — файл с меткой времени последней проверки sudo-событий.
|
- `LAST_SUDO_CHECK_FILE` — метка последней проверки sudo-событий; см. «Файлы-метки».
|
||||||
- `LAST_SECURITY_EVENTS_FILE` — метка последней проверки «тяжёлых» событий безопасности в журнале.
|
- `LAST_SECURITY_EVENTS_FILE` — метка последней проверки «тяжёлых» событий безопасности в журнале (логика первого запуска **отличается** от SSH/sudo/logind — см. там же).
|
||||||
- `LAST_LOGIND_CHECK_FILE` — метка последней обработки журнала `systemd-logind` (unix-время); при первом запуске подтягивается около 30 минут истории.
|
- `LAST_LOGIND_CHECK_FILE` — метка последней обработки журнала `systemd-logind`; см. «Файлы-метки».
|
||||||
- `BAN_LIST_FILE` — файл состояния банов (IP, время окончания бана и метаданные).
|
- `BAN_LIST_FILE` — файл состояния банов (IP, время окончания бана и метаданные).
|
||||||
- `ENABLE_LOGIND_MONITOR` — `1` включает опрос `journalctl -u systemd-logind`, `0` полностью отключает этот блок.
|
- `ENABLE_LOGIND_MONITOR` — `1` включает опрос `journalctl -u systemd-logind`, `0` полностью отключает этот блок.
|
||||||
- `LOGIND_NOTIFY_NEW` — `1` отправлять Telegram при появлении новой сессии в logind (строки вида *New session … of user …*).
|
- `LOGIND_NOTIFY_NEW` — `1` отправлять Telegram при появлении новой сессии в logind (строки вида *New session … of user …*).
|
||||||
@@ -76,6 +76,18 @@ Bash-скрипт мониторинга **SSH**, **SUDO** и событий **`
|
|||||||
|
|
||||||
Команда **`ssh-monitor --check-config`** выводит актуальные значения, в том числе параметры logind.
|
Команда **`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):
|
Обычный запуск (root):
|
||||||
|
|||||||
+43
-31
@@ -691,18 +691,45 @@ extract_ssh_client_ip() {
|
|||||||
printf '%s' "$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
|
# Мониторинг SSH
|
||||||
# ============================================
|
# ============================================
|
||||||
|
|
||||||
monitor_ssh() {
|
monitor_ssh() {
|
||||||
local last_check="0"
|
|
||||||
if [ -f "$LAST_SSH_CHECK_FILE" ]; then
|
|
||||||
last_check="$(<"$LAST_SSH_CHECK_FILE")"
|
|
||||||
fi
|
|
||||||
local current_time
|
local current_time
|
||||||
current_time=$(date '+%s')
|
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=""
|
local ssh_logs=""
|
||||||
if command -v journalctl >/dev/null 2>&1; then
|
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)
|
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() {
|
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
|
local current_time
|
||||||
current_time=$(date '+%s')
|
current_time=$(date '+%s')
|
||||||
|
|
||||||
local since_journal
|
if checkpoint_stamp_init_if_bad "$LAST_SUDO_CHECK_FILE" "SUDO-мониторинг"; then
|
||||||
if [ "$last_check" -eq 0 ]; then
|
return 0
|
||||||
since_journal="--since=-30m"
|
|
||||||
else
|
|
||||||
since_journal="--since=@${last_check}"
|
|
||||||
fi
|
fi
|
||||||
|
local last_check="$CHECKPOINT_STAMP"
|
||||||
|
|
||||||
|
local since_journal="--since=@${last_check}"
|
||||||
|
|
||||||
local sudo_logs=""
|
local sudo_logs=""
|
||||||
if command -v journalctl >/dev/null 2>&1; then
|
if command -v journalctl >/dev/null 2>&1; then
|
||||||
@@ -986,24 +1006,16 @@ monitor_logind() {
|
|||||||
return 0
|
return 0
|
||||||
fi
|
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
|
local current_time
|
||||||
current_time=$(date '+%s')
|
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
|
local logind_lines
|
||||||
logind_lines=$(journalctl -u systemd-logind $since_journal --until="now" -o cat --no-pager 2>/dev/null || true)
|
logind_lines=$(journalctl -u systemd-logind $since_journal --until="now" -o cat --no-pager 2>/dev/null || true)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user