diff --git a/README.markdown b/README.markdown index d14143ed7..2d146debb 100644 --- a/README.markdown +++ b/README.markdown @@ -518,6 +518,8 @@ firewall { '999 this runs last': * `physdev_out`: Match if the packet is leaving a bridge via the given interface. Values must match '/^[a-zA-Z0-9\-\._\+]+$/'. +* `physdev_is_bridged`: Match if the packet is transversing a bridge. Valid values are true or false. + * `pkttype`: Sets the packet type to match. Valid values are: 'unicast', 'broadcast', and'multicast'. Requires the `pkttype` feature. * `port`: The destination or source port to match for this filter (if the protocol supports ports). Will accept a single element or an array. For some firewall providers you can pass a range of ports in the format: 'start number-end number'. For example, '1-1024' would cover ports 1 to 1024. diff --git a/lib/puppet/provider/firewall/ip6tables.rb b/lib/puppet/provider/firewall/ip6tables.rb index 6ddcee280..b0ebbcd8a 100644 --- a/lib/puppet/provider/firewall/ip6tables.rb +++ b/lib/puppet/provider/firewall/ip6tables.rb @@ -64,64 +64,65 @@ def self.iptables_save(*args) @protocol = "IPv6" @resource_map = { - :burst => "--limit-burst", - :connlimit_above => "-m connlimit --connlimit-above", - :connlimit_mask => "--connlimit-mask", - :connmark => "-m connmark --mark", - :ctstate => "-m conntrack --ctstate", - :destination => "-d", - :dport => ["-m multiport --dports", "--dport"], - :dst_range => '-m iprange --dst-range', - :dst_type => "-m addrtype --dst-type", - :gid => "-m owner --gid-owner", - :hop_limit => "-m hl --hl-eq", - :icmp => "-m icmp6 --icmpv6-type", - :iniface => "-i", - :ipsec_dir => "-m policy --dir", - :ipsec_policy => "--pol", - :ipset => "-m set --match-set", - :isfirstfrag => "-m frag --fragid 0 --fragfirst", - :ishasmorefrags => "-m frag --fragid 0 --fragmore", - :islastfrag => "-m frag --fragid 0 --fraglast", - :jump => "-j", - :limit => "-m limit --limit", - :log_level => "--log-level", - :log_prefix => "--log-prefix", - :mask => "--mask", - :name => "-m comment --comment", - :mac_source => ["-m mac --mac-source", "--mac-source"], - :outiface => "-o", - :pkttype => "-m pkttype --pkt-type", - :port => '-m multiport --ports', - :proto => "-p", - :rdest => "--rdest", - :reap => "--reap", - :recent => "-m recent", - :reject => "--reject-with", - :rhitcount => "--hitcount", - :rname => "--name", - :rseconds => "--seconds", - :rsource => "--rsource", - :rttl => "--rttl", - :set_mark => mark_flag, - :socket => "-m socket", - :source => "-s", - :sport => ["-m multiport --sports", "--sport"], - :src_range => '-m iprange --src-range', - :src_type => "-m addrtype --src-type", - :stat_every => '--every', - :stat_mode => "-m statistic --mode", - :stat_packet => '--packet', - :stat_probability => '--probability', - :state => "-m state --state", - :table => "-t", - :tcp_flags => "-m tcp --tcp-flags", - :todest => "--to-destination", - :toports => "--to-ports", - :tosource => "--to-source", - :uid => "-m owner --uid-owner", - :physdev_in => "-m physdev --physdev-in", - :physdev_out => "-m physdev --physdev-out", + :burst => "--limit-burst", + :connlimit_above => "-m connlimit --connlimit-above", + :connlimit_mask => "--connlimit-mask", + :connmark => "-m connmark --mark", + :ctstate => "-m conntrack --ctstate", + :destination => "-d", + :dport => ["-m multiport --dports", "--dport"], + :dst_range => '-m iprange --dst-range', + :dst_type => "-m addrtype --dst-type", + :gid => "-m owner --gid-owner", + :hop_limit => "-m hl --hl-eq", + :icmp => "-m icmp6 --icmpv6-type", + :iniface => "-i", + :ipsec_dir => "-m policy --dir", + :ipsec_policy => "--pol", + :ipset => "-m set --match-set", + :isfirstfrag => "-m frag --fragid 0 --fragfirst", + :ishasmorefrags => "-m frag --fragid 0 --fragmore", + :islastfrag => "-m frag --fragid 0 --fraglast", + :jump => "-j", + :limit => "-m limit --limit", + :log_level => "--log-level", + :log_prefix => "--log-prefix", + :mask => "--mask", + :name => "-m comment --comment", + :mac_source => ["-m mac --mac-source", "--mac-source"], + :outiface => "-o", + :pkttype => "-m pkttype --pkt-type", + :port => '-m multiport --ports', + :proto => "-p", + :rdest => "--rdest", + :reap => "--reap", + :recent => "-m recent", + :reject => "--reject-with", + :rhitcount => "--hitcount", + :rname => "--name", + :rseconds => "--seconds", + :rsource => "--rsource", + :rttl => "--rttl", + :set_mark => mark_flag, + :socket => "-m socket", + :source => "-s", + :sport => ["-m multiport --sports", "--sport"], + :src_range => '-m iprange --src-range', + :src_type => "-m addrtype --src-type", + :stat_every => '--every', + :stat_mode => "-m statistic --mode", + :stat_packet => '--packet', + :stat_probability => '--probability', + :state => "-m state --state", + :table => "-t", + :tcp_flags => "-m tcp --tcp-flags", + :todest => "--to-destination", + :toports => "--to-ports", + :tosource => "--to-source", + :uid => "-m owner --uid-owner", + :physdev_in => "-m physdev --physdev-in", + :physdev_out => "-m physdev --physdev-out", + :physdev_is_bridged => "-m physdev --physdev-is-bridged" } # These are known booleans that do not take a value, but we want to munge @@ -134,7 +135,8 @@ def self.iptables_save(*args) :rdest, :reap, :rttl, - :socket + :socket, + :physdev_is_bridged ] # Create property methods dynamically @@ -172,7 +174,7 @@ def self.iptables_save(*args) # I put it when calling the command. So compability with manual changes # not provided with current parser [georg.koester]) @resource_list = [:table, :source, :destination, :iniface, :outiface, :physdev_in, - :physdev_out, :proto, :ishasmorefrags, :islastfrag, :isfirstfrag, :src_range, :dst_range, + :physdev_out, :physdev_is_bridged, :proto, :ishasmorefrags, :islastfrag, :isfirstfrag, :src_range, :dst_range, :tcp_flags, :gid, :uid, :mac_source, :sport, :dport, :port, :dst_type, :src_type, :socket, :pkttype, :name, :ipsec_dir, :ipsec_policy, :state, :ctstate, :icmp, :hop_limit, :limit, :burst, :recent, :rseconds, :reap, diff --git a/lib/puppet/provider/firewall/iptables.rb b/lib/puppet/provider/firewall/iptables.rb index 74fa219a3..0aa5d7172 100644 --- a/lib/puppet/provider/firewall/iptables.rb +++ b/lib/puppet/provider/firewall/iptables.rb @@ -50,63 +50,64 @@ @protocol = "IPv4" @resource_map = { - :burst => "--limit-burst", - :connlimit_above => "-m connlimit --connlimit-above", - :connlimit_mask => "--connlimit-mask", - :connmark => "-m connmark --mark", - :ctstate => "-m conntrack --ctstate", - :destination => "-d", - :dport => ["-m multiport --dports", "--dport"], - :dst_range => "-m iprange --dst-range", - :dst_type => "-m addrtype --dst-type", - :gid => "-m owner --gid-owner", - :icmp => "-m icmp --icmp-type", - :iniface => "-i", - :ipsec_dir => "-m policy --dir", - :ipsec_policy => "--pol", - :ipset => "-m set --match-set", - :isfragment => "-f", - :jump => "-j", - :limit => "-m limit --limit", - :log_level => "--log-level", - :log_prefix => "--log-prefix", - :mac_source => ["-m mac --mac-source", "--mac-source"], - :mask => '--mask', - :name => "-m comment --comment", - :outiface => "-o", - :pkttype => "-m pkttype --pkt-type", - :port => '-m multiport --ports', - :proto => "-p", - :random => "--random", - :rdest => "--rdest", - :reap => "--reap", - :recent => "-m recent", - :reject => "--reject-with", - :rhitcount => "--hitcount", - :rname => "--name", - :rseconds => "--seconds", - :rsource => "--rsource", - :rttl => "--rttl", - :set_mark => mark_flag, - :socket => "-m socket", - :source => "-s", - :sport => ["-m multiport --sports", "--sport"], - :src_range => "-m iprange --src-range", - :src_type => "-m addrtype --src-type", - :stat_every => '--every', - :stat_mode => "-m statistic --mode", - :stat_packet => '--packet', - :stat_probability => '--probability', - :state => "-m state --state", - :table => "-t", - :tcp_flags => "-m tcp --tcp-flags", - :todest => "--to-destination", - :toports => "--to-ports", - :tosource => "--to-source", - :to => "--to", - :uid => "-m owner --uid-owner", - :physdev_in => "-m physdev --physdev-in", - :physdev_out => "-m physdev --physdev-out", + :burst => "--limit-burst", + :connlimit_above => "-m connlimit --connlimit-above", + :connlimit_mask => "--connlimit-mask", + :connmark => "-m connmark --mark", + :ctstate => "-m conntrack --ctstate", + :destination => "-d", + :dport => ["-m multiport --dports", "--dport"], + :dst_range => "-m iprange --dst-range", + :dst_type => "-m addrtype --dst-type", + :gid => "-m owner --gid-owner", + :icmp => "-m icmp --icmp-type", + :iniface => "-i", + :ipsec_dir => "-m policy --dir", + :ipsec_policy => "--pol", + :ipset => "-m set --match-set", + :isfragment => "-f", + :jump => "-j", + :limit => "-m limit --limit", + :log_level => "--log-level", + :log_prefix => "--log-prefix", + :mac_source => ["-m mac --mac-source", "--mac-source"], + :mask => '--mask', + :name => "-m comment --comment", + :outiface => "-o", + :pkttype => "-m pkttype --pkt-type", + :port => '-m multiport --ports', + :proto => "-p", + :random => "--random", + :rdest => "--rdest", + :reap => "--reap", + :recent => "-m recent", + :reject => "--reject-with", + :rhitcount => "--hitcount", + :rname => "--name", + :rseconds => "--seconds", + :rsource => "--rsource", + :rttl => "--rttl", + :set_mark => mark_flag, + :socket => "-m socket", + :source => "-s", + :sport => ["-m multiport --sports", "--sport"], + :src_range => "-m iprange --src-range", + :src_type => "-m addrtype --src-type", + :stat_every => '--every', + :stat_mode => "-m statistic --mode", + :stat_packet => '--packet', + :stat_probability => '--probability', + :state => "-m state --state", + :table => "-t", + :tcp_flags => "-m tcp --tcp-flags", + :todest => "--to-destination", + :toports => "--to-ports", + :tosource => "--to-source", + :to => "--to", + :uid => "-m owner --uid-owner", + :physdev_in => "-m physdev --physdev-in", + :physdev_out => "-m physdev --physdev-out", + :physdev_is_bridged => "-m physdev --physdev-is-bridged" } # These are known booleans that do not take a value, but we want to munge @@ -118,7 +119,8 @@ :reap, :rsource, :rttl, - :socket + :socket, + :physdev_is_bridged ] @@ -154,7 +156,7 @@ # changes between puppet runs, the changed rules will be re-applied again. # This order can be determined by going through iptables source code or just tweaking and trying manually @resource_list = [ - :table, :source, :destination, :iniface, :outiface, :physdev_in, :physdev_out, :proto, :isfragment, + :table, :source, :destination, :iniface, :outiface, :physdev_in, :physdev_out, :physdev_is_bridged, :proto, :isfragment, :stat_mode, :stat_every, :stat_packet, :stat_probability, :src_range, :dst_range, :tcp_flags, :gid, :uid, :mac_source, :sport, :dport, :port, :dst_type, :src_type, :socket, :pkttype, :name, :ipsec_dir, :ipsec_policy, @@ -250,6 +252,18 @@ def self.rule_to_hash(line, table, counter) '--pol "ipsec\1\2\3\4\5\6\7\8" ' ) + # Handle resource_map values depending on whether physdev-in, physdev-out, ,physdev-is-bridged, or all three are specified + if values.include? "--physdev-in" + @resource_map[:physdev_in] = "-m physdev --physdev-in" + @resource_map[:physdev_out] = "--physdev-out" + @resource_map[:physdev_is_bridged] = "--physdev-is-bridged" + elsif values.include? "--physdev-out" + @resource_map[:physdev_out] = "-m physdev --physdev-out" + @resource_map[:physdev_is_bridged] = "--physdev-is-bridged" + else + @resource_map[:physdev_is_bridged] = "-m physdev --physdev-is-bridged" + end + # Trick the system for booleans @known_booleans.each do |bool| # append "true" because all params are expected to have values @@ -263,14 +277,6 @@ def self.rule_to_hash(line, table, counter) end end - # Handle resource_map values depending on whether physdev-in, physdev-out, or both are specified - if values.include? "--physdev-in" and values.include? "--physdev-out" then - #values = values.sub("--physdev-out","-m physdev --physdev-out") - @resource_map[:physdev_out] = "--physdev-out" - else - @resource_map[:physdev_out] = "-m physdev --physdev-out" - end - ############ # Populate parser_list with used value, in the correct order ############ @@ -452,11 +458,16 @@ def general_args resource_map = self.class.instance_variable_get('@resource_map') known_booleans = self.class.instance_variable_get('@known_booleans') - # Handle physdev args depending on whether physdev-in, physdev-out, or both are specified + # Handle physdev args depending on whether physdev-in, physdev-out, physdev-is-bridged, or all three are specified if (resource[:physdev_in]) + resource_map[:physdev_in] = "-m physdev --physdev-in" resource_map[:physdev_out] = "--physdev-out" - else + resource_map[:physdev_is_bridged] = "--physdev-is-bridged" + elsif (resource[:physdev_out]) resource_map[:physdev_out] = "-m physdev --physdev-out" + resource_map[:physdev_is_bridged] = "--physdev-is-bridged" + else + resource_map[:physdev_is_bridged] = "-m physdev --physdev-is-bridged" end resource_list.each do |res| diff --git a/lib/puppet/type/firewall.rb b/lib/puppet/type/firewall.rb index 6209c8840..46756e341 100644 --- a/lib/puppet/type/firewall.rb +++ b/lib/puppet/type/firewall.rb @@ -1087,6 +1087,13 @@ def insync?(is) newvalues(/^[a-zA-Z0-9\-\._\+]+$/) end + newproperty(:physdev_is_bridged, :required_features => :iptables) do + desc <<-EOS + Match if the packet is transversing a bridge. + EOS + newvalues(:true, :false) + end + autorequire(:firewallchain) do reqs = [] protocol = nil diff --git a/spec/acceptance/firewall_bridging_spec.rb b/spec/acceptance/firewall_bridging_spec.rb index 109806a42..b3cf9b6bd 100644 --- a/spec/acceptance/firewall_bridging_spec.rb +++ b/spec/acceptance/firewall_bridging_spec.rb @@ -90,6 +90,115 @@ class { '::firewall': } end end end + + context 'physdev_is_bridged' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '704 - test': + chain => 'FORWARD', + proto => tcp, + port => '704', + action => accept, + physdev_is_bridged => true, + } + EOS + + apply_manifest(pp, :catch_failures => true) + unless fact('selinux') == 'true' + apply_manifest(pp, :catch_changes => true) + end + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A FORWARD -p tcp -m physdev\s+--physdev-is-bridged -m multiport --ports 704 -m comment --comment "704 - test" -j ACCEPT/) + end + end + end + + context 'physdev_in eth0 and physdev_is_bridged' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '705 - test': + chain => 'FORWARD', + proto => tcp, + port => '705', + action => accept, + physdev_in => 'eth0', + physdev_is_bridged => true, + } + EOS + + apply_manifest(pp, :catch_failures => true) + unless fact('selinux') == 'true' + apply_manifest(pp, :catch_changes => true) + end + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A FORWARD -p tcp -m physdev\s+--physdev-in eth0 --physdev-is-bridged -m multiport --ports 705 -m comment --comment "705 - test" -j ACCEPT/) + end + end + end + + context 'physdev_out eth1 and physdev_is_bridged' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '706 - test': + chain => 'FORWARD', + proto => tcp, + port => '706', + action => accept, + physdev_out => 'eth1', + physdev_is_bridged => true, + } + EOS + + apply_manifest(pp, :catch_failures => true) + unless fact('selinux') == 'true' + apply_manifest(pp, :catch_changes => true) + end + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A FORWARD -p tcp -m physdev\s+--physdev-out eth1 --physdev-is-bridged -m multiport --ports 706 -m comment --comment "706 - test" -j ACCEPT/) + end + end + end + + context 'physdev_in eth0 and physdev_out eth1 and physdev_is_bridged' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '707 - test': + chain => 'FORWARD', + proto => tcp, + port => '707', + action => accept, + physdev_in => 'eth0', + physdev_out => 'eth1', + physdev_is_bridged => true, + } + EOS + + apply_manifest(pp, :catch_failures => true) + unless fact('selinux') == 'true' + apply_manifest(pp, :catch_changes => true) + end + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A FORWARD -p tcp -m physdev\s+--physdev-in eth0 --physdev-out eth1 --physdev-is-bridged -m multiport --ports 707 -m comment --comment "707 - test" -j ACCEPT/) + end + end + end + end #iptables version 1.3.5 is not suppored by the ip6tables provider @@ -176,6 +285,118 @@ class { '::firewall': } end end end + + context 'physdev_is_bridged' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '704 - test': + provider => 'ip6tables', + chain => 'FORWARD', + proto => tcp, + port => '704', + action => accept, + physdev_is_bridged => true, + } + EOS + + apply_manifest(pp, :catch_failures => true) + unless fact('selinux') == 'true' + apply_manifest(pp, :catch_changes => true) + end + end + + it 'should contain the rule' do + shell('ip6tables-save') do |r| + expect(r.stdout).to match(/-A FORWARD -p tcp -m physdev\s+--physdev-is-bridged -m multiport --ports 704 -m comment --comment "704 - test" -j ACCEPT/) + end + end + end + + context 'physdev_in eth0 and physdev_is_bridged' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '705 - test': + provider => 'ip6tables', + chain => 'FORWARD', + proto => tcp, + port => '705', + action => accept, + physdev_in => 'eth0', + physdev_is_bridged => true, + } + EOS + + apply_manifest(pp, :catch_failures => true) + unless fact('selinux') == 'true' + apply_manifest(pp, :catch_changes => true) + end + end + + it 'should contain the rule' do + shell('ip6tables-save') do |r| + expect(r.stdout).to match(/-A FORWARD -p tcp -m physdev\s+--physdev-in eth0 --physdev-is-bridged -m multiport --ports 705 -m comment --comment "705 - test" -j ACCEPT/) + end + end + end + + context 'physdev_out eth1 and physdev_is_bridged' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '706 - test': + provider => 'ip6tables', + chain => 'FORWARD', + proto => tcp, + port => '706', + action => accept, + physdev_out => 'eth1', + physdev_is_bridged => true, + } + EOS + + apply_manifest(pp, :catch_failures => true) + unless fact('selinux') == 'true' + apply_manifest(pp, :catch_changes => true) + end + end + + it 'should contain the rule' do + shell('ip6tables-save') do |r| + expect(r.stdout).to match(/-A FORWARD -p tcp -m physdev\s+--physdev-out eth1 --physdev-is-bridged -m multiport --ports 706 -m comment --comment "706 - test" -j ACCEPT/) + end + end + end + + context 'physdev_in eth0 and physdev_out eth1 and physdev_is_bridged' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '707 - test': + provider => 'ip6tables', + chain => 'FORWARD', + proto => tcp, + port => '707', + action => accept, + physdev_in => 'eth0', + physdev_out => 'eth1', + physdev_is_bridged => true, + } + EOS + + apply_manifest(pp, :catch_failures => true) + unless fact('selinux') == 'true' + apply_manifest(pp, :catch_changes => true) + end + end + + it 'should contain the rule' do + shell('ip6tables-save') do |r| + expect(r.stdout).to match(/-A FORWARD -p tcp -m physdev\s+--physdev-in eth0 --physdev-out eth1 --physdev-is-bridged -m multiport --ports 707 -m comment --comment "707 - test" -j ACCEPT/) + end + end + end end end