Files
security-alert-center/docs/agent-integration.md
T
PapaTramp ebb450be92 feat: global notification policy severity to channels (notif-22)
Singleton notification_policy table, NOTIFY_MIN_SEVERITY/CHANNELS env,
central dispatch gate, Settings policy UI, migration 007.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-29 16:19:26 +10:00

14 KiB
Raw Blame History

Интеграция агентов с SAC

Контракт между ssh-monitor, RDP-login-monitor и Security Alert Center.
Изменения вносятся в репозитории агентов отдельными задачами.


1. Режим UseSAC

1.1. Параметры конфигурации

ssh-monitor (/etc/ssh-monitor.conf):

# off | exclusive | dual | fallback
UseSAC="exclusive"
SAC_URL="https://sac.kalinamall.ru/api/v1/events"
SAC_API_KEY="sac_xxxxxxxx"
SAC_SPOOL_DIR="/var/lib/ssh-monitor/sac-spool"
SAC_SEND_HEARTBEAT="1"
SAC_FALLBACK_FAILURES="5"
SAC_TIMEOUT_SEC="12"

RDP-login-monitor (Login_Monitor.ps1 или отдельный .conf):

$UseSAC = "exclusive"   # off | exclusive | dual | fallback
$SacUrl = "https://sac.kalinamall.ru/api/v1/events"
$SacApiKey = "sac_xxxxxxxx"
$SacSpoolDir = "D:\Soft\Logs\sac-spool"

1.2. Матрица поведения

Событие off exclusive dual fallback
Auth / sudo / ban / RDP login Telegram/email с агента Только SAC JSON SAC + Telegram SAC; при сбоях → Telegram
Daily report С агента SAC формирует и шлёт Оба SAC, fallback как выше
Heartbeat С агента (если включён) Только SAC (agent.heartbeat) Оба SAC
Локальный LOG_FILE Да Да Да Да

1.3. Проверка при старте

  • UseSAC=off — без изменений: хотя бы один канал NOTIFY_CHAIN (как сейчас).
  • UseSAC≠off — обязательны SAC_URL, SAC_API_KEY; HTTP GET {base}/health OK.
  • --check-sac / Test-SacConnection — отправка agent.test, ожидание HTTP 201 (повтор с тем же event_id409, тоже успех для spool).

1.3.1. Telegram и email при UseSAC=exclusive

При exclusive агент не шлёт Telegram/email — оператору нужны оповещения из SAC (backend/app/services/telegram_notify.py).

На сервере SAC — sac-api.env или UI НастройкиПравило оповещений: один порог severity и выбор каналов (Telegram / webhook / email).

NOTIFY_MIN_SEVERITY=warning
NOTIFY_CHANNELS=telegram,webhook,email
TELEGRAM_ENABLED=true
TELEGRAM_BOT_TOKEN=<bot>
TELEGRAM_CHAT_ID=<chat>
Severity события NOTIFY_MIN_SEVERITY=warning high
info (успешный RDP/SSH login) нет нет
warning (*.login.failed, sudo) да нет
high / critical (ban, problem) да да

Events и problems используют одно глобальное правило (notification_policy в БД, миграция 007).

UI Настройки (/settings) — правило + параметры каналов (JWT admin). После деплоя: alembic upgrade head.

1.4. Версии и доставка обновлений

При любом изменении агента, влияющем на SAC или поведение на хосте, поднимайте версию и пушьте в git.kalinamall.ru:

Агент Маркер версии Как хост узнаёт о новой версии
ssh-monitor SSH_MONITOR_VERSION в ssh-monitor; # SAC client release: в sac-client.sh update_ssh_monitor.sh: git pull в клоне → сравнение sha256 ssh-monitor и sac-client.sh
RDP-login-monitor $ScriptVersion в Login_Monitor.ps1 и та же строка в version.txt на шаре NETLOGON Deploy-LoginMonitor.ps1: сверка version.txt и SHA256 пакета (Login_Monitor.ps1, Sac-Client.ps1); при отсутствии SAC в settings — UseSAC=dual из example; подсказка # $ServerDisplayName

