diff --git a/ansible/deploy_aw_windows.yml b/ansible/deploy_aw_windows.yml index 568bcc8..15e475c 100644 --- a/ansible/deploy_aw_windows.yml +++ b/ansible/deploy_aw_windows.yml @@ -18,7 +18,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: "HOST-EXAMPLE" + aw_windows_domain: "SHARKON2025" aw_windows_builtin_administrator_name: "Администратор" aw_windows_users: - Администратор @@ -171,18 +171,15 @@ - "{{ aw_windows_deploy_root }}" - "{{ aw_windows_deploy_root }}\\windows" - - name: Загрузить Windows toolkit развёртывания + - name: Загрузить Windows toolkit развёртывания (без PS-дубликатов — заменены Rust) 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 @@ -193,7 +190,6 @@ - deploy-ensemble.ps1 - hardening-recovery.ps1 - AWatchRusCollectorGuardService.cs - - aw-collector-guard.ps1 - install-collector-guard-service.ps1 - rebuild-worktime-tasks.ps1 - audit-cryptopro.ps1 diff --git a/docs/wiki/Windows-Collector-Suite.md b/docs/wiki/Windows-Collector-Suite.md index 53d3ee3..37d3254 100644 --- a/docs/wiki/Windows-Collector-Suite.md +++ b/docs/wiki/Windows-Collector-Suite.md @@ -42,13 +42,13 @@ aw_windows_builtin_administrator_name: "Администратор" Назначение: явно фиксировать локализованное имя встроенной учетной записи Administrator с SID `*-500`. -Для текущего Windows host `HOST-EXAMPLE` task name должен строиться как: +Для текущего Windows host `SHARKON2025` task name должен строиться как: ```text -ActivityWatch Launch [HOST-EXAMPLE_Администратор] +ActivityWatch Launch [SHARKON2025_Администратор] ``` -Если task по `HOST-EXAMPLE_Administrator` не найден, recovery/deploy path обязан пробовать кириллическое имя `Администратор`. Это зафиксировано через: +Если task по `SHARKON2025_Administrator` не найден, recovery/deploy path обязан пробовать кириллическое имя `Администратор`. Это зафиксировано через: - default vars в `ansible/deploy_aw_windows.yml`; - `ansible/group_vars/aw_windows.yml`; @@ -60,7 +60,7 @@ ActivityWatch Launch [HOST-EXAMPLE_Администратор] `ActivityWatch.Windows.Common.psm1` усилил recovery path: -- `Get-ActivityWatchBuiltInAdministratorName` сначала смотрит env override, затем SID-500 lookup, затем host-specific fallback `HOST-EXAMPLE -> Администратор`; +- `Get-ActivityWatchBuiltInAdministratorName` сначала смотрит env override, затем SID-500 lookup, затем host-specific fallback `SHARKON2025 -> Администратор`; - `Normalize-ActivityWatchUsers` стабилизирован для pipeline/list cases; - удаление scheduled tasks стало устойчивее к частично удаленным task definitions; - recovery task может ориентироваться на live interactive session и запускаться в interactive logon context, когда это безопаснее для watcher'ов. diff --git a/docs/windows/deployment.md b/docs/windows/deployment.md index c9be17c..7a1f64b 100755 --- a/docs/windows/deployment.md +++ b/docs/windows/deployment.md @@ -191,7 +191,7 @@ Ansible playbook `ansible/deploy_aw_windows.yml` выполняет этот mig .\windows\deploy-domain-users.ps1 ` -ServerHost ` -ServerPort 5600 ` - -Domain HOST-EXAMPLE ` + -Domain SHARKON2025 ` -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 ` -ServerPort 5600 ` - -TargetUser 'HOST-EXAMPLE\user1' ` + -TargetUser 'SHARKON2025\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 ` diff --git a/docs/windows/validation.md b/docs/windows/validation.md index 30a17c5..72ae9e5 100755 --- a/docs/windows/validation.md +++ b/docs/windows/validation.md @@ -43,7 +43,7 @@ Get-ScheduledTask -TaskName 'ActivityWatch*' | Точечная проверка: ```powershell -Get-ScheduledTask | Where-Object TaskName -eq 'ActivityWatch Launch [HOST-EXAMPLE_user1]' +Get-ScheduledTask | Where-Object TaskName -eq 'ActivityWatch Launch [SHARKON2025_user1]' Get-ScheduledTask | Where-Object TaskName -eq 'ActivityWatch Recovery' ``` diff --git a/windows/ActivityWatch.Windows.Common.psm1 b/windows/ActivityWatch.Windows.Common.psm1 index b57cd40..82eace1 100755 --- a/windows/ActivityWatch.Windows.Common.psm1 +++ b/windows/ActivityWatch.Windows.Common.psm1 @@ -883,11 +883,14 @@ function New-ActivityWatchDeploymentConfig { [Parameter(Mandatory = $true)] [string]$LogsRoot, [Parameter(Mandatory = $true)] + [AllowEmptyString()] [string]$CollectorScript, [Parameter(Mandatory = $true)] + [AllowEmptyString()] [string]$EndpointCollectorScript, [string]$PolicyClientScript, [Parameter(Mandatory = $true)] + [AllowEmptyString()] [string]$FileCollectorScript, [Parameter(Mandatory = $true)] [string]$SessionCollectorScript, diff --git a/windows/aw-collector-guard.ps1 b/windows/aw-collector-guard.ps1 index b6bac2c..e1bc05c 100644 --- a/windows/aw-collector-guard.ps1 +++ b/windows/aw-collector-guard.ps1 @@ -322,18 +322,18 @@ function Invoke-GuardSelfTest { $oldComputerName = $env:COMPUTERNAME try { - $env:COMPUTERNAME = 'HOST-EXAMPLE' + $env:COMPUTERNAME = 'SHARKON2025' $sessionRecords = @( [pscustomobject]@{ SessionName = 'USER5'; UserName = 'USER5'; SessionId = 2; State = 'Disc'; IsLive = $false }, [pscustomobject]@{ SessionName = 'console'; UserName = ''; SessionId = 1; State = 'Conn'; IsLive = $true } ) $taskDefs = @( - [pscustomobject]@{ taskName = 'ActivityWatch Launch [HOST-EXAMPLE_user5]'; userId = 'HOST-EXAMPLE\user5' } + [pscustomobject]@{ taskName = 'ActivityWatch Launch [SHARKON2025_user5]'; userId = 'SHARKON2025\user5' } ) - if (-not (Test-ActivityWatchUserHasManagedSession -UserId 'HOST-EXAMPLE\user5' -SessionRecords $sessionRecords -IncludeDisconnected)) { + if (-not (Test-ActivityWatchUserHasManagedSession -UserId 'SHARKON2025\user5' -SessionRecords $sessionRecords -IncludeDisconnected)) { throw 'expected disconnected managed session to match task user' } - if (Test-ActivityWatchUserHasManagedSession -UserId 'HOST-EXAMPLE\user5' -SessionRecords $sessionRecords -IncludeLive) { + if (Test-ActivityWatchUserHasManagedSession -UserId 'SHARKON2025\user5' -SessionRecords $sessionRecords -IncludeLive) { throw 'disconnected managed session should not match live-only filter' } $managed = @(Get-ActivityWatchManagedInteractiveSessions -TaskDefinitions $taskDefs -SessionRecords $sessionRecords -IncludeDisconnected) diff --git a/windows/hardening-recovery.ps1 b/windows/hardening-recovery.ps1 index a0385e6..da89141 100755 --- a/windows/hardening-recovery.ps1 +++ b/windows/hardening-recovery.ps1 @@ -66,9 +66,6 @@ $effectiveLogsRoot = if ($existingConfig) { [string]$existingConfig.paths.logsRo $effectiveConfigPath = if ($ConfigPath) { $ConfigPath } else { Join-Path $effectiveStateRoot 'deployment-config.json' } $effectiveLaunchScript = Join-Path $effectiveStateRoot 'launch-watchers.ps1' $effectiveRecoveryScript = Join-Path $effectiveStateRoot 'recovery-loop.ps1' -$effectiveCollector = Join-Path $effectiveStateRoot 'browser-domains-native-collector.ps1' -$effectiveEndpointCollector = if ($existingConfig -and $existingConfig.paths.PSObject.Properties.Name -contains 'endpointCollectorScript') { [string]$existingConfig.paths.endpointCollectorScript } else { Join-Path $effectiveStateRoot 'dlp-endpoint-signals-collector.ps1' } -$effectiveFileCollector = if ($existingConfig -and $existingConfig.paths.PSObject.Properties.Name -contains 'fileCollectorScript') { [string]$existingConfig.paths.fileCollectorScript } else { Join-Path $effectiveStateRoot 'file-operations-collector.ps1' } $effectiveSessionCollector = if ($existingConfig -and $existingConfig.paths.PSObject.Properties.Name -contains 'sessionCollectorScript') { [string]$existingConfig.paths.sessionCollectorScript } else { Join-Path $effectiveStateRoot 'worktime-session-collector.ps1' } $effectiveEvtxExportScript = if ($existingConfig -and $existingConfig.paths.PSObject.Properties.Name -contains 'evtxExportScript') { [string]$existingConfig.paths.evtxExportScript } else { Join-Path $effectiveStateRoot 'export-evtx-for-hayabusa.ps1' } $effectiveHayabusaUploadScript = if ($existingConfig -and $existingConfig.paths.PSObject.Properties.Name -contains 'hayabusaUploadScript') { [string]$existingConfig.paths.hayabusaUploadScript } else { Join-Path $effectiveStateRoot 'export-upload-hayabusa-to-aw-server.ps1' } @@ -78,6 +75,18 @@ $effectivePolicy = if ($existingConfig -and $existingConfig.paths.PSObject.Prope $effectivePolicyClientScript = if ($existingConfig -and $existingConfig.paths.PSObject.Properties.Name -contains 'policyClientScript') { [string]$existingConfig.paths.policyClientScript } else { Join-Path $effectiveStateRoot 'dlp-policy-client.ps1' } $effectiveTelemetryExecutable = if ($existingConfig -and $existingConfig.paths.PSObject.Properties.Name -contains 'file1cTelemetryExecutable' -and -not [string]::IsNullOrWhiteSpace([string]$existingConfig.paths.file1cTelemetryExecutable)) { [string]$existingConfig.paths.file1cTelemetryExecutable } else { Join-Path $PSScriptRoot 'aw-windows-telemetry.exe' } +function Resolve-OptionalExistingPath { + param([AllowEmptyString()][string]$Path) + + if ([string]::IsNullOrWhiteSpace($Path)) { + return '' + } + if (Test-Path -LiteralPath $Path) { + return $Path + } + return '' +} + $effectiveServerHost = if ($ServerHost) { $ServerHost } elseif ($existingConfig) { [string]$existingConfig.server.host } else { $null } $effectiveServerPort = if ($PSBoundParameters.ContainsKey('ServerPort')) { $ServerPort } elseif ($existingConfig) { [int]$existingConfig.server.port } else { 5600 } $effectiveServerScheme = if ($ServerScheme) { $ServerScheme } elseif ($existingConfig) { [string]$existingConfig.server.scheme } else { 'http' } @@ -174,6 +183,10 @@ $assetResult = Copy-ActivityWatchCollectorAssets ` -CustomRulesSource $CustomRulesPath ` -CustomPolicySource $CustomPolicyPath +$effectiveCollector = if (-not [string]::IsNullOrWhiteSpace([string]$assetResult.CollectorScript)) { [string]$assetResult.CollectorScript } elseif ($existingConfig -and $existingConfig.paths.PSObject.Properties.Name -contains 'collectorScript') { Resolve-OptionalExistingPath -Path ([string]$existingConfig.paths.collectorScript) } else { '' } +$effectiveEndpointCollector = if (-not [string]::IsNullOrWhiteSpace([string]$assetResult.EndpointCollectorScript)) { [string]$assetResult.EndpointCollectorScript } elseif ($existingConfig -and $existingConfig.paths.PSObject.Properties.Name -contains 'endpointCollectorScript') { Resolve-OptionalExistingPath -Path ([string]$existingConfig.paths.endpointCollectorScript) } else { '' } +$effectiveFileCollector = if (-not [string]::IsNullOrWhiteSpace([string]$assetResult.FileCollectorScript)) { [string]$assetResult.FileCollectorScript } elseif ($existingConfig -and $existingConfig.paths.PSObject.Properties.Name -contains 'fileCollectorScript') { Resolve-OptionalExistingPath -Path ([string]$existingConfig.paths.fileCollectorScript) } else { '' } + $taskDefinitions = New-ActivityWatchUserTaskDefinitions -Users $effectiveUsers Write-ActivityWatchLaunchScript -Path $effectiveLaunchScript -ConfigPath $effectiveConfigPath Write-ActivityWatchRecoveryScript -Path $effectiveRecoveryScript -ConfigPath $effectiveConfigPath diff --git a/windows/installkit/innosetup/AWatch-rus-InnoSetup.iss b/windows/installkit/innosetup/AWatch-rus-InnoSetup.iss index 3d0bc50..720d69b 100644 --- a/windows/installkit/innosetup/AWatch-rus-InnoSetup.iss +++ b/windows/installkit/innosetup/AWatch-rus-InnoSetup.iss @@ -5,7 +5,7 @@ #define AwDefaultServerHost "aw-server" #define AwDefaultServerPort "5600" #define AwDefaultWorktimeReportBase "http://aw-server:5610" -#define AwDefaultWorktimeHost "HOST-EXAMPLE" +#define AwDefaultWorktimeHost "SHARKON2025" #define AwDefaultUsers "user1,user2,user3,user4,user5" #define AwDefaultInstallRoot "C:\\Program Files\\AWatch-rus\\bin" #define AwDefaultStateRoot "C:\\ProgramData\\AWatch-rus" @@ -13,7 +13,7 @@ ; This installer wraps the standalone-service path. ; It is suitable for standalone/headless deployment and must not be treated -; as the canonical multi-user RDP deployment path used on HOST-EXAMPLE. +; as the canonical multi-user RDP deployment path used on SHARKON2025. [Setup] AppId={{6D6A1F74-0F4F-4A57-B5E3-1C2C2F56C0E9} @@ -45,16 +45,12 @@ Source: "..\..\deploy-single-user.ps1"; DestDir: "{app}\windows"; Flags: ignorev Source: "..\..\deploy-domain-users.ps1"; DestDir: "{app}\windows"; Flags: ignoreversion Source: "..\..\deploy-ensemble.ps1"; DestDir: "{app}\windows"; Flags: ignoreversion Source: "..\..\AWatchRusCollectorGuardService.cs"; DestDir: "{app}\windows"; Flags: ignoreversion -Source: "..\..\aw-collector-guard.ps1"; DestDir: "{app}\windows"; Flags: ignoreversion Source: "..\..\install-collector-guard-service.ps1"; DestDir: "{app}\windows"; Flags: ignoreversion Source: "..\..\..\adk-rust\target\x86_64-pc-windows-gnu\release\aw-windows-telemetry.exe"; DestDir: "{app}\windows"; Flags: ignoreversion Source: "..\..\hardening-recovery.ps1"; DestDir: "{app}\windows"; Flags: ignoreversion Source: "..\..\validate-deployment.ps1"; DestDir: "{app}\windows"; Flags: ignoreversion Source: "..\..\migrate-awatch-rus-paths.ps1"; DestDir: "{app}\windows"; Flags: ignoreversion Source: "..\..\worktime-session-collector.ps1"; DestDir: "{app}\windows"; Flags: ignoreversion -Source: "..\..\browser-domains-native-collector.ps1"; DestDir: "{app}\windows"; Flags: ignoreversion -Source: "..\..\dlp-endpoint-signals-collector.ps1"; DestDir: "{app}\windows"; Flags: ignoreversion -Source: "..\..\file-operations-collector.ps1"; DestDir: "{app}\windows"; Flags: ignoreversion Source: "..\..\email-outbound-collector.ps1"; DestDir: "{app}\windows"; Flags: ignoreversion Source: "..\..\web-category-rules.example.json"; DestDir: "{app}\windows"; Flags: ignoreversion Source: "..\..\dlp-policy.example.json"; DestDir: "{app}\windows"; Flags: ignoreversion diff --git a/windows/migrate-awatch-rus-paths.ps1 b/windows/migrate-awatch-rus-paths.ps1 index afc9225..877d83c 100644 --- a/windows/migrate-awatch-rus-paths.ps1 +++ b/windows/migrate-awatch-rus-paths.ps1 @@ -91,15 +91,42 @@ function Update-AWatchConfigPaths { [pscustomobject]$Config ) + function Resolve-OptionalCollectorRuntimePath { + param([Parameter(Mandatory = $true)][string]$FileName) + + $stateCandidate = Join-Path $NewStateRoot $FileName + if (Test-Path -LiteralPath $stateCandidate) { + return $stateCandidate + } + + if (Test-Path -LiteralPath (Join-Path $ToolkitRoot $FileName)) { + return $stateCandidate + } + + return '' + } + + function Set-ConfigPathValue { + param( + [Parameter(Mandatory = $true)][string]$Name, + [AllowEmptyString()][string]$Value + ) + + if ($Config.paths.PSObject.Properties.Name -contains $Name) { + $Config.paths.PSObject.Properties[$Name].Value = $Value + } + else { + $Config.paths | Add-Member -NotePropertyName $Name -NotePropertyValue $Value + } + } + $logsRoot = Join-Path $NewStateRoot 'logs' $Config.paths.installRoot = $NewInstallRoot $Config.paths.stateRoot = $NewStateRoot $Config.paths.logsRoot = $logsRoot - $Config.paths.collectorScript = Join-Path $NewStateRoot 'browser-domains-native-collector.ps1' - $Config.paths.endpointCollectorScript = Join-Path $NewStateRoot 'dlp-endpoint-signals-collector.ps1' - if ($Config.paths.PSObject.Properties.Name -contains 'fileCollectorScript') { - $Config.paths.fileCollectorScript = Join-Path $NewStateRoot 'file-operations-collector.ps1' - } + Set-ConfigPathValue -Name 'collectorScript' -Value (Resolve-OptionalCollectorRuntimePath -FileName 'browser-domains-native-collector.ps1') + Set-ConfigPathValue -Name 'endpointCollectorScript' -Value (Resolve-OptionalCollectorRuntimePath -FileName 'dlp-endpoint-signals-collector.ps1') + Set-ConfigPathValue -Name 'fileCollectorScript' -Value (Resolve-OptionalCollectorRuntimePath -FileName 'file-operations-collector.ps1') if ($Config.paths.PSObject.Properties.Name -contains 'file1cTelemetryExecutable') { $Config.paths.file1cTelemetryExecutable = Join-Path $ToolkitRoot 'aw-windows-telemetry.exe' } diff --git a/windows/run-user1-probe.ps1 b/windows/run-user1-probe.ps1 index 3ef5fbb..641250a 100644 --- a/windows/run-user1-probe.ps1 +++ b/windows/run-user1-probe.ps1 @@ -1,6 +1,6 @@ [CmdletBinding()] param( - [string]$UserId = 'HOST-EXAMPLE\user1' + [string]$UserId = 'SHARKON2025\user1' ) Set-StrictMode -Version Latest @@ -13,7 +13,7 @@ Start-Sleep -Seconds 10 Get-Process notepad -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue '@ | Set-Content -LiteralPath $probeScriptPath -Encoding UTF8 -schtasks /Run /TN 'ActivityWatch Launch [HOST-EXAMPLE_user1]' | Out-Null +schtasks /Run /TN 'ActivityWatch Launch [SHARKON2025_user1]' | Out-Null Start-Sleep -Seconds 3 $taskName = 'AW User1 Notepad Probe' diff --git a/windows/validate-deployment.ps1 b/windows/validate-deployment.ps1 index 9ee27de..9f5f96b 100644 --- a/windows/validate-deployment.ps1 +++ b/windows/validate-deployment.ps1 @@ -123,9 +123,14 @@ function Test-UserHasSession { function Get-CollectorProcesses { param( [Parameter(Mandatory = $true)] + [AllowEmptyString()] [string]$ScriptPath ) + if ([string]::IsNullOrWhiteSpace($ScriptPath)) { + return @() + } + return @( Get-CimInstance Win32_Process -ErrorAction SilentlyContinue | Where-Object {