feat(workforce): add ClickHouse workforce analytics contour

This commit is contained in:
igor04091968
2026-06-23 21:52:57 +03:00
parent 341799ff9d
commit 337da4bb5f
44 changed files with 3973 additions and 461 deletions
+73 -2
View File
@@ -10,13 +10,16 @@
## Быстрый старт
1. Скопировать шаблон окружения:
1. Создать приватный файл окружения вне репозитория:
```bash
cp scripts/detmir-support.env.example scripts/detmir-support.env
mkdir -p "$HOME/.config/awatch-rus"
cp scripts/detmir-support.env.example "$HOME/.config/awatch-rus/detmir-support.env"
chmod 600 "$HOME/.config/awatch-rus/detmir-support.env"
```
2. Внести фактические IP/имена хостов, сервисы, пути бэкапов, VM IDs и SSH-данные.
Реальные пароли и приватные ключи не должны храниться в репозитории.
3. Запустить нужный режим:
@@ -32,6 +35,38 @@ cp scripts/detmir-support.env.example scripts/detmir-support.env
./scripts/detmir-support-run.sh --scope daily --output-dir /var/log/detmir-support
```
## Файл окружения
Скрипт загружает параметры в таком порядке:
1. файл из переменной `DETMIR_SUPPORT_ENV_FILE`, если она задана;
2. `scripts/detmir-support.env`, если он существует;
3. `$HOME/.config/awatch-rus/detmir-support.env`, если локального файла в
репозитории нет.
Рекомендуемый промышленный вариант — хранить секреты в
`$HOME/.config/awatch-rus/detmir-support.env`, а в репозитории держать только
`scripts/detmir-support.env.example`.
Минимальные параметры для текущего контура DetMir:
```bash
DETMIR_SUPPORT_PVE_HOST=10.10.10.2
DETMIR_SUPPORT_AW_HOST=10.10.10.13
DETMIR_SUPPORT_WEB_HOST=10.10.10.2
DETMIR_SUPPORT_WEB_TLS_HOST=10.10.10.2
DETMIR_SUPPORT_WINDOWS_HOST=192.168.100.18
DETMIR_SUPPORT_SURICATA_HOST=10.10.10.2
DETMIR_SUPPORT_SSH_USER=igor
DETMIR_SUPPORT_AW_SSH_USER=igor
DETMIR_SUPPORT_PVE_BACKUP_DIRS=/var/lib/pve/local-btrfs/dump
```
Парольные переменные вида `DETMIR_SUPPORT_AW_SSH_PASSWORD` допускаются только в
локальном приватном env-файле. В документации, Git и отчетах пароли не
фиксируются.
## Что делает скрипт
Собираются проверки по режиму:
@@ -40,6 +75,20 @@ cp scripts/detmir-support.env.example scripts/detmir-support.env
- **weekly**: всё из `daily` + расширенные проверки логов и журналов.
- **monthly**: всё из `weekly` + проверка апдейтов и базовая фиксация документов/DR-процесса.
Актуальные особенности текущего контура:
- операторский gateway находится на `10.10.10.2`, а не на историческом
`10.10.10.11`;
- web health endpoint: `https://10.10.10.2/healthz`, ожидаемый ответ `200`;
- защищенный корень gateway `https://10.10.10.2/` штатно отвечает `401`;
- AW API health проверяется через
`http://10.10.10.13:5600/api/0/settings/`;
- актуальный каталог Proxmox backup storage:
`/var/lib/pve/local-btrfs/dump`;
- если `suricata.service` не активен, проверка процесса Suricata
пропускается как следствие состояния сервиса, а не как отдельный сбой
процесса.
## Результаты
В каталоге отчётов создаются файлы:
@@ -53,3 +102,25 @@ cp scripts/detmir-support.env.example scripts/detmir-support.env
- `0` — без ошибок и предупреждений
- `1` — есть WARN
- `2` — есть FAIL
`SKIP` означает, что проверка не могла быть выполнена в текущих условиях:
например, нет SSH-аутентификации, сервис намеренно выключен или отсутствует
проверяемый компонент. `SKIP` не должен маскировать причину: в строке отчета
должна быть указана диагностическая причина, например
`Permission denied (publickey,password)` или
`Skipped because suricata.service state=inactive`.
## Текущие ожидаемые предупреждения
На момент актуализации документации для контура DetMir допустимо увидеть:
- `aw-server-rust` на `10.10.10.13` в состоянии `inactive`, если фактический
production service — `activitywatch-server`;
- предупреждение по резервным копиям, если последний файл в
`/var/lib/pve/local-btrfs/dump` старше установленного порога;
- `suricata.service state=inactive`, если IDS/IPS на данном узле не введен в
штатную эксплуатацию.
Эти предупреждения нужно фиксировать в отчете и отдельно согласовывать:
включать сервис, менять список ожидаемых сервисов или отмечать компонент как
неиспользуемый.
+7
View File
@@ -82,6 +82,10 @@ DetMir: серверы доступны, виртуальные машины з
reachability внутренних сетей, ошибки TLS/auth/route push.
- Проверять Suricata: запущен ли сервис, нет ли массовых блокировок рабочего
трафика, критичных алертов и переполнения логов.
- Если Suricata на конкретном узле не введена в эксплуатацию и
`suricata.service` находится в состоянии `inactive`, фиксировать это как
контролируемое предупреждение или `SKIP` с явной причиной, а не как
самостоятельный сбой процесса.
- Проверять, что критичные systemd services, timers и cron jobs
находятся в ожидаемом состоянии.
- Проверять наличие свежих резервных копий по базовым системам.
@@ -228,6 +232,9 @@ DetMir: серверы доступны, виртуальные машины з
- административный доступ к Proxmox, pfSense, Linux-серверам, web-серверу,
VPN и другим компонентам, входящим в контур;
- безопасный способ хранения и передачи учетных данных;
- локальный приватный файл параметров для автоматизированных проверок, например
`$HOME/.config/awatch-rus/detmir-support.env`, с правами доступа только для
владельца и без помещения секретов в Git;
- контакт ответственного лица для согласования рискованных изменений;
- список критичных сервисов и допустимые окна обслуживания;
- сведения о провайдерах, доменах, сертификатах, внешних адресах и каналах
+114
View File
@@ -0,0 +1,114 @@
# Эксплуатационная проверка контура
Дата актуализации: 2026-06-23
Документ фиксирует минимальный профессиональный контур проверки после
существенных изменений Rust-кода, сборщиков telemetry, ClickHouse workforce
аналитики, gateway или Grafana dashboards.
## Rust / cargo gate
Выполнять из репозитория. `CARGO_TARGET_DIR` должен быть вне рабочей копии, чтобы
не загрязнять diff.
```bash
cd /mnt/usb_hdd2/Projects/ActivityWatch-Russian/adk-rust
export CARGO_TARGET_DIR=/home/igor/.cache/detmir-adk-rust-target
cargo fmt --all --check
cargo test --workspace --all-targets --locked
cargo test --workspace --doc --locked
cargo clippy --workspace --all-targets --locked -- -D warnings
cargo audit --deny warnings
```
Проверка политики зависимостей:
```bash
cd /mnt/usb_hdd2/Projects/ActivityWatch-Russian
cargo deny --manifest-path adk-rust/Cargo.toml check \
--config deny.toml \
--hide-inclusion-graph \
--show-stats
```
Windows/RDP collector дополнительно проверяется под целевой ABI:
```bash
cd /mnt/usb_hdd2/Projects/ActivityWatch-Russian/adk-rust
export CARGO_TARGET_DIR=/home/igor/.cache/detmir-adk-rust-target
cargo check --target x86_64-pc-windows-gnu -p aw-windows-telemetry --locked
cargo clippy --target x86_64-pc-windows-gnu \
-p aw-windows-telemetry \
--all-targets \
--locked \
-- -D warnings
```
Repository-specific gate:
```bash
cd /mnt/usb_hdd2/Projects/ActivityWatch-Russian
python3 scripts/public_secret_pattern_check.py
cd /mnt/usb_hdd2/Projects/ActivityWatch-Russian/adk-rust
export CARGO_TARGET_DIR=/home/igor/.cache/detmir-adk-rust-target
cargo run -p quality-gate -- --root /mnt/usb_hdd2/Projects/ActivityWatch-Russian
```
## Browser smoke
Browser smoke не заменяет API/CLI проверки. Он подтверждает, что операторский
контур реально открывается в браузере и dashboards рендерят панели.
Минимальный набор страниц:
- `http://10.10.10.13:5600/` - ActivityWatch WebUI.
- `http://10.10.10.13:5610/reports/worktime/today` - дневной RDP отчет.
- `http://10.10.10.13:5610/reports/worktime/management` - управленческий RDP
отчет.
- `http://10.10.10.2:8710/manager/brief` - 1C executive brief.
- `http://10.10.10.2:8710/manager/actions` - очередь управленческих действий.
- `http://10.10.10.2:8710/manager/recovery` - recovery brief.
- `http://10.10.10.2:8710/manager/digest/weekly` - weekly digest.
- `https://dm.iri1968.dpdns.org/` - gateway index через Basic Auth.
- `https://dm.iri1968.dpdns.org/d/detmir-rdp-user-activity/detmir3a-rabota-pol-zovatelej-v-rdp?orgId=1&from=now-7d&to=now&timezone=browser&var-host=SHARKON2025&refresh=5m`
- RDP user activity dashboard.
- `https://dm.iri1968.dpdns.org/d/detmir-aw-main/detmir3a-activitywatch-overview?orgId=1&from=now-24h&to=now&timezone=browser&refresh=5m`
- main ActivityWatch dashboard.
Правила:
- Basic Auth читать с `pve-detmir:/etc/detmir/proxmox-web-gateway.credentials`.
Пароль нельзя печатать в логах, документации, commit messages или final report.
- Скриншоты для диагностики хранить в `/tmp/aw-browser-smoke-*`; не коммитить.
- Ошибки `404` по Grafana endpoint `/api/dashboards/uid/*/public-dashboards`
не считаются отказом панели: это metadata public dashboard, не datasource.
Пустые panels, 5xx, ошибки datasource или отсутствие данных в body/screenshot
считаются регрессией.
## Production smoke
После deploy или изменения telemetry/workforce выполнить:
```bash
cd /mnt/usb_hdd2/Projects/ActivityWatch-Russian
NO_PROXY=localhost,127.0.0.1,10.10.10.13,10.10.10.2,192.168.100.18,10.10.10.0/24 \
no_proxy=localhost,127.0.0.1,10.10.10.13,10.10.10.2,192.168.100.18,10.10.10.0/24 \
./check-aw-full.sh
```
Для workforce ClickHouse дополнительно проверить quality views:
```sql
SELECT
(SELECT count() FROM aw_workforce.v_workforce_unknown_subjects) AS unknown_subjects,
(SELECT count() FROM aw_workforce.v_workforce_unknown_processes) AS unknown_processes,
(SELECT count() FROM aw_workforce.v_workforce_unknown_domains) AS unknown_domains,
(SELECT countIf(user_login = 'unknown'
OR (process_name = 'unknown' AND lengthUTF8(window_title) = 0))
FROM aw_workforce.aw_window_events) AS no_user_window_rows;
```
Ожидаемое состояние после cleanup/normalization: все четыре значения равны `0`.
+13
View File
@@ -45,6 +45,19 @@ evidence. Russian build-runner still required for registry release evidence.
Public security checks are advisory/public validation. Registry release
security evidence must be generated in the Russian build contour.
Current local engineering gate for product changes is documented in
`docs/OPERATIONS_VALIDATION_RUNBOOK_RU.md` and includes:
- full Rust workspace tests with `--workspace --all-targets --locked`;
- doc tests;
- `clippy` with `-D warnings`;
- RustSec check through `cargo audit --deny warnings`;
- dependency policy through `cargo deny`;
- repository `quality-gate`;
- public secret-pattern check;
- browser smoke through ActivityWatch UI, worktime reports, 1C manager pages,
gateway and Grafana dashboards.
## Conservative positioning
The quality layer does not claim certification, does not position AWatch-rus as
+11 -2
View File
@@ -26,7 +26,12 @@ release evidence должен производиться на российско
## Rust code quality
- Rust-код форматируется `cargo fmt --all --check`.
- Для затронутого Rust workspace ожидаются relevant `cargo test` checks.
- Для существенных Rust-изменений ожидается полный workspace gate:
`cargo test --workspace --all-targets --locked`,
`cargo test --workspace --doc --locked` и
`cargo clippy --workspace --all-targets --locked -- -D warnings`.
- Для Windows/RDP collector дополнительно проверяется target
`x86_64-pc-windows-gnu` через `cargo check` и `cargo clippy`.
- Ошибки обрабатываются явно; нет silent fallback для security-sensitive paths.
- Timeouts, retries and bounds are explicit for network or long-running work.
- Новые dependencies justified and license-compatible.
@@ -95,7 +100,11 @@ release evidence должен производиться на российско
`bash scripts/registry_readiness_check.sh`,
`git diff --check`.
- For shell changes, `bash -n` is mandatory for changed shell scripts.
- For Rust/product changes, Rust checks are required by risk and scope.
- For Rust/product changes, use
`docs/OPERATIONS_VALIDATION_RUNBOOK_RU.md` as the default local validation
contour.
- For operator-facing web, gateway, worktime reports or Grafana dashboards,
browser smoke through the rendered pages is required in addition to API checks.
## Evidence requirements
@@ -0,0 +1,63 @@
# Источники ActivityWatch для workforce ingest: SHARKON2025
Дата проверки: `2026-06-23`
AW API: `http://10.10.10.13:5600/api/0`
Хост: `SHARKON2025`
## Итоговое решение P1
Основные источники для `aw_workforce`:
| Поток | Bucket | Решение | Поля |
|---|---|---|---|
| Desktop/window facts | `aw-watcher-window_SHARKON2025` | Загружать в `aw_window_events` | `app`, `hostname`, `processId`, `sessionId`, `source`, `title`, `username` |
| Browser facts, Edge | `aw-watcher-web-edge_SHARKON2025` | Загружать в `aw_browser_events` | `app`, `browser`, `hostname`, `sessionId`, `source`, `title`, `url`, `username` |
| Browser facts, Chrome | `aw-watcher-web-chrome_SHARKON2025` | Загружать как исторический browser source; если `username` отсутствует, писать `unknown` | `app`, `browser`, `sessionId`, `source`, `title`, `url` |
Не использовать как основной fact-source продуктивности:
| Bucket | Причина |
|---|---|
| `aw-rdp-window_SHARKON2025` | Есть активное окно RDP bridge, но нет `username` в событии; не годится для точной per-user привязки. |
| `aw-detmir-web-category_SHARKON2025` | Это health/category signal с `signalType=collector_health` и нулевой длительностью; полезен для диагностики collector/user presence, но не для длительности продуктивности. |
| `aw-worktime-sessions_SHARKON2025` | Авторитетный источник сессий и пользователей RDP, но это presence/session facts, а не window/browser usage facts. |
## Подтвержденные факты
`aw-watcher-window_SHARKON2025`:
- type: `currentwindow`;
- client: `aw-watcher-window`;
- hostname: `SHARKON2025`;
- за последние 24 часа есть события с пользователями `USER1`, `USER4`, `USER5`, `Администратор`;
- `USER1` подтвержден в этом bucket и будет нормализован loader-ом в `user1`.
`aw-watcher-web-edge_SHARKON2025`:
- type: `web.tab.current`;
- client: `aw-watcher-web-edge`;
- hostname: `SHARKON2025`;
- события за последние 30 дней содержат `username`;
- на момент проверки событий за последние 24 часа не было, поэтому источник включается как основной browser source, но freshness контролируется отдельно.
`aw-watcher-web-chrome_SHARKON2025`:
- type: `web.tab.current`;
- client: `aw-watcher-web-chrome`;
- hostname: `SHARKON2025`;
- последние найденные события исторические и не содержат `username`;
- loader загружает их с `user_login='unknown'`, пока нет надежной session correlation.
## Первая привязка P3
Файл загрузки: `clickhouse-workforce/sample/seed_sharkon2025_p3.sql`.
| host_name | user_domain | user_login | department | branch |
|---|---|---|---|---|
| `SHARKON2025` | `sharkon2025` | `user1` | `tsj` | `tsj` |
Ключ словаря остается `(host_name, user_login)`. Домен хранится как атрибут
`user_domain`, потому что текущие raw-события ActivityWatch дают `username`, а не
стабильный `DOMAIN\user` в window/browser facts.
File diff suppressed because it is too large Load Diff