<# SOC onboarding - Windows Installs Wazuh agent (enrolled to the SOC) + CrowdSec with Windows Firewall bouncer. Usage (PowerShell as Administrator): powershell -ExecutionPolicy Bypass -File .\soc-install-windows.ps1 -Group -Password [-Name ] [-NoCrowdSec] #> #Requires -RunAsAdministrator param( [Parameter(Mandatory = $true)][string]$Group, [Parameter(Mandatory = $true)][string]$Password, [string]$Name = $env:COMPUTERNAME, [switch]$NoCrowdSec ) $ErrorActionPreference = 'Stop' $ProgressPreference = 'SilentlyContinue' [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 $Manager = 'soc.cdnfee.com' $WazuhVersion = '4.14.10-1' $CrowdSecVersion = '1.8.2' $BouncerVersion = '0.0.6' function Log($m) { Write-Host "[SOC] $m" -ForegroundColor Cyan } function Warn($m) { Write-Host "[SOC] WARNING: $m" -ForegroundColor Yellow } function Die($m) { Write-Host "[SOC] ERROR: $m" -ForegroundColor Red; exit 1 } if ($Group -notmatch '^c-[a-z0-9-]+$') { Die 'invalid enrollment code' } if (Get-Service -Name WazuhSvc -ErrorAction SilentlyContinue) { Die 'Wazuh agent already installed - aborting' } $tmp = Join-Path $env:TEMP ("soc-" + [guid]::NewGuid().ToString('N')) New-Item -ItemType Directory -Path $tmp | Out-Null function Install-Msi($url, $file, $msiArgs) { $path = Join-Path $tmp $file Log "Downloading $file ..." Invoke-WebRequest -Uri $url -OutFile $path -UseBasicParsing $sig = Get-AuthenticodeSignature $path if ($sig.Status -ne 'Valid') { Die "$file signature is not valid ($($sig.Status))" } $p = Start-Process msiexec.exe -ArgumentList (@('/i', "`"$path`"", '/qn', '/norestart') + $msiArgs) -Wait -PassThru if ($p.ExitCode -notin 0, 3010) { Die "$file install failed (exit $($p.ExitCode))" } } # ---------- connectivity ---------- Log "Checking connectivity to $Manager ..." foreach ($port in 1514, 1515) { if (-not (Test-NetConnection $Manager -Port $port -InformationLevel Quiet -WarningAction SilentlyContinue)) { Die "cannot reach ${Manager}:$port (check outbound firewall)" } } # ---------- Wazuh agent ---------- Install-Msi "https://packages.wazuh.com/4.x/windows/wazuh-agent-$WazuhVersion.msi" "wazuh-agent.msi" @( "WAZUH_MANAGER=$Manager", "WAZUH_REGISTRATION_SERVER=$Manager", "WAZUH_REGISTRATION_PASSWORD=$Password", "WAZUH_AGENT_GROUP=$Group", "WAZUH_AGENT_NAME=$Name") Start-Service WazuhSvc $ossecLog = 'C:\Program Files (x86)\ossec-agent\ossec.log' Log 'Waiting for the agent to connect ...' $ok = $false for ($i = 0; $i -lt 30; $i++) { Start-Sleep 2 if (Test-Path $ossecLog) { $c = Get-Content $ossecLog -Raw if ($c -match 'Connected to the server') { $ok = $true; break } if ($c -match 'Invalid password|Invalid group|Duplicate agent name') { break } } } if (-not $ok) { if (Test-Path $ossecLog) { Get-Content $ossecLog | Select-String 'ERROR|Invalid' | Select-Object -Last 5 } Die 'Wazuh agent did not connect - check the enrollment code/password above' } Log 'Wazuh agent connected.' # ---------- CrowdSec ---------- if (-not $NoCrowdSec) { Install-Msi "https://github.com/crowdsecurity/crowdsec/releases/download/v$CrowdSecVersion/crowdsec_$CrowdSecVersion.msi" "crowdsec.msi" @() $cscli = 'C:\Program Files\CrowdSec\cscli.exe' $csConf = 'C:\ProgramData\CrowdSec\config' if (-not (Test-Path $cscli)) { Die 'CrowdSec installed but cscli.exe not found' } # IIS if (Get-Service -Name W3SVC -ErrorAction SilentlyContinue) { Log 'IIS detected - enabling CrowdSec IIS collection' & $cscli collections install crowdsecurity/iis | Out-Null $acq = Join-Path $csConf 'acquis.yaml' if (-not (Select-String -Path $acq -Pattern 'type: iis' -Quiet)) { Add-Content $acq "`n---`nuse_time_machine: true`nfilenames:`n - C:\inetpub\logs\LogFiles\*\*.log`nlabels:`n type: iis`n" } } # CrowdSec alerts -> JSON file read by Wazuh (if the file plugin is shipped on this build) $plugin = Get-ChildItem 'C:\Program Files\CrowdSec\plugins' -Filter 'notification-file*' -ErrorAction SilentlyContinue | Select-Object -First 1 if ($plugin) { New-Item -ItemType Directory -Force -Path 'C:\ProgramData\CrowdSec\alerts' | Out-Null $fileYaml = @' type: file name: file_default log_level: info format: | {{range . -}} {"crowdsec":{"scenario":{{.Scenario | toJson}},"source_ip":{{.Source.IP | toJson}},"source_range":{{.Source.Range | toJson}},"country":{{.Source.Cn | toJson}},"as_name":{{.Source.AsName | toJson}},"events_count":{{.EventsCount}},"machine":{{.MachineID | toJson}},"decision":"{{range .Decisions}}{{.Type}} {{.Duration}}{{end}}","message":{{.Message | toJson}}}} {{ end -}} log_path: "C:\\ProgramData\\CrowdSec\\alerts\\alerts.json" rotate: enabled: true max_size: 100 max_files: 5 max_age: 30 compress: true '@ Set-Content -Path (Join-Path $csConf 'notifications\file.yaml') -Value $fileYaml -Encoding ascii $prof = Join-Path $csConf 'profiles.yaml' $pt = Get-Content $prof -Raw if ($pt -notmatch 'file_default') { Copy-Item $prof "$prof.orig" $pt = $pt -replace '(?m)^on_success: break', "notifications:`r`n - file_default`r`non_success: break" Set-Content -Path $prof -Value $pt -Encoding ascii } } else { Warn 'CrowdSec file notification plugin not found - CrowdSec alerts will not be forwarded to the SOC.' } Restart-Service crowdsec Install-Msi "https://github.com/crowdsecurity/cs-windows-firewall-bouncer/releases/download/v$BouncerVersion/cs_windows_firewall_bouncer_setup.msi" "cs-fw-bouncer.msi" @() Log 'CrowdSec running with Windows Firewall bouncer.' } Remove-Item $tmp -Recurse -Force -ErrorAction SilentlyContinue Log "Done. Agent '$Name' enrolled in SOC ($Group)."