fix: dedup RDG 302 notify by user+target+external IP (2.1.7-SAC)

Suppress duplicate SAC alerts when Windows logs two RD Gateway 302 events
within 90s for the same user, target host, and external client IP.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
PTah
2026-06-25 10:03:26 +10:00
parent 8a937b8f17
commit 5708e42c66
2 changed files with 26 additions and 2 deletions
+25 -1
View File
@@ -90,7 +90,7 @@ $script:SkipLogDetailLimit = 15
# строки ниже, если правки «мелкие» и вы не хотите менять отображаемую версию в логах). # строки ниже, если правки «мелкие» и вы не хотите менять отображаемую версию в логах).
# Рекомендация: при значимых релизах меняйте и $ScriptVersion, и version.txt одинаково; при только # Рекомендация: при значимых релизах меняйте и $ScriptVersion, и version.txt одинаково; при только
# исправлениях на шаре — достаточно поднять patch в version.txt (например 1.3.0.1). # исправлениях на шаре — достаточно поднять patch в version.txt (например 1.3.0.1).
$ScriptVersion = "2.1.6-SAC" $ScriptVersion = "2.1.7-SAC"
# Логи (все под InstallRoot) # Логи (все под InstallRoot)
$LogFile = Join-Path $script:InstallRoot "Logs\login_monitor.log" $LogFile = Join-Path $script:InstallRoot "Logs\login_monitor.log"
@@ -135,6 +135,8 @@ $LastReportFile = Join-Path $script:InstallRoot "Logs\last_daily_report.txt"
$EnableRDGatewayMonitoring = $true $EnableRDGatewayMonitoring = $true
$RDGatewayLogName = "Microsoft-Windows-TerminalServices-Gateway/Operational" $RDGatewayLogName = "Microsoft-Windows-TerminalServices-Gateway/Operational"
$RDGatewayEvents = @(302, 303) $RDGatewayEvents = @(302, 303)
# Дубликаты RDG 302 (User+Target+ExternalIP) в окне N с — один alert в SAC/Telegram.
$Rdg302NotifyDedupSeconds = 90
# Макс. возраст сохранённого курсора RD Gateway (мин): если файл старше — replay не делаем. # Макс. возраст сохранённого курсора RD Gateway (мин): если файл старше — replay не делаем.
$GatewayEventsLookbackMinutes = 60 $GatewayEventsLookbackMinutes = 60
# Security 4624/4625/4648: персистентный cursor + replay не старше N мин (медленный boot / поздний старт агента). # Security 4624/4625/4648: персистентный cursor + replay не старше N мин (медленный boot / поздний старт агента).
@@ -3818,6 +3820,20 @@ function Test-RdgGatewayBenignErrorCode {
return $false return $false
} }
function Get-Rdg302NotifyDedupKey {
param(
[string]$Username,
[string]$InternalIP,
[string]$ExternalIP
)
$userPart = Get-RdpLoginNotifyDedupUsernamePart -Username $Username
$target = if ($InternalIP) { [string]$InternalIP.Trim() } else { '-' }
if ([string]::IsNullOrWhiteSpace($target)) { $target = '-' }
$ext = if ($ExternalIP) { [string]$ExternalIP.Trim() } else { '-' }
if ([string]::IsNullOrWhiteSpace($ext)) { $ext = '-' }
return "rdg302|$userPart|$target|$ext"
}
function Get-RdgGatewaySacEventType { function Get-RdgGatewaySacEventType {
param( param(
[Parameter(Mandatory = $true)][int]$EventId, [Parameter(Mandatory = $true)][int]$EventId,
@@ -4702,6 +4718,14 @@ function Start-LoginMonitor {
Write-Log "Skip RDG 303: ephemeral channel (SessionDuration=0, User=$($ei.Username), Target=$($ei.InternalIP), ErrorCode=$($ei.ErrorCode))" Write-Log "Skip RDG 303: ephemeral channel (SessionDuration=0, User=$($ei.Username), Target=$($ei.InternalIP), ErrorCode=$($ei.ErrorCode))"
continue continue
} }
if ($event.Id -eq 302) {
$dedupKey302 = Get-Rdg302NotifyDedupKey -Username $ei.Username `
-InternalIP $ei.InternalIP -ExternalIP $ei.ExternalIP
if (Test-RdpMonitorNotifyDedup -Key $dedupKey302 -WindowSeconds $Rdg302NotifyDedupSeconds) {
Write-Log "Notify dedup RDG 302: User=$($ei.Username) Target=$($ei.InternalIP) Ext=$($ei.ExternalIP) (window ${Rdg302NotifyDedupSeconds}s)"
continue
}
}
$msg = Format-RDGatewayEvent -EventID $event.Id ` $msg = Format-RDGatewayEvent -EventID $event.Id `
-Username $ei.Username ` -Username $ei.Username `
-ExternalIP $ei.ExternalIP ` -ExternalIP $ei.ExternalIP `
+1 -1
View File
@@ -1 +1 @@
2.1.6-SAC 2.1.7-SAC