From 4d3d0393f6fbdab13feab29261fcced365862395 Mon Sep 17 00:00:00 2001 From: Mateusz Gozdek Date: Tue, 6 Nov 2018 01:16:35 +0100 Subject: [PATCH] (MODULES-8214) Handle src_type and dst_type as array So it can be parsed when specified mutliple times, as well as being configured. --- README.markdown | 12 +- lib/puppet/provider/firewall/iptables.rb | 37 +++- lib/puppet/type/firewall.rb | 46 ++++- spec/acceptance/firewall_spec.rb | 225 ++++++++++++++++++++++ spec/fixtures/iptables/conversion_hash.rb | 81 +++++++- spec/unit/puppet/type/firewall_spec.rb | 10 +- 6 files changed, 391 insertions(+), 20 deletions(-) diff --git a/README.markdown b/README.markdown index 98f7264..678d1b4 100644 --- a/README.markdown +++ b/README.markdown @@ -581,7 +581,7 @@ If Puppet is managing the iptables or iptables-persistent packages, and the prov The destination IP range is must in 'IP1-IP2' format. Values in the range must be valid IPv4 or IPv6 addresses. Requires the `iprange` feature. -* `dst_type`: The destination address type. For example: `dst_type => 'LOCAL'`. +* `dst_type`: The destination address type. Will accept a single element or an array. For example: `dst_type => ['LOCAL']`. Valid values are: @@ -597,6 +597,10 @@ If Puppet is managing the iptables or iptables-persistent packages, and the prov * 'THROW': an unroutable address * 'XRESOLVE: an unresolvable address + In addition, it accepts '--limit-iface-in' and '--limit-iface-out' flags. For example: `dst_type => ['LOCAL --limit-iface-in']`. + + It can also be negated using '!'. For example: `dst_type => ['! LOCAL']`. + Requires the `address_type` feature. * `ensure`: Ensures that the resource is present. Valid values are 'present', 'absent'. The default is 'present'. @@ -798,7 +802,7 @@ If Puppet is managing the iptables or iptables-persistent packages, and the prov * `src_range`: The source IP range. For example: `src_range => '192.168.1.1-192.168.1.10'`. The source IP range must be in 'IP1-IP2' format. Values in the range must be valid IPv4 or IPv6 addresses. Requires the `iprange` feature. -* `src_type`: Specify the source address type. For example: `src_type => 'LOCAL'`. +* `src_type`: Specify the source address type. Will accept a single element or an array. For example: `src_type => ['LOCAL']`. Valid values are: @@ -814,6 +818,10 @@ If Puppet is managing the iptables or iptables-persistent packages, and the prov * 'THROW': an unroutable address. * 'XRESOLVE': an unresolvable address. + In addition, it accepts '--limit-iface-in' and '--limit-iface-out' flags. For example: `dst_type => ['LOCAL --limit-iface-in']`. + + It can also be negated using '!'. For example: `dst_type => ['! LOCAL']`. + Requires the `address_type` feature. * `stat_every`: Match one packet every nth packet. Requires `stat_mode => 'nth'` diff --git a/lib/puppet/provider/firewall/iptables.rb b/lib/puppet/provider/firewall/iptables.rb index 3dd657c..dcbe709 100644 --- a/lib/puppet/provider/firewall/iptables.rb +++ b/lib/puppet/provider/firewall/iptables.rb @@ -403,6 +403,20 @@ Puppet::Type.type(:firewall).provide :iptables, parent: Puppet::Provider::Firewa values = values.gsub(%r{-m comment --comment ([^"].*?)[ $]}, '') values.insert(ind, "-m comment --comment \"#{comments.join(';')}\" ") end + if values =~ %r{-m addrtype (!\s+)?--src-type} + values = values.gsub(%r{(!\s+)?--src-type (\S*)(\s--limit-iface-(in|out))?}, '--src-type \1\2\3') + ind = values.index('-m addrtype --src-type') + types = values.scan(%r{-m addrtype --src-type ((?:!\s+)?\S*(?: --limit-iface-(?:in|out))?)}) + values = values.gsub(%r{-m addrtype --src-type ((?:!\s+)?\S*(?: --limit-iface-(?:in|out))?) ?}, '') + values.insert(ind, "-m addrtype --src-type \"#{types.join(';')}\" ") + end + if values =~ %r{-m addrtype (!\s+)?--dst-type} + values = values.gsub(%r{(!\s+)?--dst-type (\S*)(\s--limit-iface-(in|out))?}, '--dst-type \1\2\3') + ind = values.index('-m addrtype --dst-type') + types = values.scan(%r{-m addrtype --dst-type ((?:!\s+)?\S*(?: --limit-iface-(?:in|out))?)}) + values = values.gsub(%r{-m addrtype --dst-type ((?:!\s+)?\S*(?: --limit-iface-(?:in|out))?) ?}, '') + values.insert(ind, "-m addrtype --dst-type \"#{types.join(';')}\" ") + end # the actual rule will have the ! mark before the option. values = values.gsub(%r{(!)\s*(-\S+)\s*(\S*)}, '\2 "\1 \3"') # we do a similar thing for negated address masks (source and destination). @@ -512,7 +526,9 @@ Puppet::Type.type(:firewall).provide :iptables, parent: Puppet::Provider::Firewa hash[prop] = hash[prop].split(',') unless hash[prop].nil? end - hash[:ipset] = hash[:ipset].split(';') unless hash[:ipset].nil? + [:ipset, :dst_type, :src_type].each do |prop| + hash[prop] = hash[prop].split(';') unless hash[prop].nil? + end ## clean up DSCP class to HEX mappings valid_dscp_classes = { @@ -571,7 +587,6 @@ Puppet::Type.type(:firewall).provide :iptables, parent: Puppet::Provider::Firewa :destination, :dport, :dst_range, - :dst_type, :port, :physdev_is_bridged, :physdev_is_in, @@ -580,7 +595,6 @@ Puppet::Type.type(:firewall).provide :iptables, parent: Puppet::Provider::Firewa :source, :sport, :src_range, - :src_type, :state, ].each do |prop| if hash[prop] && hash[prop].is_a?(Array) @@ -709,6 +723,19 @@ Puppet::Type.type(:firewall).provide :iptables, parent: Puppet::Provider::Firewa raise "#{nflog_feature} is not available on iptables version #{iptables_version}" if resource[nflog_feature] && (iptables_version && iptables_version < '1.3.7') end + [:dst_type, :src_type].each do |prop| + next unless resource[prop] + + resource[prop].each do |type| + if type =~ %r{--limit-iface-(in|out)} && (iptables_version && iptables_version < '1.4.1') + raise '--limit-iface-in and --limit-iface-out are available from iptables version 1.4.1' + end + end + + raise "Multiple #{prop} elements are available from iptables version 1.4.1" if resource[prop].length > 1 && (iptables_version && iptables_version < '1.4.1') + raise "#{prop} elements must be unique" if resource[prop].map { |type| type.to_s.gsub(%r{--limit-iface-(in|out)}, '') }.uniq.length != resource[prop].length + end + resource_list.each do |res| resource_value = nil if resource[res] @@ -737,7 +764,7 @@ Puppet::Type.type(:firewall).provide :iptables, parent: Puppet::Provider::Firewa # ruby 1.8.7 can't .match Symbols ------------------ ^ resource_value = resource_value.to_s.sub!(%r{^!\s*}, '').to_sym args.insert(-2, '!') - elsif resource_value.is_a?(Array) && res != :ipset + elsif resource_value.is_a?(Array) && ![:ipset, :dst_type, :src_type].include?(res) should_negate = resource_value.index do |value| # ruby 1.8.7 can't .match symbols value.to_s.match(%r{^(!)\s+}) @@ -770,7 +797,7 @@ Puppet::Type.type(:firewall).provide :iptables, parent: Puppet::Provider::Firewa end # ipset can accept multiple values with weird iptables arguments - if res == :ipset + if [:ipset, :dst_type, :src_type].include?(res) resource_value.join(" #{[resource_map[res]].flatten.first} ").split(' ').each do |a| if a.sub!(%r{^!\s*}, '') # Negate ipset options diff --git a/lib/puppet/type/firewall.rb b/lib/puppet/type/firewall.rb index 1f4ff0e..b9feff4 100644 --- a/lib/puppet/type/firewall.rb +++ b/lib/puppet/type/firewall.rb @@ -331,11 +331,11 @@ Puppet::Type.newtype(:firewall) do end end - newproperty(:dst_type, required_features: :address_type) do + newproperty(:dst_type, required_features: :address_type, array_matching: :all) do desc <<-PUPPETCODE The destination address type. For example: - dst_type => 'LOCAL' + dst_type => ['LOCAL'] Can be one of: @@ -351,19 +351,36 @@ Puppet::Type.newtype(:firewall) do * THROW - undocumented * NAT - undocumented * XRESOLVE - undocumented + + In addition, it accepts '--limit-iface-in' and '--limit-iface-out' flags, specified as: + + dst_type => ['LOCAL --limit-iface-in'] + + It can also be negated using '!': + + dst_type => ['! LOCAL'] + + Will accept a single element or an array. PUPPETCODE newvalues(*[:UNSPEC, :UNICAST, :LOCAL, :BROADCAST, :ANYCAST, :MULTICAST, :BLACKHOLE, :UNREACHABLE, :PROHIBIT, :THROW, :NAT, :XRESOLVE].map { |address_type| - [address_type, "! #{address_type}".to_sym] + [ + address_type, + "! #{address_type}".to_sym, + "#{address_type} --limit-iface-in".to_sym, + "#{address_type} --limit-iface-out".to_sym, + "! #{address_type} --limit-iface-in".to_sym, + "! #{address_type} --limit-iface-out".to_sym, + ] }.flatten) end - newproperty(:src_type, required_features: :address_type) do + newproperty(:src_type, required_features: :address_type, array_matching: :all) do desc <<-PUPPETCODE The source address type. For example: - src_type => 'LOCAL' + src_type => ['LOCAL'] Can be one of: @@ -379,11 +396,28 @@ Puppet::Type.newtype(:firewall) do * THROW - undocumented * NAT - undocumented * XRESOLVE - undocumented + + In addition, it accepts '--limit-iface-in' and '--limit-iface-out' flags, specified as: + + src_type => ['LOCAL --limit-iface-in'] + + It can also be negated using '!': + + src_type => ['! LOCAL'] + + Will accept a single element or an array. PUPPETCODE newvalues(*[:UNSPEC, :UNICAST, :LOCAL, :BROADCAST, :ANYCAST, :MULTICAST, :BLACKHOLE, :UNREACHABLE, :PROHIBIT, :THROW, :NAT, :XRESOLVE].map { |address_type| - [address_type, "! #{address_type}".to_sym] + [ + address_type, + "! #{address_type}".to_sym, + "#{address_type} --limit-iface-in".to_sym, + "#{address_type} --limit-iface-out".to_sym, + "! #{address_type} --limit-iface-in".to_sym, + "! #{address_type} --limit-iface-out".to_sym, + ] }.flatten) end diff --git a/spec/acceptance/firewall_spec.rb b/spec/acceptance/firewall_spec.rb index 4bb93da..9f02367 100644 --- a/spec/acceptance/firewall_spec.rb +++ b/spec/acceptance/firewall_spec.rb @@ -586,6 +586,117 @@ describe 'firewall basics', docker: true do end end end + + context 'when LOCAL --limit-iface-in', unless: (fact('operatingsystem') == 'RedHat' && fact('operatingsystemmajrelease') <= '5') || + (fact('operatingsystem') == 'CentOS' && fact('operatingsystemmajrelease') <= '5') do + pp97 = <<-PUPPETCODE + class { '::firewall': } + firewall { '613 - test': + proto => tcp, + action => accept, + #{type} => 'LOCAL --limit-iface-in', + } + PUPPETCODE + it 'applies' do + apply_manifest(pp97, catch_failures: true) + end + + it 'contains the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(%r{-A INPUT -p tcp -m addrtype\s.*\sLOCAL --limit-iface-in -m comment --comment "613 - test" -j ACCEPT}) + end + end + end + + context 'when LOCAL --limit-iface-in fail', if: (fact('operatingsystem') == 'RedHat' && fact('operatingsystemmajrelease') <= '5') || + (fact('operatingsystem') == 'CentOS' && fact('operatingsystemmajrelease') <= '5') do + pp98 = <<-PUPPETCODE + class { '::firewall': } + firewall { '614 - test': + proto => tcp, + action => accept, + #{type} => 'LOCAL --limit-iface-in', + } + PUPPETCODE + it 'fails' do + apply_manifest(pp98, expect_failures: true) do |r| + expect(r.stderr).to match(%r{--limit-iface-in and --limit-iface-out are available from iptables version}) + end + end + + it 'does not contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).not_to match(%r{-A INPUT -p tcp -m addrtype\s.*\sLOCAL --limit-iface-in -m comment --comment "614 - test" -j ACCEPT}) + end + end + end + + context 'when duplicated LOCAL', unless: (fact('operatingsystem') == 'RedHat' && fact('operatingsystemmajrelease') <= '5') || + (fact('operatingsystem') == 'CentOS' && fact('operatingsystemmajrelease') <= '5') do + pp99 = <<-PUPPETCODE + class { '::firewall': } + firewall { '615 - test': + proto => tcp, + action => accept, + #{type} => ['LOCAL', 'LOCAL'], + } + PUPPETCODE + it 'fails' do + apply_manifest(pp99, expect_failures: true) do |r| + expect(r.stderr).to match(%r{#{type} elements must be unique}) + end + end + + it 'does not contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).not_to match(%r{-A INPUT -p tcp -m addrtype --#{type.tr('_', '-')} LOCAL -m addrtype --#{type.tr('_', '-')} LOCAL -m comment --comment "615 - test" -j ACCEPT}) + end + end + end + + context 'when multiple addrtype', unless: (fact('operatingsystem') == 'RedHat' && fact('operatingsystemmajrelease') <= '5') || + (fact('operatingsystem') == 'CentOS' && fact('operatingsystemmajrelease') <= '5') do + pp100 = <<-PUPPETCODE + class { '::firewall': } + firewall { '616 - test': + proto => tcp, + action => accept, + #{type} => ['LOCAL', '! LOCAL'], + } + PUPPETCODE + it 'applies' do + apply_manifest(pp100, catch_failures: true) + end + + it 'contains the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(%r{-A INPUT -p tcp -m addrtype --#{type.tr('_', '-')} LOCAL -m addrtype ! --#{type.tr('_', '-')} LOCAL -m comment --comment "616 - test" -j ACCEPT}) + end + end + end + + context 'when multiple addrtype fail', if: (fact('operatingsystem') == 'RedHat' && fact('operatingsystemmajrelease') <= '5') || + (fact('operatingsystem') == 'CentOS' && fact('operatingsystemmajrelease') <= '5') do + pp101 = <<-PUPPETCODE + class { '::firewall': } + firewall { '616 - test': + proto => tcp, + action => accept, + #{type} => ['LOCAL', '! LOCAL'], + } + PUPPETCODE + it 'fails' do + apply_manifest(pp101, expect_failures: true) do |r| + expect(r.stderr).to match(%r{Multiple #{type} elements are available from iptables version}) + end + end + + it 'does not contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).not_to match(%r{-A INPUT -p tcp -m addrtype --#{type.tr('_', '-')} LOCAL -m addrtype ! --#{type.tr('_', '-')} LOCAL -m comment --comment "616 - test" -j ACCEPT}) + end + end + end end end @@ -1574,6 +1685,120 @@ describe 'firewall basics', docker: true do end end end + + context 'when LOCAL --limit-iface-in', unless: (fact('operatingsystem') == 'RedHat' && fact('operatingsystemmajrelease') <= '5') || + (fact('operatingsystem') == 'CentOS' && fact('operatingsystemmajrelease') <= '5') do + pp102 = <<-PUPPETCODE + class { '::firewall': } + firewall { '617 - test': + proto => tcp, + action => accept, + #{type} => 'LOCAL --limit-iface-in', + } + PUPPETCODE + it 'applies' do + apply_manifest(pp102, catch_failures: true) + end + + it 'contains the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(%r{-A INPUT -p tcp -m addrtype\s.*\sLOCAL --limit-iface-in -m comment --comment "617 - test" -j ACCEPT}) + end + end + end + + context 'when LOCAL --limit-iface-in fail', if: (fact('operatingsystem') == 'RedHat' && fact('operatingsystemmajrelease') <= '5') || + (fact('operatingsystem') == 'CentOS' && fact('operatingsystemmajrelease') <= '5') do + pp103 = <<-PUPPETCODE + class { '::firewall': } + firewall { '618 - test': + proto => tcp, + action => accept, + #{type} => 'LOCAL --limit-iface-in', + } + PUPPETCODE + it 'fails' do + apply_manifest(pp103, expect_failures: true) do |r| + expect(r.stderr).to match(%r{--limit-iface-in and --limit-iface-out are available from iptables version}) + end + end + + it 'does not contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).not_to match(%r{-A INPUT -p tcp -m addrtype\s.*\sLOCAL --limit-iface-in -m comment --comment "618 - test" -j ACCEPT}) + end + end + end + + context 'when duplicated LOCAL', unless: (fact('operatingsystem') == 'RedHat' && fact('operatingsystemmajrelease') <= '5') || + (fact('operatingsystem') == 'CentOS' && fact('operatingsystemmajrelease') <= '5') do + pp104 = <<-PUPPETCODE + class { '::firewall': } + firewall { '619 - test': + proto => tcp, + action => accept, + #{type} => ['LOCAL', 'LOCAL'], + provider => 'ip6tables', + } + PUPPETCODE + it 'fails' do + apply_manifest(pp104, expect_failures: true) do |r| + expect(r.stderr).to match(%r{#{type} elements must be unique}) + end + end + + it 'does not contain the rule' do + shell('ip6tables-save') do |r| + expect(r.stdout).not_to match(%r{-A INPUT -p tcp -m addrtype\s.*\sLOCAL -m addrtype\s.*\sLOCAL -m comment --comment "619 - test" -j ACCEPT}) + end + end + end + + context 'when multiple addrtype', unless: (fact('operatingsystem') == 'RedHat' && fact('operatingsystemmajrelease') <= '5') || + (fact('operatingsystem') == 'CentOS' && fact('operatingsystemmajrelease') <= '5') do + pp105 = <<-PUPPETCODE + class { '::firewall': } + firewall { '620 - test': + proto => tcp, + action => accept, + #{type} => ['LOCAL', '! LOCAL'], + provider => 'ip6tables', + } + PUPPETCODE + it 'applies' do + apply_manifest(pp105, catch_failures: true) + end + + it 'contains the rule' do + shell('ip6tables-save') do |r| + expect(r.stdout).to match(%r{-A INPUT -p tcp -m addrtype --#{type.tr('_', '-')} LOCAL -m addrtype ! --#{type.tr('_', '-')} LOCAL -m comment --comment "620 - test" -j ACCEPT}) + end + end + end + + context 'when multiple addrtype fail', if: (fact('operatingsystem') == 'RedHat' && fact('operatingsystemmajrelease') <= '5') || + (fact('operatingsystem') == 'CentOS' && fact('operatingsystemmajrelease') <= '5') do + pp106 = <<-PUPPETCODE + class { '::firewall': } + firewall { '616 - test': + proto => tcp, + action => accept, + #{type} => ['LOCAL', '! LOCAL'], + provider => 'ip6tables', + } + PUPPETCODE + it 'fails' do + apply_manifest(pp106, expect_failures: true) do |r| + expect(r.stderr).to match(%r{Multiple #{type} elements are available from iptables version}) + end + end + + it 'does not contain the rule' do + shell('ip6tables-save') do |r| + expect(r.stdout).not_to match(%r{-A INPUT -p tcp -m addrtype --#{type.tr('_', '-')} LOCAL -m addrtype ! --#{type.tr('_', '-')} LOCAL -m comment --comment "616 - test" -j ACCEPT}) + end + end + end end end end diff --git a/spec/fixtures/iptables/conversion_hash.rb b/spec/fixtures/iptables/conversion_hash.rb index 237b569..166d935 100644 --- a/spec/fixtures/iptables/conversion_hash.rb +++ b/spec/fixtures/iptables/conversion_hash.rb @@ -176,14 +176,14 @@ ARGS_TO_HASH = { line: '-A INPUT -m addrtype --dst-type LOCAL', table: 'filter', params: { - dst_type: 'LOCAL', + dst_type: ['LOCAL'], }, }, 'src_type_1' => { line: '-A INPUT -m addrtype --src-type LOCAL', table: 'filter', params: { - src_type: 'LOCAL', + src_type: ['LOCAL'], }, }, 'dst_range_1' => { @@ -370,6 +370,39 @@ ARGS_TO_HASH = { action: 'drop', }, }, + 'addrtype_limit_iface_out' => { + line: '-A cali-POSTROUTING -o tunl0 -m comment --comment "000 cali:JHlpT-eSqR1TvyYm" -m addrtype --src-type LOCAL --limit-iface-out -j MASQUERADE', + table: 'filter', + params: { + chain: 'cali-POSTROUTING', + outiface: 'tunl0', + name: '000 cali:JHlpT-eSqR1TvyYm', + jump: 'MASQUERADE', + src_type: ['LOCAL --limit-iface-out'], + }, + }, + 'addrtype_negated' => { + line: '-A cali-POSTROUTING -o tunl0 -m comment --comment "000 cali:JHlpT-eSqR1TvyYm" -m addrtype ! --src-type LOCAL -j MASQUERADE', + table: 'filter', + params: { + chain: 'cali-POSTROUTING', + outiface: 'tunl0', + name: '000 cali:JHlpT-eSqR1TvyYm', + jump: 'MASQUERADE', + src_type: ['! LOCAL'], + }, + }, + 'addrtype_multiple' => { + line: '-A cali-POSTROUTING -o tunl0 -m comment --comment "000 cali:JHlpT-eSqR1TvyYm" -m addrtype ! --src-type LOCAL --limit-iface-out -m addrtype --src-type LOCAL -j MASQUERADE', + table: 'filter', + params: { + chain: 'cali-POSTROUTING', + outiface: 'tunl0', + name: '000 cali:JHlpT-eSqR1TvyYm', + jump: 'MASQUERADE', + src_type: ['! LOCAL --limit-iface-out', 'LOCAL'], + }, + }, 'iniface_1_negated' => { line: '-A INPUT ! -i eth0 -j DROP -m comment --comment "060 iniface"', table: 'filter', @@ -838,7 +871,31 @@ HASH_TO_ARGS = { table: 'filter', dst_type: 'LOCAL', }, - args: ['-t', :filter, '-p', :tcp, '-m', 'addrtype', '--dst-type', :LOCAL, '-m', 'comment', '--comment', '000 dst_type'], + args: ['-t', :filter, '-p', :tcp, '-m', 'addrtype', '--dst-type', 'LOCAL', '-m', 'comment', '--comment', '000 dst_type'], + }, + 'dst_type_as_array' => { + params: { + name: '000 dst_type', + table: 'filter', + dst_type: ['LOCAL'], + }, + args: ['-t', :filter, '-p', :tcp, '-m', 'addrtype', '--dst-type', 'LOCAL', '-m', 'comment', '--comment', '000 dst_type'], + }, + 'dst_type_multiple' => { + params: { + name: '000 dst_type', + table: 'filter', + dst_type: ['LOCAL', '! LOCAL'], + }, + args: ['-t', :filter, '-p', :tcp, '-m', 'addrtype', '--dst-type', 'LOCAL', '-m', 'addrtype', '!', '--dst-type', 'LOCAL', '-m', 'comment', '--comment', '000 dst_type'], + }, + 'dst_type_limit' => { + params: { + name: '000 dst_type', + table: 'filter', + dst_type: ['LOCAL --limit-iface-in'], + }, + args: ['-t', :filter, '-p', :tcp, '-m', 'addrtype', '--dst-type', 'LOCAL', '--limit-iface-in', '-m', 'comment', '--comment', '000 dst_type'], }, 'src_type_1' => { params: { @@ -846,7 +903,23 @@ HASH_TO_ARGS = { table: 'filter', src_type: 'LOCAL', }, - args: ['-t', :filter, '-p', :tcp, '-m', 'addrtype', '--src-type', :LOCAL, '-m', 'comment', '--comment', '000 src_type'], + args: ['-t', :filter, '-p', :tcp, '-m', 'addrtype', '--src-type', 'LOCAL', '-m', 'comment', '--comment', '000 src_type'], + }, + 'src_type_as_array' => { + params: { + name: '000 src_type', + table: 'filter', + src_type: ['LOCAL'], + }, + args: ['-t', :filter, '-p', :tcp, '-m', 'addrtype', '--src-type', 'LOCAL', '-m', 'comment', '--comment', '000 src_type'], + }, + 'src_type_multiple' => { + params: { + name: '000 src_type', + table: 'filter', + src_type: ['LOCAL', '! LOCAL'], + }, + args: ['-t', :filter, '-p', :tcp, '-m', 'addrtype', '--src-type', 'LOCAL', '-m', 'addrtype', '!', '--src-type', 'LOCAL', '-m', 'comment', '--comment', '000 src_type'], }, 'dst_range_1' => { params: { diff --git a/spec/unit/puppet/type/firewall_spec.rb b/spec/unit/puppet/type/firewall_spec.rb index 5a9d42f..e09c898 100755 --- a/spec/unit/puppet/type/firewall_spec.rb +++ b/spec/unit/puppet/type/firewall_spec.rb @@ -198,9 +198,13 @@ describe firewall do # rubocop:disable RSpec/MultipleDescribes [:UNSPEC, :UNICAST, :LOCAL, :BROADCAST, :ANYCAST, :MULTICAST, :BLACKHOLE, :UNREACHABLE, :PROHIBIT, :THROW, :NAT, :XRESOLVE].each do |type| - it "should accept #{addrtype} value #{type}" do - resource[addrtype] = type - expect(resource[addrtype]).to eql type + ['! ', ''].each do |negation| + ['', ' --limit-iface-in', ' --limit-iface-out'].each do |limit| + it "should accept #{addrtype} value #{negation}#{type}#{limit}" do + resource[addrtype] = type + expect(resource[addrtype]).to eql [type] + end + end end end -- 2.45.2