feat: AD lockout 4740 with IIS IPs and SMTP notifications (v1.5.2)

Monitor Security 4740 on a configured domain controller and enrich alerts
with ActiveSync client IPs from IIS logs. Add unified Telegram and email
delivery with NotifyOrder. Update README and DPAPI helper for mail password.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
PTah
2026-05-19 19:47:23 +10:00
parent 4015f9a7e4
commit 2c73526f7c
4 changed files with 389 additions and 15 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
# Запуск: от администратора на ТОМ ЖЕ компьютере, где будет Login_Monitor.ps1.
# Результат (Base64) вставьте в $TelegramBotTokenProtectedB64 / $TelegramChatIDProtectedB64.
# Результат (Base64) вставьте в $TelegramBotTokenProtectedB64 / $TelegramChatIDProtectedB64 / $MailSmtpPasswordProtectedB64.
param(
[Parameter(Mandatory = $true)][string]$PlainText
)
+380 -9
View File
@@ -3,6 +3,7 @@
Мониторинг логинов/попыток входа с уведомлениями в Telegram
.DESCRIPTION
Отслеживает события входа в систему (Security 4624/4625) и события RD Gateway (302/303),
на заданном КД — блокировки учётных записей (Security 4740) с IP из логов IIS ActiveSync,
отправляет уведомления в Telegram, делает ротацию логов, heartbeat в файл и ежедневный отчет.
.NOTES
Требуется: PowerShell 5.0+, запуск от администратора.
@@ -28,6 +29,7 @@ param(
[string]$TelegramChatID = '<TELEGRAM_CHAT_ID>',
[string]$TelegramBotTokenProtectedB64 = "",
[string]$TelegramChatIDProtectedB64 = "",
[string]$MailSmtpPasswordProtectedB64 = "",
[switch]$Watchdog,
[switch]$InstallTasks,
[switch]$SkipScheduledTaskMaintenance
@@ -69,7 +71,7 @@ $script:MonitorSingletonLockStream = $null
# строки ниже, если правки «мелкие» и вы не хотите менять отображаемую версию в логах).
# Рекомендация: при значимых релизах меняйте и $ScriptVersion, и version.txt одинаково; при только
# исправлениях на шаре — достаточно поднять patch в version.txt (например 1.3.0.1).
$ScriptVersion = "1.4.4"
$ScriptVersion = "1.5.2"
# Логи (все под InstallRoot)
$LogFile = Join-Path $script:InstallRoot "Logs\login_monitor.log"
@@ -148,6 +150,25 @@ $IgnoreAdvapiNetworkLogonSourceIps = @(
)
$IgnoreAdvapiNetworkLogonProcessContains = "Advapi"
# Блокировка учётной записи AD (Security 4740) + IP клиента из логов IIS ActiveSync.
# Мониторинг включается только если скрипт запущен на узле, имя которого совпадает с $LockoutMonitorDomainController.
$LockoutMonitorDomainController = ""
$NetBiosDomainName = ""
$ExchangeIisLogPath = ""
$ExchangeServerHostForIisExclude = ""
$ExchangeIisLogTailLines = 5000
# Очередь оповещений: telegram, email (или tg, mail). Пусто = авто: настроенные каналы, порядок telegram → email.
$NotifyOrder = ""
$MailSmtpHost = ""
$MailSmtpPort = 587
$MailSmtpUser = ""
$MailSmtpPassword = ""
$MailFrom = ""
$MailTo = ""
$MailSmtpStartTls = $true
$MailSmtpSsl = $false
# ============================================
# ИНИЦИАЛИЗАЦИЯ
# ============================================
@@ -529,6 +550,70 @@ if (-not [string]::IsNullOrWhiteSpace($TelegramChatIDProtectedB64)) {
if ($TelegramBotToken -eq '<TELEGRAM_BOT_TOKEN>') { $TelegramBotToken = "" }
if ($TelegramChatID -eq '<TELEGRAM_CHAT_ID>') { $TelegramChatID = "" }
if (-not [string]::IsNullOrWhiteSpace($MailSmtpPasswordProtectedB64)) {
try {
$MailSmtpPassword = Unprotect-RdpMonitorDpapiB64 -Base64 $MailSmtpPasswordProtectedB64
} catch {
Write-Host "Ошибка расшифровки MailSmtpPassword (DPAPI): $($_.Exception.Message)"
exit 1
}
}
function Test-NotifyTelegramConfigured {
return (-not [string]::IsNullOrWhiteSpace($TelegramBotToken)) -and
(-not [string]::IsNullOrWhiteSpace($TelegramChatID))
}
function Test-NotifyEmailConfigured {
return (-not [string]::IsNullOrWhiteSpace($MailSmtpHost)) -and
(-not [string]::IsNullOrWhiteSpace($MailFrom)) -and
(-not [string]::IsNullOrWhiteSpace($MailTo))
}
function Get-NotifyOrderChannels {
$configured = [System.Collections.Generic.List[string]]::new()
if (Test-NotifyTelegramConfigured) { $configured.Add('telegram') | Out-Null }
if (Test-NotifyEmailConfigured) { $configured.Add('email') | Out-Null }
if ([string]::IsNullOrWhiteSpace($NotifyOrder)) {
return @($configured)
}
$requested = [System.Collections.Generic.List[string]]::new()
foreach ($part in ($NotifyOrder -split '[,\s;]+')) {
$p = $part.Trim().ToLowerInvariant()
if ([string]::IsNullOrWhiteSpace($p)) { continue }
$channel = switch -Regex ($p) {
'^(tg|telegram)$' { 'telegram' }
'^(mail|email|e-mail)$' { 'email' }
default {
Write-Log "NotifyOrder: неизвестный канал '$part' (ожидается telegram/tg или email/mail)"
$null
}
}
if ($null -eq $channel) { continue }
if ($configured.Contains($channel) -and -not $requested.Contains($channel)) {
$requested.Add($channel) | Out-Null
}
}
return @($requested)
}
function Get-NotifyChainHuman {
$channels = @(Get-NotifyOrderChannels)
if ($channels.Count -eq 0) {
return 'нет (ни Telegram, ни SMTP не настроены)'
}
$labels = foreach ($ch in $channels) {
switch ($ch) {
'telegram' { 'Telegram' }
'email' { 'Email (SMTP)' }
default { $ch }
}
}
return ($labels -join ' → ')
}
function ConvertTo-TelegramHtml {
param([string]$Text)
if ($null -eq $Text) { return '' }
@@ -561,6 +646,10 @@ function Send-TelegramMessage {
}
function Test-TelegramConnection {
if (-not (Test-NotifyTelegramConfigured)) {
Write-Log "Telegram: канал не настроен, проверка пропущена."
return $false
}
Write-Log "Проверка подключения к Telegram API..."
try {
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12
@@ -577,6 +666,105 @@ function Test-TelegramConnection {
return $false
}
function ConvertTo-EmailHtmlBody {
param([string]$TelegramHtmlMessage)
$inner = [string]$TelegramHtmlMessage
if ([string]::IsNullOrEmpty($inner)) { $inner = '' }
$inner = $inner -replace "`r`n", "<br>`r`n"
return @"
<html>
<body style="font-family:Segoe UI,Arial,sans-serif;font-size:14px;line-height:1.4;">
$inner
</body>
</html>
"@
}
function Send-EmailNotification {
param(
[string]$Message,
[string]$Subject = "RDP Login Monitor"
)
if (-not (Test-NotifyEmailConfigured)) {
Write-Log "Email: SMTP не настроен (нужны MailSmtpHost, MailFrom, MailTo)"
return $false
}
try {
$toList = @($MailTo -split '[,;]' | ForEach-Object { $_.Trim() } | Where-Object { -not [string]::IsNullOrWhiteSpace($_) })
if ($toList.Count -eq 0) {
Write-Log "Email: MailTo пуст или некорректен"
return $false
}
$mailParams = @{
To = $toList
From = $MailFrom.Trim()
Subject = $Subject
Body = (ConvertTo-EmailHtmlBody -TelegramHtmlMessage $Message)
BodyAsHtml = $true
SmtpServer = $MailSmtpHost.Trim()
Port = [int]$MailSmtpPort
Encoding = [System.Text.Encoding]::UTF8
ErrorAction = 'Stop'
}
if ($MailSmtpSsl -or $MailSmtpStartTls) {
$mailParams['UseSsl'] = $true
}
if (-not [string]::IsNullOrWhiteSpace($MailSmtpUser)) {
$securePass = if ([string]::IsNullOrWhiteSpace($MailSmtpPassword)) {
New-Object System.Security.SecureString
} else {
ConvertTo-SecureString $MailSmtpPassword -AsPlainText -Force
}
$mailParams['Credential'] = New-Object System.Management.Automation.PSCredential($MailSmtpUser.Trim(), $securePass)
}
Send-MailMessage @mailParams
return $true
} catch {
Write-Log "Ошибка отправки Email: $($_.Exception.Message)"
return $false
}
}
function Test-MailSmtpConnection {
if (-not (Test-NotifyEmailConfigured)) {
Write-Log "Email: канал не настроен, проверка пропущена."
return $false
}
Write-Log "SMTP: $($MailSmtpHost):$MailSmtpPort (STARTTLS=$MailSmtpStartTls, SSL=$MailSmtpSsl), From=$MailFrom, To=$MailTo"
if (-not [string]::IsNullOrWhiteSpace($MailSmtpUser) -and [string]::IsNullOrWhiteSpace($MailSmtpPassword)) {
Write-Log "SMTP: задан MailSmtpUser, но пароль пуст (возможна ошибка при отправке)."
}
return $true
}
function Send-MonitorNotification {
param(
[string]$Message,
[string]$EmailSubject = "RDP Login Monitor"
)
$channels = @(Get-NotifyOrderChannels)
if ($channels.Count -eq 0) {
Write-Log "Оповещение не отправлено: нет настроенных каналов (Telegram и/или SMTP)"
return $false
}
$anyOk = $false
foreach ($ch in $channels) {
$ok = switch ($ch) {
'telegram' { Send-TelegramMessage -Message $Message }
'email' { Send-EmailNotification -Message $Message -Subject $EmailSubject }
default { $false }
}
if ($ok) { $anyOk = $true }
}
return $anyOk
}
function Test-Administrator {
$currentUser = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = New-Object Security.Principal.WindowsPrincipal($currentUser)
@@ -963,6 +1151,8 @@ function Send-Heartbeat {
} else {
$message += "`r`n🚫 <b>Игнорируются:</b> не задано (ignore.lst отсутствует или пуст)."
}
$notifyChain = Get-NotifyChainHuman
$message += "`r`n📢 <b>Каналы уведомлений:</b> $(ConvertTo-TelegramHtml $notifyChain)"
if (Test-RDSDeploymentPresent) {
$message += "`r`n🔐 <b>RDS (хост сессий):</b> обнаружены компоненты RDS помимо чистого шлюза — в мониторинг входят входы по RDP/RDS на этом узле (Security 4624/4625, типы входа по настройке скрипта)."
}
@@ -974,8 +1164,16 @@ function Send-Heartbeat {
}
} catch { }
}
Send-TelegramMessage -Message $message | Out-Null
Write-Log "Отправлено уведомление о запуске скрипта"
if (Test-Lockout4740MonitoringActive) {
$message += "`r`n🔒 <b>Блокировки AD:</b> на этом КД отслеживается Security <b>4740</b> (блокировка учётной записи)."
if (-not [string]::IsNullOrWhiteSpace($ExchangeIisLogPath)) {
$message += " При событии — IP из логов IIS ActiveSync (<code>ExchangeIisLogPath</code>)."
} else {
$message += " IP из IIS не заданы (<code>ExchangeIisLogPath</code> пуст)."
}
}
Send-MonitorNotification -Message $message -EmailSubject "RDP Login Monitor: запуск" | Out-Null
Write-Log "Отправлено уведомление о запуске скрипта (каналы: $notifyChain)"
} else {
Write-TextFileUtf8Bom -Path $HeartbeatFile -Text $timestamp
}
@@ -992,7 +1190,7 @@ function Send-StopNotification {
$message += "🕐 Время остановки: $timestamp`r`n"
$message += "📋 Причина: $hReason"
Send-TelegramMessage -Message $message | Out-Null
Send-MonitorNotification -Message $message -EmailSubject "RDP Login Monitor: остановка" | Out-Null
Write-Log "Уведомление об остановке отправлено: $Reason"
}
@@ -1602,7 +1800,7 @@ function Send-DailyReport {
} else {
$message += "`r`n<i>Список пользователей недоступен (quser пуст или недостаточно прав).</i>"
}
Send-TelegramMessage -Message $message | Out-Null
Send-MonitorNotification -Message $message -EmailSubject "RDP Login Monitor: ежедневный отчёт" | Out-Null
Write-TextFileUtf8Bom -Path $LastReportFile -Text ((Get-Date).ToString("yyyy-MM-dd HH:mm:ss"))
Write-Log "Ежедневный отчет отправлен"
return $true
@@ -1635,6 +1833,128 @@ function Check-AndSendDailyReport {
return (Get-NextLocalSlotBoundary -Hour $DailyReportHour -Minute $DailyReportMinute)
}
function Test-Lockout4740MonitoringActive {
if ([string]::IsNullOrWhiteSpace($LockoutMonitorDomainController)) { return $false }
$configured = ($LockoutMonitorDomainController -split '\.')[0].Trim()
$local = ($env:COMPUTERNAME -split '\.')[0].Trim()
return ($configured -ieq $local)
}
function Get-Lockout4740EventInfo {
param($Event)
$info = [pscustomobject]@{
TimeCreated = $Event.TimeCreated
Username = ""
Domain = ""
CallerComputer = ""
}
try {
$map = Get-EventDataMap -Event $Event
$info.Username = Get-FirstNonEmptyMapValue -DataMap $map -Keys @(
'TargetUserName', 'SamAccountName', 'AccountName'
)
$info.Domain = Get-FirstNonEmptyMapValue -DataMap $map -Keys @(
'TargetDomainName', 'TargetAccountDomain', 'AccountDomain'
)
$info.CallerComputer = Get-FirstNonEmptyMapValue -DataMap $map -Keys @(
'CallerComputerName', 'WorkstationName', 'Workstation'
)
} catch {
Write-Log "Ошибка разбора XML 4740: $($_.Exception.Message)"
}
if ([string]::IsNullOrWhiteSpace($info.Username) -and $Event.Properties.Count -ge 1) {
$info.Username = [string]$Event.Properties[0].Value
}
if ([string]::IsNullOrWhiteSpace($info.Domain) -and $Event.Properties.Count -ge 2) {
$info.Domain = [string]$Event.Properties[1].Value
}
if ([string]::IsNullOrWhiteSpace($info.CallerComputer) -and $Event.Properties.Count -ge 4) {
$info.CallerComputer = [string]$Event.Properties[3].Value
}
return $info
}
function Get-ExchangeActiveSyncIpsFromIisLog {
param(
[Parameter(Mandatory = $true)][string]$SamAccountName,
[string]$DomainNetBios = ""
)
if ([string]::IsNullOrWhiteSpace($ExchangeIisLogPath)) { return @() }
$logDir = $ExchangeIisLogPath.TrimEnd('\')
$logFile = Join-Path $logDir ("u_ex" + (Get-Date).ToUniversalTime().ToString("yyMMdd") + ".log")
if (-not (Test-Path -LiteralPath $logFile)) {
Write-Log "IIS: файл лога не найден: $logFile"
return @()
}
$domainPart = if ([string]::IsNullOrWhiteSpace($DomainNetBios)) { $NetBiosDomainName } else { $DomainNetBios }
$userPattern1 = if ([string]::IsNullOrWhiteSpace($domainPart)) {
$SamAccountName
} else {
"User=$domainPart%5C" + $SamAccountName
}
$userPattern2 = if ([string]::IsNullOrWhiteSpace($domainPart)) {
$SamAccountName
} else {
"$domainPart\" + $SamAccountName
}
$excludeHosts = @('127.0.0.1', '::1')
if (-not [string]::IsNullOrWhiteSpace($ExchangeServerHostForIisExclude)) {
$excludeHosts += $ExchangeServerHostForIisExclude.Trim()
}
$detected = [System.Collections.Generic.List[string]]::new()
try {
$lines = Get-Content -LiteralPath $logFile -Tail $ExchangeIisLogTailLines -ErrorAction Stop
foreach ($line in $lines) {
if ($line -notlike '*401 *' -or $line -notlike '*ActiveSync*') { continue }
if ($line -notlike "*$userPattern1*" -and $line -notlike "*$userPattern2*") { continue }
if ($line -notmatch '(?:\d{1,3}\.){3}\d{1,3}') { continue }
$ip = $Matches[0]
if ($excludeHosts -contains $ip) { continue }
if (-not $detected.Contains($ip)) { $detected.Add($ip) | Out-Null }
}
} catch {
Write-Log "IIS: ошибка чтения $logFile : $($_.Exception.Message)"
}
return @($detected)
}
function Format-Lockout4740TelegramMessage {
param(
[string]$Username,
[string]$Domain,
[datetime]$TimeCreated,
[string[]]$IisClientIps = @()
)
$domainLabel = if ([string]::IsNullOrWhiteSpace($Domain)) { $NetBiosDomainName } else { $Domain }
$accountDisplay = if ([string]::IsNullOrWhiteSpace($domainLabel)) {
$Username
} else {
"$domainLabel\$Username"
}
$hUser = ConvertTo-TelegramHtml $accountDisplay
$hTime = ConvertTo-TelegramHtml ($TimeCreated.ToString('dd.MM.yyyy HH:mm:ss'))
$message = "<b>🔒 Блокировка учётной записи AD (4740)</b>`r`n"
$message += "👤 Пользователь: $hUser`r`n"
$message += "🕐 Время: $hTime`r`n"
if ($IisClientIps.Count -gt 0) {
$message += "`r`n<b>🌐 IP (попытки ActiveSync, 401):</b>`r`n"
foreach ($ip in $IisClientIps) {
$netType = if ($ip -like '192.168.*' -or $ip -like '10.*' -or $ip -like '172.1[6-9].*' -or $ip -like '172.2[0-9].*' -or $ip -like '172.3[0-1].*') {
'внутренний'
} else {
'внешний'
}
$message += ('• {0} ({1})' -f (ConvertTo-TelegramHtml $ip), $netType) + "`r`n"
}
} elseif (-not [string]::IsNullOrWhiteSpace($ExchangeIisLogPath)) {
$message += "`r`n<i>IP в логах IIS ActiveSync для этого пользователя не найдены.</i>`r`n"
}
return $message
}
function Start-LoginMonitor {
param(
[int]$MonitorInterval = 5,
@@ -1655,6 +1975,17 @@ function Start-LoginMonitor {
Write-Log "Режим сервера: Security — LogonType 2, 3, 10"
}
Write-Log "========================================"
Write-Log "Каналы уведомлений: $(Get-NotifyChainHuman)"
$lockout4740Enabled = Test-Lockout4740MonitoringActive
if ($lockout4740Enabled) {
Write-Log "Мониторинг блокировок AD (4740) включён на этом КД ($LockoutMonitorDomainController)."
if (-not [string]::IsNullOrWhiteSpace($ExchangeIisLogPath)) {
Write-Log "Обогащение: IIS ActiveSync — $ExchangeIisLogPath"
}
} elseif (-not [string]::IsNullOrWhiteSpace($LockoutMonitorDomainController)) {
Write-Log "Мониторинг 4740 задан для КД '$LockoutMonitorDomainController', но этот узел — $env:COMPUTERNAME (блокировки не отслеживаются)."
}
Cleanup-OldLogs
Send-Heartbeat -IsStartup
@@ -1674,6 +2005,7 @@ function Start-LoginMonitor {
$lastCheckTime = (Get-Date).AddSeconds(-10)
$lastGatewayCheckTime = (Get-Date).AddSeconds(-10)
$lastRcmCheckTime = (Get-Date).AddSeconds(-10)
$lastLockout4740CheckTime = (Get-Date).AddSeconds(-10)
$monitorEvents = @(4624, 4625, 4648)
while ($true) {
@@ -1733,7 +2065,8 @@ function Start-LoginMonitor {
-SecurityLogComputerName $event.MachineName
Write-Log "Notify: ID=$($event.Id) User=$($eventInfo.Username) LT=$($eventInfo.LogonType) IP=$($eventInfo.SourceIP)"
Send-TelegramMessage -Message $formattedMessage | Out-Null
Send-MonitorNotification -Message $formattedMessage `
-EmailSubject "RDP Login Monitor: вход (ID $($event.Id))" | Out-Null
}
}
$lastCheckTime = ($events | Measure-Object -Property TimeCreated -Maximum | Select-Object -ExpandProperty Maximum).AddSeconds(1)
@@ -1759,7 +2092,8 @@ function Start-LoginMonitor {
-ErrorCode $ei.ErrorCode `
-TimeCreated $ei.TimeCreated
Write-Log "Notify RDG: ID=$($event.Id) User=$($ei.Username)"
Send-TelegramMessage -Message $msg | Out-Null
Send-MonitorNotification -Message $msg `
-EmailSubject "RDP Login Monitor: RD Gateway ($($event.Id))" | Out-Null
}
$lastGatewayCheckTime = ($gatewayEvents | Measure-Object -Property TimeCreated -Maximum | Select-Object -ExpandProperty Maximum).AddSeconds(1)
}
@@ -1783,12 +2117,39 @@ function Start-LoginMonitor {
$msg = Format-Rcm1149Event -Username $rcmInfo.Username -ClientIP $rcmInfo.ClientIP `
-TimeCreated $rcmInfo.TimeCreated -SecurityLogComputerName $event.MachineName
Write-Log "Notify RCM 1149: User=$($rcmInfo.Username) IP=$($rcmInfo.ClientIP)"
Send-TelegramMessage -Message $msg | Out-Null
Send-MonitorNotification -Message $msg `
-EmailSubject "RDP Login Monitor: RDP 1149" | Out-Null
}
$lastRcmCheckTime = ($rcmEvents | Measure-Object -Property TimeCreated -Maximum | Select-Object -ExpandProperty Maximum).AddSeconds(1)
}
}
if ($lockout4740Enabled) {
$lockoutEvents = Get-WinEvent -FilterHashtable @{
LogName = 'Security'
ID = 4740
StartTime = $lastLockout4740CheckTime
} -ErrorAction SilentlyContinue
if ($lockoutEvents) {
foreach ($event in $lockoutEvents) {
if ($event.TimeCreated -le $lastLockout4740CheckTime) { continue }
$lo = Get-Lockout4740EventInfo -Event $event
if ([string]::IsNullOrWhiteSpace($lo.Username)) { continue }
$domainForIis = if ([string]::IsNullOrWhiteSpace($lo.Domain)) { $NetBiosDomainName } else { $lo.Domain }
$iisIps = @(Get-ExchangeActiveSyncIpsFromIisLog -SamAccountName $lo.Username -DomainNetBios $domainForIis)
$msg = Format-Lockout4740TelegramMessage -Username $lo.Username -Domain $lo.Domain `
-TimeCreated $lo.TimeCreated -IisClientIps $iisIps
Write-Log "Notify 4740: User=$($lo.Username) IIS_IPs=$($iisIps -join ', ')"
Send-MonitorNotification -Message $msg `
-EmailSubject "RDP Login Monitor: блокировка УЗ $($lo.Username)" | Out-Null
}
$lastLockout4740CheckTime = ($lockoutEvents | Measure-Object -Property TimeCreated -Maximum | Select-Object -ExpandProperty Maximum).AddSeconds(1)
}
}
$now = Get-Date
if ($now -ge $nextHeartbeatTime) {
Send-Heartbeat
@@ -1810,7 +2171,17 @@ function Start-LoginMonitor {
$script:StopNotificationSent = $false
try {
Test-TelegramConnection | Out-Null
$notifyChannels = @(Get-NotifyOrderChannels)
if ($notifyChannels.Count -eq 0) {
Write-Log "ВНИМАНИЕ: не настроен ни один канал оповещений (Telegram и/или SMTP в конфигурации скрипта)."
} else {
foreach ($notifyCh in $notifyChannels) {
switch ($notifyCh) {
'telegram' { Test-TelegramConnection | Out-Null }
'email' { Test-MailSmtpConnection | Out-Null }
}
}
}
Start-LoginMonitor -MonitorInterval 5 -MonitorInteractiveOnly
} catch {
if ($_.Exception -is [System.Management.Automation.PipelineStoppedException]) {
+7 -4
View File
@@ -5,7 +5,7 @@ PowerShell-набор для мониторинга входов в Windows с
## Актуальная схема (рекомендуется)
- Базовый путь установки: **`C:\ProgramData\RDP-login-monitor\`**.
- Основной скрипт: **`Login_Monitor.ps1`** — журнал Security **`4624`/`4625`** (логика зависит от типа ОС: рабочая станция или сервер/КД), при наличии журнала — **Remote Connection Manager `1149`** (часто актуально для РС с RDP), при роли **RD Gateway****`302`/`303`**, **ежедневный отчёт** в Telegram (активные сессии через `quser`), **heartbeat**, **ротация логов**, уведомления в Telegram.
- Основной скрипт: **`Login_Monitor.ps1`** — журнал Security **`4624`/`4625`** (логика зависит от типа ОС: рабочая станция или сервер/КД), при наличии журнала — **Remote Connection Manager `1149`** (часто актуально для РС с RDP), при роли **RD Gateway****`302`/`303`**, на указанном **КД****`4740`** (блокировка УЗ + IP из IIS ActiveSync), **ежедневный отчёт** в Telegram (активные сессии через `quser`), **heartbeat**, **ротация логов**, уведомления в Telegram и/или Email.
- Установка задач: запуск **`Login_Monitor.ps1 -InstallTasks`** создаёт:
- `RDP-Login-Monitor` (основной монитор),
- `RDP-Login-Monitor-Watchdog` (контроль процесса каждые 5 минут).
@@ -27,13 +27,15 @@ PowerShell-набор для мониторинга входов в Windows с
2. Скопируйте в неё как минимум:
- `Login_Monitor.ps1`
- (для доменного развёртывания отдельно на шаре) `Deploy-LoginMonitor.ps1` и `version.txt`.
3. Откройте `Login_Monitor.ps1` и задайте токен/чат:
- `$TelegramBotToken` или `...ProtectedB64`
- `$TelegramChatID` или `...ProtectedB64`
3. Откройте `Login_Monitor.ps1` и задайте каналы оповещений:
- **Telegram:** `$TelegramBotToken` / `$TelegramChatID` или `...ProtectedB64`
- **Email (SMTP):** `$MailSmtpHost`, `$MailFrom`, `$MailTo`, при необходимости `$MailSmtpUser` / `$MailSmtpPassword` (или `$MailSmtpPasswordProtectedB64` через DPAPI)
- **Порядок:** `$NotifyOrder` — пусто = авто (Telegram → Email, только настроенные); иначе `telegram,email` или `email` и т.п. (допускаются `tg`, `mail`)
4. Запускайте с правами администратора (чтение `Security` журнала и регистрация задач).
5. Логи и служебные файлы будут в:
- `C:\ProgramData\RDP-login-monitor\Logs\`
6. (Опционально) Подавление части алертов по списку — см. раздел **«7) ignore.lst»** ниже.
7. (Опционально) Мониторинг блокировок AD на КД — **`$LockoutMonitorDomainController`**, **`$NetBiosDomainName`**, **`$ExchangeIisLogPath`** (UNC к логам IIS ActiveSync), **`$ExchangeServerHostForIisExclude`** (IP сервера Exchange, не считать клиентским). В оповещении: пользователь из 4740 и IP из IIS (401 + ActiveSync). На других узлах блок 4740 не активен.
## 2) Ручной запуск
@@ -67,6 +69,7 @@ powershell.exe -NoProfile -ExecutionPolicy Bypass -File "C:\ProgramData\RDP-logi
- Heartbeat:
- `C:\ProgramData\RDP-login-monitor\Logs\last_heartbeat.txt` обновляется по интервалу **`$HeartbeatInterval`** (по умолчанию раз в час).
- Ежедневный отчёт: после первого прохождения дневного слота (по умолчанию **09:00**, задаётся **`$DailyReportHour`** / **`$DailyReportMinute`** в `Login_Monitor.ps1`) в Telegram уходит сводка по **`quser`**; метка последнего отчёта — `Logs\last_daily_report.txt`.
- При старте в Telegram/Email: строка **«Каналы уведомлений»** (фактический порядок доставки), плюс режим RDS/4740 по конфигурации.
- Telegram при старте: при установленном **RD Session Host** (или аналогичных компонентах RDS, не только шлюз) — строка про входы по RDP/RDS на этом сервере; при доступном журнале **RD Gateway** — отдельная строка про подключения к **внутренним целевым ПК** через шлюз (302/303). Узел только с ролью RD Gateway не дублирует формулировку «хост сессий».
## 5) Автоматический перезапуск при падении
+1 -1
View File
@@ -1 +1 @@
1.4.4
1.5.2