docs(public): sanitize release materials
This commit is contained in:
@@ -39,3 +39,5 @@ data/
|
||||
# Release assets kept outside git
|
||||
/install-kit-awindows-*.zip
|
||||
/install-kit-awindows-*.tar.gz
|
||||
/install-kit-awindows-*/
|
||||
/scripts/configure_pfsense_gateway_nat.py
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
# Phase 17 Summary: Production Validation
|
||||
|
||||
## Result
|
||||
|
||||
Phase 17 is closed.
|
||||
|
||||
## What was proven live
|
||||
|
||||
A real end-to-end forensic path was executed and closed on production infrastructure:
|
||||
|
||||
1. Windows EVTX package was exported on `SHARKON2025`.
|
||||
2. The real zip package was transferred to `10.10.10.13`.
|
||||
3. `aw-hayabusa accept` and `aw-hayabusa process-inbox --mode incident` were run through the standard wrapper.
|
||||
4. Hayabusa generated a real report set with bounded traceability metadata.
|
||||
5. The result was linked back into AW-rus case management as bounded `forensics.hayabusa` metadata.
|
||||
|
||||
## Live proof record
|
||||
|
||||
- host: `SHARKON2025`
|
||||
- case id: `30`
|
||||
- intake id: `20260521T125653Z_SHARKON2025-phase17-rerun3`
|
||||
- package path: `/opt/hayabusa/archive/packages/SHARKON2025/20260521T125653Z_SHARKON2025-phase17-rerun3.zip`
|
||||
- sha256: `e86b9abbfc1d706ac706c6c8a89509ab17023344c50880641e9175f73f1198d4`
|
||||
- report dir: `/opt/hayabusa/reports/SHARKON2025/20260521T125654Z_incident_20260521T125653Z_SHARKON2025-phase17-rerun3`
|
||||
- report artifacts:
|
||||
- `summary.html`
|
||||
- `manifest.json`
|
||||
- `run.log`
|
||||
- `timeline.jsonl`
|
||||
- `logon-summary-successful.csv`
|
||||
- `logon-summary-failed.csv`
|
||||
|
||||
## Production bugs found and fixed during validation
|
||||
|
||||
- `aw-hayabusa` treated `unzip` warning return code `1` as a hard failure for Windows-created zip archives that use backslashes as path separators.
|
||||
- timeline modes were using the wrong Hayabusa config path; the wrapper must pass `rules/config`, not the rules root.
|
||||
|
||||
Both issues were fixed in `aw-server/hayabusa/aw-hayabusa.sh` and retested live against the same package.
|
||||
|
||||
## Why this closes the phase
|
||||
|
||||
- the path is no longer theoretical or docs-only; it was proven on a real Windows export package
|
||||
- traceability from host to package to report directory is explicit
|
||||
- AW-rus case linkage now stores bounded forensic metadata exactly as designed
|
||||
- the remaining gaps are operational tuning items, not missing core implementation
|
||||
|
||||
## Tuning backlog after the live run
|
||||
|
||||
- keep at least one preserved sample EVTX zip for future regression reruns
|
||||
- consider a self-check in `aw-hayabusa doctor` for `rules/config` completeness
|
||||
- optionally persist a compact machine-readable proof manifest for future audits
|
||||
@@ -1,53 +1,8 @@
|
||||
# Установка экземпляра для эксперта
|
||||
|
||||
Этот документ дает короткий воспроизводимый путь проверки экземпляра без
|
||||
привязки к личному стенду разработчика.
|
||||
Основной пошаговый документ находится здесь:
|
||||
|
||||
## 1. Подготовка
|
||||
- [`docs/INSTALL_FOR_EXPERT_RU.md`](docs/INSTALL_FOR_EXPERT_RU.md)
|
||||
|
||||
1. Склонировать репозиторий.
|
||||
2. Создать приватную конфигурацию:
|
||||
|
||||
```bash
|
||||
cp private-config/deploy.env.example private-config/deploy.env
|
||||
```
|
||||
|
||||
3. Создать локальный Ansible inventory на основе:
|
||||
|
||||
```bash
|
||||
cp ansible/inventory.example.ini ansible/inventory.ini
|
||||
```
|
||||
|
||||
4. Заполнить адреса, учетные данные и токены конкретного тестового стенда.
|
||||
|
||||
## 2. Сборка
|
||||
|
||||
```bash
|
||||
cd adk-rust
|
||||
cargo build --release --workspace
|
||||
```
|
||||
|
||||
## 3. Проверки до установки
|
||||
|
||||
```bash
|
||||
scripts/quality-gate.sh
|
||||
ansible-playbook --syntax-check -i ansible/inventory.ini ansible/deploy_aw_server.yml
|
||||
```
|
||||
|
||||
## 4. Установка
|
||||
|
||||
Базовый серверный путь описан в `docs/INSTALL_RU.md`. Конкретный playbook
|
||||
выбирается по проверяемой схеме: серверный runtime, Windows collectors,
|
||||
Grafana dashboards или портал оператора.
|
||||
|
||||
## 5. Smoke-проверка
|
||||
|
||||
После установки:
|
||||
|
||||
```bash
|
||||
detmir-check
|
||||
detmir-status
|
||||
```
|
||||
|
||||
Ожидаемый результат: статус `OK`, отсутствуют критичные service failures и
|
||||
stale/dead buckets для обязательных источников.
|
||||
Файл в корне оставлен как совместимая ссылка для внешних проверок и старых
|
||||
закладок.
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
- `<AW_SERVER_HOST>` — основной `AW-rus` server, health, worktime/reporting, DLP server-side services, `Hayabusa` processing.
|
||||
- `<GATEWAY_HOST>` — operator/gateway host, Telegram bot, web gateway, часть `1C` analytics runtime.
|
||||
- `<WINDOWS_HOST>` — `SHARKON2025`, Windows/RDP host с collector toolkit.
|
||||
- `<WINDOWS_HOST>` — `HOST-EXAMPLE`, Windows/RDP host с collector toolkit.
|
||||
- `<GRAFANA_HOST>` — Grafana.
|
||||
- `<FIREWALL_HOST>` — `pfSense`, сетевой perimeter и VPN.
|
||||
|
||||
|
||||
+9
-9
@@ -954,7 +954,7 @@ systemctl is-active tsj-guardian-bot tsj-guardian-watchdog gost-tg
|
||||
- read-only SQLite audit показал, что `/var/lib/activitywatch/aw-server-rust/sqlite.db`
|
||||
занимает около `6.8G`, `freelist_count=0`; VACUUM сам по себе не
|
||||
освободит место, потому что размер занят live events;
|
||||
- основной источник роста: `aw-session-events_SHARKON2025` - около
|
||||
- основной источник роста: `aw-session-events_HOST-EXAMPLE` - около
|
||||
`6.9M` строк и `~5GB` payload, с пиками `1.2M-2.4M` process-level
|
||||
событий в сутки за 2026-05-29..2026-06-01;
|
||||
- production live config на RDP был `pollSeconds=5` и
|
||||
@@ -967,7 +967,7 @@ systemctl is-active tsj-guardian-bot tsj-guardian-watchdog gost-tg
|
||||
`C:\ProgramData\AWatch-rus\switch-backups\deployment-config.before-disable-process-events-20260602T061836Z.json`;
|
||||
- старый `worktime-session-collector.ps1` PID `8476` остановлен, collector
|
||||
поднят заново штатными `ActivityWatch Launch [...]` tasks/guard;
|
||||
- delta-gate: `metadata.end` bucket `aw-session-events_SHARKON2025`
|
||||
- delta-gate: `metadata.end` bucket `aw-session-events_HOST-EXAMPLE`
|
||||
остался `2026-06-02T06:27:11.197Z` через 75 секунд, постоянный поток
|
||||
остановлен;
|
||||
- базовый сбор не сломан: `detmir-check --json` OK,
|
||||
@@ -982,10 +982,10 @@ systemctl is-active tsj-guardian-bot tsj-guardian-watchdog gost-tg
|
||||
- на время операции остановлены AW-related timers/services и
|
||||
`activitywatch-server.service`, чтобы не было writer'ов к SQLite;
|
||||
- scoped delete удалил только события bucket
|
||||
`aw-session-events_SHARKON2025` с `eventType=process_start` или
|
||||
`aw-session-events_HOST-EXAMPLE` с `eventType=process_start` или
|
||||
`eventType=process_stop`;
|
||||
- удалено `6,906,190` шумных process-level событий;
|
||||
- сохранены logon events: после trim в `aw-session-events_SHARKON2025`
|
||||
- сохранены logon events: после trim в `aw-session-events_HOST-EXAMPLE`
|
||||
осталось `174` события, recent samples имеют `eventType=logon`;
|
||||
- `PRAGMA integrity_check` до и после `VACUUM`: `ok`;
|
||||
- DB уменьшилась с `6.8G` до `350M`, rootfs AW server вернулся к
|
||||
@@ -1108,10 +1108,10 @@ systemctl is-active tsj-guardian-bot tsj-guardian-watchdog gost-tg
|
||||
- `hardening-recovery.ps1` выполнен по existing
|
||||
`C:\ProgramData\AWatch-rus\deployment-config.json`; после recovery
|
||||
config остался `processEventsEnabled=false`, `logonEnabled=true`,
|
||||
`pollSeconds=5`, users включают `SHARKON2025\Администратор`;
|
||||
- exact task check: `ActivityWatch Launch [SHARKON2025_Администратор]`
|
||||
`pollSeconds=5`, users включают `HOST-EXAMPLE\Администратор`;
|
||||
- exact task check: `ActivityWatch Launch [HOST-EXAMPLE_Администратор]`
|
||||
существует, ошибочный
|
||||
`ActivityWatch Launch [SHARKON2025_Administrator]` отсутствует,
|
||||
`ActivityWatch Launch [HOST-EXAMPLE_Administrator]` отсутствует,
|
||||
`ActivityWatch Recovery` существует;
|
||||
- `AWatchRusCollectorGuard` running/automatic; Windows
|
||||
`validate-deployment.ps1` вернул `overallOk=True`;
|
||||
@@ -1308,7 +1308,7 @@ systemctl is-active tsj-guardian-bot tsj-guardian-watchdog gost-tg
|
||||
`dlp_counts={ok:22,warn:0,fail:0}` and `ok_for_operator=true`.
|
||||
46. `[done]` Перенести Proxmox DetMir contour smoke на Rust-first helper:
|
||||
- добавлен crate `aw-contour-smoke`;
|
||||
- `scripts/aw-contour-smoke-<GATEWAY_HOST>.sh` теперь Rust-first wrapper:
|
||||
- `scripts/aw-contour-smoke-gateway.sh` теперь Rust-first wrapper:
|
||||
ищет `AW_CONTOUR_SMOKE_RUST`,
|
||||
`$CARGO_TARGET_DIR/release/aw-contour-smoke`,
|
||||
`adk-rust/target/release/aw-contour-smoke`,
|
||||
@@ -1575,7 +1575,7 @@ systemctl is-active tsj-guardian-bot tsj-guardian-watchdog gost-tg
|
||||
`.playwright-cli/page-2026-06-02T17-34-04-725Z.png`.
|
||||
- portal link repair after Grafana/gateway smoke: quick links now open in a
|
||||
separate tab, `worktime_report` points to explicit HTML
|
||||
`/reports/worktime/management?format=html&host=SHARKON2025`,
|
||||
`/reports/worktime/management?format=html&host=HOST-EXAMPLE`,
|
||||
`1С действия` is shown in the portal, and gateway `/r/aw-worktime` is
|
||||
pinned to the same HTML report. Production smoke: AW UI, Worktime, 1C
|
||||
brief, and 1C actions returned `200 text/html`; Grafana links correctly
|
||||
|
||||
@@ -116,7 +116,7 @@ fn run_proxmox_remote() -> Result<i32> {
|
||||
check_tcp(&mut counts, "nginx http", "127.0.0.1", 80);
|
||||
check_tcp(&mut counts, "nginx https", "127.0.0.1", 443);
|
||||
check_tcp(&mut counts, "proxmox web", "127.0.0.1", 8006);
|
||||
check_tcp(&mut counts, "1C company API", "10.10.10.2", 8710);
|
||||
check_tcp(&mut counts, "1C company API", "192.0.2.2", 8710);
|
||||
check_tcp(&mut counts, "clickhouse native", "127.0.0.1", 9000);
|
||||
check_tcp(&mut counts, "clickhouse http", "127.0.0.1", 8123);
|
||||
if let Ok(out) = command_output("ss", &["-tulpn"]) {
|
||||
@@ -141,7 +141,7 @@ fn run_proxmox_remote() -> Result<i32> {
|
||||
"go proxmox gui",
|
||||
"https://127.0.0.1/go/proxmox-gui",
|
||||
&[301, 302, 307, 308],
|
||||
Some("https://10.10.10.2:8006/"),
|
||||
Some("https://192.0.2.2:8006/"),
|
||||
);
|
||||
check_http_redirect(
|
||||
&mut counts,
|
||||
@@ -149,7 +149,7 @@ fn run_proxmox_remote() -> Result<i32> {
|
||||
"go file1c brief",
|
||||
"https://127.0.0.1/go/file1c-brief",
|
||||
&[301, 302, 307, 308],
|
||||
Some("http://10.10.10.2:8710/manager/brief"),
|
||||
Some("http://192.0.2.2:8710/manager/brief"),
|
||||
);
|
||||
check_http_redirect(
|
||||
&mut counts,
|
||||
@@ -157,7 +157,7 @@ fn run_proxmox_remote() -> Result<i32> {
|
||||
"go file1c actions",
|
||||
"https://127.0.0.1/go/file1c-actions",
|
||||
&[301, 302, 307, 308],
|
||||
Some("http://10.10.10.2:8710/manager/actions"),
|
||||
Some("http://192.0.2.2:8710/manager/actions"),
|
||||
);
|
||||
|
||||
section("1C Company API");
|
||||
@@ -165,24 +165,24 @@ fn run_proxmox_remote() -> Result<i32> {
|
||||
&mut counts,
|
||||
&no_redirect_http,
|
||||
"1C root redirect",
|
||||
"http://10.10.10.2:8710/",
|
||||
"http://192.0.2.2:8710/",
|
||||
&[307],
|
||||
);
|
||||
for (name, url) in [
|
||||
("1C /health", "http://10.10.10.2:8710/health"),
|
||||
("1C /api/health", "http://10.10.10.2:8710/api/health"),
|
||||
("1C manager brief", "http://10.10.10.2:8710/manager/brief"),
|
||||
("1C /health", "http://192.0.2.2:8710/health"),
|
||||
("1C /api/health", "http://192.0.2.2:8710/api/health"),
|
||||
("1C manager brief", "http://192.0.2.2:8710/manager/brief"),
|
||||
(
|
||||
"1C manager actions",
|
||||
"http://10.10.10.2:8710/manager/actions",
|
||||
"http://192.0.2.2:8710/manager/actions",
|
||||
),
|
||||
(
|
||||
"1C manager recovery",
|
||||
"http://10.10.10.2:8710/manager/recovery",
|
||||
"http://192.0.2.2:8710/manager/recovery",
|
||||
),
|
||||
(
|
||||
"1C weekly digest",
|
||||
"http://10.10.10.2:8710/manager/digest/weekly",
|
||||
"http://192.0.2.2:8710/manager/digest/weekly",
|
||||
),
|
||||
] {
|
||||
check_http_code(&mut counts, &http, name, url, &[200]);
|
||||
|
||||
@@ -9,7 +9,7 @@ use serde::Serialize;
|
||||
use serde_json::{Value, json};
|
||||
|
||||
const DEFAULT_DB_PATH: &str = "/var/lib/activitywatch/aw-server-rust/sqlite.db";
|
||||
const DEFAULT_HOST: &str = "SHARKON2025";
|
||||
const DEFAULT_HOST: &str = "HOST-EXAMPLE";
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(author, version, about = "Read-only ActivityWatch SQLite growth guard")]
|
||||
|
||||
@@ -11,7 +11,7 @@ use serde_json::Value;
|
||||
|
||||
const DEFAULT_DB_PATH: &str = "/var/lib/activitywatch/aw-server-rust/sqlite.db";
|
||||
const DEFAULT_BACKUP_DIR: &str = "/var/lib/activitywatch/backups/db";
|
||||
const DEFAULT_HOST: &str = "SHARKON2025";
|
||||
const DEFAULT_HOST: &str = "HOST-EXAMPLE";
|
||||
const ALLOWED_EVENT_TYPES: &[&str] = &["process_start", "process_stop"];
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
|
||||
@@ -472,14 +472,14 @@ mod tests {
|
||||
r#"
|
||||
# comment
|
||||
AW_EXPECT_START_OF_DAY="00:00"
|
||||
AW_EXPECT_LANDINGPAGE=/#/activity/SHARKON2025/view/
|
||||
AW_EXPECT_LANDINGPAGE=/#/activity/HOST-EXAMPLE/view/
|
||||
BAD KEY=value
|
||||
"#,
|
||||
);
|
||||
assert_eq!(parsed.get("AW_EXPECT_START_OF_DAY").unwrap(), "00:00");
|
||||
assert_eq!(
|
||||
parsed.get("AW_EXPECT_LANDINGPAGE").unwrap(),
|
||||
"/#/activity/SHARKON2025/view/"
|
||||
"/#/activity/HOST-EXAMPLE/view/"
|
||||
);
|
||||
assert!(!parsed.contains_key("BAD KEY"));
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ struct Cli {
|
||||
#[arg(long)]
|
||||
legacy_script: PathBuf,
|
||||
|
||||
#[arg(long, default_value = "10.10.10.13")]
|
||||
#[arg(long, default_value = "192.0.2.13")]
|
||||
server_host: String,
|
||||
|
||||
#[arg(long, default_value = "5600")]
|
||||
@@ -249,7 +249,7 @@ mod tests {
|
||||
let cli = Cli {
|
||||
kind: InstallKind::Client,
|
||||
legacy_script: script,
|
||||
server_host: "10.10.10.13".to_string(),
|
||||
server_host: "192.0.2.13".to_string(),
|
||||
server_port: "5600".to_string(),
|
||||
poll_interval: "5".to_string(),
|
||||
version: "0.13.2".to_string(),
|
||||
|
||||
@@ -25,10 +25,10 @@ struct Cli {
|
||||
#[arg(long, default_value = "http://127.0.0.1:5610")]
|
||||
worktime_api: String,
|
||||
|
||||
#[arg(long, default_value = "192.168.100.18")]
|
||||
#[arg(long, default_value = "198.51.100.18")]
|
||||
rdp_host: String,
|
||||
|
||||
#[arg(long, default_value = "SHARKON2025")]
|
||||
#[arg(long, default_value = "HOST-EXAMPLE")]
|
||||
rdp_hostname: String,
|
||||
|
||||
#[arg(long, default_value = "/var/lib/activitywatch/health")]
|
||||
|
||||
@@ -53,7 +53,7 @@ struct Cli {
|
||||
#[arg(long, default_value = "http://127.0.0.1:5610")]
|
||||
worktime_base: String,
|
||||
|
||||
#[arg(long, default_value = "SHARKON2025")]
|
||||
#[arg(long, default_value = "HOST-EXAMPLE")]
|
||||
host: String,
|
||||
|
||||
#[arg(long, default_value_t = 99.97)]
|
||||
|
||||
@@ -7,8 +7,8 @@ use clap::Parser;
|
||||
use reqwest::blocking::Client;
|
||||
use serde_json::Value;
|
||||
|
||||
const DEFAULT_SERVER: &str = "http://10.10.10.13:5600";
|
||||
const DEFAULT_HOST: &str = "SHARKON2025";
|
||||
const DEFAULT_SERVER: &str = "http://192.0.2.13:5600";
|
||||
const DEFAULT_HOST: &str = "HOST-EXAMPLE";
|
||||
const BUCKETS: &[&str] = &[
|
||||
"aw-dlp-endpoint-signals",
|
||||
"aw-dlp-incidents",
|
||||
@@ -410,7 +410,7 @@ fn get_json(client: &Client, url: &str, _timeout_seconds: u64) -> Result<Value>
|
||||
}
|
||||
|
||||
fn check_cors(_client: &Client, server: &str) -> u16 {
|
||||
let origin = "http://10.10.10.13:5600";
|
||||
let origin = "http://192.0.2.13:5600";
|
||||
let first = curl_status(&format!("{server}/api/0/settings/"), origin);
|
||||
if first == 200 {
|
||||
return first;
|
||||
@@ -532,7 +532,7 @@ mod tests {
|
||||
#[test]
|
||||
fn metadata_only_bucket_event_skips_missing_deep_event_read() {
|
||||
let index = serde_json::json!({
|
||||
"aw-watcher-window_SHARKON2025": {
|
||||
"aw-watcher-window_HOST-EXAMPLE": {
|
||||
"metadata": {
|
||||
"end": "2026-06-02T00:00:00Z"
|
||||
}
|
||||
@@ -540,7 +540,7 @@ mod tests {
|
||||
});
|
||||
let event = bucket_event(
|
||||
"http://127.0.0.1:1",
|
||||
"aw-watcher-window_SHARKON2025",
|
||||
"aw-watcher-window_HOST-EXAMPLE",
|
||||
Some(&index),
|
||||
false,
|
||||
1,
|
||||
|
||||
@@ -7,9 +7,9 @@ use clap::Parser;
|
||||
use reqwest::blocking::Client;
|
||||
use serde_json::Value;
|
||||
|
||||
const DEFAULT_SERVER: &str = "http://10.10.10.13:5600";
|
||||
const DEFAULT_HOST: &str = "SHARKON2025";
|
||||
const DEFAULT_RDP_HOST: &str = "192.168.100.18";
|
||||
const DEFAULT_SERVER: &str = "http://192.0.2.13:5600";
|
||||
const DEFAULT_HOST: &str = "HOST-EXAMPLE";
|
||||
const DEFAULT_RDP_HOST: &str = "198.51.100.18";
|
||||
const BUCKETS: &[(&str, &str)] = &[
|
||||
("aw-watcher-afk", "AFK watcher"),
|
||||
("aw-watcher-window", "Window watcher"),
|
||||
@@ -423,7 +423,7 @@ fn check_cors(client: &Client, server: &str) -> u16 {
|
||||
let url = format!("{server}/api/0/settings/");
|
||||
client
|
||||
.get(&url)
|
||||
.header("Origin", "http://10.10.10.13:5600")
|
||||
.header("Origin", "http://192.0.2.13:5600")
|
||||
.send()
|
||||
.map(|response| response.status().as_u16())
|
||||
.unwrap_or(0)
|
||||
|
||||
@@ -270,9 +270,9 @@ mod tests {
|
||||
let kit = root.join("install-kit-awindows-20260427-211240");
|
||||
fs::create_dir_all(&kit).unwrap();
|
||||
fs::write(kit.join("unexpected.txt"), "kit").unwrap();
|
||||
fs::create_dir_all(kit.join("server-configs-192.168.100.18")).unwrap();
|
||||
fs::create_dir_all(kit.join("server-configs-198.51.100.18")).unwrap();
|
||||
fs::write(
|
||||
kit.join("server-configs-192.168.100.18/config.deployment-config.json"),
|
||||
kit.join("server-configs-198.51.100.18/config.deployment-config.json"),
|
||||
"{}",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -12,10 +12,10 @@ use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
|
||||
use serde::Serialize;
|
||||
use serde_json::Value;
|
||||
|
||||
const DEFAULT_AW_API: &str = "http://10.10.10.13:5600/api/0";
|
||||
const DEFAULT_WORKTIME_URL: &str = "http://10.10.10.13:5610";
|
||||
const DEFAULT_ONE_C_URL: &str = "http://10.10.10.2:8710";
|
||||
const DEFAULT_HOSTNAME: &str = "SHARKON2025";
|
||||
const DEFAULT_AW_API: &str = "http://192.0.2.13:5600/api/0";
|
||||
const DEFAULT_WORKTIME_URL: &str = "http://192.0.2.13:5610";
|
||||
const DEFAULT_ONE_C_URL: &str = "http://192.0.2.2:8710";
|
||||
const DEFAULT_HOSTNAME: &str = "HOST-EXAMPLE";
|
||||
const DEFAULT_GATEWAY_HOST: &str = "detmir.example.local";
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
@@ -298,13 +298,13 @@ fn service_checks(args: &Cli) -> Vec<ServiceCheck> {
|
||||
}
|
||||
|
||||
checks.push(tcp_check(
|
||||
"192.168.100.18",
|
||||
"198.51.100.18",
|
||||
5985,
|
||||
args.tcp_timeout_seconds,
|
||||
true,
|
||||
));
|
||||
checks.push(tcp_check(
|
||||
"192.168.100.18",
|
||||
"198.51.100.18",
|
||||
22,
|
||||
args.tcp_timeout_seconds,
|
||||
true,
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::process::Command;
|
||||
use anyhow::{Context, Result};
|
||||
use clap::Parser;
|
||||
|
||||
const DEFAULT_SSH_TARGET: &str = "igor@10.10.10.13";
|
||||
const DEFAULT_SSH_TARGET: &str = "igor@192.0.2.13";
|
||||
const DEFAULT_REMOTE_COMMAND: &str = "sudo -n /usr/local/bin/dlp-health-check --json";
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
|
||||
@@ -12,7 +12,7 @@ use serde_json::{Value, json};
|
||||
const DEFAULT_GRAFANA_URL: &str = "http://127.0.0.1:3000";
|
||||
const DEFAULT_DASHBOARD_UID: &str = "detmir-aw-main";
|
||||
const DEFAULT_DASHBOARD_FILE: &str = "/etc/grafana/provisioning/dashboards/aw/detmir-aw-main.json";
|
||||
const DEFAULT_HOST: &str = "SHARKON2025";
|
||||
const DEFAULT_HOST: &str = "HOST-EXAMPLE";
|
||||
const OLD_MEASUREMENTS: &[&str] = &["aw_window_event", "aw_afk_event"];
|
||||
const REQUIRED_MEASUREMENTS: &[&str] = &[
|
||||
"aw_rdp_worktime_hourly",
|
||||
@@ -670,9 +670,9 @@ mod tests {
|
||||
assert_eq!(
|
||||
render_query_vars(
|
||||
"r.host == \"${host}\" or r.host == \"$host\"",
|
||||
"SHARKON2025"
|
||||
"HOST-EXAMPLE"
|
||||
),
|
||||
"r.host == \"SHARKON2025\" or r.host == \"SHARKON2025\""
|
||||
"r.host == \"HOST-EXAMPLE\" or r.host == \"HOST-EXAMPLE\""
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ use clap::Parser;
|
||||
use detmir_core::{exit_codes, now_utc_rfc3339};
|
||||
use serde::Serialize;
|
||||
|
||||
const DEFAULT_SSH_TARGET: &str = "igor@10.10.10.13";
|
||||
const DEFAULT_SSH_TARGET: &str = "igor@192.0.2.13";
|
||||
const REQUIRED_UNITS: &[&str] = &[
|
||||
"activitywatch-server.service",
|
||||
"aw-worktime-api.service",
|
||||
|
||||
@@ -52,14 +52,14 @@ struct Cli {
|
||||
|
||||
#[arg(
|
||||
long,
|
||||
default_value = "http://10.10.10.13:5610",
|
||||
default_value = "http://192.0.2.13:5610",
|
||||
env = "DETMIR_PORTAL_WORKTIME_URL"
|
||||
)]
|
||||
worktime_url: String,
|
||||
|
||||
#[arg(
|
||||
long,
|
||||
default_value = "http://10.10.10.2:8710",
|
||||
default_value = "http://192.0.2.2:8710",
|
||||
env = "DETMIR_PORTAL_ONE_C_URL"
|
||||
)]
|
||||
one_c_url: String,
|
||||
@@ -1836,7 +1836,7 @@ fn links() -> PortalLinks {
|
||||
portal: "/portal/".to_string(),
|
||||
grafana_dashboards: "/dashboards".to_string(),
|
||||
detmir_activitywatch:
|
||||
"/d/detmir-aw-main/detmir-activitywatch?orgId=1&from=now-48h&to=now&timezone=browser&var-host=SHARKON2025&refresh=5m"
|
||||
"/d/detmir-aw-main/detmir-activitywatch?orgId=1&from=now-48h&to=now&timezone=browser&var-host=HOST-EXAMPLE&refresh=5m"
|
||||
.to_string(),
|
||||
dlp_security_dashboard:
|
||||
"/d/detmir-dlp-security?orgId=1&from=now-30d&to=now&timezone=browser".to_string(),
|
||||
@@ -1845,7 +1845,7 @@ fn links() -> PortalLinks {
|
||||
dlp_overview_dashboard:
|
||||
"/d/awatch-dlp-overview?orgId=1&from=now-30d&to=now&timezone=browser".to_string(),
|
||||
aw_ui: "/r/aw/".to_string(),
|
||||
worktime_report: "/reports/worktime/management?format=html&host=SHARKON2025".to_string(),
|
||||
worktime_report: "/reports/worktime/management?format=html&host=HOST-EXAMPLE".to_string(),
|
||||
file1c_brief: "/r/file1c/brief".to_string(),
|
||||
file1c_actions: "/r/file1c/actions".to_string(),
|
||||
}
|
||||
|
||||
@@ -136,20 +136,20 @@ fn seed_server_dlp_events(inventory: &str) -> Result<()> {
|
||||
let ts = Utc::now().format("%Y-%m-%dT%H:%M:%SZ");
|
||||
let script = format!(
|
||||
r#"cat >/tmp/aw-endpoint-seed.json <<'JSON'
|
||||
{{"timestamp":"{ts}","duration":0.0,"data":{{"hostname":"SHARKON2025","signalType":"self_test","source":"diag_and_manual_restart","username":"system","queueDepth":0,"eventsEnqueued":0,"eventsFlushed":0,"sendFailures":0}}}}
|
||||
{{"timestamp":"{ts}","duration":0.0,"data":{{"hostname":"HOST-EXAMPLE","signalType":"self_test","source":"diag_and_manual_restart","username":"system","queueDepth":0,"eventsEnqueued":0,"eventsFlushed":0,"sendFailures":0}}}}
|
||||
JSON
|
||||
cat >/tmp/aw-fileops-seed-host.json <<'JSON'
|
||||
{{"timestamp":"{ts}","duration":0.0,"data":{{"hostname":"SHARKON2025","operation":"self_test","source":"diag_and_manual_restart"}}}}
|
||||
{{"timestamp":"{ts}","duration":0.0,"data":{{"hostname":"HOST-EXAMPLE","operation":"self_test","source":"diag_and_manual_restart"}}}}
|
||||
JSON
|
||||
cat >/tmp/aw-fileops-seed-server.json <<'JSON'
|
||||
{{"timestamp":"{ts}","duration":0.0,"data":{{"hostname":"10.10.10.13","operation":"self_test","source":"diag_and_manual_restart"}}}}
|
||||
{{"timestamp":"{ts}","duration":0.0,"data":{{"hostname":"192.0.2.13","operation":"self_test","source":"diag_and_manual_restart"}}}}
|
||||
JSON
|
||||
curl -sS -X POST 'http://127.0.0.1:5600/api/0/buckets/aw-dlp-endpoint-signals_SHARKON2025' -H 'Content-Type: application/json' -d '{{"client":"aw-dlp-endpoint-signals","type":"aw.dlp.endpoint.signal","hostname":"SHARKON2025"}}' >/dev/null 2>&1 || true
|
||||
curl -sS -X POST 'http://127.0.0.1:5600/api/0/buckets/aw-file-operations_SHARKON2025' -H 'Content-Type: application/json' -d '{{"client":"aw-file-operations","type":"aw.file.operation","hostname":"SHARKON2025"}}' >/dev/null 2>&1 || true
|
||||
curl -sS -X POST 'http://127.0.0.1:5600/api/0/buckets/aw-file-operations_10.10.10.13' -H 'Content-Type: application/json' -d '{{"client":"aw-file-operations","type":"aw.file.operation","hostname":"10.10.10.13"}}' >/dev/null 2>&1 || true
|
||||
curl -sS -X POST 'http://127.0.0.1:5600/api/0/buckets/aw-dlp-endpoint-signals_SHARKON2025/heartbeat?pulsetime=30' -H 'Content-Type: application/json' --data-binary @/tmp/aw-endpoint-seed.json >/dev/null
|
||||
curl -sS -X POST 'http://127.0.0.1:5600/api/0/buckets/aw-file-operations_SHARKON2025/heartbeat?pulsetime=30' -H 'Content-Type: application/json' --data-binary @/tmp/aw-fileops-seed-host.json >/dev/null
|
||||
curl -sS -X POST 'http://127.0.0.1:5600/api/0/buckets/aw-file-operations_10.10.10.13/heartbeat?pulsetime=30' -H 'Content-Type: application/json' --data-binary @/tmp/aw-fileops-seed-server.json >/dev/null
|
||||
curl -sS -X POST 'http://127.0.0.1:5600/api/0/buckets/aw-dlp-endpoint-signals_HOST-EXAMPLE' -H 'Content-Type: application/json' -d '{{"client":"aw-dlp-endpoint-signals","type":"aw.dlp.endpoint.signal","hostname":"HOST-EXAMPLE"}}' >/dev/null 2>&1 || true
|
||||
curl -sS -X POST 'http://127.0.0.1:5600/api/0/buckets/aw-file-operations_HOST-EXAMPLE' -H 'Content-Type: application/json' -d '{{"client":"aw-file-operations","type":"aw.file.operation","hostname":"HOST-EXAMPLE"}}' >/dev/null 2>&1 || true
|
||||
curl -sS -X POST 'http://127.0.0.1:5600/api/0/buckets/aw-file-operations_192.0.2.13' -H 'Content-Type: application/json' -d '{{"client":"aw-file-operations","type":"aw.file.operation","hostname":"192.0.2.13"}}' >/dev/null 2>&1 || true
|
||||
curl -sS -X POST 'http://127.0.0.1:5600/api/0/buckets/aw-dlp-endpoint-signals_HOST-EXAMPLE/heartbeat?pulsetime=30' -H 'Content-Type: application/json' --data-binary @/tmp/aw-endpoint-seed.json >/dev/null
|
||||
curl -sS -X POST 'http://127.0.0.1:5600/api/0/buckets/aw-file-operations_HOST-EXAMPLE/heartbeat?pulsetime=30' -H 'Content-Type: application/json' --data-binary @/tmp/aw-fileops-seed-host.json >/dev/null
|
||||
curl -sS -X POST 'http://127.0.0.1:5600/api/0/buckets/aw-file-operations_192.0.2.13/heartbeat?pulsetime=30' -H 'Content-Type: application/json' --data-binary @/tmp/aw-fileops-seed-server.json >/dev/null
|
||||
"#
|
||||
);
|
||||
let _ = ansible_command(
|
||||
@@ -170,7 +170,7 @@ fn restart_windows_collectors(inventory: &str) {
|
||||
|
||||
fn seed_windows_dlp_events(inventory: &str) {
|
||||
log("Seeding endpoint/file-ops events from aw_windows...");
|
||||
let script = r#"powershell -NoProfile -ExecutionPolicy Bypass -Command "$ErrorActionPreference = 'Stop'; $ts = (Get-Date).ToUniversalTime().ToString('o'); $api='http://10.10.10.13:5600/api/0'; $endpoint=@{timestamp=$ts;duration=0.0;data=@{hostname='SHARKON2025';signalType='self_test';source='diag_and_manual_restart';username=$env:USERNAME;queueDepth=0;eventsEnqueued=0;eventsFlushed=0;sendFailures=0}} | ConvertTo-Json -Depth 8 -Compress; $fileops=@{timestamp=$ts;duration=0.0;data=@{hostname='SHARKON2025';operation='self_test';source='diag_and_manual_restart';username=$env:USERNAME}} | ConvertTo-Json -Depth 8 -Compress; Invoke-RestMethod -Method Post -Uri $api'/buckets/aw-dlp-endpoint-signals_SHARKON2025' -ContentType 'application/json' -Body '{\"client\":\"aw-dlp-endpoint-signals\",\"type\":\"aw.dlp.endpoint.signal\",\"hostname\":\"SHARKON2025\"}' -TimeoutSec 15 -DisableKeepAlive -ErrorAction SilentlyContinue | Out-Null; Invoke-RestMethod -Method Post -Uri $api'/buckets/aw-file-operations_SHARKON2025' -ContentType 'application/json' -Body '{\"client\":\"aw-file-operations\",\"type\":\"aw.file.operation\",\"hostname\":\"SHARKON2025\"}' -TimeoutSec 15 -DisableKeepAlive -ErrorAction SilentlyContinue | Out-Null; Invoke-RestMethod -Method Post -Uri $api'/buckets/aw-dlp-endpoint-signals_SHARKON2025/heartbeat?pulsetime=30' -ContentType 'application/json' -Body $endpoint -TimeoutSec 15 -DisableKeepAlive | Out-Null; Invoke-RestMethod -Method Post -Uri $api'/buckets/aw-file-operations_SHARKON2025/heartbeat?pulsetime=30' -ContentType 'application/json' -Body $fileops -TimeoutSec 15 -DisableKeepAlive | Out-Null; Write-Output 'windows-dlp-seeded'""#;
|
||||
let script = r#"powershell -NoProfile -ExecutionPolicy Bypass -Command "$ErrorActionPreference = 'Stop'; $ts = (Get-Date).ToUniversalTime().ToString('o'); $api='http://192.0.2.13:5600/api/0'; $endpoint=@{timestamp=$ts;duration=0.0;data=@{hostname='HOST-EXAMPLE';signalType='self_test';source='diag_and_manual_restart';username=$env:USERNAME;queueDepth=0;eventsEnqueued=0;eventsFlushed=0;sendFailures=0}} | ConvertTo-Json -Depth 8 -Compress; $fileops=@{timestamp=$ts;duration=0.0;data=@{hostname='HOST-EXAMPLE';operation='self_test';source='diag_and_manual_restart';username=$env:USERNAME}} | ConvertTo-Json -Depth 8 -Compress; Invoke-RestMethod -Method Post -Uri $api'/buckets/aw-dlp-endpoint-signals_HOST-EXAMPLE' -ContentType 'application/json' -Body '{\"client\":\"aw-dlp-endpoint-signals\",\"type\":\"aw.dlp.endpoint.signal\",\"hostname\":\"HOST-EXAMPLE\"}' -TimeoutSec 15 -DisableKeepAlive -ErrorAction SilentlyContinue | Out-Null; Invoke-RestMethod -Method Post -Uri $api'/buckets/aw-file-operations_HOST-EXAMPLE' -ContentType 'application/json' -Body '{\"client\":\"aw-file-operations\",\"type\":\"aw.file.operation\",\"hostname\":\"HOST-EXAMPLE\"}' -TimeoutSec 15 -DisableKeepAlive -ErrorAction SilentlyContinue | Out-Null; Invoke-RestMethod -Method Post -Uri $api'/buckets/aw-dlp-endpoint-signals_HOST-EXAMPLE/heartbeat?pulsetime=30' -ContentType 'application/json' -Body $endpoint -TimeoutSec 15 -DisableKeepAlive | Out-Null; Invoke-RestMethod -Method Post -Uri $api'/buckets/aw-file-operations_HOST-EXAMPLE/heartbeat?pulsetime=30' -ContentType 'application/json' -Body $fileops -TimeoutSec 15 -DisableKeepAlive | Out-Null; Write-Output 'windows-dlp-seeded'""#;
|
||||
let _ = ansible_windows_shell(inventory, script);
|
||||
}
|
||||
|
||||
|
||||
@@ -1513,18 +1513,18 @@ mod tests {
|
||||
#[test]
|
||||
fn transport_counter_key_separates_sessions() {
|
||||
let data = json!({
|
||||
"hostname": "SHARKON2025",
|
||||
"hostname": "HOST-EXAMPLE",
|
||||
"sessionId": 4,
|
||||
"username": "USER4"
|
||||
});
|
||||
assert_eq!(
|
||||
transport_counter_key(
|
||||
"file-operations",
|
||||
"aw-file-operations_SHARKON2025",
|
||||
"aw-file-operations_HOST-EXAMPLE",
|
||||
&data,
|
||||
"sendFailures"
|
||||
),
|
||||
"file-operations:aw-file-operations_SHARKON2025:SHARKON2025:4:USER4:sendFailures"
|
||||
"file-operations:aw-file-operations_HOST-EXAMPLE:HOST-EXAMPLE:4:USER4:sendFailures"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const DEFAULT_AW_API_BASE: &str = "http://127.0.0.1:5600/api/0";
|
||||
const DEFAULT_CASE_API_BASE: &str = "http://127.0.0.1:5602/api/0/dlp/cases";
|
||||
const DEFAULT_INFLUX_ORG: &str = "proxmox";
|
||||
const DEFAULT_INFLUX_BUCKET: &str = "aw_metrics";
|
||||
const DEFAULT_HOSTS: &str = "SHARKON2025";
|
||||
const DEFAULT_HOSTS: &str = "HOST-EXAMPLE";
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(about = "AW DLP InfluxDB exporter")]
|
||||
@@ -1009,7 +1009,7 @@ mod tests {
|
||||
"id": 10,
|
||||
"timestamp": "2026-05-15T10:00:00Z",
|
||||
"data": {
|
||||
"hostname": "SHARKON2025",
|
||||
"hostname": "HOST-EXAMPLE",
|
||||
"username": "Администратор",
|
||||
"signalType": "self_test",
|
||||
"policyMode": "server",
|
||||
@@ -1025,7 +1025,7 @@ mod tests {
|
||||
"id": 11,
|
||||
"timestamp": "2026-05-15T10:01:00Z",
|
||||
"data": {
|
||||
"hostname": "SHARKON2025",
|
||||
"hostname": "HOST-EXAMPLE",
|
||||
"username": "Администратор",
|
||||
"signalType": "print_job",
|
||||
"source": "endpoint-signals-phase2",
|
||||
@@ -1034,7 +1034,7 @@ mod tests {
|
||||
}
|
||||
}),
|
||||
];
|
||||
let lines = build_endpoint_lines("SHARKON2025", &events);
|
||||
let lines = build_endpoint_lines("HOST-EXAMPLE", &events);
|
||||
assert!(
|
||||
lines
|
||||
.iter()
|
||||
@@ -1048,13 +1048,13 @@ mod tests {
|
||||
let item = json!({
|
||||
"timestamp": "2026-05-15T10:02:00Z",
|
||||
"data": {
|
||||
"host": "SHARKON2025",
|
||||
"host": "HOST-EXAMPLE",
|
||||
"incident": {"status": "open", "verdict": "incident"},
|
||||
"sourceBucket": "aw-dlp-endpoint-signals_SHARKON2025",
|
||||
"sourceBucket": "aw-dlp-endpoint-signals_HOST-EXAMPLE",
|
||||
"sourceEvent": {
|
||||
"data": {
|
||||
"signalType": "print_job",
|
||||
"hostname": "SHARKON2025",
|
||||
"hostname": "HOST-EXAMPLE",
|
||||
"username": "Администратор",
|
||||
"documentName": "Письмо",
|
||||
"printerName": "HP",
|
||||
@@ -1063,7 +1063,7 @@ mod tests {
|
||||
}
|
||||
}
|
||||
});
|
||||
let normalized = normalize_incident(&item, "SHARKON2025");
|
||||
let normalized = normalize_incident(&item, "HOST-EXAMPLE");
|
||||
assert_eq!(normalized.signal_type, "print_job");
|
||||
assert_eq!(normalized.username, "Администратор");
|
||||
assert_eq!(normalized.action, "incident");
|
||||
@@ -1074,7 +1074,7 @@ mod tests {
|
||||
fn case_lines_emit_case_state() {
|
||||
let cases = vec![json!({
|
||||
"id": 28,
|
||||
"host": "SHARKON2025",
|
||||
"host": "HOST-EXAMPLE",
|
||||
"status": "open",
|
||||
"severity": "medium",
|
||||
"assignee": null,
|
||||
@@ -1084,7 +1084,7 @@ mod tests {
|
||||
"forensics": null,
|
||||
"updated_at": "2026-05-15T10:03:00+00:00"
|
||||
})];
|
||||
let lines = build_case_lines("SHARKON2025", &cases);
|
||||
let lines = build_case_lines("HOST-EXAMPLE", &cases);
|
||||
assert_eq!(lines.len(), 1);
|
||||
assert!(lines[0].starts_with("aw_dlp_case,"));
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@ use chrono::Local;
|
||||
use clap::Parser;
|
||||
use serde::Serialize;
|
||||
|
||||
const DEFAULT_SERVER_HOST: &str = "10.10.10.13";
|
||||
const DEFAULT_SERVER_HOST: &str = "192.0.2.13";
|
||||
const DEFAULT_SERVER_USER: &str = "igor";
|
||||
const DEFAULT_LEGACY_DB: &str = "/root/.local/share/activitywatch/aw-server-rust/sqlite.db";
|
||||
const DEFAULT_LEGACY_DB: &str = "/var/lib/activitywatch/aw-server-rust/sqlite.db";
|
||||
const DEFAULT_TARGET_DB: &str =
|
||||
"/var/lib/activitywatch/.local/share/activitywatch/aw-server-rust/sqlite.db";
|
||||
const DEFAULT_REMOTE_MERGE_BIN: &str = "/tmp/merge-aw-server-dbs";
|
||||
@@ -454,7 +454,7 @@ mod tests {
|
||||
dir.path(),
|
||||
&dir.path().join("private-config/runtime.env"),
|
||||
&env,
|
||||
"10.10.10.13",
|
||||
"192.0.2.13",
|
||||
"igor",
|
||||
"20260602-000000",
|
||||
"/var/lib/activitywatch/backups/prod-restore-20260602-000000",
|
||||
|
||||
@@ -10,8 +10,8 @@ use reqwest::blocking::Client;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
const DEFAULT_AW_BASE_URL: &str = "http://10.10.10.13:5600/api/0";
|
||||
const DEFAULT_HOST: &str = "SHARKON2025";
|
||||
const DEFAULT_AW_BASE_URL: &str = "http://192.0.2.13:5600/api/0";
|
||||
const DEFAULT_HOST: &str = "HOST-EXAMPLE";
|
||||
const DEFAULT_SAMPLE_SECONDS: f64 = 30.0;
|
||||
const DEFAULT_MAX_SAMPLE_SECONDS: f64 = 300.0;
|
||||
const DEFAULT_OUT_DIR: &str = "reports";
|
||||
@@ -550,7 +550,7 @@ mod tests {
|
||||
];
|
||||
let rows = build_rows(
|
||||
events,
|
||||
"SHARKON2025",
|
||||
"HOST-EXAMPLE",
|
||||
NaiveDate::from_ymd_opt(2026, 6, 1).unwrap(),
|
||||
NaiveDate::from_ymd_opt(2026, 6, 1).unwrap(),
|
||||
30.0,
|
||||
@@ -563,7 +563,7 @@ mod tests {
|
||||
assert_eq!(rows[0].sessions_count, 2);
|
||||
assert_eq!(rows[0].samples_count, 3);
|
||||
assert_eq!(rows[0].active_samples, 2);
|
||||
assert_eq!(rows[0].user_id, "SHARKON2025\\user5");
|
||||
assert_eq!(rows[0].user_id, "HOST-EXAMPLE\\user5");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -388,7 +388,7 @@ fn suggestions_from_failures(failures: &[String]) -> Vec<String> {
|
||||
}
|
||||
if text.contains("pfsense_web") {
|
||||
suggestions
|
||||
.push("Проверить доступность pfSense 10.10.10.1:8443, перезапустить WebGUI/nginx.");
|
||||
.push("Проверить доступность pfSense 192.0.2.1:8443, перезапустить WebGUI/nginx.");
|
||||
}
|
||||
if text.contains("pfsense_mcp") {
|
||||
suggestions.push("Проверить локальный pfsense-mcp-server.service, bearer token и endpoint 127.0.0.1:3010/mcp.");
|
||||
@@ -397,18 +397,18 @@ fn suggestions_from_failures(failures: &[String]) -> Vec<String> {
|
||||
suggestions.push("Проверить контейнер InfluxDB и restart сервиса influxdb.");
|
||||
}
|
||||
if text.contains("grafana") {
|
||||
suggestions.push("Проверить grafana-server и NO_PROXY для 10.10.10.0/24.");
|
||||
suggestions.push("Проверить grafana-server и NO_PROXY для 192.0.2.0/24.");
|
||||
}
|
||||
if text.contains("loki") || text.contains("alloy") {
|
||||
suggestions.push("Проверить LXC логов и restart сервисов loki/alloy.");
|
||||
}
|
||||
if text.contains("aw-rus:watcher-") || text.contains("aw-rus:worktime:") {
|
||||
suggestions.push("Проверить Windows collector recovery: worktime-session-collector, ActivityWatch Recovery и Launch tasks на 192.168.100.18.");
|
||||
suggestions.push("Проверить Windows collector recovery: worktime-session-collector, ActivityWatch Recovery и Launch tasks на 198.51.100.18.");
|
||||
suggestions.push("После Windows recovery проверить server-side aw-worktime-autoheal/ui-bridge для пересборки afk/window bucket'ов.");
|
||||
}
|
||||
if text.contains("aw-rus:dlp-") {
|
||||
suggestions.push(
|
||||
"Проверить DLP endpoint/fileops collectors и server-side DLP transport на 10.10.10.13.",
|
||||
"Проверить DLP endpoint/fileops collectors и server-side DLP transport на 192.0.2.13.",
|
||||
);
|
||||
}
|
||||
if text.contains("filesystem_usage") {
|
||||
|
||||
@@ -11,7 +11,8 @@ use serde::Serialize;
|
||||
const DEFAULT_HEARTBEAT_FILE: &str = "/opt/infra-admin/.state/tsj_guardian_heartbeat";
|
||||
const DEFAULT_SERVICE_NAME: &str = "tsj-guardian-bot.service";
|
||||
const DEFAULT_GOST_SERVICE_NAME: &str = "gost-tg.service";
|
||||
const DEFAULT_GOST_PATTERN: &str = "/usr/local/bin/gost -L http+socks5://127.0.0.1:11090 -F socks5+wss://gw.example.local:4443";
|
||||
const DEFAULT_GOST_PATTERN: &str =
|
||||
"/usr/local/bin/gost -L http+socks5://127.0.0.1:11090 -F socks5+wss://gw.example.local:4443";
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(about = "TSJ Guardian bot heartbeat watchdog and gost duplicate guard.")]
|
||||
|
||||
@@ -22,7 +22,7 @@ use tiny_http::{Header, Method, Request, Response, Server, StatusCode};
|
||||
use url::form_urlencoded;
|
||||
|
||||
const DEFAULT_AW_URL: &str = "http://127.0.0.1:5600";
|
||||
const DEFAULT_HOST: &str = "SHARKON2025";
|
||||
const DEFAULT_HOST: &str = "HOST-EXAMPLE";
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(about = "AW Worktime Report API")]
|
||||
@@ -2536,7 +2536,7 @@ mod tests {
|
||||
],
|
||||
start,
|
||||
end,
|
||||
"SHARKON2025",
|
||||
"HOST-EXAMPLE",
|
||||
false,
|
||||
);
|
||||
assert_eq!(rows[0]["active_seconds"], 60);
|
||||
|
||||
@@ -17,7 +17,7 @@ use serde::{Deserialize, Serialize};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
const DEFAULT_AW_URL: &str = "http://127.0.0.1:5600";
|
||||
const DEFAULT_HOST: &str = "SHARKON2025";
|
||||
const DEFAULT_HOST: &str = "HOST-EXAMPLE";
|
||||
const AUTOHEAL_SOURCE: &str = "aw-worktime-autoheal";
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
|
||||
@@ -11,7 +11,7 @@ const DEFAULT_AW_BASE: &str = "http://127.0.0.1:5600";
|
||||
const DEFAULT_WORKTIME_REPORT_BASE: &str = "http://127.0.0.1:5610";
|
||||
const DEFAULT_INFLUX_ORG: &str = "proxmox";
|
||||
const DEFAULT_INFLUX_BUCKET: &str = "aw_metrics";
|
||||
const DEFAULT_HOST: &str = "SHARKON2025";
|
||||
const DEFAULT_HOST: &str = "HOST-EXAMPLE";
|
||||
const DEFAULT_DAYS: &str = "today,yesterday";
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
@@ -993,14 +993,14 @@ mod tests {
|
||||
fn heartbeat_line_uses_current_exporter_timestamp() {
|
||||
let out = line(
|
||||
"aw_worktime_exporter_heartbeat",
|
||||
vec![("host", "SHARKON2025".to_string())],
|
||||
vec![("host", "HOST-EXAMPLE".to_string())],
|
||||
vec![("run", FieldValue::Int(1))],
|
||||
42,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
out,
|
||||
"aw_worktime_exporter_heartbeat,host=SHARKON2025 run=1i 42"
|
||||
"aw_worktime_exporter_heartbeat,host=HOST-EXAMPLE run=1i 42"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1023,11 +1023,11 @@ mod tests {
|
||||
}
|
||||
})];
|
||||
|
||||
let daily = aggregate_daily_rows(&events, start, end_exclusive, "SHARKON2025", &config);
|
||||
let hourly = aggregate_hourly_rows(&events, end_exclusive, "SHARKON2025", &config);
|
||||
let daily = aggregate_daily_rows(&events, start, end_exclusive, "HOST-EXAMPLE", &config);
|
||||
let hourly = aggregate_hourly_rows(&events, end_exclusive, "HOST-EXAMPLE", &config);
|
||||
|
||||
assert_eq!(daily.len(), 1);
|
||||
assert_eq!(daily[0].user_id, "SHARKON2025\\user5");
|
||||
assert_eq!(daily[0].user_id, "HOST-EXAMPLE\\user5");
|
||||
assert_eq!(daily[0].active_seconds, 300);
|
||||
assert_eq!(daily[0].active_samples, 1);
|
||||
assert_eq!(hourly.len(), 1);
|
||||
|
||||
@@ -7,7 +7,7 @@ use reqwest::blocking::Client;
|
||||
use serde::Serialize;
|
||||
|
||||
const DEFAULT_BASE_URL: &str = "http://127.0.0.1:5610";
|
||||
const DEFAULT_HOST: &str = "SHARKON2025";
|
||||
const DEFAULT_HOST: &str = "HOST-EXAMPLE";
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(about = "AW Worktime report cache prewarm")]
|
||||
|
||||
@@ -15,7 +15,7 @@ use serde::{Deserialize, Serialize};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
const DEFAULT_AW_URL: &str = "http://127.0.0.1:5600";
|
||||
const DEFAULT_HOST: &str = "SHARKON2025";
|
||||
const DEFAULT_HOST: &str = "HOST-EXAMPLE";
|
||||
const DEFAULT_STATE_PATH: &str = "/var/lib/activitywatch/aw-worktime-ui-bridge-state.json";
|
||||
const BRIDGE_SOURCE: &str = "aw-worktime-ui-bridge";
|
||||
|
||||
|
||||
@@ -451,9 +451,9 @@
|
||||
- { src: "{{ aw_repo_root }}/aw-server/aw-worktime-panel.js", dest: "{{ aw_server_webui_dir }}/js/aw-worktime-panel.js", mode: "0644" }
|
||||
- { src: "{{ aw_repo_root }}/aw-server/aw-host-groups.json", dest: "{{ aw_server_webui_dir }}/js/aw-host-groups.json", mode: "0644" }
|
||||
|
||||
- name: Создать каталог /root/bootstrap для apply_webui_ru_patch.sh
|
||||
- name: Создать каталог /opt/detmir/bootstrap для apply_webui_ru_patch.sh
|
||||
ansible.builtin.file:
|
||||
path: /root/bootstrap
|
||||
path: /opt/detmir/bootstrap
|
||||
state: directory
|
||||
mode: "0755"
|
||||
|
||||
@@ -463,10 +463,10 @@
|
||||
dest: "{{ item.dest }}"
|
||||
mode: "{{ item.mode }}"
|
||||
loop:
|
||||
- { src: "{{ aw_repo_root }}/aw-server/aw-ru-patch.js", dest: "/root/bootstrap/aw-ru-patch.js", mode: "0644" }
|
||||
- { src: "{{ aw_repo_root }}/aw-server/aw-sw-cleanup.js", dest: "/root/bootstrap/aw-sw-cleanup.js", mode: "0644" }
|
||||
- { src: "{{ aw_repo_root }}/aw-server/aw-worktime-panel.js", dest: "/root/bootstrap/aw-worktime-panel.js", mode: "0644" }
|
||||
- { src: "{{ aw_repo_root }}/aw-server/aw-host-groups.json", dest: "/root/bootstrap/aw-host-groups.json", mode: "0644" }
|
||||
- { src: "{{ aw_repo_root }}/aw-server/aw-ru-patch.js", dest: "/opt/detmir/bootstrap/aw-ru-patch.js", mode: "0644" }
|
||||
- { src: "{{ aw_repo_root }}/aw-server/aw-sw-cleanup.js", dest: "/opt/detmir/bootstrap/aw-sw-cleanup.js", mode: "0644" }
|
||||
- { src: "{{ aw_repo_root }}/aw-server/aw-worktime-panel.js", dest: "/opt/detmir/bootstrap/aw-worktime-panel.js", mode: "0644" }
|
||||
- { src: "{{ aw_repo_root }}/aw-server/aw-host-groups.json", dest: "/opt/detmir/bootstrap/aw-host-groups.json", mode: "0644" }
|
||||
|
||||
- name: Скопировать apply_webui_ru_patch.sh скрипт
|
||||
ansible.builtin.copy:
|
||||
@@ -655,14 +655,14 @@
|
||||
AW_WORKTIME_INFLUX_URL={{ aw_worktime_influx_url | default('') }}
|
||||
AW_WORKTIME_INFLUX_ORG={{ aw_worktime_influx_org | default('proxmox') }}
|
||||
AW_WORKTIME_INFLUX_BUCKET={{ aw_worktime_influx_bucket | default('aw_metrics') }}
|
||||
AW_WORKTIME_INFLUX_HOSTS={{ aw_worktime_influx_hosts | default('SHARKON2025') }}
|
||||
AW_WORKTIME_INFLUX_HOSTS={{ aw_worktime_influx_hosts | default('HOST-EXAMPLE') }}
|
||||
AW_WORKTIME_INFLUX_DAYS={{ aw_worktime_influx_days | default('today,yesterday') }}
|
||||
AW_WORKTIME_INFLUX_TOKEN={{ aw_effective_worktime_influx_token | default('') }}
|
||||
AW_DLP_INFLUX_ENABLED={{ 'true' if (aw_dlp_influx_enabled | default(false) | bool) else 'false' }}
|
||||
AW_DLP_INFLUX_URL={{ aw_dlp_influx_url | default('') }}
|
||||
AW_DLP_INFLUX_ORG={{ aw_dlp_influx_org | default('proxmox') }}
|
||||
AW_DLP_INFLUX_BUCKET={{ aw_dlp_influx_bucket | default('aw_metrics') }}
|
||||
AW_DLP_INFLUX_HOSTS={{ aw_dlp_influx_hosts | default('SHARKON2025') }}
|
||||
AW_DLP_INFLUX_HOSTS={{ aw_dlp_influx_hosts | default('HOST-EXAMPLE') }}
|
||||
AW_DLP_INFLUX_LOOKBACK_DAYS={{ aw_dlp_influx_lookback_days | default(30) }}
|
||||
AW_DLP_INFLUX_EVENT_LIMIT={{ aw_dlp_influx_event_limit | default(2000) }}
|
||||
AW_DLP_INFLUX_TOKEN={{ aw_effective_dlp_influx_token | default('') }}
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
- name: Проверить наличие legacy root DB
|
||||
ansible.builtin.stat:
|
||||
path: /root/.local/share/activitywatch/aw-server-rust/sqlite.db
|
||||
path: /var/lib/activitywatch/aw-server-rust/sqlite.db
|
||||
register: aw_legacy_root_db
|
||||
|
||||
- name: Проверить наличие target DB
|
||||
@@ -1791,7 +1791,7 @@
|
||||
- name: Backup legacy root DB перед merge
|
||||
ansible.builtin.copy:
|
||||
remote_src: true
|
||||
src: /root/.local/share/activitywatch/aw-server-rust/sqlite.db
|
||||
src: /var/lib/activitywatch/aw-server-rust/sqlite.db
|
||||
dest: "{{ aw_server_data_dir }}/backups/db/legacy-root-{{ ansible_date_time.iso8601_basic_short }}.sqlite.db"
|
||||
owner: "{{ aw_server_user }}"
|
||||
group: "{{ aw_server_group }}"
|
||||
@@ -1805,7 +1805,7 @@
|
||||
argv:
|
||||
- /usr/local/bin/merge-aw-server-dbs
|
||||
- --base
|
||||
- /root/.local/share/activitywatch/aw-server-rust/sqlite.db
|
||||
- /var/lib/activitywatch/aw-server-rust/sqlite.db
|
||||
- --overlay
|
||||
- "{{ aw_server_db_path }}"
|
||||
- --output
|
||||
@@ -1843,7 +1843,7 @@
|
||||
- name: Скопировать legacy root DB в target DB если target ещё не существует
|
||||
ansible.builtin.copy:
|
||||
remote_src: true
|
||||
src: /root/.local/share/activitywatch/aw-server-rust/sqlite.db
|
||||
src: /var/lib/activitywatch/aw-server-rust/sqlite.db
|
||||
dest: "{{ aw_server_db_path }}"
|
||||
owner: "{{ aw_server_user }}"
|
||||
group: "{{ aw_server_group }}"
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
aw_windows_package_version: "v0.13.2"
|
||||
aw_windows_package_url: "https://github.com/ActivityWatch/activitywatch/releases/download/v0.13.2/activitywatch-v0.13.2-windows-x86_64.zip"
|
||||
aw_windows_package_zip_path: ""
|
||||
aw_windows_domain: "SHARKON2025"
|
||||
aw_windows_domain: "HOST-EXAMPLE"
|
||||
aw_windows_builtin_administrator_name: "Администратор"
|
||||
aw_windows_users:
|
||||
- Администратор
|
||||
|
||||
@@ -42,8 +42,8 @@
|
||||
DETMIR_PORTAL_STATUS_CMD=detmir-status --json
|
||||
DETMIR_PORTAL_CHECK_CMD=detmir-check --json
|
||||
DETMIR_PORTAL_FAILED_UNITS_CMD=systemctl --failed --no-pager
|
||||
DETMIR_PORTAL_WORKTIME_URL=http://10.10.10.13:5610
|
||||
DETMIR_PORTAL_ONE_C_URL=http://10.10.10.2:8710
|
||||
DETMIR_PORTAL_WORKTIME_URL=http://192.0.2.13:5610
|
||||
DETMIR_PORTAL_ONE_C_URL=http://192.0.2.2:8710
|
||||
DETMIR_PORTAL_TIMEOUT_SECONDS=10
|
||||
DETMIR_PORTAL_STATE_DIR=/var/lib/detmir-portal
|
||||
DETMIR_PORTAL_DLP_DB_PATH=/var/lib/activitywatch/dlp_warehouse.sqlite
|
||||
@@ -117,7 +117,7 @@
|
||||
vars:
|
||||
aw_repo_root: "{{ playbook_dir | dirname }}"
|
||||
aw_rust_release_dir: "{{ (lookup('env', 'CARGO_TARGET_DIR') | default(aw_repo_root + '/adk-rust/target', true)) + '/release' }}"
|
||||
detmir_evidence_bind: "{{ detmir_evidence_bind_override | default('10.10.10.13:8721') }}"
|
||||
detmir_evidence_bind: "{{ detmir_evidence_bind_override | default('192.0.2.13:8721') }}"
|
||||
detmir_evidence_env_path: "/etc/detmir-portal-evidence.env"
|
||||
detmir_evidence_upload_token_path: "/var/lib/activitywatch/dlp-evidence/upload-token"
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
aw_windows_deploy_root: "C:\\Program Files\\AWatch-rus"
|
||||
aw_windows_evidence_sync_task_name: "ActivityWatch DLP Evidence Sync"
|
||||
aw_windows_evidence_sync_interval_minutes: 5
|
||||
aw_windows_evidence_sync_api_url: "http://10.10.10.13:8721/api/dlp/evidence/upload"
|
||||
aw_windows_evidence_sync_api_url: "http://192.0.2.13:8721/api/dlp/evidence/upload"
|
||||
aw_windows_evidence_sync_script: "{{ aw_windows_state_root }}\\sync-dlp-evidence-artifacts.ps1"
|
||||
aw_windows_evidence_sync_token_path: "{{ aw_windows_state_root }}\\dlp-evidence-upload-token.txt"
|
||||
aw_windows_evidence_sync_state_path: "{{ aw_windows_state_root }}\\dlp-evidence-sync-state.json"
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
aw_file_1c_clickhouse_native_port: 9000
|
||||
aw_file_1c_company_api_host: "{{ ansible_host | default('127.0.0.1') }}"
|
||||
aw_file_1c_company_api_port: 8710
|
||||
aw_file_1c_manager_brief_grafana_url: "http://10.10.10.11:3000/d/1c-file-mgmt/1c-file-management-board?orgId=1"
|
||||
aw_file_1c_manager_brief_grafana_url: "http://192.0.2.11:3000/d/1c-file-mgmt/1c-file-management-board?orgId=1"
|
||||
aw_file_1c_manager_brief_state_dir: /opt/activitywatch/clickhouse-1c/state/manager-brief
|
||||
aw_file_1c_manager_brief_model: gpt-5.3-codex
|
||||
aw_file_1c_manager_brief_codex_user: igor
|
||||
|
||||
@@ -134,9 +134,9 @@
|
||||
$user = Split-Path $profile.LocalPath -Leaf
|
||||
$sshDir = Join-Path $profile.LocalPath '.ssh'
|
||||
$dst = Join-Path $sshDir 'awops_ed25519'
|
||||
$grantDir = 'SHARKON2025\' + $user + ':(OI)(CI)(F)'
|
||||
$grantRead = 'SHARKON2025\' + $user + ':(R)'
|
||||
$grantModify = 'SHARKON2025\' + $user + ':(M)'
|
||||
$grantDir = 'HOST-EXAMPLE\' + $user + ':(OI)(CI)(F)'
|
||||
$grantRead = 'HOST-EXAMPLE\' + $user + ':(R)'
|
||||
$grantModify = 'HOST-EXAMPLE\' + $user + ':(M)'
|
||||
|
||||
New-Item -ItemType Directory -Path $sshDir -Force | Out-Null
|
||||
& icacls.exe $sshDir /grant $grantDir | Out-Null
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
grafana_check_dashboard_uid: "{{ detmir_grafana_check_dashboard_uid | default('detmir-aw-main') }}"
|
||||
grafana_check_dashboard_file_src: "{{ aw_repo_root }}/grafana/detmir-aw-main-dashboard.json"
|
||||
grafana_check_dashboard_file_dest: /etc/grafana/provisioning/dashboards/aw/detmir-aw-main.json
|
||||
grafana_check_host: "{{ detmir_grafana_check_host | default('SHARKON2025') }}"
|
||||
grafana_check_host: "{{ detmir_grafana_check_host | default('HOST-EXAMPLE') }}"
|
||||
grafana_check_max_freshness_minutes: "{{ detmir_grafana_check_max_freshness_minutes | default(360) }}"
|
||||
grafana_check_on_calendar: "{{ detmir_grafana_check_on_calendar | default('*:0/15') }}"
|
||||
|
||||
|
||||
@@ -23,84 +23,84 @@
|
||||
title: "Proxmox VE"
|
||||
category: "Host"
|
||||
description: "Основная панель управления Proxmox VE."
|
||||
target_url: "https://10.10.10.2:8006/"
|
||||
target_url: "https://192.0.2.2:8006/"
|
||||
external_enabled: false
|
||||
- slug: "file1c-brief"
|
||||
title: "1C Executive Brief"
|
||||
category: "Management"
|
||||
description: "Сводка по предприятиям и рискам 1С."
|
||||
target_url: "http://10.10.10.2:8710/manager/brief"
|
||||
target_url: "http://192.0.2.2:8710/manager/brief"
|
||||
proxy_path: "/r/file1c/brief"
|
||||
proxy_target_url: "http://10.10.10.2:8710/manager/brief"
|
||||
proxy_target_url: "http://192.0.2.2:8710/manager/brief"
|
||||
external_enabled: true
|
||||
- slug: "file1c-actions"
|
||||
title: "1C Management Actions"
|
||||
category: "Management"
|
||||
description: "Очередь действий по предприятиям в 1С."
|
||||
target_url: "http://10.10.10.2:8710/manager/actions"
|
||||
target_url: "http://192.0.2.2:8710/manager/actions"
|
||||
proxy_path: "/r/file1c/actions"
|
||||
proxy_target_url: "http://10.10.10.2:8710/manager/actions"
|
||||
proxy_target_url: "http://192.0.2.2:8710/manager/actions"
|
||||
external_enabled: true
|
||||
- slug: "file1c-finance"
|
||||
title: "1C Financial Reporting"
|
||||
category: "Management"
|
||||
description: "Первый financial board по файловой 1С с разделением ledger/proxy."
|
||||
target_url: "http://10.10.10.11:3000/d/1c-file-finance/1c-file-financial-reporting?orgId=1"
|
||||
target_url: "http://192.0.2.11:3000/d/1c-file-finance/1c-file-financial-reporting?orgId=1"
|
||||
proxy_path: "/d/1c-file-finance/1c-file-financial-reporting?orgId=1"
|
||||
external_enabled: true
|
||||
- slug: "file1c-telemetry"
|
||||
title: "1C Telemetry Board"
|
||||
category: "Dashboards"
|
||||
description: "Read-only telemetry экран по состоянию файловых баз, reglog и host."
|
||||
target_url: "http://10.10.10.11:3000/d/1c-file-telemetry/1c-file-telemetry-board?orgId=1"
|
||||
target_url: "http://192.0.2.11:3000/d/1c-file-telemetry/1c-file-telemetry-board?orgId=1"
|
||||
proxy_path: "/d/1c-file-telemetry/1c-file-telemetry-board?orgId=1"
|
||||
external_enabled: true
|
||||
- slug: "grafana-1c"
|
||||
title: "Grafana 1C"
|
||||
category: "Dashboards"
|
||||
description: "Рабочий file-1c dashboard contour в внешней Grafana."
|
||||
target_url: "http://10.10.10.11:3000/d/1c-file-mgmt/1c-file-management-board?orgId=1"
|
||||
target_url: "http://192.0.2.11:3000/d/1c-file-mgmt/1c-file-management-board?orgId=1"
|
||||
proxy_path: "/d/1c-file-mgmt/1c-file-management-board?orgId=1"
|
||||
external_enabled: true
|
||||
- slug: "clickhouse-http"
|
||||
title: "ClickHouse HTTP"
|
||||
category: "Data"
|
||||
description: "HTTP endpoint ClickHouse для file-1C analytics."
|
||||
target_url: "http://10.10.10.2:8123/"
|
||||
target_url: "http://192.0.2.2:8123/"
|
||||
external_enabled: false
|
||||
- slug: "influxdb"
|
||||
title: "InfluxDB"
|
||||
category: "Data"
|
||||
description: "InfluxDB LXC на CT 200."
|
||||
target_url: "http://10.10.10.10:8086/"
|
||||
target_url: "http://192.0.2.10:8086/"
|
||||
external_enabled: false
|
||||
- slug: "grafana-core"
|
||||
title: "Grafana Core"
|
||||
category: "Dashboards"
|
||||
description: "Отдельный Grafana CT 201."
|
||||
target_url: "http://10.10.10.11:3000/"
|
||||
target_url: "http://192.0.2.11:3000/"
|
||||
proxy_path: "/dashboards"
|
||||
external_enabled: true
|
||||
- slug: "loki-alloy"
|
||||
title: "Grafana Alloy"
|
||||
category: "Logs"
|
||||
description: "Web UI Alloy на CT 202."
|
||||
target_url: "http://10.10.10.12:12345/"
|
||||
target_url: "http://192.0.2.12:12345/"
|
||||
external_enabled: false
|
||||
- slug: "aw-ui"
|
||||
title: "AW-rus UI"
|
||||
category: "Operations"
|
||||
description: "Основной ActivityWatch-Russian Web UI на CT 203."
|
||||
target_url: "http://10.10.10.13:5600/"
|
||||
target_url: "http://192.0.2.13:5600/"
|
||||
proxy_path: "/r/aw/"
|
||||
external_enabled: true
|
||||
- slug: "aw-worktime"
|
||||
title: "AW-rus Management Report"
|
||||
category: "Operations"
|
||||
description: "Управленческий worktime/report API на CT 203."
|
||||
target_url: "http://10.10.10.13:5610/reports/worktime/management?format=html&host=SHARKON2025"
|
||||
target_url: "http://192.0.2.13:5610/reports/worktime/management?format=html&host=HOST-EXAMPLE"
|
||||
proxy_path: "/r/aw-worktime"
|
||||
proxy_target_url: "http://10.10.10.13:5610/reports/worktime/management?format=html&host=SHARKON2025"
|
||||
proxy_target_url: "http://192.0.2.13:5610/reports/worktime/management?format=html&host=HOST-EXAMPLE"
|
||||
external_enabled: true
|
||||
- slug: "detmir-portal"
|
||||
title: "DetMir Portal"
|
||||
|
||||
@@ -201,11 +201,11 @@
|
||||
TELEGRAM_DEFAULT_CHAT_ID={{ tsj_bot_default_chat_id }}
|
||||
HTTPS_PROXY={{ tsj_bot_https_proxy_url | default(tsj_bot_telegram_proxy_url | default('http://127.0.0.1:11090')) }}
|
||||
HTTP_PROXY={{ tsj_bot_http_proxy_url | default(tsj_bot_telegram_proxy_url | default('http://127.0.0.1:11090')) }}
|
||||
NO_PROXY={{ tsj_bot_no_proxy | default('localhost,127.0.0.1,10.10.10.0/24') }}
|
||||
NODE_13_HOST={{ tsj_bot_node_13_host | default('10.10.10.13') }}
|
||||
NODE_16_HOST={{ tsj_bot_node_16_host | default('10.10.10.16') }}
|
||||
NODE_13_URL={{ tsj_bot_node_13_url | default('http://10.10.10.13:5600/api/0/info') }}
|
||||
NODE_16_URL={{ tsj_bot_node_16_url | default('http://10.10.10.16/') }}
|
||||
NO_PROXY={{ tsj_bot_no_proxy | default('localhost,127.0.0.1,192.0.2.0/24') }}
|
||||
NODE_13_HOST={{ tsj_bot_node_13_host | default('192.0.2.13') }}
|
||||
NODE_16_HOST={{ tsj_bot_node_16_host | default('192.0.2.16') }}
|
||||
NODE_13_URL={{ tsj_bot_node_13_url | default('http://192.0.2.13:5600/api/0/info') }}
|
||||
NODE_16_URL={{ tsj_bot_node_16_url | default('http://192.0.2.16/') }}
|
||||
NODE_16_ENABLED={{ tsj_bot_node_16_enabled | default('false') }}
|
||||
INFRA_ADMIN_ROOT={{ tsj_bot_runtime_root }}
|
||||
CHECK_SCRIPT={{ tsj_bot_check_script | default(tsj_bot_runtime_root + '/scripts/system_self_support.sh --check') }}
|
||||
@@ -254,24 +254,24 @@
|
||||
UPDATE_TARGETS={{ tsj_bot_update_targets | default('auto') }}
|
||||
DETMIR_AI_STATE_FILE={{ tsj_bot_detmir_ai_state_file | default('/var/lib/detmir-ai/latest-state.json') }}
|
||||
TSJ_GUARDIAN_STATUS_BIN={{ tsj_bot_guardian_status_bin | default('/usr/local/bin/tsj-guardian-status') }}
|
||||
AW_RUS_API_BASE={{ tsj_bot_aw_rus_api_base | default('http://10.10.10.13:5600/api/0') }}
|
||||
AW_RUS_WORKTIME_BASE={{ tsj_bot_aw_rus_worktime_base | default('http://10.10.10.13:5610') }}
|
||||
AW_DLP_POLICY_API_BASE={{ tsj_bot_aw_dlp_policy_api_base | default('http://10.10.10.13:5601/api/0') }}
|
||||
AW_RUS_API_BASE={{ tsj_bot_aw_rus_api_base | default('http://192.0.2.13:5600/api/0') }}
|
||||
AW_RUS_WORKTIME_BASE={{ tsj_bot_aw_rus_worktime_base | default('http://192.0.2.13:5610') }}
|
||||
AW_DLP_POLICY_API_BASE={{ tsj_bot_aw_dlp_policy_api_base | default('http://192.0.2.13:5601/api/0') }}
|
||||
AW_DLP_POLICY_ACTOR={{ tsj_bot_aw_dlp_policy_actor | default('tsj-guardian-bot') }}
|
||||
AW_RUS_WORKTIME_HEAL_CMD={{ tsj_bot_aw_rus_worktime_heal_cmd | default("ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new igor@10.10.10.13 'sudo -n /usr/local/bin/aw-worktime-autoheal-rust && sudo -n systemctl reset-failed aw-worktime-ui-bridge.service && sudo -n systemctl start aw-worktime-ui-bridge.service'") }}
|
||||
AW_RUS_DLP_HEAL_CMD={{ tsj_bot_aw_rus_dlp_heal_cmd | default("ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new igor@10.10.10.13 'sudo -n systemctl restart activitywatch-server.service && (sudo -n systemctl start activitywatch-dlp-aggregator.service || true) && sudo -n /usr/local/bin/aw-health-check && sudo -n /usr/local/bin/dlp-health-check'") }}
|
||||
AW_RUS_CASE_API_BASE={{ tsj_bot_aw_rus_case_api_base | default('http://10.10.10.13:5602') }}
|
||||
AW_RUS_WORKTIME_HEAL_CMD={{ tsj_bot_aw_rus_worktime_heal_cmd | default("ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new igor@192.0.2.13 'sudo -n /usr/local/bin/aw-worktime-autoheal-rust && sudo -n systemctl reset-failed aw-worktime-ui-bridge.service && sudo -n systemctl start aw-worktime-ui-bridge.service'") }}
|
||||
AW_RUS_DLP_HEAL_CMD={{ tsj_bot_aw_rus_dlp_heal_cmd | default("ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new igor@192.0.2.13 'sudo -n systemctl restart activitywatch-server.service && (sudo -n systemctl start activitywatch-dlp-aggregator.service || true) && sudo -n /usr/local/bin/aw-health-check && sudo -n /usr/local/bin/dlp-health-check'") }}
|
||||
AW_RUS_CASE_API_BASE={{ tsj_bot_aw_rus_case_api_base | default('http://192.0.2.13:5602') }}
|
||||
AW_RUS_HAYABUSA_ENABLED={{ tsj_bot_aw_rus_hayabusa_enabled | default('true') }}
|
||||
AW_RUS_HAYABUSA_SSH_CMD={{ tsj_bot_aw_rus_hayabusa_ssh_cmd | default("ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new igor@10.10.10.13") }}
|
||||
AW_RUS_HOST={{ tsj_bot_aw_rus_host | default('SHARKON2025') }}
|
||||
AW_RUS_HAYABUSA_SSH_CMD={{ tsj_bot_aw_rus_hayabusa_ssh_cmd | default("ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new igor@192.0.2.13") }}
|
||||
AW_RUS_HOST={{ tsj_bot_aw_rus_host | default('HOST-EXAMPLE') }}
|
||||
AW_RUS_PRIMARY_USER={{ tsj_bot_aw_rus_primary_user | default('USER1') }}
|
||||
AW_RUS_STALE_SEC={{ tsj_bot_aw_rus_stale_sec | default(900) }}
|
||||
AW_RUS_SLO_ENABLED={{ tsj_bot_aw_rus_slo_enabled | default('true') }}
|
||||
AW_RUS_SLO_ALERT_WINDOW={{ tsj_bot_aw_rus_slo_alert_window | default('24h') }}
|
||||
AW_RUS_SLO_MIN_SAMPLES={{ tsj_bot_aw_rus_slo_min_samples | default(4) }}
|
||||
AW_RUS_SLO_MAX_AGE_SEC={{ tsj_bot_aw_rus_slo_max_age_sec | default(90) }}
|
||||
AW_RUS_SLO_SUMMARY_CMD={{ tsj_bot_aw_rus_slo_summary_cmd | default("ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new igor@10.10.10.13 'cat /var/lib/activitywatch/slo/aw-slo-summary.json'") }}
|
||||
AW_RUS_WINDOWS_HOST={{ tsj_bot_aw_rus_windows_host | default(hostvars[(groups['aw_windows'] | first)].ansible_host | default('192.168.100.18')) }}
|
||||
AW_RUS_SLO_SUMMARY_CMD={{ tsj_bot_aw_rus_slo_summary_cmd | default("ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new igor@192.0.2.13 'cat /var/lib/activitywatch/slo/aw-slo-summary.json'") }}
|
||||
AW_RUS_WINDOWS_HOST={{ tsj_bot_aw_rus_windows_host | default(hostvars[(groups['aw_windows'] | first)].ansible_host | default('198.51.100.18')) }}
|
||||
AW_RUS_WINDOWS_SSH_USER={{ tsj_bot_aw_rus_windows_ssh_user | default(hostvars[(groups['aw_windows'] | first)].ansible_user | default('Администратор')) }}
|
||||
AW_RUS_WINDOWS_SSH_PASSWORD={{ tsj_bot_aw_rus_windows_ssh_password | default(hostvars[(groups['aw_windows'] | first)].ansible_password | default('')) }}
|
||||
AW_RUS_WINDOWS_CONFIG_PATH={{ tsj_bot_aw_rus_windows_config_path | default('C:\ProgramData\AWatch-rus\deployment-config.json') }}
|
||||
@@ -301,24 +301,24 @@
|
||||
group: "{{ tsj_bot_group }}"
|
||||
mode: "0640"
|
||||
loop:
|
||||
- { key: "AW_RUS_API_BASE", value: "{{ tsj_bot_aw_rus_api_base | default('http://10.10.10.13:5600/api/0') }}" }
|
||||
- { key: "AW_RUS_WORKTIME_BASE", value: "{{ tsj_bot_aw_rus_worktime_base | default('http://10.10.10.13:5610') }}" }
|
||||
- { key: "AW_DLP_POLICY_API_BASE", value: "{{ tsj_bot_aw_dlp_policy_api_base | default('http://10.10.10.13:5601/api/0') }}" }
|
||||
- { key: "AW_RUS_API_BASE", value: "{{ tsj_bot_aw_rus_api_base | default('http://192.0.2.13:5600/api/0') }}" }
|
||||
- { key: "AW_RUS_WORKTIME_BASE", value: "{{ tsj_bot_aw_rus_worktime_base | default('http://192.0.2.13:5610') }}" }
|
||||
- { key: "AW_DLP_POLICY_API_BASE", value: "{{ tsj_bot_aw_dlp_policy_api_base | default('http://192.0.2.13:5601/api/0') }}" }
|
||||
- { key: "AW_DLP_POLICY_ACTOR", value: "{{ tsj_bot_aw_dlp_policy_actor | default('tsj-guardian-bot') }}" }
|
||||
- { key: "AW_RUS_WORKTIME_HEAL_CMD", value: "{{ tsj_bot_aw_rus_worktime_heal_cmd | default(\"ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new igor@10.10.10.13 'sudo -n /usr/local/bin/aw-worktime-autoheal-rust && sudo -n systemctl reset-failed aw-worktime-ui-bridge.service && sudo -n systemctl start aw-worktime-ui-bridge.service'\") }}" }
|
||||
- { key: "AW_RUS_DLP_HEAL_CMD", value: "{{ tsj_bot_aw_rus_dlp_heal_cmd | default(\"ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new igor@10.10.10.13 'sudo -n systemctl restart activitywatch-server.service && (sudo -n systemctl start activitywatch-dlp-aggregator.service || true) && sudo -n /usr/local/bin/aw-health-check && sudo -n /usr/local/bin/dlp-health-check'\") }}" }
|
||||
- { key: "AW_RUS_CASE_API_BASE", value: "{{ tsj_bot_aw_rus_case_api_base | default('http://10.10.10.13:5602') }}" }
|
||||
- { key: "AW_RUS_WORKTIME_HEAL_CMD", value: "{{ tsj_bot_aw_rus_worktime_heal_cmd | default(\"ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new igor@192.0.2.13 'sudo -n /usr/local/bin/aw-worktime-autoheal-rust && sudo -n systemctl reset-failed aw-worktime-ui-bridge.service && sudo -n systemctl start aw-worktime-ui-bridge.service'\") }}" }
|
||||
- { key: "AW_RUS_DLP_HEAL_CMD", value: "{{ tsj_bot_aw_rus_dlp_heal_cmd | default(\"ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new igor@192.0.2.13 'sudo -n systemctl restart activitywatch-server.service && (sudo -n systemctl start activitywatch-dlp-aggregator.service || true) && sudo -n /usr/local/bin/aw-health-check && sudo -n /usr/local/bin/dlp-health-check'\") }}" }
|
||||
- { key: "AW_RUS_CASE_API_BASE", value: "{{ tsj_bot_aw_rus_case_api_base | default('http://192.0.2.13:5602') }}" }
|
||||
- { key: "AW_RUS_HAYABUSA_ENABLED", value: "{{ tsj_bot_aw_rus_hayabusa_enabled | default('true') }}" }
|
||||
- { key: "AW_RUS_HAYABUSA_SSH_CMD", value: "{{ tsj_bot_aw_rus_hayabusa_ssh_cmd | default(\"ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new igor@10.10.10.13\") }}" }
|
||||
- { key: "AW_RUS_HOST", value: "{{ tsj_bot_aw_rus_host | default('SHARKON2025') }}" }
|
||||
- { key: "AW_RUS_HAYABUSA_SSH_CMD", value: "{{ tsj_bot_aw_rus_hayabusa_ssh_cmd | default(\"ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new igor@192.0.2.13\") }}" }
|
||||
- { key: "AW_RUS_HOST", value: "{{ tsj_bot_aw_rus_host | default('HOST-EXAMPLE') }}" }
|
||||
- { key: "AW_RUS_PRIMARY_USER", value: "{{ tsj_bot_aw_rus_primary_user | default('USER1') }}" }
|
||||
- { key: "AW_RUS_STALE_SEC", value: "{{ tsj_bot_aw_rus_stale_sec | default(900) }}" }
|
||||
- { key: "AW_RUS_SLO_ENABLED", value: "{{ tsj_bot_aw_rus_slo_enabled | default('true') }}" }
|
||||
- { key: "AW_RUS_SLO_ALERT_WINDOW", value: "{{ tsj_bot_aw_rus_slo_alert_window | default('24h') }}" }
|
||||
- { key: "AW_RUS_SLO_MIN_SAMPLES", value: "{{ tsj_bot_aw_rus_slo_min_samples | default(4) }}" }
|
||||
- { key: "AW_RUS_SLO_MAX_AGE_SEC", value: "{{ tsj_bot_aw_rus_slo_max_age_sec | default(90) }}" }
|
||||
- { key: "AW_RUS_SLO_SUMMARY_CMD", value: "{{ tsj_bot_aw_rus_slo_summary_cmd | default(\"ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new igor@10.10.10.13 'cat /var/lib/activitywatch/slo/aw-slo-summary.json'\") }}" }
|
||||
- { key: "AW_RUS_WINDOWS_HOST", value: "{{ tsj_bot_aw_rus_windows_host | default(hostvars[(groups['aw_windows'] | first)].ansible_host | default('192.168.100.18')) }}" }
|
||||
- { key: "AW_RUS_SLO_SUMMARY_CMD", value: "{{ tsj_bot_aw_rus_slo_summary_cmd | default(\"ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new igor@192.0.2.13 'cat /var/lib/activitywatch/slo/aw-slo-summary.json'\") }}" }
|
||||
- { key: "AW_RUS_WINDOWS_HOST", value: "{{ tsj_bot_aw_rus_windows_host | default(hostvars[(groups['aw_windows'] | first)].ansible_host | default('198.51.100.18')) }}" }
|
||||
- { key: "AW_RUS_WINDOWS_SSH_USER", value: "{{ tsj_bot_aw_rus_windows_ssh_user | default(hostvars[(groups['aw_windows'] | first)].ansible_user | default('Администратор')) }}" }
|
||||
- { key: "AW_RUS_WINDOWS_SSH_PASSWORD", value: "{{ tsj_bot_aw_rus_windows_ssh_password | default(hostvars[(groups['aw_windows'] | first)].ansible_password | default('')) }}" }
|
||||
- { key: "AW_RUS_WINDOWS_CONFIG_PATH", value: "{{ tsj_bot_aw_rus_windows_config_path | default('C:\\ProgramData\\AWatch-rus\\deployment-config.json') }}" }
|
||||
|
||||
@@ -16,19 +16,19 @@ aw_worktime_influx_enabled: false
|
||||
aw_worktime_influx_url: "http://<INFLUXDB_HOST>:8086"
|
||||
aw_worktime_influx_org: "proxmox"
|
||||
aw_worktime_influx_bucket: "aw_metrics"
|
||||
aw_worktime_influx_hosts: "SHARKON2025"
|
||||
aw_worktime_influx_hosts: "HOST-EXAMPLE"
|
||||
aw_worktime_influx_days: "today,yesterday"
|
||||
aw_worktime_influx_token: ""
|
||||
aw_dlp_influx_enabled: false
|
||||
aw_dlp_influx_url: "http://<INFLUXDB_HOST>:8086"
|
||||
aw_dlp_influx_org: "proxmox"
|
||||
aw_dlp_influx_bucket: "aw_metrics"
|
||||
aw_dlp_influx_hosts: "SHARKON2025"
|
||||
aw_dlp_influx_hosts: "HOST-EXAMPLE"
|
||||
aw_dlp_influx_lookback_days: 30
|
||||
aw_dlp_influx_event_limit: 2000
|
||||
aw_dlp_influx_token: ""
|
||||
aw_monitored_windows_host: "<WINDOWS_HOST>"
|
||||
aw_monitored_windows_hostname: "SHARKON2025"
|
||||
aw_monitored_windows_hostname: "HOST-EXAMPLE"
|
||||
aw_rus_health_worktime_api_base: "http://127.0.0.1:5610"
|
||||
aw_rus_health_state_dir: "{{ aw_server_data_dir }}/health"
|
||||
aw_rus_health_validation_dir: "{{ aw_rus_health_state_dir }}/windows-validation"
|
||||
@@ -67,7 +67,7 @@ aw_worktime_from: "00:00"
|
||||
aw_worktime_to: "17:00"
|
||||
aw_worktime_start_of_day: "{{ aw_worktime_from }}"
|
||||
aw_server_always_active_pattern: "aw-watcher-window"
|
||||
aw_server_landingpage: "/#/activity/SHARKON2025/view/"
|
||||
aw_server_landingpage: "/#/activity/HOST-EXAMPLE/view/"
|
||||
aw_health_strict_fileops: 0
|
||||
|
||||
aw_dlp_policy_engine_enabled: true
|
||||
|
||||
@@ -13,22 +13,22 @@ aw_server_public_host: "{{ (hostvars[aw_server_inventory_host].ansible_host | de
|
||||
aw_worktime_report_base: "http://{{ aw_server_public_host }}:5610"
|
||||
aw_worktime_timezone: "Europe/Moscow"
|
||||
aw_worktime_influx_enabled: true
|
||||
aw_worktime_influx_url: "http://10.10.10.10:8086"
|
||||
aw_worktime_influx_url: "http://192.0.2.10:8086"
|
||||
aw_worktime_influx_org: "proxmox"
|
||||
aw_worktime_influx_bucket: "aw_metrics"
|
||||
aw_worktime_influx_hosts: "SHARKON2025"
|
||||
aw_worktime_influx_hosts: "HOST-EXAMPLE"
|
||||
aw_worktime_influx_days: "today,yesterday"
|
||||
aw_worktime_influx_token: "{{ lookup('env', 'AW_WORKTIME_INFLUX_TOKEN') }}"
|
||||
aw_dlp_influx_enabled: true
|
||||
aw_dlp_influx_url: "http://10.10.10.10:8086"
|
||||
aw_dlp_influx_url: "http://192.0.2.10:8086"
|
||||
aw_dlp_influx_org: "proxmox"
|
||||
aw_dlp_influx_bucket: "aw_metrics"
|
||||
aw_dlp_influx_hosts: "SHARKON2025"
|
||||
aw_dlp_influx_hosts: "HOST-EXAMPLE"
|
||||
aw_dlp_influx_lookback_days: 30
|
||||
aw_dlp_influx_event_limit: 2000
|
||||
aw_dlp_influx_token: "{{ lookup('env', 'AW_DLP_INFLUX_TOKEN') }}"
|
||||
aw_monitored_windows_host: "192.168.100.18"
|
||||
aw_monitored_windows_hostname: "SHARKON2025"
|
||||
aw_monitored_windows_host: "198.51.100.18"
|
||||
aw_monitored_windows_hostname: "HOST-EXAMPLE"
|
||||
aw_rus_health_worktime_api_base: "http://127.0.0.1:5610"
|
||||
aw_rus_health_state_dir: "{{ aw_server_data_dir }}/health"
|
||||
aw_rus_health_validation_dir: "{{ aw_rus_health_state_dir }}/windows-validation"
|
||||
@@ -74,5 +74,5 @@ aw_worktime_from: "00:00"
|
||||
aw_worktime_to: "17:00"
|
||||
aw_worktime_start_of_day: "{{ aw_worktime_from }}"
|
||||
aw_server_always_active_pattern: "aw-watcher-window"
|
||||
aw_server_landingpage: "/#/activity/SHARKON2025/view/"
|
||||
aw_server_landingpage: "/#/activity/HOST-EXAMPLE/view/"
|
||||
aw_health_strict_fileops: 0
|
||||
|
||||
@@ -14,7 +14,7 @@ aw_windows_package_version: "v0.13.2"
|
||||
aw_windows_package_url: "https://github.com/ActivityWatch/activitywatch/releases/download/v0.13.2/activitywatch-v0.13.2-windows-x86_64.zip"
|
||||
aw_windows_package_zip_path: ""
|
||||
|
||||
aw_windows_domain: "SHARKON2025"
|
||||
aw_windows_domain: "HOST-EXAMPLE"
|
||||
aw_windows_builtin_administrator_name: "Администратор"
|
||||
aw_windows_users:
|
||||
- Администратор
|
||||
|
||||
@@ -54,7 +54,7 @@ tsj_bot_aw_rus_dlp_heal_cmd: "sshpass -p 'CHANGE_ME' ssh -o PubkeyAuthentication
|
||||
tsj_bot_aw_rus_case_api_base: "http://<AW_SERVER_HOST>:5602"
|
||||
tsj_bot_aw_rus_hayabusa_enabled: "true"
|
||||
tsj_bot_aw_rus_hayabusa_ssh_cmd: "sshpass -p 'CHANGE_ME' ssh -o PubkeyAuthentication=no -o StrictHostKeyChecking=no igor@<AW_SERVER_HOST>"
|
||||
tsj_bot_aw_rus_host: "SHARKON2025"
|
||||
tsj_bot_aw_rus_host: "HOST-EXAMPLE"
|
||||
tsj_bot_aw_rus_primary_user: "USER1"
|
||||
tsj_bot_aw_rus_stale_sec: 900
|
||||
tsj_bot_aw_rus_slo_enabled: "true"
|
||||
|
||||
@@ -12,7 +12,7 @@ aw_windows_hayabusa_auto_upload_task_name: "ActivityWatch Hayabusa Upload"
|
||||
aw_windows_package_version: "v0.13.2"
|
||||
aw_windows_package_url: "https://github.com/ActivityWatch/activitywatch/releases/download/v0.13.2/activitywatch-v0.13.2-windows-x86_64.zip"
|
||||
aw_windows_package_zip_path: ""
|
||||
aw_windows_domain: "SHARKON2025"
|
||||
aw_windows_domain: "HOST-EXAMPLE"
|
||||
# Localized name of the built-in local Administrator account (SID ending in -500).
|
||||
# On the current Russian Windows host this must stay "Администратор";
|
||||
# do not replace it with "Administrator" unless the target OS account is actually named that way.
|
||||
@@ -32,7 +32,7 @@ aw_windows_extra_users: []
|
||||
# Единые Windows/RDP пути: те же, что использует InnoSetup.
|
||||
aw_windows_install_root: "C:\\Program Files\\AWatch-rus\\bin"
|
||||
aw_windows_state_root: "C:\\ProgramData\\AWatch-rus"
|
||||
aw_windows_hostname_override: "" # Например: SHARKON2025
|
||||
aw_windows_hostname_override: "" # Например: HOST-EXAMPLE
|
||||
aw_windows_afk_enabled: true
|
||||
aw_windows_window_enabled: true
|
||||
aw_windows_file_ops_enabled: true
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[proxmox]
|
||||
pve-main ansible_host=192.168.10.2 ansible_user=root ansible_port=22
|
||||
pve-main ansible_host=198.51.100.2 ansible_user=root ansible_port=22
|
||||
|
||||
[aw_server]
|
||||
aw-ct ansible_host=10.20.30.13 ansible_user=root ansible_port=22
|
||||
@@ -9,7 +9,7 @@ aw-ct ansible_host=10.20.30.13 ansible_user=root ansible_port=22
|
||||
win-node1 ansible_host=<WINDOWS_HOST> ansible_user=Администратор ansible_password=CHANGE_ME ansible_connection=winrm ansible_winrm_transport=ntlm ansible_port=5985 ansible_winrm_server_cert_validation=ignore
|
||||
|
||||
[aw_pfsense_pollers]
|
||||
# pfsense-poller1 ansible_host=192.168.100.30 ansible_user=root ansible_port=22
|
||||
# pfsense-poller1 ansible_host=198.51.100.30 ansible_user=root ansible_port=22
|
||||
|
||||
[grafana]
|
||||
# Для API-import playbook достаточно указать grafana_url.
|
||||
|
||||
@@ -127,7 +127,7 @@
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update
|
||||
apt-get install -y curl ca-certificates bash unzip xz-utils jq rsync openssh-server python3
|
||||
mkdir -p /root/bootstrap/settings /etc/activitywatch
|
||||
mkdir -p /opt/detmir/bootstrap/settings /etc/activitywatch
|
||||
systemctl enable ssh || true
|
||||
systemctl restart ssh || true
|
||||
register: ct_bootstrap_result
|
||||
@@ -142,7 +142,7 @@
|
||||
- push
|
||||
- "{{ ct_id }}"
|
||||
- "{{ proxmox_bootstrap_dir }}/{{ item }}"
|
||||
- "/root/bootstrap/{{ item }}"
|
||||
- "/opt/detmir/bootstrap/{{ item }}"
|
||||
loop: "{{ aw_bootstrap_files }}"
|
||||
|
||||
- name: Записать AW server env во временный каталог Proxmox host
|
||||
@@ -196,9 +196,9 @@
|
||||
- -lc
|
||||
- |
|
||||
set -euo pipefail
|
||||
chmod +x /root/bootstrap/install_aw_server.sh /root/bootstrap/apply_webui_ru_patch.sh
|
||||
bash /root/bootstrap/install_aw_server.sh
|
||||
bash /root/bootstrap/apply_webui_ru_patch.sh
|
||||
chmod +x /opt/detmir/bootstrap/install_aw_server.sh /opt/detmir/bootstrap/apply_webui_ru_patch.sh
|
||||
bash /opt/detmir/bootstrap/install_aw_server.sh
|
||||
bash /opt/detmir/bootstrap/apply_webui_ru_patch.sh
|
||||
systemctl restart activitywatch-server.service
|
||||
|
||||
- name: Проверить AW API изнутри CT
|
||||
|
||||
@@ -132,7 +132,7 @@
|
||||
<h1>Proxmox Web Gateway</h1>
|
||||
<p class="lead">
|
||||
Единая стартовая точка для web-сервисов контура на Proxmox host
|
||||
<strong>10.10.10.2</strong>. Внешний вход закрыт gateway-auth,
|
||||
<strong>192.0.2.2</strong>. Внешний вход закрыт gateway-auth,
|
||||
а основные операторские страницы идут через reverse proxy без выдачи
|
||||
внутренних адресов наружу.
|
||||
</p>
|
||||
|
||||
@@ -6,7 +6,7 @@ map $http_upgrade $connection_upgrade {
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
server_name {{ proxmox_web_gateway_public_hostname }} 10.10.10.2 <WAN_OR_EXTERNAL_IP> _;
|
||||
server_name {{ proxmox_web_gateway_public_hostname }} 192.0.2.2 <WAN_OR_EXTERNAL_IP> _;
|
||||
|
||||
access_log /var/log/nginx/proxmox-web-gateway.access.log;
|
||||
error_log /var/log/nginx/proxmox-web-gateway.error.log;
|
||||
@@ -17,7 +17,7 @@ server {
|
||||
server {
|
||||
listen 443 ssl http2 default_server;
|
||||
listen [::]:443 ssl http2 default_server;
|
||||
server_name {{ proxmox_web_gateway_public_hostname }} 10.10.10.2 <WAN_OR_EXTERNAL_IP> _;
|
||||
server_name {{ proxmox_web_gateway_public_hostname }} 192.0.2.2 <WAN_OR_EXTERNAL_IP> _;
|
||||
|
||||
access_log /var/log/nginx/proxmox-web-gateway.access.log;
|
||||
error_log /var/log/nginx/proxmox-web-gateway.error.log;
|
||||
@@ -67,32 +67,32 @@ server {
|
||||
location /api/0/ {
|
||||
proxy_set_header Origin "";
|
||||
proxy_set_header Authorization "";
|
||||
proxy_pass http://10.10.10.13:5600;
|
||||
proxy_pass http://192.0.2.13:5600;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
location /r/grafana/ {
|
||||
proxy_set_header Authorization "";
|
||||
proxy_pass http://10.10.10.11:3000/;
|
||||
proxy_redirect http://10.10.10.11:3000/ /r/grafana/;
|
||||
proxy_pass http://192.0.2.11:3000/;
|
||||
proxy_redirect http://192.0.2.11:3000/ /r/grafana/;
|
||||
}
|
||||
|
||||
location = /login {
|
||||
proxy_set_header Authorization "";
|
||||
proxy_pass http://10.10.10.11:3000;
|
||||
proxy_pass http://192.0.2.11:3000;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
location = /logout {
|
||||
proxy_set_header Authorization "";
|
||||
proxy_pass http://10.10.10.11:3000;
|
||||
proxy_pass http://192.0.2.11:3000;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
location /public/ {
|
||||
proxy_set_header Origin "";
|
||||
proxy_set_header Authorization "";
|
||||
proxy_pass http://10.10.10.11:3000;
|
||||
proxy_pass http://192.0.2.11:3000;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
@@ -103,138 +103,138 @@ server {
|
||||
proxy_set_header Authorization "";
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
proxy_pass http://10.10.10.11:3000;
|
||||
proxy_pass http://192.0.2.11:3000;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
location /api/ {
|
||||
proxy_set_header Origin "";
|
||||
proxy_set_header Authorization "";
|
||||
proxy_pass http://10.10.10.11:3000;
|
||||
proxy_pass http://192.0.2.11:3000;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
location /apis/ {
|
||||
proxy_set_header Origin "";
|
||||
proxy_set_header Authorization "";
|
||||
proxy_pass http://10.10.10.11:3000;
|
||||
proxy_pass http://192.0.2.11:3000;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
location /d/ {
|
||||
proxy_set_header Authorization "";
|
||||
proxy_pass http://10.10.10.11:3000;
|
||||
proxy_pass http://192.0.2.11:3000;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
location /dashboards {
|
||||
proxy_set_header Authorization "";
|
||||
proxy_pass http://10.10.10.11:3000;
|
||||
proxy_pass http://192.0.2.11:3000;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
location /dashboard/ {
|
||||
proxy_set_header Authorization "";
|
||||
proxy_pass http://10.10.10.11:3000;
|
||||
proxy_pass http://192.0.2.11:3000;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
location /avatar/ {
|
||||
proxy_set_header Authorization "";
|
||||
proxy_pass http://10.10.10.11:3000;
|
||||
proxy_pass http://192.0.2.11:3000;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
location /profile/ {
|
||||
proxy_set_header Authorization "";
|
||||
proxy_pass http://10.10.10.11:3000;
|
||||
proxy_pass http://192.0.2.11:3000;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
location /org/ {
|
||||
proxy_set_header Authorization "";
|
||||
proxy_pass http://10.10.10.11:3000;
|
||||
proxy_pass http://192.0.2.11:3000;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
location /user/ {
|
||||
proxy_set_header Authorization "";
|
||||
proxy_pass http://10.10.10.11:3000;
|
||||
proxy_pass http://192.0.2.11:3000;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
location /plugins/ {
|
||||
proxy_set_header Authorization "";
|
||||
proxy_pass http://10.10.10.11:3000;
|
||||
proxy_pass http://192.0.2.11:3000;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
location /explore {
|
||||
proxy_set_header Authorization "";
|
||||
proxy_pass http://10.10.10.11:3000;
|
||||
proxy_pass http://192.0.2.11:3000;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
location /alerting/ {
|
||||
proxy_set_header Authorization "";
|
||||
proxy_pass http://10.10.10.11:3000;
|
||||
proxy_pass http://192.0.2.11:3000;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
location /connections/ {
|
||||
proxy_set_header Authorization "";
|
||||
proxy_pass http://10.10.10.11:3000;
|
||||
proxy_pass http://192.0.2.11:3000;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
location /datasources/ {
|
||||
proxy_set_header Authorization "";
|
||||
proxy_pass http://10.10.10.11:3000;
|
||||
proxy_pass http://192.0.2.11:3000;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
location /r/aw/ {
|
||||
proxy_set_header Authorization "";
|
||||
proxy_pass http://10.10.10.13:5600/;
|
||||
proxy_redirect http://10.10.10.13:5600/ /r/aw/;
|
||||
proxy_pass http://192.0.2.13:5600/;
|
||||
proxy_redirect http://192.0.2.13:5600/ /r/aw/;
|
||||
}
|
||||
|
||||
location /reports/worktime/ {
|
||||
proxy_set_header Authorization "";
|
||||
proxy_pass http://10.10.10.13:5610;
|
||||
proxy_pass http://192.0.2.13:5610;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
location = /dark.css {
|
||||
proxy_set_header Authorization "";
|
||||
proxy_pass http://10.10.10.13:5600;
|
||||
proxy_pass http://192.0.2.13:5600;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
location /css/ {
|
||||
proxy_set_header Authorization "";
|
||||
proxy_pass http://10.10.10.13:5600;
|
||||
proxy_pass http://192.0.2.13:5600;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
location = /js/aw-worktime-panel.js {
|
||||
proxy_set_header Authorization "";
|
||||
proxy_pass http://10.10.10.13:5600;
|
||||
proxy_pass http://192.0.2.13:5600;
|
||||
proxy_redirect off;
|
||||
sub_filter_once off;
|
||||
sub_filter_types application/javascript text/javascript;
|
||||
sub_filter 'http://10.10.10.13:5610' '';
|
||||
sub_filter 'http://192.0.2.13:5610' '';
|
||||
}
|
||||
|
||||
location /js/ {
|
||||
proxy_set_header Authorization "";
|
||||
proxy_pass http://10.10.10.13:5600;
|
||||
proxy_pass http://192.0.2.13:5600;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
location /img/ {
|
||||
proxy_set_header Authorization "";
|
||||
proxy_pass http://10.10.10.13:5600;
|
||||
proxy_pass http://192.0.2.13:5600;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
@@ -242,7 +242,7 @@ server {
|
||||
auth_basic off;
|
||||
proxy_set_header Origin "";
|
||||
proxy_set_header Authorization "";
|
||||
proxy_pass http://10.10.10.13:5600;
|
||||
proxy_pass http://192.0.2.13:5600;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
@@ -264,7 +264,7 @@ server {
|
||||
location ^~ /portal/api/dlp/evidence {
|
||||
proxy_set_header Authorization "";
|
||||
proxy_set_header X-Remote-User $remote_user;
|
||||
proxy_pass http://10.10.10.13:8721/api/dlp/evidence;
|
||||
proxy_pass http://192.0.2.13:8721/api/dlp/evidence;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,10 +14,10 @@ SERVER_PUBLIC_HOST="${AW_SERVER_PUBLIC_HOST:-${AW_SERVER_HOST:-$(hostname -f 2>/
|
||||
REPORT_BASE="${AW_WORKTIME_REPORT_BASE:-http://${SERVER_PUBLIC_HOST}:5610}"
|
||||
CASE_PORT="${AW_DLP_CASE_PORT:-5602}"
|
||||
CASE_BASE="${AW_DLP_CASE_PUBLIC_BASE:-}"
|
||||
PATCH_JS_SRC="/root/bootstrap/aw-ru-patch.js"
|
||||
SW_CLEANUP_SRC="/root/bootstrap/aw-sw-cleanup.js"
|
||||
WORKTIME_PANEL_SRC="/root/bootstrap/aw-worktime-panel.js"
|
||||
HOST_GROUPS_SRC="/root/bootstrap/aw-host-groups.json"
|
||||
PATCH_JS_SRC="/opt/detmir/bootstrap/aw-ru-patch.js"
|
||||
SW_CLEANUP_SRC="/opt/detmir/bootstrap/aw-sw-cleanup.js"
|
||||
WORKTIME_PANEL_SRC="/opt/detmir/bootstrap/aw-worktime-panel.js"
|
||||
HOST_GROUPS_SRC="/opt/detmir/bootstrap/aw-host-groups.json"
|
||||
INDEX_HTML="$WEBUI_DIR/index.html"
|
||||
SERVICE_WORKER="$WEBUI_DIR/service-worker.js"
|
||||
TS=$(date +%Y%m%d%H%M%S)
|
||||
|
||||
@@ -43,10 +43,10 @@ AW_HEALTH_CHECK_ENABLED=true
|
||||
AW_HEALTH_CHECK_INTERVAL=60
|
||||
AW_EXPECT_START_OF_DAY=00:00
|
||||
AW_EXPECT_ALWAYS_ACTIVE_PATTERN=aw-watcher-window
|
||||
AW_EXPECT_LANDINGPAGE=/#/activity/SHARKON2025/view/
|
||||
AW_EXPECT_LANDINGPAGE=/#/activity/HOST-EXAMPLE/view/
|
||||
AW_HEALTH_STRICT_FILEOPS=0
|
||||
AW_MONITORED_WINDOWS_HOST=<WINDOWS_HOST>
|
||||
AW_MONITORED_WINDOWS_HOSTNAME=SHARKON2025
|
||||
AW_MONITORED_WINDOWS_HOSTNAME=HOST-EXAMPLE
|
||||
AW_RUS_HEALTH_WORKTIME_API=http://127.0.0.1:5610
|
||||
AW_RUS_HEALTH_STATE_DIR=/var/lib/activitywatch/health
|
||||
AW_RUS_HEALTH_VALIDATION_DIR=/var/lib/activitywatch/health/windows-validation
|
||||
@@ -58,7 +58,7 @@ AW_RUS_SLO_WORKTIME_BASE=http://127.0.0.1:5610
|
||||
AW_RUS_SLO_TARGET_PERCENT=99.97
|
||||
AW_BROWSER_SMOKE_AW_BASE=http://127.0.0.1:5600
|
||||
AW_BROWSER_SMOKE_WORKTIME_BASE=http://127.0.0.1:5610
|
||||
AW_BROWSER_SMOKE_HOST=SHARKON2025
|
||||
AW_BROWSER_SMOKE_HOST=HOST-EXAMPLE
|
||||
AW_BROWSER_SMOKE_OUTPUT_DIR=/var/lib/activitywatch/browser-smoke
|
||||
AW_BROWSER_SMOKE_KEEP_RUNS=24
|
||||
AW_BROWSER_SMOKE_ENGINE=chromium-cli
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
[Unit]
|
||||
Description=AW Worktime Autoheal (SHARKON2025)
|
||||
Description=AW Worktime Autoheal (HOST-EXAMPLE)
|
||||
After=network-online.target activitywatch-server.service
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
Environment=AW_URL=http://127.0.0.1:5600
|
||||
Environment=AW_WORKTIME_HOST=SHARKON2025
|
||||
Environment=AW_WORKTIME_HOST=HOST-EXAMPLE
|
||||
ExecStart=/usr/local/bin/aw-worktime-autoheal-rust
|
||||
User=root
|
||||
Group=root
|
||||
|
||||
@@ -6,7 +6,7 @@ WORKTIME_PREWARM_TIMEOUT_SECONDS="${WORKTIME_PREWARM_TIMEOUT_SECONDS:-45}"
|
||||
WORKTIME_PREWARM_HEALTH_TIMEOUT_SECONDS="${WORKTIME_PREWARM_HEALTH_TIMEOUT_SECONDS:-10}"
|
||||
WORKTIME_PREWARM_READY_TIMEOUT_SECONDS="${WORKTIME_PREWARM_READY_TIMEOUT_SECONDS:-60}"
|
||||
WORKTIME_PREWARM_READY_INTERVAL_SECONDS="${WORKTIME_PREWARM_READY_INTERVAL_SECONDS:-2}"
|
||||
WORKTIME_PREWARM_HOST="${WORKTIME_PREWARM_HOST:-${AW_WORKTIME_HOST:-SHARKON2025}}"
|
||||
WORKTIME_PREWARM_HOST="${WORKTIME_PREWARM_HOST:-${AW_WORKTIME_HOST:-HOST-EXAMPLE}}"
|
||||
WORKTIME_PREWARM_PROFILE="${WORKTIME_PREWARM_PROFILE:-full}"
|
||||
|
||||
log() {
|
||||
|
||||
@@ -8,7 +8,7 @@ StartLimitIntervalSec=120
|
||||
[Service]
|
||||
Type=simple
|
||||
Environment=AW_SERVER_URL=http://127.0.0.1:5600
|
||||
Environment=AW_WORKTIME_HOST=SHARKON2025
|
||||
Environment=AW_WORKTIME_HOST=HOST-EXAMPLE
|
||||
ExecStart=/usr/local/bin/aw-worktime-ui-bridge-rust
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# aw-rus Hayabusa Server Ops Bundle
|
||||
|
||||
This directory is the server-side operational bundle for Hayabusa on `10.10.10.13`.
|
||||
This directory is the server-side operational bundle for Hayabusa on `192.0.2.13`.
|
||||
|
||||
## Goal
|
||||
|
||||
@@ -44,7 +44,7 @@ Prerequisites:
|
||||
|
||||
- `/opt/activitywatch/aw-rus-ops/venv` contains `ansible` and `pywinrm`
|
||||
- `/opt/activitywatch/aw-rus-ops/ansible/inventory.ini` contains the live Windows connection details
|
||||
- WinRM from `10.10.10.13` to the Windows host is reachable
|
||||
- WinRM from `192.0.2.13` to the Windows host is reachable
|
||||
|
||||
Run:
|
||||
|
||||
@@ -55,14 +55,14 @@ aw-hayabusa-from-windows --days-back 1 --mode incident --case-id 30
|
||||
This performs:
|
||||
|
||||
- Windows EVTX export via WinRM
|
||||
- fetch of the newest zip directly onto `10.10.10.13`
|
||||
- fetch of the newest zip directly onto `192.0.2.13`
|
||||
- `aw-hayabusa accept`
|
||||
- `aw-hayabusa process-inbox`
|
||||
- bounded case linkage via case API
|
||||
|
||||
If WinRM from the server to Windows is blocked by network policy, use the drop-zone workflow below instead.
|
||||
|
||||
## Drop-zone automation on 10.10.10.13
|
||||
## Drop-zone automation on 192.0.2.13
|
||||
|
||||
The server can auto-process packages dropped into:
|
||||
|
||||
@@ -95,9 +95,9 @@ powershell.exe -ExecutionPolicy Bypass -File C:\ProgramData\AWatch-rus\export-up
|
||||
- run `C:\ProgramData\AWatch-rus\export-evtx-for-hayabusa.ps1`
|
||||
- upload matching `.caseid` first when `-CaseId` is specified
|
||||
- upload the newest zip to `/opt/activitywatch/aw-rus-ops/drop`
|
||||
- let `aw-hayabusa-drop.path` process the package automatically on `10.10.10.13`
|
||||
- let `aw-hayabusa-drop.path` process the package automatically on `192.0.2.13`
|
||||
|
||||
This path was validated live against case `30` after the `awops` SSH authorization was installed on `10.10.10.13`.
|
||||
This path was validated live against case `30` after the `awops` SSH authorization was installed on `192.0.2.13`.
|
||||
|
||||
Server-side prerequisite for user `awops`:
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ required_vars=(
|
||||
AW_SERVER_GROUP
|
||||
)
|
||||
|
||||
BOOTSTRAP_DIR="/root/bootstrap"
|
||||
BOOTSTRAP_DIR="/opt/detmir/bootstrap"
|
||||
VIEWS_JSON="$BOOTSTRAP_DIR/settings/views-default.json"
|
||||
CLASSES_JSON="$BOOTSTRAP_DIR/settings/classes-worktime.json"
|
||||
WORKTIME_API_RUST_SRC="$BOOTSTRAP_DIR/worktime-api"
|
||||
@@ -91,7 +91,7 @@ sed \
|
||||
-e "s#__AW_SERVER_USER__#$AW_SERVER_USER#g" \
|
||||
-e "s#__AW_SERVER_GROUP__#$AW_SERVER_GROUP#g" \
|
||||
-e "s#__AW_SERVER_DATA_DIR__#$AW_SERVER_DATA_DIR#g" \
|
||||
/root/bootstrap/activitywatch-server.service > /etc/systemd/system/activitywatch-server.service
|
||||
/opt/detmir/bootstrap/activitywatch-server.service > /etc/systemd/system/activitywatch-server.service
|
||||
chmod 0644 /etc/systemd/system/activitywatch-server.service
|
||||
|
||||
systemctl daemon-reload
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"users": {
|
||||
"SHARKON2025\\USER1": {
|
||||
"HOST-EXAMPLE\\USER1": {
|
||||
"display_name": "Иван Петров",
|
||||
"manager": "Руководитель смены",
|
||||
"department": "Бухгалтерия",
|
||||
@@ -8,14 +8,14 @@
|
||||
"notes": "Дневная смена",
|
||||
"exclude": false
|
||||
},
|
||||
"SHARKON2025\\USER4": {
|
||||
"HOST-EXAMPLE\\USER4": {
|
||||
"display_name": "Мария Сидорова",
|
||||
"manager": "Руководитель смены",
|
||||
"department": "Бухгалтерия",
|
||||
"role": "Оператор 1С",
|
||||
"exclude": false
|
||||
},
|
||||
"SHARKON2025\\ADMINISTRATOR": {
|
||||
"HOST-EXAMPLE\\ADMINISTRATOR": {
|
||||
"display_name": "Администратор",
|
||||
"manager": "Ops",
|
||||
"department": "Инфраструктура",
|
||||
|
||||
+11
-11
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
# check-aw-full.sh - Полная проверка ActivityWatch: сервер + RDP-хост
|
||||
# Сервер: 10.10.10.13:5600
|
||||
# RDP-хост: 192.168.100.18 (SHARKON2025)
|
||||
# Сервер: 192.0.2.13:5600
|
||||
# RDP-хост: 198.51.100.18 (HOST-EXAMPLE)
|
||||
|
||||
if [[ "${CHECK_AW_FULL_FORCE_LEGACY:-0}" != "1" ]]; then
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
@@ -16,9 +16,9 @@ if [[ "${CHECK_AW_FULL_FORCE_LEGACY:-0}" != "1" ]]; then
|
||||
done
|
||||
fi
|
||||
|
||||
SERVER="http://10.10.10.13:5600"
|
||||
HOSTNAME_FILTER="SHARKON2025"
|
||||
RDP_HOST="192.168.100.18"
|
||||
SERVER="http://192.0.2.13:5600"
|
||||
HOSTNAME_FILTER="HOST-EXAMPLE"
|
||||
RDP_HOST="198.51.100.18"
|
||||
NOW=$(date -u +%s)
|
||||
HOST_INACTIVE=false
|
||||
GUARD_HEALTHY=false
|
||||
@@ -100,7 +100,7 @@ echo ""
|
||||
# 1. Проверка сервера
|
||||
echo -e "${CYAN}--- 1. AW Server ($SERVER) ---${NC}"
|
||||
echo -n " Connectivity... "
|
||||
RESP=$(no_proxy=10.10.10.13 curl -s --connect-timeout 10 --max-time 15 "$SERVER/api/0/info" 2>&1)
|
||||
RESP=$(no_proxy=192.0.2.13 curl -s --connect-timeout 10 --max-time 15 "$SERVER/api/0/info" 2>&1)
|
||||
if [ $? -eq 0 ] && echo "$RESP" | jq -e '.version' > /dev/null 2>&1; then
|
||||
VERSION=$(echo "$RESP" | jq -r '.version')
|
||||
echo -e " ${GREEN}OK${NC} (aw-server $VERSION)"
|
||||
@@ -110,7 +110,7 @@ else
|
||||
fi
|
||||
|
||||
echo -n " CORS... "
|
||||
CORS_RESP=$(no_proxy=10.10.10.13 curl -s --connect-timeout 10 --max-time 15 -o /dev/null -w '%{http_code}' -H "Origin: http://10.10.10.13:5600" "$SERVER/api/0/settings/" 2>&1)
|
||||
CORS_RESP=$(no_proxy=192.0.2.13 curl -s --connect-timeout 10 --max-time 15 -o /dev/null -w '%{http_code}' -H "Origin: http://192.0.2.13:5600" "$SERVER/api/0/settings/" 2>&1)
|
||||
if [ "$CORS_RESP" = "200" ]; then
|
||||
echo -e "${GREEN}OK${NC}"
|
||||
else
|
||||
@@ -119,7 +119,7 @@ fi
|
||||
echo ""
|
||||
|
||||
# 1b. Context for inactive/event-driven classification
|
||||
WORKTIME_EVENT_DATA=$(no_proxy=10.10.10.13 curl -s --connect-timeout 10 --max-time 15 "$SERVER/api/0/buckets/aw-worktime-sessions_$HOSTNAME_FILTER/events?limit=1" 2>&1)
|
||||
WORKTIME_EVENT_DATA=$(no_proxy=192.0.2.13 curl -s --connect-timeout 10 --max-time 15 "$SERVER/api/0/buckets/aw-worktime-sessions_$HOSTNAME_FILTER/events?limit=1" 2>&1)
|
||||
WORKTIME_TS=$(echo "$WORKTIME_EVENT_DATA" | jq -r '.[0].timestamp // ""' 2>/dev/null)
|
||||
WORKTIME_ACTIVE=$(echo "$WORKTIME_EVENT_DATA" | jq -r '.[0].data.active // false' 2>/dev/null)
|
||||
if [ -n "$WORKTIME_TS" ]; then
|
||||
@@ -132,7 +132,7 @@ if [ -n "$WORKTIME_TS" ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
GUARD_EVENT_DATA=$(no_proxy=10.10.10.13 curl -s --connect-timeout 10 --max-time 15 "$SERVER/api/0/buckets/aw-rus-collector-guard_$HOSTNAME_FILTER/events?limit=1" 2>&1)
|
||||
GUARD_EVENT_DATA=$(no_proxy=192.0.2.13 curl -s --connect-timeout 10 --max-time 15 "$SERVER/api/0/buckets/aw-rus-collector-guard_$HOSTNAME_FILTER/events?limit=1" 2>&1)
|
||||
GUARD_TS=$(echo "$GUARD_EVENT_DATA" | jq -r '.[0].timestamp // ""' 2>/dev/null)
|
||||
GUARD_STATUS=$(echo "$GUARD_EVENT_DATA" | jq -r '.[0].data.status // ""' 2>/dev/null)
|
||||
GUARD_PROBLEMS=$(echo "$GUARD_EVENT_DATA" | jq -r '([.[0].data.problems[]?] | length) // 0' 2>/dev/null)
|
||||
@@ -167,7 +167,7 @@ for entry in "${BUCKETS[@]}"; do
|
||||
label="${entry##*|}"
|
||||
bucket_full="${bucket}_${HOSTNAME_FILTER}"
|
||||
|
||||
EVENT_DATA=$(no_proxy=10.10.10.13 curl -s --connect-timeout 10 --max-time 15 "$SERVER/api/0/buckets/$bucket_full/events?limit=1" 2>&1)
|
||||
EVENT_DATA=$(no_proxy=192.0.2.13 curl -s --connect-timeout 10 --max-time 15 "$SERVER/api/0/buckets/$bucket_full/events?limit=1" 2>&1)
|
||||
LAST_ID=$(echo "$EVENT_DATA" | jq '.[0].id // 0')
|
||||
LAST_TS=$(echo "$EVENT_DATA" | jq -r '.[0].timestamp // "no events"')
|
||||
|
||||
@@ -231,7 +231,7 @@ DEAD_COUNT=0
|
||||
for entry in "${BUCKETS[@]}"; do
|
||||
bucket="${entry%%|*}"
|
||||
bucket_full="${bucket}_${HOSTNAME_FILTER}"
|
||||
EVENT_DATA=$(no_proxy=10.10.10.13 curl -s --connect-timeout 10 --max-time 15 "$SERVER/api/0/buckets/$bucket_full/events?limit=1" 2>&1)
|
||||
EVENT_DATA=$(no_proxy=192.0.2.13 curl -s --connect-timeout 10 --max-time 15 "$SERVER/api/0/buckets/$bucket_full/events?limit=1" 2>&1)
|
||||
LAST_TS=$(echo "$EVENT_DATA" | jq -r '.[0].timestamp // "no events"')
|
||||
|
||||
if [ "$LAST_TS" != "no events" ] && [ -n "$LAST_TS" ]; then
|
||||
|
||||
@@ -7,7 +7,7 @@ CLICKHOUSE_HOST=clickhouse
|
||||
AW_1C_CLICKHOUSE_RUNTIME_HOST=127.0.0.1
|
||||
AW_1C_COMPANY_API_HOST=127.0.0.1
|
||||
AW_1C_COMPANY_API_PORT=8710
|
||||
AW_1C_MANAGER_BRIEF_GRAFANA_URL=http://10.10.10.11:3000/dashboards/f/file-1c/?orgId=1
|
||||
AW_1C_MANAGER_BRIEF_GRAFANA_URL=http://192.0.2.11:3000/dashboards/f/file-1c/?orgId=1
|
||||
AW_1C_COMPANY_LOOKBACK_DAYS=30
|
||||
AW_1C_COMPANY_MIN_DAYS=1
|
||||
AW_1C_COMPANY_HORIZONS=7,30
|
||||
|
||||
@@ -156,7 +156,7 @@ python ai/generate_manager_brief.py --host localhost --port 8123 --user default
|
||||
Этот слой делает не raw LLM-чат, а промышленный pipeline:
|
||||
|
||||
- строит компактный context из `v_company_portfolio_overview`;
|
||||
- вызывает локальный `codex exec` на `10.10.10.2`;
|
||||
- вызывает локальный `codex exec` на `192.0.2.2`;
|
||||
- валидирует structured output по JSON schema;
|
||||
- при сбое `codex` отдаёт deterministic fallback, чтобы контур не пустел;
|
||||
- пишет `latest.json` и `latest.md` в `state/manager-brief/`.
|
||||
|
||||
@@ -459,7 +459,7 @@ def build_company_recovery_context(summary_payload: dict[str, Any], infobase: st
|
||||
def grafana_company_dashboard_url() -> str:
|
||||
return os.getenv(
|
||||
"AW_1C_MANAGER_BRIEF_GRAFANA_URL",
|
||||
"http://10.10.10.11:3000/d/1c-file-mgmt/1c-file-management-board",
|
||||
"http://192.0.2.11:3000/d/1c-file-mgmt/1c-file-management-board",
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
},
|
||||
{
|
||||
"title": "Manager Brief",
|
||||
"url": "http://10.10.10.2:8710/manager/brief"
|
||||
"url": "http://192.0.2.2:8710/manager/brief"
|
||||
}
|
||||
],
|
||||
"panels": [
|
||||
|
||||
@@ -9,19 +9,19 @@
|
||||
"links": [
|
||||
{
|
||||
"title": "Manager Brief",
|
||||
"url": "http://10.10.10.2:8710/manager/brief"
|
||||
"url": "http://192.0.2.2:8710/manager/brief"
|
||||
},
|
||||
{
|
||||
"title": "Manager Actions",
|
||||
"url": "http://10.10.10.2:8710/manager/actions"
|
||||
"url": "http://192.0.2.2:8710/manager/actions"
|
||||
},
|
||||
{
|
||||
"title": "Weekly Digest",
|
||||
"url": "http://10.10.10.2:8710/manager/digest/weekly"
|
||||
"url": "http://192.0.2.2:8710/manager/digest/weekly"
|
||||
},
|
||||
{
|
||||
"title": "Recovery",
|
||||
"url": "http://10.10.10.2:8710/manager/recovery"
|
||||
"url": "http://192.0.2.2:8710/manager/recovery"
|
||||
},
|
||||
{
|
||||
"title": "Company Intelligence",
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
},
|
||||
{
|
||||
"title": "Manager Brief",
|
||||
"url": "http://10.10.10.2:8710/manager/brief"
|
||||
"url": "http://192.0.2.2:8710/manager/brief"
|
||||
}
|
||||
],
|
||||
"panels": [
|
||||
|
||||
@@ -8,14 +8,14 @@ INSERT INTO analytics_1c.postings (ts, infobase, registrar, operation_type, acco
|
||||
('2026-05-21 10:16:00','ФЕЛИЦТ ГРУПП 2026','DOC-3','adjustment','91.02','62.01',54000.00,'demo-postings.jsonl');
|
||||
|
||||
INSERT INTO analytics_1c.reglog_events (ts, infobase, user, host, app, event_name, level, duration_ms, message, source_file) VALUES
|
||||
('2026-05-21 07:15:00','ТРАНСГАЗ 2026','USER1','SHARKON2025','1cv8c','Login','warn',0,'Вход вне рабочего времени','demo-reglog.jsonl'),
|
||||
('2026-05-21 10:20:00','ФЕЛИЦТ ГРУПП 2026','USER4','SHARKON2025','1cv8c','PostingError','error',4200,'Ошибка проведения документа','demo-reglog.jsonl'),
|
||||
('2026-05-21 10:25:00','ФЕЛИЦТ ГРУПП 2026','USER4','SHARKON2025','1cv8c','ExchangeFailure','error',6100,'Ошибка обмена с внешней системой','demo-reglog.jsonl');
|
||||
('2026-05-21 07:15:00','ТРАНСГАЗ 2026','USER1','HOST-EXAMPLE','1cv8c','Login','warn',0,'Вход вне рабочего времени','demo-reglog.jsonl'),
|
||||
('2026-05-21 10:20:00','ФЕЛИЦТ ГРУПП 2026','USER4','HOST-EXAMPLE','1cv8c','PostingError','error',4200,'Ошибка проведения документа','demo-reglog.jsonl'),
|
||||
('2026-05-21 10:25:00','ФЕЛИЦТ ГРУПП 2026','USER4','HOST-EXAMPLE','1cv8c','ExchangeFailure','error',6100,'Ошибка обмена с внешней системой','demo-reglog.jsonl');
|
||||
|
||||
INSERT INTO analytics_1c.audit_events (ts, infobase, user, object_type, object_id, action, before_hash, after_hash, risk_tag, source_file) VALUES
|
||||
('2026-05-21 10:17:00','ФЕЛИЦТ ГРУПП 2026','USER4','document','DOC-3','repost','abc','def','repost','demo-audit.jsonl'),
|
||||
('2026-05-21 10:18:00','ФЕЛИЦТ ГРУПП 2026','USER4','counterparty','CP-77','change','old','new','critical_ref','demo-audit.jsonl');
|
||||
|
||||
INSERT INTO analytics_1c.host_events (ts, host, cpu_pct, ram_pct, disk_free_gb, disk_latency_ms, smb_errors, rdp_sessions, backup_ok, source_file) VALUES
|
||||
('2026-05-21 10:00:00','SHARKON2025',41.2,68.4,120.0,12.5,0,4,1,'demo-host.jsonl'),
|
||||
('2026-05-21 11:00:00','SHARKON2025',57.8,72.0,118.0,61.0,2,4,0,'demo-host.jsonl');
|
||||
('2026-05-21 10:00:00','HOST-EXAMPLE',41.2,68.4,120.0,12.5,0,4,1,'demo-host.jsonl'),
|
||||
('2026-05-21 11:00:00','HOST-EXAMPLE',57.8,72.0,118.0,61.0,2,4,0,'demo-host.jsonl');
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
DETMIR_AW_BASE=http://10.10.10.13:5600
|
||||
DETMIR_WORKTIME_BASE=http://10.10.10.13:5610
|
||||
DETMIR_DLP_POLICY_BASE=http://10.10.10.13:5601
|
||||
DETMIR_DLP_CASE_BASE=http://10.10.10.13:5602
|
||||
DETMIR_ONEC_BASE=http://10.10.10.2:8710
|
||||
DETMIR_GRAFANA_URL=http://10.10.10.11:3000
|
||||
DETMIR_AW_BASE=http://192.0.2.13:5600
|
||||
DETMIR_WORKTIME_BASE=http://192.0.2.13:5610
|
||||
DETMIR_DLP_POLICY_BASE=http://192.0.2.13:5601
|
||||
DETMIR_DLP_CASE_BASE=http://192.0.2.13:5602
|
||||
DETMIR_ONEC_BASE=http://192.0.2.2:8710
|
||||
DETMIR_GRAFANA_URL=http://192.0.2.11:3000
|
||||
DETMIR_GRAFANA_USER=
|
||||
DETMIR_GRAFANA_PASSWORD=
|
||||
DETMIR_DEFAULT_HOST=SHARKON2025
|
||||
DETMIR_DEFAULT_HOST=HOST-EXAMPLE
|
||||
DETMIR_HTTP_TIMEOUT_SECONDS=10
|
||||
DETMIR_MCP_TRANSPORT=stdio
|
||||
DETMIR_MCP_HOST=127.0.0.1
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
"tool_name": "worktime_today",
|
||||
"weight": 3,
|
||||
"arguments": {
|
||||
"host": "SHARKON2025",
|
||||
"host": "HOST-EXAMPLE",
|
||||
"day": "today",
|
||||
"top_n": 5
|
||||
}
|
||||
@@ -105,7 +105,7 @@
|
||||
"tool_name": "worktime_management",
|
||||
"weight": 2,
|
||||
"arguments": {
|
||||
"host": "SHARKON2025",
|
||||
"host": "HOST-EXAMPLE",
|
||||
"day": "today",
|
||||
"top_n": 5
|
||||
}
|
||||
@@ -115,7 +115,7 @@
|
||||
"tool_name": "dlp_health_summary",
|
||||
"weight": 2,
|
||||
"arguments": {
|
||||
"host": "SHARKON2025"
|
||||
"host": "HOST-EXAMPLE"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
+7
-7
@@ -26,15 +26,15 @@ def _env_int(name: str, default: int) -> int:
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
||||
GRAFANA_DASHBOARDS_DIR = PROJECT_ROOT / "grafana"
|
||||
AW_BASE = _env("DETMIR_AW_BASE", "http://10.10.10.13:5600").rstrip("/")
|
||||
WORKTIME_BASE = _env("DETMIR_WORKTIME_BASE", "http://10.10.10.13:5610").rstrip("/")
|
||||
DLP_POLICY_BASE = _env("DETMIR_DLP_POLICY_BASE", "http://10.10.10.13:5601").rstrip("/")
|
||||
DLP_CASE_BASE = _env("DETMIR_DLP_CASE_BASE", "http://10.10.10.13:5602").rstrip("/")
|
||||
ONEC_BASE = _env("DETMIR_ONEC_BASE", "http://10.10.10.2:8710").rstrip("/")
|
||||
GRAFANA_URL = _env("DETMIR_GRAFANA_URL", "http://10.10.10.11:3000").rstrip("/")
|
||||
AW_BASE = _env("DETMIR_AW_BASE", "http://192.0.2.13:5600").rstrip("/")
|
||||
WORKTIME_BASE = _env("DETMIR_WORKTIME_BASE", "http://192.0.2.13:5610").rstrip("/")
|
||||
DLP_POLICY_BASE = _env("DETMIR_DLP_POLICY_BASE", "http://192.0.2.13:5601").rstrip("/")
|
||||
DLP_CASE_BASE = _env("DETMIR_DLP_CASE_BASE", "http://192.0.2.13:5602").rstrip("/")
|
||||
ONEC_BASE = _env("DETMIR_ONEC_BASE", "http://192.0.2.2:8710").rstrip("/")
|
||||
GRAFANA_URL = _env("DETMIR_GRAFANA_URL", "http://192.0.2.11:3000").rstrip("/")
|
||||
GRAFANA_USER = os.environ.get("DETMIR_GRAFANA_USER", "")
|
||||
GRAFANA_PASSWORD = os.environ.get("DETMIR_GRAFANA_PASSWORD", "")
|
||||
DEFAULT_HOST = _env("DETMIR_DEFAULT_HOST", "SHARKON2025")
|
||||
DEFAULT_HOST = _env("DETMIR_DEFAULT_HOST", "HOST-EXAMPLE")
|
||||
HTTP_TIMEOUT_SECONDS = _env_int("DETMIR_HTTP_TIMEOUT_SECONDS", 10)
|
||||
MCP_TRANSPORT = _env("DETMIR_MCP_TRANSPORT", "stdio")
|
||||
MCP_HOST = _env("DETMIR_MCP_HOST", "127.0.0.1")
|
||||
|
||||
@@ -192,14 +192,14 @@ Grafana уже должна содержать:
|
||||
|
||||
Проверенная рабочая учётка:
|
||||
|
||||
- `SHARKON2025\Администратор`
|
||||
- `HOST-EXAMPLE\Администратор`
|
||||
|
||||
### 6.2 Команда переключения principal
|
||||
|
||||
На `<WINDOWS_HOST>`:
|
||||
|
||||
```cmd
|
||||
schtasks /Change /TN "\ActivityWatch File1C Upload" /RU "SHARKON2025\Администратор" /RP "<LOCAL_ADMIN_PASSWORD>"
|
||||
schtasks /Change /TN "\ActivityWatch File1C Upload" /RU "HOST-EXAMPLE\Администратор" /RP "<LOCAL_ADMIN_PASSWORD>"
|
||||
```
|
||||
|
||||
### 6.3 Проверка
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
### 1. Восстановление ActivityWatch-Russian
|
||||
|
||||
Симптом:
|
||||
- на `<AW_SERVER_HOST>:5600` UI не обновлялся для `SHARKON2025`;
|
||||
- на `<AW_SERVER_HOST>:5600` UI не обновлялся для `HOST-EXAMPLE`;
|
||||
- `aw-server` был жив, но stale были `aw-watcher-afk`, `aw-watcher-window`, `aw-worktime-sessions`.
|
||||
|
||||
Действия:
|
||||
@@ -33,7 +33,7 @@
|
||||
- через WinRM на `<WINDOWS_HOST>` проверены процессы, tasks и recovery-скрипты;
|
||||
- запущены:
|
||||
- `ActivityWatch Recovery`
|
||||
- `ActivityWatch Launch [SHARKON2025_user5]`
|
||||
- `ActivityWatch Launch [HOST-EXAMPLE_user5]`
|
||||
- отдельно перезапущен зависший `worktime-session-collector`.
|
||||
|
||||
Итог:
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
| `<FIREWALL_HOST>` | `pfSense`, firewall, VPN, ACL, OpenVPN export target |
|
||||
| `<GATEWAY_HOST>` | `Proxmox/DetMirAuto`, web gateway, Telegram bot, operator entrypoint |
|
||||
| `<AW_SERVER_HOST>` | основной `AW-rus` server, health, worktime/reporting, `Hayabusa` server-side processing |
|
||||
| `<WINDOWS_HOST>` | `SHARKON2025`, Windows/RDP host, collectors, worktime session path, EVTX export |
|
||||
| `<WINDOWS_HOST>` | `HOST-EXAMPLE`, Windows/RDP host, collectors, worktime session path, EVTX export |
|
||||
|
||||
Практический вывод:
|
||||
|
||||
|
||||
@@ -0,0 +1,336 @@
|
||||
# Установка экземпляра для эксперта
|
||||
|
||||
Документ описывает воспроизводимый путь: чистая VM -> установка -> проверка ->
|
||||
ожидаемый результат. Он не использует адреса, домены и учетные данные
|
||||
боевого стенда.
|
||||
|
||||
## 1. Цель проверки
|
||||
|
||||
Эксперт должен получить развернутый экземпляр DetMir/AWatch-rus, убедиться,
|
||||
что ПО собирается из исходного кода, устанавливается на чистую Linux VM,
|
||||
запускает базовые сервисы и проходит smoke-проверки без приватной
|
||||
инфраструктуры правообладателя.
|
||||
|
||||
## 2. Минимальный стенд
|
||||
|
||||
Рекомендуемая чистая VM:
|
||||
|
||||
- Debian 12 или Ubuntu Server 24.04 LTS;
|
||||
- 2 vCPU;
|
||||
- 4 GB RAM для минимальной проверки, 8 GB RAM для проверки Grafana/Prometheus;
|
||||
- 30 GB свободного диска;
|
||||
- доступ в интернет для установки пакетов и Rust crates;
|
||||
- пользователь с правами `sudo`;
|
||||
- корректные DNS, NTP и системное время.
|
||||
|
||||
Опционально для полной проверки контура:
|
||||
|
||||
- отдельная Windows VM или физический Windows host для collector toolkit;
|
||||
- отдельный host или VM для Grafana/Prometheus, если они не ставятся на ту же
|
||||
Linux VM;
|
||||
- закрытая тестовая сеть с адресами, заданными в `ansible/inventory.ini`.
|
||||
|
||||
## 3. Подготовка чистой VM
|
||||
|
||||
Войти на VM под пользователем с `sudo` и установить базовые инструменты:
|
||||
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
ca-certificates curl git jq unzip tar xz-utils \
|
||||
build-essential pkg-config libssl-dev sqlite3 \
|
||||
python3 python3-venv python3-pip ansible
|
||||
```
|
||||
|
||||
Проверить версии:
|
||||
|
||||
```bash
|
||||
git --version
|
||||
python3 --version
|
||||
ansible --version
|
||||
```
|
||||
|
||||
Ожидаемый результат:
|
||||
|
||||
- команды завершаются без ошибок;
|
||||
- установлен Git, Python 3 и Ansible;
|
||||
- VM имеет доступ к пакетным репозиториям.
|
||||
|
||||
## 4. Установка Rust toolchain
|
||||
|
||||
Установить стабильный Rust toolchain:
|
||||
|
||||
```bash
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
. "$HOME/.cargo/env"
|
||||
rustup default stable
|
||||
rustc --version
|
||||
cargo --version
|
||||
```
|
||||
|
||||
Ожидаемый результат:
|
||||
|
||||
- `rustc --version` и `cargo --version` выводят стабильную версию;
|
||||
- `$HOME/.cargo/bin` доступен в текущей shell-сессии.
|
||||
|
||||
## 5. Получение исходного кода
|
||||
|
||||
Склонировать репозиторий и перейти в рабочую директорию:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/igor04091968/AWatch-rus.git
|
||||
cd AWatch-rus
|
||||
git rev-parse --short HEAD
|
||||
```
|
||||
|
||||
Ожидаемый результат:
|
||||
|
||||
- репозиторий склонирован;
|
||||
- команда `git rev-parse --short HEAD` выводит commit id проверяемой версии.
|
||||
|
||||
## 6. Проверка публичной гигиены поставки
|
||||
|
||||
Перед установкой выполнить быстрый контроль отсутствия приватных маркеров в
|
||||
публичных документах:
|
||||
|
||||
```bash
|
||||
PRIVATE_MARKERS_REGEX='<PRIVATE_HOSTNAME>|<PRIVATE_PUBLIC_DOMAIN>|<LOCAL_OPERATOR_HOME>|<ROOT_PRIVATE_PATH>'
|
||||
git grep -n -E "$PRIVATE_MARKERS_REGEX" -- \
|
||||
README.md docs REGISTER_RU_SOFTWARE.md PRODUCT_DESCRIPTION_RU.md \
|
||||
SECURITY_OVERVIEW_RU.md adk-rust/RUNBOOK.md || true
|
||||
```
|
||||
|
||||
Ожидаемый результат:
|
||||
|
||||
- команда не должна находить реальные имена хостов, личные домены и личные
|
||||
пути оператора в публичных документах;
|
||||
- допустимы только нейтральные placeholders вроде `<AW_SERVER_HOST>`,
|
||||
`<PUBLIC_GATEWAY_FQDN>`, `HOST-EXAMPLE`.
|
||||
|
||||
## 7. Подготовка локальной конфигурации
|
||||
|
||||
Создать приватные файлы из примеров:
|
||||
|
||||
```bash
|
||||
cp private-config/deploy.env.example private-config/deploy.env
|
||||
cp ansible/inventory.example.ini ansible/inventory.ini
|
||||
```
|
||||
|
||||
Заполнить в `private-config/deploy.env` и `ansible/inventory.ini` только
|
||||
тестовые значения:
|
||||
|
||||
- адрес Linux VM;
|
||||
- адрес Windows host, если проверяются Windows collectors;
|
||||
- адрес Grafana/Prometheus, если они вынесены на отдельную VM;
|
||||
- тестовые учетные данные;
|
||||
- тестовые токены Telegram/Webhook, если проверяются уведомления.
|
||||
|
||||
Ожидаемый результат:
|
||||
|
||||
- приватная конфигурация существует локально;
|
||||
- приватные файлы не попадают в Git благодаря `.gitignore`;
|
||||
- в публичных файлах не появляются реальные пароли, IP и домены.
|
||||
|
||||
## 8. Сборка Rust-компонентов
|
||||
|
||||
Собрать workspace:
|
||||
|
||||
```bash
|
||||
cd adk-rust
|
||||
cargo build --release --workspace
|
||||
cd ..
|
||||
```
|
||||
|
||||
Ожидаемый результат:
|
||||
|
||||
- сборка завершается кодом `0`;
|
||||
- release-бинарники появляются в `adk-rust/target/release/`;
|
||||
- отсутствуют ошибки компиляции Rust-компонентов.
|
||||
|
||||
## 9. Локальные тесты до установки
|
||||
|
||||
Запустить базовые проверки исходников:
|
||||
|
||||
```bash
|
||||
cd adk-rust
|
||||
cargo fmt --all -- --check
|
||||
cargo test --workspace
|
||||
cd ..
|
||||
```
|
||||
|
||||
Проверить Ansible syntax:
|
||||
|
||||
```bash
|
||||
ansible-playbook --syntax-check -i ansible/inventory.ini ansible/deploy_aw_server.yml
|
||||
```
|
||||
|
||||
Ожидаемый результат:
|
||||
|
||||
- `cargo fmt` не сообщает diff;
|
||||
- `cargo test --workspace` завершается успешно;
|
||||
- Ansible syntax-check не находит ошибок YAML/playbook.
|
||||
|
||||
## 10. Установка минимального серверного экземпляра
|
||||
|
||||
Для чистой экспертной VM использовать inventory, где целевой Linux host
|
||||
указывает на эту же VM или на отдельный тестовый сервер.
|
||||
|
||||
Пример команды:
|
||||
|
||||
```bash
|
||||
ansible-playbook -i ansible/inventory.ini ansible/deploy_aw_server.yml
|
||||
```
|
||||
|
||||
Если playbook требует переменные окружения или группы hosts, задать их в
|
||||
локальном inventory, не меняя публичные файлы репозитория.
|
||||
|
||||
Ожидаемый результат:
|
||||
|
||||
- playbook завершается без failed tasks;
|
||||
- systemd units установлены на тестовый Linux host;
|
||||
- ActivityWatch server и DetMir helper-компоненты доступны локально на
|
||||
заданных портах;
|
||||
- конфигурация не содержит боевых адресов правообладателя.
|
||||
|
||||
## 11. Проверка сервисов после установки
|
||||
|
||||
На тестовом Linux host выполнить:
|
||||
|
||||
```bash
|
||||
systemctl --failed --no-pager
|
||||
systemctl status activitywatch-server --no-pager
|
||||
```
|
||||
|
||||
Если установлены Rust helper-бинарники:
|
||||
|
||||
```bash
|
||||
detmir-check --json
|
||||
detmir-status --json
|
||||
```
|
||||
|
||||
Ожидаемый результат:
|
||||
|
||||
- `systemctl --failed` не показывает критичных failed units DetMir/AWatch-rus;
|
||||
- `activitywatch-server` находится в состоянии `active`;
|
||||
- `detmir-check --json` возвращает машинно-читаемый статус без критичных
|
||||
ошибок;
|
||||
- `detmir-status --json` показывает агрегированный статус установленного
|
||||
экземпляра.
|
||||
|
||||
## 12. Проверка HTTP API
|
||||
|
||||
Проверить ActivityWatch API:
|
||||
|
||||
```bash
|
||||
curl -fsS http://127.0.0.1:5600/api/0/info | jq .
|
||||
curl -fsS http://127.0.0.1:5600/api/0/buckets/ | jq 'keys | length'
|
||||
```
|
||||
|
||||
Ожидаемый результат:
|
||||
|
||||
- `/api/0/info` возвращает JSON;
|
||||
- `/api/0/buckets/` возвращает JSON-объект;
|
||||
- ошибки `connection refused`, `403`, `500` отсутствуют.
|
||||
|
||||
## 13. Проверка Grafana/Prometheus
|
||||
|
||||
Если в экспертном стенде установлены Grafana/Prometheus:
|
||||
|
||||
```bash
|
||||
curl -fsS http://127.0.0.1:3000/api/health | jq .
|
||||
curl -fsS http://127.0.0.1:9090/-/ready
|
||||
```
|
||||
|
||||
Ожидаемый результат:
|
||||
|
||||
- Grafana health API возвращает статус `ok` или `database: ok`;
|
||||
- Prometheus ready endpoint возвращает успешный HTTP status;
|
||||
- DetMir dashboards импортированы или доступны через documented provisioning
|
||||
path.
|
||||
|
||||
## 14. Проверка Windows collectors
|
||||
|
||||
Этот шаг нужен только для полной проверки контура.
|
||||
|
||||
На Windows host выполнить PowerShell deployment из `windows/` или Ansible
|
||||
playbook для Windows collectors, используя тестовый domain/hostname:
|
||||
|
||||
```powershell
|
||||
Get-ScheduledTask | Where-Object TaskName -like 'ActivityWatch*'
|
||||
```
|
||||
|
||||
На Linux server проверить появление buckets:
|
||||
|
||||
```bash
|
||||
curl -fsS http://127.0.0.1:5600/api/0/buckets/ | jq 'keys[]' | sort
|
||||
```
|
||||
|
||||
Ожидаемый результат:
|
||||
|
||||
- Windows tasks созданы с тестовыми именами host/user;
|
||||
- ActivityWatch buckets получают события от Windows collectors;
|
||||
- в bucket names нет приватных имен боевого стенда.
|
||||
|
||||
## 15. Smoke-проверка портала оператора
|
||||
|
||||
Если установлен portal runtime:
|
||||
|
||||
```bash
|
||||
curl -fsS http://127.0.0.1:8080/health
|
||||
```
|
||||
|
||||
Ожидаемый результат:
|
||||
|
||||
- health endpoint возвращает успешный HTTP status;
|
||||
- карточки портала открываются по локальным адресам стенда;
|
||||
- ссылки на Grafana/ActivityWatch/incident evidence используют значения из
|
||||
конфигурации, а не захардкоженные адреса.
|
||||
|
||||
## 16. Итоговый критерий приемки
|
||||
|
||||
Экземпляр считается установленным корректно, если:
|
||||
|
||||
- исходный код собирается командой `cargo build --release --workspace`;
|
||||
- локальные Rust tests проходят;
|
||||
- Ansible syntax-check проходит;
|
||||
- серверный playbook завершается без failed tasks;
|
||||
- ActivityWatch API отвечает JSON;
|
||||
- `systemctl --failed` не показывает критичных failures;
|
||||
- `detmir-check` и `detmir-status` отрабатывают;
|
||||
- публичные документы не содержат приватных IP, доменов, hostnames и путей;
|
||||
- third-party license inventory и release/SBOM checklist заполнены.
|
||||
|
||||
## 17. Сбор диагностического пакета для эксперта
|
||||
|
||||
После проверки сохранить артефакты:
|
||||
|
||||
```bash
|
||||
mkdir -p expert-check-output
|
||||
git rev-parse HEAD > expert-check-output/commit.txt
|
||||
systemctl --failed --no-pager > expert-check-output/systemd-failed.txt
|
||||
curl -fsS http://127.0.0.1:5600/api/0/info > expert-check-output/aw-info.json
|
||||
detmir-check --json > expert-check-output/detmir-check.json || true
|
||||
detmir-status --json > expert-check-output/detmir-status.json || true
|
||||
```
|
||||
|
||||
Ожидаемый результат:
|
||||
|
||||
- в `expert-check-output/` есть commit id, systemd summary и JSON-проверки;
|
||||
- пакет не содержит паролей, токенов и приватных доменов.
|
||||
|
||||
## 18. Очистка тестового экземпляра
|
||||
|
||||
Для удаления тестовой VM достаточно уничтожить саму VM. Если нужно очистить
|
||||
только сервисы внутри VM, сначала сохранить diagnostic output, затем остановить
|
||||
установленные units:
|
||||
|
||||
```bash
|
||||
sudo systemctl stop activitywatch-server || true
|
||||
sudo systemctl list-units 'aw-*' 'detmir-*' --no-pager
|
||||
```
|
||||
|
||||
Ожидаемый результат:
|
||||
|
||||
- экспертский стенд можно воспроизвести заново из исходников и локальной
|
||||
конфигурации;
|
||||
- очистка не требует доступа к боевому стенду правообладателя.
|
||||
@@ -0,0 +1,277 @@
|
||||
# SBOM и release checklist
|
||||
|
||||
Документ фиксирует минимальный порядок подготовки публичного релиза
|
||||
DetMir/AWatch-rus: исходники, сборка, артефакты, сторонние компоненты,
|
||||
лицензии, публичная гигиена и экспертная проверка.
|
||||
|
||||
## 1. Идентификация релиза
|
||||
|
||||
Перед сборкой зафиксировать:
|
||||
|
||||
- product name: `DetMir, программный комплекс AWatch-rus`;
|
||||
- repository: `AWatch-rus`;
|
||||
- release tag: `vX.Y.Z`;
|
||||
- commit: вывод `git rev-parse HEAD`;
|
||||
- дата сборки;
|
||||
- ответственный правообладатель/maintainer;
|
||||
- состав release assets.
|
||||
|
||||
Команды:
|
||||
|
||||
```bash
|
||||
git status --short
|
||||
git rev-parse HEAD
|
||||
git tag --points-at HEAD
|
||||
```
|
||||
|
||||
Критерий:
|
||||
|
||||
- рабочее дерево не содержит незапланированных tracked-изменений;
|
||||
- tag указывает на проверенный commit;
|
||||
- release notes соответствуют фактическому составу поставки.
|
||||
|
||||
## 2. Публичная гигиена репозитория
|
||||
|
||||
Проверить отсутствие приватных идентификаторов в публичных документах:
|
||||
|
||||
```bash
|
||||
PRIVATE_MARKERS_REGEX='<PRIVATE_HOSTNAME>|<PRIVATE_PUBLIC_DOMAIN>|<LOCAL_OPERATOR_HOME>|<ROOT_PRIVATE_PATH>'
|
||||
git grep -n -E "$PRIVATE_MARKERS_REGEX" -- \
|
||||
README.md docs REGISTER_RU_SOFTWARE.md PRODUCT_DESCRIPTION_RU.md \
|
||||
SECURITY_OVERVIEW_RU.md adk-rust/RUNBOOK.md || true
|
||||
```
|
||||
|
||||
Проверить реальные IP в release-facing документах:
|
||||
|
||||
```bash
|
||||
git grep -n -E '10\\.10\\.10\\.|192\\.168\\.' -- \
|
||||
README.md docs REGISTER_RU_SOFTWARE.md PRODUCT_DESCRIPTION_RU.md \
|
||||
SECURITY_OVERVIEW_RU.md adk-rust/RUNBOOK.md || true
|
||||
```
|
||||
|
||||
Критерий:
|
||||
|
||||
- в документах нет приватных IP, доменов, usernames и live hostnames;
|
||||
- допускаются только placeholders: `<AW_SERVER_HOST>`, `<GRAFANA_HOST>`,
|
||||
`<PUBLIC_GATEWAY_FQDN>`, `HOST-EXAMPLE`, `WINDOWS_USER_EXAMPLE`;
|
||||
- приватные configs находятся только в ignored-файлах.
|
||||
|
||||
## 3. Контроль секретов
|
||||
|
||||
Проверить, что в репозиторий не попали secrets:
|
||||
|
||||
```bash
|
||||
git ls-files | grep -E '(^|/)secrets(/|$)|\\.env$|inventory\\.ini$' || true
|
||||
git grep -n -E 'sk_[A-Za-z0-9]|pk_[A-Za-z0-9]|BEGIN OPENSSH PRIVATE KEY|password\\s*=' -- . || true
|
||||
```
|
||||
|
||||
Рекомендуемый дополнительный контроль:
|
||||
|
||||
```bash
|
||||
gitleaks detect --source . --no-git --redact
|
||||
```
|
||||
|
||||
Критерий:
|
||||
|
||||
- директория `secrets/` не отслеживается Git;
|
||||
- `.env`, `inventory.ini`, tokens, passwords и private keys отсутствуют в
|
||||
tracked-файлах;
|
||||
- если секрет ранее был опубликован, он ротируется вне этого checklist.
|
||||
|
||||
## 4. Сборка Rust workspace
|
||||
|
||||
Собрать Rust-компоненты:
|
||||
|
||||
```bash
|
||||
cd adk-rust
|
||||
cargo fmt --all -- --check
|
||||
cargo test --workspace
|
||||
cargo clippy --workspace --all-targets -- -D warnings
|
||||
cargo build --release --workspace
|
||||
cd ..
|
||||
```
|
||||
|
||||
Критерий:
|
||||
|
||||
- formatting clean;
|
||||
- tests green;
|
||||
- clippy без warnings;
|
||||
- release-бинарники собраны.
|
||||
|
||||
## 5. Проверка Python-исключений
|
||||
|
||||
Python в проекте допускается только как:
|
||||
|
||||
- Telegram runtime, который решено оставить на Python;
|
||||
- совместимые legacy fallbacks;
|
||||
- installer/ops helpers, пока они не являются ядром продукта;
|
||||
- тестовые и migration utilities.
|
||||
|
||||
Проверка:
|
||||
|
||||
```bash
|
||||
git ls-files '*.py'
|
||||
```
|
||||
|
||||
Критерий:
|
||||
|
||||
- Python-файлы имеют понятную роль;
|
||||
- ядро DetMir/AWatch-rus позиционируется как Rust-first;
|
||||
- README/registry docs не обещают отсутствие Python там, где он еще остается.
|
||||
|
||||
## 6. Third-party license inventory
|
||||
|
||||
Основной license inventory:
|
||||
|
||||
- [`../THIRD_PARTY_LICENSES_RU.md`](../THIRD_PARTY_LICENSES_RU.md)
|
||||
|
||||
Проверить минимум:
|
||||
|
||||
- ActivityWatch components: MPL-2.0 или лицензии конкретных upstream parts;
|
||||
- Grafana: AGPL-3.0 или актуальная лицензия используемой версии;
|
||||
- Prometheus: Apache-2.0;
|
||||
- Hayabusa: лицензия upstream и правила распространения;
|
||||
- Ansible: GPL-3.0-or-later;
|
||||
- Rust crates: по `cargo metadata`, `cargo about`, `cargo deny`;
|
||||
- Python dependencies: по `pip-licenses` или lock-файлам;
|
||||
- JavaScript/Node dependencies для Playwright/UI helpers: по `npm ls` и
|
||||
package metadata.
|
||||
|
||||
Критерий:
|
||||
|
||||
- для каждого крупного компонента указан upstream, license, роль и риск;
|
||||
- copyleft-компоненты не скрыты;
|
||||
- release notes не противоречат лицензиям.
|
||||
|
||||
## 7. SBOM
|
||||
|
||||
Сформировать машинные перечни зависимостей.
|
||||
|
||||
Rust:
|
||||
|
||||
```bash
|
||||
cd adk-rust
|
||||
cargo metadata --format-version 1 > ../sbom-cargo-metadata.json
|
||||
cargo tree --workspace > ../sbom-cargo-tree.txt
|
||||
cd ..
|
||||
```
|
||||
|
||||
Python, если используется виртуальное окружение:
|
||||
|
||||
```bash
|
||||
python3 -m pip freeze > sbom-python-freeze.txt
|
||||
```
|
||||
|
||||
Node/Playwright, если используется frontend smoke tooling:
|
||||
|
||||
```bash
|
||||
npm ls --all --json > sbom-npm-tree.json
|
||||
```
|
||||
|
||||
OS packages на эталонной VM:
|
||||
|
||||
```bash
|
||||
dpkg-query -W -f='${Package}\\t${Version}\\n' > sbom-debian-packages.tsv
|
||||
```
|
||||
|
||||
Критерий:
|
||||
|
||||
- SBOM artifacts приложены к release assets или сохранены в build archive;
|
||||
- SBOM не содержит секретов;
|
||||
- SBOM соответствует проверяемому commit/tag.
|
||||
|
||||
## 8. Release assets
|
||||
|
||||
Собрать install-kit и бинарные артефакты только из проверенного commit:
|
||||
|
||||
```bash
|
||||
scripts/rebuild_install_kit.sh
|
||||
scripts/validate_install_kit.sh
|
||||
```
|
||||
|
||||
Критерий:
|
||||
|
||||
- dated zip/tar.gz не лежат в корне tracked-репозитория;
|
||||
- архивы публикуются как GitHub Release assets;
|
||||
- checksum каждого asset зафиксирован.
|
||||
|
||||
Пример фиксации checksum:
|
||||
|
||||
```bash
|
||||
sha256sum dist/* install-kit-awindows-*.zip install-kit-awindows-*.tar.gz \
|
||||
> SHA256SUMS
|
||||
```
|
||||
|
||||
## 9. Документы для реестра российского ПО
|
||||
|
||||
Проверить наличие и актуальность:
|
||||
|
||||
- `REGISTER_RU_SOFTWARE.md`;
|
||||
- `PRODUCT_DESCRIPTION_RU.md`;
|
||||
- `THIRD_PARTY_LICENSES_RU.md`;
|
||||
- `docs/INSTALL_FOR_EXPERT_RU.md`;
|
||||
- `docs/ARCHITECTURE_RU.md`;
|
||||
- `docs/ADMIN_GUIDE_RU.md`;
|
||||
- `docs/OPERATOR_GUIDE_RU.md`;
|
||||
- `docs/OWNERSHIP_RU.md`;
|
||||
- `docs/REGISTRY_CHECKLIST_RU.md`.
|
||||
|
||||
Критерий:
|
||||
|
||||
- документы согласованы по названию продукта;
|
||||
- не заявляются DLP/SIEM/EDR/XDR/сертифицированная СЗИ как основной класс;
|
||||
- позиционирование: операционный контроль, технический аудит, автоматизация
|
||||
эксплуатации ИТ-инфраструктуры.
|
||||
|
||||
## 10. Экспертная установка
|
||||
|
||||
Проверить релиз на чистой VM по:
|
||||
|
||||
- [`INSTALL_FOR_EXPERT_RU.md`](INSTALL_FOR_EXPERT_RU.md)
|
||||
|
||||
Критерий:
|
||||
|
||||
- чистая VM проходит путь установка -> сборка -> проверка;
|
||||
- ActivityWatch API отвечает;
|
||||
- базовые DetMir checks работают;
|
||||
- diagnostic bundle собран;
|
||||
- инструкция воспроизводима без доступа к личному стенду разработчика.
|
||||
|
||||
## 11. GitHub metadata
|
||||
|
||||
Проверить публичную страницу репозитория:
|
||||
|
||||
- description заполнен;
|
||||
- website/homepage заполнен;
|
||||
- topics заполнены;
|
||||
- license отображается;
|
||||
- releases содержат бинарные/install-kit assets;
|
||||
- root README не содержит приватных адресов и личных путей.
|
||||
|
||||
Критерий:
|
||||
|
||||
- проект выглядит как поставляемое ПО, а не как личный стенд;
|
||||
- About/Topics/Website заполнены;
|
||||
- license detected на GitHub.
|
||||
|
||||
## 12. Финальный gate перед публикацией
|
||||
|
||||
Команды:
|
||||
|
||||
```bash
|
||||
git diff --check
|
||||
git status --short
|
||||
PRIVATE_MARKERS_REGEX='<PRIVATE_HOSTNAME>|<PRIVATE_PUBLIC_DOMAIN>|<LOCAL_OPERATOR_HOME>|<ROOT_PRIVATE_PATH>'
|
||||
git grep -n -E "$PRIVATE_MARKERS_REGEX" -- \
|
||||
README.md docs REGISTER_RU_SOFTWARE.md PRODUCT_DESCRIPTION_RU.md \
|
||||
SECURITY_OVERVIEW_RU.md adk-rust/RUNBOOK.md || true
|
||||
```
|
||||
|
||||
Критерий публикации:
|
||||
|
||||
- diff не содержит whitespace errors;
|
||||
- tracked changes входят в один понятный commit;
|
||||
- release-facing docs обезличены;
|
||||
- install-kit artifacts вынесены в Release assets;
|
||||
- SBOM/license checklist приложен или воспроизводим;
|
||||
- tag создан только после прохождения проверок.
|
||||
@@ -10,7 +10,7 @@ This document records the production-verified state of advanced content analysis
|
||||
- `contentAnalysis.regexPack`
|
||||
- `contentAnalysis.ocrEnabled`
|
||||
- `ioc.*`
|
||||
- Historical incidents in `aw-dlp-incidents_SHARKON2025` already contain enriched fields:
|
||||
- Historical incidents in `aw-dlp-incidents_HOST-EXAMPLE` already contain enriched fields:
|
||||
- `dictionaryMatches`
|
||||
- `regexMatches`
|
||||
- `ocrRequested`
|
||||
|
||||
@@ -27,8 +27,8 @@ Verified on `<AW_SERVER_HOST>`:
|
||||
- recent journal runs are clean
|
||||
- current runtime result is `sent=0` / `delivered=0` because no new incidents were generated since the last seen bucket event
|
||||
- `endpoint -> incident ingest`
|
||||
- `aw-dlp-endpoint-signals_SHARKON2025` fresh
|
||||
- `aw-dlp-incidents_SHARKON2025` exists and contains valid historical incidents
|
||||
- `aw-dlp-endpoint-signals_HOST-EXAMPLE` fresh
|
||||
- `aw-dlp-incidents_HOST-EXAMPLE` exists and contains valid historical incidents
|
||||
- `health/admin`
|
||||
- `/usr/local/bin/dlp-health-check --json` = `ok=true`
|
||||
- `/usr/local/bin/dlp-admin-cli.py health check` = policy/cases/aw OK
|
||||
|
||||
@@ -42,13 +42,13 @@ aw_windows_builtin_administrator_name: "Администратор"
|
||||
|
||||
Назначение: явно фиксировать локализованное имя встроенной учетной записи Administrator с SID `*-500`.
|
||||
|
||||
Для текущего Windows host `SHARKON2025` task name должен строиться как:
|
||||
Для текущего Windows host `HOST-EXAMPLE` task name должен строиться как:
|
||||
|
||||
```text
|
||||
ActivityWatch Launch [SHARKON2025_Администратор]
|
||||
ActivityWatch Launch [HOST-EXAMPLE_Администратор]
|
||||
```
|
||||
|
||||
Если task по `SHARKON2025_Administrator` не найден, recovery/deploy path обязан пробовать кириллическое имя `Администратор`. Это зафиксировано через:
|
||||
Если task по `HOST-EXAMPLE_Administrator` не найден, recovery/deploy path обязан пробовать кириллическое имя `Администратор`. Это зафиксировано через:
|
||||
|
||||
- default vars в `ansible/deploy_aw_windows.yml`;
|
||||
- `ansible/group_vars/aw_windows.yml`;
|
||||
@@ -60,7 +60,7 @@ ActivityWatch Launch [SHARKON2025_Администратор]
|
||||
|
||||
`ActivityWatch.Windows.Common.psm1` усилил recovery path:
|
||||
|
||||
- `Get-ActivityWatchBuiltInAdministratorName` сначала смотрит env override, затем SID-500 lookup, затем host-specific fallback `SHARKON2025 -> Администратор`;
|
||||
- `Get-ActivityWatchBuiltInAdministratorName` сначала смотрит env override, затем SID-500 lookup, затем host-specific fallback `HOST-EXAMPLE -> Администратор`;
|
||||
- `Normalize-ActivityWatchUsers` стабилизирован для pipeline/list cases;
|
||||
- удаление scheduled tasks стало устойчивее к частично удаленным task definitions;
|
||||
- recovery task может ориентироваться на live interactive session и запускаться в interactive logon context, когда это безопаснее для watcher'ов.
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
### 1. Multi-user RDP host
|
||||
|
||||
Use this model on `SHARKON2025`-style hosts with multiple user sessions.
|
||||
Use this model on `HOST-EXAMPLE`-style hosts with multiple user sessions.
|
||||
|
||||
- `AWatchRusCollectorGuard` service:
|
||||
- runs under `LocalSystem`
|
||||
|
||||
@@ -191,7 +191,7 @@ Ansible playbook `ansible/deploy_aw_windows.yml` выполняет этот mig
|
||||
.\windows\deploy-domain-users.ps1 `
|
||||
-ServerHost <AW_SERVER_HOST> `
|
||||
-ServerPort 5600 `
|
||||
-Domain SHARKON2025 `
|
||||
-Domain HOST-EXAMPLE `
|
||||
-Users user2,user3,user4,user5 `
|
||||
-InstallRoot 'C:\Program Files\AWatch-rus\bin' `
|
||||
-StateRoot 'C:\ProgramData\AWatch-rus' `
|
||||
@@ -205,7 +205,7 @@ Single-user pilot в таком же стиле:
|
||||
.\windows\deploy-single-user.ps1 `
|
||||
-ServerHost <AW_SERVER_HOST> `
|
||||
-ServerPort 5600 `
|
||||
-TargetUser 'SHARKON2025\user1' `
|
||||
-TargetUser 'HOST-EXAMPLE\user1' `
|
||||
-InstallRoot 'C:\Program Files\AWatch-rus\bin' `
|
||||
-StateRoot 'C:\ProgramData\AWatch-rus' `
|
||||
-CustomRulesPath C:\Program Files\AWatch-rus\windows\web-category-rules.example.json `
|
||||
|
||||
@@ -20,7 +20,7 @@ Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process
|
||||
C:\Program Files\AWatch-rus\windows\deploy-ensemble.ps1 `
|
||||
-ServerHost <AW_SERVER_HOST> `
|
||||
-ServerPort 5600 `
|
||||
-Domain SHARKON2025 `
|
||||
-Domain HOST-EXAMPLE `
|
||||
-Users user1,user2,user3,user4,user5 `
|
||||
-InstallRoot 'C:\Program Files\AWatch-rus\bin' `
|
||||
-StateRoot 'C:\ProgramData\AWatch-rus' `
|
||||
|
||||
@@ -43,7 +43,7 @@ Get-ScheduledTask -TaskName 'ActivityWatch*' |
|
||||
Точечная проверка:
|
||||
|
||||
```powershell
|
||||
Get-ScheduledTask | Where-Object TaskName -eq 'ActivityWatch Launch [SHARKON2025_user1]'
|
||||
Get-ScheduledTask | Where-Object TaskName -eq 'ActivityWatch Launch [HOST-EXAMPLE_user1]'
|
||||
Get-ScheduledTask | Where-Object TaskName -eq 'ActivityWatch Recovery'
|
||||
```
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
Подходит для расчета рабочего времени в толстых клиентах (1С, документы, админка).
|
||||
|
||||
```javascript
|
||||
events = flood(query_bucket("aw-watcher-window_SHARKON2025"));
|
||||
not_afk = flood(query_bucket("aw-watcher-afk_SHARKON2025"));
|
||||
events = flood(query_bucket("aw-watcher-window_HOST-EXAMPLE"));
|
||||
not_afk = flood(query_bucket("aw-watcher-afk_HOST-EXAMPLE"));
|
||||
not_afk = filter_keyvals(not_afk, "status", ["not-afk"]);
|
||||
|
||||
events = filter_period_intersect(events, not_afk);
|
||||
@@ -41,8 +41,8 @@ RETURN = sort_by_duration(work);
|
||||
`rootDomain=proxmox-webui`, `categoryGroup=work`, `category=Администрирование`.
|
||||
|
||||
```javascript
|
||||
web = flood(query_bucket("aw-detmir-web-category_SHARKON2025"));
|
||||
not_afk = flood(query_bucket("aw-watcher-afk_SHARKON2025"));
|
||||
web = flood(query_bucket("aw-detmir-web-category_HOST-EXAMPLE"));
|
||||
not_afk = flood(query_bucket("aw-watcher-afk_HOST-EXAMPLE"));
|
||||
not_afk = filter_keyvals(not_afk, "status", ["not-afk"]);
|
||||
|
||||
web = filter_period_intersect(web, not_afk);
|
||||
@@ -55,8 +55,8 @@ RETURN = sort_by_duration(web);
|
||||
## Sanity-check: «куда уходит время»
|
||||
|
||||
```javascript
|
||||
events = flood(query_bucket("aw-watcher-window_SHARKON2025"));
|
||||
not_afk = flood(query_bucket("aw-watcher-afk_SHARKON2025"));
|
||||
events = flood(query_bucket("aw-watcher-window_HOST-EXAMPLE"));
|
||||
not_afk = flood(query_bucket("aw-watcher-afk_HOST-EXAMPLE"));
|
||||
not_afk = filter_keyvals(not_afk, "status", ["not-afk"]);
|
||||
|
||||
events = filter_period_intersect(events, not_afk);
|
||||
@@ -68,7 +68,7 @@ RETURN = sort_by_duration(events);
|
||||
|
||||
## Замечания
|
||||
|
||||
- Для других хостов замените суффикс `_SHARKON2025` на нужный hostname.
|
||||
- Для других хостов замените суффикс `_HOST-EXAMPLE` на нужный hostname.
|
||||
- Если web-поток пустой, рабочее время в браузере корректно посчитать по доменам не получится. Тогда либо:
|
||||
- чинить/запускать browser collector;
|
||||
- либо временно считать браузер в `window` как «Интернет/Браузер» без разделения на work/personal.
|
||||
@@ -82,7 +82,7 @@ RETURN = sort_by_duration(events);
|
||||
«кто и когда вообще был в активной удалённой сессии».
|
||||
|
||||
```javascript
|
||||
sessions = flood(query_bucket("aw-worktime-sessions_SHARKON2025"));
|
||||
sessions = flood(query_bucket("aw-worktime-sessions_HOST-EXAMPLE"));
|
||||
sessions = filter_keyvals(sessions, "active", [true]);
|
||||
sessions = merge_events_by_keys(sessions, ["username", "sessionName", "state"]);
|
||||
RETURN = sort_by_duration(sessions);
|
||||
|
||||
@@ -5,8 +5,8 @@ GRAFANA_PORT=3000
|
||||
PROMETHEUS_PORT=9090
|
||||
SQL_EXPORTER_PORT=9399
|
||||
AW_EXPORTER_PORT=9398
|
||||
AW_SERVER_HOST=10.10.10.13
|
||||
AW_SERVER_HOST=192.0.2.13
|
||||
AW_SERVER_PORT=5600
|
||||
AW_SERVER_SCHEME=http
|
||||
AW_SCRAPE_INTERVAL_SECONDS=30
|
||||
ONEC_DSN=postgres://onec_reader:change_me@10.10.10.20:5432/onec_db?sslmode=disable
|
||||
ONEC_DSN=postgres://onec_reader:change_me@192.0.2.20:5432/onec_db?sslmode=disable
|
||||
|
||||
@@ -6,7 +6,7 @@ services:
|
||||
container_name: awrus-aw-exporter
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- AW_SERVER_HOST=${AW_SERVER_HOST:-10.10.10.13}
|
||||
- AW_SERVER_HOST=${AW_SERVER_HOST:-192.0.2.13}
|
||||
- AW_SERVER_PORT=${AW_SERVER_PORT:-5600}
|
||||
- AW_SERVER_SCHEME=${AW_SERVER_SCHEME:-http}
|
||||
- EXPORTER_PORT=9398
|
||||
|
||||
@@ -16,7 +16,7 @@ logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Configuration
|
||||
AW_SERVER_HOST = os.getenv("AW_SERVER_HOST", "10.10.10.13")
|
||||
AW_SERVER_HOST = os.getenv("AW_SERVER_HOST", "192.0.2.13")
|
||||
AW_SERVER_PORT = int(os.getenv("AW_SERVER_PORT", "5600"))
|
||||
AW_SERVER_SCHEME = os.getenv("AW_SERVER_SCHEME", "http")
|
||||
AW_API_BASE = os.getenv(
|
||||
|
||||
@@ -362,8 +362,8 @@
|
||||
{
|
||||
"current": {
|
||||
"selected": false,
|
||||
"text": "SHARKON2025",
|
||||
"value": "SHARKON2025"
|
||||
"text": "HOST-EXAMPLE",
|
||||
"value": "HOST-EXAMPLE"
|
||||
},
|
||||
"datasource": {
|
||||
"type": "influxdb",
|
||||
|
||||
@@ -724,8 +724,8 @@
|
||||
{
|
||||
"current": {
|
||||
"selected": false,
|
||||
"text": "SHARKON2025",
|
||||
"value": "SHARKON2025"
|
||||
"text": "HOST-EXAMPLE",
|
||||
"value": "HOST-EXAMPLE"
|
||||
},
|
||||
"datasource": {
|
||||
"type": "influxdb",
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
856d2467afb4b413ae7833ba55340db01bd807f10d5200752b95cc113758c54e install-kit-awindows-20260427-211240/README-INSTALL-KIT.txt
|
||||
556a9f7524b61309b00b641495b6daa212115411aa5a6fc82d1537658ac0e719 install-kit-awindows-20260427-211240/ansible/README.md
|
||||
412bb766bbf0791c3593f38daa771d5d0aa58cc1f2d3c9010fcd4588d0fe87df install-kit-awindows-20260427-211240/ansible/deploy_aw_pfsense_poller.yml
|
||||
ac091618a6793a9be9b52eeb78267068df16d4f57e0051b9d868813c4a641f35 install-kit-awindows-20260427-211240/ansible/deploy_aw_server.yml
|
||||
cd09574fd0d55bf62d9d7df757bb206b5184ba9ffdcc200e755700e50cfa41f3 install-kit-awindows-20260427-211240/ansible/deploy_aw_windows.yml
|
||||
4b6f658adb98d548eedd10a0d44ff224b4d46a663d8c11e038f18597503f6c27 install-kit-awindows-20260427-211240/ansible/group_vars/all.example.yml
|
||||
dbaa55e66c65bf7e4e943c8070e53d002d8a794ffcc0593d7a8a07a5f99aa306 install-kit-awindows-20260427-211240/ansible/group_vars/pfsense-poller.example.yml
|
||||
c5cab36645065815571c99f6d360f910dcccbb54b780c8bfd526a6cdc3684e19 install-kit-awindows-20260427-211240/ansible/group_vars/proxmox-matrix.example.yml
|
||||
35a33c8a1c75ded5e85c6b79e0b3efde07959ff61ee5f66d83b7e0c2abe87fc5 install-kit-awindows-20260427-211240/ansible/group_vars/proxmox.example.yml
|
||||
9e4939d2d951870cacab6e42c5bf0ccd03350089dc0d32b9a681948ff96754d2 install-kit-awindows-20260427-211240/ansible/group_vars/windows.example.yml
|
||||
7189b5205bd25313db54e5be027b0d066199e6ae34ad74be2095a1691adaf5e3 install-kit-awindows-20260427-211240/ansible/install_full_stack.yml
|
||||
1abe542724e617c285deaef76514d49c83c148ddbee90bc99b800e9049b70444 install-kit-awindows-20260427-211240/ansible/inventory.example.ini
|
||||
3adda7422ffbbb76be50858f2f1ed6073b6bf9ee0b57283c41d0fabd3e7c7f9e install-kit-awindows-20260427-211240/ansible/provision_proxmox_ct_and_deploy_aw.yml
|
||||
234932632092226526173b9ef906de2cce961bc95be0f37c878e48d9779e1de7 install-kit-awindows-20260427-211240/ansible/provision_proxmox_ct_matrix_and_deploy_aw.yml
|
||||
a74a49371e889dc3ea404534a939f32f2dac940d8902d20770590951ab67d532 install-kit-awindows-20260427-211240/ansible/tasks/provision_ct_and_deploy_aw.yml
|
||||
ba16fe9e267194459a6082045a387acb828a1d39a98e66b401ece5069ea62e64 install-kit-awindows-20260427-211240/aw-server/activitywatch-server.service
|
||||
fea5734c99b516b01bfe8ebbef59972d0ef553c09d7691790b08472ecfb9602d install-kit-awindows-20260427-211240/aw-server/apply_webui_ru_patch.sh
|
||||
fabcdbb933b1d3b605c5ac1234af4e10495c09f18bd50931bf4a92081fcb5023 install-kit-awindows-20260427-211240/aw-server/aw-browser-smoke.service
|
||||
e59d58d4cc6b2b0dceef5b1da14ca0a1b24a202ce576ee43028f44ffe43c5ef4 install-kit-awindows-20260427-211240/aw-server/aw-browser-smoke.timer
|
||||
ba88cc284d047f521427ac038c266624a6fe8493ce3e79bc27c172a2e70ac54a install-kit-awindows-20260427-211240/aw-server/aw-host-groups.json
|
||||
509759461ce0918a2dc839832812cf5f4c77b1cea1e6ffdcab0146e02598df79 install-kit-awindows-20260427-211240/aw-server/aw-ru-patch.js
|
||||
699f697143f6be588c698bad9a14a45c736fe74f5e4b0e0ea0f48b569c7df396 install-kit-awindows-20260427-211240/aw-server/aw-rus-healthd.service
|
||||
8ae02b42f4bc33d95268624bae79c2101298b7ec6644553a1e81f7841dda74d8 install-kit-awindows-20260427-211240/aw-server/aw-rus-healthd.timer
|
||||
7580282be0a3bf057e42ca6f733a64bf0d2f51d096e02a57cde9125064ac04d1 install-kit-awindows-20260427-211240/aw-server/aw-server.env.example
|
||||
18d038e67b926eca04bec725ed5e6de34b93f8330c3b30a0b07d52eab414bd03 install-kit-awindows-20260427-211240/aw-server/aw-slo-monitor.service
|
||||
257cdc18f088e8848b1bee69e7f8b99aa860a0f600eebd74365a40ed94aa5ecb install-kit-awindows-20260427-211240/aw-server/aw-slo-monitor.timer
|
||||
98c0bed353bbda0fa7a69df23f3b008cb0e8e70cdff6cc63330d4caf79fd3280 install-kit-awindows-20260427-211240/aw-server/aw-sw-cleanup.js
|
||||
0e555853f1cb498e63b70d305ce5facfaa626a2ceee6f7edb854cf37c4371f65 install-kit-awindows-20260427-211240/aw-server/aw-worktime-api.service
|
||||
afbb4be301b4940ed9b7671be3441a48d53bee968d8870749442f81c6d066650 install-kit-awindows-20260427-211240/aw-server/aw-worktime-panel.js
|
||||
7e37dd8bbfdbbe661defb9ce69284db8ad339a49b34f8c507060d0dbc75b7aa8 install-kit-awindows-20260427-211240/aw-server/aw-worktime-prewarm.service
|
||||
0cd38bae7ad1b4718d7decc8ef3ac932939a6e5f8c25e9e27d1d0ecf8cc328b9 install-kit-awindows-20260427-211240/aw-server/aw-worktime-prewarm.timer
|
||||
6de9b272ad732ad174c32398ab8baf5cc8c83be47efe0d6df46c3190b4010680 install-kit-awindows-20260427-211240/aw-server/install_aw_server.sh
|
||||
aedffecfa24834968742cb2477faef80bf794345275a9679ac12c5a1f609acc2 install-kit-awindows-20260427-211240/aw-server/settings/classes-worktime.json
|
||||
47c50054515506b72af2d6bca0ae959ca57861803e528991c230611e6a8893d6 install-kit-awindows-20260427-211240/aw-server/settings/views-default.json
|
||||
2e728f4632e6564355a580ea1718d41a660dc35595ac38d0ac7fe0dd308dae6a install-kit-awindows-20260427-211240/scripts/aw-webui-browser-smoke.mjs
|
||||
76a24d19440676d447fac16e4a663f34c3f31d62166a6c326b5bd3754beac0eb install-kit-awindows-20260427-211240/scripts/aw-webui-browser-smoke.sh
|
||||
6770275fac17607770653a522a64cf5ab31a7a11993b68e7e80a2858c3b8930e install-kit-awindows-20260427-211240/scripts/check_install_kit_vs_repo.sh
|
||||
4e5bc6ce977e9f5b9e87c393061d5ecac0f90d5e1314e3325093692e5d19965e install-kit-awindows-20260427-211240/scripts/quality-gate.sh
|
||||
db1a9b0ccd21aec78d8e2aa08dfa28368e5ba6209c57361e6824e85c1af9f6ca install-kit-awindows-20260427-211240/scripts/rebuild_install_kit.sh
|
||||
38ad2e112796c2fa9bdc1cc2403c679bb896f90ae2b51d6ccc9a9ec103b4dea6 install-kit-awindows-20260427-211240/scripts/validate_install_kit.sh
|
||||
0b49e6db51d5abcecaeee0b85f186efd4f10d3360cf04e8aea2e4c7d4465dbda install-kit-awindows-20260427-211240/scripts/verify_innosetup_installer.sh
|
||||
e856d4b411b61dfc11e0a59b59026eff10972a47e7d8b28f6dfe367a2a5e192d install-kit-awindows-20260427-211240/windows/AWatchRusCollectorGuardService.cs
|
||||
33aa34b89246d6c079ef9afe2f5cd153bd9d5946b69a175ff6fd678c77f61da5 install-kit-awindows-20260427-211240/windows/ActivityWatch.Windows.Common.psd1
|
||||
6d5a7dd92619bbf972e17e455a7cda65b53c790f6f0ede47e1196246bfe8b1cf install-kit-awindows-20260427-211240/windows/ActivityWatch.Windows.Common.psm1
|
||||
f4405a959a52e21185234463887a4fc09ea28e48d2412ac336f9715b46be9cb9 install-kit-awindows-20260427-211240/windows/aw-collector-guard.ps1
|
||||
81e95c7e4b7336a2e1f0caed3ea99f6cdb9696b99380dc2148667b5fba577f68 install-kit-awindows-20260427-211240/windows/browser-domains-native-collector.ps1
|
||||
51eb13f689494c279ec321bbb68dfcb9b8ff81f22f11502b72c16895b65e0b65 install-kit-awindows-20260427-211240/windows/deploy-domain-users.ps1
|
||||
5bacbd48fdfc8d0d4479ba1af54cbbfe6057cc654409b385f487962576f4f1db install-kit-awindows-20260427-211240/windows/deploy-ensemble.ps1
|
||||
6ab4c496220bb46f064cc927a64ea853526a2c814182a32f39b432be6b32fe89 install-kit-awindows-20260427-211240/windows/deploy-single-user.ps1
|
||||
73f3328cde22798dafa645cdd78d648ccf6425d6577432ae8c95d24daa17dc94 install-kit-awindows-20260427-211240/windows/dlp-endpoint-signals-collector.ps1
|
||||
b97dfbd94201c83f3edd87c7940b058bf85ac64794468b44ab3200dcb1bff3c3 install-kit-awindows-20260427-211240/windows/dlp-policy.example.json
|
||||
0df53518a43bb1f0af7fdff5b44ba859e797090dd08680b239571d2e8a478d47 install-kit-awindows-20260427-211240/windows/dlp-policy.native-cross-os.example.json
|
||||
863727465497b474d13d2270d443ff96ccb6076f90a5ce3eb270bdf8088e02dc install-kit-awindows-20260427-211240/windows/email-outbound-collector.ps1
|
||||
2c0e7a96f32d546ce3ff8da2059259e6e5141c97cefc76bccae4c87c502f00cf install-kit-awindows-20260427-211240/windows/hardening-recovery.ps1
|
||||
ea68b869182587b7be41bf1891401bdf93dda3b9af6028afa0e30d245c77b0f4 install-kit-awindows-20260427-211240/windows/install-collector-guard-service.ps1
|
||||
5ef21a25d5e2da4eeaef17126e60f96f195f90f9dc17a776f8629334b904d096 install-kit-awindows-20260427-211240/windows/migrate-awatch-rus-paths.ps1
|
||||
d50228354f4c1f15b8ea9ccfef184a3a2e5cc832259ddbdc7afea4c083d03075 install-kit-awindows-20260427-211240/windows/validate-deployment.ps1
|
||||
731098681d89b9af6f3872abd586ac3b1faba2d7f9340211e503f52ad0243b3f install-kit-awindows-20260427-211240/windows/web-category-rules.example.json
|
||||
1b7c337967236474484e781dc8ac37543509b051513f01e1a3145369262f5389 install-kit-awindows-20260427-211240/windows/worktime-session-collector.ps1
|
||||
@@ -1,14 +0,0 @@
|
||||
ActivityWatch DetMir Windows Install Kit
|
||||
|
||||
Includes:
|
||||
- windows/* (deploy scripts, collectors, common module, configs/examples)
|
||||
- ansible/* (Windows and AW server playbooks, examples, inventory, tasks)
|
||||
- aw-server/* (server installer, health orchestrator, RU patch loader, host groups, default settings)
|
||||
- scripts/* (install-kit rebuild/validation, quality gates, browser/web smoke checks)
|
||||
|
||||
Source:
|
||||
- Local project snapshot at build time.
|
||||
|
||||
Customer-specific deployment configs, inventories, passwords, tokens, domains,
|
||||
IP addresses and runtime snapshots are intentionally excluded from this public
|
||||
install-kit.
|
||||
@@ -1,289 +0,0 @@
|
||||
# Ansible ensemble for AWatch-rus
|
||||
|
||||
Эта директория содержит Ansible-ensemble для полного развёртывания AWatch-rus:
|
||||
|
||||
- деплой на уже существующий Debian host/CT;
|
||||
- полный цикл с нуля в Proxmox: создание CT + bootstrap + установка ActivityWatch + RU patch;
|
||||
- централизованное развёртывание Windows/RDP collector'ов по WinRM;
|
||||
- развёртывание внешнего pfSense poller'а на Debian/Ubuntu utility VM.
|
||||
|
||||
## Файлы
|
||||
|
||||
- `ansible/deploy_aw_server.yml` — основной playbook для уже существующего Debian/CT host.
|
||||
- `ansible/provision_proxmox_ct_and_deploy_aw.yml` — полный playbook для Proxmox.
|
||||
- `ansible/provision_proxmox_ct_matrix_and_deploy_aw.yml` — массовый полный playbook (несколько CT).
|
||||
- `ansible/deploy_aw_windows.yml` — WinRM playbook для развёртывания Windows/RDP collector'ов.
|
||||
- `ansible/deploy_aw_pfsense_poller.yml` — развёртывание pfSense poller'а.
|
||||
- `ansible/deploy_grafana_dashboards.yml` — импорт version-controlled Grafana dashboard'ов через HTTP API.
|
||||
- `ansible/deploy_tsj_guardian_bot_proxmox.yml` — развёртывание TSJ Guardian Telegram Bot на Proxmox host.
|
||||
- `ansible/install_full_stack.yml` — полный установочный playbook (оркестратор всех этапов).
|
||||
- `ansible/inventory.example.ini` — шаблон inventory.
|
||||
- `ansible/group_vars/*.example.yml` — шаблоны переменных.
|
||||
|
||||
## Быстрый запуск
|
||||
|
||||
1. Скопируйте шаблоны:
|
||||
- `cp ansible/inventory.example.ini ansible/inventory.ini`
|
||||
- `cp ansible/group_vars/all.example.yml ansible/group_vars/all.yml`
|
||||
2. Заполните значения в `inventory.ini` и `group_vars/all.yml`.
|
||||
3. Запустите:
|
||||
|
||||
```bash
|
||||
cd ansible
|
||||
ansible-playbook -i inventory.ini deploy_aw_server.yml
|
||||
```
|
||||
|
||||
## Секреты (пароли) безопасно
|
||||
|
||||
Рекомендуемый способ не хранить пароли в репозитории — перед запуском экспортировать их в переменные окружения:
|
||||
|
||||
- Linux `aw_server` (SSH пароль root): `AW_SSH_PASSWORD`
|
||||
- Windows `aw_windows` (WinRM пароль): `AW_WINRM_PASSWORD`
|
||||
|
||||
В `group_vars/aw_server.yml` и `group_vars/windows.yml` они читаются через `lookup('env', ...)`.
|
||||
|
||||
## Полный установочный playbook (всё за один запуск)
|
||||
|
||||
Если нужно прогнать полный цикл одной командой:
|
||||
|
||||
```bash
|
||||
cd ansible
|
||||
ansible-playbook -i inventory.ini install_full_stack.yml
|
||||
```
|
||||
|
||||
Что делает:
|
||||
|
||||
- `provision_proxmox_ct_and_deploy_aw.yml` (если есть хосты в группе `[proxmox]`);
|
||||
- `deploy_aw_server.yml` (группа `[aw_server]`);
|
||||
- `deploy_aw_windows.yml` (группа `[aw_windows]`);
|
||||
- `deploy_aw_pfsense_poller.yml` (группа `[aw_pfsense_pollers]`);
|
||||
- `deploy_grafana_dashboards.yml` (группа `[grafana]`).
|
||||
|
||||
Пустые группы в `inventory.ini` безопасны: соответствующий play будет пропущен.
|
||||
|
||||
## Полный запуск с нуля в Proxmox
|
||||
|
||||
1. Подготовьте inventory и vars:
|
||||
- `cp ansible/inventory.example.ini ansible/inventory.ini`
|
||||
- `cp ansible/group_vars/all.example.yml ansible/group_vars/all.yml`
|
||||
- `cp ansible/group_vars/proxmox.example.yml ansible/group_vars/proxmox.yml`
|
||||
2. Заполните `group_vars/proxmox.yml` и `group_vars/all.yml`.
|
||||
3. Запустите playbook:
|
||||
|
||||
```bash
|
||||
cd ansible
|
||||
ansible-playbook -i inventory.ini provision_proxmox_ct_and_deploy_aw.yml
|
||||
```
|
||||
|
||||
## Массовый запуск (матрица CT)
|
||||
|
||||
1. Подготовьте матрицу:
|
||||
- `cp ansible/group_vars/proxmox-matrix.example.yml ansible/group_vars/proxmox-matrix.yml`
|
||||
2. Заполните `proxmox-matrix.yml`.
|
||||
3. Запустите:
|
||||
|
||||
```bash
|
||||
cd ansible
|
||||
ansible-playbook -i inventory.ini provision_proxmox_ct_matrix_and_deploy_aw.yml
|
||||
```
|
||||
|
||||
## Windows/RDP rollout (WinRM)
|
||||
|
||||
Важно:
|
||||
|
||||
- `WinRM` здесь остаётся транспортом для `Ansible deploy` и `validation`;
|
||||
- для интерактивной PowerShell-работы из Linux/Codex по DetMir используйте project MCP-over-SSH путь, а не `WSMan`;
|
||||
- каноника лежит в `docs/DETMIR_POWERSHELL_MCP_REMOTE_RU.md` и `scripts/install_detmir_powershell_mcp.sh`.
|
||||
|
||||
1. Подготовьте inventory и vars:
|
||||
- `cp ansible/inventory.example.ini ansible/inventory.ini`
|
||||
- `cp ansible/group_vars/windows.example.yml ansible/group_vars/windows.yml`
|
||||
2. Заполните `inventory.ini` (секция `[aw_windows]`) и `group_vars/windows.yml`.
|
||||
- Для русской локализации Windows часто нужен `ansible_user=Администратор` (а не `Administrator`).
|
||||
- Если WinRM закрыт, playbook не сможет стартовать и нужно сначала открыть `5985/5986` и `wsman`.
|
||||
3. Запустите:
|
||||
|
||||
```bash
|
||||
cd ansible
|
||||
AW_WINRM_PASSWORD='...' bash ./run_deploy_aw_windows.sh
|
||||
```
|
||||
|
||||
`run_deploy_aw_windows.sh` автоматически:
|
||||
- очищает proxy env (`http_proxy/https_proxy/...`), чтобы WinRM не уходил в локальный прокси;
|
||||
- включает OpenSSL legacy provider, если на хосте отключён `MD4` (нужно для NTLM в pywinrm).
|
||||
- перезапускает `ansible-playbook` при временных WinRM/NTLM сбоях (по умолчанию 5 попыток, пауза 30 сек).
|
||||
|
||||
Параметры retry:
|
||||
- `AW_DEPLOY_RETRIES` (по умолчанию `5`);
|
||||
- `AW_DEPLOY_RETRY_DELAY_SEC` (по умолчанию `30`).
|
||||
|
||||
Playbook:
|
||||
|
||||
- выгружает полный `windows/*` toolkit на целевой хост в InnoSetup-compatible каталог `C:\Program Files\AWatch-rus\windows`, включая DLP и `worktime-session-collector.ps1`;
|
||||
- если найден legacy config `C:\ProgramData\ActivityWatch-Phase2\deployment-config.json`, выполняет безопасную миграцию через `migrate-awatch-rus-paths.ps1`: backup, остановка задач, перенос данных, переписывание путей, пересоздание scheduled tasks и validation;
|
||||
- выполняет `deploy-ensemble.ps1` (deploy + hardening/recovery) с policy/rules из AWatch-rus toolkit;
|
||||
- после deploy принудительно запускает `ActivityWatch Recovery` и managed `ActivityWatch Launch *` задачи;
|
||||
- включает (`Enable-ScheduledTask`) `ActivityWatch Recovery` и managed `ActivityWatch Launch *` задачи перед запуском (иначе WebUI может показывать `Active time: 0s`);
|
||||
- оставляет `ActivityWatch Recovery` включённым даже при активном `AWatchRusCollectorGuard`: guard является основным контроллером, recovery остаётся fallback/bootstrap path;
|
||||
- выполняет API smoke-check bucket `aw-watcher-afk_<COMPUTERNAME>` и ожидает свежие события;
|
||||
- выполняет API smoke-check bucket `aw-watcher-window_<COMPUTERNAME>` и ожидает свежие события (по умолчанию включено);
|
||||
- запускает `validate-deployment.ps1`;
|
||||
- забирает JSON-отчёт в локальную директорию (`/tmp/aw-rus-validation-<USER>` по умолчанию).
|
||||
- настраивает scheduled task `ActivityWatch Hayabusa Upload` с периодом и lookback по vars.
|
||||
|
||||
Дополнительные флаги:
|
||||
|
||||
- `aw_windows_afk_enabled: false` — не запускать `aw-watcher-afk`;
|
||||
- `aw_windows_window_enabled: false` — не запускать `aw-watcher-window`;
|
||||
- `aw_windows_incident_capture_enabled: false` — отключить блок incidentCapture;
|
||||
- `aw_windows_incident_screenshot_enabled: false` — не делать скриншот при DLP-инциденте;
|
||||
- `aw_windows_incident_artifacts_root: 'C:\...\incident-artifacts'` — переопределить путь артефактов;
|
||||
- `aw_windows_deploy_root: 'C:\Program Files\AWatch-rus'` — каталог toolkit, совпадает с InnoSetup `{app}`;
|
||||
- `aw_windows_install_root: 'C:\Program Files\AWatch-rus\bin'` — каталог бинарников, совпадает с InnoSetup `AwDefaultInstallRoot`;
|
||||
- `aw_windows_state_root: 'C:\ProgramData\AWatch-rus'` — каталог состояния/отчётов, совпадает с InnoSetup `AwDefaultStateRoot`;
|
||||
- `aw_windows_validation_remote_path: '{{ aw_windows_state_root }}\aw_validate_ansible.json'` — отчёт Ansible-валидации хранится рядом с `ensemble-report-*.json`;
|
||||
- `aw_windows_migration_enabled: true` — включить guard миграции текущего production из `ActivityWatch-Phase2` в единый `AWatch-rus`;
|
||||
- `aw_windows_legacy_install_root` / `aw_windows_legacy_state_root` — старые production paths, откуда выполняется перенос;
|
||||
- `aw_windows_migration_report_remote_path` — JSON-отчёт о миграции на Windows-хосте;
|
||||
- `aw_windows_package_version`, `aw_windows_package_url`, `aw_windows_package_zip_path` — версия и источник Windows-пакета ActivityWatch;
|
||||
- `aw_windows_api_smoke_check_bucket: ""` — автоматически использовать `aw-watcher-afk_<COMPUTERNAME>`;
|
||||
- `aw_windows_api_smoke_check_window_enabled: true` — включить дополнительный smoke-check `aw-watcher-window_<COMPUTERNAME>`;
|
||||
- `aw_windows_api_smoke_check_window_bucket: ""` — переопределить bucket для window smoke-check;
|
||||
- `aw_windows_api_smoke_check_min_events: 1` — минимум событий, ожидаемых в smoke-check;
|
||||
- `aw_windows_fail_on_validation_error: true` — завершать playbook ошибкой, если `validate-deployment.ps1` возвращает `overallOk=false`;
|
||||
- `aw_windows_skip_hardening: true` — пропустить `hardening-recovery.ps1` внутри ensemble-скрипта.
|
||||
- `aw_windows_hayabusa_auto_upload_enabled: true` — включить авто-upload EVTX на AW-server;
|
||||
- `aw_windows_hayabusa_auto_upload_interval_hours: 6` — период scheduled task;
|
||||
- `aw_windows_hayabusa_auto_upload_hours_back: 6` — lookback для каждого запуска;
|
||||
- `aw_windows_hayabusa_auto_upload_mode: "incident"` — mode для server-side processing;
|
||||
- `aw_windows_hayabusa_auto_upload_task_name: "ActivityWatch Hayabusa Upload"` — имя scheduled task.
|
||||
|
||||
## Server-side Hayabusa auto-case и Telegram alerting
|
||||
|
||||
На стороне `deploy_aw_server.yml` теперь есть server-side контур:
|
||||
|
||||
- `aw-hayabusa-drop.path`
|
||||
- `aw-hayabusa-drop.service`
|
||||
- `aw-hayabusa-autoprocess`
|
||||
- `aw-hayabusa-case-alert`
|
||||
|
||||
Что делает контур:
|
||||
|
||||
- автоматически подхватывает `zip` из `/opt/activitywatch/aw-rus-ops/drop`;
|
||||
- запускает `aw-hayabusa`;
|
||||
- считает severity/score по `timeline.jsonl`;
|
||||
- создаёт или обновляет case;
|
||||
- пишет bounded metadata в `forensics.hayabusa`;
|
||||
- отправляет Telegram alert.
|
||||
|
||||
Основные vars:
|
||||
|
||||
- `aw_hayabusa_auto_case_enabled: true`
|
||||
- `aw_hayabusa_auto_case_min_severity: "medium"`
|
||||
- `aw_hayabusa_telegram_enabled: true`
|
||||
- `aw_hayabusa_telegram_min_severity: "high"`
|
||||
- `aw_hayabusa_telegram_bot_token`
|
||||
- `aw_hayabusa_telegram_chat_ids`
|
||||
|
||||
## Развёртывание pfSense poller
|
||||
|
||||
1. Подготовьте vars:
|
||||
- `cp ansible/group_vars/pfsense-poller.example.yml ansible/group_vars/pfsense-poller.yml`
|
||||
2. Добавьте inventory group `[aw_pfsense_pollers]`.
|
||||
3. Запустите:
|
||||
|
||||
```bash
|
||||
cd ansible
|
||||
ansible-playbook -i inventory.ini deploy_aw_pfsense_poller.yml
|
||||
```
|
||||
|
||||
Playbook:
|
||||
|
||||
- ставит `python3`;
|
||||
- копирует `pfsense-aw-poller.py`;
|
||||
- пишет `/etc/aw-pfsense/poller.json`;
|
||||
- поднимает `aw-pfsense-poller.service`.
|
||||
|
||||
## Импорт Grafana dashboard'ов
|
||||
|
||||
1. Подготовьте inventory и vars:
|
||||
- `cp ansible/inventory.example.ini ansible/inventory.ini`
|
||||
- `cp ansible/group_vars/grafana.example.yml ansible/group_vars/grafana.yml`
|
||||
2. Укажите в inventory группу `[grafana]` и переменную `grafana_url`.
|
||||
3. Экспортируйте пароль Grafana API:
|
||||
|
||||
```bash
|
||||
export GRAFANA_ADMIN_PASSWORD='...'
|
||||
```
|
||||
|
||||
4. Запустите:
|
||||
|
||||
```bash
|
||||
cd ansible
|
||||
ansible-playbook -i inventory.ini deploy_grafana_dashboards.yml
|
||||
```
|
||||
|
||||
Playbook:
|
||||
|
||||
- проверяет `GET /api/health`;
|
||||
- создает или актуализирует folder `AWatch-rus` в Grafana;
|
||||
- импортирует dashboard JSON из каталога `grafana/`;
|
||||
- верифицирует доступность dashboard'ов по `uid` через Grafana API.
|
||||
|
||||
По умолчанию импортируются:
|
||||
|
||||
- `DetMir: Работа пользователей в RDP`
|
||||
- `DetMir: DLP и ИБ обзор`
|
||||
- `DetMir: ИБ сводка для руководства`
|
||||
- `AW-rus: DLP обзор`
|
||||
|
||||
Подробная документация: `docs/GRAFANA_DASHBOARDS_RU.md`
|
||||
|
||||
## Развёртывание TSJ Guardian Bot на Proxmox
|
||||
|
||||
1. Подготовьте vars:
|
||||
- `cp ansible/group_vars/proxmox-bot.example.yml ansible/group_vars/proxmox-bot.yml`
|
||||
2. Заполните минимум:
|
||||
- `telegram_bot_token`
|
||||
- `telegram_allowed_chat_ids`
|
||||
- `tsj_bot_source_local_path`
|
||||
3. Убедитесь, что в inventory есть группа `[proxmox]`.
|
||||
Для текущего контура AW-Rus bot ожидает Proxmox host `<GATEWAY_HOST>`.
|
||||
Рабочая модель для этого контура: `igor` + `sudo`, а не обязательный `root` login.
|
||||
4. При необходимости задайте recovery-команды для AW-Rus:
|
||||
- `tsj_bot_aw_rus_worktime_heal_cmd`
|
||||
- `tsj_bot_aw_rus_dlp_heal_cmd`
|
||||
5. Запустите:
|
||||
|
||||
```bash
|
||||
cd ansible
|
||||
ansible-playbook -i inventory.ini deploy_tsj_guardian_bot_proxmox.yml
|
||||
```
|
||||
|
||||
После актуального production hardening:
|
||||
|
||||
- bot различает `worktime idle` и реальную деградацию;
|
||||
- bot поддерживает отдельный `AW_RUS_DLP_HEAL_CMD`;
|
||||
- redeploy не должен терять runtime env-ключи, связанные с proxy, FS checks и AI escalation.
|
||||
|
||||
## Результат
|
||||
|
||||
- Установлен ActivityWatch Server.
|
||||
- Создан systemd-unit `activitywatch-server.service`.
|
||||
- Установлен RU Web UI patch.
|
||||
- Для Web UI используется checksum-based cache-bust для `ru-patch-v5.js` и `sw-cleanup.js`, чтобы браузер не держал старую DLP/русскую статику после деплоя.
|
||||
- На `#/home` Web UI делит хосты на `Windows RDP` и `Virtual servers + Proxmox`.
|
||||
- Выполнена валидация API `http://127.0.0.1:5600/api/0/info`.
|
||||
- Для полного сценария CT создаётся автоматически через `pct create`.
|
||||
- На Windows/RDP host развёрнуты AFK/window watchers, browser domain collector, DLP endpoint collector и worktime session collector.
|
||||
- Проверочный JSON-отчёт Windows playbook должен иметь `overallOk=true`.
|
||||
|
||||
## Prod rollout одной командой
|
||||
|
||||
Для ручного запуска с dry-run и логированием используйте:
|
||||
|
||||
```bash
|
||||
bash scripts/prod_rollout.sh
|
||||
```
|
||||
|
||||
Скрипт попросит `AW_SSH_PASSWORD` и `AW_WINRM_PASSWORD` интерактивно (ввод скрыт) и сложит логи в `.rollout-logs/`.
|
||||
@@ -1,66 +0,0 @@
|
||||
---
|
||||
- name: Развернуть pfSense ActivityWatch poller
|
||||
hosts: aw_pfsense_pollers
|
||||
become: true
|
||||
gather_facts: true
|
||||
|
||||
vars:
|
||||
aw_pfsense_install_root: "/opt/aw-pfsense"
|
||||
aw_pfsense_config_dir: "/etc/aw-pfsense"
|
||||
aw_pfsense_service_name: "aw-pfsense-poller.service"
|
||||
|
||||
tasks:
|
||||
- name: Установить обязательные пакеты
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- python3
|
||||
state: present
|
||||
update_cache: true
|
||||
|
||||
- name: Создать каталоги
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
mode: "0755"
|
||||
loop:
|
||||
- "{{ aw_pfsense_install_root }}"
|
||||
- "{{ aw_pfsense_config_dir }}"
|
||||
|
||||
- name: Установить скрипт pfSense poller
|
||||
ansible.builtin.copy:
|
||||
src: "{{ aw_repo_root }}/pfsense/pfsense-aw-poller.py"
|
||||
dest: "{{ aw_pfsense_install_root }}/pfsense-aw-poller.py"
|
||||
mode: "0755"
|
||||
|
||||
- name: Установить systemd service
|
||||
ansible.builtin.copy:
|
||||
src: "{{ aw_repo_root }}/pfsense/pfsense-aw-poller.service"
|
||||
dest: "/etc/systemd/system/{{ aw_pfsense_service_name }}"
|
||||
mode: "0644"
|
||||
notify:
|
||||
- Перезагрузить systemd
|
||||
|
||||
- name: Записать конфигурацию pfSense poller
|
||||
ansible.builtin.copy:
|
||||
dest: "{{ aw_pfsense_config_dir }}/poller.json"
|
||||
mode: "0600"
|
||||
content: "{{ aw_pfsense_poller_config | to_nice_json }}"
|
||||
notify:
|
||||
- Перезапустить pfSense poller
|
||||
|
||||
- name: Включить и запустить pfSense poller
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ aw_pfsense_service_name }}"
|
||||
enabled: true
|
||||
state: restarted
|
||||
daemon_reload: true
|
||||
|
||||
handlers:
|
||||
- name: Перезагрузить systemd
|
||||
ansible.builtin.systemd:
|
||||
daemon_reload: true
|
||||
|
||||
- name: Перезапустить pfSense poller
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ aw_pfsense_service_name }}"
|
||||
state: restarted
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,559 +0,0 @@
|
||||
---
|
||||
- name: Развернуть Windows/RDP collector'ы AWatch-rus
|
||||
hosts: aw_windows
|
||||
gather_facts: false
|
||||
|
||||
vars:
|
||||
ansible_winrm_operation_timeout_sec: 120
|
||||
ansible_winrm_read_timeout_sec: 180
|
||||
aw_windows_repo_root: "{{ playbook_dir | dirname }}"
|
||||
aw_windows_deploy_root: "C:\\Program Files\\AWatch-rus"
|
||||
aw_windows_server_scheme: "http"
|
||||
aw_windows_server_port: 5600
|
||||
aw_windows_package_version: "v0.13.2"
|
||||
aw_windows_package_url: "https://github.com/ActivityWatch/activitywatch/releases/download/v0.13.2/activitywatch-v0.13.2-windows-x86_64.zip"
|
||||
aw_windows_package_zip_path: ""
|
||||
aw_windows_domain: "SHARKON2025"
|
||||
aw_windows_builtin_administrator_name: "Администратор"
|
||||
aw_windows_users:
|
||||
- Администратор
|
||||
- user1
|
||||
- user2
|
||||
- user3
|
||||
- user4
|
||||
- user5
|
||||
aw_windows_extra_users: []
|
||||
aw_windows_users_effective: "{{ (aw_windows_users + aw_windows_extra_users) | unique }}"
|
||||
aw_windows_install_root: "C:\\Program Files\\AWatch-rus\\bin"
|
||||
aw_windows_state_root: "C:\\ProgramData\\AWatch-rus"
|
||||
aw_windows_policy_mode: "server"
|
||||
aw_windows_policy_refresh_seconds: 300
|
||||
aw_windows_policy_engine_enabled: true
|
||||
aw_windows_policy_engine_port: 5601
|
||||
aw_windows_policy_engine_scheme: "http"
|
||||
aw_windows_hayabusa_auto_upload_enabled: true
|
||||
aw_windows_hayabusa_auto_upload_interval_hours: 6
|
||||
aw_windows_hayabusa_auto_upload_hours_back: 6
|
||||
aw_windows_hayabusa_auto_upload_mode: "incident"
|
||||
aw_windows_hayabusa_auto_upload_task_name: "ActivityWatch Hayabusa Upload"
|
||||
aw_windows_file_1c_auto_upload_enabled: true
|
||||
aw_windows_file_1c_auto_upload_interval_hours: 6
|
||||
aw_windows_file_1c_auto_upload_task_name: "ActivityWatch File1C Upload"
|
||||
aw_windows_file_1c_target_user: "igor"
|
||||
aw_windows_file_1c_registry_workbook_path: "E:\\USER1\\СПИСОК ПРЕДПРИЯТИЙ И ИХ РАСПРЕДЕЛЕНИЕ.xlsx"
|
||||
aw_windows_afk_enabled_default: true
|
||||
aw_windows_window_enabled_default: true
|
||||
aw_windows_file_ops_enabled: true
|
||||
aw_windows_local_agent_logs_enabled: false
|
||||
aw_windows_incident_capture_enabled: true
|
||||
aw_windows_incident_screenshot_enabled: true
|
||||
aw_windows_incident_artifacts_root: "{{ aw_windows_state_root }}\\incident-artifacts"
|
||||
aw_windows_forensics_root: "{{ aw_windows_state_root }}\\forensics\\evtx-exports"
|
||||
aw_windows_logon_marker_enabled: true
|
||||
aw_windows_process_events_enabled: false
|
||||
aw_windows_skip_hardening: false
|
||||
aw_windows_rules_path: "{{ aw_windows_deploy_root }}\\windows\\web-category-rules.example.json"
|
||||
aw_windows_policy_path: "{{ aw_windows_deploy_root }}\\windows\\dlp-policy.example.json"
|
||||
aw_windows_validation_remote_path: "{{ aw_windows_state_root }}\\aw_validate_ansible.json"
|
||||
aw_windows_validation_local_dir: "/tmp/aw-rus-validation-{{ lookup('env','USER') | default('ansible', true) }}"
|
||||
aw_windows_launch_task_pattern: "ActivityWatch Launch *"
|
||||
aw_windows_recovery_task_name: "ActivityWatch Recovery"
|
||||
aw_windows_collector_guard_enabled: true
|
||||
aw_windows_collector_guard_mode: "enforce"
|
||||
aw_windows_collector_guard_service_name: "AWatchRusCollectorGuard"
|
||||
aw_windows_collector_guard_loop_seconds: 60
|
||||
aw_windows_force_task_restart: true
|
||||
aw_windows_api_smoke_check_enabled: true
|
||||
aw_windows_api_smoke_check_bucket: ""
|
||||
aw_windows_api_smoke_check_limit: 10
|
||||
aw_windows_api_smoke_check_window_enabled_default: true
|
||||
aw_windows_api_smoke_check_window_bucket: ""
|
||||
aw_windows_api_smoke_check_min_events: 1
|
||||
aw_windows_fail_on_validation_error: true
|
||||
aw_windows_migration_enabled: true
|
||||
aw_windows_legacy_install_root: "C:\\Program Files\\ActivityWatch-Phase2"
|
||||
aw_windows_legacy_state_root: "C:\\ProgramData\\ActivityWatch-Phase2"
|
||||
aw_windows_migration_report_remote_path: "{{ aw_windows_state_root }}\\aw_migration_ansible.json"
|
||||
|
||||
tasks:
|
||||
- name: Вычислить inventory host AW server по умолчанию
|
||||
ansible.builtin.set_fact:
|
||||
aw_server_inventory_host_effective: "{{ (groups['aw_server'] | default([]) | first) | default('', true) }}"
|
||||
|
||||
- name: Вычислить inventory host analytics node по умолчанию
|
||||
ansible.builtin.set_fact:
|
||||
aw_analytics_inventory_host_effective: "{{ (groups['proxmox'] | default([]) | first) | default('', true) }}"
|
||||
|
||||
- name: Вычислить effective host для AW server
|
||||
ansible.builtin.set_fact:
|
||||
aw_windows_server_host_effective: >-
|
||||
{{
|
||||
aw_windows_server_host
|
||||
| default(
|
||||
(
|
||||
hostvars[aw_server_inventory_host_effective].ansible_host
|
||||
| default(aw_server_inventory_host_effective, true)
|
||||
)
|
||||
if (aw_server_inventory_host_effective | length) > 0
|
||||
else '',
|
||||
true
|
||||
)
|
||||
}}
|
||||
|
||||
- name: Вычислить effective каталог health validation на AW server
|
||||
ansible.builtin.set_fact:
|
||||
aw_windows_health_validation_dir_effective: >-
|
||||
{{
|
||||
(
|
||||
hostvars[aw_server_inventory_host_effective].aw_rus_health_validation_dir
|
||||
| default('/var/lib/activitywatch/health/windows-validation', true)
|
||||
)
|
||||
if (aw_server_inventory_host_effective | length) > 0
|
||||
else ''
|
||||
}}
|
||||
|
||||
- name: Вычислить effective host для policy engine
|
||||
ansible.builtin.set_fact:
|
||||
aw_windows_policy_engine_host_effective: >-
|
||||
{{
|
||||
aw_windows_policy_engine_host
|
||||
| default(aw_windows_server_host_effective, true)
|
||||
}}
|
||||
|
||||
- name: Вычислить effective host для file-1C analytics
|
||||
ansible.builtin.set_fact:
|
||||
aw_windows_file_1c_target_host_effective: >-
|
||||
{{
|
||||
aw_windows_file_1c_target_host
|
||||
| default(
|
||||
(
|
||||
hostvars[aw_analytics_inventory_host_effective].ansible_host
|
||||
| default(aw_analytics_inventory_host_effective, true)
|
||||
)
|
||||
if (aw_analytics_inventory_host_effective | length) > 0
|
||||
else '',
|
||||
true
|
||||
)
|
||||
}}
|
||||
|
||||
- name: Проверить обязательные переменные
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- aw_windows_server_host_effective | length > 0
|
||||
- aw_windows_server_port is defined
|
||||
- aw_windows_server_scheme is defined
|
||||
- aw_windows_domain is defined
|
||||
- aw_windows_builtin_administrator_name is defined
|
||||
- aw_windows_builtin_administrator_name | length > 0
|
||||
- aw_windows_users_effective | length > 0
|
||||
- aw_windows_install_root is defined
|
||||
- aw_windows_state_root is defined
|
||||
- (not (aw_windows_file_1c_auto_upload_enabled | bool)) or (aw_windows_file_1c_target_host_effective | length > 0)
|
||||
fail_msg: "Не заданы обязательные переменные Windows-развёртывания."
|
||||
|
||||
- name: Нормализовать effective флаги collector'ов и smoke-check
|
||||
ansible.builtin.set_fact:
|
||||
aw_windows_afk_enabled_effective: "{{ (aw_windows_afk_enabled | default(aw_windows_afk_enabled_default)) | bool }}"
|
||||
aw_windows_window_enabled_effective: "{{ (aw_windows_window_enabled | default(aw_windows_window_enabled_default)) | bool }}"
|
||||
aw_windows_api_smoke_check_window_enabled_effective: "{{ (aw_windows_api_smoke_check_window_enabled | default(aw_windows_api_smoke_check_window_enabled_default)) | bool }}"
|
||||
|
||||
- name: Создать каталоги развёртывания
|
||||
ansible.windows.win_file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
loop:
|
||||
- "{{ aw_windows_deploy_root }}"
|
||||
- "{{ aw_windows_deploy_root }}\\windows"
|
||||
|
||||
- name: Загрузить Windows toolkit развёртывания
|
||||
ansible.windows.win_copy:
|
||||
src: "{{ aw_windows_repo_root }}/windows/{{ item }}"
|
||||
dest: "{{ aw_windows_deploy_root }}\\windows\\{{ item }}"
|
||||
loop:
|
||||
- ActivityWatch.Windows.Common.psd1
|
||||
- ActivityWatch.Windows.Common.psm1
|
||||
- browser-domains-native-collector.ps1
|
||||
- dlp-endpoint-signals-collector.ps1
|
||||
- dlp-policy-client.ps1
|
||||
- email-outbound-collector.ps1
|
||||
- file-operations-collector.ps1
|
||||
- worktime-session-collector.ps1
|
||||
- export-evtx-for-hayabusa.ps1
|
||||
- export-upload-hayabusa-to-aw-server.ps1
|
||||
- export-upload-file-1c-telemetry.ps1
|
||||
- sync-dlp-evidence-artifacts.ps1
|
||||
- migrate-awatch-rus-paths.ps1
|
||||
- deploy-domain-users.ps1
|
||||
- deploy-ensemble.ps1
|
||||
- hardening-recovery.ps1
|
||||
- AWatchRusCollectorGuardService.cs
|
||||
- aw-collector-guard.ps1
|
||||
- install-collector-guard-service.ps1
|
||||
- rebuild-worktime-tasks.ps1
|
||||
- audit-cryptopro.ps1
|
||||
- validate-deployment.ps1
|
||||
- web-category-rules.example.json
|
||||
- dlp-policy.example.json
|
||||
|
||||
- name: Нормализовать кодировку PowerShell файлов (UTF-8 BOM для Windows PowerShell)
|
||||
ansible.windows.win_powershell:
|
||||
script: |
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$toolkitDir = "{{ aw_windows_deploy_root }}\windows"
|
||||
$encIn = New-Object System.Text.UTF8Encoding($false)
|
||||
$encOut = New-Object System.Text.UTF8Encoding($true)
|
||||
Get-ChildItem -LiteralPath $toolkitDir -File -Include *.ps1,*.psm1,*.psd1 | ForEach-Object {
|
||||
$text = [System.IO.File]::ReadAllText($_.FullName, $encIn)
|
||||
[System.IO.File]::WriteAllText($_.FullName, $text, $encOut)
|
||||
}
|
||||
|
||||
- name: Загрузить список пользователей для доменного развёртывания
|
||||
ansible.windows.win_copy:
|
||||
dest: "{{ aw_windows_deploy_root }}\\windows\\users.txt"
|
||||
content: |
|
||||
{% for user in aw_windows_users_effective -%}
|
||||
{{ user }}
|
||||
{% endfor -%}
|
||||
|
||||
- name: Проверить нужен ли migration с legacy ActivityWatch путей
|
||||
when: aw_windows_migration_enabled | bool
|
||||
ansible.windows.win_stat:
|
||||
path: "{{ aw_windows_legacy_state_root }}\\deployment-config.json"
|
||||
register: aw_windows_legacy_config
|
||||
|
||||
- name: Выполнить безопасную migration legacy prod в AWatch-rus
|
||||
when:
|
||||
- aw_windows_migration_enabled | bool
|
||||
- aw_windows_legacy_config.stat.exists | default(false)
|
||||
ansible.windows.win_powershell:
|
||||
script: |
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$result = & "{{ aw_windows_deploy_root }}\windows\migrate-awatch-rus-paths.ps1" `
|
||||
-OldInstallRoot "{{ aw_windows_legacy_install_root }}" `
|
||||
-OldStateRoot "{{ aw_windows_legacy_state_root }}" `
|
||||
-NewInstallRoot "{{ aw_windows_install_root }}" `
|
||||
-NewStateRoot "{{ aw_windows_state_root }}" `
|
||||
-ToolkitRoot "{{ aw_windows_deploy_root }}\windows"
|
||||
$result | ConvertTo-Json -Depth 8 | Out-File -FilePath "{{ aw_windows_migration_report_remote_path }}" -Encoding utf8
|
||||
|
||||
- name: Запустить Windows/RDP ensemble развёртывание
|
||||
ansible.windows.win_powershell:
|
||||
script: |
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$env:AWATCH_RUS_BUILTIN_ADMINISTRATOR_NAME = "{{ aw_windows_builtin_administrator_name }}"
|
||||
$params = @{
|
||||
ServerScheme = "{{ aw_windows_server_scheme }}"
|
||||
ServerHost = "{{ aw_windows_server_host_effective }}"
|
||||
ServerPort = {{ aw_windows_server_port }}
|
||||
Version = "{{ aw_windows_package_version }}"
|
||||
Domain = "{{ aw_windows_domain }}"
|
||||
UserListPath = "{{ aw_windows_deploy_root }}\windows\users.txt"
|
||||
InstallRoot = "{{ aw_windows_install_root }}"
|
||||
StateRoot = "{{ aw_windows_state_root }}"
|
||||
AfkEnabled = {{ '$true' if (aw_windows_afk_enabled_effective | bool) else '$false' }}
|
||||
WindowEnabled = {{ '$true' if (aw_windows_window_enabled_effective | bool) else '$false' }}
|
||||
FileOpsEnabled = {{ '$true' if (aw_windows_file_ops_enabled | bool) else '$false' }}
|
||||
LocalAgentLogsEnabled = {{ '$true' if (aw_windows_local_agent_logs_enabled | bool) else '$false' }}
|
||||
IncidentCaptureEnabled = {{ '$true' if (aw_windows_incident_capture_enabled | bool) else '$false' }}
|
||||
IncidentScreenshotEnabled = {{ '$true' if (aw_windows_incident_screenshot_enabled | bool) else '$false' }}
|
||||
IncidentArtifactsRoot = "{{ aw_windows_incident_artifacts_root }}"
|
||||
EvtxExportRoot = "{{ aw_windows_forensics_root }}"
|
||||
EvtxRetentionDays = {{ aw_windows_evtx_retention_days | int }}
|
||||
LogonMarkerEnabled = {{ '$true' if (aw_windows_logon_marker_enabled | bool) else '$false' }}
|
||||
ProcessEventsEnabled = {{ '$true' if (aw_windows_process_events_enabled | bool) else '$false' }}
|
||||
PolicyMode = "{{ aw_windows_policy_mode }}"
|
||||
PolicyEngineEnabled = {{ '$true' if (aw_windows_policy_engine_enabled | bool) else '$false' }}
|
||||
PolicyEngineHost = "{{ aw_windows_policy_engine_host_effective }}"
|
||||
PolicyEnginePort = {{ aw_windows_policy_engine_port }}
|
||||
PolicyEngineScheme = "{{ aw_windows_policy_engine_scheme }}"
|
||||
PolicyRefreshSeconds = {{ aw_windows_policy_refresh_seconds }}
|
||||
HayabusaAutoUploadEnabled = {{ '$true' if (aw_windows_hayabusa_auto_upload_enabled | bool) else '$false' }}
|
||||
HayabusaAutoUploadIntervalHours = {{ aw_windows_hayabusa_auto_upload_interval_hours | int }}
|
||||
HayabusaAutoUploadHoursBack = {{ aw_windows_hayabusa_auto_upload_hours_back | int }}
|
||||
HayabusaAutoUploadMode = "{{ aw_windows_hayabusa_auto_upload_mode }}"
|
||||
HayabusaAutoUploadTaskName = "{{ aw_windows_hayabusa_auto_upload_task_name }}"
|
||||
File1CAutoUploadEnabled = {{ '$true' if (aw_windows_file_1c_auto_upload_enabled | bool) else '$false' }}
|
||||
File1CAutoUploadIntervalHours = {{ aw_windows_file_1c_auto_upload_interval_hours | int }}
|
||||
File1CAutoUploadTaskName = "{{ aw_windows_file_1c_auto_upload_task_name }}"
|
||||
File1CTargetHost = "{{ aw_windows_file_1c_target_host_effective }}"
|
||||
File1CTargetUser = "{{ aw_windows_file_1c_target_user }}"
|
||||
File1CRegistryWorkbookPath = "{{ aw_windows_file_1c_registry_workbook_path }}"
|
||||
CustomRulesPath = "{{ aw_windows_rules_path }}"
|
||||
CustomPolicyPath = "{{ aw_windows_policy_path }}"
|
||||
}
|
||||
{% if (aw_windows_package_url | default('') | string | length) > 0 %}
|
||||
$params.PackageUrl = "{{ aw_windows_package_url }}"
|
||||
{% endif %}
|
||||
{% if (aw_windows_package_zip_path | default('') | string | length) > 0 %}
|
||||
$params.PackageZipPath = "{{ aw_windows_package_zip_path }}"
|
||||
{% endif %}
|
||||
{% if (aw_windows_evtx_channels | default([]) | length) > 0 %}
|
||||
$params.EvtxChannels = @(
|
||||
{% for channel in aw_windows_evtx_channels %}
|
||||
"{{ channel }}"{% if not loop.last %},{% endif %}
|
||||
{% endfor %}
|
||||
)
|
||||
{% endif %}
|
||||
{% if (aw_windows_hostname_override | default('') | string | length) > 0 %}
|
||||
$params.AwHostname = "{{ aw_windows_hostname_override }}"
|
||||
{% endif %}
|
||||
{% if aw_windows_skip_hardening | bool %}
|
||||
$params.SkipHardening = $true
|
||||
{% endif %}
|
||||
{% if aw_windows_integration_test_enabled | bool %}
|
||||
$params.IntegrationTestEnabled = $true
|
||||
{% endif %}
|
||||
& "{{ aw_windows_deploy_root }}\windows\deploy-ensemble.ps1" @params
|
||||
|
||||
- name: Удалить лишние ActivityWatch Launch tasks вне текущего deployment-config
|
||||
ansible.windows.win_powershell:
|
||||
script: |
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$config = Get-Content -Raw -LiteralPath "{{ aw_windows_state_root }}\deployment-config.json" | ConvertFrom-Json
|
||||
$desired = @($config.userTasks | ForEach-Object { [string]$_.LaunchTaskName })
|
||||
foreach ($task in @(Get-ScheduledTask | Where-Object { $_.TaskName -like 'ActivityWatch Launch *' })) {
|
||||
if ($desired -notcontains [string]$task.TaskName) {
|
||||
Unregister-ScheduledTask -TaskName $task.TaskName -Confirm:$false -ErrorAction SilentlyContinue
|
||||
& cmd.exe /c "schtasks /Delete /TN `"$($task.TaskName)`" /F >nul 2>&1" | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
- name: Принудительно запустить ActivityWatch recovery и launch tasks
|
||||
when: aw_windows_force_task_restart | bool
|
||||
ansible.windows.win_powershell:
|
||||
script: |
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
function Get-CollectorKey {
|
||||
param([string]$CommandLine)
|
||||
if (-not $CommandLine) { return $null }
|
||||
$cl = $CommandLine.ToLowerInvariant()
|
||||
if ($cl -like '*browser-domains-native-collector.ps1*') { return 'browser' }
|
||||
if ($cl -like '*file-operations-collector.ps1*') { return 'fileops' }
|
||||
if ($cl -like '*dlp-endpoint-signals-collector.ps1*') { return 'endpoint' }
|
||||
if ($cl -like '*email-outbound-collector.ps1*') { return 'email' }
|
||||
if ($cl -like '*worktime-session-collector.ps1*') { return 'worktime' }
|
||||
return $null
|
||||
}
|
||||
|
||||
$collectorProcs = Get-CimInstance Win32_Process |
|
||||
Where-Object { $_.Name -eq 'powershell.exe' -and $_.CommandLine } |
|
||||
ForEach-Object {
|
||||
$key = Get-CollectorKey -CommandLine $_.CommandLine
|
||||
if ($key) {
|
||||
$groupKey = if ($key -eq 'worktime') { 'worktime::global' } else { '{0}::{1}' -f $key, ([int]$_.SessionId) }
|
||||
[pscustomobject]@{
|
||||
ProcessId = [int]$_.ProcessId
|
||||
SessionId = [int]$_.SessionId
|
||||
CreationDate = $_.CreationDate
|
||||
CollectorKey = $key
|
||||
GroupKey = $groupKey
|
||||
}
|
||||
}
|
||||
} |
|
||||
Where-Object { $_ -ne $null }
|
||||
|
||||
# Keep only one process per collector scope: worktime collector is global, others stay per-session.
|
||||
foreach ($group in ($collectorProcs | Group-Object GroupKey)) {
|
||||
$ordered = @($group.Group | Sort-Object CreationDate -Descending)
|
||||
if ($ordered.Count -le 1) { continue }
|
||||
foreach ($dup in $ordered | Select-Object -Skip 1) {
|
||||
Stop-Process -Id $dup.ProcessId -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
|
||||
# Force managed collectors/watchers to reload the freshly deployed scripts.
|
||||
Get-CimInstance Win32_Process -ErrorAction SilentlyContinue |
|
||||
Where-Object {
|
||||
(
|
||||
($_.Name -ieq 'powershell.exe' -or $_.Name -ieq 'pwsh.exe') -and
|
||||
$_.CommandLine -match 'C:\\ProgramData\\AWatch-rus\\' -and
|
||||
$_.CommandLine -match '(collector|launch-watchers|recovery-loop)\.ps1'
|
||||
) -or
|
||||
($_.Name -ieq 'aw-watcher-afk.exe') -or
|
||||
($_.Name -ieq 'aw-watcher-window.exe')
|
||||
} |
|
||||
ForEach-Object {
|
||||
Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
Start-Sleep -Seconds 2
|
||||
|
||||
$modulePath = "{{ aw_windows_deploy_root }}\windows\ActivityWatch.Windows.Common.psm1"
|
||||
Import-Module $modulePath -Force
|
||||
|
||||
# Ensure tasks are enabled (some environments keep them disabled, causing "0s" in WebUI).
|
||||
try {
|
||||
Enable-ScheduledTask -TaskName "{{ aw_windows_recovery_task_name }}" -ErrorAction SilentlyContinue | Out-Null
|
||||
} catch {}
|
||||
|
||||
$config = Get-Content -Raw -LiteralPath "{{ aw_windows_state_root }}\deployment-config.json" | ConvertFrom-Json
|
||||
$configPaths = Get-ActivityWatchRecoveryConfigPaths -PrimaryConfigPath "{{ aw_windows_state_root }}\deployment-config.json"
|
||||
$taskDefs = @(Get-ActivityWatchRecoveryTaskDefinitions -ConfigPaths $configPaths)
|
||||
$sessionRecords = @(Get-ActivityWatchSessionRecords)
|
||||
|
||||
foreach ($taskDef in @($taskDefs)) {
|
||||
try { Enable-ScheduledTask -TaskName ([string]$taskDef.taskName) -ErrorAction SilentlyContinue | Out-Null } catch {}
|
||||
}
|
||||
|
||||
$recoveryTask = Get-ScheduledTask -TaskName "{{ aw_windows_recovery_task_name }}" -ErrorAction SilentlyContinue
|
||||
if ($recoveryTask -and $recoveryTask.State -notin @('Running', 'Queued')) {
|
||||
Start-ScheduledTask -TaskName "{{ aw_windows_recovery_task_name }}"
|
||||
}
|
||||
foreach ($taskDef in @($taskDefs)) {
|
||||
if (-not (Test-ActivityWatchUserHasManagedSession -UserId ([string]$taskDef.userId) -SessionRecords $sessionRecords -IncludeLive -IncludeDisconnected)) {
|
||||
continue
|
||||
}
|
||||
$launchTask = Get-ScheduledTask -TaskName ([string]$taskDef.taskName) -ErrorAction SilentlyContinue
|
||||
if ($launchTask -and $launchTask.State -notin @('Running', 'Queued')) {
|
||||
Start-ScheduledTask -TaskName ([string]$taskDef.taskName) -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
|
||||
- name: Установить session-aware AWatch-rus Collector Guard service
|
||||
when: aw_windows_collector_guard_enabled | bool
|
||||
ansible.windows.win_powershell:
|
||||
script: |
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$guardParams = @{
|
||||
ConfigPath = "{{ aw_windows_state_root }}\deployment-config.json"
|
||||
Mode = "{{ aw_windows_collector_guard_mode }}"
|
||||
ServiceName = "{{ aw_windows_collector_guard_service_name }}"
|
||||
LoopSeconds = {{ aw_windows_collector_guard_loop_seconds | int }}
|
||||
}
|
||||
& "{{ aw_windows_deploy_root }}\windows\install-collector-guard-service.ps1" @guardParams
|
||||
|
||||
- name: Получить Windows hostname для AW smoke-check bucket
|
||||
when:
|
||||
- aw_windows_api_smoke_check_enabled | bool
|
||||
ansible.windows.win_command: powershell.exe -NoProfile -Command "$env:COMPUTERNAME"
|
||||
register: aw_windows_hostname_result
|
||||
changed_when: false
|
||||
|
||||
- name: Вычислить AW worktime smoke-check bucket
|
||||
when:
|
||||
- aw_windows_api_smoke_check_enabled | bool
|
||||
- aw_windows_hostname_result.stdout is defined
|
||||
ansible.builtin.set_fact:
|
||||
aw_windows_api_smoke_check_bucket_effective: >-
|
||||
{{
|
||||
aw_windows_api_smoke_check_bucket
|
||||
if (aw_windows_api_smoke_check_bucket | default('') | string | length) > 0
|
||||
else 'aw-worktime-sessions_' ~ (aw_windows_hostname_result.stdout | trim)
|
||||
}}
|
||||
|
||||
- name: Вычислить AW Window smoke-check bucket
|
||||
when:
|
||||
- aw_windows_api_smoke_check_enabled | bool
|
||||
- aw_windows_api_smoke_check_window_enabled_effective | bool
|
||||
- aw_windows_window_enabled_effective | bool
|
||||
- aw_windows_hostname_result.stdout is defined
|
||||
ansible.builtin.set_fact:
|
||||
aw_windows_api_smoke_check_window_bucket_effective: >-
|
||||
{{
|
||||
aw_windows_api_smoke_check_window_bucket
|
||||
if (aw_windows_api_smoke_check_window_bucket | default('') | string | length) > 0
|
||||
else 'aw-watcher-window_' ~ (aw_windows_hostname_result.stdout | trim)
|
||||
}}
|
||||
|
||||
- name: Выполнить AW API smoke-check (worktime bucket должен получать события)
|
||||
when:
|
||||
- aw_windows_api_smoke_check_enabled | bool
|
||||
ansible.builtin.uri:
|
||||
url: "{{ aw_windows_server_scheme }}://{{ aw_windows_server_host_effective }}:{{ aw_windows_server_port }}/api/0/buckets/{{ aw_windows_api_smoke_check_bucket_effective }}/events?limit={{ aw_windows_api_smoke_check_limit }}"
|
||||
method: GET
|
||||
status_code: 200
|
||||
return_content: true
|
||||
register: aw_windows_api_smoke_result
|
||||
failed_when: false
|
||||
until: >-
|
||||
(aw_windows_api_smoke_result.status | default(0)) == 200
|
||||
and ((aw_windows_api_smoke_result.json | default([])) | length) >= aw_windows_api_smoke_check_min_events
|
||||
and (
|
||||
(aw_windows_api_smoke_result.json | default([]))
|
||||
| selectattr('data.source', 'equalto', 'worktime-session-collector')
|
||||
| list
|
||||
| length
|
||||
) > 0
|
||||
retries: 12
|
||||
delay: 5
|
||||
ignore_errors: "{{ (not aw_windows_fail_on_validation_error | bool) }}"
|
||||
delegate_to: localhost
|
||||
changed_when: false
|
||||
|
||||
- name: Выполнить AW API smoke-check (Window bucket должен получать события)
|
||||
when:
|
||||
- aw_windows_api_smoke_check_enabled | bool
|
||||
- aw_windows_api_smoke_check_window_enabled_effective | bool
|
||||
- aw_windows_window_enabled_effective | bool
|
||||
ansible.builtin.uri:
|
||||
url: "{{ aw_windows_server_scheme }}://{{ aw_windows_server_host_effective }}:{{ aw_windows_server_port }}/api/0/buckets/{{ aw_windows_api_smoke_check_window_bucket_effective }}/events?limit={{ aw_windows_api_smoke_check_limit }}"
|
||||
method: GET
|
||||
status_code: 200
|
||||
return_content: true
|
||||
register: aw_windows_api_smoke_window_result
|
||||
failed_when: false
|
||||
until: >-
|
||||
(aw_windows_api_smoke_window_result.status | default(0)) == 200
|
||||
and ((aw_windows_api_smoke_window_result.json | default([])) | length) >= aw_windows_api_smoke_check_min_events
|
||||
retries: 12
|
||||
delay: 5
|
||||
ignore_errors: "{{ (not aw_windows_fail_on_validation_error | bool) }}"
|
||||
delegate_to: localhost
|
||||
changed_when: false
|
||||
|
||||
- name: Валидировать развёртывание на эндпоинте
|
||||
ansible.windows.win_powershell:
|
||||
script: |
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$result = & "{{ aw_windows_deploy_root }}\windows\validate-deployment.ps1" `
|
||||
-ConfigPath "{{ aw_windows_state_root }}\deployment-config.json"
|
||||
$result | ConvertTo-Json -Depth 12 | Out-File -FilePath "{{ aw_windows_validation_remote_path }}" -Encoding utf8
|
||||
return $result
|
||||
|
||||
- name: Создать локальную директорию для отчётов валидации
|
||||
ansible.builtin.file:
|
||||
path: "{{ aw_windows_validation_local_dir }}"
|
||||
state: directory
|
||||
mode: "0755"
|
||||
delegate_to: localhost
|
||||
|
||||
- name: Стянуть отчёт валидации с эндпоинта
|
||||
ansible.builtin.fetch:
|
||||
src: "{{ aw_windows_validation_remote_path }}"
|
||||
dest: "{{ aw_windows_validation_local_dir }}/{{ inventory_hostname }}-aw_validate_ansible.json"
|
||||
flat: true
|
||||
|
||||
- name: Убедиться, что каталог Windows validation существует на AW server
|
||||
when:
|
||||
- aw_server_inventory_host_effective | length > 0
|
||||
- aw_windows_health_validation_dir_effective | length > 0
|
||||
ansible.builtin.file:
|
||||
path: "{{ aw_windows_health_validation_dir_effective }}"
|
||||
state: directory
|
||||
mode: "0755"
|
||||
delegate_to: "{{ aw_server_inventory_host_effective }}"
|
||||
|
||||
- name: Опубликовать validation report на AW server для aw-rus-healthd
|
||||
when:
|
||||
- aw_server_inventory_host_effective | length > 0
|
||||
- aw_windows_health_validation_dir_effective | length > 0
|
||||
ansible.builtin.copy:
|
||||
src: "{{ aw_windows_validation_local_dir }}/{{ inventory_hostname }}-aw_validate_ansible.json"
|
||||
dest: "{{ aw_windows_health_validation_dir_effective }}/{{ inventory_hostname }}-aw_validate_ansible.json"
|
||||
mode: "0644"
|
||||
delegate_to: "{{ aw_server_inventory_host_effective }}"
|
||||
|
||||
- name: Проверить статус валидации
|
||||
ansible.builtin.shell: |
|
||||
python3 - <<'PY'
|
||||
import json, sys
|
||||
with open('{{ aw_windows_validation_local_dir }}/{{ inventory_hostname }}-aw_validate_ansible.json', 'r', encoding='utf-8-sig') as f:
|
||||
data = json.load(f)
|
||||
if not data.get('overallOk', False):
|
||||
failed = ", ".join(data.get("summary", {}).get("failedSections", [])) or "unknown"
|
||||
print(f"Validation failed for {{ inventory_hostname }}: {failed}")
|
||||
sys.exit(1)
|
||||
PY
|
||||
delegate_to: localhost
|
||||
when: aw_windows_fail_on_validation_error | bool
|
||||
@@ -1,86 +0,0 @@
|
||||
aw_server_version: "v0.13.2"
|
||||
aw_server_download_url: "https://github.com/ActivityWatch/activitywatch/releases/download/v0.13.2/activitywatch-v0.13.2-linux-x86_64.zip"
|
||||
aw_server_bind_host: "0.0.0.0"
|
||||
aw_server_port: 5600
|
||||
aw_server_webui_dir: "/opt/activitywatch/webui-ru"
|
||||
aw_server_data_dir: "/var/lib/activitywatch"
|
||||
aw_server_db_path: "/var/lib/activitywatch/.local/share/activitywatch/aw-server-rust/sqlite.db"
|
||||
aw_server_log_dir: "/var/log/activitywatch"
|
||||
aw_server_user: "activitywatch"
|
||||
aw_server_group: "activitywatch"
|
||||
aw_server_inventory_host: "{{ (groups['aw_server'] | default([]) | first) | default('aw-server', true) }}"
|
||||
aw_server_public_host: "{{ (hostvars[aw_server_inventory_host].ansible_host | default(aw_server_inventory_host, true)) if (aw_server_inventory_host | length) > 0 else 'aw-server' }}"
|
||||
aw_worktime_report_base: "http://{{ aw_server_public_host }}:5610"
|
||||
aw_worktime_timezone: "Europe/Moscow"
|
||||
aw_worktime_influx_enabled: false
|
||||
aw_worktime_influx_url: "http://<INFLUXDB_HOST>:8086"
|
||||
aw_worktime_influx_org: "proxmox"
|
||||
aw_worktime_influx_bucket: "aw_metrics"
|
||||
aw_worktime_influx_hosts: "SHARKON2025"
|
||||
aw_worktime_influx_days: "today,yesterday"
|
||||
aw_worktime_influx_token: ""
|
||||
aw_dlp_influx_enabled: false
|
||||
aw_dlp_influx_url: "http://<INFLUXDB_HOST>:8086"
|
||||
aw_dlp_influx_org: "proxmox"
|
||||
aw_dlp_influx_bucket: "aw_metrics"
|
||||
aw_dlp_influx_hosts: "SHARKON2025"
|
||||
aw_dlp_influx_lookback_days: 30
|
||||
aw_dlp_influx_event_limit: 2000
|
||||
aw_dlp_influx_token: ""
|
||||
aw_monitored_windows_host: "<WINDOWS_HOST>"
|
||||
aw_monitored_windows_hostname: "SHARKON2025"
|
||||
aw_rus_health_worktime_api_base: "http://127.0.0.1:5610"
|
||||
aw_rus_health_state_dir: "{{ aw_server_data_dir }}/health"
|
||||
aw_rus_health_validation_dir: "{{ aw_rus_health_state_dir }}/windows-validation"
|
||||
aw_browser_smoke_enabled: true
|
||||
aw_browser_smoke_engine: "chromium-cli"
|
||||
aw_legacy_db_merge_enabled: false
|
||||
aw_browser_smoke_timeout_ms: 20000
|
||||
aw_browser_smoke_render_timeout_ms: 15000
|
||||
aw_hayabusa_auto_case_enabled: true
|
||||
aw_hayabusa_auto_case_min_severity: "medium"
|
||||
aw_hayabusa_telegram_enabled: true
|
||||
aw_hayabusa_telegram_min_severity: "high"
|
||||
aw_hayabusa_telegram_bot_token: ""
|
||||
aw_hayabusa_telegram_chat_ids: ""
|
||||
|
||||
aw_repo_root: "{{ playbook_dir | dirname }}"
|
||||
|
||||
# Применить базовые категории и views для рабочего времени через AW settings API.
|
||||
# При прод-обновлениях это нужно оставлять включённым, иначе UI остаётся без views/classes.
|
||||
aw_apply_worktime_settings: true
|
||||
|
||||
# Дополнительные origin для aw-server-rust CORS.
|
||||
# Обязательно включите тот origin, с которого реально открывается Web UI.
|
||||
aw_server_cors_origins:
|
||||
- "http://127.0.0.1:5600"
|
||||
- "http://localhost:5600"
|
||||
- "http://{{ aw_server_public_host }}:5600"
|
||||
- "http://aw-server:5600"
|
||||
|
||||
# Опциональные значения периода рабочего времени в Web UI.
|
||||
# startOfDay задаёт границу дня и стартовое время окна отчёта.
|
||||
# durationDefault задаёт диапазон по умолчанию в секундах.
|
||||
#
|
||||
# Рекомендуется явно задать рабочий интервал и дать playbook вычислить duration.
|
||||
aw_worktime_from: "00:00"
|
||||
aw_worktime_to: "17:00"
|
||||
aw_worktime_start_of_day: "{{ aw_worktime_from }}"
|
||||
aw_server_always_active_pattern: "aw-watcher-window"
|
||||
aw_server_landingpage: "/#/activity/SHARKON2025/view/"
|
||||
aw_health_strict_fileops: 0
|
||||
|
||||
aw_dlp_policy_engine_enabled: true
|
||||
aw_dlp_policy_engine_bind_host: "0.0.0.0"
|
||||
aw_dlp_policy_engine_port: 5601
|
||||
aw_dlp_policy_engine_db_path: "{{ aw_server_data_dir }}/dlp-policy-engine.sqlite"
|
||||
aw_dlp_content_analysis_enabled: true
|
||||
aw_dlp_integrations_enabled: true
|
||||
aw_dlp_case_management_enabled: true
|
||||
aw_dlp_case_bind_host: "0.0.0.0"
|
||||
aw_dlp_case_port: 5602
|
||||
aw_dlp_case_db_path: "/opt/activitywatch/dlp-case-management/cases.db"
|
||||
aw_dlp_compliance_enabled: true
|
||||
aw_dlp_compliance_report_dir: "/opt/activitywatch/dlp-compliance/reports"
|
||||
aw_dlp_compliance_template_path: "/opt/activitywatch/dlp-compliance/templates/152-fz-report.html"
|
||||
aw_server_post_deploy_health_check_enabled: true
|
||||
@@ -1,30 +0,0 @@
|
||||
aw_pfsense_poller_config:
|
||||
poll_interval_seconds: 60
|
||||
aw:
|
||||
server_host: "<AW_SERVER_HOST>"
|
||||
server_port: 5600
|
||||
hostname: "PFSENSE-EDGE01"
|
||||
pulse_time_seconds: 120
|
||||
timeout_seconds: 15
|
||||
pfsense:
|
||||
name: "pfSense Edge 01"
|
||||
host: "<FIREWALL_HOST>"
|
||||
scheme: "https"
|
||||
verify_tls: false
|
||||
timeout_seconds: 15
|
||||
auth:
|
||||
api_key: "replace-me"
|
||||
api_secret: "replace-me"
|
||||
endpoints:
|
||||
- name: "system-status"
|
||||
path: "/api/v2/status/system"
|
||||
bucket_prefix: "aw-pfsense-health"
|
||||
bucket_type: "aw.pfsense.health"
|
||||
- name: "interfaces"
|
||||
path: "/api/v2/interfaces"
|
||||
bucket_prefix: "aw-pfsense-interfaces"
|
||||
bucket_type: "aw.pfsense.interfaces"
|
||||
- name: "gateways"
|
||||
path: "/api/v2/status/gateways"
|
||||
bucket_prefix: "aw-pfsense-gateways"
|
||||
bucket_type: "aw.pfsense.gateways"
|
||||
@@ -1,38 +0,0 @@
|
||||
proxmox_ct_matrix:
|
||||
- id: "203"
|
||||
hostname: "activitywatch-user1"
|
||||
storage: "local-lvm"
|
||||
template: "local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst"
|
||||
rootfs_size: "8G"
|
||||
cores: "2"
|
||||
memory: "2048"
|
||||
swap: "512"
|
||||
bridge: "vmbr10"
|
||||
ip: "10.20.30.13/24"
|
||||
gw: "10.20.30.1"
|
||||
vlan: ""
|
||||
nameserver: "1.1.1.1 8.8.8.8"
|
||||
searchdomain: "example.internal"
|
||||
password: "CHANGE_ME"
|
||||
unprivileged: "1"
|
||||
onboot: "1"
|
||||
features: "nesting=1,keyctl=1"
|
||||
|
||||
- id: "204"
|
||||
hostname: "activitywatch-user2"
|
||||
storage: "local-lvm"
|
||||
template: "local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst"
|
||||
rootfs_size: "8G"
|
||||
cores: "2"
|
||||
memory: "2048"
|
||||
swap: "512"
|
||||
bridge: "vmbr10"
|
||||
ip: "10.20.30.14/24"
|
||||
gw: "10.20.30.1"
|
||||
vlan: ""
|
||||
nameserver: "1.1.1.1 8.8.8.8"
|
||||
searchdomain: "example.internal"
|
||||
password: "CHANGE_ME"
|
||||
unprivileged: "1"
|
||||
onboot: "1"
|
||||
features: "nesting=1,keyctl=1"
|
||||
@@ -1,18 +0,0 @@
|
||||
proxmox_ct_id: "203"
|
||||
proxmox_ct_hostname: "activitywatch-server"
|
||||
proxmox_ct_storage: "local-lvm"
|
||||
proxmox_ct_template: "local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst"
|
||||
proxmox_ct_rootfs_size: "8G"
|
||||
proxmox_ct_cores: "2"
|
||||
proxmox_ct_memory: "2048"
|
||||
proxmox_ct_swap: "512"
|
||||
proxmox_ct_bridge: "vmbr10"
|
||||
proxmox_ct_ip: "10.20.30.13/24"
|
||||
proxmox_ct_gw: "10.20.30.1"
|
||||
proxmox_ct_vlan: ""
|
||||
proxmox_ct_nameserver: "1.1.1.1 8.8.8.8"
|
||||
proxmox_ct_searchdomain: "example.internal"
|
||||
proxmox_ct_password: "CHANGE_ME"
|
||||
proxmox_ct_unprivileged: "1"
|
||||
proxmox_ct_onboot: "1"
|
||||
proxmox_ct_features: "nesting=1,keyctl=1"
|
||||
@@ -1,80 +0,0 @@
|
||||
aw_windows_repo_root: "{{ playbook_dir | dirname }}"
|
||||
aw_windows_deploy_root: "C:\\Program Files\\AWatch-rus"
|
||||
aw_windows_server_scheme: "http"
|
||||
# Leave empty to derive from the first host in [aw_server] inventory.
|
||||
aw_windows_server_host: ""
|
||||
aw_windows_server_port: 5600
|
||||
aw_windows_hayabusa_auto_upload_enabled: false
|
||||
aw_windows_hayabusa_auto_upload_interval_hours: 6
|
||||
aw_windows_hayabusa_auto_upload_hours_back: 6
|
||||
aw_windows_hayabusa_auto_upload_mode: "incident"
|
||||
aw_windows_hayabusa_auto_upload_task_name: "ActivityWatch Hayabusa Upload"
|
||||
aw_windows_package_version: "v0.13.2"
|
||||
aw_windows_package_url: "https://github.com/ActivityWatch/activitywatch/releases/download/v0.13.2/activitywatch-v0.13.2-windows-x86_64.zip"
|
||||
aw_windows_package_zip_path: ""
|
||||
aw_windows_domain: "SHARKON2025"
|
||||
# Localized name of the built-in local Administrator account (SID ending in -500).
|
||||
# On the current Russian Windows host this must stay "Администратор";
|
||||
# do not replace it with "Administrator" unless the target OS account is actually named that way.
|
||||
aw_windows_builtin_administrator_name: "Администратор"
|
||||
aw_windows_users:
|
||||
- Администратор
|
||||
- user1
|
||||
- user2
|
||||
- user3
|
||||
- user4
|
||||
- user5
|
||||
aw_windows_extra_users: []
|
||||
# Например:
|
||||
# aw_windows_extra_users:
|
||||
# - Администратор
|
||||
|
||||
# Единые Windows/RDP пути: те же, что использует InnoSetup.
|
||||
aw_windows_install_root: "C:\\Program Files\\AWatch-rus\\bin"
|
||||
aw_windows_state_root: "C:\\ProgramData\\AWatch-rus"
|
||||
aw_windows_hostname_override: "" # Например: SHARKON2025
|
||||
aw_windows_afk_enabled: true
|
||||
aw_windows_window_enabled: true
|
||||
aw_windows_file_ops_enabled: true
|
||||
aw_windows_local_agent_logs_enabled: false
|
||||
aw_windows_incident_capture_enabled: true
|
||||
aw_windows_incident_screenshot_enabled: true
|
||||
aw_windows_incident_artifacts_root: "{{ aw_windows_state_root }}\\incident-artifacts"
|
||||
aw_windows_forensics_root: "{{ aw_windows_state_root }}\\forensics\\evtx-exports"
|
||||
aw_windows_evtx_retention_days: 14
|
||||
aw_windows_evtx_channels:
|
||||
- Security
|
||||
- System
|
||||
- Application
|
||||
- Microsoft-Windows-PowerShell/Operational
|
||||
- Microsoft-Windows-TerminalServices-LocalSessionManager/Operational
|
||||
- Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational
|
||||
aw_windows_logon_marker_enabled: true
|
||||
aw_windows_process_events_enabled: false
|
||||
aw_windows_skip_hardening: false
|
||||
|
||||
aw_windows_rules_path: "{{ aw_windows_deploy_root }}\\windows\\web-category-rules.example.json"
|
||||
aw_windows_policy_path: "{{ aw_windows_deploy_root }}\\windows\\dlp-policy.example.json"
|
||||
|
||||
aw_windows_validation_remote_path: "{{ aw_windows_state_root }}\\aw_validate_ansible.json"
|
||||
aw_windows_validation_local_dir: "/tmp/aw-rus-validation-{{ lookup('env','USER') | default('ansible', true) }}"
|
||||
aw_windows_fail_on_validation_error: true
|
||||
|
||||
# Collector Guard supervises collectors, but ActivityWatch Recovery must stay enabled
|
||||
# as a fallback and as the launch-task bootstrap path for managed RDP sessions.
|
||||
aw_windows_collector_guard_enabled: true
|
||||
aw_windows_collector_guard_mode: "enforce"
|
||||
aw_windows_collector_guard_service_name: "AWatchRusCollectorGuard"
|
||||
aw_windows_collector_guard_loop_seconds: 60
|
||||
|
||||
# Безопасная миграция текущего прода со старых путей в единый профиль AWatch-rus.
|
||||
aw_windows_migration_enabled: true
|
||||
aw_windows_legacy_install_root: "C:\\Program Files\\ActivityWatch-Phase2"
|
||||
aw_windows_legacy_state_root: "C:\\ProgramData\\ActivityWatch-Phase2"
|
||||
aw_windows_migration_report_remote_path: "{{ aw_windows_state_root }}\\aw_migration_ansible.json"
|
||||
|
||||
# По умолчанию AFK bucket вычисляется как aw-watcher-afk_<COMPUTERNAME>.
|
||||
# Задайте явное значение только если watcher пишет в нестандартный bucket.
|
||||
aw_windows_api_smoke_check_enabled: true
|
||||
aw_windows_api_smoke_check_bucket: ""
|
||||
aw_windows_api_smoke_check_limit: 10
|
||||
@@ -1,18 +0,0 @@
|
||||
---
|
||||
# Полный установщик AWatch-rus.
|
||||
# Выполняет развёртывание одной командой:
|
||||
# 1) создание Proxmox CT + bootstrap AW (если в inventory есть [proxmox])
|
||||
# 2) развёртывание AW server на хостах [aw_server]
|
||||
# 3) развёртывание Windows/RDP collector'ов на [aw_windows]
|
||||
# 4) развёртывание pfSense poller'а на [aw_pfsense_pollers]
|
||||
# 5) импорт Grafana dashboard'ов на [grafana]
|
||||
#
|
||||
# Примечания:
|
||||
# - Заполняйте только нужные группы inventory для своего окружения.
|
||||
# - Play без совпадающих host groups Ansible пропускает автоматически.
|
||||
|
||||
- import_playbook: provision_proxmox_ct_and_deploy_aw.yml
|
||||
- import_playbook: deploy_aw_server.yml
|
||||
- import_playbook: deploy_aw_windows.yml
|
||||
- import_playbook: deploy_aw_pfsense_poller.yml
|
||||
- import_playbook: deploy_grafana_dashboards.yml
|
||||
@@ -1,17 +0,0 @@
|
||||
[proxmox]
|
||||
pve-main ansible_host=192.168.10.2 ansible_user=root ansible_port=22
|
||||
|
||||
[aw_server]
|
||||
aw-ct ansible_host=10.20.30.13 ansible_user=root ansible_port=22
|
||||
|
||||
[aw_windows]
|
||||
# Примечание: в русифицированных Windows часто нужен "Администратор", а не "Administrator".
|
||||
win-node1 ansible_host=<WINDOWS_HOST> ansible_user=Администратор ansible_password=CHANGE_ME ansible_connection=winrm ansible_winrm_transport=ntlm ansible_port=5985 ansible_winrm_server_cert_validation=ignore
|
||||
|
||||
[aw_pfsense_pollers]
|
||||
# pfsense-poller1 ansible_host=192.168.100.30 ansible_user=root ansible_port=22
|
||||
|
||||
[grafana]
|
||||
# Для API-import playbook достаточно указать grafana_url.
|
||||
# SSH-подключение не требуется: playbook работает через HTTP API с control host.
|
||||
grafana-main grafana_url=http://10.20.30.11:3000
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user