Кириллица в SAC (RDP): до 1.2.8-SAC Invoke-WebRequest мог слать JSON не в UTF-8 — в UI «Отчёты» summary вида RDP 24?: ?????? вместо RDP 24ч: сессий …. Обновите Sac-Client.ps1 на всех хостах; старые события в БД не пересчитываются, исправятся только новые ingest после деплоя.

HTTP 422 и spool (RDP): до 1.2.9-SAC при отклонении схемой (часто title длиннее 256 символов) JSON попадал в sac-spool и повторялся бесконечно. С 1.2.9-SAC: обрезка title/summary, UTF-8 POST, при 422 файл уходит в sac-spool/rejected/ (не ретраится). Старые .json в spool на хосте удалите или перенесите вручную после обновления.

HTTP 409 и spool (RDP): в PowerShell Invoke-WebRequest часто бросает исключение на 409 (и иногда на 201), хотя событие уже в SAC. До 1.2.10-SAC клиент считал это ошибкой и снова слал тот же event_id из spool каждые ~5 с (лог WARN: SAC POST HTTP 409). С 1.2.10-SAC коды 201/409/202 обрабатываются как успех и файл spool удаляется.

HTTP 422 на agent.lifecycle (RDP): часто из‑за битого JSON с кириллицей в summary (ConvertTo-Json + неверная кодировка POST). С 1.2.11-SAC — сериализация через JavaScriptSerializer (Unicode \uXXXX), в лог пишется тело ответа SAC (schema_errors). Файлы в sac-spool/rejected/ после 422 можно удалить.

422 json_invalid / Extra data (позиция 4): тело POST было null + JSON (утечка в pipeline от Write-Log). С 1.2.12-SAC: [void](Write-Log), одна строка JSON, без ConvertFrom-Json перед POST.

Только правки Sac-Client.ps1 / sac-client.sh: для RDP достаточно поднять patch в version.txt (и при необходимости $ScriptVersion); для Linux sac-client.sh обновится по checksum даже без смены SSH_MONITOR_VERSION, но рекомендуется поднимать обе метки для логов.

1.5. Правила Problems v1 (SAC backend)

rule_id Условие Пороги (env)
rule:brute_force_burst ssh.login.failed / rdp.login.failed, один source_ip SAC_BRUTE_FORCE_WINDOW_MINUTES=15, SAC_BRUTE_FORCE_THRESHOLD=30
rule:privilege_spike privilege.sudo.command на хосте SAC_PRIVILEGE_SPIKE_WINDOW_MINUTES=10, SAC_PRIVILEGE_SPIKE_THRESHOLD=10
rule:host_silence был heartbeat, но устарел (agent.heartbeat) SAC_HEARTBEAT_STALE_MINUTES (как для UI «Хосты»)

Свежий agent.heartbeat автоматически resolve open/ack проблему rule:host_silence. Одиночные high/critical (ban, mass bruteforce) — как раньше.


2. Протокол ingest

2.1. Запрос

POST /api/v1/events HTTP/1.1
Host: sac.kalinamall.ru
Content-Type: application/json
Authorization: Bearer sac_xxxxxxxx
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000

{ ... событие по event-schema-v1.json ... }

2.2. Ответ

HTTP Значение status в теле created
201 Событие записано впервые created true
409 Тот же event_id уже есть (идемпотентность) duplicate false
422 Ошибка JSON Schema

Пример 201:

{
  "status": "created",
  "event_id": "550e8400-e29b-41d4-a716-446655440000",
  "created": true,
  "sac_event_url": "https://sac.kalinamall.ru/api/v1/events/12345",
  "problem_id": null
}

event_id — обязательный UUID (RFC 4122), уникален глобально; в БД индекс UNIQUE (event_id).

2.3. Spool при ошибке

  1. Записать JSON в SAC_SPOOL_DIR/{event_id}.json.
  2. ssh-monitor: каждую итерацию цикла вызывается sac_flush_spool (до 20 файлов).
  3. После успеха (HTTP 201 или 409) — удалить файл из spool.
  4. Лимит размера spool (например 500 MB) — логировать WARN, не удалять без алерта.

3. Маппинг уведомлений → типы событий

3.1. ssh-monitor

