Use UTF-8 BOM for logs; document FAR encoding
This commit is contained in:
+62
-10
@@ -101,12 +101,39 @@ $LogDir = Split-Path $LogFile -Parent
|
|||||||
if (!(Test-Path $LogDir)) { New-Item -ItemType Directory -Path $LogDir -Force | Out-Null }
|
if (!(Test-Path $LogDir)) { New-Item -ItemType Directory -Path $LogDir -Force | Out-Null }
|
||||||
if (!(Test-Path $LogBackupFolder)) { New-Item -ItemType Directory -Path $LogBackupFolder -Force | Out-Null }
|
if (!(Test-Path $LogBackupFolder)) { New-Item -ItemType Directory -Path $LogBackupFolder -Force | Out-Null }
|
||||||
|
|
||||||
|
# UTF-8 с BOM: иначе часть просмотрщиков (FAR и др.) открывает лог как ANSI/OEM и показывает "кракозябры".
|
||||||
|
$script:Utf8BomEncoding = New-Object System.Text.UTF8Encoding $true
|
||||||
|
|
||||||
|
function Ensure-FileStartsWithUtf8Bom {
|
||||||
|
param([Parameter(Mandatory = $true)][string]$Path)
|
||||||
|
if (-not (Test-Path -LiteralPath $Path)) { return }
|
||||||
|
$bytes = [System.IO.File]::ReadAllBytes($Path)
|
||||||
|
if ($bytes.Length -ge 3 -and $bytes[0] -eq 0xEF -and $bytes[1] -eq 0xBB -and $bytes[2] -eq 0xBF) { return }
|
||||||
|
$bom = [byte[]](0xEF, 0xBB, 0xBF)
|
||||||
|
$combined = New-Object byte[] ($bom.Length + $bytes.Length)
|
||||||
|
[Buffer]::BlockCopy($bom, 0, $combined, 0, $bom.Length)
|
||||||
|
if ($bytes.Length -gt 0) {
|
||||||
|
[Buffer]::BlockCopy($bytes, 0, $combined, $bom.Length, $bytes.Length)
|
||||||
|
}
|
||||||
|
[System.IO.File]::WriteAllBytes($Path, $combined)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Write-TextFileUtf8Bom {
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)][string]$Path,
|
||||||
|
[Parameter(Mandatory = $true)][string]$Text
|
||||||
|
)
|
||||||
|
[System.IO.File]::WriteAllText($Path, $Text, $script:Utf8BomEncoding)
|
||||||
|
}
|
||||||
|
|
||||||
|
Ensure-FileStartsWithUtf8Bom -Path $LogFile
|
||||||
|
|
||||||
function Write-Log {
|
function Write-Log {
|
||||||
param([string]$Message)
|
param([string]$Message)
|
||||||
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
|
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
|
||||||
$logMessage = "$timestamp - $Message"
|
$logMessage = "$timestamp - $Message" + [Environment]::NewLine
|
||||||
Add-Content -Path $LogFile -Value $logMessage -Force -Encoding UTF8
|
[System.IO.File]::AppendAllText($LogFile, $logMessage, $script:Utf8BomEncoding)
|
||||||
Write-Host $logMessage
|
Write-Host ($logMessage.TrimEnd("`r`n"))
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -350,7 +377,7 @@ function Send-Heartbeat {
|
|||||||
Send-TelegramMessage -Message $message | Out-Null
|
Send-TelegramMessage -Message $message | Out-Null
|
||||||
Write-Log "Отправлено уведомление о запуске скрипта"
|
Write-Log "Отправлено уведомление о запуске скрипта"
|
||||||
} else {
|
} else {
|
||||||
$timestamp | Out-File -FilePath $HeartbeatFile -Force -Encoding UTF8
|
Write-TextFileUtf8Bom -Path $HeartbeatFile -Text $timestamp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -376,6 +403,8 @@ function Rotate-LogFile {
|
|||||||
|
|
||||||
Copy-Item -Path $LogFile -Destination $backupFilePath -Force
|
Copy-Item -Path $LogFile -Destination $backupFilePath -Force
|
||||||
Clear-Content -Path $LogFile -Force
|
Clear-Content -Path $LogFile -Force
|
||||||
|
# После Clear-Content файл пустой без BOM — восстановим UTF-8 BOM для корректного просмотра в FAR/редакторах
|
||||||
|
Ensure-FileStartsWithUtf8Bom -Path $LogFile
|
||||||
Write-Log "Лог-файл скопирован в бэкап: $backupFilePath"
|
Write-Log "Лог-файл скопирован в бэкап: $backupFilePath"
|
||||||
|
|
||||||
$oldBackups = Get-ChildItem -Path $LogBackupFolder -Filter "LoginLog_*.bak" |
|
$oldBackups = Get-ChildItem -Path $LogBackupFolder -Filter "LoginLog_*.bak" |
|
||||||
@@ -414,7 +443,7 @@ function Check-AndRotateLog {
|
|||||||
elseif ($currentTime -ge $targetRotationTime) { $shouldRotate = $true }
|
elseif ($currentTime -ge $targetRotationTime) { $shouldRotate = $true }
|
||||||
|
|
||||||
if ($shouldRotate -and (Rotate-LogFile)) {
|
if ($shouldRotate -and (Rotate-LogFile)) {
|
||||||
$currentTime.ToString("yyyy-MM-dd HH:mm:ss") | Out-File -FilePath $lastRotationFile -Force -Encoding UTF8
|
Write-TextFileUtf8Bom -Path $lastRotationFile -Text ($currentTime.ToString("yyyy-MM-dd HH:mm:ss"))
|
||||||
}
|
}
|
||||||
return $targetRotationTime
|
return $targetRotationTime
|
||||||
}
|
}
|
||||||
@@ -687,15 +716,38 @@ function Format-RDGatewayEvent {
|
|||||||
|
|
||||||
function Send-DailyReport {
|
function Send-DailyReport {
|
||||||
try {
|
try {
|
||||||
$quserOutput = & quser 2>$null
|
$quserOutput = @(& quser 2>$null)
|
||||||
$count = 0
|
$usernames = [System.Collections.Generic.List[string]]::new()
|
||||||
if ($quserOutput -and $quserOutput.Count -gt 1) { $count = $quserOutput.Count - 1 }
|
if ($quserOutput -and $quserOutput.Count -gt 1) {
|
||||||
|
$sessionLines = @($quserOutput | Select-Object -Skip 1)
|
||||||
|
foreach ($raw in $sessionLines) {
|
||||||
|
$line = ($raw -replace '\s+', ' ').Trim()
|
||||||
|
if ([string]::IsNullOrWhiteSpace($line)) { continue }
|
||||||
|
$parts = $line -split ' ', 2
|
||||||
|
if ($parts.Count -lt 1) { continue }
|
||||||
|
$u = $parts[0].Trim()
|
||||||
|
if (-not [string]::IsNullOrWhiteSpace($u) -and $u -ne 'USERNAME') {
|
||||||
|
$usernames.Add($u) | Out-Null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$count = $usernames.Count
|
||||||
|
$uniqueUsers = $usernames | Sort-Object -Unique
|
||||||
$message = "<b>📊 ЕЖЕДНЕВНЫЙ ОТЧЕТ</b>`r`n"
|
$message = "<b>📊 ЕЖЕДНЕВНЫЙ ОТЧЕТ</b>`r`n"
|
||||||
$message += "🖥️ Сервер: $env:COMPUTERNAME`r`n"
|
$message += "🖥️ Сервер: $env:COMPUTERNAME`r`n"
|
||||||
$message += "🕐 Время отчета: $(Get-Date -Format 'dd.MM.yyyy HH:mm:ss')`r`n"
|
$message += "🕐 Время отчета: $(Get-Date -Format 'dd.MM.yyyy HH:mm:ss')`r`n"
|
||||||
$message += "👥 Активных сессий (quser): $count"
|
$message += "👥 Активных сессий (quser): $count`r`n"
|
||||||
|
if ($uniqueUsers.Count -gt 0) {
|
||||||
|
$message += "`r`n<b>Уникальные логины ($($uniqueUsers.Count)):</b>`r`n"
|
||||||
|
foreach ($name in $uniqueUsers) {
|
||||||
|
$safe = [System.Net.WebUtility]::HtmlEncode($name)
|
||||||
|
$message += " • $safe`r`n"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$message += "`r`n<i>Список пользователей недоступен (quser пуст или недостаточно прав).</i>"
|
||||||
|
}
|
||||||
Send-TelegramMessage -Message $message | Out-Null
|
Send-TelegramMessage -Message $message | Out-Null
|
||||||
(Get-Date).ToString("yyyy-MM-dd HH:mm:ss") | Out-File -FilePath $LastReportFile -Force -Encoding UTF8
|
Write-TextFileUtf8Bom -Path $LastReportFile -Text ((Get-Date).ToString("yyyy-MM-dd HH:mm:ss"))
|
||||||
Write-Log "Ежедневный отчет отправлен"
|
Write-Log "Ежедневный отчет отправлен"
|
||||||
return $true
|
return $true
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ PowerShell-набор для мониторинга входов в Windows с
|
|||||||
## Что изменилось (важное)
|
## Что изменилось (важное)
|
||||||
|
|
||||||
- **Кодировка `.ps1`**: в репозитории добавлены `.editorconfig` и `.gitattributes`, чтобы `*.ps1` по умолчанию сохранялись как **UTF-8 with BOM** и с **CRLF** (это сильно снижает “кракозябры” и ошибки парсинга PowerShell).
|
- **Кодировка `.ps1`**: в репозитории добавлены `.editorconfig` и `.gitattributes`, чтобы `*.ps1` по умолчанию сохранялись как **UTF-8 with BOM** и с **CRLF** (это сильно снижает “кракозябры” и ошибки парсинга PowerShell).
|
||||||
|
- **Кодировка логов**: `login_monitor.log` / `watchdog.log` пишутся как **UTF-8 с BOM** (и при необходимости BOM добавляется к уже существующему файлу), чтобы в **FAR/старых просмотрщиках** не было ситуации “в консоли нормально, а в файле РЈРІРµ…” из‑за неверной авто-кодировки.
|
||||||
- **Фильтрация шума PMG/LDAP**: добавлено узкое исключение для `4624` с `LogonType=3`, если `IpAddress=192.168.160.57` и `LogonProcessName` содержит `Advapi` (частые периодические сетевые логоны).
|
- **Фильтрация шума PMG/LDAP**: добавлено узкое исключение для `4624` с `LogonType=3`, если `IpAddress=192.168.160.57` и `LogonProcessName` содержит `Advapi` (частые периодические сетевые логоны).
|
||||||
- **`auditpol` на русской Windows**: настройка/проверка аудита опирается на категорию **`Вход/выход`** и подкатегории **`Вход в систему` / `Выход из системы`** (ожидается строка **`Успех и сбой`**). Это устраняет ошибки вида `0x00000057` из‑за несуществующего на RU ОС имени `Logon`.
|
- **`auditpol` на русской Windows**: настройка/проверка аудита опирается на категорию **`Вход/выход`** и подкатегории **`Вход в систему` / `Выход из системы`** (ожидается строка **`Успех и сбой`**). Это устраняет ошибки вида `0x00000057` из‑за несуществующего на RU ОС имени `Logon`.
|
||||||
- **Стабильность**: `auditpol` запускается через `cmd.exe` с перехватом `stdout+stderr`, чтобы не ломать выполнение при `$ErrorActionPreference = "Stop"`.
|
- **Стабильность**: `auditpol` запускается через `cmd.exe` с перехватом `stdout+stderr`, чтобы не ломать выполнение при `$ErrorActionPreference = "Stop"`.
|
||||||
|
|||||||
@@ -18,15 +18,36 @@ param(
|
|||||||
Set-StrictMode -Version Latest
|
Set-StrictMode -Version Latest
|
||||||
$ErrorActionPreference = "Stop"
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
$script:Utf8BomEncoding = New-Object System.Text.UTF8Encoding $true
|
||||||
|
$script:WatchdogLogBomChecked = $false
|
||||||
|
|
||||||
|
function Ensure-FileStartsWithUtf8Bom {
|
||||||
|
param([Parameter(Mandatory = $true)][string]$Path)
|
||||||
|
if (-not (Test-Path -LiteralPath $Path)) { return }
|
||||||
|
$bytes = [System.IO.File]::ReadAllBytes($Path)
|
||||||
|
if ($bytes.Length -ge 3 -and $bytes[0] -eq 0xEF -and $bytes[1] -eq 0xBB -and $bytes[2] -eq 0xBF) { return }
|
||||||
|
$bom = [byte[]](0xEF, 0xBB, 0xBF)
|
||||||
|
$combined = New-Object byte[] ($bom.Length + $bytes.Length)
|
||||||
|
[Buffer]::BlockCopy($bom, 0, $combined, 0, $bom.Length)
|
||||||
|
if ($bytes.Length -gt 0) {
|
||||||
|
[Buffer]::BlockCopy($bytes, 0, $combined, $bom.Length, $bytes.Length)
|
||||||
|
}
|
||||||
|
[System.IO.File]::WriteAllBytes($Path, $combined)
|
||||||
|
}
|
||||||
|
|
||||||
function Write-WatchdogLog {
|
function Write-WatchdogLog {
|
||||||
param([string]$Message)
|
param([string]$Message)
|
||||||
$ts = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
|
$ts = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
|
||||||
$line = "$ts - $Message"
|
$line = "$ts - $Message" + [Environment]::NewLine
|
||||||
$dir = Split-Path -Parent $WatchdogLog
|
$dir = Split-Path -Parent $WatchdogLog
|
||||||
if ($dir -and -not (Test-Path $dir)) {
|
if ($dir -and -not (Test-Path $dir)) {
|
||||||
New-Item -ItemType Directory -Path $dir -Force | Out-Null
|
New-Item -ItemType Directory -Path $dir -Force | Out-Null
|
||||||
}
|
}
|
||||||
Add-Content -Path $WatchdogLog -Value $line -Encoding UTF8
|
if (-not $script:WatchdogLogBomChecked) {
|
||||||
|
Ensure-FileStartsWithUtf8Bom -Path $WatchdogLog
|
||||||
|
$script:WatchdogLogBomChecked = $true
|
||||||
|
}
|
||||||
|
[System.IO.File]::AppendAllText($WatchdogLog, $line, $script:Utf8BomEncoding)
|
||||||
}
|
}
|
||||||
|
|
||||||
function Get-MainScriptProcesses {
|
function Get-MainScriptProcesses {
|
||||||
|
|||||||
Reference in New Issue
Block a user