Fix scheduling checks and harden Telegram message formatting.

Correct daily rotation/report trigger logic to run after the configured slot once per period, avoid duplicate stop notifications on fatal errors, and HTML-escape dynamic Telegram fields to prevent markup breakage.
This commit is contained in:
2026-04-27 10:01:42 +10:00
parent 4f0b6e219c
commit 3d03d905a1
+75 -30
View File
@@ -136,6 +136,12 @@ function Write-Log {
Write-Host ($logMessage.TrimEnd("`r`n"))
}
function ConvertTo-TelegramHtml {
param([string]$Text)
if ($null -eq $Text) { return '' }
return [System.Net.WebUtility]::HtmlEncode([string]$Text)
}
try {
[System.Net.ServicePointManager]::SecurityProtocol =
[System.Net.ServicePointManager]::SecurityProtocol -bor [System.Net.SecurityProtocolType]::Tls12
@@ -395,10 +401,11 @@ function Send-Heartbeat {
param([switch]$IsStartup = $false)
$timestamp = Get-Date -Format "dd.MM.yyyy HH:mm:ss"
$hHost = (ConvertTo-TelegramHtml $env:COMPUTERNAME)
if ($IsStartup) {
$message = "<b>✅ Мониторинг логинов ЗАПУЩЕН</b>`r`n"
$message += "🖥️ Сервер: $env:COMPUTERNAME`r`n"
$message += "🖥️ Сервер: $hHost`r`n"
$message += "🕐 Время запуска: $timestamp"
if (Test-RDSDeploymentPresent) {
$message += "`r`n🔐 <b>RDS (хост сессий):</b> обнаружены компоненты RDS помимо чистого шлюза — в мониторинг входят входы по RDP/RDS на этом узле (Security 4624/4625, типы входа по настройке скрипта)."
@@ -422,10 +429,12 @@ function Send-StopNotification {
param([string]$Reason)
$timestamp = Get-Date -Format "dd.MM.yyyy HH:mm:ss"
$hHost = (ConvertTo-TelegramHtml $env:COMPUTERNAME)
$hReason = (ConvertTo-TelegramHtml $Reason)
$message = "<b>⚠️ МОНИТОРИНГ ЛОГИНОВ ОСТАНОВЛЕН</b>`r`n"
$message += "🖥️ Сервер: $env:COMPUTERNAME`r`n"
$message += "🖥️ Сервер: $hHost`r`n"
$message += "🕐 Время остановки: $timestamp`r`n"
$message += "📋 Причина: $Reason"
$message += "📋 Причина: $hReason"
Send-TelegramMessage -Message $message | Out-Null
Write-Log "Уведомление об остановке отправлено: $Reason"
@@ -459,6 +468,24 @@ function Rotate-LogFile {
return $false
}
function Get-NextLocalSlotBoundary {
param(
[int]$Hour,
[int]$Minute
)
$now = Get-Date
$slotToday = Get-Date -Year $now.Year -Month $now.Month -Day $now.Day -Hour $Hour -Minute $Minute -Second 0
if ($now -lt $slotToday) { return $slotToday }
return $slotToday.AddDays(1)
}
function Get-MostRecentRotationSlot {
$now = Get-Date
$slotToday = Get-Date -Year $now.Year -Month $now.Month -Day $now.Day -Hour $LogRotationHour -Minute $LogRotationMinute -Second 0
if ($now -ge $slotToday) { return $slotToday }
return $slotToday.AddDays(-1)
}
function Check-AndRotateLog {
$lastRotationFile = Join-Path $LogBackupFolder "last_rotation.txt"
$lastRotation = $null
@@ -471,18 +498,15 @@ function Check-AndRotateLog {
}
$currentTime = Get-Date
$targetRotationTime = Get-Date -Year $currentTime.Year -Month $currentTime.Month -Day $currentTime.Day `
-Hour $LogRotationHour -Minute $LogRotationMinute -Second 0
if ($currentTime -ge $targetRotationTime) { $targetRotationTime = $targetRotationTime.AddDays(1) }
$mostRecentSlot = Get-MostRecentRotationSlot
$shouldRotate = $false
if ($lastRotation -eq $null) { $shouldRotate = $true }
elseif ($currentTime -ge $targetRotationTime) { $shouldRotate = $true }
if ($null -eq $lastRotation) { $shouldRotate = $true }
elseif ($lastRotation -lt $mostRecentSlot) { $shouldRotate = $true }
if ($shouldRotate -and (Rotate-LogFile)) {
Write-TextFileUtf8Bom -Path $lastRotationFile -Text ($currentTime.ToString("yyyy-MM-dd HH:mm:ss"))
}
return $targetRotationTime
return (Get-NextLocalSlotBoundary -Hour $LogRotationHour -Minute $LogRotationMinute)
}
function Cleanup-OldLogs {
@@ -662,6 +686,13 @@ function Format-LoginEvent {
$logHost = $SecurityLogComputerName
if ([string]::IsNullOrWhiteSpace($logHost)) { $logHost = $env:COMPUTERNAME }
$hUser = (ConvertTo-TelegramHtml $Username)
$hLog = (ConvertTo-TelegramHtml $logHost)
$hWkst = (ConvertTo-TelegramHtml $ComputerName)
$hIp = (ConvertTo-TelegramHtml $SourceIP)
$hProc = (ConvertTo-TelegramHtml $ProcessName)
$hLtName = (ConvertTo-TelegramHtml $LogonTypeName)
$hTime = (ConvertTo-TelegramHtml ($TimeCreated.ToString('dd.MM.yyyy HH:mm:ss')))
$message = "<b>"
if ($EventID -eq 4624) { $message += "✅ УСПЕШНЫЙ ВХОД" }
@@ -669,13 +700,13 @@ function Format-LoginEvent {
else { $message += "⚠️ СОБЫТИЕ" }
$message += "</b>`r`n"
$message += "👤 Пользователь: $Username`r`n"
$message += "🏢 Сервер (журнал Security): $logHost`r`n"
$message += "🖥️ Рабочая станция (клиент из события): $ComputerName`r`n"
$message += "🌐 IP адрес: $SourceIP`r`n"
$message += "⚙️ Процесс/Код: $ProcessName`r`n"
$message += "🔑 Тип входа: $LogonTypeName ($LogonType)`r`n"
$message += "🕐 Время: $($TimeCreated.ToString('dd.MM.yyyy HH:mm:ss'))`r`n"
$message += "👤 Пользователь: $hUser`r`n"
$message += "🏢 Сервер (журнал Security): $hLog`r`n"
$message += "🖥️ Рабочая станция (клиент из события): $hWkst`r`n"
$message += "🌐 IP адрес: $hIp`r`n"
$message += "⚙️ Процесс/Код: $hProc`r`n"
$message += "🔑 Тип входа: $hLtName ($LogonType)`r`n"
$message += "🕐 Время: $hTime`r`n"
$message += "🔢 Event ID: $EventID"
return $message
@@ -738,20 +769,26 @@ function Format-RDGatewayEvent {
[datetime]$TimeCreated
)
$hUser = (ConvertTo-TelegramHtml $Username)
$hExt = (ConvertTo-TelegramHtml $ExternalIP)
$hInt = (ConvertTo-TelegramHtml $InternalIP)
$hProto = (ConvertTo-TelegramHtml $Protocol)
$hTime = (ConvertTo-TelegramHtml ($TimeCreated.ToString('dd.MM.yyyy HH:mm:ss')))
$message = "<b>"
if ($EventID -eq 302) { $message += "🖥️ УСПЕШНОЕ ПОДКЛЮЧЕНИЕ ЧЕРЕЗ RD GATEWAY" }
elseif ($EventID -eq 303) { $message += "❌ НЕУДАЧНОЕ ПОДКЛЮЧЕНИЕ ЧЕРЕЗ RD GATEWAY" }
else { $message += "⚠️ СОБЫТИЕ RD GATEWAY" }
$message += "</b>`r`n"
$message += "👤 Пользователь: $Username`r`n"
$message += "🌐 IP пользователя (внешний): $ExternalIP`r`n"
$message += "🖥️ IP внутренний: $InternalIP`r`n"
$message += "🔌 Протокол: $Protocol`r`n"
$message += "👤 Пользователь: $hUser`r`n"
$message += "🌐 IP пользователя (внешний): $hExt`r`n"
$message += "🖥️ IP внутренний: $hInt`r`n"
$message += "🔌 Протокол: $hProto`r`n"
if ($EventID -eq 303 -and $ErrorCode -ne "0" -and $ErrorCode -ne "N/A") {
$message += "⚠️ Код ошибки: $ErrorCode`r`n"
$message += "⚠️ Код ошибки: $(ConvertTo-TelegramHtml $ErrorCode)`r`n"
}
$message += "🕐 Время: $($TimeCreated.ToString('dd.MM.yyyy HH:mm:ss'))`r`n"
$message += "🕐 Время: $hTime`r`n"
$message += "🔢 Event ID: $EventID"
return $message
}
@@ -777,7 +814,7 @@ function Send-DailyReport {
# PS 5.1: без @() один логин даёт скаляр String (нет .Count); пустой список даёт $null.
$uniqueUsers = @($usernames | Sort-Object -Unique)
$message = "<b>📊 ЕЖЕДНЕВНЫЙ ОТЧЕТ</b>`r`n"
$message += "🖥️ Сервер: $env:COMPUTERNAME`r`n"
$message += "🖥️ Сервер: $(ConvertTo-TelegramHtml $env:COMPUTERNAME)`r`n"
$message += "🕐 Время отчета: $(Get-Date -Format 'dd.MM.yyyy HH:mm:ss')`r`n"
$message += "👥 Активных сессий (quser): $count`r`n"
if ($uniqueUsers.Count -gt 0) {
@@ -807,15 +844,19 @@ function Check-AndSendDailyReport {
}
$now = Get-Date
$target = Get-Date -Year $now.Year -Month $now.Month -Day $now.Day -Hour $DailyReportHour -Minute $DailyReportMinute -Second 0
if ($now -ge $target) { $target = $target.AddDays(1) }
$reportSlotToday = Get-Date -Year $now.Year -Month $now.Month -Day $now.Day -Hour $DailyReportHour -Minute $DailyReportMinute -Second 0
$shouldSend = $false
if ($lastReport -eq $null) { $shouldSend = $true }
elseif ($now -ge $target) { $shouldSend = $true }
if ($now -ge $reportSlotToday) {
if ($null -eq $lastReport) {
$shouldSend = $true
} else {
$dLast = $lastReport.Date
if ($dLast -lt $now.Date) { $shouldSend = $true }
}
}
if ($shouldSend) { Send-DailyReport | Out-Null }
return $target
return (Get-NextLocalSlotBoundary -Hour $DailyReportHour -Minute $DailyReportMinute)
}
function Start-LoginMonitor {
@@ -944,14 +985,18 @@ function Start-LoginMonitor {
}
}
$script:StopNotificationSent = $false
try {
Test-TelegramConnection | Out-Null
Start-LoginMonitor -MonitorInterval 5 -MonitorInteractiveOnly
} catch {
Write-Log "Критическая ошибка: $($_.Exception.Message)"
Send-StopNotification -Reason "Критическая ошибка: $($_.Exception.Message)"
$script:StopNotificationSent = $true
throw
} finally {
if (-not $script:StopNotificationSent) {
Send-StopNotification -Reason "Скрипт завершил работу"
}
}