diff --git a/Changelog.md b/Changelog.md
index a6ef5e2..6d1096a 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,5 +1,18 @@
# Changelog
+## v1.116.0
+
+NTP clock-tamper protection + time-authentication audit — a new **NTP Configuration → [8] Clock-Tamper Protection** item plus a read-only CLI action.
+
+Windows Time (W32Time) bounds how far a single sync may move the clock with `MaxPosPhaseCorrection` / `MaxNegPhaseCorrection`. On domain members these default to `0xFFFFFFFF` (**unbounded**) — so a wrong or hostile time source can jump the clock arbitrarily, which breaks Kerberos (auth fails past ~5 min skew) and can enable ticket/replay and certificate-validity attacks.
+
+- **`NtpHardeningAudit`** (read-only) — reports the sync type (NT5DS / NTP / NoSync), whether the time source is authenticated (domain hierarchy = MS-SNTP) or unauthenticated (manual external NTP), the current phase-correction limits (decoding `0xFFFFFFFF` as "Unbounded"), and `RequireSecureTimeSyncRequests` when the NTP server is enabled. JSON-aware; makes no changes.
+- **Clock-Tamper Protection** (reversible) — bounds `MaxPos/MaxNegPhaseCorrection` to a chosen cap (default 48 h, matching the domain-controller default; never trips on a normally-running server, only closes the unbounded default). Prior values are captured for the session undo and the change is Dry-Run aware. Corrections beyond the cap are logged instead of applied — run **Force Time Sync** to recover after a legitimate large jump.
+
+A note on terminology: Windows has **no standalone symmetric-key/`ntp.keys` file** like Unix `ntpd`. Authenticated NTP on Windows is **MS-SNTP**, keyed automatically from the machine account through the domain hierarchy (legacy MD5-derived crypto). The audit surfaces whether that authenticated path is in use rather than exposing a keys file that does not exist on the platform.
+
+Addition to 19-NTPConfiguration (no new module). CLI actions: 197 → 198.
+
## v1.115.0
Network throughput benchmark — a new interactive diagnostic under **Network Diagnostics → [14] Network Throughput Benchmark (file copy)**.
diff --git a/Header.ps1 b/Header.ps1
index ca6938f..f48d385 100644
--- a/Header.ps1
+++ b/Header.ps1
@@ -30,7 +30,7 @@
7h3 4b1d3r
.VERSION
- 1.115.0
+ 1.116.0
.LAST UPDATED
05/23/2026
@@ -1391,7 +1391,7 @@
param(
# CLI headless mode: run a specific action without interactive menus
# Usage: RackStack.exe -Action Cleanup [-Tier Standard] [-Silent] [-OutputFormat JSON]
- [ValidateSet('Cleanup', 'Debloat', 'HealthCheck', 'Batch', 'QuickScan', 'Inventory', 'DriftCheck', 'Snapshot', 'Compliance', 'Harden', 'Remediate', 'Aggregate', 'Compare', 'Export', 'Trend', 'CertCheck', 'ReportHTML', 'ListeningPorts', 'SoftwareList', 'Uptime', 'ServiceAudit', 'EventAudit', 'NetInfo', 'ScheduledExport', 'ValidateConfig', 'Watch', 'Query', 'Diff', 'Baseline', 'Alert', 'FleetScan', 'PatchStatus', 'UserAudit', 'FirewallAudit', 'TaskAudit', 'DiskAudit', 'TLSAudit', 'SMBAudit', 'DriverAudit', 'TimeAudit', 'BootAudit', 'GPOAudit', 'MemoryAudit', 'ProcessAudit', 'BackupAudit', 'ShareAudit', 'DNSAudit', 'PowerAudit', 'RegistryAudit', 'ProfileAudit', 'HyperVAudit', 'NetworkAudit', 'StorageAudit', 'FeatureAudit', 'AutoStartAudit', 'BIOSAudit', 'ClusterAudit', 'AuditPolicyAudit', 'EnvAudit', 'CrashAudit', 'LocalGroupAudit', 'WMIAudit', 'TempAudit', 'UpdatePolicyAudit', 'IISAudit', 'SSHAudit', 'BitLockerAudit', 'PrintAudit', 'CredGuardAudit', 'PortAudit', 'AntivirusAudit', 'DotNetAudit', 'RDPAudit', 'VPNAudit', 'HostsFileAudit', 'NetStatAudit', 'LicenseAudit', 'USBDeviceAudit', 'AppLockerAudit', 'EventSubAudit', 'HotfixAudit', 'SysInfoAudit', 'LogonAudit', 'ACLAudit', 'RecoveryAudit', 'ServiceAccountAudit', 'ProxyAudit', 'PendingRebootAudit', 'PageFileAudit', 'CPUAudit', 'DefenderExclusionAudit', 'KerberosAudit', 'DHCPAudit', 'NUMAAudit', 'SymlinkAudit', 'StartupScriptAudit', 'SecureChannelAudit', 'ComObjectAudit', 'FirewallLogAudit', 'ScheduledRebootAudit', 'PowerShellAudit', 'RouteTableAudit', 'TokenPrivilegeAudit', 'WindowsCapabilityAudit', 'ARPTableAudit', 'LocaleAudit', 'TaskHistoryAudit', 'NTFSAudit', 'Win11Cleanup', 'DarkMode', 'LightMode', 'iSCSIAudit', 'NICTeamAudit', 'SMBSessionAudit', 'WindowsUpdateAudit', 'ClusterQuorumAudit', 'S2DAudit', 'VirtualSwitchAudit', 'MPIOPathAudit', 'ServiceRecoveryAudit', 'VMOvercommitAudit', 'DedupAudit', 'ClusterNetworkAudit', 'ReplicaLagAudit', 'HandleLeakAudit', 'ShadowCopyAudit', 'QoSPolicyAudit', 'LiveMigrationAudit', 'DomainTrustAudit', 'DiskLatencyAudit', 'NICOffloadAudit', 'StorageTimeoutAudit', 'EventLogCapacityAudit', 'TcpSettingsAudit', 'WinRMAudit', 'ClusterHealthScore', 'VMInventoryExport', 'VMSnapshotAudit', 'StorageHealthScore', 'CSVSpaceAudit', 'SMBConnectionAudit', 'VolumeLabelAudit', 'NICErrorAudit', 'VMResourceWaste', 'HealthDashboard', 'SCCMClientAudit', 'SCOMAgentAudit', 'WACConnectivityAudit', 'AzureADAudit', 'ServerScore', 'FleetReport', 'PasswordPolicy', 'FirewallRuleAudit', 'GPResultAudit', 'DNSCacheAudit', 'TPMAudit', 'SecureBootAudit', 'TimeSkewAudit', 'NetworkProfileAudit', 'InsecureServiceAudit', 'SelfTest', 'CheckForUpdate', 'ExportLogs', 'UpdateSelf', 'Rollback', 'ScheduleUpdateCheck', 'Dashboard', 'History', 'Replay', 'AzureArcEnroll', 'DefenderEndpointOnboard', 'WSUSSetup', 'ADCSSetup', 'StorageMigrationSetup', 'GPOBackup', 'GPODrift', 'JEAList', 'NPSSetup', 'AlwaysOnVPNSetup', 'CISScan', 'SIEMSetup', 'SIEMStatus', 'WACSetup', 'WACStatus', 'VHDXEncryptionAudit', 'ADRecycleBin', 'ClusterValidationReport', 'SmbEnforce', 'SmbSecurityCheck', 'PrintServerAudit')]
+ [ValidateSet('Cleanup', 'Debloat', 'HealthCheck', 'Batch', 'QuickScan', 'Inventory', 'DriftCheck', 'Snapshot', 'Compliance', 'Harden', 'Remediate', 'Aggregate', 'Compare', 'Export', 'Trend', 'CertCheck', 'ReportHTML', 'ListeningPorts', 'SoftwareList', 'Uptime', 'ServiceAudit', 'EventAudit', 'NetInfo', 'ScheduledExport', 'ValidateConfig', 'Watch', 'Query', 'Diff', 'Baseline', 'Alert', 'FleetScan', 'PatchStatus', 'UserAudit', 'FirewallAudit', 'TaskAudit', 'DiskAudit', 'TLSAudit', 'SMBAudit', 'DriverAudit', 'TimeAudit', 'BootAudit', 'GPOAudit', 'MemoryAudit', 'ProcessAudit', 'BackupAudit', 'ShareAudit', 'DNSAudit', 'PowerAudit', 'RegistryAudit', 'ProfileAudit', 'HyperVAudit', 'NetworkAudit', 'StorageAudit', 'FeatureAudit', 'AutoStartAudit', 'BIOSAudit', 'ClusterAudit', 'AuditPolicyAudit', 'EnvAudit', 'CrashAudit', 'LocalGroupAudit', 'WMIAudit', 'TempAudit', 'UpdatePolicyAudit', 'IISAudit', 'SSHAudit', 'BitLockerAudit', 'PrintAudit', 'CredGuardAudit', 'PortAudit', 'AntivirusAudit', 'DotNetAudit', 'RDPAudit', 'VPNAudit', 'HostsFileAudit', 'NetStatAudit', 'LicenseAudit', 'USBDeviceAudit', 'AppLockerAudit', 'EventSubAudit', 'HotfixAudit', 'SysInfoAudit', 'LogonAudit', 'ACLAudit', 'RecoveryAudit', 'ServiceAccountAudit', 'ProxyAudit', 'PendingRebootAudit', 'PageFileAudit', 'CPUAudit', 'DefenderExclusionAudit', 'KerberosAudit', 'DHCPAudit', 'NUMAAudit', 'SymlinkAudit', 'StartupScriptAudit', 'SecureChannelAudit', 'ComObjectAudit', 'FirewallLogAudit', 'ScheduledRebootAudit', 'PowerShellAudit', 'RouteTableAudit', 'TokenPrivilegeAudit', 'WindowsCapabilityAudit', 'ARPTableAudit', 'LocaleAudit', 'TaskHistoryAudit', 'NTFSAudit', 'Win11Cleanup', 'DarkMode', 'LightMode', 'iSCSIAudit', 'NICTeamAudit', 'SMBSessionAudit', 'WindowsUpdateAudit', 'ClusterQuorumAudit', 'S2DAudit', 'VirtualSwitchAudit', 'MPIOPathAudit', 'ServiceRecoveryAudit', 'VMOvercommitAudit', 'DedupAudit', 'ClusterNetworkAudit', 'ReplicaLagAudit', 'HandleLeakAudit', 'ShadowCopyAudit', 'QoSPolicyAudit', 'LiveMigrationAudit', 'DomainTrustAudit', 'DiskLatencyAudit', 'NICOffloadAudit', 'StorageTimeoutAudit', 'EventLogCapacityAudit', 'TcpSettingsAudit', 'WinRMAudit', 'ClusterHealthScore', 'VMInventoryExport', 'VMSnapshotAudit', 'StorageHealthScore', 'CSVSpaceAudit', 'SMBConnectionAudit', 'VolumeLabelAudit', 'NICErrorAudit', 'VMResourceWaste', 'HealthDashboard', 'SCCMClientAudit', 'SCOMAgentAudit', 'WACConnectivityAudit', 'AzureADAudit', 'ServerScore', 'FleetReport', 'PasswordPolicy', 'FirewallRuleAudit', 'GPResultAudit', 'DNSCacheAudit', 'TPMAudit', 'SecureBootAudit', 'TimeSkewAudit', 'NetworkProfileAudit', 'InsecureServiceAudit', 'SelfTest', 'CheckForUpdate', 'ExportLogs', 'UpdateSelf', 'Rollback', 'ScheduleUpdateCheck', 'Dashboard', 'History', 'Replay', 'AzureArcEnroll', 'DefenderEndpointOnboard', 'WSUSSetup', 'ADCSSetup', 'StorageMigrationSetup', 'GPOBackup', 'GPODrift', 'JEAList', 'NPSSetup', 'AlwaysOnVPNSetup', 'CISScan', 'SIEMSetup', 'SIEMStatus', 'WACSetup', 'WACStatus', 'VHDXEncryptionAudit', 'ADRecycleBin', 'ClusterValidationReport', 'SmbEnforce', 'SmbSecurityCheck', 'PrintServerAudit', 'NtpHardeningAudit')]
[string]$Action,
[ValidateSet('Light', 'Standard', 'Aggressive')]
diff --git a/Modules/00-Initialization.ps1 b/Modules/00-Initialization.ps1
index 9098c2e..2fdffe4 100644
--- a/Modules/00-Initialization.ps1
+++ b/Modules/00-Initialization.ps1
@@ -225,7 +225,7 @@ if (-not $PSCommandPath -and $script:ScriptPath) {
if (-not $script:ModuleRoot -and $script:ScriptPath) {
$script:ModuleRoot = [System.IO.Path]::GetDirectoryName($script:ScriptPath)
}
-$script:ScriptVersion = "1.115.0"
+$script:ScriptVersion = "1.116.0"
$script:ScriptStartTime = Get-Date
# Post-update cleanup: UpdateSelf / Rollback leave a `.pending-delete` sibling next to RackStack.exe.
diff --git a/Modules/19-NTPConfiguration.ps1 b/Modules/19-NTPConfiguration.ps1
index 8a2fecb..dfa9dce 100644
--- a/Modules/19-NTPConfiguration.ps1
+++ b/Modules/19-NTPConfiguration.ps1
@@ -67,6 +67,7 @@ function Set-NTPConfiguration {
Write-MenuItem "[5] Force Time Sync Now"
Write-MenuItem "[6] Show Detailed Time Status"
Write-MenuItem "[7] View NTP Status"
+ Write-MenuItem "[8] Clock-Tamper Protection (phase-correction limits)"
Write-OutputColor " └────────────────────────────────────────────────────────────────────────┘" -color "Info"
Write-OutputColor "" -color "Info"
Write-OutputColor " [B] ◄ Back" -color "Info"
@@ -117,9 +118,12 @@ function Set-NTPConfiguration {
"7" {
Show-NTPStatus
}
+ "8" {
+ Show-NtpClockHardening
+ }
"b" { return }
"B" { return }
- default { Write-OutputColor " Invalid choice. Enter 1-7 or B." -color "Error"; Start-Sleep -Seconds 1 }
+ default { Write-OutputColor " Invalid choice. Enter 1-8 or B." -color "Error"; Start-Sleep -Seconds 1 }
}
Write-PressEnter
@@ -331,4 +335,164 @@ function Show-NTPStatus {
Write-OutputColor " Could not test time accuracy" -color "Warning"
}
}
+
+# Read-only: report W32Time tamper-protection posture. MaxPos/MaxNegPhaseCorrection
+# bound how far a single sync may move the clock; on domain members they default to
+# 0xFFFFFFFF (UNBOUNDED) — any time source can jump the clock arbitrarily. SyncType
+# NT5DS means time comes authenticated from the domain hierarchy (MS-SNTP); NTP
+# (manual peers) is UNauthenticated. Windows has no standalone symmetric-key file —
+# NTP authentication is domain-based (machine account / MS-SNTP), not a keys file.
+function Get-NtpHardeningStatus {
+ $cfgPath = 'HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Config'
+ $parPath = 'HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Parameters'
+ $srvPath = 'HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpServer'
+ $result = [PSCustomObject]@{
+ Available = $false
+ SyncType = 'Unknown'
+ AuthenticatedSource = $false
+ MaxPosPhaseCorrection = $null
+ MaxNegPhaseCorrection = $null
+ NtpServerEnabled = $false
+ RequireSecureSyncReqs = $null
+ }
+ try {
+ $cfg = Get-ItemProperty -Path $cfgPath -ErrorAction Stop
+ $result.Available = $true
+ $mp = $cfg.MaxPosPhaseCorrection
+ $mn = $cfg.MaxNegPhaseCorrection
+ $result.MaxPosPhaseCorrection = if ($null -eq $mp) { $null } elseif ([uint32]$mp -eq [uint32]::MaxValue) { 'Unbounded' } else { [int64][uint32]$mp }
+ $result.MaxNegPhaseCorrection = if ($null -eq $mn) { $null } elseif ([uint32]$mn -eq [uint32]::MaxValue) { 'Unbounded' } else { [int64][uint32]$mn }
+ } catch { return $result }
+ try {
+ $par = Get-ItemProperty -Path $parPath -ErrorAction SilentlyContinue
+ if ($par -and $par.Type) { $result.SyncType = "$($par.Type)"; $result.AuthenticatedSource = ($par.Type -eq 'NT5DS') }
+ } catch {}
+ try {
+ $srv = Get-ItemProperty -Path $srvPath -ErrorAction SilentlyContinue
+ if ($srv) {
+ $result.NtpServerEnabled = ([int]$srv.Enabled -eq 1)
+ if ($null -ne $srv.RequireSecureTimeSyncRequests) { $result.RequireSecureSyncReqs = [int]$srv.RequireSecureTimeSyncRequests }
+ }
+ } catch {}
+ return $result
+}
+
+# Reversible: bound the clock phase-correction limits so a wrong or hostile time
+# source cannot apply an arbitrarily large clock jump. Beyond the cap W32Time logs
+# an event instead of moving the clock. The default cap (48h) matches the domain
+# controller default and never trips on a normally-running server; it only closes
+# the UNBOUNDED (0xFFFFFFFF) default that domain members ship with.
+function Set-NtpClockTamperProtection {
+ param([int]$CapSeconds = 172800)
+ $cfgPath = 'HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Config'
+ if (-not (Test-Path -Path $cfgPath)) {
+ Write-OutputColor " W32Time Config registry key not found — is the Windows Time service installed?" -color "Error"; return
+ }
+ $priorPos = (Get-ItemProperty -Path $cfgPath -Name MaxPosPhaseCorrection -ErrorAction SilentlyContinue).MaxPosPhaseCorrection
+ $priorNeg = (Get-ItemProperty -Path $cfgPath -Name MaxNegPhaseCorrection -ErrorAction SilentlyContinue).MaxNegPhaseCorrection
+
+ if ($script:DryRunMode -and -not $script:ApplyingDryRunQueue) {
+ $cp = $priorPos; $cn = $priorNeg; $cap = $CapSeconds; $path = $cfgPath
+ Push-DryRunStep -Label "Bound NTP clock phase-correction to ${CapSeconds}s" -Category "Security" -OneWay $false `
+ -Params @{ CapSeconds = $CapSeconds } `
+ -Preflight { Test-Path -Path $path }.GetNewClosure() `
+ -Apply {
+ Set-ItemProperty -Path $path -Name MaxPosPhaseCorrection -Value $cap -Type DWord -ErrorAction SilentlyContinue
+ Set-ItemProperty -Path $path -Name MaxNegPhaseCorrection -Value $cap -Type DWord -ErrorAction SilentlyContinue
+ $null = w32tm /config /update 2>&1
+ Restart-Service w32time -Force -ErrorAction SilentlyContinue
+ }.GetNewClosure() `
+ -Undo {
+ if ($null -ne $cp) { Set-ItemProperty -Path $path -Name MaxPosPhaseCorrection -Value $cp -Type DWord -ErrorAction SilentlyContinue }
+ if ($null -ne $cn) { Set-ItemProperty -Path $path -Name MaxNegPhaseCorrection -Value $cn -Type DWord -ErrorAction SilentlyContinue }
+ $null = w32tm /config /update 2>&1
+ Restart-Service w32time -Force -ErrorAction SilentlyContinue
+ }.GetNewClosure()
+ Write-OutputColor " Queued (Dry-Run): bound clock phase-correction to ${CapSeconds}s." -color "Warning"
+ Add-SessionChange -Category "DryRun" -Description "Queued NTP clock-tamper protection (${CapSeconds}s cap)"
+ return
+ }
+
+ try {
+ Set-ItemProperty -Path $cfgPath -Name MaxPosPhaseCorrection -Value $CapSeconds -Type DWord -ErrorAction Stop
+ Set-ItemProperty -Path $cfgPath -Name MaxNegPhaseCorrection -Value $CapSeconds -Type DWord -ErrorAction Stop
+ $null = w32tm /config /update 2>&1
+ Restart-Service w32time -Force -ErrorAction SilentlyContinue
+ Write-OutputColor " Clock phase-correction bounded to ${CapSeconds}s (was Pos=$priorPos / Neg=$priorNeg)." -color "Success"
+ Write-OutputColor " Corrections larger than the cap are now logged instead of applied; run Force Time Sync after a legitimate large jump." -color "Info"
+ Add-SessionChange -Category "Security" -Description "Bounded NTP clock phase-correction to ${CapSeconds}s"
+ Clear-MenuCache
+ Add-UndoAction -Category "Security" -Description "Bounded NTP clock phase-correction to ${CapSeconds}s" -UndoScript {
+ param($Path, $Pos, $Neg)
+ if ($null -ne $Pos) { Set-ItemProperty -Path $Path -Name MaxPosPhaseCorrection -Value $Pos -Type DWord -ErrorAction SilentlyContinue }
+ if ($null -ne $Neg) { Set-ItemProperty -Path $Path -Name MaxNegPhaseCorrection -Value $Neg -Type DWord -ErrorAction SilentlyContinue }
+ $null = w32tm /config /update 2>&1
+ Restart-Service w32time -Force -ErrorAction SilentlyContinue
+ } -UndoParams @{ Path = $cfgPath; Pos = $priorPos; Neg = $priorNeg }
+ }
+ catch {
+ Write-OutputColor " Failed to apply clock-tamper protection: $($_.Exception.Message)" -color "Error"
+ }
+}
+
+# Interactive: clock-tamper protection audit + offer to bound phase-correction.
+function Show-NtpClockHardening {
+ Clear-Host
+ Write-CenteredOutput "Clock-Tamper Protection" -color "Info"
+ $s = Get-NtpHardeningStatus
+ if (-not $s.Available) { Write-OutputColor " W32Time configuration is not available on this host." -color "Error"; return }
+
+ $posStr = if ($s.MaxPosPhaseCorrection -eq 'Unbounded') { "Unbounded (0xFFFFFFFF)" } else { "$($s.MaxPosPhaseCorrection)s" }
+ $negStr = if ($s.MaxNegPhaseCorrection -eq 'Unbounded') { "Unbounded (0xFFFFFFFF)" } else { "$($s.MaxNegPhaseCorrection)s" }
+ $posColor = if ($s.MaxPosPhaseCorrection -eq 'Unbounded') { "Warning" } else { "Success" }
+ $negColor = if ($s.MaxNegPhaseCorrection -eq 'Unbounded') { "Warning" } else { "Success" }
+
+ Write-OutputColor " Sync type : $($s.SyncType)" -color "Info"
+ if ($s.AuthenticatedSource) {
+ Write-OutputColor " Time authentication : Domain hierarchy (MS-SNTP, authenticated)" -color "Success"
+ } else {
+ Write-OutputColor " Time authentication : Manual / external NTP (UNauthenticated)" -color "Warning"
+ }
+ Write-OutputColor " Max +correction : $posStr" -color $posColor
+ Write-OutputColor " Max -correction : $negStr" -color $negColor
+ if ($s.NtpServerEnabled -and $null -ne $s.RequireSecureSyncReqs) {
+ $rsColor = if ($s.RequireSecureSyncReqs -eq 1) { "Success" } else { "Warning" }
+ Write-OutputColor " Secure sync requests : $($s.RequireSecureSyncReqs) (NTP server)" -color $rsColor
+ }
+ Write-OutputColor "" -color "Info"
+ Write-OutputColor " NOTE: Windows authenticated NTP (MS-SNTP) is domain-based and uses legacy" -color "Info"
+ Write-OutputColor " MD5-derived crypto. There is no standalone symmetric-key file on Windows;" -color "Info"
+ Write-OutputColor " authentication comes from the domain hierarchy (NT5DS), not a keys file." -color "Info"
+ Write-OutputColor "" -color "Info"
+
+ if ($s.MaxPosPhaseCorrection -eq 'Unbounded' -or $s.MaxNegPhaseCorrection -eq 'Unbounded') {
+ Write-OutputColor " This host accepts UNBOUNDED clock corrections — a wrong or hostile time" -color "Warning"
+ Write-OutputColor " source could jump the clock arbitrarily (breaking Kerberos / enabling replay)." -color "Warning"
+ }
+ if (-not (Confirm-UserAction -Message "Bound the clock phase-correction limits now?")) { Write-OutputColor " No changes made." -color "Info"; return }
+
+ $capSeconds = 172800
+ $capInput = Read-Host " Cap in seconds (default 172800 = 48h; e.g. 3600 = 1h)"
+ if (-not [string]::IsNullOrWhiteSpace($capInput)) {
+ $parsed = 0
+ if ([int]::TryParse($capInput.Trim(), [ref]$parsed) -and $parsed -gt 0) { $capSeconds = $parsed }
+ }
+ Set-NtpClockTamperProtection -CapSeconds $capSeconds
+}
+
+# CLI: NtpHardeningAudit — read-only clock-tamper / auth posture (JSON-aware).
+function Start-NtpHardeningAudit {
+ $s = Get-NtpHardeningStatus
+ if ($script:CLIOutputFormat -eq 'JSON') {
+ Write-Output (@{
+ Tool = $script:ToolFullName; Version = $script:ScriptVersion; Action = 'NtpHardeningAudit'
+ Timestamp = (Get-Date -Format "yyyy-MM-ddTHH:mm:ss"); Hostname = $env:COMPUTERNAME
+ Available = $s.Available; SyncType = $s.SyncType; AuthenticatedSource = $s.AuthenticatedSource
+ MaxPosPhaseCorrection = "$($s.MaxPosPhaseCorrection)"; MaxNegPhaseCorrection = "$($s.MaxNegPhaseCorrection)"
+ NtpServerEnabled = $s.NtpServerEnabled; RequireSecureSyncRequests = $s.RequireSecureSyncReqs
+ } | ConvertTo-Json)
+ }
+ else { Show-NtpClockHardening }
+ return $true
+}
#endregion
\ No newline at end of file
diff --git a/Modules/34-Help.ps1 b/Modules/34-Help.ps1
index 5f0f786..52b5294 100644
--- a/Modules/34-Help.ps1
+++ b/Modules/34-Help.ps1
@@ -253,7 +253,7 @@ function Search-HelpTopics {
@{ Title = "Performance"; Keywords = @("performance", "cpu", "memory", "disk", "io", "bandwidth", "dashboard", "process"); Description = "Live performance dashboard with CPU, memory, disk I/O, and network bandwidth monitoring" }
@{ Title = "Licensing & NTP"; Keywords = @("license", "activation", "kms", "avma", "ntp", "time", "timezone", "clock"); Description = "Windows licensing status (KMS/AVMA/Retail), NTP configuration, time sync, and timezone setup" }
@{ Title = "VM Management"; Keywords = @("checkpoint", "snapshot", "export", "import", "migration", "vhd", "iso"); Description = "VM checkpoints, export/import, migration readiness, VHD health, and ISO inventory" }
- @{ Title = "CLI Actions"; Keywords = @("cli", "action", "headless", "automation", "fleet", "json", "audit", "scan", "score", "dashboard", "monitor", "policy", "sla", "netmap", "validate"); Description = "197 CLI actions for headless automation. Run -ListActions to see all. JSON output via -OutputFormat JSON. Key: ServerScore, HealthDashboard, FleetReport, CISScan, NPSSetup, AlwaysOnVPNSetup, SIEMStatus." }
+ @{ Title = "CLI Actions"; Keywords = @("cli", "action", "headless", "automation", "fleet", "json", "audit", "scan", "score", "dashboard", "monitor", "policy", "sla", "netmap", "validate"); Description = "198 CLI actions for headless automation. Run -ListActions to see all. JSON output via -OutputFormat JSON. Key: ServerScore, HealthDashboard, FleetReport, CISScan, NPSSetup, AlwaysOnVPNSetup, SIEMStatus." }
@{ Title = "SelfTest Action"; Keywords = @("selftest", "self-test", "diagnose", "diagnostic", "verify", "healthcheck", "sanity"); Description = "Internal diagnostic. -Action SelfTest checks PS version, elevation, module count, version consistency, defaults.json validity, temp path writability, FileServer reachability, and agent installer config. Exit 1 on any failure. Use -OutputFormat JSON for structured output." }
@{ Title = "Security Audits"; Keywords = @("security", "audit", "hardening", "compliance", "tls", "smb", "kerberos", "credguard", "applocker", "bitlockeraudit", "defenderexclusionaudit", "audit-policy", "secureboot", "tpm"); Description = "Security-focused CLI audits: TLSAudit, SMBAudit, KerberosAudit, CredGuardAudit, AppLockerAudit, BitLockerAudit, DefenderExclusionAudit, AuditPolicyAudit, SecureBootAudit, TPMAudit, UserAudit, LogonAudit, InsecureServiceAudit, RegistryAudit. All support -OutputFormat JSON." }
@{ Title = "Network Audits"; Keywords = @("netaudit", "dns", "firewall-audit", "firewalllog", "arp", "route", "tcp", "netstat", "dhcp", "netprofile", "winrm", "qos", "nicoffload"); Description = "Network audits: DNSAudit, DNSCacheAudit, FirewallAudit, FirewallRuleAudit, FirewallLogAudit, ARPTableAudit, RouteTableAudit, TcpSettingsAudit, NetStatAudit, DHCPAudit, NetworkProfileAudit, WinRMAudit, QoSPolicyAudit, NICOffloadAudit, NICErrorAudit, HostsFileAudit, VPNAudit, ProxyAudit." }
diff --git a/Modules/50-EntryPoint.ps1 b/Modules/50-EntryPoint.ps1
index f2c13c1..2994a5c 100644
--- a/Modules/50-EntryPoint.ps1
+++ b/Modules/50-EntryPoint.ps1
@@ -415,6 +415,7 @@ function Assert-Elevation {
@{ Action = 'SmbSecurityCheck'; Description = 'Read-only: report SMB server signing/encryption posture (JSON-aware)' }
@{ Action = 'SmbEnforce'; Description = 'Enforce SMB server signing + encryption (reversible)' }
@{ Action = 'PrintServerAudit'; Description = 'Read-only: report print-spooler posture, queue depth, orphaned ports + unused drivers (JSON-aware)' }
+ @{ Action = 'NtpHardeningAudit'; Description = 'Read-only: report W32Time clock-tamper posture (phase-correction limits, auth mode) (JSON-aware)' }
@{ Action = 'Batch'; Description = 'JSON-driven full configuration' }
)
if ($script:CLIOutputFormat -eq 'JSON') {
@@ -2077,6 +2078,11 @@ footer{text-align:center;color:#999;font-size:12px;padding:16px}
$psAuditOk = Start-PrintServerAudit
[Environment]::Exit([int](-not $psAuditOk))
}
+ 'NtpHardeningAudit' {
+ # Read-only W32Time clock-tamper / auth posture (JSON-aware).
+ $ntpAuditOk = Start-NtpHardeningAudit
+ [Environment]::Exit([int](-not $ntpAuditOk))
+ }
'Batch' {
if (-not $script:CLIConfig) {
Write-OutputColor " ERROR: -Action Batch requires -Config
-
+