chore(home): mirror from kalinamall (9883e6a) with papatramp URLs

This commit is contained in:
2026-07-14 20:43:52 +10:00
commit ed4e78f6c3
312 changed files with 42790 additions and 0 deletions
+29
View File
@@ -0,0 +1,29 @@
# Индекс документации SAC
| Документ | Назначение |
|----------|------------|
| [TZ.md](TZ.md) | **Техническое задание** (главный документ) |
| [diagrams/data-flow.md](diagrams/data-flow.md) | Диаграмма «Как это работает» (Mermaid) |
| [architecture.md](architecture.md) | Компоненты, API, модель данных |
| [agent-integration.md](agent-integration.md) | UseSAC, протокол ingest, маппинг типов |
| [event-schema-v1.json](event-schema-v1.json) | JSON Schema событий |
| [work-plan.md](work-plan.md) | План работ по фазам |
| [roadmap.md](roadmap.md) | Дорожная карта версий |
| [install-ubuntu-24.04-native.md](install-ubuntu-24.04-native.md) | **Подготовка сервера (native)** |
| [install-ubuntu-24.04-docker.md](install-ubuntu-24.04-docker.md) | Подготовка сервера (Docker, альтернатива) |
| [install-ubuntu-24.04.md](install-ubuntu-24.04.md) | Указатель |
| [ssl-certificate.md](ssl-certificate.md) | Wildcard TLS: формат, scp, пути `/etc/ssl/sac/` |
| [deployment.md](deployment.md) | Эксплуатация, backup, TLS, `sac-deploy.sh` |
| [operations-prod-status.md](operations-prod-status.md) | **Статус prod** и следующие шаги |
| [seaca-mobile.md](seaca-mobile.md) | **Seaca**: коды, устройства, сессия, API |
| [seaca-fcm.md](seaca-fcm.md) | **Seaca push**: Firebase и `SAC_FCM_*` |
| [workspace-three-repos.md](workspace-three-repos.md) | Multi-root workspace (три репо) |
| [agent-control-plane.md](agent-control-plane.md) | **v0.7:** RDG flap, qwinsta/logoff, обновления агентов, config push |
| [agent-update-backlog.md](agent-update-backlog.md) | Ранний backlog (см. agent-control-plane.md) |
## Порядок чтения
1. TZ.md
2. diagrams/data-flow.md
3. agent-integration.md + event-schema-v1.json
4. work-plan.md
+310
View File
@@ -0,0 +1,310 @@
# Техническое задание
# Security Alert Center (SAC)
| Поле | Значение |
|------|----------|
| Версия документа | 1.0 |
| Дата | 2026-05-26 |
| Статус | Черновик на согласование |
| Целевая ОС сервера | **Ubuntu 24.04 LTS** |
---
## 1. Назначение и цели
### 1.1. Назначение
**Security Alert Center (SAC)** — центральное веб-приложение, которое:
1. Принимает структурированные события безопасности от агентов **ssh-monitor** (Linux) и **RDP-login-monitor** (Windows).
2. Хранит их в базе данных для поиска, аудита и аналитики.
3. Отображает ленту событий, инциденты (Problems) и дашборды в стиле **Zabbix** (Monitoring → Problems, графики, хосты).
4. По правилам доставляет оповещения операторам (Telegram, email, webhook) — **когда на агентах включён режим `UseSAC`**.
### 1.2. Цели
- Единая точка наблюдения за входами, неудачными попытками, sudo, банами IP, RD Gateway и т.д.
- Снижение «шума» в мессенджерах за счёт дедупликации, группировки и правил в SAC.
- Сохранение привычного поведения агентов при **`UseSAC=off`** (уведомления напрямую в Telegram/email, как сейчас).
- Эксплуатация на одном сервере **Ubuntu 24.04** без обязательного Kubernetes.
### 1.3. Не входит в scope MVP (см. [roadmap.md](roadmap.md))
- Кластеризация SAC (active-active).
- Полноценный SIEM / ML-аномалии.
- Замена существующих агентов — они остаются, меняется только канал доставки при `UseSAC`.
---
## 2. Контекст: три репозитория
| № | Репозиторий | Роль |
|---|-------------|------|
| 1 | `ssh-monitor` | Агент на Linux-серверах |
| 2 | `RDP-login-monitor` | Агент на Windows Server |
| 3 | `security-alert-center` | Центральный сервер (этот проект) |
Совместная разработка в multi-root workspace — см. [workspace-three-repos.md](workspace-three-repos.md).
---
## 3. Как это работает (архитектура потоков)
Ниже — целевая схема взаимодействия компонентов (рис. 1).
```mermaid
flowchart LR
subgraph agents [Агенты на хостах]
SSH[ssh-monitor Linux]
RDP[RDP-login-monitor Windows]
end
subgraph sac [Security Alert Center Ubuntu 24.04]
API[Ingest API HTTPS]
Q[Очередь / воркер]
DB[(PostgreSQL)]
RT[Realtime SSE/WebSocket]
UI[Web UI]
NOTIFY[Правила оповещений]
end
subgraph channels [Каналы наружу]
TG[Telegram]
MAIL[Email]
WEBHOOK[Webhook / Slack]
end
SSH -->|JSON события + API key| API
RDP -->|JSON события + API key| API
API --> Q --> DB
DB --> UI
DB --> RT --> UI
DB --> NOTIFY --> TG
NOTIFY --> MAIL
NOTIFY --> WEBHOOK
```
**Рис. 1.** Поток данных: агенты → SAC → БД/UI; оповещения пользователю при `UseSAC=exclusive` идут только из SAC.
### 3.1. Жизненный цикл события
1. На хосте возникает событие (успешный SSH, неудачный RDP, sudo, бан IP и т.д.).
2. Агент формирует **каноническое JSON-событие** (схема v1 — [event-schema-v1.json](event-schema-v1.json)).
3. `POST /api/v1/events` по HTTPS с API-ключом; при сбое — запись в локальный **spool** и повтор.
4. SAC: валидация → дедупликация → запись в PostgreSQL → оценка правил → при необходимости создание **Problem**.
5. Оператор просматривает UI; при `UseSAC=exclusive` Telegram/email отправляет **SAC**, не агент.
### 3.2. Режим `UseSAC` на агентах
| Режим | Значение | Поведение агента | Кто шлёт в Telegram/email |
|-------|----------|------------------|---------------------------|
| `off` | `0` | Как сейчас | Агент |
| `exclusive` | `1` | Только SAC (расширенный JSON) | **Только SAC** |
| `dual` | `2` | SAC + локальные каналы | Оба (миграция/отладка) |
| `fallback` | `3` | SAC; при N сбоях подряд — снова локально | SAC, при аварии SAC — агент |
**Требования:**
- При `UseSAC=exclusive` функции `notify_send` / `Send-TelegramMessage` для **оповещений о событиях** не вызываются.
- Локальные **лог-файлы** на агенте (`LOG_FILE`, `login_monitor.log` и т.д.) **сохраняются** всегда.
- При `UseSAC=exclusive` обязательны `SAC_URL`, `SAC_API_KEY`; при старте — проверка доступности SAC (`--check-sac` / аналог).
- Подробности — [agent-integration.md](agent-integration.md).
---
## 4. Функциональные требования
### 4.1. Приём событий (Ingest API)
| ID | Требование |
|----|------------|
| F-ING-01 | `POST /api/v1/events` принимает одно событие JSON, соответствующее `event-schema-v1.json`. |
| F-ING-02 | Аутентификация: заголовок `Authorization: Bearer <api_key>` или `X-SAC-API-Key`. |
| F-ING-03 | Ответ `202 Accepted` с `event_id`, `sac_event_url`; при создании Problem — `problem_id` (опционально). |
| F-ING-04 | Идемпотентность: повтор с тем же `event_id` не создаёт дубликат. |
| F-ING-05 | `POST /api/v1/events/batch` — до 100 событий (фаза 1.5). |
| F-ING-06 | Rate limiting: настраиваемый лимит на ключ/хост. |
| F-ING-07 | `GET /health` — проверка БД и версии (для агентов и мониторинга). |
### 4.2. Учёт хостов (Hosts)
| ID | Требование |
|----|------------|
| F-HST-01 | Первый валидный ingest с API key регистрирует/обновляет карточку хоста. |
| F-HST-02 | Поля: hostname, display_name, os_family, os_version, ipv4/ipv6, версия агента, `use_sac_mode`, last_seen, tags. |
| F-HST-03 | Статус «жив/мёртв» по последнему `agent.heartbeat` (порог N минут — настраивается). |
| F-HST-04 | UI: список хостов, фильтр, переход к событиям хоста. |
### 4.3. Хранение событий (Events)
| ID | Требование |
|----|------------|
| F-EVT-01 | Хранение всех полей события + `received_at`, `host_id`. |
| F-EVT-02 | Поиск/фильтр: период, хост, `type`, `severity`, IP, user, текст в `summary`. |
| F-EVT-03 | Просмотр карточки события с `details`, `raw` (с лимитом отображения). |
| F-EVT-04 | Экспорт CSV за выбранный фильтр (фаза 1.5). |
| F-EVT-05 | Retention: настраиваемое хранение сырых событий (по умолчанию 90 дней). |
### 4.4. Инциденты (Problems)
| ID | Требование |
|----|------------|
| F-PRB-01 | Правила создают Problem из одного или нескольких событий (пример: ≥30 `ssh.login.failed` за 15 мин с одного IP). |
| F-PRB-02 | Статусы: `open`, `acknowledged`, `resolved`. |
| F-PRB-03 | UI: список Problems с severity, хостом, временем, действиями ack/resolve. |
| F-PRB-04 | Связь Problem ↔ Events (просмотр связанных событий). |
### 4.5. Веб-интерфейс
| ID | Требование |
|----|------------|
| F-UI-01 | Страница **Problems** — активные инциденты, счётчики по severity. |
| F-UI-02 | Страница **Events** — лента с пагинацией и фильтрами. |
| F-UI-03 | Страница **Hosts** — инвентарь агентов. |
| F-UI-04 | Страница **Dashboards** — графики: успешные/неудачные входы по времени, топ IP, sudo, баны (MVP: базовый набор виджетов). |
| F-UI-05 | Live-лента последних событий (SSE или WebSocket). |
| F-UI-06 | Аутентификация в UI: логин/пароль (MVP); LDAP — фаза 3. |
| F-UI-07 | Роли MVP: `admin`, `operator`, `viewer`. |
### 4.6. Оповещения из SAC
| ID | Требование |
|----|------------|
| F-NOT-01 | Каналы: Telegram, SMTP email, generic webhook (JSON). |
| F-NOT-02 | Правила: условие (тип, severity, хост, тег) → каналы + шаблон. |
| F-NOT-03 | Дедупликация и cooldown на уровне SAC (аналог `BRUTE_NOTIFY_COOLDOWN_SEC`, `SSH_ACCEPT_NOTIFY_DEDUP_SEC`). |
| F-NOT-04 | Расписание тишины (maintenance window) — фаза 2. |
| F-NOT-05 | Суточные отчёты (`report.daily.*`) формируются и отправляются **из SAC**, не с агента при `UseSAC=exclusive`. |
### 4.7. Типы событий (минимальный перечень MVP)
**ssh-monitor:**
- `ssh.login.success`, `ssh.login.failed`, `ssh.session.disconnected`
- `privilege.sudo.command`
- `ssh.ip.banned`, `ssh.ip.bruteforce.threshold`, `ssh.bruteforce.mass`
- `session.logind.new`, `session.logind.removed`, `session.logind.failed`
- `report.daily.ssh`, `agent.heartbeat`, `agent.recovered`, `agent.test`
**RDP-login-monitor:**
- `rdp.login.success`, `rdp.login.failed`, `auth.explicit.credentials` (4648)
- `rdg.connection.success`, `rdg.connection.failed`
- `report.daily.rdp`, `agent.heartbeat`, `agent.lifecycle`, `agent.test`
Полный контракт — [event-schema-v1.json](event-schema-v1.json) и [agent-integration.md](agent-integration.md).
### 4.8. Расширенные поля событий (при `UseSAC=exclusive`)
Агент передаёт структуру **сверх** текущего текста Telegram:
- Идентификаторы: `event_id`, `correlation_id`, `dedup_key`, `fingerprint`
- Контекст хоста: FQDN, timezone, версия продукта, `agent.instance_id`
- Пороги: `enrichment.attempt_number`, `enrichment.threshold`
- SSH/RDP-специфика: port, logon_type, gateway target, sudo command, ban_until
- `raw`: обрезанный фрагмент journal / Event XML
- `filtered_out` + `filter_reason` (опционально, отдельный тип)
Обогащение **в SAC** (не обязательно в MVP): GeoIP, «новый IP для user», корреляция SSH+RDP с одного IP.
---
## 5. Нефункциональные требования
| ID | Требование |
|----|------------|
| NF-01 | Сервер приложения: **Ubuntu 24.04 LTS** (единственная поддерживаемая платформа для SAC). |
| NF-02 | БД: **PostgreSQL 16+** (production); SQLite допустим только для dev. |
| NF-03 | Ingest: p95 &lt; 2 с при нормальной нагрузке (до 50 событий/с мин суммарно). |
| NF-04 | Доступность UI по HTTPS (TLS). |
| NF-05 | API keys хранятся в БД как hash; секреты конфигурации — в `/etc/security-alert-center/` или env, не в git. |
| NF-06 | Резервное копирование БД: документированная процедура `pg_dump` (см. [deployment.md](deployment.md)). |
| NF-07 | Логи приложения: structured JSON, ротация logrotate. |
| NF-08 | Язык UI: русский (основной); i18n — фаза 3. |
| NF-09 | Время в БД: UTC; отображение — timezone пользователя или `Europe/Moscow` по умолчанию. |
---
## 6. Технологический стек (целевой)
| Слой | Технология |
|------|------------|
| Backend | Python 3.12, FastAPI, SQLAlchemy 2, Alembic |
| БД | PostgreSQL 16 |
| Очередь (фаза 1.5+) | Redis 7, воркер (ARQ или Celery) |
| Frontend | Vue 3 + Vite, UI-kit (Naive UI / PrimeVue), ECharts |
| Realtime | Server-Sent Events (приоритет MVP) |
| Reverse proxy | nginx |
| Развёртывание | **Native:** PostgreSQL + systemd + nginx (production). Docker Compose — альтернатива для стенда |
Детали — [architecture.md](architecture.md).
---
## 7. Безопасность
- Только HTTPS для ingest и UI.
- Отдельные API keys на хост (ротация из UI).
- RBAC в UI (MVP: 3 роли).
- Аудит действий: ack/resolve, смена правил.
- PII в `raw` — ограничение размера; опция маскирования в логах SAC.
- Разделение сетей: агенты → исходящий 443 на SAC; SAC не требует входящих на агенты.
---
## 8. Интеграция с существующими агентами
Изменения в репозиториях `ssh-monitor` и `RDP-login-monitor`**отдельные задачи**, не в этом репозитории.
Обязательно:
1. Параметры `UseSAC`, `SAC_URL`, `SAC_API_KEY`, `SAC_MODE` (`off|exclusive|dual|fallback`).
2. Функция отправки структурированного события + локальный spool.
3. Команда проверки `--test-sac` / `Test-SacConnection`.
4. Сохранение локальных логов при любом режиме.
Спецификация — [agent-integration.md](agent-integration.md).
---
## 9. Критерии приёмки MVP
1. Развёрнут SAC на Ubuntu 24.04, доступны UI и `POST /api/v1/events`.
2. Тестовый агент (curl / `agent.test`) создаёт событие, видимое в UI &lt; 10 с.
3. Реализованы режимы ingest для типов из п. 4.7 (тестовыми payload).
4. Страницы Problems (базовые правила), Events, Hosts, Dashboard (минимум 3 виджета).
5. Настроен канал Telegram из SAC; при `UseSAC=exclusive` на тестовом ssh-monitor Telegram **с агента** не приходит, с SAC — приходит.
6. Heartbeat: Problem «хост недоступен» при отсутствии heartbeat &gt; N мин.
7. Документация развёртывания воспроизводима с нуля по [deployment.md](deployment.md).
8. Spool: при остановке SAC события буферизуются на агенте и доставляются после восстановления.
---
## 10. Риски и митигация
| Риск | Митигация |
|------|-----------|
| SAC недоступен, алерты потеряны | spool на агенте, режим `fallback` |
| Дубли SSH + logind | `dedup_key` на агенте и в SAC |
| Перегруз БД | партиции по месяцу, retention, агрегаты (фаза 2) |
| Двойные Telegram при миграции | явный `dual`, затем `exclusive` |
---
## 11. Связанные документы
- [architecture.md](architecture.md)
- [event-schema-v1.json](event-schema-v1.json)
- [agent-integration.md](agent-integration.md)
- [work-plan.md](work-plan.md)
- [roadmap.md](roadmap.md)
- [deployment.md](deployment.md)
- [workspace-three-repos.md](workspace-three-repos.md)
---
## 12. История изменений
| Версия | Дата | Изменения |
|--------|------|-----------|
| 1.0 | 2026-05-26 | Первоначальная версия ТЗ |
+329
View File
@@ -0,0 +1,329 @@
# Agent Control Plane — концепция SAC v0.7+
**Статус:** утверждено для реализации (2026-06).
**Связано:** [roadmap.md](roadmap.md) §v0.7, [agent-integration.md](agent-integration.md), [agent-update-backlog.md](agent-update-backlog.md).
---
## 1. Зачем
Сейчас SAC — **односторонний** канал: агент → ingest. Для задач из [roadmap.md](roadmap.md) (§ «Добавить») нужен **обратный канал** SAC ↔ агент и управление хостами:
| # | Задача | Кратко |
|---|--------|--------|
| П.1 | RDG flap 302→303 | Детект, Problem, кнопка **qwinsta** / **logoff** в UI |
| П.2 | Обновления агентов | Режим GPO или SAC (pull + fallback SSH/WinRM) |
| П.3 | Настройки агента | Desired config в карточке хоста → poll агента |
**Эталонный git для prod и агентов:** [git.kalinamall.ru/PapaTramp](https://git.kalinamall.ru/PapaTramp). GitHub — публичная sanitized-копия; mirror-скрипты (`push-mirror.sh`, `Rewrite-GitHostUrls.ps1`) живут на kalinamall и нужны только для синхронизации URL в docs между remotes, к runtime агентов не относятся.
---
## 2. Общая архитектура
```mermaid
flowchart TB
subgraph sac [SAC Ubuntu]
API[FastAPI]
UI[Vue SPA]
PG[(PostgreSQL)]
VAULT[Encrypted secrets]
end
subgraph win [Windows host]
RDP[RDP-login-monitor]
end
subgraph linux [Linux host]
SSH[ssh-monitor]
end
RDP -->|POST events / heartbeat| API
SSH -->|POST events / heartbeat| API
RDP -->|GET agent/commands poll| API
SSH -->|GET agent/commands poll| API
UI --> API
API --> PG
API --> VAULT
```
**Принципы:**
- Агент за NAT — только **исходящий HTTPS**; inbound на хост не требуется.
- Команды и конфиг — **poll** на heartbeat (или отдельный timer 30–60 с).
- Секреты (admin password, SSH private keys, WinRM password) — **encrypted at rest** в PostgreSQL; master key в `SAC_HOST_KEY_ENCRYPTION_KEY` (env).
- Audit: все команды и результаты — события ingest `agent.command.*`, `agent.update.*`.
---
## 3. П.1 — RDG flap 302→303 и qwinsta / logoff
### 3.1. Смысл проблемы
При неудачном RDP через RD Gateway типична пара событий Windows **302 → 303** за несколько секунд. Клиент не удерживает сессию; **«зависшая» сессия обычно остаётся на компьютере пользователя** (session host / рабочая станция из `details.internal_ip`), а не на gateway.
SAC уже принимает:
| Windows | SAC `type` | Пример severity |
|---------|------------|-----------------|
| 302 | `rdg.connection.success` | info / warning |
| 303 | `rdg.connection.disconnected` | info |
| 303 (alt) | `rdg.connection.failed` | warning |
Правило должно учитывать **оба** варианта 303.
### 3.2. Правило детекта `rule:rdg_session_flap`
```
WHEN event_B.type IN (rdg.connection.disconnected, rdg.connection.failed)
AND exists event_A within 1..10 seconds BEFORE event_B
AND event_A.type = rdg.connection.success
AND same host_id
AND same details.user
AND (recommended) same details.internal_ip
OPTIONAL reduce false positives:
AND event_B.details.session_duration_sec <= 15 # если агент передаёт из 303
AND/OR low bytes transferred in 303 payload
THEN:
mark event_B.details.rdg_flap = true
create Problem rule:rdg_session_flap (dedup 30 s, см. ниже)
send notification (Telegram / email / webhook по policy)
```
**Порядок:** только **302 → 303**, не наоборот.
**Окно:** **110 секунд** между 302 и 303.
**Dedup Problem:** **30 секунд** по ключу `{host_id}|{user}|{internal_ip}`. Пользователи часто пробуют 1–2 раза подряд и звонят админам — 60 с слишком долго.
**Хост:** правило **универсальное** для любого хоста с RDG-событиями в домене (любой DC / gateway / session host, где стоит агент).
**Маршрутизация команды qwinsta:** команда уходит на хост, где **установлен агент** и куда привязано событие (`host_id`). Если session host ≠ gateway, в перспективе — таблица «gateway → session hosts» или агент на session host; MVP — агент на том же сервере, что шлёт события RDG.
### 3.3. UI — веб и Seaca
Бейдж **RDG flap** и кнопка **qwinsta** на событиях **302 и 303** (пара вычисляется при чтении):
- Веб: **Обзор**, **События**, карточка события
- **Seaca:** список событий + карточка → `POST .../actions/qwinsta` / `logoff`
**Поток:**
1. Оператор нажимает **qwinsta**.
2. `POST /api/v1/events/{id}/actions/qwinsta` → SAC выполняет **WinRM qwinsta** на **клиентский ПК** (`internal_ip` из события).
3. Результат — модалка: SESSION, USER, ID, STATE.
4. **logoff:** `POST .../actions/logoff { session_id }` → WinRM `logoff` → повторный qwinsta.
5. Нужен **Windows domain admin** в настройках SAC (`SAC_WIN_ADMIN_*` или UI).
```mermaid
sequenceDiagram
participant Op as Оператор
participant UI as SAC UI
participant API as SAC API
participant Ag as RDP-agent
Op->>UI: qwinsta
UI->>API: POST events/{id}/actions/qwinsta
API->>Ag: poll command qwinsta
Ag->>Ag: runas domain admin
Ag->>API: agent.command.result
API->>UI: modal + parsed sessions
Op->>UI: logoff session_id
UI->>API: POST actions/logoff
Ag->>API: agent.command.result
```
**Автоматический logoff без участия оператора — не делаем** в первых итерациях.
---
## 4. П.2 — Обновления агентов (Windows + Linux)
### 4.1. Настройки SAC → «Обновления агентов»
| Режим | Описание |
|-------|----------|
| **A — GPO / ручной** | Как сейчас: NETLOGON, `Deploy-LoginMonitor.ps1`, `update_ssh_monitor.sh`. SAC только показывает устаревшие версии. |
| **B — SAC** | SAC инициирует обновление; агент тянет пакет сам (pull). |
**Рекомендуемые версии** по продукту (`RDP-login-monitor`, `ssh-monitor`) — min / recommended; сверка с `host.product_version` (уже есть в UI «Хосты»).
**Источник пакетов** (выбор в настройках):
| Источник | Windows | Linux |
|----------|---------|-------|
| SMB / NETLOGON | `\\dc\NETLOGON\...` | — |
| Git | private repo kalinamall | `git pull` в `/opt/ssh-monitor` |
| HTTP(S) SAC | zip + checksum с SAC | zip + checksum |
### 4.2. B1 — Self-update после «пинка» (основной путь)
1. Admin: «Запросить обновление» на хосте / группе.
2. SAC: `host.pending_update = true` в ответе poll.
3. **Windows:** агент запускает `Deploy-LoginMonitor.ps1` (существующий скрипт).
4. **Linux:** агент запускает `update_ssh_monitor.sh`.
5. Ingest: `agent.update.started``agent.update.success` | `agent.update.failed`.
### 4.3. B2 — Fallback (SSH / WinRM)
Кнопка на карточке хоста: **«Обновить через WinRM»** / **«Fallback SSH»** (`POST .../actions/agent-update-fallback`). Лог операции — модалка сразу при старте (не ждёт конца).
Если за **N минут** нет `agent.update.*` или статус `failed` — тот же fallback может сработать автоматически (если включён в настройках).
| OS | Метод | Credentials | Как работает (SAC 0.20.18+) |
|----|--------|-------------|------------------------------|
| Linux | SSH | ключ после bootstrap (§5) | SAC передаёт `REPO_URL` / `GIT_BRANCH` из **Настройки → Обновления агентов** в `update_ssh_monitor.sh` (не дефолт GitHub на хосте) |
| Windows | WinRM | encrypted login/password в БД | 1) SAC `git fetch` RDP-login-monitor на сервере → zip с UTF-8 BOM для `.ps1`<br>2) Клиент по WinRM: `Invoke-WebRequest``https://<SAC_PUBLIC_URL>/api/v1/agent/rdp-bundle/<token>`<br>3) Распаковка в `C:\ProgramData\RDP-login-monitor\_sac_staging`<br>4) `Deploy-LoginMonitor.ps1 -SourceShareRoot` (файлы в `ProgramData\RDP-login-monitor\`) |
**Windows — требования:** domain admin в **Настройки → Управление хостами → Windows**; `rdp_git_repo_url` в **Обновления агентов**; ПК должен открывать `SAC_PUBLIC_URL` (HTTPS). **Git на клиенте не нужен.** GPO/NETLOGON по-прежнему основной путь в режиме GPO.
**nginx:** для `agent-update-fallback` и SSH/WinRM-тестов — `proxy_read_timeout 960s` (см. `deploy/nginx/sac.conf.tls.example`).
---
## 5. П.2C — Доступ SAC к хостам
### 5.1. Windows — WinRM / учётки
- **Глобальный доменный admin** в **Настройки → Windows**: `DOMAIN\user` + password (encrypted). Аналогично **Linux admin** для SSH.
- **Override на карточке хоста** (**Хосты → WinRM/SSH / доступ к хосту**): `COMPUTER\user` / `.\Administrator` + password (encrypted в `hosts.mgmt_*`). Если пусто — берутся глобальные.
- Effective creds: `get_effective_win_admin_for_host` / `get_effective_linux_admin_for_host` (source: `host` | `db` | `env`).
- API: `GET` / `PUT /api/v1/hosts/{id}/access` (`user`, `password`, `clear`).
- Используются для WinRM/SSH-test, fallback-update, qwinsta/logoff, remote ops; для run_as через агента — те же creds в command poll (TLS + API key, не пишутся на диск агента).
### 5.2. Linux — bootstrap password → SSH key → удалить password
На карточке хоста → «Доступ для управления»:
```
1. Admin вводит login + password (временно)
2. SAC по SSH: генерирует ed25519, добавляет pubkey в authorized_keys
3. SAC проверяет вход по ключу
4. Успех → password DELETE из БД, status = key_ready
5. Ошибка → password остаётся, status = bootstrap_failed, алерт
6. Кнопка «Переустановить ключи» — повтор bootstrap
```
Private key — encrypted в PostgreSQL.
### 5.3. Статусы доступа
| Статус | Linux | Windows |
|--------|-------|---------|
| `no_access` | нет данных | нет WinRM |
| `bootstrap_pending` | идёт настройка ключа | — |
| `key_ready` | SSH по ключу | — |
| `winrm_ready` | — | WinRM настроен |
---
## 6. П.3 — Настройки агента в карточке хоста
**Desired state** в SAC; агент забирает на poll:
```json
{
"config_revision": 42,
"settings": {
"ServerDisplayName": "UNMS Kalina",
"EnableRcmShadowControlMonitoring": true,
"GetInventory": true,
"HEARTBEAT_INTERVAL": "300"
}
}
```
- Whitelist ключей — без секретов (`SAC_API_KEY` только локально).
- **Windows:** поля из `login_monitor.settings.ps1`.
- **Linux:** поля из `/etc/ssh-monitor.conf`.
- Merge: remote перекрывает локальное, revision монотонно растёт.
---
## 7. API (черновик)
### 7.1. Poll (агент)
```http
GET /api/v1/agent/commands?since=<last_ack>
Authorization: Bearer sac_xxx
```
```json
{
"config_revision": 42,
"config": { "settings": { } },
"update": { "requested": false, "target_version": "2.0.38-SAC", "source": "smb://..." },
"commands": [
{ "id": "uuid", "type": "qwinsta", "params": { "user": "B26\\user" } }
]
}
```
### 7.2. UI actions (JWT: admin / monitor / mobile)
| Метод | Путь | Назначение |
|-------|------|------------|
| POST | `/api/v1/events/{id}/actions/qwinsta` | qwinsta (WinRM на клиентский ПК) |
| POST | `/api/v1/events/{id}/actions/logoff` | logoff `{ "session_id": 5 }` |
| GET | `/api/v1/events/{id}/actions/{cmd_id}` | Статус / результат |
| PATCH | `/api/v1/hosts/{id}/config` | Desired config |
| GET/PUT | `/api/v1/hosts/{id}/access` | Per-host WinRM/SSH override (mgmt_user/password) |
| GET/PATCH | `/api/v1/settings/agent-updates` | Режим A/B, версии, источники |
| GET/PATCH | `/api/v1/settings/host-management` | Доменный admin Windows |
### 7.3. Ingest (новые типы)
| `type` | Назначение |
|--------|------------|
| `agent.command.result` | stdout/stderr qwinsta, logoff |
| `agent.update.started` | начало self-update |
| `agent.update.success` | успех |
| `agent.update.failed` | ошибка |
---
## 8. Фазы реализации
| Фаза | Содержание | Статус |
|------|------------|--------|
| **1** | `rule:rdg_session_flap`, Problem, notify, флаг `rdg_flap` | ✅ SAC |
| **2** | **qwinsta** / **logoff** — веб + Seaca | ✅ |
| **3** | Poll API, `agent_commands` (агент) | ✅ RDP ≥ 2.1.0-SAC |
| **4** | WinRM qwinsta с сервера SAC | ✅ |
| **5** | Settings «Обновления агентов», SSH bootstrap | частично ✅ |
| **6** | Self-update B1 | ⏳ |
| **7** | SSH bootstrap + WinRM fallback | частично ✅ (SSH/WinRM update из UI, git URL из настроек) |
| **8** | Desired config per host | ⏳ |
**Миграция:** `016_agent_commands`. Env: `SAC_RDG_FLAP_*`, `SAC_WIN_ADMIN_USER`, `SAC_WIN_ADMIN_PASSWORD`.
---
## 9. Безопасность
- Команды qwinsta/logoff — JWT (веб и Seaca); `requested_by` в `agent_commands`.
- Rate limit на actions per host.
- logoff — confirm dialog; по умолчанию только matching user.
- Password bootstrap Linux — удаляется после успеха; re-bootstrap явной кнопкой.
- WinRM password — только encrypted storage, не в логах и не в Telegram.
---
## 10. Git / remotes (справка)
| Репозиторий | Prod (kalinamall) | Public (github) |
|-------------|-------------------|-----------------|
| security-alert-center | основной | sanitized |
| RDP-login-monitor | prod paths/secrets | sanitized settings |
| ssh-monitor | mirror-скрипты + URLs | без mirror-скриптов |
Mirror-скрипты: временно переписывают URL в docs под целевой host, пушат на remote, **откатывают** локальный main — чтобы в одной ветке не смешивать kalinamall/github URLs.
---
## См. также
- [agent-integration.md](agent-integration.md) — ingest, типы RDG
- [agent-update-backlog.md](agent-update-backlog.md) — ранний backlog (superseded деталями этого документа)
- [roadmap.md](roadmap.md) — v0.7
+306
View File
@@ -0,0 +1,306 @@
# Интеграция агентов с SAC
Контракт между **ssh-monitor**, **RDP-login-monitor** и **Security Alert Center**.
Изменения вносятся в репозитории агентов отдельными задачами.
---
## 1. Режим `UseSAC`
### 1.1. Параметры конфигурации
**ssh-monitor** (`/etc/ssh-monitor.conf`):
```ini
# 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`):
```powershell
$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_id`**409**, тоже успех для 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).
```env
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`).
Telegram из SAC отправляется с **`parse_mode=HTML`** (как у RDP-login-monitor): для `rdp.login.*` — пользователь, IP, `logon_type`, рабочая станция, Event ID Windows; для `ssh.login.*` и `privilege.sudo.command` — поля из `details`.
UI **Настройки** (`/settings`) — правило + параметры каналов (JWT admin). После деплоя: `alembic upgrade head`.
### 1.4. Версии и доставка обновлений
При **любом** изменении агента, влияющем на SAC или поведение на хосте, поднимайте версию и пушьте в **GitHub** (`github.com/PTah`); для зеркала kalinamall — `.\scripts\Push-Mirror.ps1 kalinamall`:
| Агент | Маркер версии | Как хост узнаёт о новой версии |
|-------|---------------|--------------------------------|
| **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 «Хосты») |
**Proactive host_silence:** фоновый scan в `sac-api` (`SAC_HOST_SILENCE_SCAN_ENABLED`, интервал `SAC_HOST_SILENCE_SCAN_INTERVAL_MINUTES`, по умолчанию каждые 5 мин) создаёт Problem и шлёт Telegram **без ожидания** нового ingest с хоста. Альтернатива: `systemd sac-host-silence-scan.timer` + `python -m app.jobs.host_silence_scan` (отключите in-process scan при нескольких воркерах uvicorn).
Свежий `agent.heartbeat` **автоматически resolve** open/ack проблему `rule:host_silence`. Одиночные `high`/`critical` (ban, mass bruteforce) — как раньше.
---
## 2. Протокол ingest
### 2.1. Запрос
```http
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
```
### 2.2. Ответ
| HTTP | Значение | `status` в теле | `created` |
|------|----------|-----------------|-----------|
| **201** | Событие записано впервые | `created` | `true` |
| **409** | Тот же `event_id` уже есть (идемпотентность) | `duplicate` | `false` |
| **422** | Ошибка JSON Schema | — | — |
Пример **201**:
```json
{
"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` (SSH / ipset)
- RDP-login-monitor: автобан IP в схеме **пока не стандартизирован** (`rdp.ip.banned` зарезервирован в SAC для будущего); в отчёте Windows строка «Активных банов» = 0 или число событий `rdp.ip.banned`, если агент начнёт слать
- `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 |
| 4634 / 4647 выход (прямой RDP, **только рабочая станция**) | `rdp.session.logoff` | info |
| RCM **20506** Shadow Control started | `rdp.shadow.control.started` | **warning** |
| RCM **20507** Shadow Control stopped | `rdp.shadow.control.stopped` | **warning** |
| RCM **20510** Shadow Control permission | `rdp.shadow.control.permission` | **warning** |
| WinRM **91** inbound shell (Enter-PSSession) | `winrm.session.started` | **warning** |
| Security **5140** admin share (`C$`, `ADMIN$`) | `smb.admin_share.access` | **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 |
| Инвентаризация железа/ПО | `agent.inventory` | info (SAC: **warning**, если изменилось железо) |
**Инвентаризация (`agent.inventory`, RDP ≥ 2.0.21-SAC):** агент шлёт снимок в `details.inventory` (CPU, RAM, диски, GPU, Windows, IPv4). Интервал по умолчанию **12 ч**; отключение: `$GetInventory = $false` в `login_monitor.settings.ps1`. SAC хранит последний снимок в `hosts.inventory`; при изменении железа ingest повышает severity до **warning** и шлёт оповещение.
**Дополнительные поля:**
- `event_id_windows`, `logon_type`, `ip_address`, `workstation_name`
- Shadow: `shadower_user`, `target_user`, `session_id`, `shadow_mode`, `shadow_action`
- WinRM: `source_ip`, `resource_uri`, `transport`
- Admin share 5140: `share_name`, `share_path`, `relative_target`, `access_mask`
- `gateway_target`, `gateway_error_code`
- `filtered_out`, `filter_reason`
**Переключатели в `login_monitor.settings.ps1` (≥ 1.2.23-SAC):** `$EnableRcmShadowControlMonitoring`, `$EnableWinRmInboundMonitoring`, `$EnableAdminShareMonitoring` (по умолчанию `1`; Security **5140**, audit File Share). **`$GetInventory`** (по умолчанию `$true`) — опрос железа/ПО для SAC. Подавление: `ignore.lst` с префиксами `shadow:`, `winrm:`, `smb:` / `5140:`.
**Exchange (RDP ≥ 2.0.23-SAC):** на почтовом сервере `$WinRmExchangeStrictMode = 1` — WinRM **91** без user в EventData не уходит в SAC; корреляция **4624** только при `LogonProcess WinRM` (отсекает ложные связки с Outlook/LT3).
---
## 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:
```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` (события, по умолчанию **90 с**) и по `fingerprint` problem (**300 с**). Таблица `notification_cooldown`, env: `SAC_NOTIFY_EVENT_COOLDOWN_SEC`, `SAC_NOTIFY_PROBLEM_COOLDOWN_SEC`. В логах API: `notify cooldown skip`.
---
## 5. Точки встраивания в код агентов
### ssh-monitor
- Новая функция `send_sac_event()` вызывается из мест, где сейчас `notify_send "$message"`.
- Обёртка `notify_or_sac()`:
- `UseSAC=off``notify_send`
- `exclusive` → только `send_sac_event``summary` — тот же текст)
- `dual` → оба
- `fallback``send_sac_event`; при fail increment counter → при пороге `notify_send`
### RDP-login-monitor
- `Send-SacEvent` + замена вызовов `Send-TelegramMessage` для событий мониторинга.
- Heartbeat и daily report — отдельные типы в SAC.
- **Команды SAC (≥ 2.1.0-SAC):** poll `GET /api/v1/agent/commands?agent_instance_id=…` каждые `$SacCommandPollIntervalSec` (по умолчанию 60 с); типы `qwinsta`, `logoff`. Ответ: `POST /api/v1/agent/commands/{uuid}/result`. Учётные данные доменного admin приходят в `run_as` (на SAC — `SAC_WIN_ADMIN_*`). Подробнее: [agent-control-plane.md](agent-control-plane.md).
- При `UseSAC=exclusive` суточный отчёт может формироваться **на SAC** (`generated_by: sac` в `details`), если агент за день не прислал `report.daily.*` — см. `SAC_DAILY_REPORT_*`, job `python -m app.jobs.daily_report`, timer `sac-daily-report.timer`.
- **Единый шаблон отчёта** (SSH / Windows): агенты `ssh-monitor` ≥ 1.2.8-SAC, `RDP-login-monitor` ≥ 1.2.21-SAC и SAC используют одинаковые секции в `report_body` / `details.stats`.
- **Только SAC, без отчёта с агента:** на агенте `DAILY_REPORT_ENABLED=0` (ssh) или `$DailyReportEnabled = $false` (RDP); на SAC `SAC_DAILY_REPORT_ENABLED=true`, `SAC_DAILY_REPORT_SKIP_IF_AGENT_SENT=true`.
---
## 6. Обратная совместимость
- По умолчанию `UseSAC=off` — поведение 100% как сейчас.
- `BACKUP_WEBHOOK_URL` в ssh-monitor при `exclusive` не используется для обычных алертов (только emergency в `fallback` — опционально).
---
## 7. Чеклист готовности агента
- [x] Параметры конфига задокументированы в README агента (ssh-monitor, RDP-login-monitor)
- [x] `--check-sac` / `Test-SacConnection` / `-CheckSac`
- [x] Spool и повторная отправка
- [ ] Все типы из п. 3 покрыты (RDP: основные; 4648 — при появлении обработки)
- [ ] `event_id` UUID на каждое событие
- [ ] Секреты не в git
---
## 8. См. также
- [event-schema-v1.json](event-schema-v1.json)
- [TZ.md](TZ.md) §3.2, §4.8
- [TZ.md](TZ.md) §3.2, §4.8
+58
View File
@@ -0,0 +1,58 @@
# ToDo — управление версиями агентов и удалённое обновление
**Статус:** superseded — основной документ [agent-control-plane.md](agent-control-plane.md).
**Связано:** [roadmap.md](roadmap.md) (v0.7), [agent-integration.md](agent-integration.md), UI «Хосты».
---
## Цель
SAC должен:
1. **Отслеживать соответствие версий** агентов (`ssh-monitor`, `RDP-login-monitor`) ожидаемым/минимальным для текущей версии SAC.
2. **Инициировать обновление удалённо** с компьютеров/серверов из UI SAC (или по политике), без ручного захода на каждый хост.
Сейчас версии видны по `host.product_version` и heartbeat; обновление — вручную (`update_ssh_monitor.sh`, `Deploy-LoginMonitor.ps1` / NETLOGON, GPO).
---
## Вопросы для проработки
| # | Тема | Варианты / заметки |
|---|------|-------------------|
| 1 | **Источник истины версии** | Матрица в SAC (min/recommended per `product`); теги в репо; отдельный manifest API |
| 2 | **Сигнал «устарел»** | Problem `agent.version.stale`; severity на карточке хоста; дашборд «не на версии» |
| 3 | **Канал команд** | Pull-only (агент сам тянет по расписанию) vs push-команда от SAC (нужен обратный канал) |
| 4 | **Linux** | Вызов `update_ssh_monitor.sh` по systemd timer + флаг «pending update» из SAC ingest response? Отдельный `agent.command` event? SSH/Ansible с SAC — вне scope агента |
| 5 | **Windows** | GPO/NETLOGON уже есть; remote = триггер `Deploy-LoginMonitor.ps1` через scheduled task / WinRM / SCCM — что допустимо по безопасности |
| 6 | **Авторизация** | Только admin SAC; audit log; подтверждение массового обновления; rate limit |
| 7 | **Отчёт о результате** | `agent.update.started` / `agent.update.success` / `agent.update.failed` в ingest; привязка к `host_id` |
| 8 | **Сеть** | Агент за NAT только исходящий HTTPS — предпочтителен **pull**, не inbound на хост |
| 9 | **Совместимость** | Связка с `schema_version`, откат, окно maintenance |
---
## Черновые критерии приёмки (когда дойдём до реализации)
- [ ] В UI «Хосты»: колонка/бейдж «версия OK / устарела / неизвестна».
- [ ] Настройка минимальной/рекомендуемой версии по продукту в SAC (или env).
- [ ] Problem или оповещение при расхождении (с cooldown).
- [ ] Действие admin: «Запросить обновление» на одном хосте / группе (MVP).
- [ ] Агент подтверждает попытку обновления событием в SAC.
- [ ] Документация: Linux + Windows runbook, ограничения безопасности.
---
## Не в первой итерации (вероятно)
- Полная замена Ansible/GPO/SCCM.
- Обновление SAC с агентов.
- macOS-агент.
---
## См. также
- [analytics-backlog.md](analytics-backlog.md) — отложенная аналитика
- [todo-2026-05-29.md](todo-2026-05-29.md) — оповещения и настройки
- Репозитории агентов: `update_ssh_monitor.sh`, `Deploy-LoginMonitor.ps1`, `update-rdp-monitor.ps1`
+41
View File
@@ -0,0 +1,41 @@
# Analytics backlog (SAC)
Разработка отложена; задачи для `work-plan.md` / спринта.
## Фаза A — API
| ID | Задача | Готово когда |
|----|--------|--------------|
| analytics-a1 | `GET /api/v1/analytics/series` (`bucket=hour\|day\|week\|month`, `from`/`to`) | JSON series + тесты |
| analytics-a2 | `group_by=severity\|type\|product`, `exclude_types=agent.heartbeat` | heartbeat не в общем счётчике |
| analytics-a3 | Presets 24h / 7d / 30d / 90d | документация API |
| analytics-a4 | Dashboard: `daily_reports_24h` учитывает `report.daily.rdp` | ✅ test_dashboard |
| analytics-a5 | Индексы под агрегации | EXPLAIN без full scan |
## Фаза B — UI
| ID | Задача |
|----|--------|
| analytics-b1 | Страница «Аналитика» + сайдбар |
| analytics-b2 | Графики: события по дням; failed vs success login |
| analytics-b3 | Sparklines на «Обзоре» (опционально) |
| analytics-b4 | Chart.js / аналог в Vite bundle |
## Фаза C — отчёты и SOC
| ID | Задача |
|----|--------|
| analytics-c1 | Тренды из `report.daily.*``details.stats` |
| analytics-c2 | KPI: % хостов с отчётом за 25h |
| analytics-c3 | Problems opened/resolved, MTTR |
| analytics-c4 | Top `source_ip` для `*.login.failed` |
## Фаза D — масштаб
| ID | Задача |
|----|--------|
| analytics-d1 | `analytics_daily_rollups` + nightly job |
| analytics-d2 | Экспорт CSV |
| analytics-d3 | `docs/analytics.md` (метрики и лимиты retention) |
**Приоритет:** A1 → B1 → C1 → остальное.
+163
View File
@@ -0,0 +1,163 @@
# Архитектура Security Alert Center
Дополнение к [TZ.md](TZ.md). Описывает компоненты, границы и модель данных.
---
## 1. Диаграмма компонентов
```mermaid
flowchart TB
subgraph external [Внешние системы]
AG_SSH[ssh-monitor]
AG_RDP[RDP-login-monitor]
TG[Telegram API]
SMTP[SMTP]
end
subgraph sac_host [Ubuntu 24.04 — хост SAC]
NGX[nginx TLS]
API[FastAPI — api]
WRK[Worker]
FE[Static SPA]
PG[(PostgreSQL)]
RD[(Redis — фаза 1.5+)]
end
AG_SSH -->|HTTPS ingest| NGX
AG_RDP -->|HTTPS ingest| NGX
NGX --> API
NGX --> FE
API --> PG
API --> RD
WRK --> PG
WRK --> RD
WRK --> TG
WRK --> SMTP
FE -->|REST + SSE| NGX
```
---
## 2. Компоненты
| Компонент | Ответственность |
|-----------|-----------------|
| **nginx** | TLS, rate limit, раздача static UI, прокси `/api` → FastAPI |
| **api** | Ingest, REST для UI, auth JWT, health |
| **worker** | Правила Problems, отправка уведомлений, суточные отчёты, retention |
| **frontend** | SPA: Problems, Events, Hosts, Dashboards, Settings |
| **PostgreSQL** | События, хосты, problems, пользователи, правила, audit |
| **Redis** | Очередь задач, pub/sub для SSE (опционально) |
---
## 3. Границы контекстов
### 3.1. Агент (вне SAC)
- Чтение локальных журналов (journalctl, Security.evtx).
- Формирование JSON-события.
- Режим `UseSAC`: маршрутизация «локальные каналы» vs «только SAC».
- Локальный spool при недоступности SAC.
### 3.2. SAC
- Единственный источник доставки оповещений при `UseSAC=exclusive`.
- Дедупликация, корреляция, Problems.
- Долговременное хранение и UI.
---
## 4. Логическая модель данных
### 4.1. Сущности
```
tenants (опционально, фаза 3)
└── hosts
└── events (партиции по occurred_at)
└── problems
└── problem_events (M:N)
└── notification_rules
└── notification_log
└── users
└── audit_log
└── api_keys (hash)
```
### 4.2. Ключевые поля
**hosts**
- `id`, `agent_instance_id` (unique), `hostname`, `display_name`
- `os_family`, `os_version`, `product` (`ssh-monitor` | `rdp-login-monitor`)
- `use_sac_mode`, `last_seen_at`, `tags[]`
**events**
- `id`, `event_id` (UUID от агента, unique), `host_id`
- `occurred_at`, `received_at`
- `category`, `type`, `severity`
- `title`, `summary`, `details` (JSONB), `raw` (JSONB/text)
- `dedup_key`, `correlation_id`
**problems**
- `id`, `title`, `severity`, `status`
- `opened_at`, `acknowledged_at`, `resolved_at`
- `rule_id`, `dedup_key`
---
## 5. API (черновой перечень)
| Метод | Путь | Назначение |
|-------|------|------------|
| POST | `/api/v1/events` | Ingest одного события |
| POST | `/api/v1/events/batch` | Batch (фаза 1.5) |
| GET | `/health` | Healthcheck |
| GET | `/api/v1/events` | Список (UI, auth) |
| GET | `/api/v1/events/{id}` | Карточка |
| GET | `/api/v1/problems` | Список Problems |
| PATCH | `/api/v1/problems/{id}` | ack / resolve |
| GET | `/api/v1/hosts` | Хосты |
| GET | `/api/v1/dashboards/summary` | Агрегаты для виджетов |
| GET | `/api/v1/stream/events` | SSE live |
| POST | `/api/v1/auth/login` | JWT |
| CRUD | `/api/v1/notification-rules` | Правила (admin) |
OpenAPI — генерируется FastAPI при реализации.
---
## 6. Каталоги репозитория (целевая структура)
```
security-alert-center/
backend/ # FastAPI, models, services
frontend/ # Vue SPA
deploy/
docker-compose.yml
nginx/
systemd/
docs/ # ТЗ, планы (текущая фаза)
schemas/ # Копия/ссылка JSON Schema
```
На фазе документации каталоги `backend/` и `frontend/` — заглушки (см. README внутри).
---
## 7. Наблюдаемость SAC
- `GET /health` — для агентов и внешнего мониторинга.
- Метрики (фаза 2): Prometheus endpoint или textfile.
- Логирование: каждый ingest (без полного `raw` в info-логах).
---
## 8. См. также
- [TZ.md](TZ.md) — требования
- [deployment.md](deployment.md) — инфраструктура Ubuntu 24.04
+138
View File
@@ -0,0 +1,138 @@
# Развёртывание на Ubuntu 24.04
Эксплуатация SAC: **native** (PostgreSQL + systemd + nginx). Docker — только альтернатива.
## Руководства по установке
| Документ | Назначение |
|----------|------------|
| **[install-ubuntu-24.04-native.md](install-ubuntu-24.04-native.md)** | **Production** — пошаговая подготовка сервера |
| [install-ubuntu-24.04-docker.md](install-ubuntu-24.04-docker.md) | Стенд / отладка (Compose) |
| [install-ubuntu-24.04.md](install-ubuntu-24.04.md) | Указатель |
## Артефакты native в репозитории
```
deploy/
env.native.example → /opt/security-alert-center/config/sac-api.env
systemd/sac-api.service → /etc/systemd/system/
nginx/sac.conf.example → /etc/nginx/sites-available/sac (HTTP)
nginx/sac.conf.tls.example → HTTPS + wildcard
sac-deploy.sh → /opt/sac-deploy.sh (полное обновление)
```
**Production URL:** `https://sac.kalinamall.ru`
**Конфиг:** `/opt/security-alert-center/config/sac-api.env` (владелец `sac:sac`, chmod 600).
**UI:** статика `frontend/dist`, раздаётся через FastAPI (после `npm run build`).
---
## 1. Требования к серверу
| Параметр | Минимум | Рекомендуется |
|----------|---------|---------------|
| ОС | Ubuntu 24.04 LTS | Ubuntu 24.04 LTS |
| CPU | 2 vCPU | 4 vCPU |
| RAM | 4 GB | 8 GB |
| Диск | 50 GB SSD | 100+ GB SSD |
**Порты снаружи:** 443 (nginx), 80 (редирект). API `8000` — только `127.0.0.1`. PostgreSQL — только localhost.
---
## 2. Сетевые правила
**Агенты →** `https://sac.kalinamall.ru/api/v1/events`
**SAC →** Telegram, SMTP (исходящие)
**Админы →** 443 (UI/API через nginx)
---
## 3. Вариант A: Native (production)
Компоненты на хосте:
| Компонент | Как |
|-----------|-----|
| PostgreSQL 16 | `apt`, БД `sac` |
| SAC API | Python 3.12 venv, **systemd** `sac-api` |
| TLS / proxy | **nginx** |
| UI | `frontend/dist`, сборка `npm run build`, раздача из API |
Полная инструкция: **[install-ubuntu-24.04-native.md](install-ubuntu-24.04-native.md)**.
Кратко:
```bash
systemctl status sac-api
curl -sS http://127.0.0.1:8000/health
curl -sS https://sac.kalinamall.ru/health
```
---
## 4. Вариант B: Docker Compose (альтернатива)
См. [install-ubuntu-24.04-docker.md](install-ubuntu-24.04-docker.md) и `deploy/docker-compose.yml`.
**Не используется** для production KalinaMall.
---
## 5. TLS
- **Production (KalinaMall):** корпоративный **wildcard** (VeriSign) — [ssl-certificate.md](ssl-certificate.md) (копирование, форматы, `/etc/ssl/sac/`), [install-ubuntu-24.04-native.md](install-ubuntu-24.04-native.md) §9, `deploy/nginx/sac.conf.tls.example`
- Альтернатива: **certbot** + Let's Encrypt (`deploy/nginx/sac.conf.example`)
---
## 6. Резервное копирование (native)
```bash
# /etc/cron.d/sac-backup
0 3 * * * postgres pg_dump -Fc -U sac sac > /var/backups/sac/sac_$(date +\%Y\%m\%d).dump
```
Восстановление: `pg_restore -d sac_restored /var/backups/sac/sac_YYYYMMDD.dump`
---
## 7. Обслуживание
| Задача | Периодичность |
|--------|---------------|
| `unattended-upgrades` | автоматически |
| `journalctl -u sac-api` | по инцидентам |
| `/health` | каждые 5 мин |
| Тест restore БД | ежемесячно |
---
## 8. Обновление (native)
```bash
sudo /opt/sac-deploy.sh
```
Краткий **502 Bad Gateway** в UI (~10 с) возможен при перезапуске `sac-api` — список хостов автоматически повторяет запрос; после деплоя обновите страницу.
**Важно в `sac-api.env`:** `SAC_PUBLIC_URL=https://sac.kalinamall.ru` — нужен для WinRM-обновления RDP (клиент скачивает zip с SAC). API: **4 worker** uvicorn по умолчанию (`SAC_UVICORN_WORKERS`, `deploy/systemd/sac-api-start.sh`).
Установка скрипта (один раз): `sudo cp /opt/security-alert-center/deploy/sac-deploy.sh /opt/sac-deploy.sh && sudo chmod 755 /opt/sac-deploy.sh`
После **force-push** на upstream remote обычный `git pull` на сервере падает с «diverging branches». Скрипт `sac-deploy.sh` (актуальная версия) делает `fetch` и при необходимости `reset --hard` к upstream. **Один раз вручную**, если на сервере ещё старый скрипт:
```bash
sudo -u sac git -C /opt/security-alert-center fetch --prune
sudo -u sac git -C /opt/security-alert-center reset --hard origin/main
sudo cp /opt/security-alert-center/deploy/sac-deploy.sh /opt/sac-deploy.sh
sudo /opt/sac-deploy.sh
```
Подробности: [install-ubuntu-24.04-native.md §13](install-ubuntu-24.04-native.md#13-обновление-sac-деплой).
---
## См. также
- [TZ.md](TZ.md)
- [agent-integration.md](agent-integration.md)
+45
View File
@@ -0,0 +1,45 @@
# Диаграмма потоков данных (рис. 1 к ТЗ)
Используется в [TZ.md](../TZ.md), раздел 3.
В GitLab/GitHub и в VS Code с поддержкой Mermaid диаграмма рендерится автоматически.
## Экспорт в PNG/SVG
1. Открыть этот файл в preview редактора или на https://mermaid.live
2. Экспортировать как изображение для презентаций
```mermaid
flowchart LR
subgraph agents [Агенты на хостах]
SSH[ssh-monitor Linux]
RDP[RDP-login-monitor Windows]
end
subgraph sac [Security Alert Center Ubuntu 24.04]
API[Ingest API HTTPS]
Q[Очередь / воркер]
DB[(PostgreSQL)]
RT[Realtime SSE/WebSocket]
UI[Web UI]
NOTIFY[Правила оповещений]
end
subgraph channels [Каналы наружу]
TG[Telegram]
MAIL[Email]
WEBHOOK[Webhook / Slack]
end
SSH -->|JSON события + API key| API
RDP -->|JSON события + API key| API
API --> Q --> DB
DB --> UI
DB --> RT --> UI
DB --> NOTIFY --> TG
NOTIFY --> MAIL
NOTIFY --> WEBHOOK
```
## Режим UseSAC=exclusive
При включённом `UseSAC` стрелки от агентов к Telegram/email **отсутствуют**; доставка пользователю только через блок `NOTIFY` в SAC.
+174
View File
@@ -0,0 +1,174 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.papatramp.ru/PapaTramp/security-alert-center/schemas/event-v1.json",
"title": "Security Alert Center Event v1",
"description": "Каноническое событие от ssh-monitor или RDP-login-monitor",
"type": "object",
"required": [
"schema_version",
"event_id",
"occurred_at",
"source",
"host",
"category",
"type",
"severity",
"title",
"summary"
],
"additionalProperties": false,
"properties": {
"schema_version": {
"type": "string",
"const": "1.0"
},
"event_id": {
"type": "string",
"format": "uuid",
"description": "UUID v4, уникален глобально, для идемпотентности"
},
"occurred_at": {
"type": "string",
"format": "date-time",
"description": "ISO 8601 с offset, время события на хосте"
},
"source": {
"type": "object",
"required": ["product", "product_version"],
"additionalProperties": false,
"properties": {
"product": {
"type": "string",
"enum": ["ssh-monitor", "rdp-login-monitor"]
},
"product_version": {
"type": "string"
},
"agent_instance_id": {
"type": "string",
"description": "Стабильный ID установки агента"
}
}
},
"host": {
"type": "object",
"required": ["hostname", "os_family"],
"additionalProperties": false,
"properties": {
"hostname": { "type": "string" },
"display_name": { "type": "string" },
"fqdn": { "type": "string" },
"os_family": {
"type": "string",
"enum": ["linux", "windows"]
},
"os_version": { "type": "string" },
"ipv4": { "type": "string" },
"ipv6": { "type": "string" },
"timezone": {
"type": "string",
"description": "IANA, например Europe/Moscow"
}
}
},
"category": {
"type": "string",
"enum": [
"auth",
"privilege",
"network",
"session",
"report",
"agent"
]
},
"type": {
"type": "string",
"description": "Машиночитаемый тип, см. docs/agent-integration.md",
"examples": [
"ssh.login.success",
"ssh.login.failed",
"privilege.sudo.command",
"ssh.ip.banned",
"ssh.bruteforce.mass",
"session.logind.new",
"rdp.login.success",
"rdp.login.failed",
"rdp.session.logoff",
"rdp.shadow.control.started",
"rdp.shadow.control.stopped",
"rdp.shadow.control.permission",
"winrm.session.started",
"smb.admin_share.access",
"rdg.connection.success",
"report.daily.ssh",
"agent.heartbeat",
"agent.inventory",
"agent.test"
]
},
"severity": {
"type": "string",
"enum": ["info", "warning", "high", "critical"]
},
"title": {
"type": "string",
"maxLength": 256
},
"summary": {
"type": "string",
"description": "Человекочитаемый текст (как в Telegram сейчас)",
"maxLength": 8192
},
"details": {
"type": "object",
"description": "Структурированные поля по типу события",
"additionalProperties": true
},
"raw": {
"type": "object",
"properties": {
"format": {
"type": "string",
"enum": ["journal", "windows_event_xml", "text"]
},
"payload": {
"type": "string",
"maxLength": 16384
}
},
"additionalProperties": false
},
"tags": {
"type": "array",
"items": { "type": "string", "maxLength": 64 },
"maxItems": 32
},
"dedup_key": {
"type": "string",
"maxLength": 512
},
"correlation_id": {
"type": "string",
"format": "uuid"
},
"fingerprint": {
"type": "string",
"maxLength": 128
},
"enrichment": {
"type": "object",
"additionalProperties": true,
"properties": {
"attempt_number": { "type": "integer", "minimum": 0 },
"threshold": { "type": "integer", "minimum": 0 },
"risk_level": {
"type": "string",
"enum": ["low", "medium", "high", "critical"]
},
"filtered_out": { "type": "boolean" },
"filter_reason": { "type": "string" }
}
}
}
}
+50
View File
@@ -0,0 +1,50 @@
# Backlog: ingest и массовое обновление агентов
**Статус:** ToDo (не в текущем релизе).
**Контекст:** массовый SSH-update через SAC (10+ хостов) + `sac-deploy` в одно окно → часть POST в ingest «теряется» с точки зрения агента (`SAC POST HTTP :`), flood в Telegram (fallback + watchdog).
**Связано:** [runbook-ops.md](runbook-ops.md), [agent-integration.md](agent-integration.md), ssh-monitor [security-roadmap.ru.md](https://git.papatramp.ru/PapaTramp/ssh-monitor/src/branch/main/docs/security-roadmap.ru.md).
Увеличение `SAC_DB_POOL_SIZE` / defer daily в **0.5.0** лечит штурм суточных отчётов и пул БД; **не** снимает узкие места ниже при одновременном restart многих агентов.
---
## Операционно (сразу, без кода)
- [ ] **Не совмещать** `sudo /opt/sac-deploy.sh` и массовое «Обновить ssh-monitor (SSH)» по многим хостам — сначала deploy SAC, потом агенты (или наоборот).
- [ ] Обновлять Linux-хосты **пачками по 23**, не все подряд.
- [ ] На зрелых хостах перевести **`UseSAC=exclusive`** (нет дубля в Telegram с агента при fallback); watchdog по-прежнему шлёт в Telegram сам.
- [ ] Перед первым SSH-update с SAC: **`ssh-keyscan`** в `config/ssh_known_hosts` (см. runbook).
- [ ] В `sac-api.env`: только `KEY=value` на строку, комментарии отдельной строкой с `#`.
---
## SAC (backend / deploy)
- [x] **Defer** `notify_lifecycle` и `notify_auth_login` в background (как `report.daily.*``schedule_notify_daily_report`), чтобы ingest не ждал Telegram API — **0.5.5**
- [x] **Uvicorn workers:** 4 по умолчанию или `SAC_UVICORN_WORKERS` в `sac-api.env` / `sac-api-start.sh`**0.5.5**
- [x] **nginx:** отдельный `location` для `POST /api/v1/events` с увеличенным `proxy_read_timeout`**0.5.5**
- [ ] Опционально: метрики/лог длительности ingest и очереди при burst.
- [x] UI: предупреждение при массовом update («N хостов — рекомендуется пачками») — **0.5.5**
---
## ssh-monitor (агент)
- [x] При **shutdown** / `SIGTERM` не увеличивать `sac-fail.count`**2.3.2-SAC**
- [x] После успешного heartbeat сбрасывать fail counter (успешный POST ingest) — уже было
- [x] Watchdog: не слать Telegram при штатном restart во время SAC-update (state file `/var/lib/ssh-monitor/agent-update-in-progress` от updater) — **2.2.1-SAC**
- [x] Sudo bootstrap/update через SAC: не слать Telegram (`ssh_monitor_sudo_is_sac_maintenance`, state-file раньше) — **2.2.3-SAC**
- [x] SAC: suppress `privilege.sudo.command` при `agent_update_state=running` / maintenance command — **0.5.3**
- [x] Документировать рекомендуемый `SAC_TIMEOUT_SEC` при тяжёлом ingest — **2.3.2-SAC** (`docs/sac-ingest.ru.md`)
---
## Принято / сделано
- [x] Pool БД 15+25, defer daily push — SAC **0.5.0**
- [x] `sac-deploy.sh` без `source` конфига, preflight pydantic — SAC `fa1bd41`
- [x] Watchdog: строка `🖥️ Сервер:` в Telegram — ssh-monitor **2.1.9-SAC**
---
*После реализации пунктов SAC — bump `APP_VERSION` и runbook.*
+105
View File
@@ -0,0 +1,105 @@
# Ubuntu 24.04 — установка SAC (Docker Compose)
**Альтернативный** способ. В KalinaMall для production используется **[native](install-ubuntu-24.04-native.md)**.
Docker удобен для локальной отладки или изолированного стенда.
---
## 0. Исходные данные
| Параметр | Пример |
|----------|--------|
| Каталог | `/opt/security-alert-center` |
---
## 1. Базовая ОС и ufw
Как в [native-руководстве](install-ubuntu-24.04-native.md) §1 (без PostgreSQL/nginx на хосте, если всё в контейнерах — nginx можно на хосте для TLS).
---
## 2. Git и клон
```bash
sudo apt install -y git
sudo git clone https://git.papatramp.ru/PapaTramp/security-alert-center.git /opt/security-alert-center
sudo chown -R "$USER:$USER" /opt/security-alert-center
```
---
## 3. Docker Engine + Compose
```bash
sudo apt install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu noble stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
sudo usermod -aG docker "$USER"
# перелогиниться
docker compose version
```
---
## 4. Конфигурация
```bash
sudo mkdir -p /etc/security-alert-center
cp /opt/security-alert-center/deploy/.env.example /etc/security-alert-center/.env
chmod 600 /etc/security-alert-center/.env
nano /etc/security-alert-center/.env
ln -sf /etc/security-alert-center/.env /opt/security-alert-center/deploy/.env
```
Заполнить: `POSTGRES_PASSWORD`, `JWT_SECRET`, `SAC_PUBLIC_URL`, `SAC_BOOTSTRAP_API_KEY`.
---
## 5. Запуск
```bash
cd /opt/security-alert-center/deploy
docker compose up -d --build
docker compose run --rm migrate
docker compose ps
curl -sS http://127.0.0.1:8000/health
```
---
## 6. Бэкап (Docker)
```bash
sudo tee /etc/cron.d/sac-backup <<'EOF'
0 3 * * * root docker compose -f /opt/security-alert-center/deploy/docker-compose.yml exec -T postgres pg_dump -U sac -Fc sac > /var/backups/sac/sac_$(date +\%Y\%m\%d).dump
EOF
```
---
## 7. Обновление
```bash
cd /opt/security-alert-center
git pull
cd deploy
docker compose build
docker compose up -d
docker compose run --rm migrate
```
---
## См. также
- [install-ubuntu-24.04-native.md](install-ubuntu-24.04-native.md) — основной путь
- [../deploy/docker-compose.yml](../deploy/docker-compose.yml)
+465
View File
@@ -0,0 +1,465 @@
# Ubuntu 24.04 — установка SAC (native, без Docker)
Пошаговое руководство: **PostgreSQL + Python venv + systemd + nginx**.
Рекомендуемый путь для production.
---
## 0. Исходные данные
| Параметр | Пример |
|----------|--------|
| FQDN | `sac.kalinamall.ru` |
| IP | `10.0.0.50` |
| Каталог | `/opt/security-alert-center` |
| Пользователь сервиса | `sac` |
| API только на localhost | `127.0.0.1:8000` |
| Снаружи | nginx → 443 → API |
---
## 1. Базовая настройка ОС
```bash
sudo apt update
sudo apt upgrade -y
sudo timedatectl set-timezone Europe/Moscow
```
### Firewall (ufw)
```bash
sudo apt install -y ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose
```
---
## 2. Пакеты
```bash
sudo apt install -y \
git \
postgresql \
postgresql-contrib \
nginx \
python3.12 \
python3.12-venv \
python3-pip \
jq \
certbot \
python3-certbot-nginx
```
Проверка:
```bash
python3.12 --version
psql --version
nginx -v
```
---
## 3. Клонирование репозитория
```bash
sudo mkdir -p /opt
sudo git clone https://git.papatramp.ru/PapaTramp/security-alert-center.git /opt/security-alert-center
```
---
## 4. PostgreSQL
Создать пользователя и БД (пароль сохраните для `.env`):
```bash
sudo -u postgres psql <<'SQL'
CREATE USER sac WITH PASSWORD 'ЗАМЕНИТЕ_НА_ДЛИННЫЙ_ПАРОЛЬ';
CREATE DATABASE sac OWNER sac;
GRANT ALL PRIVILEGES ON DATABASE sac TO sac;
SQL
```
PostgreSQL по умолчанию слушает `127.0.0.1` — для SAC этого достаточно.
Проверка:
```bash
psql "postgresql://sac@127.0.0.1/sac" -c 'SELECT 1'
```
Каталог бэкапов:
```bash
sudo mkdir -p /var/backups/sac
sudo chown postgres:postgres /var/backups/sac
```
---
## 5. Пользователь `sac` и Python venv
```bash
sudo useradd --system --home /opt/security-alert-center --shell /usr/sbin/nologin sac || true
sudo chown -R sac:sac /opt/security-alert-center
cd /opt/security-alert-center/backend
sudo -u sac python3.12 -m venv .venv
sudo -u sac .venv/bin/pip install --upgrade pip
sudo -u sac .venv/bin/pip install -r requirements.txt
```
---
## 6. Конфигурация
Файл лежит **в каталоге приложения** (владелец `sac`) — так проще, чем права на `/etc/...`.
```bash
sudo mkdir -p /opt/security-alert-center/config
sudo cp /opt/security-alert-center/deploy/env.native.example \
/opt/security-alert-center/config/sac-api.env
sudo nano /opt/security-alert-center/config/sac-api.env
sudo chown sac:sac /opt/security-alert-center/config/sac-api.env
sudo chmod 600 /opt/security-alert-center/config/sac-api.env
```
Если уже создавали `/etc/security-alert-center/sac-api.env` — перенесите:
```bash
sudo mkdir -p /opt/security-alert-center/config
sudo cp /etc/security-alert-center/sac-api.env /opt/security-alert-center/config/sac-api.env
sudo chown sac:sac /opt/security-alert-center/config/sac-api.env
sudo chmod 600 /opt/security-alert-center/config/sac-api.env
```
Обязательно задать:
- `DATABASE_URL`с паролем PostgreSQL из шага 4 (если в пароле есть `#`, `@`, `%` — возьмите значение в **кавычки**: `DATABASE_URL="postgresql+psycopg2://..."`)
- `JWT_SECRET``openssl rand -hex 32`
- `SAC_PUBLIC_URL``https://sac.kalinamall.ru`
- `SAC_BOOTSTRAP_API_KEY``python3.12 -c "import secrets; print('sac_'+secrets.token_urlsafe(32))"`
- `SAC_ADMIN_PASSWORD` — пароль входа в веб-UI (отдельно от API key агентов)
- `EVENT_SCHEMA_PATH=/opt/security-alert-center/schemas/event-schema-v1.json`
- (опц.) Telegram из SAC: `TELEGRAM_ENABLED=true`, `TELEGRAM_BOT_TOKEN`, `TELEGRAM_CHAT_ID`, `TELEGRAM_MIN_SEVERITY=high`
---
## 7. Миграции БД
Проверка, что `sac` читает конфиг (должно быть без `Permission denied`):
```bash
ls -la /opt/security-alert-center/config/sac-api.env
# должно быть: -rw------- sac sac
sudo -u sac test -r /opt/security-alert-center/config/sac-api.env && echo OK
```
Миграции:
```bash
cd /opt/security-alert-center/backend
sudo -u sac bash -c 'set -a; source /opt/security-alert-center/config/sac-api.env; set +a; .venv/bin/alembic upgrade head'
```
---
## 8. systemd
Приложение загружает `config/sac-api.env` **само** (не через `EnvironmentFile` systemd) — так же, как при `alembic` с `source`.
```bash
cd /opt/security-alert-center && sudo git pull
sudo cp /opt/security-alert-center/deploy/systemd/sac-api.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable sac-api
sudo systemctl start sac-api
sudo systemctl status sac-api --no-pager
```
Логи:
```bash
journalctl -u sac-api -f
```
---
## 9. nginx и TLS
Для KalinaMall: **корпоративный wildcard VeriSign** (или DigiCert), не Let's Encrypt.
Хост `sac.kalinamall.ru` должен попадать под `*.kalinamall.ru`.
**Подробно:** форматы, scp с Windows/Linux, `.pfx` → PEM, права — **[ssl-certificate.md](ssl-certificate.md)**.
### 9.1. Кратко: куда положить файлы
nginx ожидает **два PEM-файла**:
| Файл на сервере | Содержимое |
|-----------------|------------|
| `/etc/ssl/sac/fullchain.pem` | Wildcard-сертификат + промежуточные CA |
| `/etc/ssl/sac/privkey.pem` | Закрытый ключ |
```bash
# каталог: группа ssl-cert, чтобы www-data мог зайти в каталог (chmod 750)
sudo mkdir -p /etc/ssl/sac
sudo chown root:ssl-cert /etc/ssl/sac
sudo chmod 750 /etc/ssl/sac
# nginx читает ключ через группу ssl-cert (на чистой Ubuntu в группе часто только postgres)
sudo groupadd -f ssl-cert
sudo usermod -aG ssl-cert www-data
```
Скопировать с рабочего ПК (пример):
```bash
# с вашего компьютера:
scp fullchain.pem privkey.pem papatramp@<IP_SAC>:/tmp/
# на сервере:
sudo mv /tmp/fullchain.pem /tmp/privkey.pem /etc/ssl/sac/
sudo chmod 644 /etc/ssl/sac/fullchain.pem
sudo chmod 640 /etc/ssl/sac/privkey.pem
sudo chown root:root /etc/ssl/sac/fullchain.pem
sudo chown root:ssl-cert /etc/ssl/sac/privkey.pem
# каталог снова root:ssl-cert (если меняли владельца при mv)
sudo chown root:ssl-cert /etc/ssl/sac
sudo chmod 750 /etc/ssl/sac
# worker nginx должен подхватить группу www-data
sudo systemctl restart nginx
sudo -u www-data test -x /etc/ssl/sac && echo dir OK || echo dir FAIL
sudo -u www-data test -r /etc/ssl/sac/fullchain.pem && echo fullchain OK || echo fullchain FAIL
sudo -u www-data test -r /etc/ssl/sac/privkey.pem && echo key OK || echo key FAIL
```
Если `test -r privkey.pem` молчит: каталог был `root:root` при `750`у `www-data` нет права `x` на `/etc/ssl/sac/`; либо `www-data` не в `ssl-cert`. См. [ssl-certificate.md §3](ssl-certificate.md#3-каталог-и-права-на-сервере).
Если PFX с другого Linux — `scp` на SAC: [ssl-certificate.md §4.3](ssl-certificate.md#43-забрать-файл-с-другого-linux-сервера-scp).
Распаковка PFX на Ubuntu 24.04: **`openssl pkcs12 -legacy`** — [§4.4](ssl-certificate.md#44-распаковка-pfx--p12-на-сервере-ubuntu-2404).
### 9.2. Конфиг nginx (HTTPS)
```bash
cd /opt/security-alert-center
sudo git pull
sudo cp deploy/nginx/sac.conf.tls.example /etc/nginx/sites-available/sac
sudo nano /etc/nginx/sites-available/sac
# Проверьте пути ssl_certificate и ssl_certificate_key → /etc/ssl/sac/...
sudo ln -sf /etc/nginx/sites-available/sac /etc/nginx/sites-enabled/sac
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginx
```
Шаблон `sac.conf.tls.example` содержит **default_server** с `return 444` (:80) и `ssl_reject_handshake` (:443) для любого Host, кроме `sac.kalinamall.ru`. Это защищает от ситуации, когда на IP SAC (например `192.168.160.145`) по ошибке DNS попадает `ext.kalinamall.ru` и открывается форма входа SAC.
Проверка после reload:
```bash
curl -sS -o /dev/null -w '%{http_code}\n' --resolve sac.kalinamall.ru:443:127.0.0.1 https://sac.kalinamall.ru/health
# ожидается 200
curl -sS -o /dev/null -w '%{http_code}\n' --resolve ext.kalinamall.ru:443:127.0.0.1 https://ext.kalinamall.ru/ 2>/dev/null || echo "connection closed (444/reject OK)"
```
Проверка:
```bash
curl -sS https://sac.kalinamall.ru/health | jq .
```
В `config/sac-api.env` должно быть: `SAC_PUBLIC_URL=https://sac.kalinamall.ru`
### 9.3. Агенты (ssh-monitor, RDP) и доверие к CA
- Агенты ходят на `https://sac.kalinamall.ru/api/v1/events`.
- Если используется **внутренний корневой CA**, а не публичный VeriSign/DigiCert в системном store — на Linux/Windows может понадобиться установка корневого сертификата в доверенные (или корпоративный прокси с подменой TLS).
- Для публичного коммерческого wildcard от известного УЦ обычно **дополнительных действий на агентах не нужно**.
### 9.4. Альтернатива: только HTTP (отладка)
Шаблон `deploy/nginx/sac.conf.example` — порт 80 без TLS. Не для production.
### 9.5. Альтернатива: Let's Encrypt
Если понадобится публичный бесплатный сертификат только на этот хост:
```bash
sudo cp deploy/nginx/sac.conf.example /etc/nginx/sites-available/sac
# ... nginx -t, reload, затем:
sudo certbot --nginx -d sac.kalinamall.ru
```
---
## 10. Сборка веб-UI (фаза 1C)
```bash
sudo apt install -y nodejs npm
cd /opt/security-alert-center
sudo -u sac git pull
cd frontend
sudo -u sac npm ci
sudo -u sac npm run build
ls -la dist/index.html
sudo systemctl restart sac-api
```
В `config/sac-api.env` задайте `SAC_ADMIN_PASSWORD`, затем откройте `https://sac.kalinamall.ru/` (логин `SAC_ADMIN_USERNAME`, по умолчанию `admin`).
После обновления с multi-user (миграция `009_sac_users`): выполните `alembic upgrade head` в каталоге `backend/`. При первом старте API создаст пользователя **admin** из `SAC_ADMIN_*`, если таблица `sac_users` пуста.
Добавить сотрудника с ролью **monitor** (просмотр без «Настройки»):
```bash
cd /opt/security-alert-center/backend
sudo -u sac python scripts/sac_manage_user.py create ivanov 'StrongPass123' --role monitor
```
Роли: **admin** — полный доступ; **monitor** — события, хосты, проблемы, отчёты (без `/settings` и `/users`).
---
## 11. Проверка
Локально на сервере:
```bash
curl -sS http://127.0.0.1:8000/health | jq .
```
Через nginx (после TLS):
```bash
curl -sS https://sac.kalinamall.ru/health | jq .
```
Тест ingest (подставьте `SAC_BOOTSTRAP_API_KEY`):
```bash
API_KEY="ваш_ключ_из_sac-api.env"
curl -sS -X POST http://127.0.0.1:8000/api/v1/events \
-H "Authorization: Bearer ${API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"schema_version": "1.0",
"event_id": "00000000-0000-4000-8000-000000000002",
"occurred_at": "2026-05-26T12:00:00+03:00",
"source": {"product": "ssh-monitor", "product_version": "test"},
"host": {"hostname": "test-host", "os_family": "linux"},
"category": "agent",
"type": "agent.test",
"severity": "info",
"title": "Test",
"summary": "Native install check"
}'
```
Ожидается HTTP **202** и `"status": "accepted"`.
---
## 12. Резервное копирование
```bash
sudo tee /etc/cron.d/sac-backup <<'EOF'
0 3 * * * postgres pg_dump -Fc -U sac sac > /var/backups/sac/sac_$(date +\%Y\%m\%d).dump
EOF
```
Для `pg_dump` без пароля в cron настройте `~postgres/.pgpass` (см. документацию PostgreSQL).
---
## 13. Обновление SAC (деплой)
### Рекомендуется: скрипт `/opt/sac-deploy.sh`
После первой установки один раз:
```bash
sudo cp /opt/security-alert-center/deploy/sac-deploy.sh /opt/sac-deploy.sh
sudo chmod 755 /opt/sac-deploy.sh
```
Каждое обновление с git:
```bash
sudo /opt/sac-deploy.sh
```
Скрипт: `chown sac:sac``git pull` (от **sac**) → `pip``alembic``npm run build``systemctl restart sac-api` → проверка `/health`.
### Вручную (если без скрипта)
```bash
sudo chown -R sac:sac /opt/security-alert-center
sudo -u sac git -C /opt/security-alert-center pull
cd /opt/security-alert-center/frontend && sudo -u sac npm ci && sudo -u sac npm run build
sudo -u sac /opt/security-alert-center/backend/.venv/bin/pip install -r /opt/security-alert-center/backend/requirements.txt
sudo -u sac bash -c 'set -a; source /opt/security-alert-center/config/sac-api.env; set +a; cd /opt/security-alert-center/backend && .venv/bin/alembic upgrade head'
sudo systemctl restart sac-api
```
### Git: только от пользователя `sac`
Не выполняйте `sudo git pull` в `/opt/security-alert-center``.git` станет root и появится:
`error: cannot open '.git/FETCH_HEAD': Permission denied`
Исправление: `sudo chown -R sac:sac /opt/security-alert-center`.
---
## 14. Чеклист
- [ ] Ubuntu 24.04, timezone
- [ ] ufw: 22, 80, 443
- [ ] PostgreSQL: БД `sac`, пользователь `sac`
- [ ] venv, зависимости установлены
- [ ] `/opt/security-alert-center/config/sac-api.env`, владелец `sac:sac`, chmod 600
- [ ] `alembic upgrade head` без ошибок
- [ ] `sac-api` active (systemd)
- [ ] nginx + TLS
- [ ] `/health` ok, ingest 202
- [ ] `frontend/dist` собран, UI открывается, вход admin OK
- [ ] cron backup
---
## 15. Устранение неполадок
| Симптом | Действие |
|---------|----------|
| `sac-api.env: Permission denied` | Конфиг в `/opt/security-alert-center/config/`, `chown sac:sac`, `chmod 600`. Не используйте `/etc/...` с каталогом `root:root` |
| `password authentication failed` в **sac-api**, но alembic OK | Старый unit с `EnvironmentFile=``git pull`, обновить `sac-api.service`, `daemon-reload`, `restart`. Либо пароль в `DATABASE_URL` ≠ PostgreSQL |
| `password authentication failed` везде | Пароль в `DATABASE_URL` в кавычках; `ALTER USER sac WITH PASSWORD`; тот же пароль в URL |
| `database: error` / SQLAlchemy connect | `systemctl status postgresql`; проверить `DATABASE_URL` после успешного `source` |
| 401 ingest | ключ в `Authorization: Bearer`; проверить `SAC_BOOTSTRAP_API_KEY` |
| 502 от nginx | API не слушает: `ss -lntp \| grep 8000`, `systemctl restart sac-api` |
| Schema errors 422 | путь `EVENT_SCHEMA_PATH`, наличие файла schema |
| `FETCH_HEAD: Permission denied` | `sudo chown -R sac:sac /opt/security-alert-center`; далее `sudo -u sac git pull` |
| UI 404 / пустая главная | `cd frontend && sudo -u sac npm run build`; `restart sac-api` |
| 503 на `/auth/login` | задать `SAC_ADMIN_PASSWORD` в `config/sac-api.env` |
| `test -r privkey.pem` без вывода | каталог `/etc/ssl/sac``root:ssl-cert`, `www-data` в группе `ssl-cert` — [ssl-certificate.md](ssl-certificate.md) §3 |
| PFX `RC2-40-CBC unsupported` | `openssl pkcs12 -legacy` — [ssl-certificate.md](ssl-certificate.md) §4.4 |
---
## См. также
- [install-ubuntu-24.04.md](install-ubuntu-24.04.md) — указатель
- [install-ubuntu-24.04-docker.md](install-ubuntu-24.04-docker.md) — альтернатива (не prod)
- [deployment.md](deployment.md)
+24
View File
@@ -0,0 +1,24 @@
# Подготовка сервера Ubuntu 24.04 для SAC
В эксплуатации KalinaMall используется **только native** (без Docker): PostgreSQL, Python venv, systemd, nginx.
## Руководства
| Путь | Когда использовать |
|------|-------------------|
| **[install-ubuntu-24.04-native.md](install-ubuntu-24.04-native.md)** | **Основной** — подготовка и запуск SAC |
| [install-ubuntu-24.04-docker.md](install-ubuntu-24.04-docker.md) | Альтернатива (Docker Compose), не для prod |
## Быстрый старт (native)
```bash
# см. полный чеклист в native-руководстве
sudo apt update && sudo apt install -y git postgresql nginx python3.12 python3.12-venv
sudo git clone https://git.papatramp.ru/PapaTramp/security-alert-center.git /opt/security-alert-center
# … PostgreSQL, venv, config/sac-api.env, systemd, nginx
```
## См. также
- [deployment.md](deployment.md)
- [../deploy/README.md](../deploy/README.md)
+66
View File
@@ -0,0 +1,66 @@
# SAC production — текущий статус (KalinaMall)
Ориентир для продолжения работ. Обновляйте при смене этапа.
**Сервер:** Ubuntu 24.04, FQDN `sac.kalinamall.ru`
**Каталог:** `/opt/security-alert-center`
**Пользователь сервиса:** `sac` (git, venv, npm — только от него)
**Конфиг:** `/opt/security-alert-center/config/sac-api.env`
---
## Что уже работает (2026-05-26)
| Компонент | Статус |
|-----------|--------|
| PostgreSQL + Alembic | ✅ |
| `POST /api/v1/events` (ingest 202) | ✅ |
| `GET /health` | ✅ |
| nginx + wildcard TLS (`/etc/ssl/sac/`) | ✅ |
| JWT UI: Events, Hosts, **Problems** | ✅ (Problems после деплоя) |
| `SAC_ADMIN_PASSWORD` + вход в браузере | ✅ |
---
## Деплой обновлений
```bash
sudo /opt/sac-deploy.sh
```
См. `deploy/sac-deploy.sh`, [install-ubuntu-24.04-native.md §13](install-ubuntu-24.04-native.md#13-обновление-sac-деплой).
**Не делать:** `sudo git pull` в каталоге приложения (ломает права `.git` для `sac`).
---
## Тестовый агент (ubabuba, 10.10.36.9)
| Параметр | Значение |
|----------|----------|
| Хост | `ubabuba` / `10.10.36.9` |
| Репозиторий | `git.papatramp.ru/PapaTramp/ssh-monitor` (`main`, есть `sac-client.sh`) |
| `UseSAC` | пилот **exclusive** — см. [pilot-2.1-exclusive.md](pilot-2.1-exclusive.md) |
| Сервис | `ssh-monitor.service`**active** |
| `--check-sac` | OK (health + ingest `agent.test` 202) |
Переключить на `dual` после настройки Telegram в `/etc/ssh-monitor.conf`.
---
## Следующие шаги (2026-05-29)
1. **Деплой SAC v0.5.0**`sudo /opt/sac-deploy.sh` (миграции **`004``008`**: каналы, правило, cooldown)
2. **notif-02** — прогон [testing-e2e-checklist.md](testing-e2e-checklist.md) § «SAC Telegram» на prod
3. **Timer**`sac-daily-report.timer` + `SAC_DAILY_REPORT_*` (миграций нет; см. runbook § F-NOT-05)
4. **Настройки UI**`/settings`: Telegram `warning+`, кнопка «Проверить Telegram»
5. **RDP 1.2.20-SAC** — NETLOGON + `Deploy-LoginMonitor.ps1` на пилотных хостах
6. Пилот **UseSAC=exclusive** на Windows — ~16.06 (см. [todo-2026-05-29.md](todo-2026-05-29.md))
---
## Ссылки
- [agent-integration.md](agent-integration.md)
- [ssl-certificate.md](ssl-certificate.md)
- [work-plan.md](work-plan.md)
+92
View File
@@ -0,0 +1,92 @@
# Пилот 2.1 — UseSAC=exclusive
Цель: один Linux-хост и (позже) один Windows-хост шлют **только в SAC**, без дублирования в Telegram с агента.
---
## Linux (ssh-monitor) — ubabuba / новые хосты
### 1. Обновить агент
```bash
sudo /opt/scripts/update_ssh_monitor.sh
# или первично: sudo ./first_deploy.sh
```
### 2. Конфиг `/etc/ssh-monitor.conf`
```ini
UseSAC=exclusive
SAC_URL=https://sac.kalinamall.ru
SAC_API_KEY=sac_xxxxxxxx
SAC_TLS_SKIP_VERIFY=0
SAC_SPOOL_DIR=/var/lib/ssh-monitor/sac-spool
SAC_TIMEOUT_SEC=12
```
Telegram/email **можно оставить** в конфиге — в `exclusive` они **не вызываются** для событий (`notify_or_sac` → только SAC).
### 3. Проверки на агенте
```bash
sudo /usr/local/bin/ssh-monitor --check-sac
sudo bash /opt/scripts/update/ssh-monitor/scripts/pilot-verify-exclusive.sh
# или из клона:
sudo bash /opt/scripts/update/ssh-monitor/scripts/pilot-verify-exclusive.sh
sudo systemctl restart ssh-monitor.service
sudo systemctl status ssh-monitor.service --no-pager
```
### 4. Проверки в SAC
1. UI → **Хосты** — появился/обновился хост пилота.
2. Сгенерировать: успешный SSH, `sudo` (успешный), при необходимости неудачный SSH.
3. UI → **События** — типы `ssh.login.success`, `privilege.sudo.command`, …
4. **Telegram бота агента** — новых сообщений от этого хоста **нет** (только SAC, если включён Telegram **сервера SAC** — это отдельно).
5. При `high`/`critical`**Проблемы** и опционально Telegram из `TELEGRAM_*` в `sac-api.env`.
### 5. Spool (сбой SAC)
При недоступности SAC события пишутся в `SAC_SPOOL_DIR`. После восстановления SAC цикл монитора вызывает `sac_flush_spool` (до 20 файлов за итерацию).
```bash
ls -la /var/lib/ssh-monitor/sac-spool/
```
### 6. Откат на dual
```ini
UseSAC=dual
```
```bash
sudo systemctl restart ssh-monitor.service
```
---
## Windows (RDP-login-monitor)
**Статус:** интеграция UseSAC в RDP-monitor **ещё не реализована** — пилот 2.1 сейчас только **Linux**.
После появления `UseSAC` в RDP — повторить сценарий с `Test-SacConnection` и событиями `rdp.login.*`.
---
## Критерии приёмки 2.1 (Linux)
| # | Критерий |
|---|----------|
| 1 | `UseSAC=exclusive`, сервис `active` |
| 2 | `--check-sac` → ingest OK |
| 3 | События видны в SAC UI |
| 4 | Telegram **агента** молчит |
| 5 | Spool очищается после восстановления SAC |
---
## См. также
- [agent-integration.md](agent-integration.md)
- [operations-prod-status.md](operations-prod-status.md)
+93
View File
@@ -0,0 +1,93 @@
# Фаза 2.2 — Heartbeat и daily report через SAC
Агент **уже** шлёт в SAC (в т.ч. в `exclusive`):
| Тип | Интервал (ssh-monitor) |
|-----|-------------------------|
| `agent.heartbeat` | каждые **12 ч** |
| `report.daily.ssh` | раз в сутки после `DAILY_REPORT_HOUR` |
SAC отображает их в **События** и на **Dashboard / Хосты**.
---
## UI SAC
- **Dashboard:** счётчики heartbeat/отчётов за 24 ч, хосты с устаревшим heartbeat.
- **Хосты:** колонки *Агент* (`online` / `stale` / `unknown`), время последнего heartbeat и отчёта (ссылка на **Отчёты** по hostname).
- **Отчёты** (`/reports`): список `report.daily.*` по узлам; клик по строке — карточка с метриками и полным текстом (HTML/`report_body` в `details`).
- Старые события без `details.report_body` показывают подсказку обновить агент.
---
## Настройка порога «живости» (сервер SAC)
В `config/sac-api.env`:
```bash
# По умолчанию 780 мин (13 ч) — чуть больше интервала heartbeat агента (12 ч)
SAC_HEARTBEAT_STALE_MINUTES=780
```
После изменения: `sudo systemctl restart sac-api`.
---
## Проверка на пилотном хосте (exclusive)
1. Дождаться heartbeat или ускорить тест: на агенте временно уменьшить интервал нельзя без правки кода — проще подождать или вручную:
```bash
sudo /usr/local/bin/ssh-monitor --dry-run
```
(не шлёт реально; для реального теста — дождаться цикла 12 ч или смотреть уже пришедшие события).
2. В SAC UI → **События**, фильтр `type=agent.heartbeat`.
3. **Хосты** → статус `online` после свежего heartbeat.
4. Ежедневный отчёт: после `DAILY_REPORT_HOUR` — событие `report.daily.ssh` (от агента) **или** от SAC при `SAC_DAILY_REPORT_ENABLED=true` и отсутствии отчёта агента за сутки.
---
## SAC: отчёт без агента (exclusive / пропуск агента)
В `config/sac-api.env`:
```bash
SAC_DAILY_REPORT_ENABLED=true
SAC_DAILY_REPORT_HOUR=9
SAC_DAILY_REPORT_TIMEZONE=Europe/Moscow
SAC_DAILY_REPORT_SKIP_IF_AGENT_SENT=true
SAC_DAILY_REPORT_REQUIRE_ACTIVITY=true
```
- Job: `cd backend && .venv/bin/python -m app.jobs.daily_report` (ручной прогон: `--force`).
- Timer: `deploy/systemd/sac-daily-report.service` + `.timer` — **09:00** в `SAC_DAILY_REPORT_TIMEZONE` (генерируется из `SAC_DAILY_REPORT_HOUR`, без `RandomizedDelaySec`).
- Оповещение: `notify_daily_report` — **не** режется `NOTIFY_MIN_SEVERITY=warning` (severity отчёта `info`).
- UI **Отчёты** и Telegram используют `details.report_html` / `report_body` как у агента.
Установка timer (один раз на сервере):
```bash
sudo cp /opt/security-alert-center/deploy/systemd/sac-daily-report.service /etc/systemd/system/
sudo bash /opt/security-alert-center/deploy/systemd/render-sac-daily-report-timer.sh \
/opt/security-alert-center/config/sac-api.env \
/etc/systemd/system/sac-daily-report.timer
sudo systemctl daemon-reload
sudo systemctl enable --now sac-daily-report.timer
```
---
## Деплой
```bash
sudo /opt/sac-deploy.sh
```
---
## См. также
- [pilot-2.1-exclusive.md](pilot-2.1-exclusive.md)
- [agent-integration.md](agent-integration.md)
+132
View File
@@ -0,0 +1,132 @@
# Дорожная карта Security Alert Center
Продуктовые фазы после MVP. Сроки ориентировочные, уточняются после фазы 1.
---
## v0.1 — MVP (ядро SAC)
**Цель:** ingest на сервере; агенты в `dual` по мере готовности фазы 1A.
- Ingest API + PostgreSQL (**prod**)
- Events, Hosts, базовый UI (**prod**)
- Problems (2 правила)
- Telegram из SAC
- 3 виджета dashboard
- Ubuntu 24.04 deploy
---
## v0.2 — Агенты + UseSAC
**Цель:** production-пилот с `UseSAC=exclusive` на части хостов.
- ssh-monitor: UseSAC, spool, все типы событий
- RDP-login-monitor: аналог
- Режимы `dual`, `fallback`
- Daily reports из SAC
- Документация миграции с Telegram-only
---
## v0.3 — Операционная зрелость
- `POST /events/batch`
- Email SMTP из SAC
- UI: редактор правил уведомлений
- Maintenance windows (тишина)
- Retention job + агрегаты по часам
- Экспорт CSV
- Redis + выделенный worker
---
## v0.4 — Аналитика и корреляция
- GeoIP / ASN по source IP
- «Новый IP для пользователя»
- Корреляция: один IP → SSH + RDP на разных хостах → один Problem
- Расширенные дашборды (RD Gateway vs RDP, sudo heatmap)
- Тёмная тема UI
---
## v0.5 — Enterprise
- LDAP / OIDC
- Multi-tenant (несколько организаций)
- RBAC расширенный, audit export
- Prometheus metrics endpoint
- Webhook CEF/JSON для SIEM
- HA: документированный cold standby (второй сервер, restore БД)
---
## v0.6 — Mobile (Seaca)
**Цель:** нативный Android-клиент и push по тем же правилам оповещений, что Telegram/email/webhook.
Репозиторий клиента: [seaca](https://git.papatramp.ru/PapaTramp/seaca) (Kotlin, Jetpack Compose, FCM).
**Сервер SAC:**
- Настройки → **Мобильные устройства**: вкл/выкл подключений, коды регистрации, список устройств, отзыв, способ логина
- API: enrollment codes, `mobile_devices`, refresh JWT, регистрация FCM-токена
- Worker: канал `push_mobile` в notification policy (severity, cooldown, dedup — как у остальных каналов)
- FCM HTTP v1 (service account на сервере; FCM — бесплатный транспорт доставки)
**Не в мобильном клиенте:** пользователи SAC, каналы Telegram/SMTP/webhook, админ-настройки.
Подробный план клиента — [seaca/docs/ROADMAP.md](https://git.papatramp.ru/PapaTramp/seaca/src/branch/main/docs/ROADMAP.md).
---
## v0.7 — Версии агентов и удалённое обновление (ToDo)
**Цель:** SAC контролирует актуальность `ssh-monitor` и `RDP-login-monitor` на хостах и может **инициировать обновление** без обхода всех машин вручную.
- Сверка `product_version` с политикой SAC (min / recommended)
- UI: статус версии на карточке хоста, список отстающих
- Problems / оповещения при устаревании агента
- Команда обновления (предпочтительно **pull** с хоста: агент видит «нужно обновиться» и запускает свой updater)
- События `agent.update.*` в ingest для аудита
**Проработка:** [agent-control-plane.md](agent-control-plane.md) (основной документ), [agent-update-backlog.md](agent-update-backlog.md).
---
## Добавить
См. [agent-control-plane.md](agent-control-plane.md):
- RDG flap 302→303 (110 с): Problem, оповещение, кнопки **qwinsta** / **logoff** на «Обзоре»
- Обновления агентов: режим GPO или SAC (pull + fallback SSH/WinRM), Windows + Linux
- Настройки агента в карточке хоста (desired config → poll)
---
## Зависимости от внешних репо
| Версия SAC | Минимальная версия ssh-monitor | Минимальная версия RDP-monitor |
|------------|-------------------------------|--------------------------------|
| v0.1 | — (не требуется) | — |
| v0.2 | TBD (тег с UseSAC) | TBD |
| v0.3+ | совместимость schema 1.0 | совместимость schema 1.0 |
| v0.6 | — | — (мобильный клиент [seaca](https://git.papatramp.ru/PapaTramp/seaca), не агент) |
При breaking change схемы — `schema_version: 1.1` с поддержкой 1.0 на ingest.
---
## Метрики успеха (KPI)
- Среднее время от события до Telegram &lt; 30 с (p95)
- 0 потерянных событий при 1 ч недоступности SAC (spool)
- Снижение сообщений в Telegram на 40%+ за счёт dedup (через 1 мес. exclusive)
---
## См. также
- [work-plan.md](work-plan.md)
- [TZ.md](TZ.md)
+130
View File
@@ -0,0 +1,130 @@
# Runbook: эксплуатация SAC
Краткие процедуры для prod (`sac.kalinamall.ru`, native install в `/opt/security-alert-center`).
## Деплой приложения
```bash
sudo /opt/sac-deploy.sh
```
Скрипт: `git pull` / `reset --hard`, `pip install`, `alembic upgrade head`, `npm run build`, `systemctl restart sac-api`.
Проверка:
```bash
curl -sS https://sac.kalinamall.ru/health | jq .
```
Ожидается `status: ok`, `database: ok`. При устаревших heartbeat агентов — `status: degraded`, поле `hosts_stale` > 0.
### `sac-api.env`: формат строк
Файл читается **pydantic** (`SAC_CONFIG_FILE`), не как произвольный bash-скрипт.
- Одна переменная — одна строка: `SAC_SSH_AUTO_ADD_HOST_KEY=false`
- Комментарии — **отдельной** строкой с `#` в начале
- **Нельзя** inline после значения: `false ← так и оставляем` — deploy упадёт на `alembic` с `bool_parsing`
Проверка без деплоя:
```bash
sudo -u sac bash -c 'export SAC_CONFIG_FILE=/opt/security-alert-center/config/sac-api.env; cd /opt/security-alert-center/backend && .venv/bin/python -c "from app.config import get_settings; get_settings(); print(\"OK\")"'
```
### Linux SSH update: `known_hosts`
Перед «Обновить ssh-monitor (SSH)» ключ хоста должен быть в файле (по умолчанию `config/ssh_known_hosts`):
```bash
ssh-keyscan -H 10.10.7.2 | sudo tee -a /opt/security-alert-center/config/ssh_known_hosts
sudo chown sac:sac /opt/security-alert-center/config/ssh_known_hosts
sudo chmod 600 /opt/security-alert-center/config/ssh_known_hosts
```
Иначе SAC: `Server '…' not found in known_hosts`. `SAC_SSH_AUTO_ADD_HOST_KEY=true` для prod не рекомендуется.
## Мобильные устройства (Seaca)
См. [seaca-mobile.md](seaca-mobile.md) и [seaca-fcm.md](seaca-fcm.md).
После деплоя 0.9.0+: в веб-SAC включить мобильные устройства, выдать код оператору. Push — после настройки FCM и сборки APK с `google-services.json`.
## Резервное копирование PostgreSQL
```bash
sudo -u postgres pg_dump -Fc sac > /var/backups/sac-$(date +%Y%m%d).dump
```
Восстановление (на тестовый инстанс):
```bash
sudo -u postgres pg_restore -d sac_test --clean /var/backups/sac-YYYYMMDD.dump
```
Перед restore на prod — остановить API: `sudo systemctl stop sac-api`.
## Retention (очистка БД)
Политика по умолчанию (`config/sac-api.env`):
| Данные | Срок |
|--------|------|
| `events` | 90 дней (`SAC_EVENTS_RETENTION_DAYS`) |
| `problems` со статусом `resolved` | 180 дней (`SAC_PROBLEMS_RETENTION_DAYS`) |
Ручной прогон:
```bash
sudo -u sac bash -c '
set -a; source /opt/security-alert-center/config/sac-api.env; set +a
cd /opt/security-alert-center/backend
.venv/bin/python -m app.jobs.retention
'
```
Установка ежедневного timer (один раз):
```bash
sudo cp /opt/security-alert-center/deploy/systemd/sac-retention.service /etc/systemd/system/
sudo cp /opt/security-alert-center/deploy/systemd/sac-retention.timer /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now sac-retention.timer
systemctl list-timers sac-retention.timer
```
## Суточные отчёты (F-NOT-05)
```bash
sudo cp /opt/security-alert-center/deploy/systemd/sac-daily-report.service /etc/systemd/system/
sudo bash /opt/security-alert-center/deploy/systemd/render-sac-daily-report-timer.sh \
/opt/security-alert-center/config/sac-api.env \
/etc/systemd/system/sac-daily-report.timer
sudo systemctl daemon-reload
sudo systemctl enable --now sac-daily-report.timer
systemctl list-timers sac-daily-report.timer
# Ручной прогон (игнор часа)
sudo -u sac bash -lc 'cd /opt/security-alert-center/backend && .venv/bin/python -m app.jobs.daily_report --force'
```
Переменные: `SAC_DAILY_REPORT_*` в `config/sac-api.env` (см. `deploy/env.native.example`).
## Health и мониторинг
| Endpoint | Назначение |
|----------|------------|
| `GET /health` | БД, версия, `hosts_stale`, `last_event_received_at` |
| `GET /api/v1/stream/events` | SSE для UI (счётчики + `last_event_id` каждые 5 с) |
Агенты: `GET /health` без JWT. UI Dashboard: блок «Последние события» подгружается при смене `last_event_id` в SSE.
## Типовые проблемы
**События в Telegram есть, в UI нет** — проверить ingest (лог агента `SAC: accepted`), режим `UseSAC`, обновить Dashboard (live ~5 с).
**422 на ingest** — см. `Logs/sac-last-post.json` на Windows-агенте; обновить `Sac-Client.ps1` (RDP) или `sac-client.sh` (Linux).
**Устаревший heartbeat** — увеличить `SAC_HEARTBEAT_STALE_MINUTES` или проверить `agent.heartbeat` на хосте.
**Чужой Host на IP SAC (форма SAC на ext.kalinamall.ru)** — в nginx только `server_name sac.kalinamall.ru`; default_server → 444 / `ssl_reject_handshake`. Обновить конфиг из `deploy/nginx/sac.conf.tls.example`, `sudo nginx -t && sudo systemctl reload nginx`. HAProxy на этом не решается — правка на **192.168.160.145** (nginx SAC).
Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

+49
View File
@@ -0,0 +1,49 @@
# FCM (push) для Seaca
Настраивается **только на сервере SAC** и в **Firebase**; в веб-UI — индикатор и тест push на устройство.
## 1. Firebase
1. [Firebase Console](https://console.firebase.google.com/) → создать проект (или использовать существующий).
2. Добавить Android-приложение с package name **`ru.kalinamall.seaca`** (как в APK Seaca).
3. Скачать `google-services.json` — файл для **сборки Seaca**, в git не коммитить.
4. **Project settings → Service accounts → Generate new private key** — JSON для **сервера SAC**.
## 2. Сервер SAC
```bash
sudo install -o sac -g sac -m 600 /path/to/firebase-key.json \
/etc/security-alert-center/fcm-service-account.json
```
В `/opt/security-alert-center/config/sac-api.env`:
```ini
SAC_FCM_ENABLED=true
SAC_FCM_PROJECT_ID=your-firebase-project-id
SAC_FCM_SERVICE_ACCOUNT_JSON=/etc/security-alert-center/fcm-service-account.json
```
Перезапуск API:
```bash
sudo systemctl restart sac-api
```
## 3. Политика оповещений
**Настройки → Правило оповещений** → канал **Seaca (push)**.
Действуют те же `min_severity`, cooldown и dedup, что для Telegram/email.
## 4. Проверка
1. Телефон привязан (enroll), в списке устройств **FCM: да**.
2. **Настройки → Мобильные устройства → Тест push** на строке устройства.
3. Либо сгенерировать событие с severity выше порога policy.
## 5. Ограничения
- FCM доставляет через инфраструктуру Google; payload формирует SAC (заголовок, id, deep link).
- Без `SAC_FCM_ENABLED=true` push не отправляется; приложение и REST API работают.
- `google-services.json` нужен приложению; service account JSON — только серверу.
+113
View File
@@ -0,0 +1,113 @@
# Seaca — мобильный клиент SAC
Репозиторий приложения: [seaca](https://git.papatramp.ru/PapaTramp/seaca).
Требуется SAC **≥ 0.9.0** с применённой миграцией `014`.
---
## Роли
| Где | Кто |
|-----|-----|
| Веб-SAC | Админ: мобильные устройства, коды, отзыв, FCM на сервере |
| Seaca (Android) | Оператор: обзор, события, проблемы, хосты, отчёты; ack/resolve |
| Веб-SAC | Пользователи SAC, Telegram/SMTP — **не** в приложении |
---
## Включение мобильных устройств (админ)
1. **Настройки → Мобильные устройства (Seaca)**
2. Включить **«Разрешать мобильным устройствам подключаться»** → **Сохранить мобильные**
3. В **Правило оповещений** при необходимости включить канал **Seaca (push)** (для push после настройки FCM)
### «Сохранить мобильные»
Сохраняет в БД:
- разрешение подключения новых устройств;
- максимум активных устройств на одного пользователя SAC;
- минимальную версию приложения (опционально).
Не настраивает FCM и не выдаёт коды.
### Коды регистрации
| Поле | Смысл |
|------|--------|
| Метка | Произвольная подпись для админа |
| Пользователь | Если выбран — enroll только для этого пользователя |
| Способ входа | **Логин и пароль + код** — в приложении нужны учётные данные SAC; **Только код** — пароль не нужен, пользователь должен быть выбран в форме |
| Срок (часов) | Код перестаёт принимать **новые** привязки; уже подключённые телефоны не отключаются |
Код показывается **один раз** после «Выдать код». Передайте оператору: URL сервера (**`https://sac-api.kalinamall.ru`** для Seaca; веб-админка — `https://sac.kalinamall.ru`), код `sacmob_…`, при режиме password — логин/пароль SAC.
### Подключённые устройства
- **Отключить** — отзыв устройства и refresh-токенов; приложение получит 401 при следующем запросе
- **Тест push** — только если FCM настроен на сервере и у устройства есть FCM-токен
---
## Сессия после привязки
1. Оператор один раз выполняет enroll (`POST /api/v1/mobile/enroll`).
2. Приложение хранит **access_token** (JWT, ~24 ч) и **refresh_token** (по умолчанию **90 дней**, `SAC_MOBILE_REFRESH_EXPIRE_DAYS` в `sac-api.env`).
3. Код регистрации после успешного enroll больше не нужен.
Для долгой работы без повторной привязки увеличьте `SAC_MOBILE_REFRESH_EXPIRE_DAYS` (например `3650`). Отзыв устройства в вебе обнуляет доступ немедленно.
---
## Push (FCM) на сервере
Подробно: [seaca-fcm.md](seaca-fcm.md).
Кратко в `config/sac-api.env`:
```ini
SAC_FCM_ENABLED=true
SAC_FCM_PROJECT_ID=ваш-firebase-project-id
SAC_FCM_SERVICE_ACCOUNT_JSON=/etc/security-alert-center/fcm-service-account.json
```
После изменений: `sudo systemctl restart sac-api` или `sudo /opt/sac-deploy.sh`.
В UI индикатор **FCM: настроен / не настроен** читает эти переменные; править их через веб нельзя.
---
## Perimeter (HAProxy)
| Hostname | Clients | IP allowlist |
|----------|---------|--------------|
| `sac.kalinamall.ru` | Web UI | yes (`git-sac-allowed`) |
| `sac-api.kalinamall.ru` | Seaca, `/api/v1/mobile/*` | no (enroll + JWT) |
Both resolve to the same SAC server (**192.168.160.145**). nginx must accept both names in `server_name`. See [reverse-proxy/docs/sac-access.md](https://git.papatramp.ru/PapaTramp/reverse-proxy/src/branch/main/docs/sac-access.md).
---
## API (для разработчиков Seaca)
| Метод | Путь |
|-------|------|
| GET | `/health` |
| POST | `/api/v1/mobile/enroll` |
| POST | `/api/v1/mobile/auth/refresh` |
| PUT | `/api/v1/mobile/devices/me/fcm` |
| GET | `/api/v1/dashboards/summary` |
| GET | `/api/v1/events`, `/api/v1/events/{id}` |
| GET | `/api/v1/problems`, `/api/v1/problems/{id}` |
| POST | `/api/v1/problems/{id}/ack`, `/resolve` |
| GET | `/api/v1/hosts`, `/api/v1/hosts/{id}` |
JWT с мобильного устройства содержит claim `device_id`.
---
## См. также
- [seaca-fcm.md](seaca-fcm.md) — Firebase и push
- [deployment.md](deployment.md) — деплой SAC
- [runbook-ops.md](runbook-ops.md) — эксплуатация
+313
View File
@@ -0,0 +1,313 @@
# TLS-сертификат для SAC (wildcard VeriSign / DigiCert)
Куда положить файлы, в каком формате, как скопировать на сервер `sac.kalinamall.ru`.
Связано: [install-ubuntu-24.04-native.md](install-ubuntu-24.04-native.md) §9, шаблон `deploy/nginx/sac.conf.tls.example`.
---
## 1. Что должно получиться на сервере
nginx читает **два PEM-файла** (текст, начинается с `-----BEGIN ...-----`):
| Путь на сервере | Содержимое | Кто читает |
|-----------------|------------|------------|
| `/etc/ssl/sac/fullchain.pem` | Сертификат сайта/wildcard **+** промежуточные CA (без корневого — обычно не нужен) | nginx |
| `/etc/ssl/sac/privkey.pem` | Закрытый ключ (RSA или EC) | nginx |
Имена файлов можно другие, если пути совпадают с `ssl_certificate` / `ssl_certificate_key` в `/etc/nginx/sites-available/sac`.
**Покрытие имени:** в сертификате должен быть CN или SAN вида `*.kalinamall.ru` (wildcard) или `sac.kalinamall.ru`.
---
## 2. Что вам могут выдать (входные форматы)
| От УЦ / админа | Расширения | Что делать |
|----------------|------------|------------|
| Готовый PEM | `.pem`, `.crt` + `.key` | Собрать fullchain, скопировать на сервер |
| PKCS#12 | `.pfx`, `.p12` | Распаковать в PEM (см. §4.3, на Ubuntu 24.04 нужен `-legacy`) |
| Отдельные файлы | `certificate.crt`, `ca-bundle.crt`, `private.key` | Склеить fullchain (см. §5) |
| Windows (MMC) | экспорт `.pfx` | Распаковать на ПК или сервере (§4) |
nginx **не** понимает напрямую: `.pfx`, `.p12`, `.der` без конвертации.
---
## 3. Каталог и права на сервере
Выполнить **один раз** на SAC-сервере:
```bash
sudo mkdir -p /etc/ssl/sac
sudo chown root:ssl-cert /etc/ssl/sac
sudo chmod 750 /etc/ssl/sac
sudo groupadd -f ssl-cert
sudo usermod -aG ssl-cert www-data
```
Каталог **`root:ssl-cert` `750`**, не `root:root`: иначе у `www-data` нет права `x` (зайти в каталог), даже если ключ `640` и `root:ssl-cert`.
После копирования файлов:
```bash
sudo chmod 644 /etc/ssl/sac/fullchain.pem
sudo chmod 640 /etc/ssl/sac/privkey.pem
sudo chown root:root /etc/ssl/sac/fullchain.pem
sudo chown root:ssl-cert /etc/ssl/sac/privkey.pem
sudo chown root:ssl-cert /etc/ssl/sac
sudo chmod 750 /etc/ssl/sac
sudo systemctl restart nginx
```
Группа `ssl-cert` нужна, чтобы **nginx** (`www-data`) читал ключ и обходил каталог. На Ubuntu в `ssl-cert` по умолчанию может быть только `postgres` — добавьте `www-data` (`usermod` выше).
Проверка:
```bash
id www-data # в groups должна быть ssl-cert
sudo -u www-data test -x /etc/ssl/sac && echo "dir OK" || echo "dir FAIL"
sudo -u www-data test -r /etc/ssl/sac/fullchain.pem && echo "fullchain OK" || echo "fullchain FAIL"
sudo -u www-data test -r /etc/ssl/sac/privkey.pem && echo "key OK" || echo "key FAIL"
```
---
## 4. Копирование на сервер
Подставьте IP или hostname SAC-сервера вместо `SAC_SERVER`.
Команды `scp` выполняются **на той машине, куда копируете** (обычно на SAC-сервере — «забрать» файл с другого хоста).
### 4.1. С Linux / macOS (scp)
Если уже есть `fullchain.pem` и `privkey.pem` на ПК:
```bash
scp fullchain.pem privkey.pem papatramp@SAC_SERVER:/tmp/
```
На сервере:
```bash
sudo mv /tmp/fullchain.pem /tmp/privkey.pem /etc/ssl/sac/
sudo chmod 644 /etc/ssl/sac/fullchain.pem
sudo chmod 640 /etc/ssl/sac/privkey.pem
sudo chown root:root /etc/ssl/sac/fullchain.pem
sudo chown root:ssl-cert /etc/ssl/sac/privkey.pem
sudo chown root:ssl-cert /etc/ssl/sac
sudo chmod 750 /etc/ssl/sac
sudo rm -f /tmp/fullchain.pem /tmp/privkey.pem
```
### 4.2. С Windows (PowerShell / pscp)
Файлы, например, в `D:\Certs\kalinamall\`:
```powershell
scp D:\Certs\kalinamall\fullchain.pem papatramp@SAC_SERVER:/tmp/
scp D:\Certs\kalinamall\privkey.pem papatramp@SAC_SERVER:/tmp/
```
Дальше на сервере — те же `sudo mv` и `chown`, что в §4.1.
Если есть только **`wildcard.pfx`**:
```powershell
scp D:\Certs\kalinamall\wildcard.pfx papatramp@SAC_SERVER:/tmp/
```
Распаковка на **сервере** (§4.3).
### 4.3. Забрать файл с другого Linux-сервера (scp)
Выполнять **на SAC-сервере** (pull с хоста, где лежит PFX/PEM):
```bash
mkdir -p /tmp/certs
# один файл
scp user@certs-host.example.com:/path/to/wildcard_kalinamall_ru.pfx /tmp/certs/
# несколько файлов
scp user@certs-host:/path/to/fullchain.pem user@certs-host:/path/to/privkey.pem /tmp/certs/
# нестандартный SSH-порт (у scp заглавная P)
scp -P 2222 user@certs-host:/path/to/wildcard_kalinamall_ru.pfx /tmp/certs/
```
С другого сервера **отдать** на SAC (push — команда на сервере-источнике):
```bash
scp /path/to/wildcard_kalinamall_ru.pfx papatramp@<IP_SAC>:/tmp/certs/
```
Нужен SSH-доступ и право **читать** файл на источнике.
### 4.4. Распаковка `.pfx` / `.p12` на сервере (Ubuntu 24.04)
На Ubuntu 24.04 стоит **OpenSSL 3.x**. Старые PFX от VeriSign/DigiCert часто используют **RC2-40-CBC** — без флага `-legacy` будет ошибка:
```text
Algorithm (RC2-40-CBC : 0), Properties (), unsupported
```
**Всегда добавляйте `-legacy`** к `openssl pkcs12`:
```bash
sudo apt install -y openssl
sudo mkdir -p /tmp/certs
cd /tmp/certs
# замените имя PFX; Enter Import Password: — пароль от PFX (не sudo)
sudo openssl pkcs12 -legacy -in wildcard_kalinamall_ru.pfx -nocerts -nodes \
-out privkey.pem
sudo openssl pkcs12 -legacy -in wildcard_kalinamall_ru.pfx -clcerts -nokeys \
-out server.crt
sudo openssl pkcs12 -legacy -in wildcard_kalinamall_ru.pfx -cacerts -nokeys \
-out ca-chain.crt
# fullchain = сервер + промежуточные (порядок важен)
cat server.crt ca-chain.crt | sudo tee /etc/ssl/sac/fullchain.pem > /dev/null
sudo cp privkey.pem /etc/ssl/sac/privkey.pem
sudo chmod 644 /etc/ssl/sac/fullchain.pem
sudo chmod 640 /etc/ssl/sac/privkey.pem
sudo chown root:root /etc/ssl/sac/fullchain.pem
sudo chown root:ssl-cert /etc/ssl/sac/privkey.pem
sudo chown root:ssl-cert /etc/ssl/sac
sudo chmod 750 /etc/ssl/sac
# очистка временных файлов
sudo shred -u server.crt ca-chain.crt privkey.pem 2>/dev/null
sudo rm -f /tmp/certs/wildcard_kalinamall_ru.pfx
```
Если `-legacy` недостаточно:
```bash
sudo openssl pkcs12 -provider legacy -provider default \
-in wildcard_kalinamall_ru.pfx -nocerts -nodes -out privkey.pem
```
Проверить содержимое PFX:
```bash
sudo openssl pkcs12 -legacy -in wildcard_kalinamall_ru.pfx -info -noout
```
Пароль PFX вводится интерактивно (`Enter Import Password`). **Не** коммитьте PFX и ключ в git.
---
## 5. Сборка fullchain из отдельных `.crt`
Типичный набор от VeriSign / DigiCert:
- `star_kalinamall_ru.crt` — ваш wildcard
- `DigiCertCA.crt` / `intermediate.crt` — промежуточный
- (иногда) `TrustedRoot.crt` — корневой **не** добавляйте в fullchain для nginx
```bash
# на ПК или на сервере, в каталоге с исходниками:
cat star_kalinamall_ru.crt intermediate.crt > fullchain.pem
# ключ переименовать:
cp private.key privkey.pem
```
Проверка порядка (первая строка сертификата — ваш сайт):
```bash
openssl crl2pkcs7 -nocrl -certfile fullchain.pem | openssl pkcs7 -print_certs -noout | grep subject=
```
Скопировать `fullchain.pem` и `privkey.pem` на сервер (§4).
---
## 6. Проверка сертификата
На сервере:
```bash
# кому выдан, срок действия
sudo openssl x509 -in /etc/ssl/sac/fullchain.pem -noout -subject -issuer -dates
# wildcard / SAN
sudo openssl x509 -in /etc/ssl/sac/fullchain.pem -noout -ext subjectAltName
# ключ соответствует сертификату
CERT_MD5=$(sudo openssl x509 -in /etc/ssl/sac/fullchain.pem -noout -modulus | openssl md5)
KEY_MD5=$(sudo openssl rsa -in /etc/ssl/sac/privkey.pem -noout -modulus 2>/dev/null | openssl md5)
# для EC-ключа:
# KEY_MD5=$(sudo openssl pkey -in /etc/ssl/sac/privkey.pem -noout -modulus | openssl md5)
echo "cert $CERT_MD5"
echo "key $KEY_MD5"
# хеши должны совпадать
```
---
## 7. Подключение в nginx
```bash
sudo cp /opt/security-alert-center/deploy/nginx/sac.conf.tls.example \
/etc/nginx/sites-available/sac
```
В файле должны быть (по умолчанию в шаблоне):
```nginx
ssl_certificate /etc/ssl/sac/fullchain.pem;
ssl_certificate_key /etc/ssl/sac/privkey.pem;
```
```bash
sudo ln -sf /etc/nginx/sites-available/sac /etc/nginx/sites-enabled/sac
sudo nginx -t
sudo systemctl reload nginx
curl -sS https://sac.kalinamall.ru/health | jq .
```
В приложении SAC (`/opt/security-alert-center/config/sac-api.env`):
```ini
SAC_PUBLIC_URL=https://sac.kalinamall.ru
```
---
## 8. Обновление сертификата (продление)
1. Получить новые файлы от УЦ.
2. Заменить `/etc/ssl/sac/fullchain.pem` и при необходимости `privkey.pem`.
3. `sudo nginx -t && sudo systemctl reload nginx`.
4. Проверить срок: `openssl x509 -in /etc/ssl/sac/fullchain.pem -noout -dates`.
Перезапуск `sac-api` **не** обязателен — TLS терминируется на nginx.
---
## 9. Безопасность
- Закрытый ключ только на сервере SAC, права `640`, группа `ssl-cert`.
- Не хранить PFX/ключ в репозитории, почте, мессенджерах.
- После распаковки PFX удалить `/tmp/*.pfx` и промежуточные копии.
---
## 10. Устранение неполадок
| Симптом | Решение |
|---------|---------|
| `RC2-40-CBC` / `unsupported` при `pkcs12` | OpenSSL 3 на Ubuntu 24.04: добавить **`-legacy`** (см. §4.4) |
| `mac verify failure` | Неверный пароль PFX |
| `nginx: PEM_read_bio_X509_AUX() failed` | Неверный PEM, проверьте `fullchain.pem` текстом, нет ли `.der` без конвертации |
| `key values mismatch` | Ключ не от этой пары сертификатов |
| `test -r privkey.pem` без вывода / key FAIL | Каталог `chown root:ssl-cert`, `750`; `usermod -aG ssl-cert www-data`; `systemctl restart nginx` |
| `permission denied` на key | То же + `chmod 640` на ключ; `test -x /etc/ssl/sac` |
| Браузер/агент не доверяет | Неполная цепочка — добавьте intermediate в fullchain |
| `certificate has expired` | Продлить у УЦ, заменить файлы |
+109
View File
@@ -0,0 +1,109 @@
# E2E и нагрузочное тестирование SAC + агенты
Чеклист после freeze MVP (`work-plan.md`). Отмечайте дату и хост.
## Дни 1–2: функциональный E2E
### Linux (ssh-monitor, `UseSAC=dual` или `exclusive`)
- [ ] `ssh-monitor --check-sac` → HTTP 201/202, событие `agent.test` в UI
- [ ] Успешный SSH login → `ssh.login.success` в SAC + Telegram (dual)
- [ ] Неудачный SSH → `ssh.login.failed`
- [ ] `SERVER_DISPLAY_NAME` задан → в SAC **Хосты** отображается display name
- [ ] Heartbeat → `agent.heartbeat`, хост не `stale` в пределах `SAC_HEARTBEAT_STALE_MINUTES`
- [ ] Daily report → `report.daily.ssh` (агент или SAC: `python -m app.jobs.daily_report --force`, `details.generated_by`)
### Windows (RDP-login-monitor, `UseSAC=dual`)
- [ ] `-CheckSac` → ingest OK
- [ ] Старт монитора → `agent.lifecycle` в SAC (HTTP 201), Telegram «ЗАПУЩЕН»
- [ ] Версия в UI **Хосты** = `$ScriptVersion` после деплоя
- [ ] `$ServerDisplayName` → display name в SAC
- [ ] 4624/4625 (тестовый вход) → `rdp.login.*` при наличии событий
- [ ] Dashboard: новое событие появляется в «Последние события» без F5 (~5 с)
### SAC UI
- [ ] Problems: open → ack → resolve
- [ ] Дубликат `event_id` → HTTP 409, одна строка в БД
- [ ] Правило brute-force (порог событий) → Problem
### SAC Telegram — severity и exclusive (`notif-02`)
**Предусловие:** на prod после `79a78dc+` выполнен деплой (`sudo /opt/sac-deploy.sh` → миграция **`004_notification_channels`**).
| # | Шаг | Ожидание |
|---|-----|----------|
| 1 | UI → **Настройки** → «Проверить Telegram» | Сообщение «✅ SAC: тестовое сообщение…» в чате |
| 2 | Включить Telegram, `min severity` = **warning**, сохранить (token/chat из env или форма) | GET: `source=db`, `configured=true` |
| 3 | Ingest **`rdp.login.failed`** (severity `warning`) — см. curl ниже | Telegram: «🚨 SAC событие», severity warning |
| 4 | Ingest **`rdp.login.success`** (severity `info`) — новый `event_id` | Событие в UI, **нет** сообщения в Telegram |
| 5 | Сменить `min severity` на **high**, сохранить | Повтор `failed` (warning) → **нет** TG; `high` problem → TG |
| 6 | Агент **`UseSAC=exclusive`**: неудачный вход | TG только из SAC, **не** из бота агента |
**Синтетический ingest (без RDP-хоста):** подставьте `SAC_API_KEY` и уникальный `event_id`.
```bash
API_KEY="sac_…"
BASE="https://sac.kalinamall.ru"
# warning → должен уйти в TG при min=warning
curl -sS -X POST "$BASE/api/v1/events" \
-H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
-d '{
"schema_version": "1.0",
"event_id": "00000000-0000-4000-8000-000000000101",
"occurred_at": "2026-05-29T12:00:00+03:00",
"source": {"product": "rdp-login-monitor", "product_version": "1.2.20-SAC"},
"host": {"hostname": "E2E-RDP", "os_family": "windows", "display_name": "E2E RDP test"},
"category": "auth",
"type": "rdp.login.failed",
"severity": "warning",
"title": "RDP login failed (E2E)",
"summary": "notif-02 synthetic failed login"
}'
# info → не должен уйти в TG при min=warning
curl -sS -X POST "$BASE/api/v1/events" \
-H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
-d '{
"schema_version": "1.0",
"event_id": "00000000-0000-4000-8000-000000000102",
"occurred_at": "2026-05-29T12:01:00+03:00",
"source": {"product": "rdp-login-monitor", "product_version": "1.2.20-SAC"},
"host": {"hostname": "E2E-RDP", "os_family": "windows"},
"category": "auth",
"type": "rdp.login.success",
"severity": "info",
"title": "RDP login success (E2E)",
"summary": "notif-02 synthetic success — no TG expected"
}'
```
Отметка: дата ______, оператор ______, результат TG шагов 3–4: ☐ OK / ☐ fail
## Дни 3–4: нагрузка и устойчивость
- [ ] Пакет 50+ `ssh.login.failed` с одного IP → агрегация/Problem, без зависания UI
- [ ] Повтор ingest с тем же `event_id` → 409
- [ ] Остановка SAC API → spool на агенте, восстановление после `/health`
- [ ] Flapping heartbeat: stale → warning в health / хостах
## День 5: регресс
- [ ] Обновление RDP через Deploy: одна версия в `deployed_version.txt`, ingest после 1.2.14+ JSON
- [ ] `sac-retention.timer` active; ручной `systemctl start sac-retention.service` — exit 0
- [ ] Известные issues занесены в issue tracker / `known-issues.md`
## Дни 67: soak
- [ ] 4872 ч dual mode: нет роста spool без причины, диск БД в норме
- [ ] Только bugfix по результатам выше
## Версии на момент чеклиста
| Компонент | Версия |
|-----------|--------|
| ssh-monitor | 1.2.6-SAC |
| RDP-login-monitor | 1.2.20-SAC |
| security-alert-center | 0.7.0 (`backend/app/version.py`) |
+89
View File
@@ -0,0 +1,89 @@
# ToDo — 29.05.2026 (оповещения SAC + «Настройки»)
Контекст: при **`UseSAC=exclusive`** на агентах Telegram/email с хоста **не идут** — оператору нужны оповещения **из SAC**. Аналитика и прочее — отдельно ([analytics-backlog.md](analytics-backlog.md)).
---
## Уже есть в коде (не путать с «ещё не сделано»)
| Что | Где | Ограничение |
|-----|-----|-------------|
| Telegram из SAC | `backend/app/services/telegram_notify.py` | Только если в `sac-api.env`: `TELEGRAM_ENABLED=true`, токен, chat_id |
| Порог severity | `telegram_min_severity` (по умолчанию **`high`**) | События **`info`** (успешный RDP `rdp.login.success`) **не** уходят в TG, пока порог не снизить до `warning` |
| Вызов при ingest | `events.py``notify_event` / `notify_problem` | Шаблон короткий («🚨 SAC событие»), не как у агента |
| UI «Настройки» | `/settings`, GET/PUT Telegram | БД `notification_channels` (миграция `004`) + fallback на `sac-api.env` |
| Email / webhook | TZ F-NOT-01 | Email ✅, Webhook ✅ |
**Вывод на завтра:** для exclusive нужно (1) включить и настроить TG на сервере SAC **или** (2) сделать раздел **«Настройки»** в UI + хранение каналов; порог **`warning` и выше** для событий и problems.
**Замечание по коду:** `notify_problem()` сейчас **не проверяет** `telegram_min_severity` (в отличие от `notify_event`) — исправить в рамках `notif-03`.
---
## Задачи (приоритет)
### P0 — exclusive + Telegram
| ID | Задача | Критерий |
|----|--------|----------|
| `notif-01` | Зафиксировать в `agent-integration.md` / runbook: при **exclusive** канал оповещений = SAC; рекомендуемый `TELEGRAM_MIN_SEVERITY=warning` | ✅ docs + `env.native.example` |
| `notif-02` | Проверить на staging/prod: ingest `rdp.login.failed` (warning) → TG; `rdp.login.success` (info) → нет TG при `min=warning` | ✅ чеклист [testing-e2e-checklist.md](testing-e2e-checklist.md) § SAC Telegram |
| `notif-03` | `notify_problem`: учитывать `telegram_min_severity` | ✅ unit-тест |
### P1 — UI «Настройки» (каналы)
| ID | Задача | Критерий |
|----|--------|----------|
| `notif-10` | Маршрут `/settings`, пункт в сайдбаре (только admin JWT) | ✅ страница + GET API |
| `notif-11` | Блок **Telegram**: enabled, bot token, chat id, min severity | ✅ форма + GET/PUT |
| `notif-12` | Хранение: **вариант A** — запись в БД `notification_channels` + fallback на env | ✅ миграция `004`, `PUT /notifications/telegram` |
| `notif-13` | Секреты: токен не возвращать целиком в GET (маска `***…last4`) | ✅ GET settings |
| `notif-14` | Кнопка «Проверить Telegram» → тестовое сообщение | ✅ `POST …/telegram/test` + UI |
### P2 — Email и webhook (после Telegram)
| ID | Задача | Критерий |
|----|--------|----------|
| `notif-20` | SMTP: host, port, user, from, to, TLS — по аналогии с RDP `login_monitor.settings` | ✅ PUT/test API + UI + ingest |
| `notif-21` | Webhook: URL, optional secret header, JSON payload (event/problem) | ✅ PUT/test API + UI + ingest |
| `notif-22` | TZ F-NOT-02 урезанно: правило «severity ≥ X → каналы» без сложного конструктора | ✅ `notification_policy` + UI «Правило оповещений» |
### P3 — Качество сообщений (можно сдвинуть)
| ID | Задача |
|----|--------|
| `notif-30` | Шаблон TG ближе к агенту (HTML, поля user/ip/logon_type из `details`) | ✅ `telegram_templates.py`, `parse_mode=HTML` |
| `notif-31` | Cooldown/dedup на уровне SAC (F-NOT-03) — не дублировать problem при burst | ✅ `notification_cooldown`, env 90/300 с |
| `notif-32` | F-NOT-05: daily report из SAC при exclusive (отдельный эпик) | ✅ `daily_report.py`, timer `sac-daily-report` |
---
## Связь с агентами
```text
UseSAC=off → только агент (Telegram на хосте)
UseSAC=dual → агент + SAC ingest (+ опционально SAC TG, риск дублей)
UseSAC=exclusive→ только SAC ingest; TG/email = настройки SAC (UI/env)
```
---
## Оценка на день
| Блок | Часы (оценка) |
|------|----------------|
| P0 док + notif-03 + smoke | 23 |
| P1 UI Settings + API Telegram | 46 |
| P2 webhook/email | +1 день |
**Минимум на завтра:** `notif-01``notif-03` + черновик `notif-10``notif-14` (хотя бы API без полировки UI).
---
## См. также
- [TZ.md](TZ.md) §4.6 F-NOT-01 … F-NOT-05
- [agent-integration.md](agent-integration.md) — режимы UseSAC
- [agent-update-backlog.md](agent-update-backlog.md) — **ToDo:** версии агентов и удалённое обновление из SAC
- `backend/app/config.py``telegram_*`
- `backend/app/services/telegram_notify.py`
+171
View File
@@ -0,0 +1,171 @@
# План работ — Security Alert Center
**Актуальный порядок:** сначала подготовка агентов (параллельно с минимальным SAC ingest), затем полный SAC MVP, затем prod rollout `UseSAC=exclusive`.
---
## Фаза 0. Документация ✅
| # | Задача | Статус |
|---|--------|--------|
| 0.1–0.3 | ТЗ, архитектура, схема, deployment | ✅ |
| 0.5 | Репозиторий GitHub + зеркало kalinamall | ✅ |
| 0.6 | Multi-root workspace | ✅ |
---
## Фаза 1A. Агенты — контракт SAC (параллельно с 1B)
**Репозитории:** `ssh-monitor`, `RDP-login-monitor`
**Прод:** везде `UseSAC=off` по умолчанию.
| # | Задача | Статус |
|---|--------|--------|
| 1A.1 | Параметры `UseSAC`, `SAC_URL`, `SAC_API_KEY`, spool | ✅ ssh-monitor |
| 1A.2 | `build_sac_event()` + `send_sac_event()` по schema v1 | ✅ `sac-client.sh` |
| 1A.3 | `notify_or_sac()`: off / dual / exclusive / fallback | ✅ + маппинг событий |
| 1A.4 | `--check-sac` / `Test-SacConnection` | ✅ ssh-monitor `--check-sac` |
| 1A.5 | README агентов, first deploy | ✅ `first_deploy.sh`, `--deploy` |
**Выход:** на тестовом хосте `dual` шлёт JSON в SAC + Telegram.
---
## Фаза 1B. SAC — минимальный ingest ✅ (prod)
| # | Задача | Статус |
|---|--------|--------|
| 1B.1 | Scaffold backend FastAPI | ✅ |
| 1B.2 | PostgreSQL + Alembic (hosts, events, api_keys) | ✅ |
| 1B.3 | `POST /api/v1/events`, `GET /health` | ✅ prod `sac.kalinamall.ru` |
| 1B.4 | systemd + nginx + TLS | ✅ |
| 1B.5 | Валидация JSON Schema v1 | ✅ |
**Выход:** ingest с curl и UI на prod — достигнут.
---
## Фаза 1C. SAC — MVP UI и оповещения
| # | Задача | Статус |
|---|--------|--------|
| 1C.1 | Frontend Vue: Events, Hosts | ✅ prod |
| 1C.2 | Auth JWT, admin bootstrap | ✅ prod |
| 1C.3 | Problems (базовые правила) | ✅ API + UI (деплой ⏳) |
| 1C.4 | Telegram из SAC | ✅ backend (деплой + TELEGRAM_* в env) |
| 1C.5 | Dashboard (3 виджета), SSE | ✅ v0.2.0 |
**Выход:** тег `v0.2.0-mvp`.
---
## Фаза 2. Пилот и exclusive
| # | Задача |
|---|--------|
| 2.1 | E2E: 1 Linux + 1 Windows, `UseSAC=exclusive` | ✅ Linux [pilot-2.1-exclusive.md](pilot-2.1-exclusive.md) |
| 2.2 | Daily report / heartbeat через SAC | ✅ [pilot-2.2-heartbeat-reports.md](pilot-2.2-heartbeat-reports.md) |
| 2.3 | fallback на агентах | ✅ счётчик `SAC_FALLBACK_FAILURES` в sac-client |
**Выход:** `v0.2.0`.
---
## Фаза 3. Эксплуатация
См. [roadmap.md](roadmap.md).
---
## Ближайшие 2 дня (чеклист исполнения)
### День 1
- [x] `d1-1` Ingest: `event_id` UUID обязателен, UNIQUE индекс, коды duplicate, логи, docs/schema
- [x] `d1-2` MVP Problems: модель, корреляция `host+type+окно`, API `GET/ack/resolve`
- [x] `d1-3` Правила v1: `brute-force burst`, `privilege spike`, `host silence`
### День 2
- [x] `d2-1` UI Problems: список, фильтры, `Ack/Resolve`, карточка с таймлайном
- [x] `d2-2` Dashboard: top hosts/types, `open vs resolved 24h`, drill-down
- [x] `d2-3` Ops: retention, health checks, runbook backup/restore/deploy
- [x] `dod` DoD: нет дублей `event_id`, Problems e2e, 3 правила, UI MVP, docs, push в kalinamall
### Отображение хостов (`display_name`, как `SERVER_DISPLAY_NAME` у ssh)
Сейчас: **ssh**`SERVER_DISPLAY_NAME` только в Telegram; в SAC уходит `socket.gethostname()`. **RDP** — параметра нет, в SAC только `$env:COMPUTERNAME`. В SAC UI «Хосты» уже показывает `display_name || hostname`, если поле пришло в ingest.
- [x] `agent-display-rdp` **RDP-login-monitor:** `$ServerDisplayName` в `login_monitor.settings.ps1` / example; подпись в Telegram; в `Sac-Client.ps1``host.display_name` (и при необходимости согласовать `hostname`)
- [x] `agent-display-ssh` **ssh-monitor:** в `sac-client.sh` передавать `SERVER_DISPLAY_NAME` как `host.display_name` в JSON ingest
- [x] `sac-display-hosts` **SAC:** проверить ingest/обновление `Host.display_name`; колонка «Хосты» = человекочитаемое имя; поиск по `display_name`; `docs/agent-integration.md` + schema `host.display_name`
**Приёмка:** хост с `SERVER_DISPLAY_NAME="UNMS Kalina"` (или RDP `$ServerDisplayName`) в SAC → **Хосты** отображается как **UNMS Kalina**, а не только короткое имя ОС.
### Почасовой чеклист
#### День 1
- [x] `09:0010:00` миграция `event_id` + UNIQUE, валидация ingest
- [x] `10:0011:00` поведение duplicate (`201/409`) и логирование reject
- [x] `11:0012:00` обновить `event-schema-v1.json` и `agent-integration.md` (в т.ч. `host.display_name` для агентов)
- [x] `13:0014:30` модель Problems + миграция
- [x] `14:3016:00` корреляция при ingest (`fingerprint`, `count`, `last_seen`)
- [x] `16:0017:30` API `GET /problems`, `ack`, `resolve` + smoke
- [x] `18:0019:00` правила `brute/privilege/silence` + unit tests
#### День 2
- [x] `09:0011:00` UI Problems: таблица + фильтры (`status/severity/host`)
- [x] `11:0012:00` карточка проблемы + таймлайн событий
- [x] `13:0014:30` Dashboard: виджеты top hosts/types
- [x] `14:3016:00` Dashboard: `open/resolved 24h` + drill-down
- [x] `16:0017:00` retention job (`events 3090d`, `problems 180d+`)
- [x] `17:0018:00` health checks (DB/worker/heartbeat stale)
- [x] `18:0019:00` runbook + финальный push в kalinamall + freeze dev
### Неделя после freeze (только тестирование)
Чеклист: [testing-e2e-checklist.md](testing-e2e-checklist.md)
- [ ] `дни 12` функциональные E2E (`RDP + SSH + mixed load`)
- [ ] `дни 34` нагрузка, дубликаты, флаппинг heartbeat
- [ ] `день 5` регресс + known issues
- [ ] `дни 67` soak-test, только bugfix
---
## Gantt (упрощённо)
```mermaid
gantt
title SAC — актуальный план
dateFormat YYYY-MM-DD
section Документы
Фаза 0 :done, 2026-05-26, 1d
section SAC
1B ingest+deploy :done, 2026-05-26, 1d
1C UI базовый :done, 2026-05-26, 1d
1C notify+dash :active, 2026-05-27, 14d
section Агенты
1A UseSAC dual :2026-05-26, 10d
section Prod
Пилот exclusive :2026-06-16, 7d
```
---
## Следующий рабочий день (29.05.2026)
Оповещения SAC (**notif-01…32**) — ✅ см. [todo-2026-05-29.md](todo-2026-05-29.md). На prod: деплой, `notif-02` E2E, timer `sac-daily-report`.
Отложенная аналитика: **[analytics-backlog.md](analytics-backlog.md)**.
---
## См. также
- [install-ubuntu-24.04.md](install-ubuntu-24.04.md) — подготовка сервера
- [operations-prod-status.md](operations-prod-status.md) — статус prod и деплой
- [roadmap.md](roadmap.md)
- [TZ.md](TZ.md)
+76
View File
@@ -0,0 +1,76 @@
# Multi-root workspace: три репозитория
Как работать в одном multi-root workspace над **ssh-monitor**, **RDP-login-monitor** и **security-alert-center** одновременно (VS Code или совместимый редактор).
---
## 1. Расположение репозиториев (пример)
| Имя в workspace | Путь (пример Windows) | Remote |
|-----------------|----------------------|--------|
| `ssh-monitor` | `D:\Soft\Git\ssh-monitor` | git.papatramp.ru/PapaTramp/ssh-monitor |
| `rdp-monitor` | `D:\Soft\Git\RDP-login-monitor` | git.papatramp.ru/PapaTramp/RDP-login-monitor |
| `security-alert-center` | `D:\Soft\Git\security-alert-center` | git.papatramp.ru/PapaTramp/security-alert-center |
Пути подставьте свои.
---
## 2. Файл workspace
Создайте `security-monitors.code-workspace`:
```json
{
"folders": [
{ "path": "D:/Soft/Git/ssh-monitor", "name": "ssh-monitor" },
{ "path": "D:/Soft/Git/RDP-login-monitor", "name": "rdp-monitor" },
{ "path": "D:/Soft/Git/security-alert-center", "name": "security-alert-center" }
],
"settings": {
"files.eol": "\n"
}
}
```
Открыть: **File → Open Workspace from File**.
---
## 3. Порядок работ по фазам
| Фаза | Где править |
|------|-------------|
| 0 — ТЗ | `security-alert-center/docs/` |
| 1 — MVP SAC | `security-alert-center/backend`, `frontend` |
| 2 — UseSAC | `ssh-monitor`, `RDP-login-monitor` + мелкие правки SAC при необходимости |
---
## 4. Соглашения
- Контракт событий: `security-alert-center/schemas/event-schema-v1.json`**единственный источник истины**.
- Изменение схемы → обновить ТЗ + версию `schema_version` + оба агента.
- Коммиты в три репо **раздельные**; связь через теги/версии в README (см. [roadmap.md](roadmap.md)).
```bash
git add …
git commit -m "…"
git push kalinamall main
```
---
## 5. URL репозиториев
| Проект | URL |
|--------|-----|
| security-alert-center | `https://git.papatramp.ru/PapaTramp/security-alert-center.git` |
| ssh-monitor | `https://git.papatramp.ru/PapaTramp/ssh-monitor.git` |
| RDP-login-monitor | `https://git.papatramp.ru/PapaTramp/RDP-login-monitor.git` |
---
## См. также
- [work-plan.md](work-plan.md) фаза 0.6