Compare commits

..

193 Commits

Author SHA1 Message Date
PapaTramp 6ae04dd967 chore(private): fix PapaTramp URLs on papatramp mirror
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-07 17:49:22 +10:00
PapaTramp 6d52a34a37 fix(scripts): papatramp Git URLs use PapaTramp org, not PTah
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-07 17:49:19 +10:00
PapaTramp f033d5bd87 fix: daily report без локального Telegram при fallback SAC (2.1.9-SAC)
Аналог ssh-monitor: report.daily.* только SAC/spool, timeout 45s,
spool flush приоритет отчётам (50 файлов/цикл).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-06 12:00:36 +10:00
PapaTramp f2bcc6c7df fix: restore production secrets on kalinamall and repair placeholder settings (2.1.8-SAC)
Real SAC/Telegram tokens and org paths back in example and NETLOGON publish script. Deploy now treats sac.example.com and sac_CHANGE_ME as unconfigured and overwrites from example.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-25 14:32:44 +10:00
PapaTramp 5708e42c66 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>
2026-06-25 10:03:26 +10:00
PapaTramp 8a937b8f17 fix: quarantine corrupt SAC spool files, flush newest first (2.1.6-SAC)
Reject null-padded spool payloads to rejected/, read UTF-16 spool, process recent events first, and raise flush batch to 20 so daily reports are not blocked by legacy junk.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-25 09:09:54 +10:00
PapaTramp ccd50a085d fix: RCM 1149 silent drop on RDS servers (2.1.5-SAC)
Should-IgnoreEvent rejected every 1149 because ComputerName is always '-'; add persistent poll cursor, skip logging, and Diagnose on NETLOGON publish list.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-24 14:29:02 +10:00
PapaTramp 55d58b25a3 docs: sync README version lines with 2.1.4-SAC
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-24 14:14:43 +10:00
PapaTramp 4b8e3313ae docs: document -NetlogonDest UNC path for update-rdp-monitor.ps1
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-24 09:21:17 +10:00
PapaTramp ab53d47329 feat: RCM 1149 on RDS servers and login dedup (2.1.4-SAC)
Enable Rcm1149 monitoring on servers when RCM journal is available; dedup 1149 notifications like 4624.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-23 15:01:09 +10:00
PapaTramp eecc0e1619 docs(deploy): clarify DailyReportEnabled comment in settings patch
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-23 14:41:53 +10:00
PapaTramp 128dd278f6 feat(deploy): sync ServerIPv4 hint and DailyReportEnabled on upgrade (2.1.3-SAC)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-22 10:01:16 +10:00
PapaTramp 8cefba1952 Merge branch 'main' of ssh://git.kalinamall.ru:2222/PapaTramp/RDP-login-monitor 2026-06-22 09:04:36 +10:00
PapaTramp b4dd593160 docs: SAC WinRM update path without NETLOGON
Добавлен раздел об обновлении RDP через SAC: zip с сервера, staging и Deploy-LoginMonitor.ps1.
2026-06-20 20:03:59 +10:00
PapaTramp f3a0adb7b8 fix: remove UTF-8 BOM from PowerShell entry scripts 2026-06-20 19:07:52 +10:00
PapaTramp a0b473b4d7 docs: concise README for 2.1.2-SAC capabilities 2026-06-20 19:02:59 +10:00
PapaTramp 2c1724fd90 fix: SAC poll URL interpolation in Sac-Client (2.1.2-SAC) 2026-06-19 23:49:44 +10:00
PapaTramp a9ca50bc15 fix: default HeartbeatStaleAlertMultiplier (2.1.1-SAC)
Startup notification failed when settings lacked the variable; default 2 in script and deploy bootstrap.
2026-06-19 23:47:48 +10:00
PapaTramp fb50fb3ea7 chore: bump version to 2.1.0-SAC 2026-06-19 23:42:08 +10:00
PapaTramp 5d2b90779c feat: SAC agent command poll qwinsta/logoff (2.0.39-SAC)
Poll GET /api/v1/agent/commands every SacCommandPollIntervalSec;
execute qwinsta/logoff with run_as from SAC; POST result.
2026-06-19 23:37:58 +10:00
PapaTramp afa80d169f chore(github): keep sanitized settings on public main
Production tokens and NETLOGON paths live on kalinamall/papatramp only. Use scripts/Push-PrivateMirror.ps1 after feature pushes.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-17 11:12:59 +10:00
PapaTramp 7f38c00094 fix: restore production secrets and paths for private git
Real Telegram/SAC tokens, NETLOGON and org hostnames back on kalinamall. GitHub main keeps placeholders; use scripts/Push-PrivateMirror.ps1 after public pushes.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-17 11:12:29 +10:00
PapaTramp c12f169189 merge: sync remotes; keep deploy StartupRebootDetectMinutes bootstrap
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-17 11:04:04 +10:00
PapaTramp 37fdeb44c5 feat: deploy bootstrap StartupRebootDetectMinutes in agent settings
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-17 11:02:50 +10:00
PapaTramp 746f9e23b2 feat: detect OS reboot on startup via System log and LastBootUpTime
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-17 10:42:40 +10:00
PapaTramp 35e25063e3 chore(github): generic NETLOGON paths, remove mirror scripts
Replace \\B26\\NETLOGON with \\dc.contoso.local\\NETLOGON.
Remove scripts/ mirror tools and tools/Push-KalinamallOnly.ps1.
Sanitize login_monitor.settings.example (placeholders, no secrets).
Simplify update-rdp-monitor.ps1 to use origin remote only.
Keep dev tooling under tools/ only.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-16 11:57:35 +10:00
PapaTramp 7466254401 chore(docs): sync repository URLs for kalinamall mirror 2026-06-16 11:38:26 +10:00
PapaTramp 8d17a0f560 chore(docs): GitHub default clone URL, remove .cursor from git
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-16 11:37:48 +10:00
PapaTramp ae86cb3ea7 feat: persistent Security poll cursor with lookback replay (2.0.36-SAC)
Save last_security_poll.txt between runs and replay Security events up to SecurityEventsLookbackMinutes on startup so slow boot or late agent start does not miss RDP/WinRM-related logons.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-16 11:37:48 +10:00
PapaTramp c146bb119a chore(docs): GitHub default clone URL, remove .cursor from git
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-16 11:36:51 +10:00
PapaTramp 1fcd5cb5cf feat: persistent Security poll cursor with lookback replay (2.0.36-SAC)
Save last_security_poll.txt between runs and replay Security events up to SecurityEventsLookbackMinutes on startup so slow boot or late agent start does not miss RDP/WinRM-related logons.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-16 09:40:15 +10:00
PapaTramp f48aee1d93 feat: persistent Security poll cursor with lookback replay (2.0.36-SAC)
Save last_security_poll.txt between runs and replay Security events up to SecurityEventsLookbackMinutes on startup so slow boot or late agent start does not miss RDP/WinRM-related logons.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-16 09:39:43 +10:00
PapaTramp d5db38e2ae feat: deploy дописывает HeartbeatInterval при отсутствии в settings (2.0.35-SAC)
Ранний выход при совпадении версии не пропускает heartbeat; reload settings у работающего монитора.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-12 16:46:43 +10:00
PapaTramp 91cde1668c merge: GitHub main + heartbeat on startup (2.0.34-SAC)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-12 11:52:15 +10:00
PapaTramp 09f1b576bf feat: send agent.heartbeat on monitor startup (2.0.34-SAC)
SAC sees host online immediately after restart; periodic heartbeat every 4h unchanged.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-12 11:51:16 +10:00
PapaTramp b6d293d21b fix: normalize ExecutionTimeLimit PT0S and load TaskQuery locally (2.0.33-SAC)
Treat Get-ScheduledTask limit strings as unlimited, prefer ProgramData for TaskQuery import, and copy UNC modules to temp before dot-source.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-11 15:27:47 +10:00
PapaTramp 6ee5fdcbf2 fix: normalize ExecutionTimeLimit PT0S and load TaskQuery locally (2.0.33-SAC)
Treat Get-ScheduledTask limit strings as unlimited, prefer ProgramData for TaskQuery import, and copy UNC modules to temp before dot-source.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-11 15:27:38 +10:00
PapaTramp 0da1d0f2ac fix: deploy TaskQuery script scope (2.0.32-SAC)
Publish TaskQuery helpers to script scope after import so ExecutionTimeLimit checks work from nested deploy callers; add RDP_DEPLOY_FUNCTIONS_ONLY early exit for isolated function loading.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-11 15:23:59 +10:00
PapaTramp 5e9939724c fix: deploy TaskQuery script scope and internal autotests (2.0.32-SAC)
Publish TaskQuery helpers to script scope after import so deploy checks work from nested callers; add kalinamall-only smoke tests and RDP_DEPLOY_FUNCTIONS_ONLY hook.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-11 15:15:42 +10:00
PapaTramp 31ebd5d653 fix: deploy task limit check without TaskQuery helper deps (2.0.31-SAC)
Deploy uses Get-RdpMonitorScheduledTaskExecutionTimeLimitResolved only; no call to Test-RdpMonitorScheduledTaskNeedsUnlimitedExecutionTimeLimit from external module.
2026-06-11 15:05:02 +10:00
PapaTramp 5c23ac5f36 fix: deploy TaskQuery init must load Resolved helper (2.0.30-SAC)
Verify TaskQuery module by Get-RdpMonitorScheduledTaskExecutionTimeLimitResolved, prefer share path over stale InstallRoot, and guard verification when init fails.
2026-06-11 15:02:08 +10:00
PapaTramp 6fa940dd43 fix: SendDeploySacNotice StrictMode and inline deploy task verify (2.0.29-SAC)
Safe ServerDisplayName access for SAC deploy notice; deploy falls back to built-in schtasks XML check when TaskQuery file is missing on NETLOGON.
2026-06-11 14:59:17 +10:00
PapaTramp 463a3ef08c fix: deploy task verify via schtasks XML and notify SAC on update (2.0.28-SAC)
Stop false redeploy loops when Get-ScheduledTask fails, push agent version to SAC immediately after deploy, and skip redundant schtasks /Run during task maintenance.
2026-06-11 14:47:49 +10:00
PapaTramp 399597c8a1 fix: sync HeartbeatInterval on every deploy, not only version upgrade
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-11 09:26:03 +10:00
PapaTramp 365a7113cf feat: heartbeat 4h and deploy rewrites settings (2.0.27-SAC)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-10 17:25:20 +10:00
PapaTramp 01d7525c4a fix(deploy): escape TaskName in log string (PowerShell parser); 2.0.25-SAC
${TaskName}: after colon was parsed as drive scope — deploy failed at parse time.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-08 11:51:06 +10:00
PapaTramp 1d30ca2284 fix: RDP-Login-Monitor scheduled task unlimited ExecutionTimeLimit (2.0.24-SAC)
Default PT72H caused Task Scheduler to stop the monitor after 72 hours.
Register main task with PT0S, re-register on deploy and Ensure when limit is set.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-08 11:47:46 +10:00
PapaTramp 80b2337d61 fix(winrm): Exchange strict mode to suppress false 91/4624 alerts
On Exchange role: require user in WinRM Event 91 EventData; correlate 4624 only when LogonProcess is WinRM. Prevents Outlook/LT3 false positives. Version 2.0.23-SAC.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-05 11:07:21 +10:00
PapaTramp fe2e6d624f fix: inventory JSON without JavaScriptSerializer WARN (2.0.22-SAC)
Use ConvertTo-Json after CIM-safe Convert-AnyToJsonSerializable; pre-sanitize inventory details.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-05 10:00:11 +10:00
PapaTramp 82c63fb9a8 feat: host inventory to SAC every 12h (GetInventory)
Collect CPU/RAM/disks/GPU/OS via agent.inventory; setting $GetInventory (default true); deploy adds missing setting.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-05 09:53:03 +10:00
PapaTramp bd83dc84dc fix: StrictMode-safe File Share audit init (UseGuid key)
Enable-SecurityAudit crashed on init when checking RU/EN File Share attempts because accessing UseGuid on a hashtable without that key throws under Set-StrictMode -Version Latest.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 10:20:17 +10:00
PapaTramp afded8a495 docs: README highlights table and 2.0.19-SAC admin share 5140
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 10:17:11 +10:00
PapaTramp 5fc5f2a9ad feat: monitor admin share access via Security 5140 C$/ADMIN$ (2.0.19-SAC)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 10:14:14 +10:00
PapaTramp e3149785dc fix: daily report survives empty quser sessions (2.0.18-SAC)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 09:32:43 +10:00
PapaTramp 6384a45395 fix: recognize auditpol RU Success+Failure as otказ not only sboy (2.0.17-SAC)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 15:08:20 +10:00
PapaTramp 0acd591c83 fix: PS 5.1 empty SkipEntries breaks monitor loop (2.0.16-SAC)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 15:06:35 +10:00
PapaTramp fa5248d501 fix: Sac-Client splat syntax for PS 5.1
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 11:45:49 +10:00
PapaTramp f08a8a8561 fix: allow null SacOccurredAt on lifecycle notifications (2.0.15-SAC)
PS 5.1 rejects binding null to [datetime]; use optional splatting for SAC occurred_at.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 11:45:35 +10:00
PapaTramp 550cea9759 fix: RD Gateway startup replay, 1226 benign, SAC occurred_at (2.0.14-SAC)
Persist RD Gateway poll cursor to avoid replaying 302/303 on monitor restart;
treat ErrorCode 1226 as normal disconnect; pass event TimeCreated to SAC ingest.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 11:42:03 +10:00
PapaTramp e00513f050 feat: deploy patch for MaxBackupDays default (2.0.13-SAC)
Add deploy-time patch when login_monitor.settings.ps1 lacks MaxBackupDays,
enable the setting in example, and remove duplicate default in Login_Monitor.ps1.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 11:25:21 +10:00
PapaTramp 22ac7a32c7 feat: shutdown diagnostics and log backup retention (2.0.12-SAC)
- Log phase, uptime, batch sizes and shutdown path on unexpected stop

