]> review.fuel-infra Code Review - puppet-modules/puppetlabs-firewall.git/commitdiff
Add additional acceptance tests to cover all parameters.
authorAshley Penney <ashley.penney@puppetlabs.com>
Mon, 13 Jan 2014 21:20:50 +0000 (16:20 -0500)
committerAshley Penney <ashley.penney@puppetlabs.com>
Wed, 15 Jan 2014 18:26:55 +0000 (13:26 -0500)
spec/acceptance/firewall_spec.rb [new file with mode: 0644]
spec/acceptance/firewallchain_spec.rb [new file with mode: 0644]
spec/acceptance/rules_spec.rb [new file with mode: 0644]

diff --git a/spec/acceptance/firewall_spec.rb b/spec/acceptance/firewall_spec.rb
new file mode 100644 (file)
index 0000000..8bc2da4
--- /dev/null
@@ -0,0 +1,1465 @@
+require 'spec_helper_acceptance'
+
+describe 'firewall type' do
+
+  describe 'reset' do
+    it 'deletes all rules' do
+      shell('iptables --flush; iptables -t nat --flush; iptables -t mangle --flush')
+    end
+  end
+
+  describe 'name' do
+    context 'valid' do
+      it 'applies cleanly' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '001 - test': ensure => present }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+    end
+
+    context 'invalid' do
+      it 'fails' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { 'test': ensure => present }
+        EOS
+
+        apply_manifest(pp, :expect_failures => true) do |r|
+          expect(r.stderr).to match(/Invalid value "test"./)
+        end
+      end
+    end
+  end
+
+  describe 'ensure' do
+    context 'default' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '555 - test':
+            proto  => tcp,
+            port   => '555',
+            action => accept,
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+         shell('iptables -S') do |r|
+           expect(r.stdout).to match(/-A INPUT -p tcp -m multiport --ports 555 -m comment --comment "555 - test" -j ACCEPT/)
+         end
+      end
+    end
+
+    context 'present' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '555 - test':
+            ensure => present,
+            proto  => tcp,
+            port   => '555',
+            action => accept,
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+         shell('iptables -S') do |r|
+           expect(r.stdout).to match(/-A INPUT -p tcp -m multiport --ports 555 -m comment --comment "555 - test" -j ACCEPT/)
+         end
+      end
+    end
+
+    context 'absent' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '555 - test':
+            ensure => absent,
+            proto  => tcp,
+            port   => '555',
+            action => accept,
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should not contain the rule' do
+         shell('iptables -S') do |r|
+           expect(r.stdout).to_not match(/-A INPUT -p tcp -m multiport --ports 555 -m comment --comment "555 - test" -j ACCEPT/)
+         end
+      end
+    end
+  end
+
+  describe 'source' do
+    context '192.168.2.0/24' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '556 - test':
+            proto  => tcp,
+            port   => '556',
+            action => accept,
+            source => '192.168.2.0/24',
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+         shell('iptables -S') do |r|
+           expect(r.stdout).to match(/-A INPUT -s 192.168.2.0\/24 -p tcp -m multiport --ports 556 -m comment --comment "556 - test" -j ACCEPT/)
+         end
+      end
+    end
+
+    context '! 192.168.2.0/24' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '556 - test':
+            proto  => tcp,
+            port   => '556',
+            action => accept,
+            source => '! 192.168.2.0/24',
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+         shell('iptables -S') do |r|
+           expect(r.stdout).to match(/-A INPUT ! -s 192.168.2.0\/24 -p tcp -m multiport --ports 556 -m comment --comment "556 - test" -j ACCEPT/)
+         end
+      end
+    end
+
+    # Invalid address
+    context '256.168.2.0/24' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '556 - test':
+            proto  => tcp,
+            port   => '556',
+            action => accept,
+            source => '256.168.2.0/24',
+          }
+        EOS
+
+        apply_manifest(pp, :expect_failures => true) do |r|
+          expect(r.stderr).to match(/host_to_ip failed for 256.168.2.0\/24/)
+        end
+      end
+
+      it 'should not contain the rule' do
+         shell('iptables -S') do |r|
+           expect(r.stdout).to_not match(/-A INPUT -s 256.168.2.0\/24 -p tcp -m multiport --ports 556 -m comment --comment "556 - test" -j ACCEPT/)
+         end
+      end
+    end
+  end
+
+  describe 'src_range' do
+    context '192.168.1.1-192.168.1.10' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '557 - test':
+            proto  => tcp,
+            port   => '557',
+            action => accept,
+            src_range => '192.168.1.1-192.168.1.10',
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+         shell('iptables -S') do |r|
+           expect(r.stdout).to match(/-A INPUT -p tcp -m iprange --src-range 192.168.1.1-192.168.1.10 -m multiport --ports 557 -m comment --comment "557 - test" -j ACCEPT/)
+         end
+      end
+    end
+
+    # Invalid IP
+    context '392.168.1.1-192.168.1.10' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '557 - test':
+            proto  => tcp,
+            port   => '557',
+            action => accept,
+            src_range => '392.168.1.1-192.168.1.10',
+          }
+        EOS
+
+        apply_manifest(pp, :expect_failures => true) do |r|
+          expect(r.stderr).to match(/Invalid value "392.168.1.1-192.168.1.10"/)
+        end
+      end
+
+      it 'should not contain the rule' do
+         shell('iptables -S') do |r|
+           expect(r.stdout).to_not match(/-A INPUT -p tcp -m iprange --src-range 392.168.1.1-192.168.1.10 -m multiport --ports 557 -m comment --comment "557 - test" -j ACCEPT/)
+         end
+      end
+    end
+  end
+
+  describe 'destination' do
+    context '192.168.2.0/24' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '558 - test':
+            proto  => tcp,
+            port   => '558',
+            action => accept,
+            destination => '192.168.2.0/24',
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+         shell('iptables -S') do |r|
+           expect(r.stdout).to match(/-A INPUT -d 192.168.2.0\/24 -p tcp -m multiport --ports 558 -m comment --comment "558 - test" -j ACCEPT/)
+         end
+      end
+    end
+
+    context '! 192.168.2.0/24' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '558 - test':
+            proto  => tcp,
+            port   => '558',
+            action => accept,
+            destination => '! 192.168.2.0/24',
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+         shell('iptables -S') do |r|
+           expect(r.stdout).to match(/-A INPUT ! -d 192.168.2.0\/24 -p tcp -m multiport --ports 558 -m comment --comment "558 - test" -j ACCEPT/)
+         end
+      end
+    end
+
+    # Invalid address
+    context '256.168.2.0/24' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '558 - test':
+            proto  => tcp,
+            port   => '558',
+            action => accept,
+            destination => '256.168.2.0/24',
+          }
+        EOS
+
+        apply_manifest(pp, :expect_failures => true) do |r|
+          expect(r.stderr).to match(/host_to_ip failed for 256.168.2.0\/24/)
+        end
+      end
+
+      it 'should not contain the rule' do
+         shell('iptables -S') do |r|
+           expect(r.stdout).to_not match(/-A INPUT -d 256.168.2.0\/24 -p tcp -m multiport --ports 558 -m comment --comment "558 - test" -j ACCEPT/)
+         end
+      end
+    end
+  end
+
+  describe 'dst_range' do
+    context '192.168.1.1-192.168.1.10' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '559 - test':
+            proto  => tcp,
+            port   => '559',
+            action => accept,
+            dst_range => '192.168.1.1-192.168.1.10',
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+         shell('iptables -S') do |r|
+           expect(r.stdout).to match(/-A INPUT -p tcp -m iprange --dst-range 192.168.1.1-192.168.1.10 -m multiport --ports 559 -m comment --comment "559 - test" -j ACCEPT/)
+         end
+      end
+    end
+
+    # Invalid IP
+    context '392.168.1.1-192.168.1.10' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '559 - test':
+            proto  => tcp,
+            port   => '559',
+            action => accept,
+            dst_range => '392.168.1.1-192.168.1.10',
+          }
+        EOS
+
+        apply_manifest(pp, :expect_failures => true) do |r|
+          expect(r.stderr).to match(/Invalid value "392.168.1.1-192.168.1.10"/)
+        end
+      end
+
+      it 'should not contain the rule' do
+         shell('iptables -S') do |r|
+           expect(r.stdout).to_not match(/-A INPUT -p tcp -m iprange --dst-range 392.168.1.1-192.168.1.10 -m multiport --ports 559 -m comment --comment "559 - test" -j ACCEPT/)
+         end
+      end
+    end
+  end
+
+  describe 'sport' do
+    context 'single port' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '560 - test':
+            proto  => tcp,
+            sport  => '560',
+            action => accept,
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+         shell('iptables -S') do |r|
+           expect(r.stdout).to match(/-A INPUT -p tcp -m multiport --sports 560 -m comment --comment "560 - test" -j ACCEPT/)
+         end
+      end
+    end
+
+    context 'multiple ports' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '560 - test':
+            proto  => tcp,
+            sport  => '560-561',
+            action => accept,
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+         shell('iptables -S') do |r|
+           expect(r.stdout).to match(/-A INPUT -p tcp -m multiport --sports 560:561 -m comment --comment "560 - test" -j ACCEPT/)
+         end
+      end
+    end
+
+    context 'invalid ports' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '560 - test':
+            proto  => tcp,
+            sport  => '9999560-561',
+            action => accept,
+          }
+        EOS
+
+        apply_manifest(pp, :expect_failures => true) do |r|
+          expect(r.stderr).to match(/invalid port\/service `9999560' specified/)
+        end
+      end
+
+      it 'should contain the rule' do
+         shell('iptables -S') do |r|
+           expect(r.stdout).to_not match(/-A INPUT -p tcp -m multiport --sports 9999560-561 -m comment --comment "560 - test" -j ACCEPT/)
+         end
+      end
+    end
+  end
+
+  describe 'dport' do
+    context 'single port' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '561 - test':
+            proto  => tcp,
+            dport  => '561',
+            action => accept,
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+         shell('iptables -S') do |r|
+           expect(r.stdout).to match(/-A INPUT -p tcp -m multiport --dports 561 -m comment --comment "561 - test" -j ACCEPT/)
+         end
+      end
+    end
+
+    context 'multiple ports' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '561 - test':
+            proto  => tcp,
+            dport  => '561-562',
+            action => accept,
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+         shell('iptables -S') do |r|
+           expect(r.stdout).to match(/-A INPUT -p tcp -m multiport --dports 561:562 -m comment --comment "561 - test" -j ACCEPT/)
+         end
+      end
+    end
+
+    context 'invalid ports' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '561 - test':
+            proto  => tcp,
+            dport  => '9999561-562',
+            action => accept,
+          }
+        EOS
+
+        apply_manifest(pp, :expect_failures => true) do |r|
+          expect(r.stderr).to match(/invalid port\/service `9999561' specified/)
+        end
+      end
+
+      it 'should contain the rule' do
+         shell('iptables -S') do |r|
+           expect(r.stdout).to_not match(/-A INPUT -p tcp -m multiport --dports 9999561-562 -m comment --comment "560 - test" -j ACCEPT/)
+         end
+      end
+    end
+  end
+
+  describe 'port' do
+    context 'single port' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '562 - test':
+            proto  => tcp,
+            port  => '562',
+            action => accept,
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+         shell('iptables -S') do |r|
+           expect(r.stdout).to match(/-A INPUT -p tcp -m multiport --ports 562 -m comment --comment "562 - test" -j ACCEPT/)
+         end
+      end
+    end
+
+    context 'multiple ports' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '562 - test':
+            proto  => tcp,
+            port  => '562-563',
+            action => accept,
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+         shell('iptables -S') do |r|
+           expect(r.stdout).to match(/-A INPUT -p tcp -m multiport --ports 562:563 -m comment --comment "562 - test" -j ACCEPT/)
+         end
+      end
+    end
+
+    context 'invalid ports' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '562 - test':
+            proto  => tcp,
+            port  => '9999562-563',
+            action => accept,
+          }
+        EOS
+
+        apply_manifest(pp, :expect_failures => true) do |r|
+          expect(r.stderr).to match(/invalid port\/service `9999562' specified/)
+        end
+      end
+
+      it 'should contain the rule' do
+         shell('iptables -S') do |r|
+           expect(r.stdout).to_not match(/-A INPUT -p tcp -m multiport --ports 9999562-563 -m comment --comment "562 - test" -j ACCEPT/)
+         end
+      end
+    end
+  end
+
+  ['dst_type', 'src_type'].each do |type|
+    describe "#{type}" do
+      context 'MULTICAST' do
+        it 'applies' do
+          pp = <<-EOS
+            class { '::firewall': }
+            firewall { '563 - test':
+              proto  => tcp,
+              action => accept,
+              #{type} => 'MULTICAST',
+            }
+          EOS
+
+          apply_manifest(pp, :catch_failures => true)
+        end
+
+        it 'should contain the rule' do
+          shell('iptables -S') do |r|
+            expect(r.stdout).to match(/-A INPUT -p tcp -m addrtype\s.*\sMULTICAST -m comment --comment "563 - test" -j ACCEPT/)
+          end
+        end
+      end
+
+      context 'BROKEN' do
+        it 'fails' do
+          pp = <<-EOS
+            class { '::firewall': }
+            firewall { '563 - test':
+              proto  => tcp,
+              action => accept,
+              #{type} => 'BROKEN',
+            }
+          EOS
+
+          apply_manifest(pp, :expect_failures => true) do |r|
+            expect(r.stderr).to match(/Invalid value "BROKEN"./)
+          end
+        end
+
+        it 'should not contain the rule' do
+          shell('iptables -S') do |r|
+            expect(r.stdout).to_not match(/-A INPUT -p tcp -m addrtype\s.*\sBROKEN -m comment --comment "563 - test" -j ACCEPT/)
+          end
+        end
+      end
+    end
+  end
+
+  describe 'tcp_flags' do
+    context 'FIN,SYN ACK' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '564 - test':
+            proto  => tcp,
+            action => accept,
+            tcp_flags => 'FIN,SYN ACK',
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+        shell('iptables -S') do |r|
+          expect(r.stdout).to match(/-A INPUT -p tcp -m tcp --tcp-flags FIN,SYN ACK -m comment --comment "564 - test" -j ACCEPT/)
+        end
+      end
+    end
+  end
+
+  describe 'chain' do
+    context 'INPUT' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '565 - test':
+            proto  => tcp,
+            action => accept,
+            chain  => 'FORWARD',
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+        shell('iptables -S') do |r|
+          expect(r.stdout).to match(/-A FORWARD -p tcp -m comment --comment "565 - test" -j ACCEPT/)
+        end
+      end
+    end
+  end
+
+  describe 'table' do
+    context 'mangle' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '566 - test':
+            proto  => tcp,
+            action => accept,
+            table  => 'mangle',
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+        shell('iptables -t mangle -S') do |r|
+          expect(r.stdout).to match(/-A INPUT -p tcp -m comment --comment "566 - test" -j ACCEPT/)
+        end
+      end
+    end
+    context 'NAT' do
+      it 'fails' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '566 - test':
+            proto  => tcp,
+            action => accept,
+            table  => 'MANGLE',
+          }
+        EOS
+
+        apply_manifest(pp, :expect_failures => true) do |r|
+          expect(r.stderr).to match(/Invalid value "MANGLE"/)
+        end
+      end
+
+      it 'should not contain the rule' do
+        shell('iptables -t MANGLE -S', :acceptable_exit_codes => [3]) do |r|
+          expect(r.stdout).to_not match(/-A INPUT -p tcp -m comment --comment "566 - test" -j ACCEPT/)
+        end
+      end
+    end
+  end
+
+  describe 'jump' do
+    context 'MARK' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewallchain { 'TEST:filter:IPv4':
+            ensure => present,
+          }
+          firewall { '567 - test':
+            proto  => tcp,
+            chain  => 'INPUT',
+            jump  => 'TEST',
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+        shell('iptables -S') do |r|
+          expect(r.stdout).to match(/-A INPUT -p tcp -m comment --comment "567 - test" -j TEST/)
+        end
+      end
+    end
+
+    context 'jump and apply' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewallchain { 'TEST:filter:IPv4':
+            ensure => present,
+          }
+          firewall { '568 - test':
+            proto  => tcp,
+            chain  => 'INPUT',
+            action => 'accept',
+            jump  => 'TEST',
+          }
+        EOS
+
+        apply_manifest(pp, :expect_failures => true) do |r|
+          expect(r.stderr).to match(/Only one of the parameters 'action' and 'jump' can be set/)
+        end
+      end
+
+      it 'should not contain the rule' do
+        shell('iptables -S') do |r|
+          expect(r.stdout).to_not match(/-A INPUT -p tcp -m comment --comment "568 - test" -j TEST/)
+        end
+      end
+    end
+  end
+
+  describe 'tosource' do
+    context '192.168.1.1' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '568 - test':
+            proto  => tcp,
+            table  => 'nat',
+            chain  => 'POSTROUTING',
+            jump  => 'SNAT',
+            tosource => '192.168.1.1',
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+        shell('iptables -t nat -S') do |r|
+          expect(r.stdout).to match(/A POSTROUTING -p tcp -m comment --comment "568 - test" -j SNAT --to-source 192.168.1.1/)
+        end
+      end
+    end
+  end
+
+  describe 'todest' do
+    context '192.168.1.1' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '569 - test':
+            proto  => tcp,
+            table  => 'nat',
+            chain  => 'PREROUTING',
+            jump   => 'DNAT',
+            source => '200.200.200.200',
+            todest => '192.168.1.1',
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+        shell('iptables -t nat -S') do |r|
+          expect(r.stdout).to match(/-A PREROUTING -s 200.200.200.200\/32 -p tcp -m comment --comment "569 - test" -j DNAT --to-destination 192.168.1.1/)
+        end
+      end
+    end
+  end
+
+  describe 'toports' do
+    context '192.168.1.1' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '570 - test':
+            proto  => icmp,
+            table  => 'nat',
+            chain  => 'PREROUTING',
+            jump  => 'REDIRECT',
+            toports => '2222',
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+        shell('iptables -t nat -S') do |r|
+          expect(r.stdout).to match(/-A PREROUTING -p icmp -m comment --comment "570 - test" -j REDIRECT --to-ports 2222/)
+        end
+      end
+    end
+  end
+
+  describe 'icmp' do
+    context 'any' do
+      it 'fails' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '571 - test':
+            proto  => icmp,
+            icmp   => 'any',
+          }
+        EOS
+
+        apply_manifest(pp, :expect_failures => true) do |r|
+          expect(r.stderr).to match(/This behaviour should be achieved by omitting or undefining the ICMP parameter/)
+        end
+      end
+
+      it 'should not contain the rule' do
+        shell('iptables -S') do |r|
+          expect(r.stdout).to_not match(/-A INPUT -p icmp -m comment --comment "570 - test" -m icmp --icmp-type 11/)
+        end
+      end
+    end
+  end
+
+  describe 'hop_limit' do
+    context '5' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '571 - test':
+            ensure => present,
+            proto => tcp,
+            port   => '571',
+            action => accept,
+            hop_limit => '5',
+            provider => 'ip6tables',
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+        shell('ip6tables -S') do |r|
+          expect(r.stdout).to match(/-A INPUT -p tcp -m multiport --ports 571 -m comment --comment "571 - test" -m hl --hl-eq 5 -j ACCEPT/)
+        end
+      end
+    end
+
+    context 'invalid' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '571 - test':
+            ensure => present,
+            proto => tcp,
+            port   => '571',
+            action => accept,
+            hop_limit => 'invalid',
+            provider => 'ip6tables',
+          }
+        EOS
+
+        apply_manifest(pp, :expect_failures => true) do |r|
+          expect(r.stderr).to match(/Invalid value "invalid"./)
+        end
+      end
+
+      it 'should not contain the rule' do
+        shell('ip6tables -S') do |r|
+          expect(r.stdout).to_not match(/-A INPUT -p tcp -m multiport --ports 571 -m comment --comment "571 - test" -m hl --hl-eq invalid -j ACCEPT/)
+        end
+      end
+    end
+  end
+
+  describe 'limit' do
+    context '500/sec' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '572 - test':
+            ensure => present,
+            proto => tcp,
+            port   => '572',
+            action => accept,
+            limit => '500/sec',
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+        shell('iptables -S') do |r|
+          expect(r.stdout).to match(/-A INPUT -p tcp -m multiport --ports 572 -m comment --comment "572 - test" -m limit --limit 500\/sec -j ACCEPT/)
+        end
+      end
+    end
+  end
+
+  describe 'burst' do
+    context '500' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '573 - test':
+            ensure => present,
+            proto => tcp,
+            port   => '573',
+            action => accept,
+            limit => '500/sec',
+            burst => '1500',
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+        shell('iptables -S') do |r|
+          expect(r.stdout).to match(/-A INPUT -p tcp -m multiport --ports 573 -m comment --comment "573 - test" -m limit --limit 500\/sec --limit-burst 1500 -j ACCEPT/)
+        end
+      end
+    end
+
+    context 'invalid' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '571 - test':
+            ensure => present,
+            proto => tcp,
+            port   => '571',
+            action => accept,
+            limit => '500/sec',
+            burst => '1500/sec',
+          }
+        EOS
+
+        apply_manifest(pp, :expect_failures => true) do |r|
+          expect(r.stderr).to match(/Invalid value "1500\/sec"./)
+        end
+      end
+
+      it 'should not contain the rule' do
+        shell('iptables -S') do |r|
+          expect(r.stdout).to_not match(/-A INPUT -p tcp -m multiport --ports 573 -m comment --comment "573 - test" -m limit --limit 500\/sec --limit-burst 1500\/sec -j ACCEPT/)
+        end
+      end
+    end
+  end
+
+  describe 'uid' do
+    context 'vagrant' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '574 - test':
+            ensure => present,
+            proto => tcp,
+            chain => 'OUTPUT',
+            port   => '574',
+            action => accept,
+            uid => 'vagrant',
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+        shell('iptables -S') do |r|
+          expect(r.stdout).to match(/-A OUTPUT -p tcp -m owner --uid-owner vagrant -m multiport --ports 574 -m comment --comment "574 - test" -j ACCEPT/)
+        end
+      end
+    end
+  end
+
+  describe 'gid' do
+    context 'vagrant' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '575 - test':
+            ensure => present,
+            proto => tcp,
+            chain => 'OUTPUT',
+            port   => '575',
+            action => accept,
+            gid => 'vagrant',
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+        shell('iptables -S') do |r|
+          expect(r.stdout).to match(/-A OUTPUT -p tcp -m owner --gid-owner vagrant -m multiport --ports 575 -m comment --comment "575 - test" -j ACCEPT/)
+        end
+      end
+    end
+  end
+
+  describe 'set_mark' do
+    context '0x3e8/0xffffffff' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '580 - test':
+            ensure => present,
+            chain => 'OUTPUT',
+            proto => tcp,
+            port   => '580',
+            jump => 'MARK',
+            table => 'mangle',
+            set_mark => '0x3e8/0xffffffff',
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+        shell('iptables -t mangle -S') do |r|
+          expect(r.stdout).to match(/-A OUTPUT -p tcp -m multiport --ports 580 -m comment --comment "580 - test" -j MARK --set-xmark 0x3e8\/0xffffffff/)
+        end
+      end
+    end
+  end
+
+  describe 'pkttype' do
+    context 'multicast' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '581 - test':
+            ensure => present,
+            proto => tcp,
+            port   => '581',
+            action => accept,
+            pkttype => 'multicast',
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+        shell('iptables -S') do |r|
+          expect(r.stdout).to match(/-A INPUT -p tcp -m multiport --ports 581 -m pkttype --pkt-type multicast -m comment --comment "581 - test" -j ACCEPT/)
+        end
+      end
+    end
+
+    context 'test' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '582 - test':
+            ensure => present,
+            proto => tcp,
+            port   => '582',
+            action => accept,
+            pkttype => 'test',
+          }
+        EOS
+
+        apply_manifest(pp, :expect_failures => true) do |r|
+          expect(r.stderr).to match(/Invalid value "test"./)
+        end
+      end
+
+      it 'should not contain the rule' do
+        shell('iptables -S') do |r|
+          expect(r.stdout).to_not match(/-A INPUT -p tcp -m multiport --ports 582 -m pkttype --pkt-type multicast -m comment --comment "582 - test" -j ACCEPT/)
+        end
+      end
+    end
+  end
+
+  describe 'isfragment' do
+    context 'true' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '583 - test':
+            ensure => present,
+            proto => tcp,
+            port   => '583',
+            action => accept,
+            isfragment => true,
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+        shell('iptables -S') do |r|
+          expect(r.stdout).to match(/-A INPUT -p tcp -f -m multiport --ports 583 -m comment --comment "583 - test" -j ACCEPT/)
+        end
+      end
+    end
+
+    context 'false' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '584 - test':
+            ensure => present,
+            proto => tcp,
+            port   => '584',
+            action => accept,
+            isfragment => false,
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+        shell('iptables -S') do |r|
+          expect(r.stdout).to match(/-A INPUT -p tcp -m multiport --ports 584 -m comment --comment "584 - test" -j ACCEPT/)
+        end
+      end
+    end
+  end
+
+  describe 'socket' do
+    context 'true' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '585 - test':
+            ensure => present,
+            proto => tcp,
+            port   => '585',
+            action => accept,
+            chain  => 'PREROUTING',
+            table  => 'nat',
+            socket => true,
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+        shell('iptables -t nat -S') do |r|
+          expect(r.stdout).to match(/-A PREROUTING -p tcp -m multiport --ports 585 -m socket -m comment --comment "585 - test" -j ACCEPT/)
+        end
+      end
+    end
+
+    context 'false' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '586 - test':
+            ensure => present,
+            proto => tcp,
+            port   => '586',
+            action => accept,
+            chain  => 'PREROUTING',
+            table  => 'nat',
+            socket => false,
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+        shell('iptables -t nat -S') do |r|
+          expect(r.stdout).to match(/-A PREROUTING -p tcp -m multiport --ports 586 -m comment --comment "586 - test" -j ACCEPT/)
+        end
+      end
+    end
+  end
+
+  describe 'ishasmorefrags' do
+    context 'true' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '587 - test':
+            ensure => present,
+            proto => tcp,
+            port   => '587',
+            action => accept,
+            ishasmorefrags => true,
+            provider => 'ip6tables',
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+        shell('ip6tables -S') do |r|
+          expect(r.stdout).to match(/A INPUT -p tcp -m frag --fragid 0 --fragmore -m multiport --ports 587 -m comment --comment "587 - test" -j ACCEPT/)
+        end
+      end
+    end
+
+    context 'false' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '588 - test':
+            ensure => present,
+            proto => tcp,
+            port   => '588',
+            action => accept,
+            ishasmorefrags => false,
+            provider => 'ip6tables',
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+        shell('ip6tables -S') do |r|
+          expect(r.stdout).to match(/-A INPUT -p tcp -m multiport --ports 588 -m comment --comment "588 - test" -j ACCEPT/)
+        end
+      end
+    end
+  end
+
+  describe 'islastfrag' do
+    context 'true' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '589 - test':
+            ensure => present,
+            proto => tcp,
+            port   => '589',
+            action => accept,
+            islastfrag => true,
+            provider => 'ip6tables',
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+        shell('ip6tables -S') do |r|
+          expect(r.stdout).to match(/-A INPUT -p tcp -m frag --fragid 0 --fraglast -m multiport --ports 589 -m comment --comment "589 - test" -j ACCEPT/)
+        end
+      end
+    end
+
+    context 'false' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '590 - test':
+            ensure => present,
+            proto => tcp,
+            port   => '590',
+            action => accept,
+            islastfrag => false,
+            provider => 'ip6tables',
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+        shell('ip6tables -S') do |r|
+          expect(r.stdout).to match(/-A INPUT -p tcp -m multiport --ports 590 -m comment --comment "590 - test" -j ACCEPT/)
+        end
+      end
+    end
+  end
+
+  describe 'isfirstfrag' do
+    context 'true' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '591 - test':
+            ensure => present,
+            proto => tcp,
+            port   => '591',
+            action => accept,
+            isfirstfrag => true,
+            provider => 'ip6tables',
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+        shell('ip6tables -S') do |r|
+          expect(r.stdout).to match(/-A INPUT -p tcp -m frag --fragid 0 --fragfirst -m multiport --ports 591 -m comment --comment "591 - test" -j ACCEPT/)
+        end
+      end
+    end
+
+    context 'false' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '592 - test':
+            ensure => present,
+            proto => tcp,
+            port   => '592',
+            action => accept,
+            isfirstfrag => false,
+            provider => 'ip6tables',
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+        shell('ip6tables -S') do |r|
+          expect(r.stdout).to match(/-A INPUT -p tcp -m multiport --ports 592 -m comment --comment "592 - test" -j ACCEPT/)
+        end
+      end
+    end
+  end
+
+  describe 'ipsec_policy' do
+    context 'ipsec' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '593 - test':
+            ensure       => 'present',
+            action       => 'reject',
+            chain        => 'OUTPUT',
+            destination  => '20.0.0.0/8',
+            ipsec_dir    => 'out',
+            ipsec_policy => 'ipsec',
+            proto        => 'all',
+            reject       => 'icmp-net-unreachable',
+            table        => 'filter',
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+        shell('iptables -S') do |r|
+          expect(r.stdout).to match(/-A OUTPUT -d 20.0.0.0\/8 -m comment --comment "593 - test" -m policy --dir out --pol ipsec -j REJECT --reject-with icmp-net-unreachable/)
+        end
+      end
+    end
+
+    context 'none' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '594 - test':
+            ensure       => 'present',
+            action       => 'reject',
+            chain        => 'OUTPUT',
+            destination  => '20.0.0.0/8',
+            ipsec_dir    => 'out',
+            ipsec_policy => 'none',
+            proto        => 'all',
+            reject       => 'icmp-net-unreachable',
+            table        => 'filter',
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+        shell('iptables -S') do |r|
+          expect(r.stdout).to match(/-A OUTPUT -d 20.0.0.0\/8 -m comment --comment "594 - test" -m policy --dir out --pol none -j REJECT --reject-with icmp-net-unreachable/)
+        end
+      end
+    end
+  end
+
+  describe 'ipsec_dir' do
+    context 'out' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '595 - test':
+            ensure       => 'present',
+            action       => 'reject',
+            chain        => 'OUTPUT',
+            destination  => '20.0.0.0/8',
+            ipsec_dir    => 'out',
+            ipsec_policy => 'ipsec',
+            proto        => 'all',
+            reject       => 'icmp-net-unreachable',
+            table        => 'filter',
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+        shell('iptables -S') do |r|
+          expect(r.stdout).to match(/-A OUTPUT -d 20.0.0.0\/8 -m comment --comment "595 - test" -m policy --dir out --pol ipsec -j REJECT --reject-with icmp-net-unreachable/)
+        end
+      end
+    end
+
+    context 'in' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '596 - test':
+            ensure       => 'present',
+            action       => 'reject',
+            chain        => 'INPUT',
+            destination  => '20.0.0.0/8',
+            ipsec_dir    => 'in',
+            ipsec_policy => 'none',
+            proto        => 'all',
+            reject       => 'icmp-net-unreachable',
+            table        => 'filter',
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+      end
+
+      it 'should contain the rule' do
+        shell('iptables -S') do |r|
+          expect(r.stdout).to match(/-A INPUT -d 20.0.0.0\/8 -m comment --comment "596 - test" -m policy --dir in --pol none -j REJECT --reject-with icmp-net-unreachable/)
+        end
+      end
+    end
+  end
+
+  describe 'reset' do
+    it 'deletes all rules' do
+      shell('ip6tables --flush')
+      shell('iptables --flush; iptables -t nat --flush; iptables -t mangle --flush')
+    end
+  end
+
+end
diff --git a/spec/acceptance/firewallchain_spec.rb b/spec/acceptance/firewallchain_spec.rb
new file mode 100644 (file)
index 0000000..679c0bf
--- /dev/null
@@ -0,0 +1,74 @@
+require 'spec_helper_acceptance'
+
+describe 'puppet resource firewallchain command:' do
+  before :all do
+    iptables_flush_all_tables
+  end
+  context 'creating firewall chains:' do
+    it 'applies cleanly' do
+      pp = <<-EOS
+        firewallchain { 'MY_CHAIN:filter:IPv4':
+          ensure  => present,
+        }
+      EOS
+      # Run it twice and test for idempotency
+      apply_manifest(pp, :catch_failures => true)
+      expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero
+    end
+  end
+
+  context 'adding a firewall rule to a chain:' do
+    it 'applies cleanly' do
+      pp = <<-EOS
+        firewall { '100 my rule':
+          chain   => 'MY_CHAIN',
+          action  => 'accept',
+          proto   => 'tcp',
+          dport   => 5000,
+        }
+      EOS
+      # Run it twice and test for idempotency
+      apply_manifest(pp, :catch_failures => true)
+      expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero
+    end
+  end
+
+  context 'not purge firewallchain chains:' do
+    it 'does not purge the rule' do
+      pp = <<-EOS
+        firewallchain { 'MY_CHAIN:filter:IPv4':
+          ensure  => present,
+          purge   => false,
+          before  => Resources['firewall'],
+        }
+        resources { 'firewall':
+          purge => true,
+        }
+      EOS
+      # Run it twice and test for idempotency
+      apply_manifest(pp, :catch_failures => true) do |r|
+        expect(r.stdout).to_not match(/removed/)
+        expect(r.stderr).to eq('')
+        expect(r.exit_code).to eq(0)
+      end
+      expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero
+    end
+
+    it 'still has the rule' do
+      pp = <<-EOS
+        firewall { '100 my rule':
+          chain   => 'MY_CHAIN',
+          action  => 'accept',
+          proto   => 'tcp',
+          dport   => 5000,
+        }
+      EOS
+      # Run it twice and test for idempotency
+      apply_manifest(pp, :catch_failures => true) do |r|
+        expect(r.stderr).to eq('')
+        expect(r.exit_code).to eq(0)
+      end
+      expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero
+    end
+  end
+end
diff --git a/spec/acceptance/rules_spec.rb b/spec/acceptance/rules_spec.rb
new file mode 100644 (file)
index 0000000..eac0d12
--- /dev/null
@@ -0,0 +1,199 @@
+require 'spec_helper_acceptance'
+
+describe 'complex ruleset 1' do
+  it 'applies cleanly' do
+    pp = <<-EOS
+      firewall { '090 forward allow local':
+        chain       => 'FORWARD',
+        proto       => 'all',
+        source      => '10.0.0.0/8',
+        destination => '10.0.0.0/8',
+        action      => 'accept',
+      }
+      firewall { '100 forward standard allow tcp':
+        chain       => 'FORWARD',
+        source      => '10.0.0.0/8',
+        destination => '!10.0.0.0/8',
+        proto       => 'tcp',
+        state       => 'NEW',
+        port        => [80,443,21,20,22,53,123,43,873,25,465],
+        action      => 'accept',
+      }
+      firewall { '100 forward standard allow udp':
+        chain       => 'FORWARD',
+        source      => '10.0.0.0/8',
+        destination => '!10.0.0.0/8',
+        proto       => 'udp',
+        port        => [53,123],
+        action      => 'accept',
+      }
+      firewall { '100 forward standard allow icmp':
+        chain       => 'FORWARD',
+        source      => '10.0.0.0/8',
+        destination => '!10.0.0.0/8',
+        proto       => 'icmp',
+        action      => 'accept',
+      }
+
+      firewall { '090 ignore ipsec':
+        table        => 'nat',
+        chain        => 'POSTROUTING',
+        outiface     => 'eth0',
+        ipsec_policy => 'ipsec',
+        ipsec_dir    => 'out',
+        action       => 'accept',
+      }
+      firewall { '093 ignore 10.0.0.0/8':
+        table       => 'nat',
+        chain       => 'POSTROUTING',
+        outiface    => 'eth0',
+        destination => '10.0.0.0/8',
+        action      => 'accept',
+      }
+      firewall { '093 ignore 172.16.0.0/12':
+        table       => 'nat',
+        chain       => 'POSTROUTING',
+        outiface    => 'eth0',
+        destination => '172.16.0.0/12',
+        action      => 'accept',
+      }
+      firewall { '093 ignore 192.168.0.0/16':
+        table       => 'nat',
+        chain       => 'POSTROUTING',
+        outiface    => 'eth0',
+        destination => '192.168.0.0/16',
+        action      => 'accept',
+      }
+      firewall { '100 masq outbound':
+        table    => 'nat',
+        chain    => 'POSTROUTING',
+        outiface => 'eth0',
+        jump     => 'MASQUERADE',
+      }
+      firewall { '101 redirect port 1':
+        table   => 'nat',
+        chain   => 'PREROUTING',
+        iniface => 'eth0',
+        proto   => 'tcp',
+        dport   => '1',
+        toports => '22',
+        jump    => 'REDIRECT',
+      }
+    EOS
+
+    # Run it twice and test for idempotency
+    apply_manifest(pp, :catch_failures => true)
+    expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero
+  end
+
+  it 'contains appropriate rules' do
+  end
+end
+
+describe 'complex ruleset 2' do
+  it 'applies cleanly' do
+    pp = <<-EOS
+      class { '::firewall': }
+      
+      Firewall {
+        proto => 'all',
+        stage => 'pre',
+      }
+      Firewallchain {
+        stage  => 'pre',
+        purge  => 'true',
+        ignore => [
+          '--comment "[^"]*(?i:ignore)[^"]*"',
+        ],
+      }
+
+      firewall { '010 INPUT allow established and related':
+        proto  => 'all',
+        state  => ['ESTABLISHED', 'RELATED'],
+        action => 'accept',
+        before => Firewallchain['INPUT:filter:IPv4'],
+      }
+      firewall { '012 accept loopback':
+        iniface => 'lo',
+        action  => 'accept',
+        before => Firewallchain['INPUT:filter:IPv4'],
+      }
+      firewall { '020 ssh':
+        proto  => 'tcp',
+        dport  => '22',
+        state  => 'NEW',
+        action => 'accept',
+        before => Firewallchain['INPUT:filter:IPv4'],
+      }
+
+      firewall { '013 icmp echo-request':
+        proto  => 'icmp',
+        icmp   => 'echo-request',
+        action => 'accept',
+        source => '10.0.0.0/8',
+      }
+      firewall { '013 icmp destination-unreachable':
+        proto  => 'icmp',
+        icmp   => 'destination-unreachable',
+        action => 'accept',
+      }
+      firewall { '013 icmp time-exceeded':
+        proto  => 'icmp',
+        icmp   => 'time-exceeded',
+        action => 'accept',
+      }
+      firewall { '999 reject':
+        action => 'reject',
+        reject => 'icmp-host-prohibited',
+      }
+
+
+      firewallchain { 'LOCAL_INPUT_PRE:filter:IPv4': }
+      firewall { '001 LOCAL_INPUT_PRE':
+        jump    => 'LOCAL_INPUT_PRE',
+        require => Firewallchain['LOCAL_INPUT_PRE:filter:IPv4'],
+      }
+      firewallchain { 'LOCAL_INPUT:filter:IPv4': }
+      firewall { '900 LOCAL_INPUT':
+        jump    => 'LOCAL_INPUT',
+        require => Firewallchain['LOCAL_INPUT:filter:IPv4'],
+      }
+      firewallchain { 'INPUT:filter:IPv4':
+        policy => 'drop',
+        ignore => [
+          '-j fail2ban-ssh',
+          '--comment "[^"]*(?i:ignore)[^"]*"',
+        ],
+      }
+
+
+      firewall { '010 allow established and related':
+        chain  => 'FORWARD',
+        proto  => 'all',
+        state  => ['ESTABLISHED','RELATED'],
+        action => 'accept',
+        before => Firewallchain['FORWARD:filter:IPv4'],
+      }
+      firewallchain { 'FORWARD:filter:IPv4':
+        policy => 'drop',
+      }
+
+      firewallchain { 'OUTPUT:filter:IPv4': }
+
+
+      # purge unknown rules from mangle table
+      firewallchain { ['PREROUTING:mangle:IPv4', 'INPUT:mangle:IPv4', 'FORWARD:mangle:IPv4', 'OUTPUT:mangle:IPv4', 'POSTROUTING:mangle:IPv4']: }
+
+      # and the nat table
+      firewallchain { ['PREROUTING:nat:IPv4', 'INPUT:nat:IPv4', 'OUTPUT:nat:IPv4', 'POSTROUTING:nat:IPv4']: }
+    EOS
+
+    # Run it twice and test for idempotency
+    apply_manifest(pp, :catch_failures => true)
+    expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero
+  end
+
+  it 'contains appropriate rules' do
+  end
+end
+