Revert "merge: apply windows standalone service installer and awHostname hardening"
This reverts commite643576aa9, reversing changes made to669501f20a.
This commit is contained in:
@@ -376,7 +376,6 @@ function New-ActivityWatchDeploymentConfig {
|
||||
[string]$LaunchScriptPath,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$RecoveryScriptPath,
|
||||
[string]$AwHostname,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[pscustomobject[]]$UserTasks,
|
||||
[string]$PackageVersion = 'v0.13.2'
|
||||
@@ -387,7 +386,6 @@ function New-ActivityWatchDeploymentConfig {
|
||||
return [pscustomobject]@{
|
||||
version = 1
|
||||
generatedAtUtc = (Get-Date).ToUniversalTime().ToString('o')
|
||||
awHostname = if ([string]::IsNullOrWhiteSpace($AwHostname)) { [string]$env:COMPUTERNAME } else { [string]$AwHostname }
|
||||
server = [pscustomobject]@{
|
||||
host = $ServerHost
|
||||
port = $ServerPort
|
||||
@@ -744,7 +742,7 @@ function Start-CollectorScriptIfNeeded {
|
||||
`$installRoot = [string]`$config.paths.installRoot
|
||||
`$stateRoot = [string]`$config.paths.stateRoot
|
||||
`$script:ApiBase = '{0}://{1}:{2}/api/0' -f [string]`$config.server.scheme, [string]`$config.server.host, [string]`$config.server.port
|
||||
`$script:Hostname = if (`$config.PSObject.Properties.Name -contains 'awHostname' -and -not [string]::IsNullOrWhiteSpace([string]`$config.awHostname)) { [string]`$config.awHostname } else { `$env:COMPUTERNAME }
|
||||
`$script:Hostname = `$env:COMPUTERNAME
|
||||
`$script:KnownBuckets = @{}
|
||||
`$collectorScript = [string]`$config.paths.collectorScript
|
||||
`$endpointCollectorScript = if (`$config.paths.PSObject.Properties.Name -contains 'endpointCollectorScript') { [string]`$config.paths.endpointCollectorScript } else { Join-Path `$stateRoot 'dlp-endpoint-signals-collector.ps1' }
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$ConfigPath = 'C:\ProgramData\AWatch-rus\deployment-config.json',
|
||||
[int]$LoopSeconds = 20
|
||||
)
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
function Get-Config {
|
||||
param([string]$Path)
|
||||
if (-not (Test-Path -LiteralPath $Path)) {
|
||||
throw "Config not found: $Path"
|
||||
}
|
||||
Get-Content -LiteralPath $Path -Raw | ConvertFrom-Json
|
||||
}
|
||||
|
||||
function Write-ServiceLog {
|
||||
param([string]$Message)
|
||||
try {
|
||||
Add-Content -LiteralPath $script:LogPath -Value ('{0} {1}' -f (Get-Date -Format s), $Message)
|
||||
}
|
||||
catch {}
|
||||
}
|
||||
|
||||
function Start-CollectorIfNeeded {
|
||||
param(
|
||||
[string]$ScriptPath,
|
||||
[string]$ConfigPath
|
||||
)
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($ScriptPath) -or -not (Test-Path -LiteralPath $ScriptPath)) {
|
||||
return
|
||||
}
|
||||
|
||||
$escaped = [Regex]::Escape($ScriptPath)
|
||||
$running = Get-CimInstance Win32_Process -ErrorAction SilentlyContinue |
|
||||
Where-Object {
|
||||
$_.Name -eq 'powershell.exe' -and
|
||||
$_.CommandLine -match $escaped -and
|
||||
$_.CommandLine -match [Regex]::Escape($ConfigPath)
|
||||
} |
|
||||
Select-Object -First 1
|
||||
|
||||
if ($running) {
|
||||
return
|
||||
}
|
||||
|
||||
$args = @('-NoProfile', '-ExecutionPolicy', 'Bypass')
|
||||
if ($ScriptPath -like '*dlp-endpoint-signals*') {
|
||||
$args += '-STA'
|
||||
}
|
||||
$args += @('-File', $ScriptPath, '-ConfigPath', $ConfigPath)
|
||||
Start-Process -FilePath 'powershell.exe' -ArgumentList $args -WindowStyle Hidden | Out-Null
|
||||
Write-ServiceLog ("started collector: {0}" -f $ScriptPath)
|
||||
}
|
||||
|
||||
$cfg = Get-Config -Path $ConfigPath
|
||||
$stateRoot = if ($cfg.paths -and $cfg.paths.stateRoot) { [string]$cfg.paths.stateRoot } else { 'C:\ProgramData\AWatch-rus' }
|
||||
$logsRoot = Join-Path $stateRoot 'logs'
|
||||
if (-not (Test-Path -LiteralPath $logsRoot)) {
|
||||
New-Item -Path $logsRoot -ItemType Directory -Force | Out-Null
|
||||
}
|
||||
$script:LogPath = Join-Path $logsRoot 'standalone-agent-service.log'
|
||||
|
||||
Write-ServiceLog ('service loop started, config={0}' -f $ConfigPath)
|
||||
|
||||
while ($true) {
|
||||
try {
|
||||
$cfg = Get-Config -Path $ConfigPath
|
||||
$paths = $cfg.paths
|
||||
|
||||
Start-CollectorIfNeeded -ScriptPath ([string]$paths.collectorScript) -ConfigPath $ConfigPath
|
||||
Start-CollectorIfNeeded -ScriptPath ([string]$paths.endpointCollectorScript) -ConfigPath $ConfigPath
|
||||
Start-CollectorIfNeeded -ScriptPath ([string]$paths.fileCollectorScript) -ConfigPath $ConfigPath
|
||||
if ($paths.PSObject.Properties.Name -contains 'emailCollectorScript') {
|
||||
Start-CollectorIfNeeded -ScriptPath ([string]$paths.emailCollectorScript) -ConfigPath $ConfigPath
|
||||
}
|
||||
if ($paths.PSObject.Properties.Name -contains 'sessionCollectorScript') {
|
||||
Start-CollectorIfNeeded -ScriptPath ([string]$paths.sessionCollectorScript) -ConfigPath $ConfigPath
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-ServiceLog ("loop error: {0}" -f $_.Exception.Message)
|
||||
}
|
||||
Start-Sleep -Seconds ([Math]::Max($LoopSeconds, 5))
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[CmdletBinding()]
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$ConfigPath = 'C:\ProgramData\AWatch-rus\deployment-config.json',
|
||||
[string]$ServerHost,
|
||||
@@ -59,18 +59,16 @@ $resolvedPulseSeconds = if ($PSBoundParameters.ContainsKey('PulseSeconds')) { $P
|
||||
$resolvedLogsRoot = if ($deploymentConfig) { [string]$deploymentConfig.paths.logsRoot } else { 'C:\ProgramData\AWatch-rus\logs' }
|
||||
$resolvedLogPath = if ($LogPath) { $LogPath } else { Join-Path $resolvedLogsRoot ("browser-domains-{0}.log" -f $env:USERNAME) }
|
||||
$resolvedIncidentLogPath = if ($IncidentLogPath) { $IncidentLogPath } else { Join-Path $resolvedLogsRoot ("dlp-incidents-{0}.log" -f $env:USERNAME) }
|
||||
$resolvedHealthPath = Join-Path $resolvedLogsRoot ("health-browser-domains-{0}.json" -f $env:USERNAME)
|
||||
$resolvedLocalAgentLogsEnabled = if ($deploymentConfig -and $deploymentConfig.PSObject.Properties.Name -contains 'logging' -and $deploymentConfig.logging.PSObject.Properties.Name -contains 'localAgentLogsEnabled') { [bool]$deploymentConfig.logging.localAgentLogsEnabled } else { $true }
|
||||
$resolvedIncidentArtifactsRoot = if ($deploymentConfig -and $deploymentConfig.PSObject.Properties.Name -contains 'incidentCapture' -and $deploymentConfig.incidentCapture.PSObject.Properties.Name -contains 'artifactsRoot') { [string]$deploymentConfig.incidentCapture.artifactsRoot } else { Join-Path $env:LOCALAPPDATA 'AWatch-rus\\incident-artifacts' }
|
||||
$resolvedIncidentScreenshotEnabled = if ($deploymentConfig -and $deploymentConfig.PSObject.Properties.Name -contains 'incidentCapture' -and $deploymentConfig.incidentCapture.PSObject.Properties.Name -contains 'screenshotEnabled') { [bool]$deploymentConfig.incidentCapture.screenshotEnabled } else { $true }
|
||||
$resolvedHostname = if ($deploymentConfig -and $deploymentConfig.PSObject.Properties.Name -contains 'awHostname' -and -not [string]::IsNullOrWhiteSpace([string]$deploymentConfig.awHostname)) { [string]$deploymentConfig.awHostname } else { [string]$env:COMPUTERNAME }
|
||||
|
||||
if ($resolvedLocalAgentLogsEnabled -and -not (Test-Path -LiteralPath $resolvedLogsRoot)) {
|
||||
New-Item -Path $resolvedLogsRoot -ItemType Directory -Force | Out-Null
|
||||
}
|
||||
|
||||
$script:ApiBase = '{0}://{1}:{2}/api/0' -f $resolvedServerScheme, $resolvedServerHost, $resolvedServerPort
|
||||
$script:Hostname = $resolvedHostname
|
||||
$script:Hostname = $env:COMPUTERNAME
|
||||
$script:SessionId = (Get-Process -Id $PID).SessionId
|
||||
$script:KnownBuckets = @{}
|
||||
$script:LocalAgentLogsEnabled = $resolvedLocalAgentLogsEnabled
|
||||
@@ -87,7 +85,6 @@ $script:DlpDefaults = [ordered]@{
|
||||
action = 'log'
|
||||
severity = 'low'
|
||||
}
|
||||
$script:HealthPath = $resolvedHealthPath
|
||||
$script:BrowserMap = @{
|
||||
msedge = 'edge'
|
||||
chrome = 'chrome'
|
||||
@@ -137,23 +134,6 @@ function Write-DlpIncidentLog {
|
||||
}
|
||||
}
|
||||
|
||||
function Write-CollectorHealth {
|
||||
param([string]$Status = 'running')
|
||||
try {
|
||||
$health = @{
|
||||
collector = 'browser-domains-native'
|
||||
hostname = $script:Hostname
|
||||
sessionId = $script:SessionId
|
||||
status = $Status
|
||||
apiBase = $script:ApiBase
|
||||
ts = (Get-Date).ToUniversalTime().ToString('o')
|
||||
} | ConvertTo-Json -Depth 4
|
||||
Set-Content -LiteralPath $script:HealthPath -Value $health -Encoding UTF8
|
||||
}
|
||||
catch {
|
||||
}
|
||||
}
|
||||
|
||||
function Test-DomainMatch {
|
||||
param(
|
||||
[string]$DomainHost,
|
||||
@@ -810,52 +790,46 @@ Load-CustomCategoryRules -Path $resolvedRulesPath
|
||||
Load-DlpPolicy -Path $resolvedPolicyPath
|
||||
Write-CollectorLog ("коллектор запущен для {0}" -f $script:ApiBase)
|
||||
|
||||
try {
|
||||
while ($true) {
|
||||
try {
|
||||
Write-CollectorHealth -Status 'running'
|
||||
$context = Get-ForegroundWindowContext
|
||||
if ($context -and $script:BrowserMap.ContainsKey($context.ProcessName)) {
|
||||
$url = Get-BrowserUrlFromWindow -Handle $context.Handle
|
||||
if ($url) {
|
||||
$browserKey = $script:BrowserMap[$context.ProcessName]
|
||||
$domain = Get-HostFromUrl -Url $url
|
||||
if (-not $domain) {
|
||||
$domain = 'unknown'
|
||||
}
|
||||
while ($true) {
|
||||
try {
|
||||
$context = Get-ForegroundWindowContext
|
||||
if ($context -and $script:BrowserMap.ContainsKey($context.ProcessName)) {
|
||||
$url = Get-BrowserUrlFromWindow -Handle $context.Handle
|
||||
if ($url) {
|
||||
$browserKey = $script:BrowserMap[$context.ProcessName]
|
||||
$domain = Get-HostFromUrl -Url $url
|
||||
if (-not $domain) {
|
||||
$domain = 'unknown'
|
||||
}
|
||||
|
||||
$rootDomain = Get-RootDomain -DomainHost $domain
|
||||
if (-not $rootDomain) {
|
||||
$rootDomain = $domain
|
||||
}
|
||||
$rootDomain = Get-RootDomain -DomainHost $domain
|
||||
if (-not $rootDomain) {
|
||||
$rootDomain = $domain
|
||||
}
|
||||
|
||||
$category = Get-WebCategory -DomainHost $domain
|
||||
$bucketId = 'aw-watcher-web-{0}_{1}' -f $browserKey, $script:Hostname
|
||||
Ensure-Bucket -BucketId $bucketId -ClientName ('aw-watcher-web-' + $browserKey)
|
||||
Send-Heartbeat -BucketId $bucketId -Url $url -Title $context.Title -BrowserKey $browserKey -ProcessName $context.ProcessName
|
||||
Send-CategoryHeartbeat -Url $url -Title $context.Title -BrowserKey $browserKey -ProcessName $context.ProcessName -Domain $domain -RootDomain $rootDomain -Category $category.Name -CategoryGroup $category.Group -CategoryRule $category.Rule
|
||||
$category = Get-WebCategory -DomainHost $domain
|
||||
$bucketId = 'aw-watcher-web-{0}_{1}' -f $browserKey, $script:Hostname
|
||||
Ensure-Bucket -BucketId $bucketId -ClientName ('aw-watcher-web-' + $browserKey)
|
||||
Send-Heartbeat -BucketId $bucketId -Url $url -Title $context.Title -BrowserKey $browserKey -ProcessName $context.ProcessName
|
||||
Send-CategoryHeartbeat -Url $url -Title $context.Title -BrowserKey $browserKey -ProcessName $context.ProcessName -Domain $domain -RootDomain $rootDomain -Category $category.Name -CategoryGroup $category.Group -CategoryRule $category.Rule
|
||||
|
||||
$decision = Get-DlpDecision -Domain $domain -RootDomain $rootDomain -Url $url -Title $context.Title -BrowserKey $browserKey -Category $category.Name -CategoryGroup $category.Group
|
||||
if ($decision) {
|
||||
$fingerprint = '{0}|{1}|{2}|{3}' -f $decision.id, $browserKey, $rootDomain, $env:USERNAME
|
||||
$cooldown = [Math]::Max([int]$decision.cooldownSeconds, 30)
|
||||
if (Should-EmitIncident -Fingerprint $fingerprint -CooldownSeconds $cooldown) {
|
||||
Write-DlpIncidentLog ("{0} {1} {2} {3}" -f $decision.severity, $decision.action, $decision.id, $url)
|
||||
if (@('alert', 'block', 'quarantine') -contains ([string]$decision.action).ToLowerInvariant()) {
|
||||
Send-DlpIncidentHeartbeat -Decision $decision -Url $url -Title $context.Title -BrowserKey $browserKey -ProcessName $context.ProcessName -Domain $domain -RootDomain $rootDomain -Category $category.Name -CategoryGroup $category.Group
|
||||
}
|
||||
$decision = Get-DlpDecision -Domain $domain -RootDomain $rootDomain -Url $url -Title $context.Title -BrowserKey $browserKey -Category $category.Name -CategoryGroup $category.Group
|
||||
if ($decision) {
|
||||
$fingerprint = '{0}|{1}|{2}|{3}' -f $decision.id, $browserKey, $rootDomain, $env:USERNAME
|
||||
$cooldown = [Math]::Max([int]$decision.cooldownSeconds, 30)
|
||||
if (Should-EmitIncident -Fingerprint $fingerprint -CooldownSeconds $cooldown) {
|
||||
Write-DlpIncidentLog ("{0} {1} {2} {3}" -f $decision.severity, $decision.action, $decision.id, $url)
|
||||
if (@('alert', 'block', 'quarantine') -contains ([string]$decision.action).ToLowerInvariant()) {
|
||||
Send-DlpIncidentHeartbeat -Decision $decision -Url $url -Title $context.Title -BrowserKey $browserKey -ProcessName $context.ProcessName -Domain $domain -RootDomain $rootDomain -Category $category.Name -CategoryGroup $category.Group
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-CollectorLog ("ошибка коллектора: {0}" -f $_.Exception.Message)
|
||||
}
|
||||
|
||||
Start-Sleep -Seconds $resolvedPollSeconds
|
||||
}
|
||||
}
|
||||
finally {
|
||||
Write-CollectorHealth -Status 'stopped'
|
||||
catch {
|
||||
Write-CollectorLog ("ошибка коллектора: {0}" -f $_.Exception.Message)
|
||||
}
|
||||
|
||||
Start-Sleep -Seconds $resolvedPollSeconds
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[CmdletBinding()]
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$ServerHost,
|
||||
@@ -24,7 +24,6 @@ param(
|
||||
[bool]$IncidentScreenshotEnabled = $true,
|
||||
[string]$IncidentArtifactsRoot,
|
||||
[bool]$LogonMarkerEnabled = $true,
|
||||
[string]$AwHostname,
|
||||
[string]$CustomRulesPath,
|
||||
[string]$CustomPolicyPath
|
||||
)
|
||||
@@ -101,7 +100,6 @@ $config = New-ActivityWatchDeploymentConfig `
|
||||
-IncidentScreenshotEnabled $IncidentScreenshotEnabled `
|
||||
-IncidentArtifactsRoot $IncidentArtifactsRoot `
|
||||
-LogonMarkerEnabled $LogonMarkerEnabled `
|
||||
-AwHostname $AwHostname `
|
||||
-LaunchScriptPath $launchScriptPath `
|
||||
-RecoveryScriptPath $recoveryScriptPath `
|
||||
-UserTasks $taskDefinitions `
|
||||
@@ -114,8 +112,8 @@ Register-ActivityWatchUserTasks -TaskDefinitions $taskDefinitions -LaunchScriptP
|
||||
Register-ActivityWatchRecoveryTask -TaskName $config.recovery.taskName -RecoveryScriptPath $recoveryScriptPath -ConfigPath $configPath
|
||||
Start-ActivityWatchTasks -TaskDefinitions $taskDefinitions -RecoveryTaskName $config.recovery.taskName
|
||||
|
||||
Write-Output 'ActivityWatch развёрнут для пользователей:'
|
||||
$targetUsers | ForEach-Object { Write-Output " - $_" }
|
||||
Write-Output "Сервер: ${ServerScheme}://$ServerHost`:$ServerPort"
|
||||
Write-Output "Каталог данных: $StateRoot"
|
||||
Write-Output "Файл DLP-политики: $($assetResult.ActivePolicy)"
|
||||
Write-Host 'ActivityWatch развёрнут для пользователей:'
|
||||
$targetUsers | ForEach-Object { Write-Host " - $_" }
|
||||
Write-Host "Сервер: ${ServerScheme}://$ServerHost`:$ServerPort"
|
||||
Write-Host "Каталог данных: $StateRoot"
|
||||
Write-Host "Файл DLP-политики: $($assetResult.ActivePolicy)"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[CmdletBinding()]
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$ServerHost,
|
||||
@@ -24,7 +24,6 @@ param(
|
||||
[bool]$IncidentScreenshotEnabled = $true,
|
||||
[string]$IncidentArtifactsRoot,
|
||||
[bool]$LogonMarkerEnabled = $true,
|
||||
[string]$AwHostname,
|
||||
[string]$CustomRulesPath,
|
||||
[string]$CustomPolicyPath,
|
||||
[string]$ReportPath,
|
||||
@@ -72,7 +71,6 @@ if (-not (Test-Path -LiteralPath $deployScript)) {
|
||||
-IncidentScreenshotEnabled $IncidentScreenshotEnabled `
|
||||
-IncidentArtifactsRoot $IncidentArtifactsRoot `
|
||||
-LogonMarkerEnabled $LogonMarkerEnabled `
|
||||
-AwHostname $AwHostname `
|
||||
-CustomRulesPath $CustomRulesPath `
|
||||
-CustomPolicyPath $CustomPolicyPath
|
||||
|
||||
@@ -96,7 +94,6 @@ if (-not $SkipHardening) {
|
||||
-IncidentScreenshotEnabled $IncidentScreenshotEnabled `
|
||||
-IncidentArtifactsRoot $IncidentArtifactsRoot `
|
||||
-LogonMarkerEnabled $LogonMarkerEnabled `
|
||||
-AwHostname $AwHostname `
|
||||
-CustomRulesPath $CustomRulesPath `
|
||||
-CustomPolicyPath $CustomPolicyPath
|
||||
}
|
||||
@@ -139,6 +136,6 @@ if ($reportDirectory) {
|
||||
|
||||
$report | ConvertTo-Json -Depth 12 | Set-Content -LiteralPath $effectiveReportPath -Encoding UTF8
|
||||
|
||||
Write-Output 'Комплексное развёртывание ActivityWatch завершено.'
|
||||
Write-Output "Пользователи: $($resolvedUsers -join ', ')"
|
||||
Write-Output "Отчёт: $effectiveReportPath"
|
||||
Write-Host 'Комплексное развёртывание ActivityWatch завершено.'
|
||||
Write-Host "Пользователи: $($resolvedUsers -join ', ')"
|
||||
Write-Host "Отчёт: $effectiveReportPath"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[CmdletBinding()]
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$ServerHost,
|
||||
@@ -22,7 +22,6 @@ param(
|
||||
[bool]$IncidentScreenshotEnabled = $true,
|
||||
[string]$IncidentArtifactsRoot,
|
||||
[bool]$LogonMarkerEnabled = $true,
|
||||
[string]$AwHostname,
|
||||
[string]$CustomRulesPath,
|
||||
[string]$CustomPolicyPath
|
||||
)
|
||||
@@ -93,7 +92,6 @@ $config = New-ActivityWatchDeploymentConfig `
|
||||
-IncidentScreenshotEnabled $IncidentScreenshotEnabled `
|
||||
-IncidentArtifactsRoot $IncidentArtifactsRoot `
|
||||
-LogonMarkerEnabled $LogonMarkerEnabled `
|
||||
-AwHostname $AwHostname `
|
||||
-LaunchScriptPath $launchScriptPath `
|
||||
-RecoveryScriptPath $recoveryScriptPath `
|
||||
-UserTasks $taskDefinitions `
|
||||
@@ -106,9 +104,9 @@ Register-ActivityWatchUserTasks -TaskDefinitions $taskDefinitions -LaunchScriptP
|
||||
Register-ActivityWatchRecoveryTask -TaskName $config.recovery.taskName -RecoveryScriptPath $recoveryScriptPath -ConfigPath $configPath
|
||||
Start-ActivityWatchTasks -TaskDefinitions $taskDefinitions -RecoveryTaskName $config.recovery.taskName
|
||||
|
||||
Write-Output "ActivityWatch развёрнут для пользователя: $TargetUser"
|
||||
Write-Output "Сервер: ${ServerScheme}://$ServerHost`:$ServerPort"
|
||||
Write-Output "Каталог установки: $InstallRoot"
|
||||
Write-Output "Каталог данных: $StateRoot"
|
||||
Write-Output "Файл правил: $($assetResult.ActiveRules)"
|
||||
Write-Output "Файл DLP-политики: $($assetResult.ActivePolicy)"
|
||||
Write-Host "ActivityWatch развёрнут для пользователя: $TargetUser"
|
||||
Write-Host "Сервер: ${ServerScheme}://$ServerHost`:$ServerPort"
|
||||
Write-Host "Каталог установки: $InstallRoot"
|
||||
Write-Host "Каталог данных: $StateRoot"
|
||||
Write-Host "Файл правил: $($assetResult.ActiveRules)"
|
||||
Write-Host "Файл DLP-политики: $($assetResult.ActivePolicy)"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -53,79 +53,13 @@ function Write-CollectorLog {
|
||||
catch { }
|
||||
}
|
||||
|
||||
function Add-WalEntry {
|
||||
function Invoke-AwJsonPost {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string]$Uri,
|
||||
[Parameter(Mandatory = $true)][string]$Json
|
||||
)
|
||||
if ([string]::IsNullOrWhiteSpace($script:WalPath)) { return }
|
||||
try {
|
||||
$entry = @{ ts = (Get-Date).ToUniversalTime().ToString('o'); uri = $Uri; json = $Json } | ConvertTo-Json -Compress
|
||||
Add-Content -LiteralPath $script:WalPath -Value $entry -Encoding UTF8
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function Flush-Wal {
|
||||
if ([string]::IsNullOrWhiteSpace($script:WalPath) -or -not (Test-Path -LiteralPath $script:WalPath)) { return }
|
||||
$remaining = New-Object System.Collections.Generic.List[string]
|
||||
try {
|
||||
$script:WalFlushing = $true
|
||||
foreach ($line in (Get-Content -LiteralPath $script:WalPath -ErrorAction SilentlyContinue)) {
|
||||
if ([string]::IsNullOrWhiteSpace($line)) { continue }
|
||||
try {
|
||||
$entry = $line | ConvertFrom-Json
|
||||
if ($null -eq $entry -or -not $entry.uri -or -not $entry.json) { continue }
|
||||
if (-not (Invoke-AwJsonPost -Uri ([string]$entry.uri) -Json ([string]$entry.json))) { $remaining.Add($line) }
|
||||
} catch { $remaining.Add($line) }
|
||||
}
|
||||
if ($remaining.Count -eq 0) {
|
||||
Remove-Item -LiteralPath $script:WalPath -Force -ErrorAction SilentlyContinue
|
||||
} else {
|
||||
Set-Content -LiteralPath $script:WalPath -Value ($remaining -join [Environment]::NewLine) -Encoding UTF8
|
||||
}
|
||||
} finally {
|
||||
$script:WalFlushing = $false
|
||||
}
|
||||
}
|
||||
|
||||
function Write-CollectorHealth {
|
||||
param([string]$Status = 'running')
|
||||
if ([string]::IsNullOrWhiteSpace($script:HealthPath)) { return }
|
||||
try {
|
||||
$walDepth = 0
|
||||
if ($script:WalPath -and (Test-Path -LiteralPath $script:WalPath)) { $walDepth = @((Get-Content -LiteralPath $script:WalPath)).Count }
|
||||
$health = @{
|
||||
collector = 'email-outbound'; hostname = $script:Hostname; sessionId = $script:SessionId;
|
||||
status = $Status; apiBase = $script:ApiBase; walDepth = $walDepth; ts = (Get-Date).ToUniversalTime().ToString('o')
|
||||
} | ConvertTo-Json -Depth 5
|
||||
Set-Content -LiteralPath $script:HealthPath -Value $health -Encoding UTF8
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function Invoke-AwJsonPost {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string]$Uri,
|
||||
[Parameter(Mandatory = $true)][string]$Json,
|
||||
[int]$MaxAttempts = 5,
|
||||
[int]$InitialBackoffMs = 500
|
||||
)
|
||||
$attempt = 1
|
||||
$backoff = [Math]::Max(100, $InitialBackoffMs)
|
||||
$bytes = [Text.Encoding]::UTF8.GetBytes($Json)
|
||||
while ($attempt -le $MaxAttempts) {
|
||||
try {
|
||||
Invoke-RestMethod -Method Post -Uri $Uri -ContentType 'application/json; charset=utf-8' -Body $bytes | Out-Null
|
||||
return $true
|
||||
} catch {
|
||||
if ($attempt -ge $MaxAttempts) {
|
||||
if (-not $script:WalFlushing) { Add-WalEntry -Uri $Uri -Json $Json }
|
||||
return $false
|
||||
}
|
||||
Start-Sleep -Milliseconds $backoff
|
||||
$backoff = [Math]::Min($backoff * 2, 10000)
|
||||
$attempt++
|
||||
}
|
||||
}
|
||||
Invoke-RestMethod -Method Post -Uri $Uri -ContentType 'application/json; charset=utf-8' -Body $bytes | Out-Null
|
||||
}
|
||||
|
||||
function Ensure-Bucket {
|
||||
@@ -583,10 +517,6 @@ $script:SeenSmtpConnections = @{}
|
||||
$script:PulseSeconds = [Math]::Max($resolvedPollSeconds * 3, 30)
|
||||
$script:LocalAgentLogsEnabled = $resolvedLocalAgentLogsEnabled
|
||||
$script:LogPath = $resolvedLogPath
|
||||
$stateRoot = if ($deploymentConfig -and $deploymentConfig.paths -and $deploymentConfig.paths.stateRoot) { [string]$deploymentConfig.paths.stateRoot } else { 'C:\ProgramData\AWatch-rus' }
|
||||
$script:WalPath = Join-Path $stateRoot 'wal-email-outbound.ndjson'
|
||||
$script:HealthPath = Join-Path $stateRoot 'health-email-outbound.json'
|
||||
$script:WalFlushing = $false
|
||||
$script:OutlookApp = $null
|
||||
$script:OutlookNamespace = $null
|
||||
$script:SentFolder = $null
|
||||
@@ -610,65 +540,43 @@ if ($useOutlook) {
|
||||
# Main loop
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
try {
|
||||
while ($true) {
|
||||
try {
|
||||
Flush-Wal
|
||||
Write-CollectorHealth -Status 'running'
|
||||
if (-not $script:Policy.defaults.enabled) {
|
||||
Start-Sleep -Seconds $resolvedPollSeconds
|
||||
continue
|
||||
}
|
||||
while ($true) {
|
||||
try {
|
||||
if (-not $script:Policy.defaults.enabled) {
|
||||
Start-Sleep -Seconds $resolvedPollSeconds
|
||||
continue
|
||||
}
|
||||
|
||||
if ($useOutlook) {
|
||||
if (-not $outlookReady) {
|
||||
$outlookReady = Initialize-OutlookCom
|
||||
}
|
||||
if ($outlookReady) {
|
||||
try {
|
||||
Poll-OutlookSentItems
|
||||
}
|
||||
catch {
|
||||
Write-CollectorLog ("outlook poll error: {0}" -f $_.Exception.Message)
|
||||
$outlookReady = $false
|
||||
$script:OutlookApp = $null
|
||||
$script:OutlookNamespace = $null
|
||||
$script:SentFolder = $null
|
||||
}
|
||||
}
|
||||
if ($useOutlook) {
|
||||
if (-not $outlookReady) {
|
||||
$outlookReady = Initialize-OutlookCom
|
||||
}
|
||||
|
||||
if ($useSmtp) {
|
||||
if ($outlookReady) {
|
||||
try {
|
||||
Poll-SmtpConnections
|
||||
Poll-OutlookSentItems
|
||||
}
|
||||
catch {
|
||||
Write-CollectorLog ("smtp poll error: {0}" -f $_.Exception.Message)
|
||||
Write-CollectorLog ("outlook poll error: {0}" -f $_.Exception.Message)
|
||||
$outlookReady = $false
|
||||
$script:OutlookApp = $null
|
||||
$script:OutlookNamespace = $null
|
||||
$script:SentFolder = $null
|
||||
}
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-CollectorLog ("collector error: {0}" -f $_.Exception.Message)
|
||||
}
|
||||
|
||||
Start-Sleep -Seconds $resolvedPollSeconds
|
||||
}
|
||||
}
|
||||
finally {
|
||||
Write-CollectorHealth -Status 'stopped'
|
||||
try {
|
||||
if ($null -ne $script:SentFolder) {
|
||||
[void][System.Runtime.InteropServices.Marshal]::ReleaseComObject($script:SentFolder)
|
||||
$script:SentFolder = $null
|
||||
}
|
||||
if ($null -ne $script:OutlookNamespace) {
|
||||
[void][System.Runtime.InteropServices.Marshal]::ReleaseComObject($script:OutlookNamespace)
|
||||
$script:OutlookNamespace = $null
|
||||
}
|
||||
if ($null -ne $script:OutlookApp) {
|
||||
[void][System.Runtime.InteropServices.Marshal]::ReleaseComObject($script:OutlookApp)
|
||||
$script:OutlookApp = $null
|
||||
if ($useSmtp) {
|
||||
try {
|
||||
Poll-SmtpConnections
|
||||
}
|
||||
catch {
|
||||
Write-CollectorLog ("smtp poll error: {0}" -f $_.Exception.Message)
|
||||
}
|
||||
}
|
||||
}
|
||||
catch {}
|
||||
catch {
|
||||
Write-CollectorLog ("collector error: {0}" -f $_.Exception.Message)
|
||||
}
|
||||
|
||||
Start-Sleep -Seconds $resolvedPollSeconds
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[CmdletBinding()]
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$ConfigPath = 'C:\ProgramData\AWatch-rus\deployment-config.json',
|
||||
[string]$ServerHost,
|
||||
@@ -22,9 +22,6 @@ Add-Type -AssemblyName System.Net.Http
|
||||
$script:KnownBuckets = @{}
|
||||
$script:Hostname = $env:COMPUTERNAME
|
||||
$script:SessionId = [System.Diagnostics.Process]::GetCurrentProcess().SessionId
|
||||
$script:WalPath = $null
|
||||
$script:HealthPath = $null
|
||||
$script:WalFlushing = $false
|
||||
|
||||
# Настройка логирования
|
||||
$script:LogPath = $LogPath
|
||||
@@ -46,88 +43,28 @@ function Write-FileCollectorLog {
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function Add-WalEntry {
|
||||
function Invoke-AwJsonPost {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string]$Uri,
|
||||
[Parameter(Mandatory = $true)][string]$Json
|
||||
)
|
||||
if ([string]::IsNullOrWhiteSpace($script:WalPath)) { return }
|
||||
$httpClient = $null
|
||||
try {
|
||||
$entry = @{ ts = (Get-Date).ToUniversalTime().ToString('o'); uri = $Uri; json = $Json } | ConvertTo-Json -Compress
|
||||
Add-Content -LiteralPath $script:WalPath -Value $entry -Encoding UTF8
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function Flush-Wal {
|
||||
if ([string]::IsNullOrWhiteSpace($script:WalPath) -or -not (Test-Path -LiteralPath $script:WalPath)) { return }
|
||||
$remaining = New-Object System.Collections.Generic.List[string]
|
||||
try {
|
||||
$script:WalFlushing = $true
|
||||
foreach ($line in (Get-Content -LiteralPath $script:WalPath -ErrorAction SilentlyContinue)) {
|
||||
if ([string]::IsNullOrWhiteSpace($line)) { continue }
|
||||
try {
|
||||
$entry = $line | ConvertFrom-Json
|
||||
if ($null -eq $entry -or -not $entry.uri -or -not $entry.json) { continue }
|
||||
if (-not (Invoke-AwJsonPost -Uri ([string]$entry.uri) -Json ([string]$entry.json))) { $remaining.Add($line) }
|
||||
} catch { $remaining.Add($line) }
|
||||
}
|
||||
if ($remaining.Count -eq 0) {
|
||||
Remove-Item -LiteralPath $script:WalPath -Force -ErrorAction SilentlyContinue
|
||||
} else {
|
||||
Set-Content -LiteralPath $script:WalPath -Value ($remaining -join [Environment]::NewLine) -Encoding UTF8
|
||||
$httpClient = New-Object System.Net.Http.HttpClient
|
||||
$content = New-Object System.Net.Http.StringContent($Json, [System.Text.Encoding]::UTF8, "application/json")
|
||||
$response = $httpClient.PostAsync($Uri, $content).Result
|
||||
if (-not $response.IsSuccessStatusCode) {
|
||||
$status = [int]$response.StatusCode
|
||||
$reason = [string]$response.ReasonPhrase
|
||||
$body = $response.Content.ReadAsStringAsync().Result
|
||||
Write-FileCollectorLog ("POST failed: uri={0} status={1} reason={2} body={3}" -f $Uri, $status, $reason, $body)
|
||||
}
|
||||
} catch {
|
||||
Write-FileCollectorLog "POST Error: $($_.Exception.Message)"
|
||||
} finally {
|
||||
$script:WalFlushing = $false
|
||||
}
|
||||
}
|
||||
|
||||
function Write-CollectorHealth {
|
||||
param([string]$Status = 'running')
|
||||
if ([string]::IsNullOrWhiteSpace($script:HealthPath)) { return }
|
||||
try {
|
||||
$walDepth = 0
|
||||
if ($script:WalPath -and (Test-Path -LiteralPath $script:WalPath)) { $walDepth = @((Get-Content -LiteralPath $script:WalPath)).Count }
|
||||
$health = @{
|
||||
collector = 'file-operations'; hostname = $script:Hostname; sessionId = $script:SessionId;
|
||||
status = $Status; apiBase = $script:ApiBase; walDepth = $walDepth; ts = (Get-Date).ToUniversalTime().ToString('o')
|
||||
} | ConvertTo-Json -Depth 5
|
||||
Set-Content -LiteralPath $script:HealthPath -Value $health -Encoding UTF8
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function Invoke-AwJsonPost {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string]$Uri,
|
||||
[Parameter(Mandatory = $true)][string]$Json,
|
||||
[int]$MaxAttempts = 5,
|
||||
[int]$InitialBackoffMs = 500
|
||||
)
|
||||
$attempt = 1
|
||||
$backoff = [Math]::Max(100, $InitialBackoffMs)
|
||||
while ($attempt -le $MaxAttempts) {
|
||||
$httpClient = $null
|
||||
try {
|
||||
$httpClient = New-Object System.Net.Http.HttpClient
|
||||
$content = New-Object System.Net.Http.StringContent($Json, [System.Text.Encoding]::UTF8, "application/json")
|
||||
$response = $httpClient.PostAsync($Uri, $content).Result
|
||||
if ($response.IsSuccessStatusCode) { return $true }
|
||||
if ($attempt -ge $MaxAttempts) {
|
||||
if (-not $script:WalFlushing) { Add-WalEntry -Uri $Uri -Json $Json }
|
||||
return $false
|
||||
}
|
||||
} catch {
|
||||
if ($attempt -ge $MaxAttempts) {
|
||||
if (-not $script:WalFlushing) { Add-WalEntry -Uri $Uri -Json $Json }
|
||||
return $false
|
||||
}
|
||||
} finally {
|
||||
if ($null -ne $httpClient) {
|
||||
$httpClient.Dispose()
|
||||
}
|
||||
if ($null -ne $httpClient) {
|
||||
$httpClient.Dispose()
|
||||
}
|
||||
Start-Sleep -Milliseconds $backoff
|
||||
$backoff = [Math]::Min($backoff * 2, 10000)
|
||||
$attempt++
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,15 +145,11 @@ function Send-FileOperationEvent {
|
||||
|
||||
$config = Get-DeploymentConfig -Path $ConfigPath
|
||||
if (-not $config) { throw "Configuration file not found: $ConfigPath" }
|
||||
$script:Hostname = if ($config.PSObject.Properties.Name -contains 'awHostname' -and -not [string]::IsNullOrWhiteSpace([string]$config.awHostname)) { [string]$config.awHostname } else { [string]$env:COMPUTERNAME }
|
||||
|
||||
$scheme = if ($ServerScheme) { $ServerScheme } elseif ($config.server.scheme) { $config.server.scheme } else { 'http' }
|
||||
$hostName = if ($ServerHost) { $ServerHost } elseif ($config.server.host) { $config.server.host } else { 'localhost' }
|
||||
$port = if ($ServerPort) { $ServerPort } elseif ($config.server.port) { $config.server.port } else { 5600 }
|
||||
$script:ApiBase = "{0}://{1}:{2}/api/0" -f $scheme, $hostName, $port
|
||||
$stateRoot = if ($config.paths -and $config.paths.stateRoot) { [string]$config.paths.stateRoot } else { 'C:\ProgramData\AWatch-rus' }
|
||||
$script:WalPath = Join-Path $stateRoot 'wal-file-operations.ndjson'
|
||||
$script:HealthPath = Join-Path $stateRoot 'health-file-operations.json'
|
||||
|
||||
$bucketId = 'aw-file-operations_' + $script:Hostname
|
||||
Ensure-Bucket -BucketId $bucketId -ClientName 'aw-file-operations' -BucketType 'aw.file.operation'
|
||||
@@ -273,14 +206,11 @@ Write-FileCollectorLog "Collector started. Waiting for events..."
|
||||
|
||||
try {
|
||||
while ($true) {
|
||||
Flush-Wal
|
||||
Write-CollectorHealth -Status 'running'
|
||||
Start-Sleep -Seconds $PollSeconds
|
||||
}
|
||||
}
|
||||
finally {
|
||||
Write-FileCollectorLog "Stopping collector..."
|
||||
Write-CollectorHealth -Status 'stopped'
|
||||
foreach ($sub in @($subscriptions)) {
|
||||
try {
|
||||
if ($sub -and $sub.Id) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[CmdletBinding()]
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$ConfigPath = 'C:\ProgramData\AWatch-rus\deployment-config.json',
|
||||
[string]$ServerHost,
|
||||
@@ -21,7 +21,6 @@ param(
|
||||
[bool]$IncidentScreenshotEnabled,
|
||||
[string]$IncidentArtifactsRoot,
|
||||
[bool]$LogonMarkerEnabled,
|
||||
[string]$AwHostname,
|
||||
[string]$CustomRulesPath,
|
||||
[string]$CustomPolicyPath,
|
||||
[switch]$RepairPackage,
|
||||
@@ -74,7 +73,6 @@ $effectiveIncidentCaptureEnabled = if ($PSBoundParameters.ContainsKey('IncidentC
|
||||
$effectiveIncidentScreenshotEnabled = if ($PSBoundParameters.ContainsKey('IncidentScreenshotEnabled')) { [bool]$IncidentScreenshotEnabled } elseif ($existingConfig -and $existingConfig.PSObject.Properties.Name -contains 'incidentCapture' -and $existingConfig.incidentCapture.PSObject.Properties.Name -contains 'screenshotEnabled') { [bool]$existingConfig.incidentCapture.screenshotEnabled } else { $true }
|
||||
$effectiveIncidentArtifactsRoot = if ($PSBoundParameters.ContainsKey('IncidentArtifactsRoot') -and $IncidentArtifactsRoot) { $IncidentArtifactsRoot } elseif ($existingConfig -and $existingConfig.PSObject.Properties.Name -contains 'incidentCapture' -and $existingConfig.incidentCapture.PSObject.Properties.Name -contains 'artifactsRoot') { [string]$existingConfig.incidentCapture.artifactsRoot } else { Join-Path $effectiveStateRoot 'incident-artifacts' }
|
||||
$effectiveLogonMarkerEnabled = if ($PSBoundParameters.ContainsKey('LogonMarkerEnabled')) { [bool]$LogonMarkerEnabled } elseif ($existingConfig -and $existingConfig.PSObject.Properties.Name -contains 'sessionEvents' -and $existingConfig.sessionEvents.PSObject.Properties.Name -contains 'logonEnabled') { [bool]$existingConfig.sessionEvents.logonEnabled } else { $true }
|
||||
$effectiveAwHostname = if ($PSBoundParameters.ContainsKey('AwHostname') -and -not [string]::IsNullOrWhiteSpace($AwHostname)) { [string]$AwHostname } elseif ($existingConfig -and $existingConfig.PSObject.Properties.Name -contains 'awHostname' -and -not [string]::IsNullOrWhiteSpace([string]$existingConfig.awHostname)) { [string]$existingConfig.awHostname } else { [string]$env:COMPUTERNAME }
|
||||
$effectiveVersion = if ($Version) { $Version } elseif ($existingConfig) { [string]$existingConfig.package.version } else { 'v0.13.2' }
|
||||
|
||||
$effectiveUsers = if ($Users -or $UserListPath) {
|
||||
@@ -141,7 +139,6 @@ $config = New-ActivityWatchDeploymentConfig `
|
||||
-IncidentScreenshotEnabled $effectiveIncidentScreenshotEnabled `
|
||||
-IncidentArtifactsRoot $effectiveIncidentArtifactsRoot `
|
||||
-LogonMarkerEnabled $effectiveLogonMarkerEnabled `
|
||||
-AwHostname $effectiveAwHostname `
|
||||
-LaunchScriptPath $effectiveLaunchScript `
|
||||
-RecoveryScriptPath $effectiveRecoveryScript `
|
||||
-UserTasks $taskDefinitions `
|
||||
@@ -154,6 +151,6 @@ Register-ActivityWatchUserTasks -TaskDefinitions $taskDefinitions -LaunchScriptP
|
||||
Register-ActivityWatchRecoveryTask -TaskName $config.recovery.taskName -RecoveryScriptPath $effectiveRecoveryScript -ConfigPath $effectiveConfigPath
|
||||
Start-ActivityWatchTasks -TaskDefinitions $taskDefinitions -RecoveryTaskName $config.recovery.taskName
|
||||
|
||||
Write-Output 'Укрепление и восстановление ActivityWatch завершены.'
|
||||
Write-Output "Конфигурация: $effectiveConfigPath"
|
||||
Write-Output "Пользователи восстановлены: $($effectiveUsers -join ', ')"
|
||||
Write-Host 'Укрепление и восстановление ActivityWatch завершены.'
|
||||
Write-Host "Конфигурация: $effectiveConfigPath"
|
||||
Write-Host "Пользователи восстановлены: $($effectiveUsers -join ', ')"
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$ServerHost,
|
||||
[int]$ServerPort = 5600,
|
||||
[ValidateSet('http', 'https')]
|
||||
[string]$ServerScheme = 'http',
|
||||
[string]$StateRoot = 'C:\ProgramData\AWatch-rus',
|
||||
[string]$InstallRoot = 'C:\Program Files\AWatch-rus\bin',
|
||||
[string]$ServiceName = 'AWatchRusStandaloneAgent',
|
||||
[string]$AwHostname
|
||||
)
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
function Assert-Admin {
|
||||
$id = [Security.Principal.WindowsIdentity]::GetCurrent()
|
||||
$p = [Security.Principal.WindowsPrincipal]::new($id)
|
||||
if (-not $p.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
|
||||
throw 'Run as Administrator.'
|
||||
}
|
||||
}
|
||||
|
||||
function Ensure-Dir {
|
||||
param([string]$Path)
|
||||
if (-not (Test-Path -LiteralPath $Path)) {
|
||||
New-Item -Path $Path -ItemType Directory -Force | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
Assert-Admin
|
||||
|
||||
$logsRoot = Join-Path $StateRoot 'logs'
|
||||
Ensure-Dir -Path $StateRoot
|
||||
Ensure-Dir -Path $logsRoot
|
||||
|
||||
$collectorScript = Join-Path $StateRoot 'browser-domains-native-collector.ps1'
|
||||
$endpointCollectorScript = Join-Path $StateRoot 'dlp-endpoint-signals-collector.ps1'
|
||||
$fileCollectorScript = Join-Path $StateRoot 'file-operations-collector.ps1'
|
||||
$emailCollectorScript = Join-Path $StateRoot 'email-outbound-collector.ps1'
|
||||
$sessionCollectorScript = Join-Path $StateRoot 'worktime-session-collector.ps1'
|
||||
$rulesPath = Join-Path $StateRoot 'web-category-rules.json'
|
||||
$policyPath = Join-Path $StateRoot 'dlp-policy.json'
|
||||
$configPath = Join-Path $StateRoot 'deployment-config.json'
|
||||
$serviceScriptPath = Join-Path $PSScriptRoot 'aw-standalone-service.ps1'
|
||||
|
||||
Copy-Item -LiteralPath (Join-Path $PSScriptRoot 'browser-domains-native-collector.ps1') -Destination $collectorScript -Force
|
||||
Copy-Item -LiteralPath (Join-Path $PSScriptRoot 'dlp-endpoint-signals-collector.ps1') -Destination $endpointCollectorScript -Force
|
||||
Copy-Item -LiteralPath (Join-Path $PSScriptRoot 'file-operations-collector.ps1') -Destination $fileCollectorScript -Force
|
||||
if (Test-Path -LiteralPath (Join-Path $PSScriptRoot 'email-outbound-collector.ps1')) {
|
||||
Copy-Item -LiteralPath (Join-Path $PSScriptRoot 'email-outbound-collector.ps1') -Destination $emailCollectorScript -Force
|
||||
}
|
||||
if (Test-Path -LiteralPath (Join-Path $PSScriptRoot 'worktime-session-collector.ps1')) {
|
||||
Copy-Item -LiteralPath (Join-Path $PSScriptRoot 'worktime-session-collector.ps1') -Destination $sessionCollectorScript -Force
|
||||
}
|
||||
|
||||
if (-not (Test-Path -LiteralPath $rulesPath)) {
|
||||
Copy-Item -LiteralPath (Join-Path $PSScriptRoot 'web-category-rules.example.json') -Destination $rulesPath -Force
|
||||
}
|
||||
if (-not (Test-Path -LiteralPath $policyPath)) {
|
||||
Copy-Item -LiteralPath (Join-Path $PSScriptRoot 'dlp-policy.example.json') -Destination $policyPath -Force
|
||||
}
|
||||
|
||||
$effectiveHostname = if ([string]::IsNullOrWhiteSpace($AwHostname)) { [string]$env:COMPUTERNAME } else { [string]$AwHostname }
|
||||
|
||||
$config = [pscustomobject]@{
|
||||
version = 1
|
||||
generatedAtUtc = (Get-Date).ToUniversalTime().ToString('o')
|
||||
awHostname = $effectiveHostname
|
||||
server = [pscustomobject]@{
|
||||
host = $ServerHost
|
||||
port = $ServerPort
|
||||
scheme = $ServerScheme
|
||||
}
|
||||
paths = [pscustomobject]@{
|
||||
installRoot = $InstallRoot
|
||||
stateRoot = $StateRoot
|
||||
logsRoot = $logsRoot
|
||||
collectorScript = $collectorScript
|
||||
endpointCollectorScript = $endpointCollectorScript
|
||||
fileCollectorScript = $fileCollectorScript
|
||||
emailCollectorScript = $emailCollectorScript
|
||||
sessionCollectorScript = $sessionCollectorScript
|
||||
rulesPath = $rulesPath
|
||||
policyPath = $policyPath
|
||||
}
|
||||
collector = [pscustomobject]@{
|
||||
pollSeconds = 5
|
||||
pulseSeconds = 30
|
||||
}
|
||||
collectors = [pscustomobject]@{
|
||||
afkEnabled = $false
|
||||
windowEnabled = $false
|
||||
fileOpsEnabled = $true
|
||||
emailEnabled = $true
|
||||
}
|
||||
logging = [pscustomobject]@{
|
||||
localAgentLogsEnabled = $true
|
||||
}
|
||||
}
|
||||
|
||||
$config | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $configPath -Encoding UTF8
|
||||
|
||||
$existing = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
|
||||
if ($existing) {
|
||||
sc.exe stop $ServiceName | Out-Null
|
||||
Start-Sleep -Seconds 1
|
||||
sc.exe delete $ServiceName | Out-Null
|
||||
Start-Sleep -Seconds 1
|
||||
}
|
||||
|
||||
$binPath = "`"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe`" -NoProfile -ExecutionPolicy Bypass -File `"$serviceScriptPath`" -ConfigPath `"$configPath`""
|
||||
sc.exe create $ServiceName binPath= "$binPath" start= auto DisplayName= "AWatch-rus Standalone Agent" | Out-Null
|
||||
sc.exe description $ServiceName "Standalone AWatch-rus DLP agent service wrapper" | Out-Null
|
||||
sc.exe failure $ServiceName reset= 60 actions= restart/5000/restart/5000/restart/5000 | Out-Null
|
||||
sc.exe start $ServiceName | Out-Null
|
||||
|
||||
Write-Output "Standalone service installed: $ServiceName"
|
||||
Write-Output "Config: $configPath"
|
||||
Write-Output ("Host: {0} -> {1}://{2}:{3}" -f $effectiveHostname, $ServerScheme, $ServerHost, $ServerPort)
|
||||
@@ -34,8 +34,6 @@ Name: "validate"; Description: "Запустить validate-deployment (чере
|
||||
[Files]
|
||||
Source: "..\..\ActivityWatch.Windows.Common.psd1"; DestDir: "{app}\windows"; Flags: ignoreversion
|
||||
Source: "..\..\ActivityWatch.Windows.Common.psm1"; DestDir: "{app}\windows"; Flags: ignoreversion
|
||||
Source: "..\..\install-standalone-service.ps1"; DestDir: "{app}\windows"; Flags: ignoreversion
|
||||
Source: "..\..\aw-standalone-service.ps1"; DestDir: "{app}\windows"; Flags: ignoreversion
|
||||
Source: "..\..\deploy-single-user.ps1"; DestDir: "{app}\windows"; Flags: ignoreversion
|
||||
Source: "..\..\deploy-domain-users.ps1"; DestDir: "{app}\windows"; Flags: ignoreversion
|
||||
Source: "..\..\deploy-ensemble.ps1"; DestDir: "{app}\windows"; Flags: ignoreversion
|
||||
@@ -45,7 +43,6 @@ Source: "..\..\migrate-awatch-rus-paths.ps1"; DestDir: "{app}\windows"; Flags: i
|
||||
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
|
||||
@@ -54,11 +51,95 @@ Source: "payload\{#AwDefaultZipName}"; DestDir: "{app}\payload"; Flags: ignoreve
|
||||
Source: "innosetup-rdp-package-filelist.md"; DestDir: "{app}\windows\installkit\innosetup"; Flags: ignoreversion
|
||||
|
||||
[Run]
|
||||
Filename: "powershell.exe"; Parameters: "{code:GetStandaloneInstallParams}"; Flags: runhidden; Tasks: deploy
|
||||
Filename: "powershell.exe"; Parameters: "{code:GetDeployEnsembleParams}"; Flags: runhidden; Tasks: deploy
|
||||
|
||||
[Code]
|
||||
var
|
||||
ServerHostPage: TInputQueryWizardPage;
|
||||
UsersPage: TInputQueryWizardPage;
|
||||
OptionsPage: TInputOptionWizardPage;
|
||||
|
||||
function NormalizeUserCsv(const UserCsv: string): string;
|
||||
var
|
||||
i: Integer;
|
||||
s: string;
|
||||
token: string;
|
||||
begin
|
||||
Result := '';
|
||||
s := UserCsv;
|
||||
while True do
|
||||
begin
|
||||
i := Pos(',', s);
|
||||
if i = 0 then
|
||||
begin
|
||||
token := Trim(s);
|
||||
s := '';
|
||||
end
|
||||
else
|
||||
begin
|
||||
token := Trim(Copy(s, 1, i - 1));
|
||||
Delete(s, 1, i);
|
||||
end;
|
||||
|
||||
if token <> '' then
|
||||
begin
|
||||
if Result <> '' then
|
||||
Result := Result + ',';
|
||||
Result := Result + token;
|
||||
end;
|
||||
|
||||
if s = '' then
|
||||
Break;
|
||||
end;
|
||||
end;
|
||||
|
||||
function BuildUsersPowerShellArg(const UserCsv: string): string;
|
||||
var
|
||||
i: Integer;
|
||||
s: string;
|
||||
token: string;
|
||||
quoted: string;
|
||||
begin
|
||||
Result := '';
|
||||
s := UserCsv;
|
||||
while True do
|
||||
begin
|
||||
i := Pos(',', s);
|
||||
if i = 0 then
|
||||
begin
|
||||
token := Trim(s);
|
||||
s := '';
|
||||
end
|
||||
else
|
||||
begin
|
||||
token := Trim(Copy(s, 1, i - 1));
|
||||
Delete(s, 1, i);
|
||||
end;
|
||||
|
||||
if token <> '' then
|
||||
begin
|
||||
quoted := '"' + token + '"';
|
||||
if Result <> '' then
|
||||
Result := Result + ',';
|
||||
Result := Result + quoted;
|
||||
end;
|
||||
|
||||
if s = '' then
|
||||
Break;
|
||||
end;
|
||||
if Result <> '' then
|
||||
Result := '-Users ' + Result;
|
||||
end;
|
||||
|
||||
function PayloadZipPath: string;
|
||||
begin
|
||||
Result := ExpandConstant('{app}\payload\{#AwDefaultZipName}');
|
||||
end;
|
||||
|
||||
function HasPayloadZip: Boolean;
|
||||
begin
|
||||
Result := FileExists(ExpandConstant('{src}\payload\{#AwDefaultZipName}'));
|
||||
end;
|
||||
|
||||
procedure InitializeWizard;
|
||||
begin
|
||||
@@ -74,24 +155,62 @@ begin
|
||||
ServerHostPage.Add('ServerPort', False);
|
||||
ServerHostPage.Values[0] := '{#AwDefaultServerHost}';
|
||||
ServerHostPage.Values[1] := '{#AwDefaultServerPort}';
|
||||
|
||||
UsersPage := CreateInputQueryPage(
|
||||
ServerHostPage.ID,
|
||||
'Пользователи (RDP)',
|
||||
'Перечень пользователей, для которых разворачиваем агенты.',
|
||||
'Введите список через запятую. Пример: user1,user2,user3'
|
||||
);
|
||||
UsersPage.Add('Users (CSV)', False);
|
||||
UsersPage.Values[0] := '{#AwDefaultUsers}';
|
||||
|
||||
OptionsPage := CreateInputOptionPage(
|
||||
UsersPage.ID,
|
||||
'Опции деплоя',
|
||||
'Выберите опции для установки/валидации.',
|
||||
'',
|
||||
False,
|
||||
False
|
||||
);
|
||||
OptionsPage.Add('Использовать offline payload (встроенный ZIP)');
|
||||
OptionsPage.Add('Запустить validate-deployment после деплоя');
|
||||
OptionsPage.Values[0] := HasPayloadZip;
|
||||
OptionsPage.Values[1] := True;
|
||||
end;
|
||||
|
||||
function GetStandaloneInstallParams(Param: string): string;
|
||||
function GetDeployEnsembleParams(Param: string): string;
|
||||
var
|
||||
serverHost: string;
|
||||
serverPort: string;
|
||||
usersCsv: string;
|
||||
usersArg: string;
|
||||
zipArg: string;
|
||||
validateArg: string;
|
||||
begin
|
||||
serverHost := Trim(ServerHostPage.Values[0]);
|
||||
serverPort := Trim(ServerHostPage.Values[1]);
|
||||
if serverHost = '' then
|
||||
RaiseException('ServerHost is empty.');
|
||||
if serverPort = '' then
|
||||
RaiseException('ServerPort is empty.');
|
||||
usersCsv := NormalizeUserCsv(UsersPage.Values[0]);
|
||||
|
||||
usersArg := BuildUsersPowerShellArg(usersCsv);
|
||||
if usersArg = '' then
|
||||
RaiseException('Users list is empty.');
|
||||
|
||||
zipArg := '';
|
||||
if OptionsPage.Values[0] then
|
||||
zipArg := ' -PackageZipPath "' + PayloadZipPath + '"';
|
||||
|
||||
validateArg := '';
|
||||
if OptionsPage.Values[1] and WizardIsTaskSelected('validate') then
|
||||
validateArg := ' -ValidateAfterDeploy';
|
||||
|
||||
Result :=
|
||||
'-NoProfile -ExecutionPolicy Bypass -File "' + ExpandConstant('{app}\windows\install-standalone-service.ps1') + '"' +
|
||||
'-NoProfile -ExecutionPolicy Bypass -File "' + ExpandConstant('{app}\windows\deploy-ensemble.ps1') + '"' +
|
||||
' -ServerHost "' + serverHost + '"' +
|
||||
' -ServerPort ' + serverPort +
|
||||
' ' + usersArg +
|
||||
zipArg +
|
||||
' -InstallRoot "{#AwDefaultInstallRoot}"' +
|
||||
' -StateRoot "{#AwDefaultStateRoot}"';
|
||||
' -StateRoot "{#AwDefaultStateRoot}"' +
|
||||
validateArg;
|
||||
end;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[CmdletBinding(SupportsShouldProcess = $true)]
|
||||
[CmdletBinding(SupportsShouldProcess = $true)]
|
||||
param(
|
||||
[string]$OldInstallRoot = 'C:\Program Files\ActivityWatch-Phase2',
|
||||
[string]$OldStateRoot = 'C:\ProgramData\ActivityWatch-Phase2',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$ConfigPath = 'C:\ProgramData\ActivityWatch\deployment-config.json'
|
||||
[string]$ConfigPath = 'C:\ProgramData\AWatch-rus\deployment-config.json'
|
||||
)
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
@@ -13,26 +13,70 @@ $config = Read-ActivityWatchDeploymentConfig -Path $ConfigPath
|
||||
$installRoot = [string]$config.paths.installRoot
|
||||
$stateRoot = [string]$config.paths.stateRoot
|
||||
$collectorScript = [string]$config.paths.collectorScript
|
||||
$endpointCollectorScript = if ($config.paths.PSObject.Properties.Name -contains 'endpointCollectorScript') { [string]$config.paths.endpointCollectorScript } else { Join-Path $stateRoot 'dlp-endpoint-signals-collector.ps1' }
|
||||
$fileCollectorScript = if ($config.paths.PSObject.Properties.Name -contains 'fileCollectorScript') { [string]$config.paths.fileCollectorScript } else { Join-Path $stateRoot 'file-operations-collector.ps1' }
|
||||
$sessionCollectorScript = if ($config.paths.PSObject.Properties.Name -contains 'sessionCollectorScript') { [string]$config.paths.sessionCollectorScript } else { Join-Path $stateRoot 'worktime-session-collector.ps1' }
|
||||
$rulesPath = [string]$config.paths.rulesPath
|
||||
$policyPath = if ($config.paths.PSObject.Properties.Name -contains 'policyPath') { [string]$config.paths.policyPath } else { Join-Path $stateRoot 'dlp-policy.json' }
|
||||
$launchScript = [string]$config.paths.launchScript
|
||||
$recoveryScript = [string]$config.paths.recoveryScript
|
||||
|
||||
$afkExpected = if ($config.PSObject.Properties.Name -contains 'collectors' -and $config.collectors.PSObject.Properties.Name -contains 'afkEnabled') { [bool]$config.collectors.afkEnabled } else { $true }
|
||||
$windowExpected = if ($config.PSObject.Properties.Name -contains 'collectors' -and $config.collectors.PSObject.Properties.Name -contains 'windowEnabled') { [bool]$config.collectors.windowEnabled } else { $true }
|
||||
$fileOpsExpected = if ($config.PSObject.Properties.Name -contains 'collectors' -and $config.collectors.PSObject.Properties.Name -contains 'fileOpsEnabled') { [bool]$config.collectors.fileOpsEnabled } else { $true }
|
||||
$printServiceOperationalEnabled = $false
|
||||
try {
|
||||
$printServiceLog = Get-WinEvent -ListLog 'Microsoft-Windows-PrintService/Operational' -ErrorAction Stop
|
||||
$printServiceOperationalEnabled = [bool]$printServiceLog.IsEnabled
|
||||
}
|
||||
catch {
|
||||
}
|
||||
$printJobTitlePolicyEnabled = $false
|
||||
try {
|
||||
$printPolicy = Get-ItemProperty -LiteralPath 'HKLM:\Software\Policies\Microsoft\Windows NT\Printers' -Name 'ShowJobTitleInEventLogs' -ErrorAction Stop
|
||||
$printJobTitlePolicyEnabled = ([int]$printPolicy.ShowJobTitleInEventLogs -eq 1)
|
||||
}
|
||||
catch {
|
||||
}
|
||||
$requiredFiles = @(
|
||||
(Join-Path $installRoot 'aw-watcher-afk\aw-watcher-afk.exe'),
|
||||
(Join-Path $installRoot 'aw-watcher-window\aw-watcher-window.exe'),
|
||||
$collectorScript,
|
||||
$endpointCollectorScript,
|
||||
$sessionCollectorScript,
|
||||
$rulesPath,
|
||||
$policyPath,
|
||||
$launchScript,
|
||||
$recoveryScript,
|
||||
$ConfigPath
|
||||
)
|
||||
if ($fileOpsExpected) {
|
||||
$requiredFiles += $fileCollectorScript
|
||||
}
|
||||
if ($afkExpected) {
|
||||
$requiredFiles += (Join-Path $installRoot 'aw-watcher-afk\aw-watcher-afk.exe')
|
||||
}
|
||||
if ($windowExpected) {
|
||||
$requiredFiles += (Join-Path $installRoot 'aw-watcher-window\aw-watcher-window.exe')
|
||||
}
|
||||
|
||||
$missingFiles = @(
|
||||
$requiredFiles | Where-Object { -not (Test-Path -LiteralPath $_) }
|
||||
)
|
||||
|
||||
$processNames = @('aw-watcher-afk', 'aw-watcher-window')
|
||||
$runningProcesses = Get-Process -Name $processNames -ErrorAction SilentlyContinue | Select-Object Name, Id, SessionId
|
||||
$processNames = @()
|
||||
if ($afkExpected) { $processNames += 'aw-watcher-afk' }
|
||||
if ($windowExpected) { $processNames += 'aw-watcher-window' }
|
||||
$runningProcesses = @()
|
||||
if ($processNames.Count -gt 0) {
|
||||
$runningProcesses = Get-Process -Name $processNames -ErrorAction SilentlyContinue | Select-Object Name, Id, SessionId
|
||||
}
|
||||
$sessionCollectorProcesses = @(
|
||||
Get-CimInstance Win32_Process -ErrorAction SilentlyContinue |
|
||||
Where-Object {
|
||||
($_.Name -ieq 'powershell.exe' -or $_.Name -ieq 'pwsh.exe') -and
|
||||
$_.CommandLine -match [Regex]::Escape($sessionCollectorScript)
|
||||
} |
|
||||
Select-Object Name, ProcessId, SessionId, CommandLine
|
||||
)
|
||||
|
||||
$taskNames = @()
|
||||
if ($config.userTasks) {
|
||||
@@ -41,25 +85,28 @@ if ($config.userTasks) {
|
||||
$taskNames += [string]$config.recovery.taskName
|
||||
$taskNames = $taskNames | Sort-Object -Unique
|
||||
|
||||
$tasks = foreach ($taskName in $taskNames) {
|
||||
$task = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
|
||||
if ($task) {
|
||||
[pscustomobject]@{
|
||||
taskName = $task.TaskName
|
||||
state = [string]$task.State
|
||||
present = $true
|
||||
$tasks = @(
|
||||
foreach ($taskName in $taskNames) {
|
||||
$task = Get-ScheduledTask -ErrorAction SilentlyContinue | Where-Object { $_.TaskName -eq $taskName } | Select-Object -First 1
|
||||
if ($task) {
|
||||
[pscustomobject]@{
|
||||
taskName = $task.TaskName
|
||||
state = [string]$task.State
|
||||
present = $true
|
||||
}
|
||||
}
|
||||
else {
|
||||
[pscustomobject]@{
|
||||
taskName = $taskName
|
||||
state = 'Отсутствует'
|
||||
present = $false
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
[pscustomobject]@{
|
||||
taskName = $taskName
|
||||
state = 'Missing'
|
||||
present = $false
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
$serverUrl = '{0}://{1}:{2}' -f [string]$config.server.scheme, [string]$config.server.host, [int]$config.server.port
|
||||
$uniqueRunningProcessNames = @($runningProcesses | Select-Object -ExpandProperty Name -Unique)
|
||||
$result = [ordered]@{
|
||||
generatedAtUtc = (Get-Date).ToUniversalTime().ToString('o')
|
||||
configPath = $ConfigPath
|
||||
@@ -76,11 +123,24 @@ $result = [ordered]@{
|
||||
ok = [bool]($tasks.Count -gt 0 -and -not ($tasks | Where-Object { -not $_.present }))
|
||||
}
|
||||
processes = [ordered]@{
|
||||
expected = $processNames
|
||||
list = @($runningProcesses)
|
||||
ok = [bool](($runningProcesses | Select-Object -ExpandProperty Name -Unique).Count -ge 2)
|
||||
sessionCollectors = @($sessionCollectorProcesses)
|
||||
ok = [bool](
|
||||
(
|
||||
($processNames.Count -eq 0) -or
|
||||
($uniqueRunningProcessNames.Count -ge $processNames.Count)
|
||||
) -and
|
||||
($sessionCollectorProcesses.Count -ge 1)
|
||||
)
|
||||
}
|
||||
printTelemetry = [ordered]@{
|
||||
operationalLogEnabled = $printServiceOperationalEnabled
|
||||
jobTitlePolicyEnabled = $printJobTitlePolicyEnabled
|
||||
ok = [bool]($printServiceOperationalEnabled -and $printJobTitlePolicyEnabled)
|
||||
}
|
||||
}
|
||||
|
||||
$result.overallOk = [bool]($result.files.ok -and $result.tasks.ok -and $result.processes.ok)
|
||||
$result.overallOk = [bool]($result.files.ok -and $result.tasks.ok -and $result.processes.ok -and $result.printTelemetry.ok)
|
||||
|
||||
$result
|
||||
|
||||
@@ -1,268 +1,4 @@
|
||||
param(
|
||||
[string]$ConfigPath = 'C:\ProgramData\AWatch-rus\deployment-config.json',
|
||||
[string]$Hostname,
|
||||
[int]$PollSeconds = 30
|
||||
)
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
function Get-Config {
|
||||
param([string]$Path)
|
||||
|
||||
if (-not (Test-Path -LiteralPath $Path)) {
|
||||
throw "Конфигурация не найдена: $Path"
|
||||
}
|
||||
|
||||
Get-Content -LiteralPath $Path -Raw | ConvertFrom-Json
|
||||
}
|
||||
|
||||
function Invoke-AwJsonPost {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string]$Uri,
|
||||
[Parameter(Mandatory = $true)][string]$Json
|
||||
)
|
||||
|
||||
$bytes = [Text.Encoding]::UTF8.GetBytes($Json)
|
||||
Invoke-RestMethod -Method Post -Uri $Uri -ContentType 'application/json; charset=utf-8' -Body $bytes | Out-Null
|
||||
}
|
||||
|
||||
function Ensure-Bucket {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string]$ApiBase,
|
||||
[Parameter(Mandatory = $true)][string]$BucketId,
|
||||
[Parameter(Mandatory = $true)][string]$HostnameValue
|
||||
)
|
||||
|
||||
try {
|
||||
Invoke-RestMethod -Method Get -Uri "$ApiBase/buckets/$BucketId" | Out-Null
|
||||
return
|
||||
}
|
||||
catch { Write-Error param(
|
||||
[string]$ConfigPath = 'C:\ProgramData\AWatch-rus\deployment-config.json',
|
||||
[string]$Hostname,
|
||||
[int]$PollSeconds = 30
|
||||
)
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
function Get-Config {
|
||||
param([string]$Path)
|
||||
|
||||
if (-not (Test-Path -LiteralPath $Path)) {
|
||||
throw "Конфигурация не найдена: $Path"
|
||||
}
|
||||
|
||||
Get-Content -LiteralPath $Path -Raw | ConvertFrom-Json
|
||||
}
|
||||
|
||||
function Invoke-AwJsonPost {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string]$Uri,
|
||||
[Parameter(Mandatory = $true)][string]$Json
|
||||
)
|
||||
|
||||
$bytes = [Text.Encoding]::UTF8.GetBytes($Json)
|
||||
Invoke-RestMethod -Method Post -Uri $Uri -ContentType 'application/json; charset=utf-8' -Body $bytes | Out-Null
|
||||
}
|
||||
|
||||
function Ensure-Bucket {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string]$ApiBase,
|
||||
[Parameter(Mandatory = $true)][string]$BucketId,
|
||||
[Parameter(Mandatory = $true)][string]$HostnameValue
|
||||
)
|
||||
|
||||
try {
|
||||
Invoke-RestMethod -Method Get -Uri "$ApiBase/buckets/$BucketId" | Out-Null
|
||||
return
|
||||
}
|
||||
catch {
|
||||
}
|
||||
|
||||
$body = @{
|
||||
client = 'aw-worktime-session-collector'
|
||||
type = 'aw.worktime.session'
|
||||
hostname = $HostnameValue
|
||||
} | ConvertTo-Json -Compress
|
||||
|
||||
try {
|
||||
Invoke-AwJsonPost -Uri "$ApiBase/buckets/$BucketId" -Json $body
|
||||
}
|
||||
catch {
|
||||
Invoke-RestMethod -Method Get -Uri "$ApiBase/buckets/$BucketId" | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
function Get-SessionRecords {
|
||||
$records = @()
|
||||
|
||||
try {
|
||||
$lines = quser 2>$null
|
||||
if (-not $lines) {
|
||||
return @()
|
||||
}
|
||||
|
||||
foreach ($line in ($lines | Select-Object -Skip 1)) {
|
||||
$clean = ($line -replace '^\s*>?', '').Trim()
|
||||
if (-not $clean) {
|
||||
continue
|
||||
}
|
||||
|
||||
$parts = $clean -split '\s+'
|
||||
if ($parts.Count -lt 4) {
|
||||
continue
|
||||
}
|
||||
|
||||
$sessionName = ''
|
||||
$sessionIdIndex = 2
|
||||
if ($parts[1] -match '^\d+$') {
|
||||
$sessionIdIndex = 1
|
||||
}
|
||||
else {
|
||||
$sessionName = $parts[1]
|
||||
}
|
||||
|
||||
$sessionId = 0
|
||||
if ($parts[$sessionIdIndex] -match '^\d+$') {
|
||||
$sessionId = [int]$parts[$sessionIdIndex]
|
||||
}
|
||||
|
||||
$records += [pscustomobject]@{
|
||||
username = $parts[0]
|
||||
sessionName = $sessionName
|
||||
sessionId = $sessionId
|
||||
state = $parts[$sessionIdIndex + 1]
|
||||
}
|
||||
}
|
||||
}
|
||||
catch {
|
||||
}
|
||||
|
||||
return $records
|
||||
}
|
||||
|
||||
function Test-SessionIsActive {
|
||||
param([AllowNull()][string]$State)
|
||||
if ([string]::IsNullOrWhiteSpace($State)) { return $false }
|
||||
$s = $State.Trim().ToLowerInvariant()
|
||||
return ($s -eq 'active') -or ($s -like 'актив*')
|
||||
}
|
||||
|
||||
$cfg = Get-Config -Path $ConfigPath
|
||||
$hostValue = if ($Hostname) { $Hostname } elseif ($cfg.PSObject.Properties.Name -contains 'awHostname' -and -not [string]::IsNullOrWhiteSpace([string]$cfg.awHostname)) { [string]$cfg.awHostname } else { [string]$env:COMPUTERNAME }
|
||||
$apiBase = '{0}://{1}:{2}/api/0' -f [string]$cfg.server.scheme, [string]$cfg.server.host, [string]$cfg.server.port
|
||||
$bucketId = 'aw-worktime-sessions_' + $hostValue
|
||||
$pulse = 120
|
||||
$sleepSec = if ($PollSeconds -gt 0) {
|
||||
$PollSeconds
|
||||
}
|
||||
elseif ($cfg.collector -and $cfg.collector.pollSeconds) {
|
||||
[int]$cfg.collector.pollSeconds
|
||||
}
|
||||
else {
|
||||
30
|
||||
}
|
||||
|
||||
Ensure-Bucket -ApiBase $apiBase -BucketId $bucketId -HostnameValue $hostValue
|
||||
|
||||
while ($true) {
|
||||
$now = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fffZ')
|
||||
$records = Get-SessionRecords
|
||||
if (-not $records -or $records.Count -eq 0) {
|
||||
$records = @([pscustomobject]@{
|
||||
username = $env:USERNAME
|
||||
sessionName = ''
|
||||
sessionId = (Get-Process -Id $PID).SessionId
|
||||
state = 'Unknown'
|
||||
})
|
||||
}
|
||||
|
||||
foreach ($rec in $records) {
|
||||
$payload = @{
|
||||
timestamp = $now
|
||||
duration = 0
|
||||
data = @{
|
||||
username = [string]$rec.username
|
||||
userId = "$($env:USERDOMAIN)\$($rec.username)"
|
||||
sessionId = [int]$rec.sessionId
|
||||
sessionName = [string]$rec.sessionName
|
||||
state = [string]$rec.state
|
||||
active = (Test-SessionIsActive -State ([string]$rec.state))
|
||||
hostname = $hostValue
|
||||
source = 'worktime-session-collector'
|
||||
}
|
||||
} | ConvertTo-Json -Depth 6 -Compress
|
||||
|
||||
try {
|
||||
Invoke-AwJsonPost -Uri "$apiBase/buckets/$bucketId/heartbeat?pulsetime=$pulse" -Json $payload
|
||||
}
|
||||
catch {
|
||||
}
|
||||
}
|
||||
|
||||
Start-Sleep -Seconds $sleepSec
|
||||
}
|
||||
; }
|
||||
|
||||
$body = @{
|
||||
client = 'aw-worktime-session-collector'
|
||||
type = 'aw.worktime.session'
|
||||
hostname = $HostnameValue
|
||||
} | ConvertTo-Json -Compress
|
||||
|
||||
try {
|
||||
Invoke-AwJsonPost -Uri "$ApiBase/buckets/$BucketId" -Json $body
|
||||
}
|
||||
catch {
|
||||
Invoke-RestMethod -Method Get -Uri "$ApiBase/buckets/$BucketId" | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
function Get-SessionRecords {
|
||||
$records = @()
|
||||
|
||||
try {
|
||||
$lines = quser 2>$null
|
||||
if (-not $lines) {
|
||||
return @()
|
||||
}
|
||||
|
||||
foreach ($line in ($lines | Select-Object -Skip 1)) {
|
||||
$clean = ($line -replace '^\s*>?', '').Trim()
|
||||
if (-not $clean) {
|
||||
continue
|
||||
}
|
||||
|
||||
$parts = $clean -split '\s+'
|
||||
if ($parts.Count -lt 4) {
|
||||
continue
|
||||
}
|
||||
|
||||
$sessionName = ''
|
||||
$sessionIdIndex = 2
|
||||
if ($parts[1] -match '^\d+$') {
|
||||
$sessionIdIndex = 1
|
||||
}
|
||||
else {
|
||||
$sessionName = $parts[1]
|
||||
}
|
||||
|
||||
$sessionId = 0
|
||||
if ($parts[$sessionIdIndex] -match '^\d+$') {
|
||||
$sessionId = [int]$parts[$sessionIdIndex]
|
||||
}
|
||||
|
||||
$records += [pscustomobject]@{
|
||||
username = $parts[0]
|
||||
sessionName = $sessionName
|
||||
sessionId = $sessionId
|
||||
state = $parts[$sessionIdIndex + 1]
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { Write-Error param(
|
||||
param(
|
||||
[string]$ConfigPath = 'C:\ProgramData\AWatch-rus\deployment-config.json',
|
||||
[string]$Hostname,
|
||||
[int]$PollSeconds = 30
|
||||
@@ -428,234 +164,3 @@ while ($true) {
|
||||
|
||||
Start-Sleep -Seconds $sleepSec
|
||||
}
|
||||
; }
|
||||
|
||||
return $records
|
||||
}
|
||||
|
||||
function Test-SessionIsActive {
|
||||
param([AllowNull()][string]$State)
|
||||
if ([string]::IsNullOrWhiteSpace($State)) { return $false }
|
||||
$s = $State.Trim().ToLowerInvariant()
|
||||
return ($s -eq 'active') -or ($s -like 'актив*')
|
||||
}
|
||||
|
||||
$cfg = Get-Config -Path $ConfigPath
|
||||
$hostValue = if ($Hostname) { $Hostname } else { [string]$env:COMPUTERNAME }
|
||||
$apiBase = '{0}://{1}:{2}/api/0' -f [string]$cfg.server.scheme, [string]$cfg.server.host, [string]$cfg.server.port
|
||||
$bucketId = 'aw-worktime-sessions_' + $hostValue
|
||||
$pulse = 120
|
||||
$sleepSec = if ($PollSeconds -gt 0) {
|
||||
$PollSeconds
|
||||
}
|
||||
elseif ($cfg.collector -and $cfg.collector.pollSeconds) {
|
||||
[int]$cfg.collector.pollSeconds
|
||||
}
|
||||
else {
|
||||
30
|
||||
}
|
||||
|
||||
Ensure-Bucket -ApiBase $apiBase -BucketId $bucketId -HostnameValue $hostValue
|
||||
|
||||
while ($true) {
|
||||
$now = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fffZ')
|
||||
$records = Get-SessionRecords
|
||||
if (-not $records -or $records.Count -eq 0) {
|
||||
$records = @([pscustomobject]@{
|
||||
username = $env:USERNAME
|
||||
sessionName = ''
|
||||
sessionId = (Get-Process -Id $PID).SessionId
|
||||
state = 'Unknown'
|
||||
})
|
||||
}
|
||||
|
||||
foreach ($rec in $records) {
|
||||
$payload = @{
|
||||
timestamp = $now
|
||||
duration = 0
|
||||
data = @{
|
||||
username = [string]$rec.username
|
||||
userId = "$($env:USERDOMAIN)\$($rec.username)"
|
||||
sessionId = [int]$rec.sessionId
|
||||
sessionName = [string]$rec.sessionName
|
||||
state = [string]$rec.state
|
||||
active = (Test-SessionIsActive -State ([string]$rec.state))
|
||||
hostname = $hostValue
|
||||
source = 'worktime-session-collector'
|
||||
}
|
||||
} | ConvertTo-Json -Depth 6 -Compress
|
||||
|
||||
try {
|
||||
Invoke-AwJsonPost -Uri "$apiBase/buckets/$bucketId/heartbeat?pulsetime=$pulse" -Json $payload
|
||||
}
|
||||
catch { Write-Error param(
|
||||
[string]$ConfigPath = 'C:\ProgramData\AWatch-rus\deployment-config.json',
|
||||
[string]$Hostname,
|
||||
[int]$PollSeconds = 30
|
||||
)
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
function Get-Config {
|
||||
param([string]$Path)
|
||||
|
||||
if (-not (Test-Path -LiteralPath $Path)) {
|
||||
throw "Конфигурация не найдена: $Path"
|
||||
}
|
||||
|
||||
Get-Content -LiteralPath $Path -Raw | ConvertFrom-Json
|
||||
}
|
||||
|
||||
function Invoke-AwJsonPost {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string]$Uri,
|
||||
[Parameter(Mandatory = $true)][string]$Json
|
||||
)
|
||||
|
||||
$bytes = [Text.Encoding]::UTF8.GetBytes($Json)
|
||||
Invoke-RestMethod -Method Post -Uri $Uri -ContentType 'application/json; charset=utf-8' -Body $bytes | Out-Null
|
||||
}
|
||||
|
||||
function Ensure-Bucket {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string]$ApiBase,
|
||||
[Parameter(Mandatory = $true)][string]$BucketId,
|
||||
[Parameter(Mandatory = $true)][string]$HostnameValue
|
||||
)
|
||||
|
||||
try {
|
||||
Invoke-RestMethod -Method Get -Uri "$ApiBase/buckets/$BucketId" | Out-Null
|
||||
return
|
||||
}
|
||||
catch {
|
||||
}
|
||||
|
||||
$body = @{
|
||||
client = 'aw-worktime-session-collector'
|
||||
type = 'aw.worktime.session'
|
||||
hostname = $HostnameValue
|
||||
} | ConvertTo-Json -Compress
|
||||
|
||||
try {
|
||||
Invoke-AwJsonPost -Uri "$ApiBase/buckets/$BucketId" -Json $body
|
||||
}
|
||||
catch {
|
||||
Invoke-RestMethod -Method Get -Uri "$ApiBase/buckets/$BucketId" | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
function Get-SessionRecords {
|
||||
$records = @()
|
||||
|
||||
try {
|
||||
$lines = quser 2>$null
|
||||
if (-not $lines) {
|
||||
return @()
|
||||
}
|
||||
|
||||
foreach ($line in ($lines | Select-Object -Skip 1)) {
|
||||
$clean = ($line -replace '^\s*>?', '').Trim()
|
||||
if (-not $clean) {
|
||||
continue
|
||||
}
|
||||
|
||||
$parts = $clean -split '\s+'
|
||||
if ($parts.Count -lt 4) {
|
||||
continue
|
||||
}
|
||||
|
||||
$sessionName = ''
|
||||
$sessionIdIndex = 2
|
||||
if ($parts[1] -match '^\d+$') {
|
||||
$sessionIdIndex = 1
|
||||
}
|
||||
else {
|
||||
$sessionName = $parts[1]
|
||||
}
|
||||
|
||||
$sessionId = 0
|
||||
if ($parts[$sessionIdIndex] -match '^\d+$') {
|
||||
$sessionId = [int]$parts[$sessionIdIndex]
|
||||
}
|
||||
|
||||
$records += [pscustomobject]@{
|
||||
username = $parts[0]
|
||||
sessionName = $sessionName
|
||||
sessionId = $sessionId
|
||||
state = $parts[$sessionIdIndex + 1]
|
||||
}
|
||||
}
|
||||
}
|
||||
catch {
|
||||
}
|
||||
|
||||
return $records
|
||||
}
|
||||
|
||||
function Test-SessionIsActive {
|
||||
param([AllowNull()][string]$State)
|
||||
if ([string]::IsNullOrWhiteSpace($State)) { return $false }
|
||||
$s = $State.Trim().ToLowerInvariant()
|
||||
return ($s -eq 'active') -or ($s -like 'актив*')
|
||||
}
|
||||
|
||||
$cfg = Get-Config -Path $ConfigPath
|
||||
$hostValue = if ($Hostname) { $Hostname } else { [string]$env:COMPUTERNAME }
|
||||
$apiBase = '{0}://{1}:{2}/api/0' -f [string]$cfg.server.scheme, [string]$cfg.server.host, [string]$cfg.server.port
|
||||
$bucketId = 'aw-worktime-sessions_' + $hostValue
|
||||
$pulse = 120
|
||||
$sleepSec = if ($PollSeconds -gt 0) {
|
||||
$PollSeconds
|
||||
}
|
||||
elseif ($cfg.collector -and $cfg.collector.pollSeconds) {
|
||||
[int]$cfg.collector.pollSeconds
|
||||
}
|
||||
else {
|
||||
30
|
||||
}
|
||||
|
||||
Ensure-Bucket -ApiBase $apiBase -BucketId $bucketId -HostnameValue $hostValue
|
||||
|
||||
while ($true) {
|
||||
$now = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fffZ')
|
||||
$records = Get-SessionRecords
|
||||
if (-not $records -or $records.Count -eq 0) {
|
||||
$records = @([pscustomobject]@{
|
||||
username = $env:USERNAME
|
||||
sessionName = ''
|
||||
sessionId = (Get-Process -Id $PID).SessionId
|
||||
state = 'Unknown'
|
||||
})
|
||||
}
|
||||
|
||||
foreach ($rec in $records) {
|
||||
$payload = @{
|
||||
timestamp = $now
|
||||
duration = 0
|
||||
data = @{
|
||||
username = [string]$rec.username
|
||||
userId = "$($env:USERDOMAIN)\$($rec.username)"
|
||||
sessionId = [int]$rec.sessionId
|
||||
sessionName = [string]$rec.sessionName
|
||||
state = [string]$rec.state
|
||||
active = (Test-SessionIsActive -State ([string]$rec.state))
|
||||
hostname = $hostValue
|
||||
source = 'worktime-session-collector'
|
||||
}
|
||||
} | ConvertTo-Json -Depth 6 -Compress
|
||||
|
||||
try {
|
||||
Invoke-AwJsonPost -Uri "$apiBase/buckets/$bucketId/heartbeat?pulsetime=$pulse" -Json $payload
|
||||
}
|
||||
catch {
|
||||
}
|
||||
}
|
||||
|
||||
Start-Sleep -Seconds $sleepSec
|
||||
}
|
||||
; }
|
||||
}
|
||||
|
||||
Start-Sleep -Seconds $sleepSec
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user