feat: map security events to SAC typed payloads
This commit is contained in:
+35
-5
@@ -99,6 +99,19 @@ def details():
|
|||||||
return json.loads(raw)
|
return json.loads(raw)
|
||||||
|
|
||||||
host = socket.gethostname()
|
host = socket.gethostname()
|
||||||
|
etype = os.environ["SAC_EVENT_TYPE"]
|
||||||
|
if etype.startswith(("ssh.", "auth.", "rdp.")):
|
||||||
|
category = "auth"
|
||||||
|
elif etype.startswith("privilege."):
|
||||||
|
category = "privilege"
|
||||||
|
elif etype.startswith("session."):
|
||||||
|
category = "session"
|
||||||
|
elif etype.startswith("report."):
|
||||||
|
category = "report"
|
||||||
|
elif etype.startswith("rdg."):
|
||||||
|
category = "network"
|
||||||
|
else:
|
||||||
|
category = "agent"
|
||||||
payload = {
|
payload = {
|
||||||
"schema_version": "1.0",
|
"schema_version": "1.0",
|
||||||
"event_id": str(uuid.uuid4()),
|
"event_id": str(uuid.uuid4()),
|
||||||
@@ -112,7 +125,7 @@ payload = {
|
|||||||
"hostname": host,
|
"hostname": host,
|
||||||
"os_family": "linux",
|
"os_family": "linux",
|
||||||
},
|
},
|
||||||
"category": "agent",
|
"category": category,
|
||||||
"type": os.environ["SAC_EVENT_TYPE"],
|
"type": os.environ["SAC_EVENT_TYPE"],
|
||||||
"severity": os.environ["SAC_SEVERITY"],
|
"severity": os.environ["SAC_SEVERITY"],
|
||||||
"title": os.environ["SAC_TITLE"],
|
"title": os.environ["SAC_TITLE"],
|
||||||
@@ -152,13 +165,30 @@ PY
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# notify_or_sac type severity title summary telegram_message
|
# JSON details из переменных SAC_D_* (опционально)
|
||||||
|
sac_build_details_json() {
|
||||||
|
python3 <<'PY'
|
||||||
|
import json, os
|
||||||
|
out = {}
|
||||||
|
for k, v in os.environ.items():
|
||||||
|
if k.startswith("SAC_D_") and v:
|
||||||
|
out[k[6:].lower()] = v
|
||||||
|
print(json.dumps(out) if out else "")
|
||||||
|
PY
|
||||||
|
}
|
||||||
|
|
||||||
|
# notify_or_sac type severity title summary telegram_message [details_json]
|
||||||
notify_or_sac() {
|
notify_or_sac() {
|
||||||
local event_type="$1"
|
local event_type="$1"
|
||||||
local severity="$2"
|
local severity="$2"
|
||||||
local title="$3"
|
local title="$3"
|
||||||
local summary="$4"
|
local summary="$4"
|
||||||
local telegram_message="${5:-$summary}"
|
local telegram_message="${5:-$summary}"
|
||||||
|
local details_json="${6:-}"
|
||||||
|
if [ -z "$details_json" ] && [ -n "${SAC_D_USER:-}${SAC_D_IP:-}${SAC_D_COMMAND:-}" ]; then
|
||||||
|
details_json="$(sac_build_details_json)"
|
||||||
|
fi
|
||||||
|
unset SAC_D_USER SAC_D_IP SAC_D_COMMAND SAC_D_ATTEMPT SAC_D_MAX SAC_D_RUN_AS SAC_D_PWD 2>/dev/null || true
|
||||||
local mode
|
local mode
|
||||||
mode="$(sac_normalize_mode "${UseSAC:-off}")"
|
mode="$(sac_normalize_mode "${UseSAC:-off}")"
|
||||||
|
|
||||||
@@ -167,14 +197,14 @@ notify_or_sac() {
|
|||||||
notify_send "$telegram_message"
|
notify_send "$telegram_message"
|
||||||
;;
|
;;
|
||||||
exclusive)
|
exclusive)
|
||||||
sac_send_event "$event_type" "$severity" "$title" "$summary"
|
sac_send_event "$event_type" "$severity" "$title" "$summary" "$details_json"
|
||||||
;;
|
;;
|
||||||
dual)
|
dual)
|
||||||
sac_send_event "$event_type" "$severity" "$title" "$summary" || true
|
sac_send_event "$event_type" "$severity" "$title" "$summary" "$details_json" || true
|
||||||
NOTIFY_SKIP_SAC_MIRROR=1 notify_send "$telegram_message"
|
NOTIFY_SKIP_SAC_MIRROR=1 notify_send "$telegram_message"
|
||||||
;;
|
;;
|
||||||
fallback)
|
fallback)
|
||||||
if sac_send_event "$event_type" "$severity" "$title" "$summary"; then
|
if sac_send_event "$event_type" "$severity" "$title" "$summary" "$details_json"; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
NOTIFY_SKIP_SAC_MIRROR=1 notify_send "$telegram_message"
|
NOTIFY_SKIP_SAC_MIRROR=1 notify_send "$telegram_message"
|
||||||
|
|||||||
+22
-14
@@ -991,7 +991,8 @@ block_ip() {
|
|||||||
message="${message}👤 Попыток: ${attempts} (порог ${MAX_ATTEMPTS}; ENABLE_IP_BAN=0)"$'\n'
|
message="${message}👤 Попыток: ${attempts} (порог ${MAX_ATTEMPTS}; ENABLE_IP_BAN=0)"$'\n'
|
||||||
message="${message}ℹ️ ipset/firewall не меняются. При ENABLE_IP_BAN=1 бан был бы ~${ban_time_hours} ч."$'\n'
|
message="${message}ℹ️ ipset/firewall не меняются. При ENABLE_IP_BAN=1 бан был бы ~${ban_time_hours} ч."$'\n'
|
||||||
message="${message}🕐 Время: $(notification_date '+%d.%m.%Y %H:%M:%S')"
|
message="${message}🕐 Время: $(notification_date '+%d.%m.%Y %H:%M:%S')"
|
||||||
notify_send "$message"
|
SAC_D_IP="$ip" SAC_D_ATTEMPT="$attempts" SAC_D_MAX="$MAX_ATTEMPTS" \
|
||||||
|
notify_or_sac "ssh.ip.bruteforce.threshold" "warning" "Лимит SSH без бана" "${ip}: ${attempts} попыток" "$message"
|
||||||
write_log "Порог неудачных SSH для $ip ($attempts попыток), автобан отключён (ENABLE_IP_BAN=0)"
|
write_log "Порог неудачных SSH для $ip ($attempts попыток), автобан отключён (ENABLE_IP_BAN=0)"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
@@ -1015,7 +1016,8 @@ block_ip() {
|
|||||||
message="${message}📊 Неудачных попыток: ${attempts}"$'\n'
|
message="${message}📊 Неудачных попыток: ${attempts}"$'\n'
|
||||||
message="${message}⏱️ Блокировка на: ${ban_time_hours} час(ов) (ipset timeout)"$'\n'
|
message="${message}⏱️ Блокировка на: ${ban_time_hours} час(ов) (ipset timeout)"$'\n'
|
||||||
message="${message}🕐 Время: $(notification_date '+%d.%m.%Y %H:%M:%S')"
|
message="${message}🕐 Время: $(notification_date '+%d.%m.%Y %H:%M:%S')"
|
||||||
notify_send "$message"
|
SAC_D_IP="$ip" SAC_D_ATTEMPT="$attempts" \
|
||||||
|
notify_or_sac "ssh.ip.banned" "high" "IP заблокирован" "${ip} (${attempts} попыток)" "$message"
|
||||||
write_log "AUTO-BAN: IP $ip в ipset на $ban_time_hours ч. ($attempts попыток)"
|
write_log "AUTO-BAN: IP $ip в ipset на $ban_time_hours ч. ($attempts попыток)"
|
||||||
|
|
||||||
# Один IP — одна строка: после истечения ipset-таймаута is_ip_blocked=0, а в файле
|
# Один IP — одна строка: после истечения ipset-таймаута is_ip_blocked=0, а в файле
|
||||||
@@ -1281,13 +1283,15 @@ monitor_ssh() {
|
|||||||
message="${message}👤 Пользователь: ${user}"$'\n'
|
message="${message}👤 Пользователь: ${user}"$'\n'
|
||||||
message="${message}🌐 IP адрес: ${ip:-local}"$'\n'
|
message="${message}🌐 IP адрес: ${ip:-local}"$'\n'
|
||||||
message="${message}🕐 Время: $(notification_date '+%d.%m.%Y %H:%M:%S')"
|
message="${message}🕐 Время: $(notification_date '+%d.%m.%Y %H:%M:%S')"
|
||||||
notify_send "$message"
|
SAC_D_USER="$user" SAC_D_IP="${ip:-}" \
|
||||||
|
notify_or_sac "ssh.login.success" "info" "Успешное SSH" "${user} с ${ip:-local}" "$message"
|
||||||
write_log "SSH успешный вход: $user с IP ${ip:-local}"
|
write_log "SSH успешный вход: $user с IP ${ip:-local}"
|
||||||
if [ "$user" = "root" ]; then
|
if [ "$user" = "root" ]; then
|
||||||
local rmsg=" 🔑 SSH ВХОД ПОД ROOT "$'\n'
|
local rmsg=" 🔑 SSH ВХОД ПОД ROOT "$'\n'
|
||||||
rmsg="${rmsg}🌐 IP: ${ip:-local}"$'\n'
|
rmsg="${rmsg}🌐 IP: ${ip:-local}"$'\n'
|
||||||
rmsg="${rmsg}🕐 Время: $(notification_date '+%d.%m.%Y %H:%M:%S')"
|
rmsg="${rmsg}🕐 Время: $(notification_date '+%d.%m.%Y %H:%M:%S')"
|
||||||
notify_send "$rmsg"
|
SAC_D_USER="root" SAC_D_IP="${ip:-}" \
|
||||||
|
notify_or_sac "ssh.login.success" "warning" "SSH вход под root" "root с ${ip:-local}" "$rmsg"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
elif echo "$line" | grep -q "Failed password"; then
|
elif echo "$line" | grep -q "Failed password"; then
|
||||||
@@ -1316,7 +1320,8 @@ monitor_ssh() {
|
|||||||
message="${message}🌐 IP адрес: ${ip}"$'\n'
|
message="${message}🌐 IP адрес: ${ip}"$'\n'
|
||||||
message="${message}📊 Попытка ${current_attempts} из ${MAX_ATTEMPTS}"$'\n'
|
message="${message}📊 Попытка ${current_attempts} из ${MAX_ATTEMPTS}"$'\n'
|
||||||
message="${message}🕐 Время: $(notification_date '+%d.%m.%Y %H:%M:%S')"
|
message="${message}🕐 Время: $(notification_date '+%d.%m.%Y %H:%M:%S')"
|
||||||
notify_send "$message"
|
SAC_D_USER="$user" SAC_D_IP="$ip" SAC_D_ATTEMPT="$current_attempts" SAC_D_MAX="$MAX_ATTEMPTS" \
|
||||||
|
notify_or_sac "ssh.login.failed" "warning" "Неудачная SSH" "${user} с ${ip} (${current_attempts}/${MAX_ATTEMPTS})" "$message"
|
||||||
fi
|
fi
|
||||||
write_log "SSH неудачная попытка: $user с IP $ip (попытка $current_attempts из $MAX_ATTEMPTS)"
|
write_log "SSH неудачная попытка: $user с IP $ip (попытка $current_attempts из $MAX_ATTEMPTS)"
|
||||||
fi
|
fi
|
||||||
@@ -1432,6 +1437,7 @@ monitor_sudo() {
|
|||||||
message="${message}📁 Директория: ${pwd:-unknown}"$'\n'
|
message="${message}📁 Директория: ${pwd:-unknown}"$'\n'
|
||||||
message="${message}🕐 Время: $(notification_date '+%d.%m.%Y %H:%M:%S')"
|
message="${message}🕐 Время: $(notification_date '+%d.%m.%Y %H:%M:%S')"
|
||||||
local sac_summary="${user}: ${sudo_cmd}"
|
local sac_summary="${user}: ${sudo_cmd}"
|
||||||
|
SAC_D_USER="$user" SAC_D_COMMAND="$sudo_cmd" SAC_D_RUN_AS="${run_as:-}" SAC_D_PWD="${pwd:-}" \
|
||||||
notify_or_sac "privilege.sudo.command" "warning" "Использование sudo" "$sac_summary" "$message"
|
notify_or_sac "privilege.sudo.command" "warning" "Использование sudo" "$sac_summary" "$message"
|
||||||
write_log "SUDO: $user выполнил $sudo_cmd${run_as:+ (USER=$run_as)}"
|
write_log "SUDO: $user выполнил $sudo_cmd${run_as:+ (USER=$run_as)}"
|
||||||
fi
|
fi
|
||||||
@@ -1473,7 +1479,7 @@ monitor_security_events() {
|
|||||||
local m=" 🔐 ВНИМАНИЕ: изменение SSH host key / MITM? "$'\n'
|
local m=" 🔐 ВНИМАНИЕ: изменение SSH host key / MITM? "$'\n'
|
||||||
m="${m}$(printf '%s\n' "$hk")"
|
m="${m}$(printf '%s\n' "$hk")"
|
||||||
m="${m}🕐 Время: $(notification_date '+%d.%m.%Y %H:%M:%S')"
|
m="${m}🕐 Время: $(notification_date '+%d.%m.%Y %H:%M:%S')"
|
||||||
notify_send "$m"
|
notify_or_sac "agent.notification" "high" "SSH host key changed" "MITM?" "$m"
|
||||||
write_log "SECURITY: обнаружено сообщение об изменении host key"
|
write_log "SECURITY: обнаружено сообщение об изменении host key"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
@@ -1494,7 +1500,8 @@ monitor_security_events() {
|
|||||||
bm="${bm}🌐 IP: ${ip}"$'\n'
|
bm="${bm}🌐 IP: ${ip}"$'\n'
|
||||||
bm="${bm}📊 Неудачных попыток за ~${BRUTE_WINDOW_SEC}s: ${count}"$'\n'
|
bm="${bm}📊 Неудачных попыток за ~${BRUTE_WINDOW_SEC}s: ${count}"$'\n'
|
||||||
bm="${bm}🕐 Время: $(notification_date '+%d.%m.%Y %H:%M:%S')"
|
bm="${bm}🕐 Время: $(notification_date '+%d.%m.%Y %H:%M:%S')"
|
||||||
notify_send "$bm"
|
SAC_D_IP="$ip" SAC_D_ATTEMPT="$count" \
|
||||||
|
notify_or_sac "ssh.bruteforce.mass" "high" "Массовый брутфорс" "${ip}: ${count} за ${BRUTE_WINDOW_SEC}s" "$bm"
|
||||||
write_log "SECURITY: массовый брутфорс? IP=$ip fails=${count} за окно ${BRUTE_WINDOW_SEC}s"
|
write_log "SECURITY: массовый брутфорс? IP=$ip fails=${count} за окно ${BRUTE_WINDOW_SEC}s"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
@@ -1605,7 +1612,8 @@ monitor_logind() {
|
|||||||
msg="${msg}👤 Пользователь: ${user:-неизвестно}"$'\n'
|
msg="${msg}👤 Пользователь: ${user:-неизвестно}"$'\n'
|
||||||
msg="${msg}🆔 Сессия: ${sid:-?}"$'\n'
|
msg="${msg}🆔 Сессия: ${sid:-?}"$'\n'
|
||||||
msg="${msg}🕐 Время: $(notification_date '+%d.%m.%Y %H:%M:%S')"
|
msg="${msg}🕐 Время: $(notification_date '+%d.%m.%Y %H:%M:%S')"
|
||||||
notify_send "$msg"
|
SAC_D_USER="${user:-}" \
|
||||||
|
notify_or_sac "session.logind.new" "info" "Новая сессия logind" "${user:-?} sid=${sid:-?}" "$msg"
|
||||||
write_log "LOGIND: новая сессия: ${line}"
|
write_log "LOGIND: новая сессия: ${line}"
|
||||||
else
|
else
|
||||||
write_log "LOGIND: новая сессия (разбор user/sid не удался): ${line}"
|
write_log "LOGIND: новая сессия (разбор user/sid не удался): ${line}"
|
||||||
@@ -1622,7 +1630,7 @@ monitor_logind() {
|
|||||||
local msg_r=" 🚪 СЕССИЯ ЗАКРЫТА (systemd-logind) "$'\n'
|
local msg_r=" 🚪 СЕССИЯ ЗАКРЫТА (systemd-logind) "$'\n'
|
||||||
msg_r="${msg_r}🆔 Сессия: ${sess_r:-?}"$'\n'
|
msg_r="${msg_r}🆔 Сессия: ${sess_r:-?}"$'\n'
|
||||||
msg_r="${msg_r}🕐 Время: $(notification_date '+%d.%m.%Y %H:%M:%S')"
|
msg_r="${msg_r}🕐 Время: $(notification_date '+%d.%m.%Y %H:%M:%S')"
|
||||||
notify_send "$msg_r"
|
notify_or_sac "session.logind.removed" "info" "Сессия закрыта" "sid=${sess_r:-?}" "$msg_r"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
elif [[ "$low" == *"failed"* ]]; then
|
elif [[ "$low" == *"failed"* ]]; then
|
||||||
@@ -1630,7 +1638,7 @@ monitor_logind() {
|
|||||||
local msg_f=" ❌ СБОЙ (systemd-logind) "$'\n'
|
local msg_f=" ❌ СБОЙ (systemd-logind) "$'\n'
|
||||||
msg_f="${msg_f}📝 ${line}"$'\n'
|
msg_f="${msg_f}📝 ${line}"$'\n'
|
||||||
msg_f="${msg_f}🕐 Время: $(notification_date '+%d.%m.%Y %H:%M:%S')"
|
msg_f="${msg_f}🕐 Время: $(notification_date '+%d.%m.%Y %H:%M:%S')"
|
||||||
notify_send "$msg_f"
|
notify_or_sac "session.logind.failed" "warning" "Сбой logind" "logind failed" "$msg_f"
|
||||||
write_log "LOGIND: сбой: $line"
|
write_log "LOGIND: сбой: $line"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
@@ -1746,7 +1754,7 @@ send_daily_report() {
|
|||||||
message="${message} Нет активных пользователей"$'\n'
|
message="${message} Нет активных пользователей"$'\n'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
notify_send "$message"
|
notify_or_sac "report.daily.ssh" "info" "Ежедневный отчёт SSH" "Отчёт за сутки" "$message"
|
||||||
write_log "Ежедневный отчет отправлен"
|
write_log "Ежедневный отчет отправлен"
|
||||||
|
|
||||||
echo "$(report_date '+%F')" >"$LAST_REPORT_FILE"
|
echo "$(report_date '+%F')" >"$LAST_REPORT_FILE"
|
||||||
@@ -1804,7 +1812,7 @@ send_heartbeat() {
|
|||||||
local message=" ❤️ Heartbeat - скрипт мониторинга работает "$'\n'
|
local message=" ❤️ Heartbeat - скрипт мониторинга работает "$'\n'
|
||||||
message="${message}🕐 Время: ${timestamp_human}"
|
message="${message}🕐 Время: ${timestamp_human}"
|
||||||
|
|
||||||
notify_send "$message"
|
notify_or_sac "agent.heartbeat" "info" "Heartbeat" "ssh-monitor alive" "$message"
|
||||||
echo "$timestamp" >"$LAST_HEARTBEAT_FILE"
|
echo "$timestamp" >"$LAST_HEARTBEAT_FILE"
|
||||||
write_log "Heartbeat отправлен"
|
write_log "Heartbeat отправлен"
|
||||||
}
|
}
|
||||||
@@ -1871,7 +1879,7 @@ send_startup_notification() {
|
|||||||
message="${message}❤️ Heartbeat будет отправляться каждые 12 часов"$'\n'
|
message="${message}❤️ Heartbeat будет отправляться каждые 12 часов"$'\n'
|
||||||
message="${message}📢 Каналы уведомлений (все по списку): $(notify_chain_human)"
|
message="${message}📢 Каналы уведомлений (все по списку): $(notify_chain_human)"
|
||||||
|
|
||||||
notify_send "$message"
|
notify_or_sac "agent.lifecycle" "info" "Мониторинг запущен" "ssh-monitor started" "$message"
|
||||||
write_log "Скрипт мониторинга запущен (белый список: $whitelist_count записей)"
|
write_log "Скрипт мониторинга запущен (белый список: $whitelist_count записей)"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1884,7 +1892,7 @@ send_shutdown_notification() {
|
|||||||
local message=" ⚠️ СКРИПТ МОНИТОРИНГА ОСТАНОВЛЕН "$'\n'
|
local message=" ⚠️ СКРИПТ МОНИТОРИНГА ОСТАНОВЛЕН "$'\n'
|
||||||
message="${message}🕐 Время остановки: ${timestamp}"
|
message="${message}🕐 Время остановки: ${timestamp}"
|
||||||
|
|
||||||
notify_send "$message"
|
notify_or_sac "agent.lifecycle" "warning" "Мониторинг остановлен" "ssh-monitor stopped" "$message"
|
||||||
write_log "Скрипт мониторинга остановлен"
|
write_log "Скрипт мониторинга остановлен"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user