From f6d398a7daf777d6854d05f58b88c6309c0e557a Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Wed, 4 Jan 2017 13:56:59 -0500 Subject: [PATCH] firewall: add IPv6 support This patch adds support for ip6tables rules in TripleO, in a intuitive and flexible fashion. 1) Default firewal rules 'source' parameter to undef. It was 0.0.0.0/0 before but now undef, so we don't need complex logic to support ipv6 rules. undef will create empty source, which is the same as 0.0.0.0/0 or ::/0. 2) Automatically convert icmp rules to ipv6-icmp for ipv6 rules. 3) Automatically create IPv6 rules like it's for IPv4. 4) Only create rules that can be created, depending on source/destination ip version. This patch should be backward compatible and adds a layer of security for IPv6 deployments. If previous deployments were manually creating Ipv6 rules, it's possible that this patch will override them. Our framework is able to configure any rule, so it shouldn't be a problem for upgrades. Note: the code had to be partially rewritten because of Puppet3 vs Puppet4. Co-Authored-By: Ben Nemec Co-Authored-By: Alex Schultz Closes-Bug: #1654050 Change-Id: I98a00a9ae265d3e5854632e749cc8c3a1647298c (cherry picked from commit 8c990738900cd74c2c5c046435517393d1afb92e) --- manifests/firewall/rule.pp | 32 ++++++++++-- spec/classes/tripleo_firewall_spec.rb | 70 ++++++++++++++++++++++----- 2 files changed, 84 insertions(+), 18 deletions(-) diff --git a/manifests/firewall/rule.pp b/manifests/firewall/rule.pp index 58132f01a..b193b60f6 100644 --- a/manifests/firewall/rule.pp +++ b/manifests/firewall/rule.pp @@ -49,7 +49,7 @@ # # [*source*] # (optional) The source IP address associated to the rule. -# Defaults to '0.0.0.0/0' +# Defaults to undef # # [*iniface*] # (optional) The network interface associated to the rule. @@ -74,7 +74,7 @@ define tripleo::firewall::rule ( $proto = 'tcp', $action = 'accept', $state = ['NEW'], - $source = '0.0.0.0/0', + $source = undef, $iniface = undef, $chain = 'INPUT', $destination = undef, @@ -110,6 +110,16 @@ define tripleo::firewall::rule ( 'destination' => $destination, 'jump' => $jump_real, } + if $proto == 'icmp' { + $ipv6 = { + 'provider' => 'ip6tables', + 'proto' => 'ipv6-icmp', + } + } else { + $ipv6 = { + 'provider' => 'ip6tables', + } + } if $proto != 'gre' { $state_rule = { 'state' => $state @@ -119,8 +129,10 @@ define tripleo::firewall::rule ( } - $rule = merge($basic, $state_rule, $extras) - validate_hash($rule) + $ipv4_rule = merge($basic, $state_rule, $extras) + $ipv6_rule = merge($basic, $state_rule, $ipv6, $extras) + validate_hash($ipv4_rule) + validate_hash($ipv6_rule) # This conditional will ensure that TCP and UDP firewall rules have # a port specified in the configuration when using INPUT or OUTPUT chains. @@ -131,6 +143,16 @@ define tripleo::firewall::rule ( if ($proto in ['tcp', 'udp']) and (! ($port or $dport or $sport) and ($chain != 'FORWARD')) { fail("${title} firewall rule cannot be created. TCP or UDP rules for INPUT or OUTPUT need port or sport or dport.") } - create_resources('firewall', { "${title}" => $rule }) + if $source or $destination { + if (('.' in join(any2array($destination), ',')) or ('.' in join(any2array($source), ','))){ + create_resources('firewall', { "${title} ipv4" => $ipv4_rule }) + } + if ((':' in join(any2array($destination), ',')) or (':' in join(any2array($source), ','))){ + create_resources('firewall', { "${title} ipv6" => $ipv6_rule }) + } + } else { + create_resources('firewall', { "${title} ipv4" => $ipv4_rule }) + create_resources('firewall', { "${title} ipv6" => $ipv6_rule }) + } } diff --git a/spec/classes/tripleo_firewall_spec.rb b/spec/classes/tripleo_firewall_spec.rb index 3a1a0a091..92b51e50d 100644 --- a/spec/classes/tripleo_firewall_spec.rb +++ b/spec/classes/tripleo_firewall_spec.rb @@ -34,35 +34,65 @@ describe 'tripleo::firewall' do end it 'configure basic pre firewall rules' do - is_expected.to contain_firewall('000 accept related established rules').with( + is_expected.to contain_firewall('000 accept related established rules ipv4').with( :proto => 'all', :state => ['RELATED', 'ESTABLISHED'], :action => 'accept', ) - is_expected.to contain_firewall('001 accept all icmp').with( + is_expected.to contain_firewall('000 accept related established rules ipv6').with( + :proto => 'all', + :state => ['RELATED', 'ESTABLISHED'], + :action => 'accept', + :provider => 'ip6tables', + ) + is_expected.to contain_firewall('001 accept all icmp ipv4').with( :proto => 'icmp', :action => 'accept', :state => ['NEW'], ) - is_expected.to contain_firewall('002 accept all to lo interface').with( + is_expected.to contain_firewall('001 accept all icmp ipv6').with( + :proto => 'ipv6-icmp', + :action => 'accept', + :state => ['NEW'], + :provider => 'ip6tables', + ) + is_expected.to contain_firewall('002 accept all to lo interface ipv4').with( :proto => 'all', :iniface => 'lo', :action => 'accept', :state => ['NEW'], ) - is_expected.to contain_firewall('003 accept ssh').with( + is_expected.to contain_firewall('002 accept all to lo interface ipv6').with( + :proto => 'all', + :iniface => 'lo', + :action => 'accept', + :state => ['NEW'], + :provider => 'ip6tables', + ) + is_expected.to contain_firewall('003 accept ssh ipv4').with( :dport => '22', :proto => 'tcp', :action => 'accept', :state => ['NEW'], ) + is_expected.to contain_firewall('003 accept ssh ipv6').with( + :dport => '22', + :proto => 'tcp', + :action => 'accept', + :state => ['NEW'], + :provider => 'ip6tables', + ) end it 'configure basic post firewall rules' do - is_expected.to contain_firewall('999 drop all').with( + is_expected.to contain_firewall('999 drop all ipv4').with( :proto => 'all', :action => 'drop', - :source => '0.0.0.0/0', + ) + is_expected.to contain_firewall('999 drop all ipv6').with( + :proto => 'all', + :action => 'drop', + :provider => 'ip6tables', ) end end @@ -77,41 +107,55 @@ describe 'tripleo::firewall' do '302 fwd custom cidr 1' => {'port' => 'all', 'chain' => 'FORWARD', 'destination' => '192.0.2.0/24'}, '303 add custom application 3' => {'dport' => '8081', 'proto' => 'tcp', 'action' => 'accept'}, '304 add custom application 4' => {'sport' => '1000', 'proto' => 'tcp', 'action' => 'accept'}, - '305 add gre rule' => {'proto' => 'gre'} + '305 add gre rule' => {'proto' => 'gre'}, + '306 add custom cidr 2' => {'port' => 'all', 'destination' => '::1/24'}, } ) end it 'configure custom firewall rules' do - is_expected.to contain_firewall('300 add custom application 1').with( + is_expected.to contain_firewall('300 add custom application 1 ipv4').with( :port => '999', :proto => 'udp', :action => 'accept', :state => ['NEW'], ) - is_expected.to contain_firewall('301 add custom application 2').with( + is_expected.to contain_firewall('301 add custom application 2 ipv4').with( :port => '8081', :proto => 'tcp', :action => 'accept', :state => ['NEW'], ) - is_expected.to contain_firewall('302 fwd custom cidr 1').with( + is_expected.to contain_firewall('302 fwd custom cidr 1 ipv4').with( :chain => 'FORWARD', :proto => 'tcp', :destination => '192.0.2.0/24', ) - is_expected.to contain_firewall('303 add custom application 3').with( + is_expected.to_not contain_firewall('302 fwd custom cidr 1 ipv6') + is_expected.to contain_firewall('303 add custom application 3 ipv4').with( :dport => '8081', :proto => 'tcp', :action => 'accept', :state => ['NEW'], ) - is_expected.to contain_firewall('304 add custom application 4').with( + is_expected.to contain_firewall('304 add custom application 4 ipv4').with( :sport => '1000', :proto => 'tcp', :action => 'accept', :state => ['NEW'], ) - is_expected.to contain_firewall('305 add gre rule').without(:state) + is_expected.to contain_firewall('304 add custom application 4 ipv6').with( + :sport => '1000', + :proto => 'tcp', + :action => 'accept', + :state => ['NEW'], + ) + is_expected.to contain_firewall('305 add gre rule ipv4').without(:state) + is_expected.to contain_firewall('306 add custom cidr 2 ipv6').with( + :proto => 'tcp', + :destination => '::1/24', + :action => 'accept', + :provider => 'ip6tables', + ) end end