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