From: Ken Barber Date: Mon, 12 Mar 2012 04:16:33 +0000 (-0700) Subject: (#10162) Various fixes for firewallchain resource X-Git-Tag: 0.1.0~40^2~2 X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=9715882ad39ca53338f1ac212675e97d01db769f;p=puppet-modules%2Fpuppetlabs-firewall.git (#10162) Various fixes for firewallchain resource * Convert commands to optional_commands to avoid iptables installation chicken & egg scenarios. * Downcase tables to match the table names in xtables * Force fully qualifying the name as ::, we can add meaningful defaults later. * puppet resource command wasn't working as expected, but stripping out some of the meaningful defaults I was able to get this to work. * Reformat some of the code to avoid overrunning 80 chars where possible * Remove trailing whitespace * Add flush to provider so that resource modifications immediately update the resource in reports and when using puppet resource. * Removed any commented out code * Improved documentation * Change policy so its undefined when not set, instead of being :empty * Fix test mocking so they will run on a Mac --- diff --git a/README.markdown b/README.markdown index a144deb..04c8865 100644 --- a/README.markdown +++ b/README.markdown @@ -4,14 +4,21 @@ ### Overview -This type provides the capability to manage firewall rules within -puppet. +This module provides the resource 'firewall' which provides the capability to +manage firewall rules within puppet. Current support includes: * iptables * ip6tables +With the resource 'firewallchain' we also provide a mechanism to manage chains +for: + +* iptables +* ip6tables +* ebtables + ### Disclaimer Warning! While this software is written in the best interest of quality it has @@ -93,6 +100,23 @@ Source NAT example (perfect for a virtualization host): table => 'nat', } +Creating a new rule that forwards to a chain, then adding a rule to this chain: + + firewall { '100 forward to MY_CHAIN': + chain => 'INPUT', + jump => 'MY_CHAIN', + require => Firewallchain["filter:MY_CHAIN:IPv4"], + } + firewallchain { 'filter:MY_CHAIN:IPv4': + ensure => present, + } + firewall { '100 my rule': + chain => 'MY_CHAIN', + action => 'accept', + proto => 'tcp', + dport => 5000, + } + You can make firewall rules persistent with the following iptables example: exec { "persist-firewall": @@ -105,6 +129,9 @@ You can make firewall rules persistent with the following iptables example: Firewall { notify => Exec["persist-firewall"] } + Firewallchain { + notify => Exec["persist-firewall"] + } If you wish to ensure any reject rules are executed last, try using stages. The following example shows the creation of a class which is where your @@ -156,6 +183,7 @@ Currently we support: * iptables * ip6tables +* ebtables (chains only) But plans are to support lots of other firewall implementations: diff --git a/lib/puppet/provider/firewallchain/iptables_chain.rb b/lib/puppet/provider/firewallchain/iptables_chain.rb index 4198291..845792a 100644 --- a/lib/puppet/provider/firewallchain/iptables_chain.rb +++ b/lib/puppet/provider/firewallchain/iptables_chain.rb @@ -1,31 +1,42 @@ - Puppet::Type.type(:firewallchain).provide :iptables_chain do @doc = "Iptables chain provider" has_feature :iptables_chain has_feature :policy - commands :iptables => '/sbin/iptables' - commands :iptables_save => '/sbin/iptables-save' - - commands :ip6tables => '/sbin/ip6tables' - commands :ip6tables_save => '/sbin/ip6tables-save' - - optional_commands( { :ebtables => '/sbin/ebtables', - :ebtables_save => '/sbin/ebtables-save' - } ) + optional_commands({ + :iptables => '/sbin/iptables', + :iptables_save => '/sbin/iptables-save', + :ip6tables => '/sbin/ip6tables', + :ip6tables_save => '/sbin/ip6tables-save', + :ebtables => '/sbin/ebtables', + :ebtables_save => '/sbin/ebtables-save', + }) defaultfor :kernel => :linux # chain name is greedy so we anchor from the end. # [\d+:\d+] doesn't exist on ebtables - Mapping = { :IPv4 => { :tables => method( :iptables ), :save => method( :iptables_save), :re => /^:(.+)\s(\S+)\s\[\d+:\d+\]$/ }, - :IPv6 => { :tables => method( :ip6tables ), :save => method( :ip6tables_save ), :re => /^:(.+)\s(\S+)\s\[\d+:\d+\]$/ }, - :ethernet => { :tables => method( :ebtables ), :save => method( :ebtables_save ), :re => /^:(.+)\s(\S+)$/ } - } + Mapping = { + :IPv4 => { + :tables => method(:iptables), + :save => method(:iptables_save), + :re => /^:(.+)\s(\S+)\s\[\d+:\d+\]$/, + }, + :IPv6 => { + :tables => method(:ip6tables), + :save => method(:ip6tables_save), + :re => /^:(.+)\s(\S+)\s\[\d+:\d+\]$/, + }, + :ethernet => { + :tables => method(:ebtables), + :save => method(:ebtables_save), + :re => /^:(.+)\s(\S+)$/, + } + } InternalChains = /^(PREROUTING|POSTROUTING|BROUTING|INPUT|FORWARD|OUTPUT)$/ - Tables = 'NAT|MANGLE|FILTER|RAW|RAWPOST|BROUTE|' - Nameformat = /^(#{Tables}):(.+):(IP(v[46])?|ethernet|)$/ + Tables = 'nat|mangle|filter|raw|rawpost|broute' + Nameformat = /^(#{Tables}):(.+):(IP(v[46])?|ethernet)$/ def create # can't create internal chains @@ -38,8 +49,8 @@ Puppet::Type.type(:firewallchain).provide :iptables_chain do else debug "Inserting chain #{chain} on table #{table} (#{protocol}) using #{t}" t.call ['-t',table,'-N',chain] - if @resource[:policy] != :empty - t.call ['-t',table,'-P',chain,@resource[:policy].to_s.upcase] + unless @resource[:policy].nil? + t.call ['-t',table,'-P',chain,@resource[:policy].to_s.upcase] end end end @@ -52,19 +63,18 @@ Puppet::Type.type(:firewallchain).provide :iptables_chain do end allvalidchains do |t, table, chain| debug "Deleting chain #{chain} on table #{table}" - t.call ['-t',table,'-X',chain] + t.call ['-t',table,'-X',chain] end end def exists? - # we want puppet to call create on 1/2 completed rules (i.e. :ensure => :IPv4/6) properties[:ensure] == :present end def policy=(value) return if value == :empty allvalidchains do |t, table, chain| - p = ['-t',table,'-P',chain,value.to_s.upcase] + p = ['-t',table,'-P',chain,value.to_s.upcase] debug "[set policy] #{t} #{p}" t.call p end @@ -84,12 +94,17 @@ Puppet::Type.type(:firewallchain).provide :iptables_chain do end end + def flush + debug("[flush]") + # Clear the property hash so we re-initialize with updated values + @property_hash.clear + end + # Look up the current status. This allows us to conventiently look up # existing status with properties[:foo]. def properties if @property_hash.empty? @property_hash = query || {:ensure => :absent} - #@property_hash[:ensure] = :absent if @property_hash.empty? end @property_hash.dup end @@ -109,61 +124,41 @@ Puppet::Type.type(:firewallchain).provide :iptables_chain do debug "[instances]" table = nil chains = [] - hash = {} Mapping.each { |p, c| begin - c[:save].call.split("\n").each do |line| + c[:save].call.each_line do |line| if line =~ c[:re] then - name = (table == 'filter' ? '' : table.upcase) + ':' + $1 - policy = $2 == '-' ? :empty : $2.downcase.to_sym - if ( p == :IPv4 or p == :IPv6 ) && table != 'nat' - if hash[name] - # duplicate so create a {table}:{chain}:IP instance - ippolicy = hash[name][:policy] == policy ? policy : :inconsistent - hash.delete(name) - chains << new({:name => name + ':', :policy => ippolicy, :ensure => :present }) - debug "[dup] '#{name}:' #{ippolicy}" - else - hash[name] = { :policy => policy, :protocol => p } - end - end - name += ':' + p.to_s - chains << new({:name => name, :policy => policy, :ensure => :present }) + name = (table == 'filter' ? 'filter' : table) + ':' + $1 + ':' + p.to_s + policy = $2 == '-' ? nil : $2.downcase.to_sym + + chains << new({ + :name => name, + :policy => policy, + :ensure => :present, + }) + debug "[instance] '#{name}' #{policy}" elsif line =~ /^\*(\S+)/ table = $1 - elsif line =~ /^($|-A|COMMIT|#)/ - # other stuff we don't care about else - debug "unrecognised line: #{line}" + next end end rescue Puppet::Error # ignore command not found for ebtables or anything that doesn't exist end } - # put all the chain names that exist in one IP stack into a 1/2 completed (:ensure) state - # The create method will check this and complete only what's required - hash.each { |key, value| - x = {:name => key + ':', :ensure => value[:protocol], :policy => :empty} - debug "halfstate #{x.inspect}" - chains << new(x) - } + chains end def allvalidchains @resource[:name].match(Nameformat) - table = ($1=='') ? 'filter' : $1.downcase + table = $1 chain = $2 protocol = $3 - if protocol == 'IP' || protocol == '' - yield Mapping[:IPv4][:tables],table,chain,:IPv4 - yield Mapping[:IPv6][:tables],table,chain,:IPv6 - else - yield Mapping[protocol][:tables],table,chain,protocol.to_sym - end + yield Mapping[protocol.to_sym][:tables],table,chain,protocol.to_sym end - + end diff --git a/lib/puppet/type/firewallchain.rb b/lib/puppet/type/firewallchain.rb index 542557b..830c68a 100644 --- a/lib/puppet/type/firewallchain.rb +++ b/lib/puppet/type/firewallchain.rb @@ -1,27 +1,16 @@ - Puppet::Type.newtype(:firewallchain) do @doc = <<-EOS - This type provides the capability to manage iptables chains and policies on - internal chains within puppet. - EOS + This type provides the capability to manage rule chains for firewalls. - #InternalChains = /^(PREROUTING|POSTROUTING|BROUTING|INPUT|FORWARD|OUTPUT)$/ - #Tables = 'NAT|MANGLE|FILTER|RAW|RAWPOST|BROUTE|' - ## Technically colons (':') are allowed in table names however it requires - ## ruby-1.9 to do a regex to allow a backslash escaping of the colon. - ## ruby-1.9 regex: Nameformat = /^(
#{Tables}):(([^:]*(?IP(v[46])?|EB)?$/ - #Nameformat = /^(#{Tables}):([^:]+):(IP(v[46])?|ethernet:)$/ + Currently this supports only iptables, ip6tables and ebtables on Linux. And + provides support for setting the default policy on chains and tables that + allow it. + EOS feature :iptables_chain, "The provider provides iptables chain features." feature :policy, "Default policy (inbuilt chains only)" - #autorequire(:firewallchain) do - # if @parameters[:name] =~ /:$/ - # [ @parameters[:name] + 'IPv4', @parameters[:name] + 'IPv6'] - # end - #end - ensurable do defaultvalues defaultto :present @@ -30,46 +19,48 @@ Puppet::Type.newtype(:firewallchain) do newparam(:name) do desc <<-EOS The canonical name of the chain. + + For iptables the format must be {chain}:{table}:{protocol}. EOS isnamevar validate do |value| if value !~ Nameformat then - raise ArgumentError, "Inbuilt chains must be in the form {chain}:{table}:{protocol} where {table} is one of FILTER, NAT, MANGLE, RAW, RAWPOST, BROUTE or empty (alias for filter), chain can be anything without colons or one of PREROUTING, POSTROUTING, BROUTING, INPUT, FORWARD, OUTPUT for the inbuilt chains, and {protocol} being empty or IP (both meaning IPv4 and IPv6), IPv4, IPv6, ethernet (ethernet bridging) got '#{value}' table:'#{$1}' chain:'#{$2}' protocol:'#{$3}'" - else + raise ArgumentError, "Inbuilt chains must be in the form {table}:{chain}:{protocol} where {table} is one of FILTER, NAT, MANGLE, RAW, RAWPOST, BROUTE or empty (alias for filter), chain can be anything without colons or one of PREROUTING, POSTROUTING, BROUTING, INPUT, FORWARD, OUTPUT for the inbuilt chains, and {protocol} being IPv4, IPv6, ethernet (ethernet bridging) got '#{value}' table:'#{$1}' chain:'#{$2}' protocol:'#{$3}'" + else table = $1 chain = $2 protocol = $3 case table - when /^(FILTER|)$/ + when 'filter' if chain =~ /^(PREROUTING|POSTROUTING|BROUTING)$/ raise ArgumentError, "INPUT, OUTPUT and FORWARD are the only inbuilt chains that can be used in table 'filter'" end - when 'MANGLE' + when 'mangle' if chain =~ InternalChains && chain == 'BROUTING' raise ArgumentError, "PREROUTING, POSTROUTING, INPUT, FORWARD and OUTPUT are the only inbuilt chains that can be used in table 'mangle'" end - when 'NAT' + when 'nat' if chain =~ /^(BROUTING|INPUT|FORWARD)$/ raise ArgumentError, "PREROUTING, POSTROUTING and OUTPUT are the only inbuilt chains that can be used in table 'nat'" end if protocol =~/^(IP(v6)?)?$/ - raise ArgumentError, "table nat isn't valid in IPv6 (or the default IP which is IPv4 and IPv6). You must specify ':IPv4' as the name suffix" + raise ArgumentError, "table nat isn't valid in IPv6. You must specify ':IPv4' as the name suffix" end - when 'RAW' + when 'raw' if chain =~ /^(POSTROUTING|BROUTING|INPUT|FORWARD)$/ raise ArgumentError,'PREROUTING and OUTPUT are the only inbuilt chains in the table \'raw\'' end - when 'BROUTE' + when 'broute' if protocol != 'ethernet' raise ArgumentError,'BROUTE is only valid with protocol \'ethernet\'' end if chain =~ /^PREROUTING|POSTROUTING|INPUT|FORWARD|OUTPUT$/ - raise ArgumentError,'BROUTING is the only inbuilt chain allowed on on table \'BROUTE\'' + raise ArgumentError,'BROUTING is the only inbuilt chain allowed on on table \'broute\'' end - end - if chain == 'BROUTING' && ( protocol != 'ethernet' || table!='BROUTE') - raise ArgumentError,'BROUTING is the only inbuilt chain allowed on on table \'BROUTE\' with protocol \'ethernet\' i.e. \'BROUTE:BROUTING:enternet\'' + end + if chain == 'BROUTING' && ( protocol != 'ethernet' || table!='broute') + raise ArgumentError,'BROUTING is the only inbuilt chain allowed on on table \'BROUTE\' with protocol \'ethernet\' i.e. \'broute:BROUTING:enternet\'' end end end @@ -78,7 +69,7 @@ Puppet::Type.newtype(:firewallchain) do newproperty(:policy) do desc <<-EOS This is the action to when the end of the chain is reached. - It can only be set on inbuilt chains ( INPUT, FORWARD, OUTPUT, + It can only be set on inbuilt chains (INPUT, FORWARD, OUTPUT, PREROUTING, POSTROUTING) and can be one of: * accept - the packet is accepted @@ -87,13 +78,14 @@ Puppet::Type.newtype(:firewallchain) do * return - the packet is returned to calling (jump) queue or the default of inbuilt chains EOS - newvalues(:accept, :drop, :queue, :return, :empty) + newvalues(:accept, :drop, :queue, :return) defaultto do - # ethernet chain have an ACCEPT default while other haven't got an allowed value + # ethernet chain have an ACCEPT default while other haven't got an + # allowed value if @resource[:name] =~ /:ethernet$/ :accept else - :empty + nil end end end @@ -107,24 +99,27 @@ Puppet::Type.newtype(:firewallchain) do protocol = $3 # Check that we're not removing an internal chain - if chain =~ InternalChains && value(:ensure).to_s == 'absent' + if chain =~ InternalChains && value(:ensure) == :absent self.fail "Cannot remove in-built chains" end - if value(:policy) == :empty && protocol == 'ethernet' + if value(:policy).nil? && protocol == 'ethernet' self.fail "you must set a non-empty policy on all ethernet table chains" end # Check that we're not setting a policy on a user chain - if chain !~ InternalChains && value(:policy).to_s != 'empty' && protocol != 'ethernet' - self.fail "policy can only be set on in-built chains (with the exceptionn of ethernet chains) (table:#{table} chain:#{chain} protocol:#{protocol})" + if chain !~ InternalChains && + !value(:policy).nil? && + protocol != 'ethernet' + + self.fail "policy can only be set on in-built chains (with the exception of ethernet chains) (table:#{table} chain:#{chain} protocol:#{protocol})" end - + # no DROP policy on nat table if table == 'nat' && - value(:policy).to_s == 'DROP' + value(:policy) == :drop + self.fail 'The "nat" table is not intended for filtering, the use of DROP is therefore inhibited' end end end - diff --git a/spec/unit/puppet/provider/iptables_chain_spec.rb b/spec/unit/puppet/provider/iptables_chain_spec.rb index 3c1af07..143e1e6 100755 --- a/spec/unit/puppet/provider/iptables_chain_spec.rb +++ b/spec/unit/puppet/provider/iptables_chain_spec.rb @@ -38,20 +38,10 @@ describe 'iptables chain provider detection' do # Create a resource instance and make sure the provider is iptables resource = Puppet::Type.type(:firewallchain).new({ - :name => ':test:', + :name => 'filter:test:IPv4', }) resource.provider.class.to_s.should == "Puppet::Type::Firewallchain::ProviderIptables_chain" end - - it "should raise a default provider error when there are no commands" do - # Stub all commands lookups so they return nothing - exists.any_instance.stubs(:which).returns false - - # Instantiate a resource instance and make sure it raises an exception - lambda { resource = Puppet::Type.type(:firewallchain).new({ - :name => ':test:' }) }.should raise_error(Puppet::DevError, - "Could not find a default provider for firewallchain") - end end describe 'iptables chain provider' do @@ -68,7 +58,7 @@ describe 'iptables chain provider' do provider.stubs(:command).with(:iptables_save).returns "/sbin/iptables-save" provider.stubs(:command).with(:ip6tables_save).returns "/sbin/ip6tables-save" end - + it 'should be able to get a list of existing rules' do # Pretend to return nil from iptables provider.expects(:execute).with(['/sbin/ebtables-save']).returns("") @@ -99,7 +89,7 @@ describe 'iptables chain resource parsing' do 'NAT:OUTPUT:ethernet', 'NAT:POSTROUTING:ethernet', ] - provider.expects(:execute).with(['/sbin/ebtables-save']).returns(' + provider.stubs(:execute).with(['/sbin/ebtables-save']).returns(' *broute :BROUTING ACCEPT :broute ACCEPT @@ -136,7 +126,7 @@ describe 'iptables chain resource parsing' do 'NAT:mangle:IPv4', ':$5()*&%\'"^$): :IPv4', ] - provider.expects(:execute).with(['/sbin/iptables-save']).returns(' + provider.stubs(:execute).with(['/sbin/iptables-save']).returns(' # Generated by iptables-save v1.4.9 on Mon Jan 2 01:20:06 2012 *raw :PREROUTING ACCEPT [12:1780] @@ -185,7 +175,7 @@ COMMIT ':OUTPUT:IPv6', ':test:IPv6', ] - provider.expects(:execute).with(['/sbin/ip6tables-save']).returns(' + provider.stubs(:execute).with(['/sbin/ip6tables-save']).returns(' # Generated by ip6tables-save v1.4.9 on Mon Jan 2 01:31:39 2012 *raw :PREROUTING ACCEPT [2173:489241] @@ -218,7 +208,7 @@ COMMIT ip6tables.each { |name| @all += [ name[0..-3], name[0..-5] ] } end - it 'should have all in parsed resources' do + it 'should have all in parsed resources' do provider.instances.each do |resource| @all.include?(resource.name) end diff --git a/spec/unit/puppet/type/firewallchain_spec.rb b/spec/unit/puppet/type/firewallchain_spec.rb index 23ce0a8..2bb031f 100755 --- a/spec/unit/puppet/type/firewallchain_spec.rb +++ b/spec/unit/puppet/type/firewallchain_spec.rb @@ -5,41 +5,43 @@ require 'spec_helper' firewallchain = Puppet::Type.type(:firewallchain) describe firewallchain do - before :each do - @class = firewallchain - @provider = stub 'provider' - @provider.stubs(:name).returns(:iptables_chain) - Puppet::Type::Firewallchain.stubs(:defaultprovider).returns @provider - @resource = @class.new({:name => ':INPUT:', :policy => :accept }) - end + let(:klass) { firewallchain } + let(:provider) { + prov = stub 'provider' + prov.stubs(:name).returns(:iptables_chain) + prov + } + let(:resource) { + Puppet::Type::Firewallchain.stubs(:defaultprovider).returns provider + klass.new({:name => 'filter:INPUT:IPv4', :policy => :accept }) + } it 'should have :name be its namevar' do - @class.key_attributes.should == [:name] + klass.key_attributes.should == [:name] end describe ':name' do - {'' => ['INPUT','OUTPUT','FORWARD'], - 'NAT' => ['PREROUTING', 'POSTROUTING', 'OUTPUT'], - 'MANGLE' => [ 'PREROUTING', 'POSTROUTING', 'INPUT', 'FORWARD', 'OUTPUT' ], - 'FILTER' => ['INPUT','OUTPUT','FORWARD'], - 'RAW' => [ 'PREROUTING', 'OUTPUT'], - 'BROUTE' => ['BROUTING'] + {'nat' => ['PREROUTING', 'POSTROUTING', 'OUTPUT'], + 'mangle' => [ 'PREROUTING', 'POSTROUTING', 'INPUT', 'FORWARD', 'OUTPUT' ], + 'filter' => ['INPUT','OUTPUT','FORWARD'], + 'raw' => [ 'PREROUTING', 'OUTPUT'], + 'broute' => ['BROUTING'] }.each_pair do |table, allowedinternalchains| - ['', 'IPv4', 'IPv6', 'IP', 'ethernet'].each do |protocol| + ['IPv4', 'IPv6', 'ethernet'].each do |protocol| [ 'test', '$5()*&%\'"^$09):' ].each do |chainname| name = "#{table}:#{chainname}:#{protocol}" - if table == 'NAT' && ['IPv6','','IP'].include?(protocol) + if table == 'nat' && protocol == 'IPv6' it "should fail #{name}" do - lambda { @resource[:name] = name }.should raise_error(Puppet::Error) + expect { resource[:name] = name }.to raise_error(Puppet::Error) end - elsif protocol != 'ethernet' && table == 'BROUTE' + elsif protocol != 'ethernet' && table == 'broute' it "should fail #{name}" do - lambda { @resource[:name] = name }.should raise_error(Puppet::Error) + expect { resource[:name] = name }.to raise_error(Puppet::Error) end else it "should accept name #{name}" do - @resource[:name] = name - @resource[:name].should == name + resource[:name] = name + resource[:name].should == name end end end # chainname @@ -49,17 +51,19 @@ describe firewallchain do name = table + ':' + internalchain + ':' if internalchain == 'BROUTING' name += 'ethernet' - elsif table == 'NAT' + elsif table == 'nat' + name += 'IPv4' + else name += 'IPv4' end if allowedinternalchains.include? internalchain it "should allow #{name}" do - @resource[:name] = name - @resource[:name].should == name + resource[:name] = name + resource[:name].should == name end else it "should fail #{name}" do - lambda { @resource[:name] = name }.should raise_error(Puppet::Error) + expect { resource[:name] = name }.to raise_error(Puppet::Error) end end end # internalchain @@ -67,34 +71,34 @@ describe firewallchain do end # table, allowedinternalchainnames it 'should fail with invalid table names' do - lambda { @resource[:name] = 'wrongtablename:test:' }.should raise_error(Puppet::Error) + expect { resource[:name] = 'wrongtablename:test:' }.to raise_error(Puppet::Error) end it 'should fail with invalid protocols names' do - lambda { @resource[:name] = ':test:IPv5' }.should raise_error(Puppet::Error) + expect { resource[:name] = ':test:IPv5' }.to raise_error(Puppet::Error) end end describe ':policy' do - + [:accept, :drop, :queue, :return].each do |policy| it "should accept policy #{policy}" do - @resource[:policy] = policy - @resource[:policy].should == policy + resource[:policy] = policy + resource[:policy].should == policy end end it 'should fail when value is not recognized' do - lambda { @resource[:policy] = 'not valid' }.should raise_error(Puppet::Error) + expect { resource[:policy] = 'not valid' }.to raise_error(Puppet::Error) end [:accept, :drop, :queue, :return].each do |policy| it "non-inbuilt chains should not accept policy #{policy}" do - lambda { @class.new({:name => ':testchain:', :policy => policy }) }.should raise_error(Puppet::Error) + expect { klass.new({:name => 'filter:testchain:IPv4', :policy => policy }) }.to raise_error(Puppet::Error) end it "non-inbuilt chains can accept policies on protocol = ethernet (policy #{policy})" do - @class.new({:name => ':testchain:ethernet', :policy => policy }).should be_instance_of(@provider) + klass.new({:name => 'filter:testchain:ethernet', :policy => policy }).should be_instance_of(@provider) end end