From f8a8cb80c8011410b5e1083b2cde2c0de81856e4 Mon Sep 17 00:00:00 2001 From: djpbessems Date: Thu, 8 Apr 2021 17:13:24 +0200 Subject: [PATCH] Separate firewall configuration between linked OUs --- ...5.Firewall.yml => 05.Firewall.clients.yml} | 3 + .../scripts/05.Firewall.domaincontrollers.yml | 65 ++++++ scripts/ADDS/payload/scripts/05.Firewall.ps1 | 213 ++++++++++-------- .../payload/scripts/05.Firewall.servers.yml | 65 ++++++ 4 files changed, 255 insertions(+), 91 deletions(-) rename scripts/ADDS/payload/scripts/{05.Firewall.yml => 05.Firewall.clients.yml} (94%) create mode 100644 scripts/ADDS/payload/scripts/05.Firewall.domaincontrollers.yml create mode 100644 scripts/ADDS/payload/scripts/05.Firewall.servers.yml diff --git a/scripts/ADDS/payload/scripts/05.Firewall.yml b/scripts/ADDS/payload/scripts/05.Firewall.clients.yml similarity index 94% rename from scripts/ADDS/payload/scripts/05.Firewall.yml rename to scripts/ADDS/payload/scripts/05.Firewall.clients.yml index 3a70610..e6f12a3 100644 --- a/scripts/ADDS/payload/scripts/05.Firewall.yml +++ b/scripts/ADDS/payload/scripts/05.Firewall.clients.yml @@ -1,3 +1,6 @@ +Name: 'COMP: Firewall (Clients)' +LinkedOUs: +- OU=Clients,OU=Computer accounts FirewallRules: - Description: Rule A Action: Block diff --git a/scripts/ADDS/payload/scripts/05.Firewall.domaincontrollers.yml b/scripts/ADDS/payload/scripts/05.Firewall.domaincontrollers.yml new file mode 100644 index 0000000..11082c8 --- /dev/null +++ b/scripts/ADDS/payload/scripts/05.Firewall.domaincontrollers.yml @@ -0,0 +1,65 @@ +Name: 'COMP: Firewall (DomainControllers)' +LinkedOUs: +- OU=Domain Controllers +FirewallRules: +- Description: Rule A + Action: Block + Direction: Inbound + Program: '' + Port: '21-22,25' + Protocol: TCP +- Description: Rule B + Action: Allow + Direction: Inbound + Program: D:\MSSQL\sqlsvr.exe + Port: '' + Protocol: '' +FirewallProfiles: +- Name: Domain + Enabled: 'True' + Connections: + Inbound: Block + Outbound: Allow + Settings: + DisplayNotification: 'False' + ApplyLocalFirewallRules: 'True' + ApplyLocalConnectionSecurityRules: 'True' + Logging: + Name: '%SYSTEMROOT%\System32\Logfiles\Firewall\domainfw.log' + SizeLimit: 16384 + LogDroppedPackets: 'True' + LogSuccessfullConnections: 'False' +- Name: Private + Enabled: 'True' + Connections: + Inbound: Block + Outbound: Allow + Settings: + DisplayNotification: 'False' + ApplyLocalFirewallRules: 'True' + ApplyLocalConnectionSecurityRules: 'True' + Logging: + Name: '%SYSTEMROOT%\System32\Logfiles\Firewall\privatefw.log' + SizeLimit: 16384 + LogDroppedPackets: 'True' + LogSuccessfullConnections: 'False' +- Name: Public + Enabled: 'True' + Connections: + Inbound: Block + Outbound: Allow + Settings: + DisplayNotification: 'False' + ApplyLocalFirewallRules: 'True' + ApplyLocalConnectionSecurityRules: 'True' + Logging: + Name: '%SYSTEMROOT%\System32\Logfiles\Firewall\publicfw.log' + SizeLimit: 16384 + LogDroppedPackets: 'True' + LogSuccessfullConnections: 'False' + +# --- +# Variables: +# - Name: foo +# Expression: | +# Write-Host 'bar' \ No newline at end of file diff --git a/scripts/ADDS/payload/scripts/05.Firewall.ps1 b/scripts/ADDS/payload/scripts/05.Firewall.ps1 index aa20f1f..4423922 100644 --- a/scripts/ADDS/payload/scripts/05.Firewall.ps1 +++ b/scripts/ADDS/payload/scripts/05.Firewall.ps1 @@ -6,104 +6,135 @@ Param( # Only executed on primary or standalone Domain Controller If (@('primary','standalone') -contains $Parameter['deployment.type']) { - $GetContentSplat = @{ + $GetItemSplat = @{ Path = "$($PSScriptRoot)\$($MyInvocation.MyCommand)".Replace('.ps1', '.yml') - Raw = $true } - $RawContent = Get-Content @GetContentSplat - $ConvertFromYamlSplat = @{ - Yaml = $RawContent - AllDocuments = $True - } - $YamlDocuments = ConvertFrom-Yaml @ConvertFromYamlSplat - - # Check if the respective .yml file declared substitutions which need to be parsed - If (($YamlDocuments.Count -gt 1) -and $YamlDocuments[-1].Variables) { - ForEach ($Pattern in $YamlDocuments[-1].Variables) { - $RawContent = $RawContent -replace "\{\{ ($($Pattern.Name)) \}\}", [string](Invoke-Expression -Command $Pattern.Expression) + ForEach ($File in (Get-Item @GetItemSplat)) { + Try { + Write-Host "Loading/parsing file '$($File)' ..." + $GetContentSplat = @{ + Path = $File + Raw = $True + } + $RawContent = Get-Content @GetContentSplat + $ConvertFromYamlSplat = @{ + Yaml = $RawContent + AllDocuments = $True + } + $YamlDocuments = ConvertFrom-Yaml @ConvertFromYamlSplat } - # Perform conversion to Yaml again, now with parsed file contents - $ConvertFromYamlSplat = @{ - Yaml = $RawContent - AllDocuments = $True + Catch { + $ParseErrors += "While processing '$($File)': $($_.Exception.Message)" + Continue } - $YamlDocuments = ConvertFrom-Yaml @ConvertFromYamlSplat - $Settings = $YamlDocuments[0..($YamlDocuments.Count - 2)] - } - Else { - $Settings = $YamlDocuments - } + + # Check if the respective .yml file declared substitutions which need to be parsed + If (($YamlDocuments.Count -gt 1) -and $YamlDocuments[-1].Variables) { + Try { + ForEach ($Pattern in $YamlDocuments[-1].Variables) { + $RawContent = $RawContent -replace "\{\{ ($($Pattern.Name)) \}\}", [string](Invoke-Expression -Command $Pattern.Expression) + } + # Perform conversion to Yaml again, now with parsed file contents + $ConvertFromYamlSplat = @{ + Yaml = $RawContent + AllDocuments = $True + } + $YamlDocuments = ConvertFrom-Yaml @ConvertFromYamlSplat + } + Catch { + $ParseErrors += "While processing '$($File)' (after substitutions): $($_.Exception.Message)" + Continue + } - $NewGPOSplat = @{ - Name = 'COMP: Firewall (Servers)' - } - $NewGPO = New-GPO @NewGPOSplat - - $OpenNetGPOSplat = @{ - PolicyStore = "$($Parameter['addsconfig.domainname'])\$($NewGPO.DisplayName)" - } - $GPOSession = Open-NetGPO @OpenNetGPOSplat - - ForEach ($Rule in $Settings.FirewallRules) { - $NewNetFirewallRuleSplat = @{ - # Using so-called string formatting with the '-f' operator (looks more complicated than it is) to create consistent policy names: - # Examples: - # 'DENY: Inbound port 443 (TCP)' - # 'ALLOW: Inbound 'D:\MSSQL\bin\sqlservr.exe' - DisplayName = ("{0}: {1} {2} {3} {4}" -f - $Rule.Action.ToUpper(), - $Rule.Direction, - ("'$($Rule.Program)'", $NULL)[!($Rule.Program)], - ("Port $($Rule.Port)", $NULL)[!($Rule.Port)], - ("($($Rule.Protocol))", $NULL)[!($Rule.Protocol)] - ) -replace '\s+',' ' - Description = $Rule.Description - Action = $Rule.Action - Direction = $Rule.Direction - Program = ($Rule.Program, 'Any')[!($Rule.Program)] - LocalPort = ($Rule.Port.Split(','), 'Any')[!($Rule.Port)] - Protocol = ($Rule.Protocol, 'Any')[!($Rule.Protocol)] - GPOSession = $GPOSession - PolicyStore = $NewGPO.DisplayName - Confirm = $False + $Settings = $YamlDocuments[0..($YamlDocuments.Count - 2)] } - New-NetFirewallRule @NewNetFirewallRuleSplat - } - - ForEach ($Profile in $Settings.FirewallProfiles) { - $SetNetFirewallProfileSplat = @{ - Name = $Profile.Name - Enabled = $Profile.Enabled - DefaultInboundAction = $Profile.Connections.Inbound - DefaultOutboundAction = $Profile.Connections.Outbound - LogAllowed = $Profile.Logging.LogSuccessfullConnections - LogBlocked = $Profile.Logging.LogDroppedPackets - LogFileName = $Profile.Logging.Name - LogMaxSizeKilobytes = $Profile.Logging.SizeLimit - AllowLocalFirewallRules = $Profile.Settings.ApplyLocalFirewallRules - AllowLocalIPsecRules = $Profile.Settings.ApplyLocalConnectionSecurityRules - NotifyOnListen = $Profile.Settings.DisplayNotification - GPOSession = $GPOSession - PolicyStore = $NewGPO.DisplayName - Confirm = $False + Else { + $Settings = $YamlDocuments + } + + $NewGPOSplat = @{ + Name = $Settings.Name + } + $NewGPO = New-GPO @NewGPOSplat + + $OpenNetGPOSplat = @{ + PolicyStore = "$($Parameter['addsconfig.domainname'])\$($NewGPO.DisplayName)" + } + $GPOSession = Open-NetGPO @OpenNetGPOSplat + + ForEach ($Rule in $Settings.FirewallRules) { + $NewNetFirewallRuleSplat = @{ + # Using so-called string formatting with the '-f' operator (looks more complicated than it is) to create consistent policy names: + # Examples: + # 'DENY: Inbound port 443 (TCP)' + # 'ALLOW: Inbound 'D:\MSSQL\bin\sqlservr.exe' + DisplayName = ("{0}: {1} {2} {3} {4}" -f + $Rule.Action.ToUpper(), + $Rule.Direction, + ("'$($Rule.Program)'", $NULL)[!($Rule.Program)], + ("Port $($Rule.Port)", $NULL)[!($Rule.Port)], + ("($($Rule.Protocol))", $NULL)[!($Rule.Protocol)] + ) -replace '\s+',' ' + Description = $Rule.Description + Action = $Rule.Action + Direction = $Rule.Direction + Program = ($Rule.Program, 'Any')[!($Rule.Program)] + LocalPort = ($Rule.Port.Split(','), 'Any')[!($Rule.Port)] + Protocol = ($Rule.Protocol, 'Any')[!($Rule.Protocol)] + GPOSession = $GPOSession + PolicyStore = $NewGPO.DisplayName + Confirm = $False + } + New-NetFirewallRule @NewNetFirewallRuleSplat + } + + ForEach ($Profile in $Settings.FirewallProfiles) { + $SetNetFirewallProfileSplat = @{ + Name = $Profile.Name + Enabled = $Profile.Enabled + DefaultInboundAction = $Profile.Connections.Inbound + DefaultOutboundAction = $Profile.Connections.Outbound + LogAllowed = $Profile.Logging.LogSuccessfullConnections + LogBlocked = $Profile.Logging.LogDroppedPackets + LogFileName = $Profile.Logging.Name + LogMaxSizeKilobytes = $Profile.Logging.SizeLimit + AllowLocalFirewallRules = $Profile.Settings.ApplyLocalFirewallRules + AllowLocalIPsecRules = $Profile.Settings.ApplyLocalConnectionSecurityRules + NotifyOnListen = $Profile.Settings.DisplayNotification + GPOSession = $GPOSession + PolicyStore = $NewGPO.DisplayName + Confirm = $False + } + Set-NetFirewallProfile @SetNetFirewallProfileSplat + } + + $SaveNetGPOSplat = @{ + GPOSession = $GPOSession + } + Save-NetGPO @SaveNetGPOSplat + + ForEach ($OU in $Settings.LinkedOUs) { + If (Test-Path "AD:\$($OU + (',{0}' -f (Get-ADRootDSE).rootDomainNamingContext))") { + Try { + Write-Host "Linking policy '$($NewGPO.DisplayName)' to OU '$($OU)' ..." + $NewGPLinkSplat = @{ + Name = $NewGPO.DisplayName + Target = $OU + (',{0}' -f (Get-ADRootDSE).rootDomainNamingContext) + } + New-GPLink @NewGPLinkSplat | Out-Null + } + Catch { + $ParseErrors += "Could not link GPO '$($NewGPO.DisplayName)' to OU '$($OU)'" + Continue + } + } + Else { + $ParseErrors += "Path not accessible (referred to by '$($NewGPO.DisplayName)'): 'AD:\$($OU + (',{0}' -f (Get-ADRootDSE).rootDomainNamingContext))'" + Continue + } } - Set-NetFirewallProfile @SetNetFirewallProfileSplat } - - $SaveNetGPOSplat = @{ - GPOSession = $GPOSession + If ($ParseErrors) { + Throw "One or more errors occurred:`n$($ParseErrors -join "`n")" } - Save-NetGPO @SaveNetGPOSplat - - $NewGPLinkSplat = @{ - Name = $NewGPO.DisplayName -# Should probably be configurable through yml - Target = 'OU=Servers,OU=Computer accounts,DC=' + $Parameter['addsconfig.domainname'].Replace('.', ',DC=') - } - New-GPLink @NewGPLinkSplat - $NewGPLinkSplat = @{ - Name = $NewGPO.DisplayName - Target = 'OU=Domain Controllers,DC=' + $Parameter['addsconfig.domainname'].Replace('.', ',DC=') - } - New-GPLink @NewGPLinkSplat } diff --git a/scripts/ADDS/payload/scripts/05.Firewall.servers.yml b/scripts/ADDS/payload/scripts/05.Firewall.servers.yml new file mode 100644 index 0000000..946ad9b --- /dev/null +++ b/scripts/ADDS/payload/scripts/05.Firewall.servers.yml @@ -0,0 +1,65 @@ +Name: 'COMP: Firewall (Servers)' +LinkedOUs: +- OU=Servers,OU=Computer accounts +FirewallRules: +- Description: Rule A + Action: Block + Direction: Inbound + Program: '' + Port: '21-22,25' + Protocol: TCP +- Description: Rule B + Action: Allow + Direction: Inbound + Program: D:\MSSQL\sqlsvr.exe + Port: '' + Protocol: '' +FirewallProfiles: +- Name: Domain + Enabled: 'True' + Connections: + Inbound: Block + Outbound: Allow + Settings: + DisplayNotification: 'False' + ApplyLocalFirewallRules: 'True' + ApplyLocalConnectionSecurityRules: 'True' + Logging: + Name: '%SYSTEMROOT%\System32\Logfiles\Firewall\domainfw.log' + SizeLimit: 16384 + LogDroppedPackets: 'True' + LogSuccessfullConnections: 'False' +- Name: Private + Enabled: 'True' + Connections: + Inbound: Block + Outbound: Allow + Settings: + DisplayNotification: 'False' + ApplyLocalFirewallRules: 'True' + ApplyLocalConnectionSecurityRules: 'True' + Logging: + Name: '%SYSTEMROOT%\System32\Logfiles\Firewall\privatefw.log' + SizeLimit: 16384 + LogDroppedPackets: 'True' + LogSuccessfullConnections: 'False' +- Name: Public + Enabled: 'True' + Connections: + Inbound: Block + Outbound: Allow + Settings: + DisplayNotification: 'False' + ApplyLocalFirewallRules: 'True' + ApplyLocalConnectionSecurityRules: 'True' + Logging: + Name: '%SYSTEMROOT%\System32\Logfiles\Firewall\publicfw.log' + SizeLimit: 16384 + LogDroppedPackets: 'True' + LogSuccessfullConnections: 'False' + +# --- +# Variables: +# - Name: foo +# Expression: | +# Write-Host 'bar' \ No newline at end of file