ops: remediate deployment drift

This commit is contained in:
igor04091968
2026-06-07 22:00:41 +03:00
parent c751035374
commit 8310bc3616
4 changed files with 196 additions and 84 deletions
+78 -77
View File
@@ -2,10 +2,11 @@
Дата проверки: 2026-06-07. Дата проверки: 2026-06-07.
Статус: рабочий внутренний pilot-контур подтвержден частично. Контур собирает и Статус после TASK_014: рабочий внутренний pilot-контур приведен к Demo Freeze
показывает реальные данные, но перед расширением пилота нужно закрыть v1 по portal runtime, production-hardening endpoints и основным live smoke.
deployment/version drift между Demo Freeze v1 и фактически запущенным portal Контур собирает и показывает реальные данные; расширение пилота допустимо
runtime. только контролируемо, после ручной проверки UEBA evidence и назначения
операционного ownership.
## Executive Summary ## Executive Summary
@@ -24,7 +25,7 @@ runtime.
- Forensics view и базовые portal tabs открываются; - Forensics view и базовые portal tabs открываются;
- Windows runtime содержит активный текущий агентский процесс и watcher-процессы. - Windows runtime содержит активный текущий агентский процесс и watcher-процессы.
Ключевые gaps: Ключевые gaps, найденные до TASK_014:
- фактический portal runtime отстает от Demo Freeze v1: отдельные endpoints - фактический portal runtime отстает от Demo Freeze v1: отдельные endpoints
`/portal/api/workforce/kpi/explain`, `/portal/api/risk/narrative` и `/portal/api/workforce/kpi/explain`, `/portal/api/risk/narrative` и
@@ -39,9 +40,21 @@ runtime.
- UEBA на live-контуре возвращает `critical` score; нужна ручная проверка - UEBA на live-контуре возвращает `critical` score; нужна ручная проверка
evidence, чтобы отличить реальный риск от шумного правила. evidence, чтобы отличить реальный риск от шумного правила.
Вывод: контур можно использовать для ограниченного внутреннего просмотра TASK_014 remediation update:
реальных данных и сбора обратной связи, но не стоит расширять пилот до
10-50 пользователей, пока не закрыт deployment/version drift. - deployment/version drift закрыт controlled deploy актуального release binary;
- live `/healthz`, `/readyz`, `/version`, `/metrics` теперь отвечают `200`;
- request id / correlation id headers возвращаются;
- live Explainable KPI, Risk Narrative и Executive Action Center endpoints
отвечают `200`;
- live production hardening smoke прошел;
- live browser conformance smoke прошел;
- live portal tabs smoke прошел.
Вывод: контур можно использовать для контролируемого внутреннего pilot review и
подготовки ограниченного пилота. Перед расширением на 10-50 пользователей
остается вручную разобрать UEBA `critical` evidence и закрепить порядок
операционной поддержки.
## Scope ## Scope
@@ -90,37 +103,38 @@ runtime.
| Gateway `/portal/` | `401` снаружи | Внешний доступ закрыт авторизацией | | Gateway `/portal/` | `401` снаружи | Внешний доступ закрыт авторизацией |
| Portal local `/portal/` | `200` | UI доступен на gateway host | | Portal local `/portal/` | `200` | UI доступен на gateway host |
| Portal local `/portal/api/health` | `200` | API health доступен | | Portal local `/portal/api/health` | `200` | API health доступен |
| Portal local `/healthz` | `404` | Production-hardening endpoint не доступен на live runtime | | Portal local `/healthz` | `200` | Production-hardening endpoint подтвержден после TASK_014 |
| Portal local `/readyz` | `404` | Production-hardening endpoint не доступен на live runtime | | Portal local `/readyz` | `200` | Production-hardening endpoint подтвержден после TASK_014 |
| Portal local `/version` | `404` | Production-hardening endpoint не доступен на live runtime | | Portal local `/version` | `200` | Production-hardening endpoint подтвержден после TASK_014 |
| Portal local `/metrics` | `404` | Production-hardening endpoint не доступен на live runtime | | Portal local `/metrics` | `200` | Prometheus text metrics подтверждены после TASK_014 |
| ActivityWatch `/api/0/settings` | `200` | AW API отвечает | | ActivityWatch `/api/0/settings` | `200` | AW API отвечает |
Request/correlation headers: Request/correlation headers после TASK_014:
- `X-Request-Id`: не возвращается live portal API; - `X-Request-Id`: возвращается live portal API;
- `X-Correlation-Id`: не возвращается live portal API. - `X-Correlation-Id`: возвращается live portal API.
Metrics: Metrics:
- Prometheus metrics format на фактическом portal runtime не подтвержден, - Prometheus metrics format на фактическом portal runtime подтвержден через
потому что `/metrics` возвращает `404`. `/metrics`.
## Portal Validation ## Portal Validation
Проверено через tunnel к фактическому gateway-local portal port. Screenshots Проверено через tunnel к фактическому gateway-local portal port. Screenshots
создавались только во временном каталоге вне репозитория и не коммитились. создавались только во временном каталоге вне репозитория и не коммитились.
Результат `scripts/browser-conformance-smoke.mjs` на live-контуре: Результат `scripts/browser-conformance-smoke.mjs` на live-контуре после
TASK_014:
| View | Результат | Комментарий | | View | Результат | Комментарий |
| --- | --- | --- | | --- | --- | --- |
| Executive | FAIL | Не найдены Pilot v1 blocks: KPI, Explainable KPI, Risk Narrative, Recommended Actions | | Executive | OK | KPI, Explainable KPI, Risk Narrative и Recommended Actions отображаются |
| Workforce | FAIL | Не найдены ожидаемые freeze-маркеры KPI/Trend/Explainability | | Workforce | OK | KPI, подразделения, тренды и explainability отображаются |
| Security | FAIL | Не найдены ожидаемые freeze-маркеры security/risk/action blocks | | Security | OK | Security actions, events, correlation и candidates отображаются |
| Forensics | OK | Расследования, timeline, материалы и аудит отображаются | | Forensics | OK | Расследования, timeline, материалы и аудит отображаются |
Результат `scripts/detmir-portal-tabs-smoke.mjs` на live-контуре: Результат `scripts/detmir-portal-tabs-smoke.mjs` на live-контуре после TASK_014:
- базовые tabs открываются; - базовые tabs открываются;
- loading status доходит до ready; - loading status доходит до ready;
@@ -128,7 +142,7 @@ Metrics:
- Security events доступны; - Security events доступны;
- manager/security/forensics/admin view checks проходят; - manager/security/forensics/admin view checks проходят;
- server role gates проходят; - server role gates проходят;
- Executive dashboard layer и expected management block order не проходят. - Executive dashboard layer и expected management block order проходят.
## KPI Validation ## KPI Validation
@@ -159,22 +173,17 @@ Metrics:
## Explainable KPI Validation ## Explainable KPI Validation
Live endpoint: Live endpoint после TASK_014:
```text ```text
/portal/api/workforce/kpi/explain -> 404 /portal/api/workforce/kpi/explain -> 200
``` ```
Вывод: Вывод:
- Explainable KPI реализован и покрыт тестами в текущей кодовой базе; - Explainable KPI развернут на рабочем контуре;
- на рабочем контуре отдельный endpoint не развернут; - live UI показывает ожидаемый блок `Почему такой индекс активности?`;
- live UI не показывает ожидаемый блок `Почему такой индекс активности?` в - прежний `404` был следствием устаревшего deployed binary.
соответствии с freeze smoke.
Gap:
- deployment/version drift между текущим repository state и live portal runtime.
## UEBA Validation ## UEBA Validation
@@ -203,35 +212,33 @@ Live endpoint:
## Risk Narrative Validation ## Risk Narrative Validation
Live endpoint: Live endpoint после TASK_014:
```text ```text
/portal/api/risk/narrative -> 404 /portal/api/risk/narrative -> 200
``` ```
Вывод: Вывод:
- Risk Narrative реализован в текущей кодовой базе и задокументирован в - Risk Narrative развернут на рабочем контуре;
`docs/RISK_NARRATIVE_RU.md`; - Executive UI block соответствует Demo Freeze v1 ожиданиям;
- на рабочем контуре отдельный endpoint и Executive UI block не соответствуют - Risk Narrative можно показывать как deployed capability, сохраняя честное
Demo Freeze v1 ожиданиям; ограничение: это rule-based объяснение, не ML-прогноз.
- risk narrative нельзя демонстрировать на live-контуре как подтвержденный
deployed capability до обновления runtime.
## Executive Action Center Validation ## Executive Action Center Validation
Live endpoint: Live endpoint после TASK_014:
```text ```text
/portal/api/actions -> 404 /portal/api/actions -> 200
``` ```
Вывод: Вывод:
- Executive Action Center реализован в текущей кодовой базе и задокументирован; - Executive Action Center развернут на рабочем контуре;
- live runtime не отдает отдельный actions endpoint; - live runtime отдает отдельный actions endpoint;
- рекомендации в demo/freeze сценарии нельзя заявлять как live-deployed feature - рекомендации можно показывать как deployed rule-based guidance, без claims об
до обновления portal runtime. автоматическом remediation.
## Agent/Data Flow Validation ## Agent/Data Flow Validation
@@ -311,18 +318,15 @@ Backlog/dead-letter:
## Documentation Mismatches ## Documentation Mismatches
Найдены важные расхождения: Расхождения, найденные TASK_013, закрыты TASK_014:
1. Документация Demo Freeze v1 описывает production-hardening endpoints 1. Production-hardening endpoints `/healthz`, `/readyz`, `/version`,
`/healthz`, `/readyz`, `/version`, `/metrics`; live portal runtime их не `/metrics` теперь доступны на фактическом portal port.
отдает на фактическом portal port. 2. Standalone endpoints `/api/workforce/kpi/explain`,
2. Документация и текущая кодовая база описывают standalone endpoints `/api/risk/narrative`, `/api/actions` теперь доступны на live runtime.
`/api/workforce/kpi/explain`, `/api/risk/narrative`, `/api/actions`; live 3. Visual smoke текущей freeze-ветки проходит Executive, Workforce, Security и
portal runtime на gateway-local path возвращает `404`. Forensics views.
3. Visual smoke текущей freeze-ветки ожидает Executive blocks, которых нет в 4. Live runtime обновлен controlled deploy актуального release binary.
live runtime.
4. Public naming hygiene в repository docs закрыт, но live runtime/report text
может сохранять старую внутреннюю терминологию до обновления deployment.
## Security / Privacy Notes ## Security / Privacy Notes
@@ -332,20 +336,18 @@ Backlog/dead-letter:
- raw JSON payload не коммитился; - raw JSON payload не коммитился;
- screenshots с реальными данными не коммитились; - screenshots с реальными данными не коммитились;
- реальные IP/hostname/usernames/ФИО/подразделения в этот документ не внесены; - реальные IP/hostname/usernames/ФИО/подразделения в этот документ не внесены;
- проверка выполнялась read-only; - deploy выполнялся controlled способом с backup старого binary и rollback path;
- destructive commands, restarts и deploy не выполнялись. - destructive data operations не выполнялись;
- collectors, scoring rules и источники данных не менялись.
## Gaps ## Gaps
Критично перед расширением пилота: Критично перед расширением пилота:
1. Закрыть deployment/version drift live portal runtime относительно Demo Freeze 1. Провести ручной разбор UEBA `critical` с evidence, не меняя правила вслепую.
v1. 2. Назначить операционного владельца live smoke, deploy parity и rollback.
2. После обновления runtime повторить production-hardening smoke на реальном 3. Зафиксировать регламент: после каждого deploy проверять binary/version,
контуре. endpoint matrix и live smoke.
3. Проверить request id / correlation id headers на live API.
4. Проверить `/metrics` на live runtime и мониторинг low-cardinality metrics.
5. Провести ручной разбор UEBA `critical` с evidence, не меняя правила вслепую.
Желательно до пилотного расширения: Желательно до пилотного расширения:
@@ -353,9 +355,7 @@ Backlog/dead-letter:
эксплуатации и расследователей. эксплуатации и расследователей.
2. Разделить freshness report по bucket types: active, inactive, event-driven, 2. Разделить freshness report по bucket types: active, inactive, event-driven,
historical. historical.
3. Подготовить live validation checklist для deploy parity: repo commit, 3. Проверить Windows-side spool/backlog безопасной командой без раскрытия путей
deployed binary version, endpoint matrix, smoke results.
4. Проверить Windows-side spool/backlog безопасной командой без раскрытия путей
и payload. и payload.
Можно перенести после первого ограниченного пилота: Можно перенести после первого ограниченного пилота:
@@ -379,7 +379,7 @@ operations oriented:
## Explicit Non-Goals ## Explicit Non-Goals
В рамках TASK_013 не выполнялись: В рамках TASK_013/TASK_014 не выполнялись:
- новые API; - новые API;
- новый UI; - новый UI;
@@ -387,7 +387,7 @@ operations oriented:
- ML/LLM; - ML/LLM;
- DLP/SIEM/EDR claims; - DLP/SIEM/EDR claims;
- изменение scoring logic; - изменение scoring logic;
- restart/redeploy production services; - изменение collectors или источников данных;
- выгрузка персональных данных; - выгрузка персональных данных;
- сохранение real screenshots в git. - сохранение real screenshots в git.
@@ -397,11 +397,12 @@ operations oriented:
Portal, ActivityWatch, Security events, UEBA endpoint, Forensics и базовые role Portal, ActivityWatch, Security events, UEBA endpoint, Forensics и базовые role
views частично подтверждены. views частично подтверждены.
При этом live runtime не соответствует Demo Freeze v1 по production-hardening После TASK_014 live runtime соответствует Demo Freeze v1 по production-hardening
endpoints и новым risk/explain/action endpoints. До расширения пилота нужно endpoints, request/correlation headers, Explainable KPI, Risk Narrative,
закрыть deployment/version drift и повторить live validation. Текущий статус: Executive Action Center и основным smoke-проверкам. Текущий статус:
```text ```text
ready for controlled internal review; ready for controlled internal pilot review;
not ready for expanded pilot until live runtime parity is restored. ready for limited pilot preparation after manual UEBA evidence review and
operations ownership assignment.
``` ```
+5 -6
View File
@@ -29,16 +29,15 @@ headline, KPI label и CLI help используют публичное назв
- Добавлен browser-level conformance smoke с Playwright и screenshots runtime - Добавлен browser-level conformance smoke с Playwright и screenshots runtime
artifacts. artifacts.
Оставшиеся acceptance gaps: Оставшиеся acceptance gaps после TASK_014:
- Полная production-приемка требует live validation на стенде заказчика: - Полная production-приемка требует live validation на стенде заказчика:
доступность, TLS/reverse proxy, источники данных, backup/restore и ownership доступность, TLS/reverse proxy, источники данных, backup/restore и ownership
действий. действий.
- TASK_013 live validation выявил deployment/version drift: рабочий внутренний - TASK_013 live validation выявил deployment/version drift, но TASK_014 закрыл
runtime собирает и показывает реальные данные, но не соответствует Demo его controlled deploy актуального portal binary и повторным live smoke.
Freeze v1 по production-hardening endpoints и отдельным risk/explain/action - Перед расширением пилота остается ручной разбор UEBA evidence и закрепление
endpoints. До расширения пилота нужен controlled deploy/parity check и операционного ownership за deploy parity, rollback и регулярными smoke.
повторный live smoke.
## Overall Status ## Overall Status
@@ -63,3 +63,102 @@ PASS
``` ```
на живом контуре. на живом контуре.
## Выполнение
Дата выполнения: 2026-06-07.
Статус: выполнено.
### Причина drift
Рабочий portal runtime на gateway host был запущен из устаревшего release
binary. Из-за этого live-контур не соответствовал Demo Freeze v1:
* production-hardening endpoints `/healthz`, `/readyz`, `/version`,
`/metrics` возвращали `404`;
* отдельные API `/portal/api/workforce/kpi/explain`,
`/portal/api/risk/narrative`, `/portal/api/actions` возвращали `404`;
* request/correlation headers отсутствовали на live API;
* browser conformance smoke видел старый Executive/Workforce/Security слой.
Кодовая база при этом уже содержала нужные контракты и UI-блоки. Проблема была
не в архитектуре и не в отсутствующем функционале, а в несовпадении deployed
binary с freeze-срезом.
### Ремедиация
Выполнен controlled deploy актуального release binary на gateway host:
* старый бинарник сохранен в backup-каталог на gateway host;
* новый release binary собран из текущей freeze-ветки;
* бинарник установлен в штатный путь portal service;
* `detmir-portal.service` перезапущен;
* rollback path сохранен через backup старого бинарника.
В репозиторий не добавлялись runtime payload, реальные logs, screenshots с
живыми данными, IP-адреса, hostname, логины, ФИО или подразделения.
### Live endpoint matrix после ремедиации
Проверено через gateway-local portal port:
| Endpoint | Результат |
| --- | --- |
| `/healthz` | `200` |
| `/readyz` | `200` |
| `/version` | `200` |
| `/metrics` | `200` |
| `/portal/api/health` | `200` |
| `/portal/api/reports?role=executive` | `200` |
| `/portal/api/workforce/kpi/explain` | `200` |
| `/portal/api/risk/narrative` | `200` |
| `/portal/api/actions` | `200` |
Также подтверждено:
* `X-Request-Id` возвращается;
* `X-Correlation-Id` возвращается;
* внешний gateway `/healthz` отвечает `200`;
* внешний `/portal/` остается закрыт авторизацией;
* свежий scan journal не показал panic, HTTP 500 или явных timeout.
### Smoke results
```text
AWATCH_PORTAL_SMOKE_URL=http://127.0.0.1:18720 \
node scripts/awatch-production-hardening-smoke.mjs
PASS
```
```text
AWATCH_BROWSER_SMOKE_URL=http://127.0.0.1:18720/portal/ \
AWATCH_BROWSER_SMOKE_ARTIFACT_DIR=/tmp/awatch-live-remediation-browser-smoke \
node scripts/browser-conformance-smoke.mjs
PASS
```
```text
DETMIR_PORTAL_SMOKE_URL=http://127.0.0.1:18720/portal/ \
node scripts/detmir-portal-tabs-smoke.mjs
PASS
```
### Test harness hardening
`scripts/browser-conformance-smoke.mjs` был усилен: после переключения роли он
ожидает фактические маркеры контента в `#content`, а не делает снимок через
фиксированную короткую задержку. Это устраняет ложный FAIL на холодной
асинхронной загрузке Executive/Security views.
Продуктовая бизнес-логика, API contracts, роли, scoring, collectors и
архитектура не менялись.
### Итог
```text
live runtime == documented runtime
live runtime == Demo Freeze v1
Browser smoke: PASS
Production hardening smoke: PASS
```
+14 -1
View File
@@ -70,11 +70,24 @@ async function switchView(page, selector, timeout) {
selector.match(/data-view-mode="([^"]+)"/)?.[1] || "", selector.match(/data-view-mode="([^"]+)"/)?.[1] || "",
{ timeout }, { timeout },
); );
await page.waitForTimeout(250); }
async function waitForViewContent(page, spec, timeout) {
await page.waitForFunction(
({ markers, minLength }) => {
const content = document.querySelector("#content")?.innerText || "";
if (content.trim().length < minLength) return false;
const normalized = content.toLocaleLowerCase("ru-RU");
return markers.every((marker) => normalized.includes(String(marker).toLocaleLowerCase("ru-RU")));
},
{ markers: spec.required, minLength: 120 },
{ timeout },
);
} }
async function checkView(page, spec, artifactDir, timeout) { async function checkView(page, spec, artifactDir, timeout) {
await switchView(page, `[data-view-mode="${spec.viewMode}"]`, timeout); await switchView(page, `[data-view-mode="${spec.viewMode}"]`, timeout);
await waitForViewContent(page, spec, timeout);
const content = await page.locator("#content").innerText({ timeout }); const content = await page.locator("#content").innerText({ timeout });
const body = await page.locator("body").innerText({ timeout }); const body = await page.locator("body").innerText({ timeout });
const missing = missingMarkers(content, spec.required); const missing = missingMarkers(content, spec.required);