724 lines
28 KiB
Markdown
Executable File
724 lines
28 KiB
Markdown
Executable File
# Runbook
|
||
|
||
## Быстрый health-check
|
||
|
||
### Proxmox web gateway
|
||
|
||
Если на host `<GATEWAY_HOST>` развёрнут `nginx` gateway, базовые проверки такие:
|
||
|
||
```sh
|
||
systemctl status nginx --no-pager
|
||
nginx -t
|
||
curl -I -sS -H 'Host: <PUBLIC_GATEWAY_FQDN>' http://127.0.0.1/
|
||
curl -k -fsS -H 'Host: <PUBLIC_GATEWAY_FQDN>' https://127.0.0.1/healthz
|
||
curl -k -I -sS -H 'Host: <PUBLIC_GATEWAY_FQDN>' https://127.0.0.1/ | head
|
||
curl -k -u "$(awk -F= '/^user=/{u=$2}/^password=/{p=$2}END{print u\":\"p}' <GATEWAY_CREDENTIALS_FILE>)" \
|
||
-H 'Host: <PUBLIC_GATEWAY_FQDN>' -fsS https://127.0.0.1/ | grep -F '<PUBLIC_GATEWAY_FQDN>'
|
||
```
|
||
|
||
Playbook для повторного rollout:
|
||
|
||
```sh
|
||
ANSIBLE_HOST_KEY_CHECKING=False \
|
||
ansible-playbook -i ansible/inventory.ini ansible/deploy_proxmox_web_gateway.yml
|
||
```
|
||
|
||
### Public gateway через pfSense
|
||
|
||
Публичная схема:
|
||
|
||
```text
|
||
Internet -> <PUBLIC_GATEWAY_FQDN> -> pfSense WAN <WAN_IP> -> NAT 80/443 -> nginx <GATEWAY_HOST>
|
||
```
|
||
|
||
Нормальное состояние:
|
||
|
||
- `https://<PUBLIC_GATEWAY_FQDN>/healthz` -> `200 ok` без auth;
|
||
- `https://<PUBLIC_GATEWAY_FQDN>/` без auth -> `401`;
|
||
- `http://<PUBLIC_GATEWAY_FQDN>/healthz` -> `301` на HTTPS;
|
||
- после Basic Auth:
|
||
- `/` -> gateway index;
|
||
- `/r/file1c/brief` -> 1C brief;
|
||
- `/r/grafana/api/health` -> Grafana health;
|
||
- `/r/aw/api/0/info` -> AW server info.
|
||
|
||
Gateway credential хранится только на `<GATEWAY_HOST>`:
|
||
|
||
```sh
|
||
sudo cat <GATEWAY_CREDENTIALS_FILE>
|
||
```
|
||
|
||
pfSense NAT backup перед автоматической правкой:
|
||
|
||
```sh
|
||
ls -1t /opt/infra-admin/backups/pfsense-gateway-nat-*.json | head
|
||
```
|
||
|
||
Проверка pfSense REST/API и pending firewall changes:
|
||
|
||
```sh
|
||
set -a
|
||
. <OPERATOR_HOME>/.config/tsj-bot/pfsense.env.readonly
|
||
set +a
|
||
curl -ksS -H "X-API-Key: $PFSENSE_API_KEY" "$PFSENSE_URL/api/v2/firewall/apply"
|
||
```
|
||
|
||
NAT должен содержать `WAN tcp 443 -> <GATEWAY_HOST>:443` и `WAN tcp 80 -> <GATEWAY_HOST>:80`.
|
||
WAN rules должны содержать pass на `<GATEWAY_HOST>:80` и `<GATEWAY_HOST>:443`.
|
||
|
||
### На Proxmox
|
||
|
||
```sh
|
||
pct status <CT_ID>
|
||
pct config <CT_ID>
|
||
pct exec <CT_ID> -- systemctl is-active activitywatch-server.service
|
||
pct exec <CT_ID> -- curl -fsS http://127.0.0.1:5600/api/0/info
|
||
```
|
||
|
||
### На host-based инсталляции
|
||
|
||
Для подтвержденного размещения на `<GATEWAY_HOST>`:
|
||
|
||
```sh
|
||
ps -ef | grep -E 'aw-server-rust|pfsense-aw-poller' | grep -v grep
|
||
curl -fsS http://127.0.0.1:5600/api/0/info
|
||
ss -ltnp | grep 5600
|
||
```
|
||
|
||
Ожидаемо должны быть видны:
|
||
|
||
- `aw-server-rust` с `--webpath /opt/aw-webui-ru`;
|
||
- `pfsense-aw-poller.py --config /etc/aw-pfsense/poller.json`.
|
||
|
||
### Внутри CT
|
||
|
||
```sh
|
||
systemctl status activitywatch-server.service --no-pager
|
||
journalctl -u activitywatch-server.service -n 100 --no-pager
|
||
curl -fsS http://127.0.0.1:5600/api/0/info
|
||
ss -ltnp | grep 5600
|
||
```
|
||
|
||
### Расширенный DLP transport health-check
|
||
|
||
На AW-server:
|
||
|
||
```sh
|
||
/usr/local/bin/aw-health-check
|
||
/usr/local/bin/dlp-health-check --json
|
||
```
|
||
|
||
Что проверяет дополнительно:
|
||
- свежесть DLP bucket-ов (`aw-dlp-endpoint-signals_*`, `aw-file-operations_*`);
|
||
- наличие transport/self-test telemetry (`queueDepth`, `eventsEnqueued`, `eventsFlushed`, `sendFailures`) в endpoint self-test;
|
||
- последние значения transport-счетчиков по каждому `aw-dlp-endpoint-signals_*` bucket;
|
||
- runtime-срез `aw-file-operations_*`: последние `collector_health`, счетчики очереди/отправки, последние операции без полного пути;
|
||
- runtime-срез `aw-dlp-incidents_*`: по умолчанию только metadata/age без тяжёлого чтения events; при ручном `--incident-sample-limit > 0` — количество реальных incident events в sample, self-test count, severity/action/rule rollup и до 5 последних incidents с коротким `message_excerpt`;
|
||
- API-доступность базовых сервисов.
|
||
|
||
Интерпретация:
|
||
- `FAIL` — есть критичная проблема (service/API/stale transport);
|
||
- `WARN` — сигнал для оператора (например, bucket еще не активирован на хосте, `queueDepth > 100`, новый рост `sendFailuresDelta >= 1`, нет `collector_health` в sample), но без hard-fail.
|
||
- `sendFailures` — накопительный счётчик collector-а; основной health-check предупреждает только по delta относительно сохранённого baseline в `AW_DLP_HEALTH_STATE_DIR`, чтобы старые восстановленные ошибки не висели вечным warning.
|
||
|
||
Пороги можно переопределить без изменения collector-ов:
|
||
|
||
```sh
|
||
/usr/local/bin/dlp-health-check --json \
|
||
--endpoint-queue-warn-depth 100 \
|
||
--endpoint-send-failure-warn-count 1 \
|
||
--incident-sample-limit 0 \
|
||
--fileops-sample-limit 20 \
|
||
--fileops-queue-warn-depth 100 \
|
||
--fileops-send-failure-warn-count 1
|
||
```
|
||
|
||
Для разового разбора последних DLP incidents можно включить sampling вручную:
|
||
|
||
```sh
|
||
/usr/local/bin/dlp-health-check --json --incident-sample-limit 20
|
||
```
|
||
|
||
Если AW-server медленно отдаёт большой `aw-dlp-incidents_*` bucket, такой запуск может вернуть `incident-runtime: WARN` по timeout. Это не должно использоваться как hard-fail основного мониторинга.
|
||
|
||
## Проверка RU patch
|
||
|
||
```sh
|
||
grep -n 'aw-ru-patch\|aw-sw-cleanup' /opt/activitywatch/webui-ru/index.html
|
||
ls -l /opt/activitywatch/webui-ru/js/
|
||
```
|
||
|
||
## Telegram bot: DLP режим и форензика
|
||
|
||
Для оператора `DetMirAuto` каноническое поведение такое:
|
||
|
||
- Кнопка DLP должна явно показывать текущий режим и целевое действие:
|
||
- `DLP сейчас: наблюдение | включить блокировку`
|
||
- `DLP сейчас: блокировка | включить наблюдение`
|
||
- `DLP сейчас: смешанный | выровнять в блокировку`
|
||
- Slash-путь для проверки без переключения: `/dlp_mode`
|
||
- Slash-путь для переключения: `/dlp_mode_toggle`
|
||
|
||
Интерпретация:
|
||
|
||
- `наблюдение` = endpoint/email правила работают в monitor-поведении (`alert/log`), без жёсткого `block`;
|
||
- `блокировка` = endpoint/email правила переведены в `block`;
|
||
- `смешанный` = часть endpoint/email правил уже `block`, часть ещё monitor-like; нормальный следующий ход — выровнять policy.
|
||
|
||
Форензика:
|
||
|
||
- Кнопка в меню называется `Форензика Windows логов`;
|
||
- Это человеко-понятный операторский вход в bounded Hayabusa path;
|
||
- Slash-команда остаётся прежней: `/aw_dfir /path/to/package.zip HOST [CASE_ID] [MODE]`
|
||
|
||
Если Telegram показывает старую клавиатуру:
|
||
|
||
- Нажать `Статус` или `/start`;
|
||
- Бот обязан прислать свежую custom-keyboard с актуальным DLP label;
|
||
- Старая кнопка в чате может ещё приходить как текст, но бот должен её принять и после ответа вернуть свежую клавиатуру.
|
||
|
||
Проверить:
|
||
|
||
- есть `aw-ru-patch.js`;
|
||
- есть `aw-sw-cleanup.js`;
|
||
- `index.html` содержит оба include;
|
||
- `service-worker.js` заменён cleanup-версией.
|
||
|
||
## Рабочее время (worktime)
|
||
|
||
Если в деплое включен `aw_apply_worktime_settings: true`, playbook применяет базовую категоризацию (classes) и view `worktime`.
|
||
|
||
Контрольные AQL-шаблоны для расчета рабочего времени: `docs/worktime_aql_detmir.md`.
|
||
|
||
Период рабочего времени задаётся переменными:
|
||
|
||
- `aw_worktime_from` (например `00:00`)
|
||
- `aw_worktime_to` (например `17:00`)
|
||
|
||
Playbook вычисляет `durationDefault` автоматически (включая смены через полночь) и выставляет:
|
||
`/api/0/settings/startOfDay` и `/api/0/settings/durationDefault`.
|
||
|
||
### Management report API
|
||
|
||
На `:5610` сейчас есть два server-side отчёта:
|
||
|
||
- классический `RDP worktime`:
|
||
- `/reports/worktime/today`
|
||
- форматы: `json`, `csv`, `html`
|
||
- управленческий `management`:
|
||
- `/reports/worktime/management`
|
||
- форматы: `json`, `csv`, `html`
|
||
|
||
Управленческий отчёт дополнительно показывает:
|
||
|
||
- рабочее окно отдельно от календарной активности;
|
||
- очередь действий руководителя;
|
||
- тренд за несколько дней;
|
||
- свежесть источников данных;
|
||
- executive summary `Что делать сегодня`.
|
||
|
||
Практические проверки:
|
||
|
||
```sh
|
||
curl -fsS 'http://127.0.0.1:5610/reports/worktime/today?day=today' | jq '.[:5]'
|
||
curl -fsS 'http://127.0.0.1:5610/reports/worktime/management?day=today' | jq '.summary,.executive'
|
||
curl -fsS 'http://127.0.0.1:5610/reports/worktime/management?format=html&day=today' | head
|
||
curl -fsS 'http://127.0.0.1:5610/reports/worktime/management?day=today&owner=Сменный%20руководитель' | jq '.filters,.summary,.owner_rollups'
|
||
curl -fsS 'http://127.0.0.1:5610/reports/worktime/management?day=today&department=Операторы%201С' | jq '.filters,.summary,.department_rollups'
|
||
```
|
||
|
||
Важно:
|
||
|
||
- первый `management`-запрос после очистки cache может быть тяжёлым, потому что
|
||
API пересчитывает trend и source freshness;
|
||
- повторные запросы должны быть быстрыми за счёт cache в
|
||
`/var/lib/activitywatch/worktime-cache`;
|
||
- по умолчанию cache живёт `300` секунд и прогревается локально через
|
||
`aw-worktime-autoheal.service`, чтобы manager-страницы не висели на cold-start;
|
||
- default warm-path должен ходить в `format=json`, потому что этого достаточно
|
||
для наполнения cache и это заметно легче, чем cold HTML render;
|
||
- warm timeout по умолчанию `70` секунд и задаётся через
|
||
`AW_WORKTIME_MANAGEMENT_WARM_TIMEOUT_SECONDS`;
|
||
- management report поддерживает scope-фильтры `owner` и `department`; они
|
||
пересчитывают `summary`, `rows`, `actions` и rollups под выбранный срез, а не
|
||
просто прячут строки в HTML;
|
||
- `trend` в filtered-view сейчас сознательно отключён: это удерживает latency в
|
||
рабочем диапазоне и не заставляет сервер заново пересчитывать исторический
|
||
ряд на каждый менеджерский клик;
|
||
- alias-файл теперь может содержать не только `users`, но и `owners`;
|
||
блок `owners` нужен для manager-facing каталога ответственных:
|
||
`display_name`, `title`, `department`, `contact`, `escalation_to`, `notes`;
|
||
- alias-файл для нормализации сотрудников по умолчанию:
|
||
`/etc/activitywatch/worktime-manager-aliases.json`.
|
||
|
||
## Типовые инциденты
|
||
|
||
### Hayabusa: операторский сценарий по умолчанию
|
||
|
||
Текущий production-сценарий уже не требует ручного `accept/process-inbox`.
|
||
|
||
Нормальный путь для оператора:
|
||
|
||
1. На Windows-хосте запустить:
|
||
|
||
```powershell
|
||
powershell.exe -ExecutionPolicy Bypass -File C:\ProgramData\AWatch-rus\export-upload-hayabusa-to-aw-server.ps1 -HoursBack 6 -CaseId 30
|
||
```
|
||
|
||
2. Сервер `<AW_SERVER_HOST>` сам:
|
||
|
||
- примет `zip` и `.meta.json` в `/opt/activitywatch/aw-rus-ops/drop`;
|
||
- запустит `aw-hayabusa`;
|
||
- посчитает severity/score;
|
||
- создаст case при уровне от `medium`;
|
||
- отправит Telegram alert при уровне от `high`.
|
||
|
||
3. Проверить результат:
|
||
|
||
```bash
|
||
systemctl is-active aw-hayabusa-drop.path
|
||
systemctl is-failed aw-hayabusa-drop.service || true
|
||
aw-hayabusa doctor
|
||
aw-hayabusa inventory
|
||
cat /opt/hayabusa/state/latest-intake.json
|
||
journalctl -u aw-hayabusa-drop.service -n 80 --no-pager
|
||
curl -fsS http://127.0.0.1:5602/api/0/dlp/cases/30
|
||
```
|
||
|
||
Ожидаемо:
|
||
|
||
- `latest-intake.json` имеет `status=ok`;
|
||
- `drop` после обработки пустой;
|
||
- в case есть `forensics.hayabusa`;
|
||
- Telegram alert уже уходит в операторский чат.
|
||
|
||
Production scheduled task на `SHARKON2025`:
|
||
|
||
- `ActivityWatch Hayabusa Upload`
|
||
- principal `Администратор`, `LogonType=Interactive`, `RunLevel=Highest`
|
||
- период `6` часов, lookback `6` часов
|
||
- нормальный `LastTaskResult=0`
|
||
|
||
Если `LastTaskResult=3221225794` (`0xC0000142`) и в `C:\ProgramData\AWatch-rus\logs\hayabusa-upload.log` нет новой строки, скрипт не стартовал. На текущем RDP-хосте это воспроизводится даже минимальной SYSTEM-задачей с `powershell.exe`; пересоздать Hayabusa task как interactive/highest от `Администратор`, не от `SYSTEM`.
|
||
|
||
Если upload прошёл, но сервер не обработал пакет:
|
||
|
||
```bash
|
||
sudo systemctl reset-failed aw-hayabusa-drop.path aw-hayabusa-drop.service
|
||
sudo systemctl start aw-hayabusa-drop.path
|
||
sudo systemctl start aw-hayabusa-drop.service
|
||
find /opt/activitywatch/aw-rus-ops/drop -maxdepth 1 -type f -ls
|
||
find /opt/hayabusa/inbox/incoming -maxdepth 1 -type f -ls
|
||
```
|
||
|
||
`drop` и `incoming` после успешной обработки должны быть пустыми; latest intake должен указывать на последний пакет `SHARKON2025`.
|
||
|
||
### Hayabusa: manual fallback / production validation end-to-end
|
||
|
||
Цель: подтвердить один реальный путь
|
||
|
||
- Windows EVTX export
|
||
- перенос пакета на `<AW_SERVER_HOST>`
|
||
- intake через `aw-hayabusa`
|
||
- генерация отчёта
|
||
- привязка bounded metadata к операторскому follow-up
|
||
|
||
Минимальный production-proven сценарий:
|
||
|
||
Этот путь нужен только если:
|
||
|
||
- надо руками прогнать старый пакет;
|
||
- надо повторно разобрать archived zip;
|
||
- надо отладить сам `aw-hayabusa` без drop-автоматики.
|
||
|
||
1. На Windows-хосте сделать экспорт:
|
||
|
||
```powershell
|
||
powershell.exe -ExecutionPolicy Bypass -File C:\ProgramData\AWatch-rus\export-evtx-for-hayabusa.ps1 -DaysBack 1
|
||
```
|
||
|
||
2. Проверить, что пакет реально появился:
|
||
|
||
```powershell
|
||
Get-ChildItem 'C:\ProgramData\AWatch-rus\forensics\evtx-exports' |
|
||
Sort-Object LastWriteTime -Descending |
|
||
Select-Object -First 5 Name,Length,LastWriteTime
|
||
```
|
||
|
||
Ожидаемо должен появиться zip вида:
|
||
|
||
- `HOST-YYYYMMDD-HHMMSS.zip`
|
||
|
||
3. Перенести zip на `<AW_SERVER_HOST>` в операторскую рабочую зону.
|
||
|
||
4. На `AW-server` проверить раннер:
|
||
|
||
```bash
|
||
aw-hayabusa doctor
|
||
aw-hayabusa inventory
|
||
aw-hayabusa profiles
|
||
```
|
||
|
||
5. Принять пакет:
|
||
|
||
```bash
|
||
aw-hayabusa accept --package /path/to/HOST-YYYYMMDD-HHMMSS.zip --host HOST
|
||
```
|
||
|
||
6. Обработать inbox:
|
||
|
||
```bash
|
||
aw-hayabusa process-inbox --mode incident
|
||
```
|
||
|
||
7. Проверить результат:
|
||
|
||
```bash
|
||
aw-hayabusa inventory
|
||
readlink -f /opt/hayabusa/state/latest-run
|
||
readlink -f /opt/hayabusa/state/latest-HOST
|
||
find /opt/hayabusa/reports/HOST -maxdepth 2 -type f | sort
|
||
```
|
||
|
||
Ожидаемо должны быть:
|
||
|
||
- `summary.html`
|
||
- `manifest.json`
|
||
- `run.log`
|
||
- `timeline.jsonl` или `timeline.csv`
|
||
- `logon-summary-*.csv`
|
||
|
||
8. Проверить traceability:
|
||
|
||
```bash
|
||
cat /opt/hayabusa/state/latest-intake.json
|
||
find /opt/hayabusa/archive/packages/HOST -maxdepth 1 -type f | sort
|
||
find /opt/hayabusa/archive/extracted/HOST -maxdepth 2 -type f | sort
|
||
```
|
||
|
||
Нужно зафиксировать:
|
||
|
||
- `host`
|
||
- `intake_id`
|
||
- `sha256`
|
||
- `package_path`
|
||
- `report_dir`
|
||
- `status`
|
||
|
||
9. Для AW-rus/operator follow-up заносить только bounded metadata:
|
||
|
||
- `tool=hayabusa`
|
||
- `host`
|
||
- `mode=incident`
|
||
- `status`
|
||
- `intake_id`
|
||
- `package_path`
|
||
- `sha256`
|
||
- `report_dir`
|
||
- `summary_html`
|
||
- `timeline_path`
|
||
- `manifest_path`
|
||
|
||
Не заносить в case / comments:
|
||
|
||
- сырые EVTX
|
||
- полный Sigma output
|
||
- полный timeline body
|
||
|
||
10. Если прогона не получилось, записать tuning backlog:
|
||
|
||
- пустой/битый zip
|
||
- нет EVTX в payload
|
||
- слабый audit scope на Windows
|
||
- шумный или слишком тяжёлый результат
|
||
- неочевидная трассировка от пакета к отчёту
|
||
|
||
Acceptance для этого сценария:
|
||
|
||
- есть хотя бы один реальный zip-пакет;
|
||
- `aw-hayabusa` провёл intake и analysis без ручной импровизации;
|
||
- артефакты трассируются от `HOST` до `report_dir`;
|
||
- follow-up не тащит сырые forensic данные в обычные AW buckets.
|
||
|
||
Example regression proof template:
|
||
|
||
- `host=HOST-EXAMPLE`
|
||
- `case_id=CASE_ID_EXAMPLE`
|
||
- `intake_id=INTAKE_ID_EXAMPLE`
|
||
- `sha256=SHA256_EXAMPLE`
|
||
- `report_dir=/opt/hayabusa/reports/HOST-EXAMPLE/REPORT_RUN_ID_EXAMPLE`
|
||
- `latest-intake.json` status: `ok`
|
||
- AW-rus case linkage stored under `forensics.hayabusa`
|
||
|
||
Типовые проблемы, найденные при validation:
|
||
|
||
- Windows zip с backslash path separators давал `unzip` warning rc=1; wrapper не должен валить intake на таком предупреждении.
|
||
- timeline режимы должны использовать `rules/config`, а не корень rules directory.
|
||
|
||
После live proof держать как regression checks:
|
||
|
||
```bash
|
||
aw-hayabusa doctor
|
||
aw-hayabusa inventory
|
||
cat /opt/hayabusa/state/latest-intake.json
|
||
readlink -f /opt/hayabusa/state/latest-run
|
||
```
|
||
|
||
### DLP не виден в вебе
|
||
|
||
Быстрый чек сервера:
|
||
|
||
```sh
|
||
curl -fsS http://127.0.0.1:5600/api/0/buckets | jq -r 'keys[] | select(test("^aw-dlp-"))'
|
||
curl -fsS http://127.0.0.1:5600/api/0/buckets/aw-dlp-incidents_HOST-EXAMPLE | jq '{end:.metadata.end}'
|
||
```
|
||
|
||
Контролируемый тест ingest:
|
||
|
||
```sh
|
||
TS=$(date -u +%Y-%m-%dT%H:%M:%S.000Z)
|
||
PAYLOAD=$(jq -nc --arg ts "$TS" '{timestamp:$ts,duration:0,data:{ruleId:"selftest-dlp-incident",action:"alert",severity:"low",message:"Self-test DLP incident from runbook",signalType:"self_test",username:"AUTOTEST",sessionId:0,hostname:"HOST-EXAMPLE",source:"self-test"}}')
|
||
curl -fsS -X POST 'http://127.0.0.1:5600/api/0/buckets/aw-dlp-incidents_HOST-EXAMPLE/heartbeat?pulsetime=60' -H 'Content-Type: application/json' --data "$PAYLOAD"
|
||
curl -fsS 'http://127.0.0.1:5600/api/0/buckets/aw-dlp-incidents_HOST-EXAMPLE/events?limit=5' | jq '.[0].data'
|
||
```
|
||
|
||
Если API видит событие, а bucket-страница в UI показывает старые `First/last event`, нажать `Обновить` на странице bucket и раскрыть `Events`.
|
||
|
||
### Проверка WAL failover (Windows collectors)
|
||
|
||
Цель: подтвердить, что при недоступности AW API события не теряются, а буферизуются в локальной очереди и автоматически отправляются после восстановления связи.
|
||
|
||
На RDP-хосте (`<WINDOWS_HOST>`) в PowerShell под администратором:
|
||
|
||
1) Проверить/обнулить очереди:
|
||
|
||
```powershell
|
||
$q1 = 'C:\ProgramData\AWatch-rus\file-operations-queue*.jsonl'
|
||
$q2 = 'C:\ProgramData\AWatch-rus\dlp-endpoint-signals-queue*.jsonl'
|
||
Get-Item $q1,$q2 | Select Name,Length,LastWriteTime
|
||
```
|
||
|
||
2) Временно заблокировать исходящий доступ на AW API (`:5600`):
|
||
|
||
```powershell
|
||
New-NetFirewallRule -DisplayName 'AWatch WAL Test Block 5600' -Direction Outbound -Action Block -Protocol TCP -RemotePort 5600 -ErrorAction SilentlyContinue
|
||
Enable-NetFirewallRule -DisplayName 'AWatch WAL Test Block 5600'
|
||
```
|
||
|
||
3) Сгенерировать тест-событие `file-operations`:
|
||
|
||
```powershell
|
||
$p = Join-Path $env:USERPROFILE 'Desktop\aw_wal_test.txt'
|
||
Set-Content -LiteralPath $p -Value ('wal-test ' + (Get-Date -Format o))
|
||
Start-Sleep -Seconds 5
|
||
```
|
||
|
||
4) Убедиться, что очередь выросла:
|
||
|
||
```powershell
|
||
Get-Item $q1,$q2 | Select Name,Length,LastWriteTime
|
||
```
|
||
|
||
5) Снять блокировку и дождаться flush:
|
||
|
||
```powershell
|
||
Disable-NetFirewallRule -DisplayName 'AWatch WAL Test Block 5600'
|
||
Start-Sleep -Seconds 20
|
||
Get-Item $q1,$q2 | Select Name,Length,LastWriteTime
|
||
```
|
||
|
||
Ожидаемо:
|
||
- на шаге 4 длина как минимум одного queue-файла увеличивается;
|
||
- на шаге 5 очередь уменьшается (в идеале до `0` или близко к фоновому уровню).
|
||
|
||
6) Проверка на AW server:
|
||
|
||
```sh
|
||
curl -fsS 'http://<AW_SERVER_HOST>:5600/api/0/buckets/aw-file-operations_<AW_SERVER_HOST>/events?limit=10' | jq '.[0].data'
|
||
```
|
||
|
||
После теста удалить правило:
|
||
|
||
```powershell
|
||
Remove-NetFirewallRule -DisplayName 'AWatch WAL Test Block 5600' -ErrorAction SilentlyContinue
|
||
```
|
||
|
||
### PowerShell MCP на AWatch-rus Windows host
|
||
|
||
Для `AWatch-rus` Windows host `<WINDOWS_HOST>` интерактивный путь из Linux/Codex закреплён через `SSH`, а не через `WSMan`.
|
||
|
||
Быстрый вход:
|
||
|
||
```bash
|
||
cd <PROJECT_ROOT>
|
||
bash scripts/install_detmir_powershell_mcp.sh
|
||
```
|
||
|
||
После переоткрытия `pwsh` или новой Codex-session:
|
||
|
||
```powershell
|
||
detmir-win-test
|
||
detmir-win-ps 'hostname; Get-Date -Format o'
|
||
detmir-win-shell
|
||
```
|
||
|
||
Канонический документ:
|
||
|
||
- `docs/POWERSHELL_MCP_REMOTE_RU.md`
|
||
|
||
Правило:
|
||
|
||
- `WinRM` использовать для `ansible aw_windows` и playbook-ов;
|
||
- `SSH` использовать для `powershell-windows` MCP, operator PowerShell и ad-hoc remote execution.
|
||
|
||
### У пользователей всплывает окно PowerShell
|
||
|
||
Ожидаемое поведение collector-ов: запуск hidden (`-WindowStyle Hidden`).
|
||
Проверка на Windows хосте:
|
||
|
||
```powershell
|
||
Get-CimInstance Win32_Process |
|
||
Where-Object { $_.CommandLine -and ($_.CommandLine -match 'browser-domains-native-collector.ps1' -or $_.CommandLine -match 'dlp-endpoint-signals-collector.ps1') } |
|
||
Select-Object SessionId, ProcessId, CommandLine
|
||
```
|
||
|
||
Если нужно экстренно убрать снимки инцидентов:
|
||
|
||
1. Поставить `incidentCapture.screenshotEnabled = false` в `deployment-config.json` (для каждого StateRoot).
|
||
2. Запустить `Start-ScheduledTask -TaskName 'ActivityWatch Recovery'`.
|
||
|
||
### Windows host: `Активное время = 0s`, хотя `window`-события есть
|
||
|
||
Симптом:
|
||
|
||
- в Activity view за день видно `Worktime = 0s`;
|
||
- `Top Window Titles / Top Categories / Category Tree` пустые;
|
||
- при этом bucket `aw-watcher-window_HOST-EXAMPLE` содержит свежие события.
|
||
|
||
Подтвержденная причина:
|
||
|
||
- watcher `afk` "залип" в `status=afk` без `not-afk`;
|
||
- из-за этого дневная сводка не считает подтвержденную активность.
|
||
|
||
Быстрый recovery (с Linux admin host):
|
||
|
||
1. Проверить учетку входа. Рабочую учетную запись задавать как параметр экземпляра: `HOST-EXAMPLE\WINDOWS_USER_EXAMPLE`.
|
||
2. Поднять remote execution через `wmiexec.py` с auth-file:
|
||
|
||
```sh
|
||
cat > /tmp/detmir-windows.auth << 'EOF'
|
||
username = WINDOWS_USER_EXAMPLE
|
||
password = <PASSWORD>
|
||
domain = HOST-EXAMPLE
|
||
EOF
|
||
chmod 600 /tmp/detmir-windows.auth
|
||
```
|
||
|
||
3. Запустить recovery task:
|
||
|
||
```sh
|
||
wmiexec.py -nooutput -A /tmp/detmir-windows.auth <WINDOWS_HOST> \
|
||
"powershell -NoProfile -Command \"Start-ScheduledTask -TaskName 'ActivityWatch Recovery'\""
|
||
```
|
||
|
||
4. Запустить все launch tasks:
|
||
|
||
```sh
|
||
wmiexec.py -nooutput -A /tmp/detmir-windows.auth <WINDOWS_HOST> \
|
||
"powershell -NoProfile -Command \"Get-ScheduledTask | Where-Object TaskName -like 'ActivityWatch Launch *' | ForEach-Object { Start-ScheduledTask -TaskName \$_.TaskName }\""
|
||
```
|
||
|
||
5. Подождать 10-20 секунд и проверить API на AW server (`<AW_SERVER_HOST>:5600`):
|
||
|
||
```sh
|
||
curl -fsS 'http://<AW_SERVER_HOST>:5600/api/0/buckets/aw-watcher-afk_HOST-EXAMPLE/events?limit=30' \
|
||
| jq '{latest:.[0].timestamp, statuses:(group_by(.data.status)|map({status:.[0].data.status,count:length}))}'
|
||
```
|
||
|
||
Ожидаемо после фикса:
|
||
|
||
- в свежих AFK-событиях появляется `status=not-afk`;
|
||
- `aw-watcher-window_HOST-EXAMPLE` продолжает обновляться;
|
||
- после обновления страницы UI дневная сводка перестает быть `0s`.
|
||
|
||
### Сервис не стартует
|
||
|
||
```sh
|
||
systemctl cat activitywatch-server.service
|
||
cat /etc/activitywatch/aw-server.env
|
||
journalctl -xeu activitywatch-server.service --no-pager
|
||
```
|
||
|
||
Частые причины:
|
||
|
||
- битый URL релиза;
|
||
- неполная распаковка архива;
|
||
- занят порт;
|
||
- не созданы каталоги или пользователь;
|
||
- ошибка в env-файле.
|
||
|
||
### API отвечает, но UI без русификации
|
||
|
||
Проверить:
|
||
|
||
- патч реально вставлен в `index.html`;
|
||
- browser cache/service worker очищен;
|
||
- reverse proxy не отдаёт старую статику;
|
||
- сервис был перезапущен после правок.
|
||
|
||
Повторное применение:
|
||
|
||
```sh
|
||
bash <CT_BOOTSTRAP_DIR>/apply_webui_ru_patch.sh
|
||
systemctl restart activitywatch-server.service
|
||
```
|
||
|
||
### После обновления UI сломался патч
|
||
|
||
- сравнить `index.html` с backup;
|
||
- заново применить patch script;
|
||
- проверить словарь в `aw-ru-patch.js`;
|
||
- при необходимости откатить только Web UI override.
|
||
|
||
## Перед любыми изменениями
|
||
|
||
1. Сделать snapshot или `vzdump`.
|
||
2. Сохранить текущий `/etc/activitywatch/aw-server.env`.
|
||
3. Сохранить текущий `index.html`.
|
||
4. Зафиксировать текущую версию `aw-server-rust`.
|
||
|
||
## Критерии готовности
|
||
|
||
- systemd unit стартует без ручного вмешательства;
|
||
- API `/api/0/info` отвечает локально;
|
||
- UI открывается;
|
||
- русификация присутствует;
|
||
- rollback-путь понятен оператору.
|
||
|
||
## Аудит CryptoPro и готовности подписантов
|
||
|
||
Для повторяемой проверки сертификатов подписантов и встроенных лицензий CryptoPro:
|
||
|
||
```bash
|
||
cd <PROJECT_ROOT>/ansible
|
||
ansible-playbook -i inventory.ini audit_cryptopro_windows.yml
|
||
```
|
||
|
||
Итоговый JSON-отчёт сохраняется локально в:
|
||
|
||
```text
|
||
/tmp/aw-rus-cryptopro-audit-<user>/rdp-prod-cryptopro-audit.json
|
||
```
|
||
|
||
Отчёт содержит матрицу по профилям:
|
||
|
||
- `requestedUser`
|
||
- `profileUser`
|
||
- `thumbprint`
|
||
- `subject`
|
||
- `hasPrivateKey`
|
||
- `embeddedLicenseOk`
|
||
- `embeddedLicenseStatus`
|
||
- `container` если виден
|
||
- `actionNeeded`
|