]> review.fuel-infra Code Review - puppet-modules/puppetlabs-firewall.git/commitdiff
add conntrack module support
authorMatt Haught <dmhaught@ncsu.edu>
Fri, 8 Nov 2019 06:14:27 +0000 (01:14 -0500)
committerMatt Haught <dmhaught@ncsu.edu>
Fri, 8 Nov 2019 06:14:27 +0000 (01:14 -0500)
lib/puppet/provider/firewall/ip6tables.rb
lib/puppet/provider/firewall/iptables.rb
lib/puppet/type/firewall.rb
spec/unit/puppet/type/firewall_spec.rb

index eca66df95bcf7959bbcd5beff0e60f831008c5bc..85f77df0544a7b4d985cb48969362f526ea69c1d 100644 (file)
@@ -3,6 +3,7 @@ Puppet::Type.type(:firewall).provide :ip6tables, parent: :iptables, source: :ip6
 
   has_feature :iptables
   has_feature :connection_limiting
+  has_feature :conntrack
   has_feature :hop_limiting
   has_feature :rate_limiting
   has_feature :recent_limiting
@@ -71,7 +72,19 @@ Puppet::Type.type(:firewall).provide :ip6tables, parent: :iptables, source: :ip6
     connlimit_above: '-m connlimit --connlimit-above',
     connlimit_mask: '--connlimit-mask',
     connmark: '-m connmark --mark',
-    ctstate: '-m conntrack --ctstate',
+    ctstate: '--ctstate',
+    ctproto: '--ctproto',
+    ctorigsrc: '--ctorigsrc',
+    ctorigdst: '--ctorigdst',
+    ctreplsrc: '--ctreplsrc',
+    ctrepldst: '--ctrepldst',
+    ctorigsrcport: '--ctorigsrcport',
+    ctorigdstport: '--ctorigdstport',
+    ctreplsrcport: '--ctreplsrcport',
+    ctrepldstport: '--ctrepldstport',
+    ctstatus: '--ctstatus',
+    ctexpire: '--ctexpire',
+    ctdir: '--ctdir',
     destination: '-d',
     dport: ['-m multiport --dports', '--dport'],
     dst_range: '--dst-range',
@@ -207,6 +220,8 @@ Puppet::Type.type(:firewall).provide :ip6tables, parent: :iptables, source: :ip6
     addrtype: [:src_type, :dst_type],
     iprange: [:src_range, :dst_range],
     owner: [:uid, :gid],
