feat(rust): advance powershell migration
This commit is contained in:
@@ -8,6 +8,8 @@
|
||||
ansible_winrm_read_timeout_sec: 180
|
||||
aw_windows_repo_root: "{{ playbook_dir | dirname }}"
|
||||
aw_windows_deploy_root: "C:\\Program Files\\AWatch-rus"
|
||||
aw_windows_rust_target_root: "{{ lookup('env', 'CARGO_TARGET_DIR') | default('/tmp/detmir-adk-rust-target', true) }}"
|
||||
aw_windows_telemetry_exe_source: "{{ aw_windows_rust_target_root }}/x86_64-pc-windows-gnu/release/aw-windows-telemetry.exe"
|
||||
aw_windows_server_scheme: "http"
|
||||
aw_windows_server_port: 5600
|
||||
aw_windows_package_version: "v0.13.2"
|
||||
@@ -37,8 +39,9 @@
|
||||
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_interval_minutes: 15
|
||||
aw_windows_file_1c_auto_upload_task_name: "ActivityWatch File1C Upload"
|
||||
aw_windows_file_1c_auto_upload_run_as_user: "{{ aw_windows_domain }}\\{{ aw_windows_builtin_administrator_name }}"
|
||||
aw_windows_file_1c_target_user: "igor"
|
||||
aw_windows_file_1c_registry_workbook_path: "E:\\USER1\\СПИСОК ПРЕДПРИЯТИЙ И ИХ РАСПРЕДЕЛЕНИЕ.xlsx"
|
||||
aw_windows_afk_enabled_default: true
|
||||
@@ -195,6 +198,11 @@
|
||||
- web-category-rules.example.json
|
||||
- dlp-policy.example.json
|
||||
|
||||
- name: Загрузить Rust Windows telemetry binary
|
||||
ansible.windows.win_copy:
|
||||
src: "{{ aw_windows_telemetry_exe_source }}"
|
||||
dest: "{{ aw_windows_deploy_root }}\\windows\\aw-windows-telemetry.exe"
|
||||
|
||||
- name: Нормализовать кодировку PowerShell файлов (UTF-8 BOM для Windows PowerShell)
|
||||
ansible.windows.win_powershell:
|
||||
script: |
|
||||
@@ -273,8 +281,9 @@
|
||||
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 }}
|
||||
File1CAutoUploadIntervalMinutes = {{ aw_windows_file_1c_auto_upload_interval_minutes | int }}
|
||||
File1CAutoUploadTaskName = "{{ aw_windows_file_1c_auto_upload_task_name }}"
|
||||
File1CAutoUploadRunAsUser = "{{ aw_windows_file_1c_auto_upload_run_as_user }}"
|
||||
File1CTargetHost = "{{ aw_windows_file_1c_target_host_effective }}"
|
||||
File1CTargetUser = "{{ aw_windows_file_1c_target_user }}"
|
||||
File1CRegistryWorkbookPath = "{{ aw_windows_file_1c_registry_workbook_path }}"
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
content: |
|
||||
DETMIR_PORTAL_BIND={{ detmir_portal_bind }}
|
||||
DETMIR_PORTAL_STATUS_CMD=detmir-status --json
|
||||
DETMIR_PORTAL_CHECK_CMD='timeout 8s detmir-check --json --aw-api "$DETMIR_AW_API" --worktime-url "$DETMIR_WORKTIME_URL" --one-c-url "$DETMIR_ONE_C_URL" --rdp-host "$DETMIR_RDP_HOST" --hostname "$DETMIR_HOSTNAME"'
|
||||
DETMIR_PORTAL_CHECK_CMD='cat /var/lib/detmir-ai/latest-run/detmir-check.json'
|
||||
DETMIR_PORTAL_FAILED_UNITS_CMD=systemctl --failed --no-pager
|
||||
DETMIR_PORTAL_WORKTIME_URL={{ detmir_portal_worktime_url }}
|
||||
DETMIR_PORTAL_ONE_C_URL={{ detmir_portal_one_c_url }}
|
||||
@@ -65,6 +65,61 @@
|
||||
CLICKHOUSE_USER={{ detmir_clickhouse_user | default('default') }}
|
||||
CLICKHOUSE_PASSWORD={{ detmir_clickhouse_password | default('') }}
|
||||
|
||||
- name: Preserve local ClickHouse security-events settings when available
|
||||
ansible.builtin.shell: |
|
||||
set -euo pipefail
|
||||
python3 - <<'PY'
|
||||
from pathlib import Path
|
||||
|
||||
source = Path("/opt/activitywatch/clickhouse-1c/.env")
|
||||
target = Path("{{ detmir_portal_env_path }}")
|
||||
if not source.exists() or not target.exists():
|
||||
raise SystemExit(0)
|
||||
|
||||
kv = {}
|
||||
for line in source.read_text().splitlines():
|
||||
if not line or line.startswith("#") or "=" not in line:
|
||||
continue
|
||||
key, value = line.split("=", 1)
|
||||
kv[key.strip()] = value.strip().strip('"').strip("'")
|
||||
|
||||
updates = {
|
||||
"SECURITY_EVENTS_BACKEND": "clickhouse",
|
||||
"CLICKHOUSE_URL": "http://127.0.0.1:8123",
|
||||
"CLICKHOUSE_DATABASE": kv.get("CLICKHOUSE_DB", "analytics_1c"),
|
||||
"CLICKHOUSE_USER": kv.get("CLICKHOUSE_USER", "default"),
|
||||
"CLICKHOUSE_PASSWORD": kv.get("CLICKHOUSE_PASSWORD", ""),
|
||||
}
|
||||
|
||||
lines = []
|
||||
seen = set()
|
||||
for line in target.read_text().splitlines():
|
||||
if "=" in line and not line.startswith("#"):
|
||||
key = line.split("=", 1)[0].strip()
|
||||
if key in updates:
|
||||
lines.append(f"{key}={updates[key]}")
|
||||
seen.add(key)
|
||||
continue
|
||||
lines.append(line)
|
||||
for key, value in updates.items():
|
||||
if key not in seen:
|
||||
lines.append(f"{key}={value}")
|
||||
target.write_text("\n".join(lines) + "\n")
|
||||
PY
|
||||
args:
|
||||
executable: /bin/bash
|
||||
no_log: true
|
||||
when: detmir_security_events_backend | default('clickhouse') == 'clickhouse'
|
||||
|
||||
- name: Remove stale detmir-portal systemd overrides
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: absent
|
||||
loop:
|
||||
- /etc/systemd/system/detmir-portal.service.d/20-timeouts.conf
|
||||
- /etc/systemd/system/detmir-portal.service.d/30-warm-cache.conf
|
||||
register: detmir_portal_stale_overrides
|
||||
|
||||
- name: Install initial workforce policy when absent
|
||||
ansible.builtin.copy:
|
||||
dest: "{{ detmir_portal_workforce_policy_path }}"
|
||||
@@ -122,7 +177,6 @@
|
||||
- name: Reload systemd
|
||||
ansible.builtin.systemd:
|
||||
daemon_reload: true
|
||||
when: detmir_portal_service_unit.changed
|
||||
|
||||
- name: Enable and restart detmir-portal
|
||||
ansible.builtin.systemd:
|
||||
@@ -150,7 +204,10 @@
|
||||
failed_when: >
|
||||
detmir_portal_reports.status != 200
|
||||
or detmir_portal_reports.json.kpis is not defined
|
||||
or 'derived detections/cases' not in (detmir_portal_reports.json.markdown | default(''))
|
||||
or (
|
||||
'derived detections/cases' not in (detmir_portal_reports.json.markdown | default(''))
|
||||
and 'расчетными выводами' not in (detmir_portal_reports.json.markdown | default(''))
|
||||
)
|
||||
changed_when: false
|
||||
|
||||
- name: Deploy DetMir DLP evidence API on AW server
|
||||
|
||||
@@ -7,8 +7,12 @@
|
||||
aw_repo_root: "{{ playbook_dir | dirname }}"
|
||||
aw_windows_state_root: "C:\\ProgramData\\AWatch-rus"
|
||||
aw_windows_deploy_root: "C:\\Program Files\\AWatch-rus"
|
||||
aw_windows_rust_target_root: "{{ lookup('env', 'CARGO_TARGET_DIR') | default('/tmp/detmir-adk-rust-target', true) }}"
|
||||
aw_windows_telemetry_exe_source: "{{ aw_windows_rust_target_root }}/x86_64-pc-windows-gnu/release/aw-windows-telemetry.exe"
|
||||
aw_windows_telemetry_exe_path: "{{ aw_windows_deploy_root }}\\windows\\aw-windows-telemetry.exe"
|
||||
aw_windows_evidence_sync_task_name: "ActivityWatch DLP Evidence Sync"
|
||||
aw_windows_evidence_sync_interval_minutes: 5
|
||||
aw_windows_evidence_sync_interval_minutes: 15
|
||||
aw_windows_evidence_sync_run_as_user: "HOST-EXAMPLE\\Администратор"
|
||||
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"
|
||||
@@ -38,6 +42,11 @@
|
||||
src: "{{ aw_repo_root }}/windows/sync-dlp-evidence-artifacts.ps1"
|
||||
dest: "{{ aw_windows_evidence_sync_script }}"
|
||||
|
||||
- name: Install Rust Windows telemetry binary
|
||||
ansible.windows.win_copy:
|
||||
src: "{{ aw_windows_telemetry_exe_source }}"
|
||||
dest: "{{ aw_windows_telemetry_exe_path }}"
|
||||
|
||||
- name: Normalize DLP evidence sync script encoding
|
||||
ansible.windows.win_powershell:
|
||||
script: |
|
||||
@@ -76,23 +85,26 @@
|
||||
script: |
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$taskName = "{{ aw_windows_evidence_sync_task_name }}"
|
||||
$ps = Join-Path $env:SystemRoot 'System32\WindowsPowerShell\v1.0\powershell.exe'
|
||||
$telemetryExe = "{{ aw_windows_telemetry_exe_path }}"
|
||||
$args = @(
|
||||
'-NoProfile',
|
||||
'-ExecutionPolicy', 'Bypass',
|
||||
'-File', '"{{ aw_windows_evidence_sync_script }}"',
|
||||
'-EvidenceApiUrl', '"{{ aw_windows_evidence_sync_api_url }}"',
|
||||
'-TokenPath', '"{{ aw_windows_evidence_sync_token_path }}"',
|
||||
'-StatePath', '"{{ aw_windows_evidence_sync_state_path }}"',
|
||||
'-LogPath', '"{{ aw_windows_evidence_sync_log_path }}"'
|
||||
'dlp-evidence-sync',
|
||||
'--evidence-api-url', '"{{ aw_windows_evidence_sync_api_url }}"',
|
||||
'--token-path', '"{{ aw_windows_evidence_sync_token_path }}"',
|
||||
'--state-path', '"{{ aw_windows_evidence_sync_state_path }}"',
|
||||
'--log-path', '"{{ aw_windows_evidence_sync_log_path }}"'
|
||||
) -join ' '
|
||||
$action = New-ScheduledTaskAction -Execute $ps -Argument $args
|
||||
$action = New-ScheduledTaskAction -Execute $telemetryExe -Argument $args
|
||||
$trigger = New-ScheduledTaskTrigger `
|
||||
-Once `
|
||||
-At ((Get-Date).AddMinutes(1)) `
|
||||
-RepetitionInterval (New-TimeSpan -Minutes {{ aw_windows_evidence_sync_interval_minutes | int }}) `
|
||||
-RepetitionDuration (New-TimeSpan -Days 3650)
|
||||
$principal = New-ScheduledTaskPrincipal -UserId 'SYSTEM' -LogonType ServiceAccount -RunLevel Highest
|
||||
$runAsUser = "{{ aw_windows_evidence_sync_run_as_user }}"
|
||||
if ([string]::IsNullOrWhiteSpace($runAsUser)) {
|
||||
$principal = New-ScheduledTaskPrincipal -UserId 'SYSTEM' -LogonType ServiceAccount -RunLevel Highest
|
||||
} else {
|
||||
$principal = New-ScheduledTaskPrincipal -UserId $runAsUser -LogonType Interactive -RunLevel Highest
|
||||
}
|
||||
$settings = New-ScheduledTaskSettingsSet `
|
||||
-AllowStartIfOnBatteries `
|
||||
-StartWhenAvailable `
|
||||
@@ -112,11 +124,12 @@
|
||||
ansible.windows.win_powershell:
|
||||
script: |
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$result = & "{{ aw_windows_evidence_sync_script }}" `
|
||||
-EvidenceApiUrl "{{ aw_windows_evidence_sync_api_url }}" `
|
||||
-TokenPath "{{ aw_windows_evidence_sync_token_path }}" `
|
||||
-StatePath "{{ aw_windows_evidence_sync_state_path }}" `
|
||||
-LogPath "{{ aw_windows_evidence_sync_log_path }}"
|
||||
$result = & "{{ aw_windows_telemetry_exe_path }}" `
|
||||
dlp-evidence-sync `
|
||||
--evidence-api-url "{{ aw_windows_evidence_sync_api_url }}" `
|
||||
--token-path "{{ aw_windows_evidence_sync_token_path }}" `
|
||||
--state-path "{{ aw_windows_evidence_sync_state_path }}" `
|
||||
--log-path "{{ aw_windows_evidence_sync_log_path }}"
|
||||
$result
|
||||
register: aw_windows_evidence_sync_smoke
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
vars:
|
||||
aw_file_1c_repo_root: "{{ playbook_dir | dirname }}"
|
||||
aw_rust_release_dir: "{{ (lookup('env', 'CARGO_TARGET_DIR') | default(aw_file_1c_repo_root + '/adk-rust/target', true)) + '/release' }}"
|
||||
aw_file_1c_release_root: /opt/activitywatch/releases
|
||||
aw_file_1c_release_dir: "{{ aw_file_1c_release_root }}/clickhouse-1c"
|
||||
aw_file_1c_root: /opt/activitywatch/clickhouse-1c
|
||||
@@ -230,6 +231,26 @@
|
||||
- 04_company_intelligence.sql
|
||||
- 05_financial_reporting.sql
|
||||
|
||||
- name: Проверить локальный Rust binary aw-1c-ingest
|
||||
ansible.builtin.stat:
|
||||
path: "{{ aw_rust_release_dir }}/aw-1c-ingest"
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
register: aw_file_1c_ingest_binary
|
||||
|
||||
- name: Остановить deploy без Rust binary aw-1c-ingest
|
||||
ansible.builtin.fail:
|
||||
msg: "Missing {{ aw_rust_release_dir }}/aw-1c-ingest. Build with cargo build --release -p aw-1c-ingest."
|
||||
when: not (aw_file_1c_ingest_binary.stat.exists | default(false))
|
||||
|
||||
- name: Установить Rust binary aw-1c-ingest
|
||||
ansible.builtin.copy:
|
||||
src: "{{ aw_rust_release_dir }}/aw-1c-ingest"
|
||||
dest: /usr/local/bin/aw-1c-ingest-rust
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
|
||||
- name: Установить systemd unit aw-1c-ingest.service
|
||||
ansible.builtin.copy:
|
||||
src: "{{ aw_file_1c_repo_root }}/clickhouse-1c/ops/aw-1c-ingest.service"
|
||||
|
||||
@@ -7,9 +7,13 @@
|
||||
aw_windows_repo_root: "{{ playbook_dir | dirname }}"
|
||||
aw_windows_state_root: "C:\\ProgramData\\AWatch-rus"
|
||||
aw_windows_deploy_root: "C:\\Program Files\\AWatch-rus"
|
||||
aw_windows_rust_target_root: "{{ lookup('env', 'CARGO_TARGET_DIR') | default('/tmp/detmir-adk-rust-target', true) }}"
|
||||
aw_windows_telemetry_exe_source: "{{ aw_windows_rust_target_root }}/x86_64-pc-windows-gnu/release/aw-windows-telemetry.exe"
|
||||
aw_windows_telemetry_exe_path: "{{ aw_windows_deploy_root }}\\windows\\aw-windows-telemetry.exe"
|
||||
aw_windows_file_1c_target_user: "igor"
|
||||
aw_windows_file_1c_auto_upload_interval_hours: 6
|
||||
aw_windows_file_1c_auto_upload_interval_minutes: 15
|
||||
aw_windows_file_1c_auto_upload_task_name: "ActivityWatch File1C Upload"
|
||||
aw_windows_file_1c_auto_upload_run_as_user: "HOST-EXAMPLE\\Администратор"
|
||||
aw_windows_file_1c_remote_root: "/opt/activitywatch/clickhouse-1c/landing"
|
||||
aw_windows_file_1c_remote_key_path: ""
|
||||
aw_windows_file_1c_registry_workbook_path: "E:\\USER1\\СПИСОК ПРЕДПРИЯТИЙ И ИХ РАСПРЕДЕЛЕНИЕ.xlsx"
|
||||
@@ -67,6 +71,11 @@
|
||||
src: "{{ aw_windows_repo_root }}/windows/export-upload-file-1c-telemetry.ps1"
|
||||
dest: "{{ aw_windows_deploy_root }}\\windows\\export-upload-file-1c-telemetry.ps1"
|
||||
|
||||
- name: Загрузить Rust telemetry binary в toolkit
|
||||
ansible.windows.win_copy:
|
||||
src: "{{ aw_windows_telemetry_exe_source }}"
|
||||
dest: "{{ aw_windows_telemetry_exe_path }}"
|
||||
|
||||
- name: Загрузить file-1C telemetry script в state root
|
||||
ansible.windows.win_copy:
|
||||
src: "{{ aw_windows_repo_root }}/windows/export-upload-file-1c-telemetry.ps1"
|
||||
@@ -87,6 +96,11 @@
|
||||
} else {
|
||||
$config.paths | Add-Member -NotePropertyName 'file1cTelemetryScript' -NotePropertyValue "{{ aw_windows_state_root }}\export-upload-file-1c-telemetry.ps1"
|
||||
}
|
||||
if ($config.paths.PSObject.Properties.Name -contains 'file1cTelemetryExecutable') {
|
||||
$config.paths.file1cTelemetryExecutable = "{{ aw_windows_telemetry_exe_path }}"
|
||||
} else {
|
||||
$config.paths | Add-Member -NotePropertyName 'file1cTelemetryExecutable' -NotePropertyValue "{{ aw_windows_telemetry_exe_path }}"
|
||||
}
|
||||
|
||||
if ($config.PSObject.Properties.Name -notcontains 'analytics') {
|
||||
$config | Add-Member -NotePropertyName 'analytics' -NotePropertyValue ([pscustomobject]@{})
|
||||
@@ -94,10 +108,11 @@
|
||||
|
||||
$automation = [pscustomobject]@{
|
||||
enabled = $true
|
||||
intervalHours = {{ aw_windows_file_1c_auto_upload_interval_hours | int }}
|
||||
intervalMinutes = {{ aw_windows_file_1c_auto_upload_interval_minutes | int }}
|
||||
taskName = "{{ aw_windows_file_1c_auto_upload_task_name }}"
|
||||
targetHost = "{{ aw_windows_file_1c_target_host_effective }}"
|
||||
targetUser = "{{ aw_windows_file_1c_target_user }}"
|
||||
runAsUser = "{{ aw_windows_file_1c_auto_upload_run_as_user }}"
|
||||
remoteRoot = "{{ aw_windows_file_1c_remote_root }}"
|
||||
registryWorkbookPath = "{{ aw_windows_file_1c_registry_workbook_path }}"
|
||||
remoteKeyPath = "{{ aw_windows_file_1c_remote_key_path }}"
|
||||
@@ -154,19 +169,20 @@
|
||||
script: |
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$taskName = "{{ aw_windows_file_1c_auto_upload_task_name }}"
|
||||
$powerShellExe = Join-Path $env:SystemRoot 'System32\WindowsPowerShell\v1.0\powershell.exe'
|
||||
$taskCommand = "`"$powerShellExe`" -NoProfile -ExecutionPolicy Bypass -File `"{{ aw_windows_state_root }}\export-upload-file-1c-telemetry.ps1`" -ConfigPath `"{{ aw_windows_state_root }}\deployment-config.json`""
|
||||
$telemetryExe = "{{ aw_windows_telemetry_exe_path }}"
|
||||
$runAsUser = "{{ aw_windows_file_1c_auto_upload_run_as_user }}"
|
||||
$taskArgs = "file1c-upload --config-path `"{{ aw_windows_state_root }}\deployment-config.json`""
|
||||
$action = New-ScheduledTaskAction -Execute $telemetryExe -Argument $taskArgs
|
||||
$trigger = New-ScheduledTaskTrigger -Once -At ((Get-Date).Date) -RepetitionInterval (New-TimeSpan -Minutes {{ aw_windows_file_1c_auto_upload_interval_minutes | int }}) -RepetitionDuration (New-TimeSpan -Days 3650)
|
||||
if ([string]::IsNullOrWhiteSpace($runAsUser)) {
|
||||
$principal = New-ScheduledTaskPrincipal -UserId 'SYSTEM' -LogonType ServiceAccount -RunLevel Highest
|
||||
} else {
|
||||
$principal = New-ScheduledTaskPrincipal -UserId $runAsUser -LogonType Interactive -RunLevel Highest
|
||||
}
|
||||
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -StartWhenAvailable -MultipleInstances IgnoreNew -ExecutionTimeLimit (New-TimeSpan -Minutes 10)
|
||||
$existingTask = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
|
||||
if ($existingTask) {
|
||||
$action = New-ScheduledTaskAction -Execute $powerShellExe -Argument "-NoProfile -ExecutionPolicy Bypass -File `"{{ aw_windows_state_root }}\export-upload-file-1c-telemetry.ps1`" -ConfigPath `"{{ aw_windows_state_root }}\deployment-config.json`""
|
||||
try {
|
||||
Set-ScheduledTask -TaskName $taskName -Action $action -ErrorAction Stop | Out-Null
|
||||
} catch {
|
||||
Write-Host "skip task action update for $taskName because the existing principal requires stored credentials: $($_.Exception.Message)"
|
||||
}
|
||||
Set-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Principal $principal -Settings $settings -ErrorAction Stop | Out-Null
|
||||
} else {
|
||||
& schtasks.exe /Create /TN $taskName /TR $taskCommand /SC HOURLY /MO {{ aw_windows_file_1c_auto_upload_interval_hours | int }} /ST 00:00 /RU SYSTEM /RL HIGHEST /F | Out-Null
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "Не удалось создать scheduled task $taskName"
|
||||
}
|
||||
Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Principal $principal -Settings $settings | Out-Null
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user