Текущий текст (сокращённо) type severity
Успешное SSH ssh.login.success info
Неудачная SSH ssh.login.failed warning
IP заблокирован ssh.ip.banned high
Лимит без бана ssh.ip.bruteforce.threshold warning
Массовый брутфорс ssh.bruteforce.mass high
Sudo privilege.sudo.command warningcritical*
logind new/removed/failed session.logind.* infowarning
Ежедневный отчёт report.daily.ssh info
Heartbeat agent.heartbeat info

* critical — по эвристике команды (useradd, passwd, rm -rf, …) в details.risk_level.

Дополнительные поля в details (exclusive):

  • user, source_ip, port, attempt_number, max_attempts
  • sudo: run_as, command, pwd, risk_level
  • ban: ban_until, enable_ip_ban
  • brute: window_sec, fails_in_window
  • whitelist_matched: boolean

3.2. RDP-login-monitor

Событие type severity
4624 успех rdp.login.success info
4625 неудача rdp.login.failed warning
4648 auth.explicit.credentials warning
RD Gateway 302 rdg.connection.success info
RD Gateway 303 rdg.connection.failed warning
Старт/стоп agent.lifecycle info
Отчёт report.daily.rdp info

Дополнительные поля:

  • event_id_windows, logon_type, ip_address, workstation_name
  • gateway_target, gateway_error_code
  • filtered_out, filter_reason

3.3. Человекочитаемое имя хоста (host.display_name)

В UI SAC (Хосты, фильтры Problems/Events) показывается display_name, если оно задано; иначе hostname из ОС.

Агент Параметр В ingest
ssh-monitor SERVER_DISPLAY_NAME в /etc/ssh-monitor.conf host.display_name (пусто — поле не передаётся)
RDP-login-monitor $ServerDisplayName в login_monitor.settings.ps1 host.display_name; hostname = $env:COMPUTERNAME

Пример фрагмента JSON:

"host": {
  "hostname": "NEW-ADMIN-PC",
  "display_name": "UNMS Kalina",
  "os_family": "windows"
}

Telegram у ssh/RDP использует ту же подпись, что и display_name, когда параметр задан.

Версии агентов (единый номер)

Репозиторий Источник версии SAC product_version
ssh-monitor SSH_MONITOR_VERSION в ssh-monitor + version.txt из SSH_MONITOR_VERSION
RDP-login-monitor $ScriptVersion в Login_Monitor.ps1 + version.txt из $ScriptVersion

Не задавайте разные номера для «скрипта» и «SAC-модуля» — в UI Хосты отображается одна версия с ingest.


4. Поля dedup_key (рекомендации)

Тип Формат dedup_key
ssh.login.success {product}|{host}|ssh.login.success|{user}|{ip}
ssh.login.failed {product}|{host}|ssh.login.failed|{ip} (окно в SAC)
privilege.sudo {product}|{host}|sudo|{user}|{hash(command)}
rdp.login.failed {product}|{host}|rdp.failed|{ip}|{user}

SAC применяет cooldown по dedup_key + правилам.


5. Точки встраивания в код агентов

ssh-monitor

  • Новая функция send_sac_event() вызывается из мест, где сейчас notify_send "$message".
  • Обёртка notify_or_sac():
    • UseSAC=offnotify_send
    • exclusive → только send_sac_eventsummary — тот же текст)
    • dual → оба
    • fallbacksend_sac_event; при fail increment counter → при пороге notify_send

RDP-login-monitor

  • Send-SacEvent + замена вызовов Send-TelegramMessage для событий мониторинга.
  • Heartbeat и daily report — отдельные типы в SAC.

6. Обратная совместимость

  • По умолчанию UseSAC=off — поведение 100% как сейчас.
  • BACKUP_WEBHOOK_URL в ssh-monitor при exclusive не используется для обычных алертов (только emergency в fallback — опционально).

7. Чеклист готовности агента

  • Параметры конфига задокументированы в README агента (ssh-monitor, RDP-login-monitor)
  • --check-sac / Test-SacConnection / -CheckSac
  • Spool и повторная отправка
  • Все типы из п. 3 покрыты (RDP: основные; 4648 — при появлении обработки)
  • event_id UUID на каждое событие
  • Секреты не в git

8. См. также