feat: unify daily SSH report format with SAC; DAILY_REPORT_ENABLED toggle

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
PTah
2026-05-29 17:17:59 +10:00
parent eceac58a60
commit 3820687310
4 changed files with 42 additions and 21 deletions
+38 -18
View File
@@ -52,6 +52,7 @@ MAIL_SMTP_SSL="0"
LOG_FILE="/var/log/ssh_monitor.log"
LAST_HEARTBEAT_FILE="/var/log/last_heartbeat.txt"
LAST_REPORT_FILE="/var/log/last_daily_report.txt"
DAILY_REPORT_ENABLED=1
DAILY_REPORT_HOUR=9
DAILY_REPORT_TOP_IPS=5
# Пусто = календарь и час отчёта в зоне процесса (как `date`, обычно совпадает с /etc/localtime).
@@ -183,6 +184,7 @@ load_config() {
local cfg_log_file="${LOG_FILE}"
local cfg_last_heartbeat_file="${LAST_HEARTBEAT_FILE}"
local cfg_last_report_file="${LAST_REPORT_FILE}"
local cfg_daily_report_enabled="${DAILY_REPORT_ENABLED:-1}"
local cfg_daily_report_hour="${DAILY_REPORT_HOUR}"
local cfg_daily_report_top_ips="${DAILY_REPORT_TOP_IPS}"
local cfg_daily_report_tz="${DAILY_REPORT_TZ}"
@@ -258,6 +260,7 @@ load_config() {
IPSET_NAME_V6="${IPSET_NAME_V6:-$cfg_ipset_name_v6}"
IPSET_MAXELEM="${IPSET_MAXELEM:-$cfg_ipset_maxelem}"
DAILY_REPORT_ENABLED="$(trim_value "${DAILY_REPORT_ENABLED:-$cfg_daily_report_enabled}")"
DAILY_REPORT_HOUR="${DAILY_REPORT_HOUR:-$cfg_daily_report_hour}"
DAILY_REPORT_TOP_IPS="${DAILY_REPORT_TOP_IPS:-$cfg_daily_report_top_ips}"
DAILY_REPORT_TZ="$(trim_value "${DAILY_REPORT_TZ:-$cfg_daily_report_tz}")"
@@ -1732,30 +1735,35 @@ send_daily_report() {
ban_line="🚫 Автобан IP: отключён (ENABLE_IP_BAN=0)"
fi
local message=" 📊 ЕЖЕДНЕВНЫЙ ОТЧЕТ SSH МОНИТОРИНГА "$'\n'
message="${message}🕐 Время отчета: ${current_time}"$'\n'
message="${message}"$'\n'
message="${message} 📈 СТАТИСТИКА ЗА ПОСЛЕДНИЕ 24 ЧАСА: "$'\n'
message="${message} ✅ Успешных SSH подключений: ${successful_logins:-0}"$'\n'
message="${message} ❌ Неудачных попыток SSH: ${failed_logins:-0}"$'\n'
message="${message} ⚠️ Команд через sudo: ${sudo_commands:-0}"$'\n'
message="${message} ${ban_line}"$'\n'
message="${message}"$'\n'
message="${message} 🧾 ТОП-${DAILY_REPORT_TOP_IPS} IP ПО НЕУДАЧНЫМ ПОПЫТКАМ:"$'\n'
local message=""
message+="📊 ЕЖЕДНЕВНЫЙ ОТЧЕТ SSH МОНИТОРИНГА"$'\n'
message+="🖥️ Сервер: $(server_display_name_with_ip)"$'\n'
message+="🕐 Время отчета: ${current_time}"$'\n'
message+=""$'\n'
message+="📈 СТАТИСТИКА ЗА ПОСЛЕДНИЕ 24 ЧАСА:"$'\n'
message+=" ✅ Успешных SSH подключений: ${successful_logins:-0}"$'\n'
message+=" ❌ Неудачных попыток SSH: ${failed_logins:-0}"$'\n'
message+=" ⚠️ Команд через sudo: ${sudo_commands:-0}"$'\n'
message+=" ${ban_line}"$'\n'
message+=""$'\n'
message+="🧾 ТОП-${DAILY_REPORT_TOP_IPS:-5} IP ПО НЕУДАЧНЫМ ПОПЫТКАМ:"$'\n'
if [ -n "$top_lines" ]; then
message="${message}${top_lines}"$'\n'
message+="${top_lines}"$'\n'
else
message="${message} (нет данных)"$'\n'
message+=" (нет данных)"$'\n'
fi
message="${message}"$'\n'
message="${message} 👥 АКТИВНЫЕ ПОЛЬЗОВАТЕЛИ (${active_count}):"$'\n'
message+=""$'\n'
message+="👥 АКТИВНЫЕ ПОЛЬЗОВАТЕЛИ (${active_count}):"$'\n'
if [ -n "$active_sessions" ]; then
message="${message}${active_sessions}"$'\n'
message+="${active_sessions}"$'\n'
else
message="${message} Нет активных пользователей"$'\n'
message+=" (нет данных)"$'\n'
fi
local active_users_json
active_users_json="$(printf '%s' "$active_sessions" | python3 -c 'import json,sys; print(json.dumps([l.strip() for l in sys.stdin.read().splitlines() if l.strip()]))' 2>/dev/null || echo '[]')"
local sac_summary sac_details_json
sac_summary="SSH 24ч: успех ${successful_logins:-0}, неудач ${failed_logins:-0}, sudo ${sudo_commands:-0}"
sac_details_json="$(
@@ -1765,18 +1773,27 @@ send_daily_report() {
SAC_SUDO="${sudo_commands:-0}" \
SAC_BANS="${active_bans:-0}" \
SAC_ACTIVE="${active_count:-0}" \
SAC_ACTIVE_USERS="$active_users_json" \
python3 <<'PY'
import html
import json
import os
body = os.environ.get("SAC_REPORT_BODY", "")
active_users = json.loads(os.environ.get("SAC_ACTIVE_USERS", "[]") or "[]")
ok = int(os.environ.get("SAC_OK", "0") or 0)
fail = int(os.environ.get("SAC_FAIL", "0") or 0)
stats = {
"successful_ssh": int(os.environ.get("SAC_OK", "0") or 0),
"failed_ssh": int(os.environ.get("SAC_FAIL", "0") or 0),
"platform": "ssh",
"successful_logins": ok,
"failed_logins": fail,
"successful_ssh": ok,
"failed_ssh": fail,
"sudo_commands": int(os.environ.get("SAC_SUDO", "0") or 0),
"active_bans": int(os.environ.get("SAC_BANS", "0") or 0),
"active_sessions": int(os.environ.get("SAC_ACTIVE", "0") or 0),
"active_users": active_users,
"generated_by": "agent",
}
escaped = html.escape(body)
report_html = '<div class="agent-report">' + escaped.replace("\n", "<br>\n") + "</div>"
@@ -1819,6 +1836,9 @@ read_last_report_date() {
}
check_daily_report() {
if [ "${DAILY_REPORT_ENABLED:-1}" != "1" ]; then
return 0
fi
local today
today=$(report_date '+%F')
local last_date