diff --git a/manifests/exception.pp b/manifests/exception.pp index e13dcee..6a8c24e 100644 --- a/manifests/exception.pp +++ b/manifests/exception.pp @@ -8,7 +8,7 @@ # # === Requirements/Dependencies # -# Currently reequires the puppetlabs/stdlib module on the Puppet Forge in +# Currently requires the puppetlabs/stdlib module on the Puppet Forge in # order to validate much of the the provided configuration. # # === Parameters @@ -81,119 +81,168 @@ Enum['in', 'out'] $direction = 'in', Enum['allow', 'block'] $action = 'allow', Boolean $enabled = true, - Optional[Enum['TCP', 'UDP', 'ICMPv4', 'ICMPv6']] $protocol = undef, - Optional[Variant[Integer[1, 65535], Enum['any']]] $local_port = undef, - Optional[Variant[Integer[1, 65535], Enum['any']]] $remote_port = undef, + Optional[ + Enum['TCP', 'UDP', 'ICMPv4', 'ICMPv6'] + ] $protocol = undef, + Optional[ + Variant[ + Integer[1, 65535], + Enum['any','RPC','RPC-EPMap'], + Pattern[/\A[1-9]{1}\Z|[1-9]{1}[0-9,-]*[0-9]{1}\Z/] + ] + ] $local_port = undef, + Optional[ + Variant[ + Integer[1, 65535], + Enum['any'], + Pattern[/\A[1-9]{1}\Z|[1-9]{1}[0-9,-]*[0-9]{1}\Z/] + ] + ]$remote_port = undef, Optional[String] $remote_ip = undef, Optional[String] $program = undef, String[0, 255] $display_name = '', - String $description = '', + String $description = 'windows_firewall::exception generated rule', Boolean $allow_edge_traversal = false, ) { - # Check if we're allowing a program or port/protocol and validate accordingly - if $program == undef { - #check whether to use 'localport', or just 'port' depending on OS - case $::operatingsystemversion { - /Windows Server 2003/, /Windows XP/: { - $local_port_param = 'port' - unless empty($remote_port) { - fail "Sorry, :remote_port param is not supported on ${::operatingsystemversion}" - } - } - default: { - $local_port_param = 'localport' - $remote_port_param = 'remoteport' - } + if($protocol){ + if ($protocol =~ /^ICMPv(4|6)/) and ($remote_port or $local_port) { + fail 'Sorry, local and/or remote ports are not needed when protocol is ICMP' + } + + if ($protocol == 'UDP') and ($local_port in ['RPC','RPC-EPMap']){ + fail 'Sorry, RPC and RPC-EPMap local ports require TCP' + } + } + + #check whether to use 'localport', or just 'port' depending on OS + case $::operatingsystemversion { + /Windows Server 2003/, /Windows XP/: { + $local_port_param = 'port' + unless empty($remote_port) { + fail "Sorry, :remote_port param is not supported on ${::operatingsystemversion}" } + } + default: { + $local_port_param = 'localport' + $remote_port_param = 'remoteport' + } + } + + if $remote_port or $local_port { + unless $protocol { + fail 'Sorry, protocol is required, when defining local or remote port' + } + } - $fw_command = 'portopening' + if $local_port { + $local_port_cmd = "${local_port_param}=\"${local_port}\"" + } else { + $local_port_cmd = '' + } - if $remote_port or $local_port { - unless $protocol { - fail 'Sorry, protocol is required, when defining local or remote port' - } + if $remote_port { + $remote_port_cmd = "${remote_port_param}=\"${remote_port}\"" + } else { + $remote_port_cmd = '' + } + + if($protocol){ + $protocol_cmd = "protocol=${protocol}" + }else{ + $protocol_cmd = undef + } + + case $::operatingsystemversion { + 'Windows Server 2012', 'Windows Server 2008', 'Windows Server 2008 R2', 'Windows Vista','Windows 7','Windows 8': { + validate_slength($description,255) + } + default: { } + } + + # Set command to check for existing rules + $netsh_exe = "${facts['os']['windows']['system32']}\\netsh.exe" + + case $::operatingsystemversion { + /Windows Server 2003/, /Windows XP/: { + $mode = $enabled ? { + true => 'ENABLE', + false => 'DISABLE', } + if $program == undef { + + $fw_command = 'portopening' + $allow_context = rstrip("${protocol_cmd} ${local_port_cmd}") + $check_rule_existance= "${netsh_exe} firewall show portopening | find \"${display_name}\"" + $program_cmd = undef - if $protocol =~ /^ICMPv(4|6)/ { - $allow_context = "protocol=${protocol}" } else { - if $local_port { - $local_port_cmd = "${local_port_param}=${local_port}" - } else { - $local_port_cmd = '' - } - - if $remote_port { - $remote_port_cmd = "${remote_port_param}=${remote_port}" - } else { - $remote_port_cmd = '' - } - - # Strip whitespace that in case remore_port_cmd is empty - $allow_context = rstrip("protocol=${protocol} ${local_port_cmd} ${remote_port_cmd}") + $fw_command = 'allowedprogram' + $program_cmd = "program=\"${program}\"" + validate_absolute_path($program) + $allow_context = $program_cmd + $check_rule_existance= "${netsh_exe} firewall show allowedprogram | find \"${display_name}\"" } - } else { - $fw_command = 'allowedprogram' - $allow_context = "program=\"${program}\"" - validate_absolute_path($program) - } - case $::operatingsystemversion { - 'Windows Server 2012', 'Windows Server 2008', 'Windows Server 2008 R2', 'Windows Vista','Windows 7','Windows 8': { - validate_slength($description,255) + if $ensure == 'present' { + $fw_action = 'add' + } else { + $fw_action = 'delete' } - default: { } - } - # Set command to check for existing rules - $netsh_exe = "${facts['os']['windows']['system32']}\\netsh.exe" - $check_rule_existance= "${netsh_exe} advfirewall firewall show rule name=\"${display_name}\"" - - # Use unless for exec if we want the rule to exist, include a description - if $ensure == 'present' { - $fw_action = 'add' - $unless = $check_rule_existance - $onlyif = undef - $fw_description = "description=\"${description}\"" - } else { - # Or onlyif if we expect it to be absent; no description argument - $fw_action = 'delete' - $onlyif = $check_rule_existance - $unless = undef - $fw_description = '' + $netsh_command = "${netsh_exe} firewall ${fw_action} ${fw_command} name=\"${display_name}\" mode=${mode} ${allow_context}" } + default: { + $check_rule_existance= "${netsh_exe} advfirewall firewall show rule name=\"${display_name}\"" + if $program { + $program_cmd = "program=\"${program}\"" + validate_absolute_path($program) + }else{ + $program_cmd = undef + } - case $::operatingsystemversion { - /Windows Server 2003/, /Windows XP/: { - $mode = $enabled ? { - true => 'ENABLE', - false => 'DISABLE', - } - $netsh_command = "${netsh_exe} firewall ${fw_action} ${fw_command} name=\"${display_name}\" mode=${mode} ${allow_context}" + $mode = $enabled ? { + true => 'yes', + false => 'no', } - default: { - $mode = $enabled ? { - true => 'yes', - false => 'no', - } - $edge = $allow_edge_traversal ? { - true => 'yes', - false => 'no', - } - - if $fw_action == 'delete' and $program == undef { - $netsh_command = "${netsh_exe} advfirewall firewall ${fw_action} rule name=\"${display_name}\" ${fw_description} dir=${direction} ${allow_context} remoteip=\"${remote_ip}\"" - } else { - $netsh_command = "${netsh_exe} advfirewall firewall ${fw_action} rule name=\"${display_name}\" ${fw_description} dir=${direction} action=${action} enable=${mode} edge=${edge} ${allow_context} remoteip=\"${remote_ip}\"" - } + $edge = $allow_edge_traversal ? { + true => 'yes', + false => 'no', } - } - exec { "set rule ${display_name}": - command => $netsh_command, - provider => windows, - onlyif => $onlyif, - unless => $unless, + if($remote_ip){ + $remote_ip_cmd = "remoteip=\"${remote_ip}\"" + }else{ + $remote_ip_cmd = undef + } + + $allow_context = regsubst("action=${action} enable=${mode} edge=${edge} ${program_cmd} ${protocol_cmd} ${local_port_cmd} ${remote_port_cmd} ${remote_ip_cmd} profile=\"Any\"",'[\s]{2,}',' ','G') + + + if $ensure != 'present' { + $fw_description = '' + $netsh_command = "${netsh_exe} advfirewall firewall delete rule name=\"${display_name}\"" + } else { + $fw_description = "description=\"${description}\"" + $netsh_command = "${netsh_exe} advfirewall firewall add rule name=\"${display_name}\" description=\"${description}\" dir=${direction} ${allow_context}" + } } + } + + + if $ensure == 'present' { + $unless = $check_rule_existance + $onlyif = undef + } else { + + $onlyif = $check_rule_existance + $unless = undef + } + + exec { "set rule ${display_name}": + command => $netsh_command, + provider => windows, + onlyif => $onlyif, + unless => $unless, + } } diff --git a/manifests/init.pp b/manifests/init.pp index 02b2467..b6f087b 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -8,7 +8,7 @@ # # === Requirements/Dependencies # -# Currently reequires the puppetlabs/stdlib module on the Puppet Forge in +# Currently requires the puppetlabs/stdlib module on the Puppet Forge in # order to validate much of the the provided configuration. # # === Parameters @@ -18,7 +18,7 @@ # # === Examples # -# To ensure that windows_firwall is running: +# To ensure that windows_firewall is running: # # include ::windows_firewall # diff --git a/spec/defines/windows_firewall/exception_spec.rb b/spec/defines/windows_firewall/exception_spec.rb index 1829a7a..9b37620 100644 --- a/spec/defines/windows_firewall/exception_spec.rb +++ b/spec/defines/windows_firewall/exception_spec.rb @@ -24,7 +24,7 @@ it do is_expected.to contain_exec('set rule Windows Remote Management').with( - 'command' => 'C:\\windows\\system32\\netsh.exe firewall add portopening name="Windows Remote Management" mode=ENABLE protocol=TCP port=5985', + 'command' => 'C:\\windows\\system32\\netsh.exe firewall add portopening name="Windows Remote Management" mode=ENABLE protocol=TCP port="5985"', 'provider' => 'windows' ) end @@ -54,7 +54,7 @@ it do is_expected.to contain_exec('set rule Windows Remote Management').with( - 'command' => 'C:\\windows\\system32\\netsh.exe advfirewall firewall add rule name="Windows Remote Management" description="Inbound rule for WinRM" dir=in action=allow enable=yes edge=no protocol=TCP localport=5985 remoteport=any remoteip=""', + 'command' => 'C:\\windows\\system32\\netsh.exe advfirewall firewall add rule name="Windows Remote Management" description="Inbound rule for WinRM" dir=in action=allow enable=yes edge=no protocol=TCP localport="5985" remoteport="any" profile="Any"', 'provider' => 'windows' ) end @@ -114,7 +114,7 @@ it do is_expected.to contain_exec('set rule Windows Remote Management').with( - 'command' => 'C:\\windows\\system32\\netsh.exe advfirewall firewall add rule name="Windows Remote Management" description="Inbound rule for WinRM" dir=in action=allow enable=yes edge=no program="C:\\foo.exe" remoteip=""', + 'command' => 'C:\\windows\\system32\\netsh.exe advfirewall firewall add rule name="Windows Remote Management" description="Inbound rule for WinRM" dir=in action=allow enable=yes edge=no program="C:\\foo.exe" profile="Any"', 'provider' => 'windows' ) end @@ -144,7 +144,7 @@ it do is_expected.to contain_exec('set rule Windows Remote Management').with( - 'command' => 'C:\\windows\\system32\\netsh.exe firewall delete portopening name="Windows Remote Management" mode=ENABLE protocol=TCP port=5985', + 'command' => 'C:\\windows\\system32\\netsh.exe firewall delete portopening name="Windows Remote Management" mode=ENABLE protocol=TCP port="5985"', 'provider' => 'windows' ) end @@ -174,7 +174,7 @@ it do is_expected.to contain_exec('set rule Windows Remote Management').with( - 'command' => 'C:\\windows\\system32\\netsh.exe advfirewall firewall delete rule name="Windows Remote Management" dir=in protocol=TCP localport=5985 remoteport=any remoteip=""', + 'command' => 'C:\\windows\\system32\\netsh.exe advfirewall firewall delete rule name="Windows Remote Management"', 'provider' => 'windows' ) end @@ -234,7 +234,7 @@ it do is_expected.to contain_exec('set rule Windows Remote Management').with( - 'command' => 'C:\\windows\\system32\\netsh.exe advfirewall firewall delete rule name="Windows Remote Management" dir=in action=allow enable=yes edge=no program="C:\\foo.exe" remoteip=""', + 'command' => 'C:\\windows\\system32\\netsh.exe advfirewall firewall delete rule name="Windows Remote Management"', 'provider' => 'windows' ) end @@ -577,4 +577,148 @@ end end end + ['Windows Server 2012', 'Windows Server 2008', 'Windows Server 2008 R2', 'Windows 8', 'Windows 7', 'Windows Vista'].each do |os| + context "port rule with OS: #{os}, ensure: present" do + let :facts do + { + operatingsystemversion: os, + os: { + windows: { + system32: 'C:\\windows\\system32' + } + } + } + end + let(:title) { 'Windows Remote Management' } + let :params do + { + ensure: 'present', direction: 'in', action: 'allow', enabled: true, + protocol: 'TCP', local_port: '1000-2000,2048', remote_port: 'any', + display_name: 'Windows Remote Management', description: 'Inbound rule for WinRM' + } + end + + it do + is_expected.to contain_exec('set rule Windows Remote Management').with( + 'command' => 'C:\\windows\\system32\\netsh.exe advfirewall firewall add rule name="Windows Remote Management" description="Inbound rule for WinRM" dir=in action=allow enable=yes edge=no protocol=TCP localport="1000-2000,2048" remoteport="any" profile="Any"', + 'provider' => 'windows' + ) + end + end + end + ['Windows Server 2012', 'Windows Server 2008', 'Windows Server 2008 R2', 'Windows 8', 'Windows 7', 'Windows Vista'].each do |os| + context "port rule with OS: #{os}, ensure: present" do + let :facts do + { + operatingsystemversion: os, + os: { + windows: { + system32: 'C:\\windows\\system32' + } + } + } + end + let(:title) { 'Windows Remote Management' } + let :params do + { + ensure: 'present', direction: 'in', action: 'allow', enabled: true, + protocol: 'TCP', local_port: 'RPC', remote_port: 'any', + display_name: 'Windows Remote Management', description: 'Inbound rule for WinRM' + } + end + + it do + is_expected.to contain_exec('set rule Windows Remote Management').with( + 'command' => 'C:\\windows\\system32\\netsh.exe advfirewall firewall add rule name="Windows Remote Management" description="Inbound rule for WinRM" dir=in action=allow enable=yes edge=no protocol=TCP localport="RPC" remoteport="any" profile="Any"', + 'provider' => 'windows' + ) + end + end + end + ['Windows Server 2012', 'Windows Server 2008', 'Windows Server 2008 R2', 'Windows 8', 'Windows 7', 'Windows Vista'].each do |os| + context "with invalid custom param: os => #{os}, action => invalid" do + let :facts do + { + operatingsystemversion: os, + os: { + windows: { + system32: 'C:\\windows\\system32' + } + } + } + end + let(:title) { 'Windows Remote Management' } + let :params do + { + ensure: 'present', direction: 'in', action: 'allow', enabled: true, + protocol: 'UDP', local_port: 'RPC', + display_name: 'Windows Remote Management', description: 'Inbound rule for WinRM' + } + end + + it do + expect do + is_expected.to contain_exec('set rule Windows Remote Management') + end.to raise_error(Puppet::Error, %r{RPC and RPC-EPMap local ports require TCP}) + end + end + end + ['Windows Server 2012', 'Windows Server 2008', 'Windows Server 2008 R2', 'Windows 8', 'Windows 7', 'Windows Vista'].each do |os| + context "with invalid custom param: os => #{os}, action => invalid" do + let :facts do + { + operatingsystemversion: os, + os: { + windows: { + system32: 'C:\\windows\\system32' + } + } + } + end + let(:title) { 'Windows Remote Management' } + let :params do + { + ensure: 'present', direction: 'in', action: 'allow', enabled: true, + protocol: 'ICMPv4', local_port: 'RPC', + display_name: 'Windows Remote Management', description: 'Inbound rule for WinRM' + } + end + + it do + expect do + is_expected.to contain_exec('set rule Windows Remote Management') + end.to raise_error(Puppet::Error, %r{local and/or remote ports are not needed when protocol is ICMP}) + end + end + end + ['Windows Server 2012', 'Windows Server 2008', 'Windows Server 2008 R2', 'Windows 8', 'Windows 7', 'Windows Vista'].each do |os| + context "port rule with OS: #{os}, ensure: present" do + let :facts do + { + operatingsystemversion: os, + os: { + windows: { + system32: 'C:\\windows\\system32' + } + } + } + end + let(:title) { 'Windows Remote Management' } + let :params do + { + ensure: 'present', direction: 'in', action: 'allow', enabled: true, + protocol: 'TCP', local_port: 'RPC', remote_port: 'any', + display_name: 'Windows Remote Management', description: 'Inbound rule for WinRM', + remote_ip: '10.10.10.10' + } + end + + it do + is_expected.to contain_exec('set rule Windows Remote Management').with( + 'command' => 'C:\\windows\\system32\\netsh.exe advfirewall firewall add rule name="Windows Remote Management" description="Inbound rule for WinRM" dir=in action=allow enable=yes edge=no protocol=TCP localport="RPC" remoteport="any" remoteip="10.10.10.10" profile="Any"', + 'provider' => 'windows' + ) + end + end + end end