+    conntrack: [:ctstate, :ctproto, :ctorigsrc, :ctorigdst, :ctreplsrc, :ctrepldst, 
+                :ctorigsrcport, :ctorigdstport, :ctreplsrcport, :ctrepldstport, :ctstatus, :ctexpire, :ctdir],
     time: [:time_start, :time_stop, :month_days, :week_days, :date_start, :date_stop, :time_contiguous, :kernel_timezone],
     geoip: [:src_cc, :dst_cc],
     hashlimit: [:hashlimit_upto, :hashlimit_above, :hashlimit_name, :hashlimit_burst, :hashlimit_mode, :hashlimit_srcmask, :hashlimit_dstmask,
@@ -253,7 +268,9 @@ Puppet::Type.type(:firewall).provide :ip6tables, parent: :iptables, source: :ip6
                     :proto, :ishasmorefrags, :islastfrag, :isfirstfrag, :src_range, :dst_range,
                     :tcp_flags, :uid, :gid, :mac_source, :sport, :dport, :port, :src_type,
                     :dst_type, :socket, :pkttype, :ipsec_dir, :ipsec_policy, :state,
-                    :ctstate, :icmp, :hop_limit, :limit, :burst, :length, :recent, :rseconds, :reap,
+                    :ctstate, :ctproto, :ctorigsrc, :ctorigdst, :ctreplsrc, :ctrepldst,
+                    :ctorigsrcport, :ctorigdstport, :ctreplsrcport, :ctrepldstport, :ctstatus, :ctexpire, :ctdir,
+                    :icmp, :hop_limit, :limit, :burst, :length, :recent, :rseconds, :reap,
                     :rhitcount, :rttl, :rname, :mask, :rsource, :rdest, :ipset, :string, :string_algo,
                     :string_from, :string_to, :jump, :clamp_mss_to_pmtu, :gateway, :todest,
                     :tosource, :toports, :checksum_fill, :log_level, :log_prefix, :log_uid, :reject, :set_mss, :set_dscp, :set_dscp_class, :mss, :queue_num, :queue_bypass,
index 381906061dc66bb5a0ffc33fde50183c546a387f..30a16712b3ed761ff5ff88ff3b7ff75d1787ee34 100644 (file)
@@ -8,6 +8,7 @@ Puppet::Type.type(:firewall).provide :iptables, parent: Puppet::Provider::Firewa
 
   has_feature :iptables
   has_feature :connection_limiting
+  has_feature :conntrack
   has_feature :rate_limiting
   has_feature :recent_limiting
   has_feature :snat
@@ -67,7 +68,19 @@ Puppet::Type.type(:firewall).provide :iptables, parent: Puppet::Provider::Firewa
     connlimit_above: '-m connlimit --connlimit-above',
     connlimit_mask: '--connlimit-mask',
     connmark: '-m connmark --mark',
-    ctstate: '-m conntrack --ctstate',
+    ctstate: '--ctstate',
+    ctproto: '--ctproto',
+    ctorigsrc: '--ctorigsrc',
+    ctorigdst: '--ctorigdst',
+    ctreplsrc: '--ctreplsrc',
+    ctrepldst: '--ctrepldst',
+    ctorigsrcport: '--ctorigsrcport',
+    ctorigdstport: '--ctorigdstport',
+    ctreplsrcport: '--ctreplsrcport',
+    ctrepldstport: '--ctrepldstport',
+    ctstatus: '--ctstatus',
+    ctexpire: '--ctexpire',
+    ctdir: '--ctdir',
     destination: '-d',
     dport: ['-m multiport --dports', '--dport'],
     dst_range: '--dst-range',
@@ -214,6 +227,8 @@ Puppet::Type.type(:firewall).provide :iptables, parent: Puppet::Provider::Firewa
     addrtype: [:src_type, :dst_type],
     iprange: [:src_range, :dst_range],
     owner: [:uid, :gid],
+    conntrack: [:ctstate, :ctproto, :ctorigsrc, :ctorigdst, :ctreplsrc, :ctrepldst, 
+                :ctorigsrcport, :ctorigdstport, :ctreplsrcport, :ctrepldstport, :ctstatus, :ctexpire, :ctdir],
     time: [:time_start, :time_stop, :month_days, :week_days, :date_start, :date_stop, :time_contiguous, :kernel_timezone],
     geoip: [:src_cc, :dst_cc],
     hashlimit: [:hashlimit_upto, :hashlimit_above, :hashlimit_name, :hashlimit_burst, :hashlimit_mode, :hashlimit_srcmask, :hashlimit_dstmask,
@@ -296,7 +311,9 @@ Puppet::Type.type(:firewall).provide :iptables, parent: Puppet::Provider::Firewa
     :proto, :isfragment, :stat_mode, :stat_every, :stat_packet, :stat_probability,
     :src_range, :dst_range, :tcp_flags, :uid, :gid, :mac_source, :sport, :dport, :port,
     :src_type, :dst_type, :socket, :pkttype, :ipsec_dir, :ipsec_policy,
-    :state, :ctstate, :icmp, :limit, :burst, :length, :recent, :rseconds, :reap,
+    :state, :ctstate, :ctproto, :ctorigsrc, :ctorigdst, :ctreplsrc, :ctrepldst,
+    :ctorigsrcport, :ctorigdstport, :ctreplsrcport, :ctrepldstport, :ctstatus, :ctexpire, :ctdir,
+    :icmp, :limit, :burst, :length, :recent, :rseconds, :reap,
     :rhitcount, :rttl, :rname, :mask, :rsource, :rdest, :ipset, :string, :string_algo,
     :string_from, :string_to, :jump, :goto, :clusterip_new, :clusterip_hashmode,
     :clusterip_clustermac, :clusterip_total_nodes, :clusterip_local_node, :clusterip_hash_init, :queue_num, :queue_bypass,
@@ -545,7 +562,7 @@ Puppet::Type.type(:firewall).provide :iptables, parent: Puppet::Provider::Firewa
     # POST PARSE CLUDGING
     #####################
 
-    [:dport, :sport, :port, :state, :ctstate].each do |prop|
+    [:dport, :sport, :port, :state, :ctstate, :ctstatus].each do |prop|
       hash[prop] = hash[prop].split(',') unless hash[prop].nil?
     end
 
@@ -607,6 +624,17 @@ Puppet::Type.type(:firewall).provide :iptables, parent: Puppet::Provider::Firewa
     [
       :connmark,
       :ctstate,
+      :ctproto,
+      :ctorigsrc,
+      :ctorigdst,
+      :ctreplsrc,
+      :ctrepldst,
+      :ctorigsrcport,
+      :ctorigdstport,
+      :ctreplsrcport,
+      :ctrepldstport,
+      :ctstatus,
+      :ctexpire,
       :destination,
       :dport,
       :dst_range,
@@ -646,6 +674,7 @@ Puppet::Type.type(:firewall).provide :iptables, parent: Puppet::Provider::Firewa
     # iptables-save and user supplied resources is consistent.
     hash[:state]   = hash[:state].sort   unless hash[:state].nil?
     hash[:ctstate] = hash[:ctstate].sort unless hash[:ctstate].nil?
+    hash[:ctstatus] = hash[:ctstatus].sort unless hash[:ctstatus].nil?
 
     # This forces all existing, commentless rules or rules with invalid comments to be moved
     # to the bottom of the stack.
index cf27165369bf09290d1d325461f4180235aa8e65..9beb4a702816894e41f4f7d326284236da81064e 100644 (file)
@@ -33,7 +33,7 @@ Puppet::Type.newtype(:firewall) do
       * ip6tables: Ip6tables type provider
 
         * Required binaries: ip6tables-save, ip6tables.
-        * Supported features: address_type, connection_limiting, dnat, hop_limiting, icmp_match,
+        * Supported features: address_type, connection_limiting, conntrack, dnat, hop_limiting, icmp_match,
         interface_match, iprange, ipsec_dir, ipsec_policy, ipset, iptables, isfirstfrag,
         ishasmorefrags, islastfrag, length, log_level, log_prefix, log_uid, mark, mask, mss,
         owner, pkttype, queue_bypass, queue_num, rate_limiting, recent_limiting, reject_type,
@@ -43,7 +43,7 @@ Puppet::Type.newtype(:firewall) do
 
         * Required binaries: iptables-save, iptables.
         * Default for kernel == linux.
-        * Supported features: address_type, clusterip, connection_limiting, dnat, icmp_match,
+        * Supported features: address_type, clusterip, connection_limiting, conntrack, dnat, icmp_match,
         interface_match, iprange, ipsec_dir, ipsec_policy, ipset, iptables, isfragment, length,
         log_level, log_prefix, log_uid, mark, mask, mss, netmap, nflog_group, nflog_prefix,
         nflog_range, nflog_threshold, owner, pkttype, queue_bypass, queue_num, rate_limiting,
@@ -56,6 +56,8 @@ Puppet::Type.newtype(:firewall) do
 
       * connection_limiting: Connection limiting features.
 
+      * conntrack: Connection tracking features.
+
       * dnat: Destination NATing.
 
       * hop_limiting: Hop limiting features.
@@ -134,6 +136,7 @@ Puppet::Type.newtype(:firewall) do
   PUPPETCODE
 
   feature :connection_limiting, 'Connection limiting features.'
+  feature :conntrack, 'Connection tracking features.'
   feature :hop_limiting, 'Hop limiting features.'
   feature :rate_limiting, 'Rate limiting features.'
   feature :recent_limiting, 'The netfilter recent module'
@@ -910,7 +913,7 @@ Puppet::Type.newtype(:firewall) do
     end
   end
 
-  newproperty(:ctstate, array_matching: :all, required_features: :state_match) do
+  newproperty(:ctstate, array_matching: :all, required_features: :conntrack) do
     desc <<-PUPPETCODE
       Matches a packet based on its state in the firewall stateful inspection
       table, using the conntrack module. Values can be:
@@ -920,9 +923,11 @@ Puppet::Type.newtype(:firewall) do
       * NEW
       * RELATED
       * UNTRACKED
+      * SNAT
+      * DNAT
     PUPPETCODE
 
-    newvalues(:INVALID, :ESTABLISHED, :NEW, :RELATED, :UNTRACKED)
+    newvalues(:INVALID, :ESTABLISHED, :NEW, :RELATED, :UNTRACKED, :SNAT, :DNAT)
 
     # States should always be sorted. This normalizes the resource states to
     # keep it consistent with the sorted result from iptables-save.
@@ -940,6 +945,286 @@ Puppet::Type.newtype(:firewall) do
     end
   end
 
+  newproperty(:ctproto, required_features: :conntrack) do
+    desc <<-PUPPETCODE
+      The specific layer-4 protocol number to match for this rule using the 
+      conntrack module.
+    PUPPETCODE
+    newvalue(%r{^!?\s?\d+$})
+  end
+
+  newproperty(:ctorigsrc, required_features: :conntrack) do
+    desc <<-PUPPETCODE
+      The original source address using the conntrack module. For example:
+
+          ctorigsrc => '192.168.2.0/24'
+
+      You can also negate a mask by putting ! in front. For example:
+
+          ctorigsrc => '! 192.168.2.0/24'
+
+      The ctorigsrc can also be an IPv6 address if your provider supports it.
+    PUPPETCODE
+
+    munge do |value|
+      case @resource[:provider]
+      when :iptables
+        protocol = :IPv4
+      when :ip6tables
+        protocol = :IPv6
+      else
+        raise('cannot work out protocol family')
+      end
+
+      begin
+        @resource.host_to_mask(value, protocol)
+        if protocol == :IPv4
+          value.chomp("/32")
+        elsif protocol == :IPv6
+          value.chomp("/128")
+        end
+      rescue StandardError => e
+        raise("host_to_ip failed for #{value}, exception #{e}")
+      end
+    end
+  end
+
+  newproperty(:ctorigdst, required_features: :conntrack) do
+    desc <<-PUPPETCODE
+      The original destination address using the conntrack module. For example:
+
+          ctorigdst => '192.168.2.0/24'
+
+      You can also negate a mask by putting ! in front. For example:
+
+          ctorigdst => '! 192.168.2.0/24'
+
+      The ctorigdst can also be an IPv6 address if your provider supports it.
+    PUPPETCODE
+
+    munge do |value|
+      case @resource[:provider]
+      when :iptables
+        protocol = :IPv4
+      when :ip6tables
+        protocol = :IPv6
+      else
+        raise('cannot work out protocol family')
+      end
+
+      begin
+        @resource.host_to_mask(value, protocol)
+        if protocol == :IPv4
+          value.chomp("/32")
+        elsif protocol == :IPv6
+          value.chomp("/128")
+        end
+      rescue StandardError => e
+        raise("host_to_ip failed for #{value}, exception #{e}")
+      end
+    end
+  end
+
+  newproperty(:ctreplsrc, required_features: :conntrack) do
+    desc <<-PUPPETCODE
+      The reply source address using the conntrack module. For example:
+
+          ctreplsrc => '192.168.2.0/24'
+
+      You can also negate a mask by putting ! in front. For example:
+
+          ctreplsrc => '! 192.168.2.0/24'
+
+      The ctreplsrc can also be an IPv6 address if your provider supports it.
+    PUPPETCODE
+
+    munge do |value|
+      case @resource[:provider]
+      when :iptables
+        protocol = :IPv4
+      when :ip6tables
+        protocol = :IPv6
+      else
+        raise('cannot work out protocol family')
+      end
+
+      begin
+        @resource.host_to_mask(value, protocol)
+        if protocol == :IPv4
+          value.chomp("/32")
+        elsif protocol == :IPv6
+          value.chomp("/128")
+        end
+      rescue StandardError => e
+        raise("host_to_ip failed for #{value}, exception #{e}")
+      end
+    end
+  end
+
+  newproperty(:ctrepldst, required_features: :conntrack) do
+    desc <<-PUPPETCODE
+      The reply destination address using the conntrack module. For example:
+
+          ctrepldst => '192.168.2.0/24'
+
+      You can also negate a mask by putting ! in front. For example:
+
+          ctrepldst => '! 192.168.2.0/24'
+
+      The ctrepldst can also be an IPv6 address if your provider supports it.
+    PUPPETCODE
+
+    munge do |value|
+      case @resource[:provider]
+      when :iptables
+        protocol = :IPv4
+      when :ip6tables
+        protocol = :IPv6
+      else
+        raise('cannot work out protocol family')
+      end
+
+      begin
+        @resource.host_to_mask(value, protocol)
+        if protocol == :IPv4
+          value.chomp("/32")
+        elsif protocol == :IPv6
+          value.chomp("/128")
+        end
+      rescue StandardError => e
+        raise("host_to_ip failed for #{value}, exception #{e}")
+      end
+    end
+  end
+
+  newproperty(:ctorigsrcport, required_features: :conntrack) do
+    desc <<-PUPPETCODE
+      The original source port to match for this filter using the conntrack module. 
+      For example:
+
+          ctorigsrcport => '80'
+
+      You can also specify a port range: For example:
+
+          ctorigsrcport => '80:81'
+
+      You can also negate a port by putting ! in front. For example:
+
+          ctorigsrcport => '! 80'
+
+    PUPPETCODE
+    newvalue(%r{^!?\s?\d+$|^!?\s?\d+\:\d+$})
+  end
+
+  newproperty(:ctorigdstport, required_features: :conntrack) do
+    desc <<-PUPPETCODE
+      The original destination port to match for this filter using the conntrack module. 
+      For example:
+
+          ctorigdstport => '80'
+
+      You can also specify a port range: For example:
+
+          ctorigdstport => '80:81'
+
+      You can also negate a port by putting ! in front. For example:
+
+          ctorigdstport => '! 80'
+
+    PUPPETCODE
+    newvalue(%r{^!?\s?\d+$|^!?\s?\d+\:\d+$})
+  end
+
+  newproperty(:ctreplsrcport, required_features: :conntrack) do
+    desc <<-PUPPETCODE
+      The reply source port to match for this filter using the conntrack module. 
+      For example:
+
+          ctreplsrcport => '80'
+
+      You can also specify a port range: For example:
+
+          ctreplsrcport => '80:81'
+
+      You can also negate a port by putting ! in front. For example:
+
+          ctreplsrcport => '! 80'
+
+    PUPPETCODE
+    newvalue(%r{^!?\s?\d+$|^!?\s?\d+\:\d+$})
+  end
+
+  newproperty(:ctrepldstport, required_features: :conntrack) do
+    desc <<-PUPPETCODE
+      The reply destination port to match for this filter using the conntrack module. 
+      For example:
+
+          ctrepldstport => '80'
+
+      You can also specify a port range: For example:
+
+          ctrepldstport => '80:81'
+
+      You can also negate a port by putting ! in front. For example:
+
+          ctrepldstport => '! 80'
+
+    PUPPETCODE
+    newvalue(%r{^!?\s?\d+$|^!?\s?\d+\:\d+$})
+  end
+
+  newproperty(:ctstatus, array_matching: :all, required_features: :conntrack) do
+    desc <<-PUPPETCODE
+      Matches a packet based on its status using the conntrack module. Values can be:
+
+      * EXPECTED
+      * SEEN_REPLY
+      * ASSURED
+      * CONFIRMED
+    PUPPETCODE
+
+    newvalues(:NONE, :EXPECTED, :SEEN_REPLY, :ASSURED, :CONFIRMED)
+
+    # Statuses should always be sorted. This normalizes the resource status to
+    # keep it consistent with the sorted result from iptables-save.
+    def should=(values)
+      @should = super(values).sort_by { |sym| sym.to_s }
+    end
+
+    def to_s?(value)
+      should_to_s(value)
+    end
+
+    def should_to_s(value)
+      value = [value] unless value.is_a?(Array)
+      value.join(',')
+    end
+  end
+
+  newproperty(:ctexpire, required_features: :conntrack) do
+    desc <<-PUPPETCODE
+      Matches a packet based on lifetime remaining in seconds or range of values
+      using the conntrack module. For example:
+
+          ctexpire => '100:150'
+
+    PUPPETCODE
+    newvalue(%r{^!?\s?\d+$|^!?\s?\d+\:\d+$})
+  end
+
+  newproperty(:ctdir, required_features: :conntrack) do
+    desc <<-PUPPETCODE
+      Matches a packet that is flowing in the specified direction using the 
+      conntrack module. If this flag is not specified at all, matches packets 
+      in both directions. Values can be:
+
+      * REPLY
+      * ORIGINAL
+    PUPPETCODE
+
+    newvalues(:REPLY, :ORIGINAL)
+  end
+
   # Connection mark
   newproperty(:connmark, required_features: :mark) do
     desc <<-PUPPETCODE
index 6f29602a807083bf268b7b5de9377b8e2a3a3881..8897b32461667f33205f6e7a7e7eadcd3c5374cc 100755 (executable)
@@ -436,6 +436,108 @@ describe firewall do # rubocop:disable RSpec/MultipleDescribes
     end
   end
 
+  describe ':ctproto' do
+    it 'accepts numeric value' do
+      resource[:ctproto] = 6
+      expect(resource[:ctproto]).to eql 6
+    end
+    it 'accepts negated string value' do
+      resource[:ctproto] = '! 6'
+      expect(resource[:ctproto]).to eql '! 6'
+    end
+  end
+
+  [:ctorigsrc, :ctorigdst, :ctreplsrc, :ctrepldst].each do |addr|
+    describe addr do
+      it "should accept a #{addr} as a string without /32" do
+        resource[addr] = '127.0.0.1'
+        expect(resource[addr]).to eql '127.0.0.1'
+      end
+      it "should accept a #{addr} as a string with /32" do
+        resource[addr] = '127.0.0.1/32'
+        expect(resource[addr]).to eql '127.0.0.1'
+      end
+      it "should accept a #{addr} as a string with cidr" do
+        resource[addr] = '10.0.0.0/8'
+        expect(resource[addr]).to eql '10.0.0.0/8'
+      end
+      it "should accept a #{addr} as a string with ipv6 cidr" do
+        resource[addr] = '2001:DB8::/64'
+        expect(resource[addr]).to eql '2001:DB8::/64'
+      end
+      it "should accept a negated #{addr} as a string" do
+        resource[addr] = '! 127.0.0.1'
+        expect(resource[addr]).to eql '! 127.0.0.1'
+      end
+      it "should accept a negated #{addr} as a string with cidr" do
+        resource[addr] = '! 10.0.0.0/8'
+        expect(resource[addr]).to eql '! 10.0.0.0/8'
+      end
+    end
+  end
+
+  [:ctorigsrcport, :ctorigdstport, :ctreplsrcport, :ctrepldstport].each do |port|
+    describe port do
+      it "should accept #{port} as numeric value" do
+        resource[port] = 80
+        expect(resource[port]).to eql 80
+      end
+      it "should accept #{port} as range value" do
+        resource[port] = '80:81'
+        expect(resource[port]).to eql '80:81'
+      end
+      it "should accept a negated #{port} as string value" do
+        resource[port] = '! 80'
+        expect(resource[port]).to eql '! 80'
+      end
+      it "should accept a negated #{port} as range value" do
+        resource[port] = '! 80:81'
+        expect(resource[port]).to eql '! 80:81'
+      end
+    end
+  end
+
+  describe ':ctstatus' do
+    it 'accepts value as a string - EXPECTED' do
+      resource[:ctstatus] = :EXPECTED
+      expect(resource[:ctstatus]).to eql [:EXPECTED]
+    end
+
+    it 'accepts value as an array - EXPECTED, SEEN_REPLY' do
+      resource[:ctstatus] = [:EXPECTED, :SEEN_REPLY]
+      expect(resource[:ctstatus]).to eql [:EXPECTED, :SEEN_REPLY]
+    end
+
+    it 'sorts values alphabetically - SEEN_REPLY, EXPECTED' do
+      resource[:ctstatus] = [:SEEN_REPLY, :EXPECTED]
+      expect(resource[:ctstatus]).to eql [:EXPECTED, :SEEN_REPLY]
+    end
+  end
+
+  describe ':ctexpire' do
+    it 'accepts numeric values' do
+      resource[:ctexpire] = 100
+      expect(resource[:ctexpire]).to eql 100
+    end
+
+    it 'accepts numeric range values' do
+      resource[:ctexpire] = '100:120'
+      expect(resource[:ctexpire]).to eql '100:120'
+    end
+  end
+
+  describe ':ctdir' do
+    it 'accepts value as a string - REPLY' do
+      resource[:ctdir] = :REPLY
+      expect(resource[:ctdir]).to be :REPLY
+    end
+
+    it 'accepts value as a string - ORIGINAL' do
+      resource[:ctdir] = :ORIGINAL
+      expect(resource[:ctdir]).to be :ORIGINAL
+    end
+  end
+
   describe ':burst' do
     it 'accepts numeric values' do
       resource[:burst] = 12