Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions modules/roles_profiles/manifests/profiles/disable_services.pp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,23 @@
case $facts['custom_win_location'] {
'datacenter': {
include win_disable_services::disable_optional_services
# Disable Windows Defender real-time/on-access scanning on the
# datacenter hardware fleet explicitly. Do NOT rely on the
# custom_win_release_id in ['2004','2009'] gate above: Win11 freezes
# ReleaseId at 2009 (DisplayVersion carries 24H2), so that branch
# only fires by coincidence and would silently stop disabling
# Defender if the reported ReleaseId ever changed. Including the
# class is idempotent, so this is harmless where the gate also fires.
# Tamper Protection is enforced on this fleet (TamperProtectionSource=5),
# so Set-MpPreference / policy DisableRealtimeMonitoring do not stick;
# the schtask renames the WdFilter/WdBoot/WdNisDrv drivers at boot,
# which works below Tamper Protection. State is asserted by the
# win_nsclient check_defender check.
include win_disable_services::disable_windows_defender_schtask
# Declare the policy/passive/real-time-off intent. Ignored while Tamper
# Protection is on (the schtask rename is what works there), but correct
# and immediate on any Tamper-off image.
include win_disable_services::disable_windows_defender
}
'azure': {
$apx_uninstall = 'uninstall.ps1'
Expand Down
89 changes: 78 additions & 11 deletions modules/win_disable_services/manifests/disable_windows_defender.pp
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,94 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# Policy-based Windows Defender / real-time protection disable.
#
# IMPORTANT: on Windows 11 with Tamper Protection ENABLED (the state on the
# datacenter hardware fleet, TamperProtectionSource=5, which cannot be turned off
# in-OS), Defender IGNORES these policy values - Tamper Protection guards them.
# They are honored only when Tamper Protection is OFF (e.g. disabled in the image
# before first boot). They declare intent and make the disable correct + immediate
# for any Tamper-off image, but they are NOT sufficient on their own while Tamper
# is on.
#
# The mechanism that actually disables on-access scanning while Tamper is on is the
# driver rename performed by win_disable_services::disable_windows_defender_schtask
# (renames WdFilter/WdBoot/WdNisDrv at boot, below Tamper's reach), re-asserted at
# boot by the maintain-system script (Invoke-DefenderRealtimeGuard), and monitored
# by the win_nsclient check_defender check.
class win_disable_services::disable_windows_defender {
if $facts['os']['name'] == 'Windows' {
## Taken from https://github.com/mozilla-platform-ops/worker-images/blob/main/scripts/windows/CustomFunctions/Bootstrap/Public/Disable-AntiVirus.ps1
#exec { 'disable_windows_defender':
# command => file('win_disable_services/windows_defender/set.ps1'),
# onlyif => file('win_disable_services/windows_defender/validate.ps1'),
# provider => powershell,
# timeout => 300,
#}
registry_key { 'HKLM\SOFTWARE\Policies\Microsoft\Windows Defender':
ensure => present,
}
registry_value { 'HKLM\SOFTWARE\Policies\Microsoft\Windows Defender\DisableAntiSpyware':
ensure => present,
type => dword,
data => '1',
}

## Taken from https://github.com/mozilla-platform-ops/worker-images/blob/main/scripts/windows/CustomFunctions/Bootstrap/Public/Disable-AntiVirus.ps1
registry_value { 'HKLM\\SOFTWARE\\Policies\\Microsoft\\Windows Advanced Threat Protection':
registry_key { 'HKLM\SOFTWARE\Policies\Microsoft\Windows Defender\Real-Time Protection':
ensure => present,
}
registry_value { 'HKLM\SOFTWARE\Policies\Microsoft\Windows Defender\Real-Time Protection\DisableRealtimeMonitoring':
ensure => present,
type => dword,
data => '1',
}
registry_value { 'HKLM\SOFTWARE\Policies\Microsoft\Windows Defender\Real-Time Protection\DisableBehaviorMonitoring':
ensure => present,
type => dword,
data => '1',
}
registry_value { 'HKLM\SOFTWARE\Policies\Microsoft\Windows Defender\Real-Time Protection\DisableOnAccessProtection':
ensure => present,
type => dword,
data => '1',
}
registry_value { 'HKLM\SOFTWARE\Policies\Microsoft\Windows Defender\Real-Time Protection\DisableIOAVProtection':
ensure => present,
type => dword,
data => '1',
}

## Taken from https://github.com/mozilla-platform-ops/worker-images/blob/main/scripts/windows/CustomFunctions/Bootstrap/Public/Disable-AntiVirus.ps1
registry_value { 'HKLM\\SOFTWARE\\Policies\\Microsoft\\Windows Advanced Threat Protection\\ForceDefenderPassiveMode':
# Force Defender into passive mode (honored when Tamper is off / another AV is
# registered). Harmless otherwise.
registry_key { 'HKLM\SOFTWARE\Policies\Microsoft\Windows Advanced Threat Protection':
ensure => present,
}
registry_value { 'HKLM\SOFTWARE\Policies\Microsoft\Windows Advanced Threat Protection\ForceDefenderPassiveMode':
ensure => present,
type => dword,
data => '1',
}

# The following DO take effect even while Tamper Protection is on (verified on the
# 24h2 hw fleet) - they are not guarded by Tamper Protection the way the AV
# engine/services/drivers are:

# Disable the Defender for Endpoint (EDR) sensor service. This is the ONE Defender
# service whose Start value is writable under Tamper (WinDefend/WdFilter/WdNisSvc are
# not). 4 = disabled.
registry_value { 'HKLM\SYSTEM\CurrentControlSet\Services\Sense\Start':
ensure => present,
type => dword,
data => '4',
}

# Disable Defender's built-in scheduled tasks (scans/cleanup/cache/verification) so
# they cannot fire during CI tasks. Tamper Protection does not protect these.
exec { 'disable_defender_scheduled_tasks':
command => 'Get-ScheduledTask -TaskPath "\\Microsoft\\Windows\\Windows Defender\\" -ErrorAction SilentlyContinue | Disable-ScheduledTask -ErrorAction SilentlyContinue | Out-Null',
onlyif => 'if (Get-ScheduledTask -TaskPath "\\Microsoft\\Windows\\Windows Defender\\" -ErrorAction SilentlyContinue | Where-Object { $_.State -ne "Disabled" }) { exit 0 } else { exit 1 }',
provider => powershell,
}

# Blanket on-access path exclusions for the CI volumes (GP-managed; writable and
# honored under Tamper). These minimise scan overhead during the window where
# WdFilter is still loaded (e.g. right after a Defender platform update, before the
# maintain-system guard reboots). They are (re)asserted every boot by
# Invoke-DefenderRealtimeGuard in maintainsystem-hw.ps1 (value names contain a
# trailing backslash, which is set there rather than via registry_value titles).
}
}
# Bug List
Expand Down
78 changes: 78 additions & 0 deletions modules/win_nsclient/files/check_defender.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# scripts\check_defender.ps1
# NSClient++ external check that asserts Windows Defender real-time / on-access
# scanning is effectively DISABLED on the datacenter hardware fleet.
#
# Why not Get-MpComputerStatus? On this fleet the Defender management provider
# fails to load ("Provider load failure") because the disable mechanism renames
# the Defender driver binaries (see win_disable_services::disable_windows_defender_schtask).
# So we assert the ground-truth signals instead:
# - WdFilter (the file-system minifilter that performs on-access scanning) is
# NOT running, AND its driver binary is renamed to WdFilter.sys.bak.
# Real-time scanning cannot occur without WdFilter loaded, regardless of whether
# the WinDefend service or MsMpEng process happen to be alive.
#
# The key risk this guards against: a Defender platform/signature update can
# restore WdFilter.sys and re-arm on-access scanning until the next boot (when
# the disable schtask re-renames it). That silently injects scan overhead into
# CI perf runs, so we surface it as CRITICAL.
#
# Nagios contract: print one line "STATE - text | perfdata" and exit 0/1/2/3.

$ErrorActionPreference = 'Stop'

function Exit-With([int]$code, [string]$msg) {
Write-Output $msg
exit $code
}

$driver = "$env:SystemRoot\System32\drivers\WdFilter.sys"
$driverBak = "$env:SystemRoot\System32\drivers\WdFilter.sys.bak"

# 1) Is the on-access minifilter currently running?
$wdfState = 'unknown'
try {
$q = (& "$env:SystemRoot\System32\sc.exe" query WdFilter 2>$null | Select-String 'STATE')
if ($q -match 'RUNNING') { $wdfState = 'running' }
elseif ($q -match 'STOPPED') { $wdfState = 'stopped' }
elseif (-not $q) { $wdfState = 'absent' }
}
catch { $wdfState = 'unknown' }

# 2) Is the driver binary renamed (disabled) or restored (re-armed)?
$sysPresent = Test-Path -LiteralPath $driver
$bakPresent = Test-Path -LiteralPath $driverBak

# 3) WinDefend service state (informational only — not the deciding factor)
$winDefend = 'unknown'
try {
$svc = Get-Service WinDefend -ErrorAction SilentlyContinue
if ($svc) { $winDefend = "$($svc.Status)" } else { $winDefend = 'absent' }
}
catch { }

# Numeric health code for Grafana/InfluxDB: 0 disabled(OK) / 2 active(CRIT) / 3 unknown
$rtRunning = if ($wdfState -eq 'running' -or $sysPresent) { 1 } else { 0 }
$health = switch ($wdfState) {
'stopped' { if ($sysPresent) { 2 } else { 0 } }
'absent' { if ($sysPresent) { 2 } else { 0 } }
'running' { 2 }
default { 3 }
}

$perf = "defender_rt_on=$rtRunning;1;1;0;1 health=$health;1;2;0;3"
$summary = "WdFilter=$wdfState WdFilter.sys=$(if($sysPresent){'PRESENT'}else{'renamed'}) bak=$(if($bakPresent){'yes'}else{'no'}) WinDefend=$winDefend"

if ($wdfState -eq 'unknown') {
Exit-With 3 "UNKNOWN - cannot determine WdFilter state - $summary | $perf"
}

# Re-armed: the minifilter is running, or its binary was restored by an update.
if ($wdfState -eq 'running') {
Exit-With 2 "CRITICAL - Defender on-access scanning is ACTIVE (WdFilter running) - $summary | $perf"
}
if ($sysPresent) {
Exit-With 2 "CRITICAL - WdFilter.sys restored (likely Defender platform update); will re-arm at next boot - $summary | $perf"
}

# WdFilter stopped/absent and binary renamed => real-time effectively off.
Exit-With 0 "OK - Defender real-time scanning disabled (WdFilter not loaded) - $summary | $perf"
8 changes: 8 additions & 0 deletions modules/win_nsclient/manifests/init.pp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@
notify => Service['nscp'],
}

# Asserts Windows Defender real-time/on-access scanning is disabled (WdFilter
# minifilter not loaded). Catches a Defender platform update re-arming it.
file { "${scripts_dir}\\check_defender.ps1":
content => file('win_nsclient/check_defender.ps1'),
require => File[$scripts_dir],
notify => Service['nscp'],
}

service { 'nscp':
ensure => running,
enable => true,
Expand Down
1 change: 1 addition & 0 deletions modules/win_nsclient/templates/nsclient.ini.epp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ worker_bootstrap_stage = powershell.exe -ExecutionPolicy RemoteSigned -File scri
thermal = powershell.exe -NoProfile -ExecutionPolicy RemoteSigned -File scripts\check_thermal.ps1
thermal_hp = powershell.exe -NoProfile -ExecutionPolicy RemoteSigned -File scripts\check_thermal_hp.ps1
worker_pool_id = powershell.exe -NoProfile -ExecutionPolicy RemoteSigned -File scripts\worker_pool_id.ps1
defender = powershell.exe -NoProfile -ExecutionPolicy RemoteSigned -File scripts\check_defender.ps1

[/settings/external scripts/alias/cpu_5s]
alias = cpu_5s
Expand Down
Loading
Loading