Separate firewall configuration between linked OUs
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Danny Bessems 2021-04-08 17:13:24 +02:00
parent ed69de5d27
commit f8a8cb80c8
4 changed files with 255 additions and 91 deletions

View File

@ -1,3 +1,6 @@
Name: 'COMP: Firewall (Clients)'
LinkedOUs:
- OU=Clients,OU=Computer accounts
FirewallRules: FirewallRules:
- Description: Rule A - Description: Rule A
Action: Block Action: Block

View File

@ -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'

View File

@ -6,104 +6,135 @@ Param(
# Only executed on primary or standalone Domain Controller # Only executed on primary or standalone Domain Controller
If (@('primary','standalone') -contains $Parameter['deployment.type']) { If (@('primary','standalone') -contains $Parameter['deployment.type']) {
$GetContentSplat = @{ $GetItemSplat = @{
Path = "$($PSScriptRoot)\$($MyInvocation.MyCommand)".Replace('.ps1', '.yml') Path = "$($PSScriptRoot)\$($MyInvocation.MyCommand)".Replace('.ps1', '.yml')
Raw = $true
} }
$RawContent = Get-Content @GetContentSplat ForEach ($File in (Get-Item @GetItemSplat)) {
$ConvertFromYamlSplat = @{ Try {
Yaml = $RawContent Write-Host "Loading/parsing file '$($File)' ..."
AllDocuments = $True $GetContentSplat = @{
} Path = $File
$YamlDocuments = ConvertFrom-Yaml @ConvertFromYamlSplat Raw = $True
}
# Check if the respective .yml file declared substitutions which need to be parsed $RawContent = Get-Content @GetContentSplat
If (($YamlDocuments.Count -gt 1) -and $YamlDocuments[-1].Variables) { $ConvertFromYamlSplat = @{
ForEach ($Pattern in $YamlDocuments[-1].Variables) { Yaml = $RawContent
$RawContent = $RawContent -replace "\{\{ ($($Pattern.Name)) \}\}", [string](Invoke-Expression -Command $Pattern.Expression) AllDocuments = $True
}
$YamlDocuments = ConvertFrom-Yaml @ConvertFromYamlSplat
} }
# Perform conversion to Yaml again, now with parsed file contents Catch {
$ConvertFromYamlSplat = @{ $ParseErrors += "While processing '$($File)': $($_.Exception.Message)"
Yaml = $RawContent Continue
AllDocuments = $True
} }
$YamlDocuments = ConvertFrom-Yaml @ConvertFromYamlSplat
$Settings = $YamlDocuments[0..($YamlDocuments.Count - 2)] # Check if the respective .yml file declared substitutions which need to be parsed
} If (($YamlDocuments.Count -gt 1) -and $YamlDocuments[-1].Variables) {
Else { Try {
$Settings = $YamlDocuments 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 = @{ $Settings = $YamlDocuments[0..($YamlDocuments.Count - 2)]
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
} }
New-NetFirewallRule @NewNetFirewallRuleSplat Else {
} $Settings = $YamlDocuments
}
ForEach ($Profile in $Settings.FirewallProfiles) {
$SetNetFirewallProfileSplat = @{ $NewGPOSplat = @{
Name = $Profile.Name Name = $Settings.Name
Enabled = $Profile.Enabled }
DefaultInboundAction = $Profile.Connections.Inbound $NewGPO = New-GPO @NewGPOSplat
DefaultOutboundAction = $Profile.Connections.Outbound
LogAllowed = $Profile.Logging.LogSuccessfullConnections $OpenNetGPOSplat = @{
LogBlocked = $Profile.Logging.LogDroppedPackets PolicyStore = "$($Parameter['addsconfig.domainname'])\$($NewGPO.DisplayName)"
LogFileName = $Profile.Logging.Name }
LogMaxSizeKilobytes = $Profile.Logging.SizeLimit $GPOSession = Open-NetGPO @OpenNetGPOSplat
AllowLocalFirewallRules = $Profile.Settings.ApplyLocalFirewallRules
AllowLocalIPsecRules = $Profile.Settings.ApplyLocalConnectionSecurityRules ForEach ($Rule in $Settings.FirewallRules) {
NotifyOnListen = $Profile.Settings.DisplayNotification $NewNetFirewallRuleSplat = @{
GPOSession = $GPOSession # Using so-called string formatting with the '-f' operator (looks more complicated than it is) to create consistent policy names:
PolicyStore = $NewGPO.DisplayName # Examples:
Confirm = $False # '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
} }
If ($ParseErrors) {
$SaveNetGPOSplat = @{ Throw "One or more errors occurred:`n$($ParseErrors -join "`n")"
GPOSession = $GPOSession
} }
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
} }

View File

@ -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'