- PipelineStopped sends SAC alert with diagnostics; fix StopNotificationSent on settings reload

- Summarize large Skip 4624 batches; Remove-LogBackupsBeyondRetention uses MaxBackupDays from settings

- Purge LoginLog_*.bak on rotation, daily rotation check and startup

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 11:07:03 +10:00
PapaTramp 92773948a7 fix: apply login_monitor.settings.ps1 to script scope (2.0.10-SAC)
2.0.9 dot-sourced settings inside Import-LoginMonitorSettingsFile left UseSAC/Telegram in function scope; monitor ran with defaults off and no SAC ingest.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 10:33:27 +10:00
PapaTramp 7b83a19db0 fix: InstallTasks fails when settings WinRM lines lack dollar sign (2.0.9-SAC)
Defer login_monitor.settings.ps1 dot-source until after -InstallTasks so broken WinRM assignments cannot abort task registration. Deploy repairs settings lines missing $ before variable names.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 10:14:17 +10:00
PapaTramp 33367b05a8 fix: parse RD Gateway EventInfo XML and skip ephemeral 303 alerts
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 10:07:51 +10:00
PapaTramp 94d12e0b5f chore: sync Cursor rules from CursorRules (version bump RDP/SSH split)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 09:31:56 +10:00
PapaTramp 4b8ff95344 chore: add Cursor rule to bump ScriptVersion and version.txt on script changes
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 09:29:23 +10:00
PapaTramp 47f95ba186 chore: bump release to 2.0.7-SAC
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 09:17:28 +10:00
PapaTramp 335768915b feat: deploy UseSAC fallback by default and prevent duplicate daily reports
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 09:15:22 +10:00
PapaTramp 5e5bc86e1b docs: add one-command quick start for first install
Put NETLOGON deploy flow at the top of README so initial setup is straightforward: run one command and verify with log tail.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-01 13:28:28 +10:00
PapaTramp 42d6af83f2 docs: add 5-minute WinRM stabilization checklist
Document quick post-deploy validation for Enter-PSSession -> WinRM 91 -> SAC event flow and minimal noise-suppression guidance.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-01 13:26:32 +10:00
PapaTramp 52e9e62ede docs: refresh README for WinRM and current SAC integration state
Update current RDP monitor version and document recent WinRM parsing, deploy self-heal, and skip reason diagnostics behavior.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-01 13:25:16 +10:00
PapaTramp fae185421a fix: harden WinRM local IP detection and bump to 2.0.6-SAC
Normalize WinRM source IP candidate before loopback/link-local checks and use explicit -Ip binding in WinRM ignore reason path to avoid false local-or-linklocal-ip skips.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-01 13:20:38 +10:00
PapaTramp afc7bf9dca fix: log exact WinRM skip reason and bump to 2.0.5-SAC
Add Get-WinRmIgnoreReason to expose precise built-in/ignore decision path (empty user, excluded pattern, machine account, local IP, ignore-list match) for WinRM 91 diagnostics.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-01 13:17:48 +10:00
PapaTramp ac095fe1a0 fix: parse WinRM 91 user/ip correctly and bump to 2.0.4-SAC
Improve WinRM Operational event parsing to extract username and clientIP from EventData/Message fallback, preventing false Skip for valid Enter-PSSession events.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-01 13:05:14 +10:00
PapaTramp 81323e8596 chore: bump RDP monitor version to 2.0.3-SAC
Increase ScriptVersion and version.txt after deploy-side reliability updates affecting WinRM monitoring behavior.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-01 12:58:40 +10:00
PapaTramp 1eedfc736f fix: enforce WinRM inbound block and enable Operational log in deploy
Deploy now ensures required WinRM inbound settings exist in login_monitor.settings.ps1 and attempts to enable Microsoft-Windows-WinRM/Operational via wevtutil when disabled.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-01 12:55:05 +10:00
PapaTramp d54d04623f fix: PS 5.1 Sac-Client JSON types without uint alias 2.0.2-SAC
Replace [uint]/[ulong] -is checks with GetType().IsPrimitive for Windows PowerShell 5.1 on domain hosts.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-01 09:13:19 +10:00
PapaTramp 084494cfa8 fix: SAC JSON serialize daily report without PSMethod cycle 2.0.1-SAC
Convert-AnyToJsonSerializable returns plain arrays and skips PSMethod/ScriptBlock instead of Generic.List objects that broke JavaScriptSerializer.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-01 09:08:14 +10:00
PapaTramp 1a1467f910 chore: bump release to 2.0.0-SAC
Major version line so Deploy upgrades agents stuck on 1.6.x and older 1.2.x-SAC builds from NETLOGON.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-01 09:03:48 +10:00
PapaTramp 543369832a fix: restore RD Gateway 302/303 polling and SAC ingest 1.2.35-SAC
Fix poll cursor skipping events at time boundary, add 60min lookback on start, parse RU gateway messages, and map 303 disconnect to rdg.connection.disconnected.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-01 08:14:37 +10:00
PapaTramp f970e284ac fix: stronger 4624 login dedup and logon type label 1.2.34-SAC
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-31 19:14:05 +10:00
PapaTramp 6a9b2b70dc fix: use full path to schtasks.exe in deploy post-update start 1.2.33-SAC
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-31 19:03:28 +10:00
PapaTramp 2118d4a4a0 fix: hide redundant workstation line in RDP login alerts 1.2.32-SAC
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-31 18:55:11 +10:00
PapaTramp 78f241df32 feat: add Diagnose-RdpLoginMonitor.ps1 for post-login troubleshooting
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-31 18:40:10 +10:00
PapaTramp e1ae9834a9 feat: single deploy startup, stop mode, notification_body 1.2.31-SAC
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-31 18:11:18 +10:00
PapaTramp a74c8df877 feat: lifecycle notifications with telegram_via and settings reload 1.2.30-SAC
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-31 12:25:50 +10:00
PapaTramp 2395f536cd feat: notification source line in agent alerts (1.2.29-SAC)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-31 11:51:55 +10:00
PapaTramp b56ad9c7e4 feat: daily report agent version line and per-line active users (1.2.28-SAC)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-31 11:28:29 +10:00
PapaTramp e738d97c9c chore: release 1.2.27-SAC (deploy version compare + docs)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-31 11:18:58 +10:00
PapaTramp 1fe8bd9f7d fix(deploy): version compare for -SAC labels; refresh deploy docs
Compare deployed_version.txt with full label (e.g. 1.2.26-SAC) so GPO deploy exits when up to date. Restructure deploy-rdp-login-monitor.md with mermaid flow, Exchange vs DomainMonitors split, and remove duplicated sections.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-31 11:13:33 +10:00
PapaTramp bf04c8865c feat(deploy): auto-add Exchange WinRM/4624 noise settings on role detect (1.2.26-SAC)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-30 10:36:00 +10:00
PapaTramp b010ad01eb fix: filter WinRM noise (HealthMailbox, machine accounts, loopback)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-30 10:31:30 +10:00
PapaTramp 1ff8dd1a95 fix: init NotifyDedupCache under StrictMode (1.2.24-SAC)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-30 10:26:07 +10:00
PapaTramp 724baa6c0a feat: monitor RDS Shadow Control and WinRM inbound (warning alerts, 1.2.23-SAC)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-30 10:20:57 +10:00
PapaTramp 2bbf1face6 fix(deploy): patch DailyReportEnabled hint and invalid false assignment (1.2.22-SAC)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-29 17:50:17 +10:00
PapaTramp d23e99a885 feat: unify Windows daily report with SAC layout; DailyReportEnabled toggle
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-29 17:18:02 +10:00
PapaTramp 70f06790f6 docs: deploy adds commented ServerDisplayName hint in settings
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-29 15:57:35 +10:00
PapaTramp c249451eba fix: deploy syncs Sac-Client bundle, SAC dual settings, and ServerDisplayName hint (1.2.20-SAC)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-29 15:55:16 +10:00
PapaTramp 1f27272883 fix: dedup 4624 login alerts and log Skip/dedup reasons (1.2.18-SAC)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-28 15:17:01 +10:00
PapaTramp 49359c07a6 fix: tolerate missing ServerDisplayName and send host.ipv4 (1.2.17-SAC)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-28 13:33:26 +10:00
PapaTramp 611d9c68ca chore: bump RDP monitor version to 1.2.16-SAC
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-28 13:01:20 +10:00
PapaTramp 737da07d6a fix: add Exchange 4624 LT3 empty-IP filter switch
Add configurable `${Ignore4624-LT3-EmptyIP-Event}` for RDP monitor to suppress noisy 4624 network logons with empty IP (-) on mail/Exchange hosts.
Update settings example and README with usage instructions.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-28 12:35:40 +10:00
PapaTramp b9d385f51b feat: ServerDisplayName для Telegram и SAC host.display_name (1.2.15-SAC)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-28 11:49:13 +10:00
PapaTramp e7f19e5c92 fix: SAC ingest POST через HttpWebRequest и проверка JSON bytes (1.2.14-SAC)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-28 11:13:43 +10:00
PapaTramp e4f4d1bb1f fix: SAC POST не срезать '{' из-за StartsWith(BOM) в PowerShell (1.2.13-SAC)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-28 10:57:42 +10:00
PapaTramp 74d1e4db75 fix: SAC POST body null prefix from Write-Log pipeline (1.2.12-SAC)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-28 10:44:22 +10:00
PapaTramp 5284734092 fix: SAC JSON Cyrillic via JavaScriptSerializer, log 422 body (1.2.11-SAC)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-28 10:34:21 +10:00
PapaTramp 1c210a40d8 fix: treat HTTP 201/409/202 as SAC success in WebException path (1.2.10-SAC)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-28 10:27:23 +10:00
PapaTramp 83315cc3b8 fix: SAC 422 no spool loop, truncate title/summary, UTF-8 spool read (1.2.9-SAC)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-28 10:20:30 +10:00
PapaTramp 04f69aac40 fix: update-rdp-monitor always reset --hard kalinamall/main on publish
- Ignore stderr noise; parse HEAD; clean -fd; verify NETLOGON version.txt

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-28 10:10:51 +10:00
PapaTramp 6c86e172b9 fix: SAC ingest UTF-8 JSON body for Cyrillic (1.2.8-SAC)
Invoke-WebRequest now posts application/json; charset=utf-8 bytes

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-28 10:05:49 +10:00
PapaTramp 71cb1fa9b6 fix: update-rdp-monitor fetch+reset for kalinamall force-push
Prefer kalinamall remote; abort stale merge; ff-only or hard reset.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-28 09:35:55 +10:00
PapaTramp ef237f345b chore: release 1.2.7-SAC — SAC ingest HTTP 201/409/202
Bump version.txt and ScriptVersion for Deploy-LoginMonitor on NETLOGON.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-28 09:34:19 +10:00
PapaTramp 1092f6340d fix: accept SAC ingest HTTP 201, 409, and legacy 202
Remove .cursorignore (patterns covered by repo hygiene elsewhere)
2026-05-28 09:13:14 +10:00
PapaTramp 9030fb3c30 fix: heartbeat only to SAC, never Telegram/email
agent.heartbeat in Send-NotifyOrSac skips local channels in dual/fallback/off.
Bump to 1.2.6-SAC.
2026-05-28 07:56:18 +10:00
PapaTramp 3ed20b130f chore: bump release to 1.2.5-SAC
Raise ScriptVersion/version.txt for Sac-Client payload product_version and
update README release reference to keep docs aligned.
2026-05-27 15:53:29 +10:00
PapaTramp 1b4ca5fa01 feat: bootstrap SAC settings on deploy when upgrading from pre-SAC
Deploy overwrites login_monitor.settings.ps1 from the trusted example when
Sac-Client.ps1 or SacApiKey/UseSAC are missing, with a .bak backup. Skips
overwrite when SAC is already configured; continues deploy on version match
if SAC bootstrap is still required.
2026-05-27 15:23:32 +10:00
PapaTramp a1a3bca0c3 fix: do not stop monitor when SAC /health is temporarily down 2026-05-27 15:02:07 +10:00
PapaTramp 004a8c0026 fix: deploy graceful recycle writes restart.request directly (no 90s wait) 2026-05-27 14:53:54 +10:00
PapaTramp 904ab13880 chore: bump version to 1.2.2-SAC 2026-05-27 14:44:02 +10:00
PapaTramp 22a32bc1cb feat: graceful restart without Stop-Process; SAC key in settings example 2026-05-27 14:43:04 +10:00
PapaTramp bd8076a1d0 docs: Windows/SAC update checklist; default Telegram in settings example 2026-05-27 14:27:27 +10:00
PapaTramp edbe00ffb3 feat: SAC client 1.2.0-SAC and daily report details for ingest 2026-05-27 14:15:16 +10:00
PapaTramp 577b8bc220 feat(Login): вынести локальные настройки в login_monitor.settings.ps1
Секреты, SMTP, 4740 и IP-исключения больше не в Login_Monitor.ps1 —
не слетают при автообновлении с NETLOGON. Deploy создаёт settings из example при первой установке.
2026-05-25 09:44:43 +10:00
PapaTramp 971461accf fix(Exchange): stop Import-ExchangeManagementShell leaking True (v1.6.7)
return True from EMS import was collected into mailbox lists and domain joins, causing Get-InboxRule on mailbox True. Suppress import output at call sites and log accepted domains via @() enumeration.
2026-05-22 16:10:36 +10:00
PapaTramp 744f254c09 fix(Exchange): define Get-ExchangeInboxScanScopeLabel before InstallTasks (v1.6.6) 2026-05-22 16:02:05 +10:00
PapaTramp 1584be89e2 feat(Exchange): skip corrupt inbox rule stores (v1.6.5)
Add SkipInboxScanMailboxes with k.selezneva@kalinamall.ru by default. Filter inbox scan list, log SKIP, and catch Get-InboxRule failures to avoid Watson aborting full scans.
2026-05-22 16:00:09 +10:00
PapaTramp cdf563cbe9 fix(deploy): copy exchange_monitor.settings.example to ProgramData (v1.6.4)
Example settings were published to NETLOGON but omitted from Deploy-DomainMonitors file list, so Copy-Item from ProgramData failed on fresh installs.
2026-05-22 15:00:30 +10:00
PapaTramp 872df98a2c fix: Exchange monitor encoding for PS 5.1 (v1.6.3)
ASCII runtime strings in Exchange-MailSecurity and Notify-Common avoid parser errors when scripts are copied without UTF-8 BOM. Deploy copies .ps1 to ProgramData with UTF-8 BOM via Copy-ScriptFileWithUtf8Bom.
2026-05-22 14:57:35 +10:00
PapaTramp 7c13c96622 fix: Deploy-DomainMonitors encoding for PS 5.1 / NETLOGON (v1.6.2)
- ASCII runtime strings in domain deploy/install scripts
- Publish *.ps1 to NETLOGON with UTF-8 BOM
2026-05-22 14:53:28 +10:00
PapaTramp 0c916b50c6 feat(Exchange): VIP scan, disabled rules, first-baseline suppress (v1.6.1)
- VipMailboxes + VipMailboxPatterns; unified VIP scope
- Scan disabled inbox rules with external forward (lower severity)
- Suppress per-finding alerts on first baseline run
- Post-scan summary and install notification
2026-05-22 14:44:57 +10:00
PapaTramp 9ab6cc64a1 docs: add Docs/ guides; Exchange Mail Security scripts (v1.6.0)
- Docs: RDP deploy, Exchange mail security, NETLOGON publish
- Exchange-MailSecurity.ps1, Notify-Common, Deploy/Install-DomainMonitors
- DEPLOY.md redirects to Docs/
2026-05-22 14:32:50 +10:00
PapaTramp 40c557f041 feat: aggregated 4625 burst alerts (two-tier rate limit, v1.5.10)
- Tier 1: 5 failures per 60s per IP+user
- Tier 2: 12 failures per 60s per IP (spraying)
- Suppress per-event 4625 during burst; no auto-ban
2026-05-22 14:13:06 +10:00
PapaTramp a324841b7d chore: clarify IIS log line wording for 4740 (v1.5.9) 2026-05-22 11:06:19 +10:00
PapaTramp a29ef8e0f7 config: LockoutMonitorDomainController K6A-DC3 (v1.5.8) 2026-05-22 11:01:38 +10:00
PapaTramp 16d555037f fix: daily report uses full path to quser.exe when System32 missing from PATH (v1.5.7) 2026-05-22 10:54:57 +10:00
PapaTramp ab98eab49c fix: invoke auditpol by full path when System32 is missing from PATH (v1.5.6) 2026-05-22 10:41:59 +10:00
PapaTramp eccffe927a Fixed BOM 2026-05-22 10:28:40 +10:00
PapaTramp 83f5c7de63 config: fifth IIS UNC, NETLOGON publish script (v1.5.5) 2026-05-22 10:13:54 +10:00
PapaTramp 67c33fd4ff config: IIS logs on fifth UNC path (v1.5.5) 2026-05-22 10:05:18 +10:00
PapaTramp 6d7b4874ce config: Kalina Telegram, DC3/B26 lockout, IIS path (v1.5.4) 2026-05-22 09:58:09 +10:00
PapaTramp f4375c2349 readme and docs editing 2026-05-22 09:19:26 +10:00
PapaTramp 52b1723137 feat: ignore.lst for 4740, IIS time window, stale heartbeat alert (v1.5.3)
- ignore.lst scopes: 4740:/all: prefixes; IP rules match IIS client IPs
- IIS ActiveSync IPs only within minutes before lockout event
- Alert when last_heartbeat.txt is older than 2x HeartbeatInterval
2026-05-19 20:46:35 +10:00
PapaTramp b7786e8690 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.
2026-05-19 19:47:23 +10:00
PapaTramp 938df9bfe0 fix: refresh ignore.lst each monitor loop for log/mtime (v1.4.4) 2026-05-04 10:22:03 +10:00
PapaTramp a3a5b45cf8 fix: ignore list startup line use -f format (avoid $kindLabel: parse), bump 1.4.3 2026-05-04 09:42:32 +10:00
PapaTramp 362318707e fix: PowerShell parse error for ignore list line (), bump 1.4.2 2026-05-04 09:40:34 +10:00
PapaTramp 98031a20a1 fix: InstallTasks before DPAPI, deploy elevation and installtasks logs (1.4.1) 2026-05-04 09:38:02 +10:00
PapaTramp b3da911d51 chore: bump version to 1.4.0 2026-05-04 09:33:54 +10:00
PapaTramp 967e470e39 feat: list ignore.lst rules in startup Telegram notice 2026-05-04 09:30:24 +10:00
PapaTramp ace7220752 feat: log ignore.lst reload with rule counts 2026-05-04 09:11:03 +10:00
PapaTramp bdcf2bdc5f docs: expand ignore.lst section in README and README_eng 2026-05-04 09:02:14 +10:00
PapaTramp 57bc617d26 Sanitize ignore.lst.example placeholders
Replace sample workstation, domain user, and IP with generic values.
2026-05-04 08:25:21 +10:00
PapaTramp 17befc867e Document ignore.lst in README and DEPLOY
Describe optional Security 4624/4625 suppression, UTF-8, and that deploy does not ship ignore.lst.
2026-05-04 07:56:49 +10:00
PapaTramp d3154ceb1b Add ignore.lst filtering for Security logon alerts
Support user/workstation/IP rules and Telegram-style lines; add ignore.lst.example; bump to 1.3.14.
2026-05-04 07:55:24 +10:00
PapaTramp fe600790e8 docs(license): update copyright holder name 2026-05-03 18:55:00 +10:00
PapaTramp 11736501ec chore: add MIT LICENSE 2026-05-03 18:54:16 +10:00
PapaTramp e96131d722 docs: refresh README, add English README_eng.md
Document daily report, deploy marker, SkipScheduledTaskMaintenance, and legacy watchdog scripts. Add full English translation.
2026-05-03 18:52:18 +10:00
PapaTramp 99be51467e Refine RD Gateway 303 semantics and show deploy update timestamp 2026-04-30 16:55:30 +10:00
PapaTramp d26784fb7f Refresh README for current ProgramData + Deploy workflow 2026-04-30 16:43:14 +10:00
PapaTramp 02424875fb Add repository search keywords to README 2026-04-30 16:34:26 +10:00
PapaTramp 0e2b3f2534 Clarify manual deploy option for small server fleets 2026-04-30 16:31:33 +10:00
PapaTramp b6ac34a52d Add periodic deploy task installer and docs for rarely rebooted servers 2026-04-30 16:21:42 +10:00
PapaTramp a37a639be1 Fix RD Gateway 302/303 field mapping (XML + updated Properties layout).
Bump version to 1.3.12.
2026-04-30 16:05:21 +10:00
PapaTramp 1c14eec3cd Update DEPLOY.md with a tested GPO startup setup and troubleshooting notes.
Document PowerShell Startup tab usage, security filtering for computer groups, SYSVOL/NETLOGON behavior, reboot expectations, and where to inspect logs.
2026-04-29 17:47:21 +10:00
PapaTramp cc8925f88a Start main task immediately after InstallTasks and include script version in startup notification.
Also bump script/version.txt to 1.3.11 for rollout tracking.
2026-04-29 16:45:13 +10:00
PapaTramp a20055f9d3 Singleton lock: DeleteOnClose, Ctrl+C propagates, release in finally (v1.3.9) 2026-04-29 16:23:16 +10:00
PapaTramp 313cc189e9 v1.3.5: путь скрипта при Start-Process, диагностика watchdog, DEPLOY.md 2026-04-29 16:13:14 +10:00
PapaTramp e0b0fa3d7e Watchdog: schtasks /Run после установки; DEPLOY/README про задачи и UNC; версия 1.3.4 2026-04-29 16:07:35 +10:00
PapaTramp 80f57c5351 Добавлен доменный деплой (Deploy-LoginMonitor.ps1, version.txt), DEPLOY.md, правки README и комментарий к версии в Login_Monitor.ps1 2026-04-29 15:14:41 +10:00
PapaTramp 306b0762a1 Workstation vs server: ProductType; WKS uses LT10 + RCM 1149; server keeps 2/3/10 (v1.2.0) 2026-04-29 14:30:28 +10:00
PapaTramp 6bc5173236 auditpol: detect RU only from /list, not culture; fall back to EN (Hyper-V); v1.1.3 2026-04-27 10:47:16 +10:00
PapaTramp be0c394ac4 Fix Uc RU category string: add missing 0x0445 in vyhod (auditpol 0x57); v1.1.2 2026-04-27 10:29:10 +10:00
PapaTramp 850781a38d Revert "Use ASCII-only source for Telegram and logs; remove emoji (v1.2.0)"
This reverts commit b16ebb028b8b28a25e6eed396b14a8cfa1f3295f.
2026-04-27 10:25:55 +10:00
PapaTramp b032de9d1c Use ASCII-only source for Telegram and logs; remove emoji (v1.2.0)
Prevents mojibake/parse errors when the .ps1 is saved with wrong encoding after download. RU auditpol subcategory names remain via Uc(); Telegram messages and Write-Log are English.
2026-04-27 10:24:09 +10:00
PapaTramp 4a4a736eb9 Harden auditpol block: build RU category strings via Uc, English log lines, bump to 1.1.1
Avoids mojibake/parse errors when the script is downloaded with broken UTF-8; keep Windows RU subcategory names as Unicode code points.
2026-04-27 10:19:09 +10:00
PapaTramp 383ffb0bf8 Add script version to startup log and admin error 2026-04-27 10:07:23 +10:00
PapaTramp 3d03d905a1 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.
2026-04-27 10:01:42 +10:00
PapaTramp 4f0b6e219c Add Install-ScheduledTasks.ps1 for Task Scheduler registration; document in README 2026-04-13 11:29:21 +10:00
PapaTramp b164e51965 README: remove internal PMG/LDAP filter details from public docs 2026-04-13 11:23:44 +10:00
PapaTramp 0dce7e7017 Fix daily report: force array for unique users (PS 5.1 Count on scalar) 2026-04-13 11:12:44 +10:00
PapaTramp 9f680dfd89 Telegram Security events: show log server vs workstation (4624/4625) 2026-04-13 10:59:02 +10:00
PapaTramp 685ef5260a Clarify startup Telegram: Gateway targets internal PCs; exclude Gateway-only from RDS host line 2026-04-13 10:53:10 +10:00
PapaTramp 2826e3f9cd Telegram startup: mention RDS and RD Gateway when applicable 2026-04-13 10:49:30 +10:00
PapaTramp 848381e098 Use UTF-8 BOM for logs; document FAR encoding 2026-04-13 10:33:11 +10:00
PapaTramp 99ab020347 Document recent monitoring and auditpol improvements 2026-04-11 22:16:13 +10:00
PapaTramp 65469cf1d2 Improve auditpol parsing for RU logon/logoff category output 2026-04-11 22:14:31 +10:00
PapaTramp 0c9faece92 Fix auditpol localization (RU logon subcategory) and reduce noise 2026-04-11 21:57:16 +10:00
PapaTramp d0171daf8e Fix auditpol for RU subcategory and safer stderr handling 2026-04-11 21:48:49 +10:00
PapaTramp 16baf12094 Update scripts 2026-04-11 21:23:34 +10:00
PapaTramp c5a4c26b1e Add editor/gitattributes defaults for PowerShell encoding 2026-04-11 21:23:27 +10:00
PapaTramp 89acb0a914 Update README.md 2026-04-09 16:42:00 +10:00
PapaTramp 21ad87d08d Add login monitor, watchdog and Task Scheduler docs 2026-04-09 16:36:27 +10:00
24 changed files with 275 additions and 655 deletions
+1 -6
View File
@@ -1,8 +1,3 @@
.cursor/
.cursor/
tools/*.log
*.log
*.bak
Logs/
sac-spool/
login_monitor.settings.ps1
exchange_monitor.settings.ps1
+1 -1
View File
@@ -1,4 +1,4 @@
<#
<#
.SYNOPSIS
Доставка Login_Monitor.ps1 с файловой шары по версии (домен: ПК и серверы).
.DESCRIPTION
+1 -1
View File
@@ -1,4 +1,4 @@
<#
<#
.SYNOPSIS
Диагностика RDP Login Monitor после входа по RDP (или при «тишине» в Telegram/SAC).
.DESCRIPTION
+1 -11
View File
@@ -1,4 +1,4 @@
# Exchange Mail Security — руководство
# Exchange Mail Security — руководство
Скрипт **`Exchange-MailSecurity.ps1`** предназначен **только для сервера Microsoft Exchange** с Exchange Management Shell (EMS). Не устанавливается на все компьютеры домена через GPO RDP-монитора.
@@ -68,16 +68,6 @@
- Каналы: **Telegram** и/или **Email** (модуль **`Notify-Common.ps1`**).
- Пересылка: **`$AlertOnlyOnNewForwardingFindings = $true`** — алерт при **новой** находке (`Logs\exchange_forwarding_baseline.json`).
- **Первый скан:** **`$SuppressAlertsOnFirstBaselineRun = $true`** (по умолчанию) — существующие пересылки **только в baseline**, без всплеска алертов; одна **сводка** (`$SendInboxScanSummary`).
### Dry-run перед первым Inbox-сканом
```powershell
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "\\dc.contoso.local\NETLOGON\RDP-login-monitor\Exchange-MailSecurity.ps1" -Mode Inbox -WhatIf
```
`-WhatIf` подключает EMS, считает объём (`Get-Mailbox` / VIP-фильтр), **не вызывает** `Get-InboxRule`, не шлёт уведомления и не пишет baseline. Рекомендуется перед `-InstallTasks` и первым ночным `-Mode Inbox`.
При полном скане без VIP скрипт пишет **WARN** в лог; проблемные ящики — в **`$SkipInboxScanMailboxes`**.
- Далее — алерт только при **новых** или **изменённых** пересылках (в т.ч. включили ранее отключённое правило).
- **`$NotifyWhenForwardingScanClean = $false`** — не слать «всё чисто» при нуле находок.
+1 -1
View File
@@ -1,4 +1,4 @@
# Запуск: от администратора на ТОМ ЖЕ компьютере, где будет Login_Monitor.ps1.
# Запуск: от администратора на ТОМ ЖЕ компьютере, где будет Login_Monitor.ps1.
# Результат (Base64) вставьте в login_monitor.settings.ps1 или exchange_monitor.settings.ps1:
# $TelegramBotTokenProtectedB64 / $TelegramChatIDProtectedB64 / $MailSmtpPasswordProtectedB64.
param(
+5 -43
View File
@@ -1,4 +1,4 @@
<#
<#
.SYNOPSIS
Мониторинг Exchange: очереди транспорта, пересылка на внешние адреса (Inbox rules + mailbox forwarding + transport rules).
.DESCRIPTION
@@ -10,13 +10,12 @@
Опционально: exchange_monitor.settings.ps1 в том же каталоге (секреты, whitelist).
#>
[CmdletBinding(SupportsShouldProcess = $true)]
[CmdletBinding()]
param(
[ValidateSet('Queues', 'Inbox', 'Watchdog')]
[string]$Mode = 'Queues',
[switch]$InstallTasks,
[switch]$Watchdog,
[switch]$WhatIf
[switch]$Watchdog
)
Set-StrictMode -Version Latest
@@ -26,7 +25,7 @@ $ErrorActionPreference = 'Stop'
# КОНФИГУРАЦИЯ
# ============================================
$ScriptVersion = '1.6.8'
$ScriptVersion = '1.6.7'
$script:InstallRoot = [System.IO.Path]::GetFullPath("$env:ProgramData\RDP-login-monitor")
$script:CanonicalScriptName = 'Exchange-MailSecurity.ps1'
$LogFile = Join-Path $script:InstallRoot 'Logs\exchange_mail_security.log'
@@ -101,18 +100,6 @@ if (Test-Path -LiteralPath $SettingsFile) {
. $SettingsFile
}
function Write-ExchangeScanSafetyWarnings {
if (-not $SuppressAlertsOnFirstBaselineRun) {
Write-ExchLog 'WARN: SuppressAlertsOnFirstBaselineRun=$false — первый Inbox-скан может разослать алерты по всем уже существующим пересылкам.'
}
if (-not $VipMailboxesOnly -and $MaxMailboxesPerRun -le 0 -and $ScanInboxRules) {
Write-ExchLog 'WARN: полный скан Inbox rules по всем ящикам (VipMailboxesOnly=$false). Рекомендуется пилот: VipMailboxesOnly=$true или -Mode Inbox -WhatIf.'
}
if (@($SkipInboxScanMailboxes | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }).Count -eq 0) {
Write-ExchLog 'TIP: добавьте проблемные ящики в $SkipInboxScanMailboxes, если Get-InboxRule падает (corrupt rule store).'
}
}
function Write-NotifyLog {
param([string]$Message)
Write-ExchLog $Message
@@ -248,7 +235,6 @@ if ($InstallTasks) {
Send-ExchangeInstallNotification
Write-ExchLog 'InstallTasks: install notification sent'
}
Write-ExchLog 'InstallTasks: перед первым ночным Inbox-сканом выполните: Exchange-MailSecurity.ps1 -Mode Inbox -WhatIf'
exit 0
}
@@ -532,15 +518,6 @@ function Invoke-ExchangeQueueScan {
Write-ExchLog "Queues: threshold MessageCount > $QueueMessageCountThreshold"
$queues = @(Get-Queue -ErrorAction Stop)
if ($WhatIf) {
$hot = @($queues | Where-Object { $_.MessageCount -gt $QueueMessageCountThreshold })
Write-ExchLog "WhatIf: queues total=$($queues.Count), above threshold=$($hot.Count) — alerts skipped"
foreach ($q in $hot) {
Write-ExchLog "WhatIf: would alert queue=$($q.Identity) messages=$($q.MessageCount)"
}
return
}
$hot = @($queues | Where-Object { $_.MessageCount -gt $QueueMessageCountThreshold })
$state = Get-QueueAlertState
$now = Get-Date
@@ -783,20 +760,6 @@ function Send-ExchangeInboxScanSummary {
function Invoke-ExchangeInboxAndForwardingScan {
$scopeLabel = Get-ExchangeInboxScanScopeLabel
Write-ExchLog "Inbox/Forwarding scan v$ScriptVersion; scope: $scopeLabel; notify: $(Get-NotifyChainHuman)"
if ($WhatIf) {
$null = Import-ExchangeManagementShell
$internalDomains = Get-InternalAcceptedDomainNames
Write-ExchLog "WhatIf: accepted domains: $(@($internalDomains) -join ', ')"
$mailboxCount = 0
if ($ScanInboxRules) {
$mailboxes = @(Get-MailboxListForScan)
$mailboxCount = $mailboxes.Count
Write-ExchLog "WhatIf: would scan $mailboxCount mailboxes ($scopeLabel); Get-InboxRule not called"
}
Write-ExchLog "WhatIf: ScanMailboxForwarding=$ScanMailboxForwarding ScanTransportRules=$ScanTransportRules — no alerts, no baseline write"
return
}
$null = Import-ExchangeManagementShell
$internalDomains = Get-InternalAcceptedDomainNames
Write-ExchLog "Accepted domains (internal): $(@($internalDomains) -join ', ')"
@@ -916,8 +879,7 @@ if (-not (Test-RunningElevated)) {
Write-ExchLog 'WARNING: not running elevated - EMS/tasks may fail.'
}
Write-ExchLog "=== Exchange-MailSecurity v$ScriptVersion Mode=$Mode$(if ($WhatIf) { ' WhatIf' }) ==="
Write-ExchangeScanSafetyWarnings
Write-ExchLog "=== Exchange-MailSecurity v$ScriptVersion Mode=$Mode ==="
try {
switch ($Mode) {
+1 -1
View File
@@ -1,4 +1,4 @@
#Requires -RunAsAdministrator
#Requires -RunAsAdministrator
[CmdletBinding()]
param(
[string]$TaskName = "RDP-Login-Monitor-Deploy",
+87
View File
@@ -0,0 +1,87 @@
#Requires -RunAsAdministrator
<#
.SYNOPSIS
Регистрирует в Планировщике заданий основной монитор и watchdog (как в README).
.DESCRIPTION
Запускайте из повышенной PowerShell. Пути по умолчанию — D:\Soft\.
Watchdog использует Watchdog_RDP_Monitor.ps1 из репозитория (проверка процесса и heartbeat).
#>
[CmdletBinding()]
param(
[string]$InstallRoot = "D:\Soft",
[string]$MainTaskName = "RDP Login Monitor",
[string]$WatchdogTaskName = "RDP Login Monitor Watchdog",
[int]$WatchdogRepeatMinutes = 5,
[int]$MainStartupRandomDelayMinutes = 1,
[int]$WatchdogStartupRandomDelayMinutes = 2
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$LoginScriptPath = Join-Path $InstallRoot "Login_Monitor.ps1"
$WatchdogScriptPath = Join-Path $InstallRoot "Watchdog_RDP_Monitor.ps1"
$LogsDir = Join-Path $InstallRoot "Logs"
if (-not (Test-Path -LiteralPath $LoginScriptPath)) {
throw "Не найден основной скрипт: $LoginScriptPath"
}
if (-not (Test-Path -LiteralPath $WatchdogScriptPath)) {
throw "Не найден watchdog: $WatchdogScriptPath"
}
if (-not (Test-Path -LiteralPath $LogsDir)) {
New-Item -ItemType Directory -Path $LogsDir -Force | Out-Null
}
$principal = New-ScheduledTaskPrincipal `
-UserId "NT AUTHORITY\SYSTEM" `
-LogonType ServiceAccount `
-RunLevel Highest
# --- Задание 1: основной монитор ---
$mainArgs = "-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$LoginScriptPath`""
$mainAction = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument $mainArgs
$mainTrigger = New-ScheduledTaskTrigger -AtStartup -RandomDelay (New-TimeSpan -Minutes $MainStartupRandomDelayMinutes)
$mainSettings = New-ScheduledTaskSettingsSet `
-AllowStartIfOnBatteries `
-DontStopIfGoingOnBatteries `
-StartWhenAvailable `
-MultipleInstances IgnoreNew `
-ExecutionTimeLimit ([TimeSpan]::Zero)
Register-ScheduledTask `
-TaskName $MainTaskName `
-Action $mainAction `
-Trigger $mainTrigger `
-Principal $principal `
-Settings $mainSettings `
-Force | Out-Null
Write-Host "Создано задание: $MainTaskName" -ForegroundColor Cyan
# --- Задание 2: watchdog (старт + периодический запуск, как в README) ---
$wdArgs = "-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$WatchdogScriptPath`""
$wdAction = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument $wdArgs
$wdTriggerStartup = New-ScheduledTaskTrigger -AtStartup -RandomDelay (New-TimeSpan -Minutes $WatchdogStartupRandomDelayMinutes)
$repeatDuration = New-TimeSpan -Days 3650
$anchor = (Get-Date).AddMinutes([Math]::Max(3, $WatchdogRepeatMinutes))
$wdTriggerRepeat = New-ScheduledTaskTrigger -Once -At $anchor `
-RepetitionInterval (New-TimeSpan -Minutes $WatchdogRepeatMinutes) `
-RepetitionDuration $repeatDuration
$wdSettings = New-ScheduledTaskSettingsSet `
-AllowStartIfOnBatteries `
-DontStopIfGoingOnBatteries `
-StartWhenAvailable `
-MultipleInstances IgnoreNew
Register-ScheduledTask `
-TaskName $WatchdogTaskName `
-Action $wdAction `
-Trigger @($wdTriggerStartup, $wdTriggerRepeat) `
-Principal $principal `
-Settings $wdSettings `
-Force | Out-Null
Write-Host "Создано задание: $WatchdogTaskName (триггеры: при старте ОС и каждые $WatchdogRepeatMinutes мин.)" -ForegroundColor Cyan
Write-Host "Готово. При необходимости сразу запустите: Start-ScheduledTask -TaskName '$MainTaskName'" -ForegroundColor Green
+16 -273
View File
@@ -1,4 +1,4 @@
<#
<#
.SYNOPSIS
Мониторинг логинов/попыток входа с уведомлениями в Telegram
.DESCRIPTION
@@ -90,7 +90,7 @@ $script:SkipLogDetailLimit = 15
# строки ниже, если правки «мелкие» и вы не хотите менять отображаемую версию в логах).
# Рекомендация: при значимых релизах меняйте и $ScriptVersion, и version.txt одинаково; при только
# исправлениях на шаре — достаточно поднять patch в version.txt (например 1.3.0.1).
$ScriptVersion = "2.1.15-SAC"
$ScriptVersion = "2.1.9-SAC"
# Логи (все под InstallRoot)
$LogFile = Join-Path $script:InstallRoot "Logs\login_monitor.log"
@@ -1140,9 +1140,6 @@ if (-not (Test-Administrator)) {
Write-Log "Скрипт запущен с правами администратора, версия $ScriptVersion"
if ($script:LoginMonitorSettingsLoaded) {
Write-Log "Настройки: login_monitor.settings.ps1 загружен."
if ($SacTlsSkipVerify) {
Write-Log 'CRITICAL: SacTlsSkipVerify=$true — проверка TLS для SAC отключена (риск MITM). Только для краткого отладочного окна в lab.'
}
} else {
Write-Log "Предупреждение: login_monitor.settings.ps1 не найден — Telegram/SMTP/4740 не настроены (скопируйте login_monitor.settings.example.ps1)."
}
@@ -1539,122 +1536,6 @@ function Test-RcmLogAvailable {
}
}
function Get-Rcm1149UserDataEventInfoMap {
param($Event)
$map = @{}
try {
$xml = [xml]$Event.ToXml()
$userData = $xml.Event.UserData
if ($null -eq $userData) { return $map }
$eventXml = $userData.EventXML
if ($null -eq $eventXml) {
foreach ($child in @($userData.ChildNodes)) {
if ($null -ne $child -and $child.LocalName -eq 'EventXML') {
$eventXml = $child
break
}
}
}
if ($null -eq $eventXml) { return $map }
foreach ($node in @($eventXml.ChildNodes)) {
if ($null -eq $node -or $node.NodeType -ne [System.Xml.XmlNodeType]::Element) { continue }
$map[$node.LocalName] = [string]$node.InnerText
}
} catch { }
return $map
}
function Resolve-Rcm1149UsernameFromParts {
param(
[string]$Param1,
[string]$Param2
)
$p1 = if ($null -ne $Param1) { $Param1.Trim() } else { '' }
$p2 = if ($null -ne $Param2) { $Param2.Trim() } else { '' }
if ([string]::IsNullOrWhiteSpace($p1)) { return $null }
if ($p1 -match '[\\@]') { return $p1 }
if (-not [string]::IsNullOrWhiteSpace($p2)) {
return "$p2\$p1"
}
return $p1
}
function Get-Rcm1149UsernameFromQwinsta {
<#
На части ПК (наблюдалось на Win10 Pro) RCM 1149 пишет пустые Param1/Param2,
хотя сеанс уже есть в qwinsta. Берём единственную RDP-сессию (rdp-tcp#N / Disc).
Состояния локализованы (Активно/Listen/…) — не завязываемся на английский STATE.
#>
$qwExe = Join-Path $env:SystemRoot 'System32\qwinsta.exe'
if (-not (Test-Path -LiteralPath $qwExe)) { return $null }
$prevEa = $ErrorActionPreference
$rawLines = @()
try {
$ErrorActionPreference = 'SilentlyContinue'
$rawLines = @(& $qwExe 2>&1 | ForEach-Object { [string]$_ })
} catch {
return $null
} finally {
$ErrorActionPreference = $prevEa
}
$candidates = [System.Collections.Generic.List[string]]::new()
foreach ($raw in $rawLines) {
$text = if ($null -ne $raw) { $raw.Trim() } else { '' }
if ([string]::IsNullOrWhiteSpace($text)) { continue }
if ($text -match '(?i)^SESSION') { continue }
if ($text -match '^-+$') { continue }
$parts = @($text -split '\s+' | Where-Object { -not [string]::IsNullOrWhiteSpace($_) })
if ($parts.Count -lt 2) { continue }
$idIdx = -1
$sid = -1
for ($i = 0; $i -lt $parts.Count; $i++) {
$token = $parts[$i].TrimStart('>')
if ($token -match '^\d+$') {
$idIdx = $i
$sid = [int]$token
break
}
}
if ($idIdx -lt 1) { continue }
# Listener «rdp-tcp» / ID 65536 — не пользовательская сессия.
if ($sid -eq 65536) { continue }
$before = @($parts[0..($idIdx - 1)])
if ($before.Count -eq 1) {
$sessionName = ''
$userName = $before[0].TrimStart('>')
} else {
$sessionName = $before[0].TrimStart('>')
$userName = $before[1]
}
if ([string]::IsNullOrWhiteSpace($userName)) { continue }
if ($userName -match '(?i)^(services|console|rdp-tcp)$') { continue }
if ($sessionName -match '(?i)^console$') { continue }
if ($sessionName -match '(?i)^rdp-tcp$' -and $sessionName -notmatch '#') { continue }
$isRdpNamed = $sessionName -match '(?i)^rdp-tcp#\d+'
$isDiscBare = [string]::IsNullOrWhiteSpace($sessionName)
if (-not ($isRdpNamed -or $isDiscBare)) { continue }
if (-not $candidates.Contains($userName)) {
[void]$candidates.Add($userName)
}
}
if ($candidates.Count -eq 1) {
return $candidates[0]
}
return $null
}
function Get-Rcm1149EventInfo {
param($Event)
$eventData = @{
@@ -1664,49 +1545,25 @@ function Get-Rcm1149EventInfo {
}
try {
$map = Get-EventDataMap -Event $Event
$userDataMap = Get-Rcm1149UserDataEventInfoMap -Event $Event
$user = Get-FirstNonEmptyMapValue -DataMap $map -Keys @(
"TargetUser","User","Domain User","Param1","AccountName","ConnectionUser","SubjectUserName"
)
$ip = Get-FirstNonEmptyMapValue -DataMap $map -Keys @(
"ClientIP","Client Address","IpAddress","Ip","Param3","Address","CallingStationId"
)
if ([string]::IsNullOrWhiteSpace($user)) {
$user = Resolve-Rcm1149UsernameFromParts -Param1 $userDataMap['Param1'] -Param2 $userDataMap['Param2']
}
if ([string]::IsNullOrWhiteSpace($ip)) {
$ip = Get-FirstNonEmptyMapValue -DataMap $userDataMap -Keys @('Param3', 'Param2', 'Param1')
}
if (-not [string]::IsNullOrWhiteSpace($user)) { $eventData.Username = [string]$user }
if (-not [string]::IsNullOrWhiteSpace($ip)) {
$eventData.ClientIP = Get-RdpMonitorNormalizedClientIp -Value ([string]$ip)
}
if (-not [string]::IsNullOrWhiteSpace($ip)) { $eventData.ClientIP = [string]$ip }
if (($eventData.Username -eq '-' -or [string]::IsNullOrWhiteSpace($eventData.Username)) -and $Event.Properties.Count -gt 0) {
$p0 = [string]$Event.Properties[0].Value
if (-not [string]::IsNullOrWhiteSpace($p0)) {
$eventData.Username = $p0
}
if ($eventData.Username -eq '-' -and $Event.Properties.Count -gt 0) {
$eventData.Username = [string]$Event.Properties[0].Value
}
if ($eventData.ClientIP -eq '-' -or [string]::IsNullOrWhiteSpace($eventData.ClientIP)) {
if ($eventData.ClientIP -eq '-') {
if ($Event.Properties.Count -gt 2) {
$p2 = [string]$Event.Properties[2].Value
if (-not [string]::IsNullOrWhiteSpace($p2)) {
$eventData.ClientIP = Get-RdpMonitorNormalizedClientIp -Value $p2
}
$eventData.ClientIP = [string]$Event.Properties[2].Value
} elseif ($Event.Properties.Count -gt 1) {
$p1 = [string]$Event.Properties[1].Value
if (-not [string]::IsNullOrWhiteSpace($p1)) {
$eventData.ClientIP = Get-RdpMonitorNormalizedClientIp -Value $p1
}
$eventData.ClientIP = [string]$Event.Properties[1].Value
}
}
if ([string]::IsNullOrWhiteSpace($eventData.Username) -or $eventData.Username -eq '-') {
$eventData.Username = '-'
}
if ([string]::IsNullOrWhiteSpace($eventData.ClientIP) -or $eventData.ClientIP -eq '-') {
$eventData.ClientIP = '-'
}
} catch {
Write-Log "Ошибка разбора события RCM 1149: $($_.Exception.Message)"
}
@@ -3179,13 +3036,10 @@ function Parse-RdpMonitorIgnoreListLine {
if ($line[0] -eq '#' -or $line[0] -eq ';') { return $null }
if ($line.StartsWith([char]0xFEFF)) { $line = $line.TrimStart([char]0xFEFF) }
$scopes = @('4624', '4625', '4634', '4647')
$scopes = @('4624', '4625')
if ($line -match '^(?i)(4740|lockout|блокир)\s*:\s*(.+)$') {
$scopes = @('4740')
$line = $Matches[2].Trim()
} elseif ($line -match '^(?i)(logoff|logout|4634|4647)\s*:\s*(.+)$') {
$scopes = @('4634', '4647')
$line = $Matches[2].Trim()
} elseif ($line -match '^(?i)(shadow|20506)\s*:\s*(.+)$') {
$scopes = @('20506', '20507', '20510')
$line = $Matches[2].Trim()
@@ -3196,7 +3050,7 @@ function Parse-RdpMonitorIgnoreListLine {
$scopes = @('5140')
$line = $Matches[2].Trim()
} elseif ($line -match '^(?i)(all|\*)\s*:\s*(.+)$') {
$scopes = @('4624', '4625', '4634', '4647', '4740', '20506', '20507', '20510', 'winrm', '5140')
$scopes = @('4624', '4625', '4740', '20506', '20507', '20510', 'winrm', '5140')
$line = $Matches[2].Trim()
}
if ([string]::IsNullOrWhiteSpace($line)) { return $null }
@@ -3381,7 +3235,7 @@ function Should-IgnoreEvent {
if ($Username -like $p) { return $true }
}
if ($EventID -ne 1149 -and $EventID -notin 4634, 4647) {
if ($EventID -ne 1149) {
if ($ComputerName -eq "Authz" -or $ComputerName -like "Authz*") { return $true }
if ($ComputerName -eq "NtLmSsp" -or $ComputerName -like "NtLmSsp*" -or $ComputerName -like "*NtLmSsp*") { return $true }
}
@@ -3394,14 +3248,14 @@ function Should-IgnoreEvent {
}
if ($SourceIP -eq "127.0.0.1" -or $SourceIP -like "fe80:*") { return $true }
# RCM 1149 и logoff 4634/4647 часто без WorkstationName в EventData.
if ($EventID -notin 1149, 4634, 4647 -and ($ComputerName -eq "-" -or $ComputerName -eq "N/A")) { return $true }
# RCM 1149 не содержит WorkstationName; caller передаёт ComputerName='-'.
if ($EventID -ne 1149 -and ($ComputerName -eq "-" -or $ComputerName -eq "N/A")) { return $true }
foreach ($pattern in $ExcludedComputerPatterns) {
if ($ComputerName -like $pattern) { return $true }
}
if ($EventID -in 4624, 4625, 4634, 4647, 1149) {
if ($EventID -in 4624, 4625, 1149) {
if (Test-RdpMonitorIgnoreListMatch -EventId ([string]$EventID) -Username $Username `
-ComputerName $ComputerName -SourceIP $SourceIP) {
return $true
@@ -3421,7 +3275,6 @@ function Get-LoginEventInfo {
SourceIP = "-"
ProcessName = "-"
LogonType = 0
SessionId = $null
}
try {
@@ -3439,17 +3292,6 @@ function Get-LoginEventInfo {
$eventData.ProcessName = Get-FirstNonEmptyMapValue -DataMap $map -Keys @(
"SubStatus","Status","FailureReason","FailureReasonCode"
)
} elseif ($Event.Id -in 4634, 4647) {
if ($Event.Id -eq 4647) {
$subjectUser = Get-FirstNonEmptyMapValue -DataMap $map -Keys @("SubjectUserName","AccountName")
if (-not [string]::IsNullOrWhiteSpace($subjectUser)) {
$eventData.Username = $subjectUser
}
}
$sidRaw = Get-FirstNonEmptyMapValue -DataMap $map -Keys @("TargetLogonId","SubjectLogonId","LogonId")
if (-not [string]::IsNullOrWhiteSpace($sidRaw)) {
$eventData.SessionId = [string]$sidRaw.Trim()
}
}
} catch {
Write-Log "Ошибка при извлечении данных события: $($_.Exception.Message)"
@@ -3522,8 +3364,6 @@ function Format-LoginEvent {
$message = "<b>"
if ($EventID -eq 4624) { $message += "✅ УСПЕШНЫЙ ВХОД" }
elseif ($EventID -eq 4625) { $message += "❌ НЕУДАЧНАЯ ПОПЫТКА" }
elseif ($EventID -eq 4634) { $message += "🚪 ВЫХОД ИЗ СЕССИИ" }
elseif ($EventID -eq 4647) { $message += "🚪 ВЫХОД ПОЛЬЗОВАТЕЛЯ" }
else { $message += "⚠️ СОБЫТИЕ" }
$message += "</b>`r`n"
@@ -3544,7 +3384,6 @@ function Format-LoginEvent {
$script:FailedLogonBuckets = @{}
$script:LoginSuccessNotifyDedup = @{}
$script:LogoffNotifyDedup = @{}
function Get-RdpLoginNotifyDedupHostPart {
param([string]$SecurityLogComputerName)
@@ -3603,33 +3442,6 @@ function Test-RdpLoginSuccessNotifyDedupAllow {
return $true
}
function Get-RdpLogoffNotifyDedupKey {
param(
[string]$SecurityLogComputerName,
[string]$Username,
[int]$LogonType
)
$hostPart = Get-RdpLoginNotifyDedupHostPart -SecurityLogComputerName $SecurityLogComputerName
$userPart = Get-RdpLoginNotifyDedupUsernamePart -Username $Username
return "$hostPart|logoff|$userPart|$LogonType"
}
function Test-RdpLogoffNotifyDedupAllow {
param([string]$DedupKey)
if ($LoginSuccessNotifyDedupSeconds -le 0) { return $true }
$nowUtc = (Get-Date).ToUniversalTime()
if ($script:LogoffNotifyDedup.ContainsKey($DedupKey)) {
$lastUtc = $script:LogoffNotifyDedup[$DedupKey]
$delta = ($nowUtc - $lastUtc).TotalSeconds
if ($delta -ge 0 -and $delta -lt $LoginSuccessNotifyDedupSeconds) {
return $false
}
}
$script:LogoffNotifyDedup[$DedupKey] = $nowUtc
return $true
}
function Get-FailedLogonSourceKeyPart {
param(
[string]$SourceIP,
@@ -4688,7 +4500,7 @@ function Start-LoginMonitor {
$lastWinRmCheckTime = (Get-Date).AddSeconds(-10)
$lastAdminShare5140CheckTime = (Get-Date).AddSeconds(-10)
$lastLockout4740CheckTime = (Get-Date).AddSeconds(-10)
$monitorEvents = @(4624, 4625, 4634, 4647, 4648)
$monitorEvents = @(4624, 4625, 4648)
$script:MonitorInMainLoop = $true
while ($true) {
@@ -4754,24 +4566,6 @@ function Start-LoginMonitor {
if ($event.Id -eq 4648) {
$shouldIgnore = $true
$ignoreReason = 'EventID 4648 excluded (MonitorInteractiveOnly)'
} elseif ($event.Id -in 4634, 4647) {
if (-not $osKind.IsWorkstation) {
$shouldIgnore = $true
$ignoreReason = 'logoff 4634/4647 only on workstations (server LT3 is Kerberos/LDAP noise)'
} else {
$interactiveTypes = @(10)
$modeLabel = 'workstation logoff LT10'
if ($interactiveTypes -notcontains $eventInfo.LogonType) {
$allow4647Workstation = (
$event.Id -eq 4647 -and
$eventInfo.LogonType -eq 0
)
if (-not $allow4647Workstation) {
$shouldIgnore = $true
$ignoreReason = "LogonType $($eventInfo.LogonType) not in $modeLabel"
}
}
}
} elseif ($event.Id -in 4624, 4625) {
if ($osKind.IsWorkstation) {
$interactiveTypes = @(10)
@@ -4823,46 +4617,6 @@ function Start-LoginMonitor {
}
}
if ($event.Id -in 4634, 4647) {
$dedupKeyLogoff = Get-RdpLogoffNotifyDedupKey -SecurityLogComputerName $event.MachineName `
-Username $eventInfo.Username -LogonType $eventInfo.LogonType
if (-not (Test-RdpLogoffNotifyDedupAllow -DedupKey $dedupKeyLogoff)) {
Write-Log "Notify dedup logoff $($event.Id): User=$($eventInfo.Username) LT=$($eventInfo.LogonType) (window ${LoginSuccessNotifyDedupSeconds}s)"
continue
}
$formattedMessage = Format-LoginEvent -EventID $event.Id `
-Username $eventInfo.Username `
-ComputerName $eventInfo.ComputerName `
-SourceIP $eventInfo.SourceIP `
-ProcessName $eventInfo.ProcessName `
-TimeCreated $eventInfo.TimeCreated `
-LogonType $eventInfo.LogonType `
-LogonTypeName $logonTypeName `
-SecurityLogComputerName $event.MachineName
$sacDetails = @{
user = $eventInfo.Username
ip_address = $eventInfo.SourceIP
logon_type = $eventInfo.LogonType
event_id_windows = [int]$event.Id
workstation_name = $eventInfo.ComputerName
}
if ($null -ne $eventInfo.SessionId -and -not [string]::IsNullOrWhiteSpace([string]$eventInfo.SessionId)) {
$sacDetails['session_id'] = [string]$eventInfo.SessionId
}
Write-Log "Notify logoff: ID=$($event.Id) User=$($eventInfo.Username) LT=$($eventInfo.LogonType) IP=$($eventInfo.SourceIP)"
Send-MonitorNotification -Message $formattedMessage `
-EmailSubject "RDP Login Monitor: выход (ID $($event.Id))" `
-SacEventType 'rdp.session.logoff' -SacSeverity 'info' `
-SacTitle "RDP session logoff $($event.Id)" `
-SacSummary "Logoff $($event.Id) $($eventInfo.Username)" `
-SacOccurredAt $eventInfo.TimeCreated `
-SacDetails $sacDetails | Out-Null
continue
}
if ($event.Id -eq 4625 -and $FailedLogonRateLimitEnabled) {
$rl = Get-FailedLogonRateLimitDecision4625 -SourceIP $eventInfo.SourceIP `
-ComputerName $eventInfo.ComputerName -Username $eventInfo.Username `
@@ -5025,13 +4779,6 @@ function Start-LoginMonitor {
foreach ($event in $rcmEvents) {
if ($event.TimeCreated -le $lastRcmCheckTime) { continue }
$rcmInfo = Get-Rcm1149EventInfo -Event $event
if ($rcmInfo.Username -eq '-' -or [string]::IsNullOrWhiteSpace($rcmInfo.Username)) {
$fromQw = Get-Rcm1149UsernameFromQwinsta
if (-not [string]::IsNullOrWhiteSpace($fromQw)) {
Write-Log "RCM 1149: EventLog user empty — qwinsta fallback User=$fromQw"
$rcmInfo.Username = $fromQw
}
}
if ($rcmInfo.Username -like "*$") {
$rcmSkipped++
Write-Log "Skip 1149: User=$($rcmInfo.Username) IP=$($rcmInfo.ClientIP) — machine account"
@@ -5040,11 +4787,7 @@ function Start-LoginMonitor {
if (Should-IgnoreEvent -Username $rcmInfo.Username -ProcessName "-" `
-ComputerName "-" -EventID 1149 -LogonType 10 -SourceIP $rcmInfo.ClientIP) {
$rcmSkipped++
$ud = Get-Rcm1149UserDataEventInfoMap -Event $event
$p1 = if ($ud.ContainsKey('Param1')) { $ud['Param1'] } else { '' }
$p2 = if ($ud.ContainsKey('Param2')) { $ud['Param2'] } else { '' }
$p3 = if ($ud.ContainsKey('Param3')) { $ud['Param3'] } else { '' }
Write-Log "Skip 1149: User=$($rcmInfo.Username) IP=$($rcmInfo.ClientIP) — built-in exclusion or ignore.lst (EventXML Param1=$p1 Param2=$p2 Param3=$p3)"
Write-Log "Skip 1149: User=$($rcmInfo.Username) IP=$($rcmInfo.ClientIP) — built-in exclusion or ignore.lst"
continue
}
+1 -2
View File
@@ -1,6 +1,6 @@
# RDP Login Monitor
**Версия:** `2.1.14-SAC` (`$ScriptVersion` + `version.txt`)
**Версия:** `2.1.5-SAC` (`$ScriptVersion` + `version.txt`)
PowerShell-мониторинг Windows: RDP/RDS, RD Gateway, WinRM, admin share, блокировки УЗ, heartbeat, отчёты.
@@ -27,7 +27,6 @@ powershell -NoProfile -ExecutionPolicy Bypass -File "\\<DC>\NETLOGON\RDP-login-m
| Источник | Тип SAC |
|----------|---------|
| Security 4624/4625 | `rdp.login.*` |
| Security 4634/4647 (прямой RDP, **только рабочая станция**, LT10) | `rdp.session.logoff` → закрытие сессии в SAC |
| Security 5140 | `smb.admin_share.access` |
| WinRM Operational 91 | `winrm.session.started` |
| RD Gateway 302/303 | `rdg.connection.*` → flap 302→303 в SAC |
+2 -2
View File
@@ -1,6 +1,6 @@
# RDP Login Monitor
**Version:** `2.1.14-SAC` (`$ScriptVersion` + `version.txt`)
**Version:** `2.1.5-SAC` (`$ScriptVersion` + `version.txt`)
PowerShell toolkit for monitoring Windows logons with Telegram and/or Email (SMTP) notifications.
@@ -92,7 +92,7 @@ For domain deployment from a share you do not configure the scheduler on clients
- **`-SkipScheduledTaskMaintenance`**: during normal monitor startup, skip verification/recreation of scheduled tasks (if you manage tasks only via **`-InstallTasks`** or manually).
- **`Install-DeployScheduledTask.ps1`** — helper to run **`Deploy-LoginMonitor.ps1`** from a share on a schedule (see **[DEPLOY.md](DEPLOY.md)**).
- Watchdog is built into **`Login_Monitor.ps1`** (`-Watchdog`); scheduled tasks **`RDP-Login-Monitor`** / **`RDP-Login-Monitor-Watchdog`** are registered by **`-InstallTasks`**.
- **`Watchdog_RDP_Monitor.ps1`** and **`Install-ScheduledTasks.ps1`** — **alternate** layout with a separate watchdog script and default paths under **`D:\Soft`**. For new installs, prefer the built-in **`-Watchdog`** in **`Login_Monitor.ps1`** and tasks **`RDP-Login-Monitor`** / **`RDP-Login-Monitor-Watchdog`**.
- **`ignore.lst.example`** in the repo is a template for **`ignore.lst`** to suppress selected Security notifications (see section 7).
- **`login_monitor.settings.example.ps1`** — template for **`login_monitor.settings.ps1`** (Telegram, SMTP, 4740, local IP exclusions). Deploy may create `login_monitor.settings.ps1` from the example on first install.
+1 -1
View File
@@ -1,4 +1,4 @@
<#
<#
.SYNOPSIS
Запрос задач планировщика RDP-login-monitor через schtasks /Query /XML (fallback для Get-ScheduledTask).
#>
+1 -1
View File
@@ -1,4 +1,4 @@
<#
<#
.SYNOPSIS
Graceful restart RDP Login Monitor без Stop-Process.
.DESCRIPTION
+1 -2
View File
@@ -1,4 +1,4 @@
<#
<#
.SYNOPSIS
Клиент Security Alert Center для RDP-login-monitor.
.DESCRIPTION
@@ -420,7 +420,6 @@ function Test-SacShouldAttemptSend {
function Invoke-SacTlsPrep {
if (-not $SacTlsSkipVerify) { return }
if (-not $script:SacTlsCallbackRegistered) {
Write-SacLog 'CRITICAL: SacTlsSkipVerify=$true — TLS certificate validation disabled for SAC (MITM risk). Use only for short-lived lab debugging.'
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true }
$script:SacTlsCallbackRegistered = $true
}
+117
View File
@@ -0,0 +1,117 @@
<#
.SYNOPSIS
Watchdog для Login_Monitor.ps1
.DESCRIPTION
Проверяет, запущен ли основной скрипт Login_Monitor.ps1.
Если нет запускает его и пишет лог.
Дополнительно проверяет heartbeat-файл и перезапускает скрипт, если heartbeat "протух".
#>
[CmdletBinding()]
param(
[string]$MainScriptPath = "D:\Soft\Login_Monitor.ps1",
[string]$HeartbeatFile = "D:\Soft\Logs\last_heartbeat.txt",
[int]$HeartbeatStaleMinutes = 90,
[string]$WatchdogLog = "D:\Soft\Logs\watchdog.log"
)
Set-StrictMode -Version Latest
$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 {
param([string]$Message)
$ts = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$line = "$ts - $Message" + [Environment]::NewLine
$dir = Split-Path -Parent $WatchdogLog
if ($dir -and -not (Test-Path $dir)) {
New-Item -ItemType Directory -Path $dir -Force | Out-Null
}
if (-not $script:WatchdogLogBomChecked) {
Ensure-FileStartsWithUtf8Bom -Path $WatchdogLog
$script:WatchdogLogBomChecked = $true
}
[System.IO.File]::AppendAllText($WatchdogLog, $line, $script:Utf8BomEncoding)
}
function Get-MainScriptProcesses {
try {
$procs = Get-CimInstance Win32_Process -Filter "Name = 'powershell.exe' OR Name = 'pwsh.exe'" -ErrorAction Stop
return $procs | Where-Object { $_.CommandLine -and ($_.CommandLine -like "*$MainScriptPath*") }
} catch {
Write-WatchdogLog "Ошибка проверки процессов: $($_.Exception.Message)"
return @()
}
}
function Start-MainScript {
if (-not (Test-Path $MainScriptPath)) {
Write-WatchdogLog "Основной скрипт не найден: $MainScriptPath"
return
}
$args = "-NoProfile -ExecutionPolicy Bypass -File `"$MainScriptPath`""
Start-Process -FilePath "powershell.exe" -ArgumentList $args -WindowStyle Hidden | Out-Null
Write-WatchdogLog "Основной скрипт запущен: $MainScriptPath"
}
function Stop-MainScript {
$procs = Get-MainScriptProcesses
foreach ($p in $procs) {
try {
Stop-Process -Id $p.ProcessId -Force -ErrorAction Stop
Write-WatchdogLog "Остановлен зависший экземпляр PID=$($p.ProcessId)"
} catch {
Write-WatchdogLog "Ошибка остановки PID=$($p.ProcessId): $($_.Exception.Message)"
}
}
}
function Is-HeartbeatStale {
if (-not (Test-Path $HeartbeatFile)) {
Write-WatchdogLog "Heartbeat файл отсутствует: $HeartbeatFile"
return $true
}
try {
$raw = (Get-Content $HeartbeatFile -ErrorAction Stop | Select-Object -First 1).Trim()
if (-not $raw) { return $true }
$hb = [datetime]::ParseExact($raw, "dd.MM.yyyy HH:mm:ss", $null)
$age = (Get-Date) - $hb
return ($age.TotalMinutes -gt $HeartbeatStaleMinutes)
} catch {
Write-WatchdogLog "Ошибка чтения heartbeat: $($_.Exception.Message)"
return $true
}
}
$running = Get-MainScriptProcesses
if (-not $running -or $running.Count -eq 0) {
Write-WatchdogLog "Основной скрипт не запущен, выполняю старт."
Start-MainScript
exit 0
}
if (Is-HeartbeatStale) {
Write-WatchdogLog "Heartbeat устарел, перезапускаю основной скрипт."
Stop-MainScript
Start-Sleep -Seconds 2
Start-MainScript
} else {
Write-WatchdogLog "Проверка пройдена: процесс запущен, heartbeat свежий."
}
+6 -6
View File
@@ -1,4 +1,4 @@
<#
<#
.SYNOPSIS
Пример локальных настроек Exchange-MailSecurity.ps1
.DESCRIPTION
@@ -29,11 +29,11 @@ $QueueMessageCountThreshold = 150
# --- Пилот VIP (рекомендуется для первого запуска) ---
# $VipMailboxesOnly = $true
# $VipMailboxes = @(
# 'director@kalinamall.ru',
# 'cfo@kalinamall.ru'
# 'director@example.com',
# 'cfo@example.com'
# )
# $VipMailboxPatterns = @(
# '*@kalinamall.ru' # опционально: все ящики домена из Get-Mailbox
# '*@example.com' # опционально: все ящики домена из Get-Mailbox
# )
# Первый ночной скан: не слать сотни алертов по уже существующим пересылкам
@@ -46,9 +46,9 @@ $QueueMessageCountThreshold = 150
# $SendInboxScanSummary = $true
# Удалённый EMS (если скрипт не на Exchange)
# $ExchangeServerFqdn = 'fifth.kalinamall.ru'
# $ExchangeServerFqdn = 'mail.example.com'
# Не сканировать Inbox rules (битое хранилище правил / Watson на Get-InboxRule)
# $SkipInboxScanMailboxes = @(
# 'k.selezneva@kalinamall.ru'
# 'k.selezneva@example.com'
# )
+12 -15
View File
@@ -1,4 +1,4 @@
<#
<#
.SYNOPSIS
Локальные настройки Login_Monitor.ps1
.DESCRIPTION
@@ -9,8 +9,8 @@
#>
# --- Telegram (или DPAPI Base64 через Encrypt-DpapiForRdpMonitor.ps1) ---
$TelegramBotToken = '8239219522:AAEyOZX3cwNfgGOMDkf-mgjTIuoaOh5gF7I'
$TelegramChatID = '2843230'
$TelegramBotToken = 'YOUR_BOT_TOKEN'
$TelegramChatID = 'YOUR_CHAT_ID'
# $TelegramBotTokenProtectedB64 = ''
# $TelegramChatIDProtectedB64 = ''
@@ -29,17 +29,15 @@ $NotifyOrder = 'tg'
# --- Подпись сервера в Telegram и SAC (host.display_name); пусто = $env:COMPUTERNAME ---
# $ServerDisplayName = 'UNMS Kalina'
# --- Явный IPv4 хоста для SAC (опционально; иначе автоопределение) ---
# $ServerIPv4 = '192.168.160.57'
# $ServerIPv4 = '10.0.0.10'
# --- Security Alert Center (SAC) ---
# off | exclusive | dual | fallback — см. security-alert-center/docs/agent-integration.md
$UseSAC = 'fallback'
$SacUrl = 'https://sac.kalinamall.ru'
$SacApiKey = 'sac_UkOsAT3UWiQS54KK5OJPBDCSucysQDrKFju28wmYiz8'
$SacSpoolDir = 'C:\ProgramData\RDP-login-monitor\sac-spool'
$SacTimeoutSec = 45
$SacSpoolFlushMaxFiles = 50
$SacSpoolMaxAgeHours = 72
$SacUrl = 'https://sac.example.com'
$SacApiKey = 'sac_CHANGE_ME'
# $SacSpoolDir = 'C:\ProgramData\RDP-login-monitor\sac-spool'
# $SacTimeoutSec = 12
# $SacTlsSkipVerify = $false
# $SacFallbackFailures = 5
# $false = не слать report.daily.rdp с агента (суточный отчёт только из SAC)
@@ -60,7 +58,6 @@ $StartupRebootDetectMinutes = 5
$GetInventory = $true
# --- RDS Shadow Control + WinRM inbound (Enter-PSSession), severity warning ---
# $EnableRcm1149Monitoring = 1 # RCM Operational 1149 (RDP auth; workstation + RDS server)
# $EnableRcmShadowControlMonitoring = 1 # RCM Operational 20506/20507/20510
# $EnableWinRmInboundMonitoring = 1 # WinRM Operational 91 (+ correlate Security 4624)
# $EnableAdminShareMonitoring = 1 # Security 5140 C$/ADMIN$ (audit File Share)
@@ -72,7 +69,7 @@ $GetInventory = $true
# --- Узкое исключение шумовых сетевых логонов (LogonType=3, Advapi) ---
$IgnoreAdvapiNetworkLogonSourceIps = @(
'192.168.160.57'
'10.0.0.1'
)
# --- Exchange noise filter: 4624 + LogonType=3 + IP='-' (часто Outlook/почтовые клиенты) ---
# Включайте на почтовом сервере, если нужен только полезный интерактивный сигнал.
@@ -85,9 +82,9 @@ $MaxBackupDays = 31
# --- Блокировка учётной записи AD (4740) + IP из IIS ActiveSync ---
# Мониторинг включается только на КД с именем $LockoutMonitorDomainController.
$LockoutMonitorDomainController = 'K6A-DC3'
$NetBiosDomainName = 'B26'
$ExchangeIisLogPath = '\\fifth.kalinamall.ru\c$\inetpub\logs\LogFiles\W3SVC1'
$LockoutMonitorDomainController = 'DC01'
$NetBiosDomainName = 'CONTOSO'
$ExchangeIisLogPath = '\\mail.example.com\c$\inetpub\logs\LogFiles\W3SVC1'
$ExchangeServerHostForIisExclude = ''
$ExchangeIisLogTailLines = 5000
$ExchangeIisLogMinutesBeforeLockout = 30
-39
View File
@@ -1,39 +0,0 @@
# Push sanitized main to GitHub without leaving secrets on local main (kalinamall workflow).
# Usage: .\scripts\Push-GitHubMirror.ps1
param(
[string]$Remote = 'github',
[string]$Branch = 'main'
)
$ErrorActionPreference = 'Stop'
$Root = Split-Path -Parent $PSScriptRoot
Set-Location $Root
git remote get-url $Remote 2>$null | Out-Null
if ($LASTEXITCODE -ne 0) {
throw "remote not configured: $Remote"
}
if ((git status --porcelain)) {
throw 'working tree not clean; commit or stash first'
}
$before = (git rev-parse HEAD).Trim()
Write-Output "local HEAD before GitHub push: $before"
& "$PSScriptRoot\Sanitize-ForGitHub.ps1"
& "$PSScriptRoot\Rewrite-GitHostUrls.ps1" -Target github
& "$PSScriptRoot\Test-NoSecretsForGitHub.ps1"
$status = git status --porcelain
if (-not $status) {
Write-Output 'no changes after sanitize; pushing current HEAD to GitHub'
git push $Remote $Branch
exit 0
}
git add -A
git commit -m "chore(github): sanitize secrets and sync public mirror URLs"
git push --force-with-lease $Remote $Branch
git reset --hard $before
Write-Output "pushed $Remote/$Branch (force-with-lease mirror); local main restored to $before (production/kalinamall)"
+2 -2
View File
@@ -1,4 +1,4 @@
# Push main to a mirror remote with host-specific doc URLs, without leaving URL churn on main.
# Push main to a mirror remote with host-specific doc URLs, without leaving URL churn on main.
# Usage: .\scripts\Push-Mirror.ps1 github|kalinamall|papatramp
param(
[Parameter(Mandatory = $true)]
@@ -11,7 +11,7 @@ $Root = Split-Path -Parent $PSScriptRoot
Set-Location $Root
$remote = switch ($Target) {
'github' { 'github' }
'github' { 'origin' }
'kalinamall' { 'kalinamall' }
'papatramp' { 'papatramp' }
}
+7 -7
View File
@@ -1,4 +1,4 @@
# Rewrite cross-repo URLs in tracked docs/config for the target Git host.
# Rewrite cross-repo URLs in tracked docs/config for the target Git host.
# Usage: .\scripts\Rewrite-GitHostUrls.ps1 github|kalinamall|papatramp
param(
[Parameter(Mandatory = $true)]
@@ -28,12 +28,12 @@ switch ($Target) {
$BaseHost = $Base -replace '^https://', ''
$patterns = @(
@{ From = 'https://git.papatramp.ru/PapaTramp/([^)/''"\s]+)/blob/main/'; To = "$Base/`${1}$BlobSuffix/" }
@{ From = 'https://git.papatramp.ru/PapaTramp/([^)/''"\s]+)/src/branch/main/'; To = "$Base/`${1}$BlobSuffix/" }
@{ From = 'https://git.papatramp.ru/PapaTramp/([^)/''"\s]+)/blob/main/'; To = "$Base/`${1}$BlobSuffix/" }
@{ From = 'https://git\.kalinamall\.ru/PapaTramp/([^)/''"\s]+)/src/branch/main/'; To = "$Base/`${1}$BlobSuffix/" }
@{ From = 'https://git.papatramp.ru/PapaTramp/([^)/''"\s]+)/src/branch/main/'; To = "$Base/`${1}$BlobSuffix/" }
@{ From = 'https://git.papatramp.ru/PapaTramp/([^)/''"\s]+)/src/branch/main/'; To = "$Base/`${1}$BlobSuffix/" }
@{ From = 'https://git.papatramp.ru/PapaTramp/([^)/''"\s]+)/blob/main/'; To = "$Base/`$1$BlobSuffix/" }
@{ From = 'https://git.papatramp.ru/PapaTramp/([^)/''"\s]+)/src/branch/main/'; To = "$Base/`$1$BlobSuffix/" }
@{ From = 'https://git.papatramp.ru/PapaTramp/([^)/''"\s]+)/blob/main/'; To = "$Base/`$1$BlobSuffix/" }
@{ From = 'https://git.papatramp.ru/PapaTramp/([^)/''"\s]+)/src/branch/main/'; To = "$Base/`$1$BlobSuffix/" }
@{ From = 'https://git.papatramp.ru/PapaTramp/([^)/''"\s]+)/src/branch/main/'; To = "$Base/`$1$BlobSuffix/" }
@{ From = 'https://git.papatramp.ru/PapaTramp/([^)/''"\s]+)/src/branch/main/'; To = "$Base/`$1$BlobSuffix/" }
@{ From = 'https://git.papatramp.ru/PapaTramp/'; To = "$Base/" }
@{ From = 'https://git.papatramp.ru/PapaTramp/'; To = "$Base/" }
@{ From = 'https://git.papatramp.ru/PapaTramp/'; To = "$Base/" }
-169
View File
@@ -1,169 +0,0 @@
# Replace production-only values with public-safe placeholders (GitHub mirror).
# Usage: .\scripts\Sanitize-ForGitHub.ps1
# Reversible: production copies live on kalinamall/papatramp; restore via git reset --hard.
param(
[switch]$WhatIf
)
$ErrorActionPreference = 'Stop'
$Root = Split-Path -Parent $PSScriptRoot
Set-Location $Root
function Set-TrackedFileText {
param(
[Parameter(Mandatory = $true)][string]$RelativePath,
[Parameter(Mandatory = $true)][string]$Content
)
$path = Join-Path $Root $RelativePath
if ($WhatIf) {
Write-Output "WhatIf: would write $RelativePath"
return
}
$utf8Bom = New-Object System.Text.UTF8Encoding $true
[System.IO.File]::WriteAllText($path, $Content.TrimEnd() + "`r`n", $utf8Bom)
Write-Output "sanitized: $RelativePath"
}
$loginSettings = @'
<#
.SYNOPSIS
Локальные настройки Login_Monitor.ps1
.DESCRIPTION
Скопируйте в C:\ProgramData\RDP-login-monitor\login_monitor.settings.ps1
и при необходимости отредактируйте. Deploy-LoginMonitor.ps1 не перезаписывает settings,
если SAC уже настроен (UseSAC не off и задан SacApiKey). При первой установке или апгрейде
с версии без SAC (нет Sac-Client.ps1 / пустой ключ) example копируется поверх с резервной .bak.
#>
# --- Telegram (или DPAPI Base64 через Encrypt-DpapiForRdpMonitor.ps1) ---
$TelegramBotToken = 'YOUR_BOT_TOKEN'
$TelegramChatID = 'YOUR_CHAT_ID'
# $TelegramBotTokenProtectedB64 = ''
# $TelegramChatIDProtectedB64 = ''
# --- Email (опционально) ---
$NotifyOrder = 'tg'
# $MailSmtpHost = 'smtp.example.com'
# $MailSmtpPort = 587
# $MailSmtpUser = ''
# $MailSmtpPassword = ''
# $MailFrom = 'monitor@example.com'
# $MailTo = 'admin@example.com'
# $MailSmtpStartTls = $true
# $MailSmtpSsl = $false
# $MailSmtpPasswordProtectedB64 = ''
# --- Подпись сервера в Telegram и SAC (host.display_name); пусто = $env:COMPUTERNAME ---
# $ServerDisplayName = 'RDP-Server-01'
# --- Явный IPv4 хоста для SAC (опционально; иначе автоопределение) ---
# $ServerIPv4 = '192.168.1.10'
# --- Security Alert Center (SAC) ---
# off | exclusive | dual | fallback — см. security-alert-center/docs/agent-integration.md
$UseSAC = 'fallback'
$SacUrl = 'https://sac.example.com'
$SacApiKey = 'sac_CHANGE_ME'
$SacSpoolDir = 'C:\ProgramData\RDP-login-monitor\sac-spool'
$SacTimeoutSec = 45
$SacSpoolFlushMaxFiles = 50
$SacSpoolMaxAgeHours = 72
# $SacTlsSkipVerify = $false
# $SacFallbackFailures = 5
# $false = не слать report.daily.rdp с агента (суточный отчёт только из SAC)
# В settings.ps1 используйте 1/0 или $true/$false — не пишите голое false без $
$DailyReportEnabled = 1
# --- Heartbeat SAC (agent.heartbeat): интервал в секундах; 14400 = 4 ч ---
$HeartbeatInterval = 14400
# Оповещение, если last_heartbeat.txt не обновлялся > множитель × интервал (2 × 4 ч = 8 ч)
$HeartbeatStaleAlertMultiplier = 2
# Poll SAC на команды qwinsta/logoff (сек); см. security-alert-center/docs/agent-control-plane.md
# $SacCommandPollIntervalSec = 60
# Окно (мин): LastBootUpTime + System 41/1074/6005/6008/6009 → «старт после перезагрузки ОС»
$StartupRebootDetectMinutes = 5
# --- Инвентаризация железа/ПО для SAC (agent.inventory, раз в 12 ч) ---
$GetInventory = $true
# --- RDS Shadow Control + WinRM inbound (Enter-PSSession), severity warning ---
# $EnableRcm1149Monitoring = 1 # RCM Operational 1149 (RDP auth; workstation + RDS server)
# $EnableRcmShadowControlMonitoring = 1 # RCM Operational 20506/20507/20510
# $EnableWinRmInboundMonitoring = 1 # WinRM Operational 91 (+ correlate Security 4624)
# $EnableAdminShareMonitoring = 1 # Security 5140 C$/ADMIN$ (audit File Share)
# $WinRmIgnoreLocalSource = 1 # ::1, 127.0.0.1, fe80 (шум Exchange/локальный WinRM)
# $WinRmIgnoreMachineAccounts = 1 # учётки, оканчивающиеся на $
# $WinRmExchangeStrictMode = 1 # Exchange: user в Event 91 обязателен; 4624 только LogonProcess WinRM
# HealthMailbox* уже в ExcludedUserPatterns скрипта
# Проверка: powershell -File Login_Monitor.ps1 -CheckSac
# --- Узкое исключение шумовых сетевых логонов (LogonType=3, Advapi) ---
$IgnoreAdvapiNetworkLogonSourceIps = @(
'192.168.1.10'
)
# --- Exchange noise filter: 4624 + LogonType=3 + IP='-' (часто Outlook/почтовые клиенты) ---
# Включайте на почтовом сервере, если нужен только полезный интерактивный сигнал.
${Ignore4624-LT3-EmptyIP-Event} = $false
# --- Ротация login_monitor.log и хранение бэкапов (Logs\Backup\LoginLog_*.bak) ---
# $LogRotationHour = 0
# $LogRotationMinute = 0
$MaxBackupDays = 31
# --- Блокировка учётной записи AD (4740) + IP из IIS ActiveSync ---
# Мониторинг включается только на КД с именем $LockoutMonitorDomainController.
$LockoutMonitorDomainController = 'dc01.contoso.local'
$NetBiosDomainName = 'CONTOSO'
$ExchangeIisLogPath = '\\mail.contoso.local\c$\inetpub\logs\LogFiles\W3SVC1'
$ExchangeServerHostForIisExclude = ''
$ExchangeIisLogTailLines = 5000
$ExchangeIisLogMinutesBeforeLockout = 30
'@
Set-TrackedFileText -RelativePath 'login_monitor.settings.example.ps1' -Content $loginSettings
$exchangeSettingsPath = Join-Path $Root 'exchange_monitor.settings.example.ps1'
if (Test-Path -LiteralPath $exchangeSettingsPath) {
$ex = Get-Content -LiteralPath $exchangeSettingsPath -Raw
$ex = $ex -replace 'kalinamall\.ru', 'example.com'
$ex = $ex -replace 'fifth\.example\.com', 'mail.contoso.local'
$ex = $ex -replace 'k\.selezneva@example\.com', 'broken-mailbox@example.com'
Set-TrackedFileText -RelativePath 'exchange_monitor.settings.example.ps1' -Content $ex
}
$updatePath = Join-Path $Root 'update-rdp-monitor.ps1'
if (Test-Path -LiteralPath $updatePath) {
$upd = Get-Content -LiteralPath $updatePath -Raw
$upd = $upd -replace "Posle fetch: vsegda reset --hard na kalinamall/main \(bez merge\), zatem clean -fd\.",
'Posle fetch: reset --hard na upstream/main (bez merge), zatem clean -fd.'
$upd = $upd -replace "\\\\b26\\NETLOGON\\RDP-login-monitor", '\\dc.contoso.local\NETLOGON\RDP-login-monitor'
$upd = $upd -replace "https://git\.kalinamall\.ru/PapaTramp/RDP-login-monitor\.git",
'https://git.papatramp.ru/PapaTramp/RDP-login-monitor.git'
Set-TrackedFileText -RelativePath 'update-rdp-monitor.ps1' -Content $upd
}
$netlogonDoc = Join-Path $Root 'Docs/deploy-netlogon-publish.md'
if (Test-Path -LiteralPath $netlogonDoc) {
$doc = Get-Content -LiteralPath $netlogonDoc -Raw
$doc = $doc -replace 'K6A-DC3', 'dc01.corp.example.com'
$doc = $doc -replace '\\\\b26\\', '\\dc.contoso.local\'
$doc = $doc -replace "https://git\.kalinamall\.ru/PapaTramp/RDP-login-monitor\.git",
'https://git.example.com/org/RDP-login-monitor.git'
Set-TrackedFileText -RelativePath 'Docs/deploy-netlogon-publish.md' -Content $doc
}
$mdFiles = @(git ls-files '*.md' 2>$null | Where-Object { $_ -and (Test-Path $_) })
foreach ($rel in $mdFiles) {
$path = Join-Path $Root $rel
$md = Get-Content -LiteralPath $path -Raw
$orig = $md
$md = $md -replace 'https://git\.kalinamall\.ru/PapaTramp/([^)/\s]+)/src/branch/main/', 'https://git.papatramp.ru/PapaTramp/$1/src/branch/main/'
$md = $md -replace 'https://git\.papatramp\.ru/PapaTramp/([^)/\s]+)/src/branch/main/', 'https://git.papatramp.ru/PapaTramp/$1/src/branch/main/'
$md = $md -replace 'https://git\.kalinamall\.ru/PapaTramp/', 'https://git.papatramp.ru/PapaTramp/'
$md = $md -replace 'https://git\.papatramp\.ru/PapaTramp/', 'https://git.papatramp.ru/PapaTramp/'
if ($md -ne $orig) {
Set-TrackedFileText -RelativePath $rel -Content $md
}
}
Write-Output 'Sanitize-ForGitHub: done'
-47
View File
@@ -1,47 +0,0 @@
# Fail if tracked text still contains production-only markers (run before GitHub push).
$ErrorActionPreference = 'Stop'
$Root = Split-Path -Parent $PSScriptRoot
Set-Location $Root
$patterns = @(
'8239219522',
'sac_UkOsAT',
'2843230',
'sac\.kalinamall\.ru',
'\\\\b26\\',
'K6A-DC3',
'fifth\.kalinamall',
'192\.168\.160\.57',
'kalinamall\.ru',
'git\.kalinamall\.ru',
'git\.papatramp\.ru',
'\d{8,12}:[A-Za-z0-9_-]{20,}'
)
$extensions = @('*.md', '*.ps1', '*.example', '*.txt', '*.json', '*.yml', '*.yaml')
$files = git ls-files $extensions 2>$null | Where-Object { $_ -and (Test-Path $_) }
$hits = @()
foreach ($file in $files) {
if ($file -like 'scripts/Rewrite-GitHostUrls.ps1') { continue }
if ($file -like 'scripts/Push-PrivateMirror.ps1') { continue }
if ($file -like 'scripts/Sanitize-ForGitHub.ps1') { continue }
if ($file -like 'scripts/Test-NoSecretsForGitHub.ps1') { continue }
if ($file -like 'scripts/Push-GitHubMirror.ps1') { continue }
if ($file -like 'scripts/Push-Mirror.ps1') { continue }
$text = Get-Content -LiteralPath $file -Raw -ErrorAction SilentlyContinue
if ([string]::IsNullOrEmpty($text)) { continue }
foreach ($pat in $patterns) {
if ($text -match $pat) {
$hits += "${file}: matches /$pat/"
}
}
}
if ($hits.Count -gt 0) {
Write-Error ("GitHub secret scan failed:`n" + ($hits -join "`n"))
}
Write-Output "GitHub secret scan: OK ($($files.Count) files)"
+10 -24
View File
@@ -1,10 +1,10 @@
<#
<#
.SYNOPSIS
Obnovlyaet klon RDP-login-monitor s upstream git i kopiruet dist na NETLOGON.
.DESCRIPTION
Dlya servera publikatsii (napr. DC3). Po umolchaniyu GitHub (github.com/PTah).
Na zakrytom zerkale ukazhite -GitUrl URL vashego Gitea.
Posle fetch: vsegda reset --hard na kalinamall/main (bez merge), zatem clean -fd.
Posle fetch: vsegda reset --hard na vetku upstream (bez merge), zatem clean -fd.
Kopiruyutsya: polnyj spisok v Docs/deploy-netlogon-publish.md.
.EXAMPLE
powershell.exe -NoProfile -ExecutionPolicy Bypass -File C:\soft\update-rdp-monitor.ps1
@@ -14,7 +14,7 @@
[CmdletBinding(SupportsShouldProcess = $true)]
param(
[string]$RepoPath = 'C:\Soft\Git\RDP-login-monitor',
[string]$NetlogonDest = '\\b26\NETLOGON\RDP-login-monitor',
[string]$NetlogonDest = '\\dc.contoso.local\NETLOGON\RDP-login-monitor',
[string]$GitUrl = 'https://git.papatramp.ru/PapaTramp/RDP-login-monitor.git',
[string]$GitBranch = 'main',
[string]$LogFile = 'C:\soft\Logs\update-rdp-monitor.log',
@@ -34,7 +34,6 @@ $DistFiles = @(
'Install-DomainMonitors.ps1',
'Deploy-DomainMonitors.ps1',
'exchange_monitor.settings.example.ps1',
'Diagnose-RdpLoginMonitor.ps1',
'login_monitor.settings.example.ps1'
)
@@ -113,38 +112,25 @@ function Ensure-GitRepository {
function Get-ConfiguredGitRemoteName {
$names = @(Invoke-GitCommand -Arguments @('remote') | ForEach-Object { "$_".Trim() } | Where-Object { $_ })
if ($names.Count -eq 0) { return $null }
if ('kalinamall' -in $names) { return 'kalinamall' }
foreach ($n in $names) {
$url = (& git -C $RepoPath remote get-url $n 2>$null)
if ($url -match 'git\.kalinamall\.ru') { return $n }
}
if ('origin' -in $names) { return 'origin' }
return $names[0]
}
function Ensure-GitKalinamallRemote {
function Ensure-GitUpstreamRemote {
$name = Get-ConfiguredGitRemoteName
if ($null -ne $name) {
$url = (& git -C $RepoPath remote get-url $name 2>$null)
if ($url -match 'git\.kalinamall\.ru') {
Write-UpdateLog "Using remote: $name ($url)"
return $name
}
Write-UpdateLog "Remote $name is not kalinamall ($url); adding kalinamall -> $GitUrl"
} else {
Write-UpdateLog "No remotes; adding kalinamall -> $GitUrl"
Write-UpdateLog "Using remote: $name ($url)"
return $name
}
if ('kalinamall' -in @(& git -C $RepoPath remote 2>$null)) {
Invoke-GitCommand -Arguments @('remote', 'set-url', 'kalinamall', $GitUrl)
return 'kalinamall'
}
Invoke-GitCommand -Arguments @('remote', 'add', 'kalinamall', $GitUrl)
return 'kalinamall'
Write-UpdateLog "No remotes; adding origin -> $GitUrl"
Invoke-GitCommand -Arguments @('remote', 'add', 'origin', $GitUrl)
return 'origin'
}
function Update-Repository {
Ensure-GitRepository
$remote = Ensure-GitKalinamallRemote
$remote = Ensure-GitUpstreamRemote
Invoke-GitCommand -Arguments @('fetch', '--prune', $remote, $GitBranch)
$upstream = "${remote}/${GitBranch}"
+1 -1
View File
@@ -1 +1 @@
2.1.15-SAC
2.1.9-SAC