From: Ashley Penney Date: Mon, 13 Jan 2014 21:20:50 +0000 (-0500) Subject: Add additional acceptance tests to cover all parameters. X-Git-Tag: 0.5.0~15^2~1 X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=f930685d9ce662a46695185cb5e22a282a475437;p=puppet-modules%2Fpuppetlabs-firewall.git Add additional acceptance tests to cover all parameters. --- diff --git a/spec/acceptance/firewall_spec.rb b/spec/acceptance/firewall_spec.rb new file mode 100644 index 0000000..8bc2da4 --- /dev/null +++ b/spec/acceptance/firewall_spec.rb @@ -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 index 0000000..679c0bf --- /dev/null +++ b/spec/acceptance/firewallchain_spec.rb @@ -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 index 0000000..eac0d12 --- /dev/null +++ b/spec/acceptance/rules_spec.rb @@ -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 +