Separate firewall configuration between linked OUs
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				continuous-integration/drone/push Build is passing
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	continuous-integration/drone/push Build is passing
				
			This commit is contained in:
		| @@ -1,3 +1,6 @@ | ||||
| Name: 'COMP: Firewall (Clients)' | ||||
| LinkedOUs: | ||||
| - OU=Clients,OU=Computer accounts | ||||
| FirewallRules: | ||||
| - Description: Rule A | ||||
|   Action: Block | ||||
| @@ -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' | ||||
| @@ -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 | ||||
| } | ||||
|   | ||||
							
								
								
									
										65
									
								
								scripts/ADDS/payload/scripts/05.Firewall.servers.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								scripts/ADDS/payload/scripts/05.Firewall.servers.yml
									
									
									
									
									
										Normal 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' | ||||
		Reference in New Issue
	
	Block a user