Allow host-level login/password for home/workgroup PCs while keeping global domain admins in Settings.
16 KiB
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, не наоборот.
Окно: 1–10 секунд между 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
Поток:
- Оператор нажимает qwinsta.
POST /api/v1/events/{id}/actions/qwinsta→ SAC выполняет WinRM qwinsta на клиентский ПК (internal_ipиз события).- Результат — модалка: SESSION, USER, ID, STATE.
- logoff:
POST .../actions/logoff { session_id }→ WinRMlogoff→ повторный qwinsta. - Нужен 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 после «пинка» (основной путь)
- Admin: «Запросить обновление» на хосте / группе.
- SAC:
host.pending_update = trueв ответе poll. - Windows: агент запускает
Deploy-LoginMonitor.ps1(существующий скрипт). - Linux: агент запускает
update_ssh_monitor.sh. - 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 для .ps12) Клиент по WinRM: Invoke-WebRequest → https://<SAC_PUBLIC_URL>/api/v1/agent/rdp-bundle/<token>3) Распаковка в C:\ProgramData\RDP-login-monitor\_sac_staging4) 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.
См. также
- agent-integration.md — ingest, типы RDG
- agent-update-backlog.md — ранний backlog (superseded деталями этого документа)
- roadmap.md — v0.7