Files

16 KiB
Raw Permalink Blame History

Agent Control Plane — концепция SAC v0.7+

Статус: утверждено для реализации (2026-06).
Связано: roadmap.md §v0.7, agent-integration.md, agent-update-backlog.md.


1. Зачем

Сейчас SAC — односторонний канал: агент → ingest. Для задач из 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. GitHub — публичная sanitized-копия; mirror-скрипты (push-mirror.sh, Rewrite-GitHostUrls.ps1) живут на kalinamall и нужны только для синхронизации URL в docs между remotes, к runtime агентов не относятся.


2. Общая архитектура

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).
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.startedagent.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
2) Клиент по WinRM: Invoke-WebRequesthttps://<SAC_PUBLIC_URL>/api/v1/agent/rdp-bundle/<token>
3) Распаковка в C:\ProgramData\RDP-login-monitor\_sac_staging
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:

{
  "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 (агент)

GET /api/v1/agent/commands?since=<last_ack>
Authorization: Bearer sac_xxx
{
  "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.


См. также