]> review.fuel-infra Code Review - puppet-modules/puppetlabs-firewall.git/commitdiff
(MODULES-439) Work around existing rules
authorHunter Haugen <hunter@puppetlabs.com>
Tue, 28 Jan 2014 01:31:22 +0000 (17:31 -0800)
committerHunter Haugen <hunter@puppetlabs.com>
Tue, 28 Jan 2014 18:36:44 +0000 (10:36 -0800)
The firewall resource is not intended to be used with rules that are not
also managed by puppet; the behavior when doing so was undefined. This
is an attempt to make it more defined.

The behavior is that any rule added by puppet will be inserted in its
given order in relation to the other rules managed by puppet, but ahead
of any rules not managed by puppet.

14 files changed:
Gemfile
lib/puppet/provider/firewall/iptables.rb
spec/acceptance/change_source_spec.rb [new file with mode: 0644]
spec/acceptance/nodesets/centos-59-x64.yml
spec/acceptance/nodesets/centos-64-x64-fusion.yml [new file with mode: 0644]
spec/acceptance/nodesets/centos-64-x64-pe.yml [new file with mode: 0644]
spec/acceptance/nodesets/centos-64-x64.yml
spec/acceptance/nodesets/debian-607-x64.yml [new file with mode: 0644]
spec/acceptance/nodesets/debian-70rc1-x64.yml [new file with mode: 0644]
spec/acceptance/nodesets/fedora-18-x64.yml [new file with mode: 0644]
spec/acceptance/nodesets/sles-11sp1-x64.yml [new file with mode: 0644]
spec/acceptance/nodesets/ubuntu-server-10044-x64.yml [new file with mode: 0644]
spec/acceptance/nodesets/ubuntu-server-12042-x64.yml
spec/unit/puppet/provider/iptables_spec.rb

diff --git a/Gemfile b/Gemfile
index 2909acebd59e633f81624ac93512588e2512bd45..690b772e31bf8c7a57a317f559ff57bac779b000 100644 (file)
--- a/Gemfile
+++ b/Gemfile
@@ -6,6 +6,7 @@ group :development, :test do
   gem 'serverspec',             :require => false
   gem 'beaker-rspec',           :require => false
   gem 'puppet-lint',            :require => false
+  gem 'pry',                    :require => false
 end
 
 if puppetversion = ENV['PUPPET_GEM_VERSION']
index 8c07a62df49a1bb92cb250a81aed4b38997d78c4..905ab54e95eedccd55733337b8f5c57816442ac5 100644 (file)
@@ -408,8 +408,39 @@ Puppet::Type.type(:firewall).provide :iptables, :parent => Puppet::Provider::Fir
     # No rules at all? Just bail now.
     return 1 if rules.empty?
 
+    # Add our rule to the end of the array of known rules
     my_rule = resource[:name].to_s
     rules << my_rule
-    rules.sort.index(my_rule) + 1
+
+    # Find if this is a new rule or an existing rule, then find how many
+    # unmanaged rules preceed it.
+    if rules.length == rules.uniq.length
+      # This is a new rule so find its ordered location.
+      new_rule_location = rules.sort.uniq.index(my_rule)
+      if new_rule_location == 0
+        # The rule will be the first rule in the chain because nothing came
+        # before it.
+        offset_rule = rules[0]
+      else
+        # This rule will come after other managed rules, so find the rule
+        # immediately preceeding it.
+        offset_rule = rules.sort.uniq[new_rule_location - 1]
+      end
+    else
+      # This is a pre-existing rule, so find the offset from the original
+      # ordering.
+      offset_rule = my_rule
+    end
+    # Count how many unmanaged rules are ahead of the target rule so we know
+    # how much to add to the insert order
+    unnamed_offset = rules[0..rules.index(offset_rule)].inject(0) do |sum,rule|
+      # This regex matches the names given to unmanaged rules (a number
+      # 9000-9999 followed by an MD5 hash).
+      sum + (rule.match(/^9[0-9]{3}\s[a-f0-9]{32}$/) ? 1 : 0)
+    end
+
+    # Insert our new or updated rule in the correct order of named rules, but
+    # offset for unnamed rules.
+    rules.sort.index(my_rule) + 1 + unnamed_offset
   end
 end
diff --git a/spec/acceptance/change_source_spec.rb b/spec/acceptance/change_source_spec.rb
new file mode 100644 (file)
index 0000000..02760b0
--- /dev/null
@@ -0,0 +1,77 @@
+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 'when unmanaged rules exist' do
+    it 'applies with 8.0.0.1 first' do
+      pp = <<-EOS
+          class { '::firewall': }
+          firewall { '101 test source changes':
+            proto  => tcp,
+            port   => '101',
+            action => accept,
+            source => '8.0.0.1',
+          }
+          firewall { '100 test source static':
+            proto  => tcp,
+            port   => '100',
+            action => accept,
+            source => '8.0.0.2',
+          }
+      EOS
+
+      apply_manifest(pp, :catch_failures => true)
+    end
+
+    it 'contains the changable 8.0.0.1 rule' do
+      shell('iptables -S') do |r|
+        expect(r.stdout).to match(/-A INPUT -s 8\.0\.0\.1\/32 -p tcp -m multiport --ports 101 -m comment --comment "101 test source changes" -j ACCEPT/)
+      end
+    end
+    it 'contains the static 8.0.0.2 rule' do
+      shell('iptables -S') do |r|
+        expect(r.stdout).to match(/-A INPUT -s 8\.0\.0\.2\/32 -p tcp -m multiport --ports 100 -m comment --comment "100 test source static" -j ACCEPT/)
+      end
+    end
+
+    it 'adds a unmanaged rule without a comment' do
+      shell('/sbin/iptables -R INPUT 1 -t filter -s 8.0.0.3/32 -p tcp -m multiport --ports 100 -j ACCEPT')
+      expect(shell('iptables -S').stdout).to match(/-A INPUT -s 8\.0\.0\.3\/32 -p tcp -m multiport --ports 100 -j ACCEPT/)
+    end
+
+    it 'changes to 8.0.0.4 second' do
+      pp = <<-EOS
+          class { '::firewall': }
+          firewall { '101 test source changes':
+            proto  => tcp,
+            port   => '101',
+            action => accept,
+            source => '8.0.0.4',
+          }
+      EOS
+
+      expect(apply_manifest(pp, :catch_failures => true).stdout).to match(/Notice: \/Stage\[main\]\/Main\/Firewall\[101 test source changes\]\/source: source changed '8\.0\.0\.1\/32' to '8\.0\.0\.4\/32'/)
+    end
+
+    it 'does not contain the old changing 8.0.0.1 rule' do
+      shell('iptables -S') do |r|
+        expect(r.stdout).to_not match(/8\.0\.0\.1/)
+      end
+    end
+    it 'contains the staic 8.0.0.2 rule' do
+      shell('iptables -S') do |r|
+        expect(r.stdout).to match(/-A INPUT -s 8\.0\.0\.2\/32 -p tcp -m multiport --ports 100 -m comment --comment "100 test source static" -j ACCEPT/)
+      end
+    end
+    it 'contains the changing new 8.0.0.4 rule' do
+      shell('iptables -S') do |r|
+        expect(r.stdout).to match(/-A INPUT -s 8\.0\.0\.4\/32 -p tcp -m multiport --ports 101 -m comment --comment "101 test source changes" -j ACCEPT/)
+      end
+    end
+  end
+end
index b0a4ba845e4e6ca27feabcd9a444fbdbf2e1873a..b41a947169aa25f587f44963f9f8e86bd6a30952 100644 (file)
@@ -2,7 +2,9 @@ HOSTS:
   centos-59-x64:
     roles:
       - master
-    platform: centos-59-x64
+    platform: el-5-x86_64
     box : centos-59-x64-vbox4210-nocm
     box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-59-x64-vbox4210-nocm.box
     hypervisor : vagrant
+CONFIG:
+  type: foss
diff --git a/spec/acceptance/nodesets/centos-64-x64-fusion.yml b/spec/acceptance/nodesets/centos-64-x64-fusion.yml
new file mode 100644 (file)
index 0000000..d516673
--- /dev/null
@@ -0,0 +1,10 @@
+HOSTS:
+  centos-64-x64:
+    roles:
+      - master
+    platform: el-6-x86_64
+    box : centos-64-x64-fusion503-nocm
+    box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-64-x64-fusion503-nocm.box
+    hypervisor : fusion
+CONFIG:
+  type: foss
diff --git a/spec/acceptance/nodesets/centos-64-x64-pe.yml b/spec/acceptance/nodesets/centos-64-x64-pe.yml
new file mode 100644 (file)
index 0000000..7d9242f
--- /dev/null
@@ -0,0 +1,12 @@
+HOSTS:
+  centos-64-x64:
+    roles:
+      - master
+      - database
+      - dashboard
+    platform: el-6-x86_64
+    box : centos-64-x64-vbox4210-nocm
+    box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-64-x64-vbox4210-nocm.box
+    hypervisor : vagrant
+CONFIG:
+  type: pe
index 8f57028b14ff012f20b36e0e8bb8fb9c7569a47f..05540ed8c5a90f83b10392ddaf54660f83a41372 100644 (file)
@@ -2,7 +2,9 @@ HOSTS:
   centos-64-x64:
     roles:
       - master
-    platform: el-6-i386
+    platform: el-6-x86_64
     box : centos-64-x64-vbox4210-nocm
     box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-64-x64-vbox4210-nocm.box
     hypervisor : vagrant
+CONFIG:
+  type: foss
diff --git a/spec/acceptance/nodesets/debian-607-x64.yml b/spec/acceptance/nodesets/debian-607-x64.yml
new file mode 100644 (file)
index 0000000..4c8be42
--- /dev/null
@@ -0,0 +1,10 @@
+HOSTS:
+  debian-607-x64:
+    roles:
+      - master
+    platform: debian-6-amd64
+    box : debian-607-x64-vbox4210-nocm
+    box_url : http://puppet-vagrant-boxes.puppetlabs.com/debian-607-x64-vbox4210-nocm.box
+    hypervisor : vagrant
+CONFIG:
+  type: git
diff --git a/spec/acceptance/nodesets/debian-70rc1-x64.yml b/spec/acceptance/nodesets/debian-70rc1-x64.yml
new file mode 100644 (file)
index 0000000..19181c1
--- /dev/null
@@ -0,0 +1,10 @@
+HOSTS:
+  debian-70rc1-x64:
+    roles:
+      - master
+    platform: debian-7-amd64
+    box : debian-70rc1-x64-vbox4210-nocm
+    box_url : http://puppet-vagrant-boxes.puppetlabs.com/debian-70rc1-x64-vbox4210-nocm.box
+    hypervisor : vagrant
+CONFIG:
+  type: git
diff --git a/spec/acceptance/nodesets/fedora-18-x64.yml b/spec/acceptance/nodesets/fedora-18-x64.yml
new file mode 100644 (file)
index 0000000..624b537
--- /dev/null
@@ -0,0 +1,10 @@
+HOSTS:
+  fedora-18-x64:
+    roles:
+      - master
+    platform: fedora-18-x86_64
+    box : fedora-18-x64-vbox4210-nocm
+    box_url : http://puppet-vagrant-boxes.puppetlabs.com/fedora-18-x64-vbox4210-nocm.box
+    hypervisor : vagrant
+CONFIG:
+  type: git
diff --git a/spec/acceptance/nodesets/sles-11sp1-x64.yml b/spec/acceptance/nodesets/sles-11sp1-x64.yml
new file mode 100644 (file)
index 0000000..554c37a
--- /dev/null
@@ -0,0 +1,10 @@
+HOSTS:
+  sles-11sp1-x64:
+    roles:
+      - master
+    platform: sles-11-x86_64
+    box : sles-11sp1-x64-vbox4210-nocm
+    box_url : http://puppet-vagrant-boxes.puppetlabs.com/sles-11sp1-x64-vbox4210-nocm.box
+    hypervisor : vagrant
+CONFIG:
+  type: git
diff --git a/spec/acceptance/nodesets/ubuntu-server-10044-x64.yml b/spec/acceptance/nodesets/ubuntu-server-10044-x64.yml
new file mode 100644 (file)
index 0000000..5047017
--- /dev/null
@@ -0,0 +1,10 @@
+HOSTS:
+  ubuntu-server-10044-x64:
+    roles:
+      - master
+    platform: ubuntu-10.04-amd64
+    box : ubuntu-server-10044-x64-vbox4210-nocm
+    box_url : http://puppet-vagrant-boxes.puppetlabs.com/ubuntu-server-10044-x64-vbox4210-nocm.box
+    hypervisor : vagrant
+CONFIG:
+  type: git
index 2b8fe4a121e5b4a3a74942ca248e58abea3c654b..d065b304f831a196852757c59b1bb14665990199 100644 (file)
@@ -2,7 +2,9 @@ HOSTS:
   ubuntu-server-12042-x64:
     roles:
       - master
-    platform: ubuntu-server-12.04-amd64
+    platform: ubuntu-12.04-amd64
     box : ubuntu-server-12042-x64-vbox4210-nocm
     box_url : http://puppet-vagrant-boxes.puppetlabs.com/ubuntu-server-12042-x64-vbox4210-nocm.box
     hypervisor : vagrant
+CONFIG:
+  type: foss
index d4dcea57cae33132ded9cd0d3b8ac1a37ff0b6e5..d34679f76466440900ad68e552b2f857e0c422fc 100644 (file)
@@ -79,6 +79,90 @@ describe 'iptables provider' do
     expect(provider.instances.length).to be_zero
   end
 
+  describe '#insert_order' do
+    let(:iptables_save_output) { [
+      '-A INPUT -s 8.0.0.2/32 -p tcp -m multiport --ports 100 -m comment --comment "100 test" -j ACCEPT',
+      '-A INPUT -s 8.0.0.3/32 -p tcp -m multiport --ports 200 -m comment --comment "200 test" -j ACCEPT',
+      '-A INPUT -s 8.0.0.4/32 -p tcp -m multiport --ports 300 -m comment --comment "300 test" -j ACCEPT'
+    ] }
+    let(:resources) do
+      iptables_save_output.each_with_index.collect { |l,index| provider.rule_to_hash(l, 'filter', index) }
+    end
+    let(:providers) do
+      resources.collect { |r| provider.new(r) }
+    end
+    it 'understands offsets for adding rules to the beginning' do
+      resource = Puppet::Type.type(:firewall).new({ :name => '001 test', })
+      allow(resource.provider.class).to receive(:instances).and_return(providers)
+      expect(resource.provider.insert_order).to eq(1) # 1-indexed
+    end
+    it 'understands offsets for editing rules at the beginning' do
+      resource = Puppet::Type.type(:firewall).new({ :name => '100 test', })
+      allow(resource.provider.class).to receive(:instances).and_return(providers)
+      expect(resource.provider.insert_order).to eq(1)
+    end
+    it 'understands offsets for adding rules to the middle' do
+      resource = Puppet::Type.type(:firewall).new({ :name => '101 test', })
+      allow(resource.provider.class).to receive(:instances).and_return(providers)
+      expect(resource.provider.insert_order).to eq(2)
+    end
+    it 'understands offsets for editing rules at the middle' do
+      resource = Puppet::Type.type(:firewall).new({ :name => '200 test', })
+      allow(resource.provider.class).to receive(:instances).and_return(providers)
+      expect(resource.provider.insert_order).to eq(2)
+    end
+    it 'understands offsets for adding rules to the end' do
+      resource = Puppet::Type.type(:firewall).new({ :name => '301 test', })
+      allow(resource.provider.class).to receive(:instances).and_return(providers)
+      expect(resource.provider.insert_order).to eq(4)
+    end
+    it 'understands offsets for editing rules at the end' do
+      resource = Puppet::Type.type(:firewall).new({ :name => '300 test', })
+      allow(resource.provider.class).to receive(:instances).and_return(providers)
+      expect(resource.provider.insert_order).to eq(3)
+    end
+
+    context 'with an unname rule' do
+      let(:iptables_save_output) { [
+        '-A INPUT -s 8.0.0.2/32 -p tcp -m multiport --ports 100 -m comment --comment "100 test" -j ACCEPT',
+        '-A INPUT -s 8.0.0.3/32 -p tcp -m multiport --ports 200 -j ACCEPT',
+        '-A INPUT -s 8.0.0.4/32 -p tcp -m multiport --ports 300 -m comment --comment "300 test" -j ACCEPT',
+        '-A INPUT -s 8.0.0.5/32 -p tcp -m multiport --ports 400 -j ACCEPT',
+        '-A INPUT -s 8.0.0.6/32 -p tcp -m multiport --ports 500 -m comment --comment "500 test" -j ACCEPT'
+      ] }
+      it 'understands offsets for adding rules before unnamed rules' do
+        resource = Puppet::Type.type(:firewall).new({ :name => '001 test', })
+        allow(resource.provider.class).to receive(:instances).and_return(providers)
+        expect(resource.provider.insert_order).to eq(1)
+      end
+      it 'understands offsets for editing rules before unnamed rules' do
+        resource = Puppet::Type.type(:firewall).new({ :name => '100 test', })
+        allow(resource.provider.class).to receive(:instances).and_return(providers)
+        expect(resource.provider.insert_order).to eq(1)
+      end
+      it 'understands offsets for adding rules between unnamed rules' do
+        resource = Puppet::Type.type(:firewall).new({ :name => '301 test', })
+        allow(resource.provider.class).to receive(:instances).and_return(providers)
+        expect(resource.provider.insert_order).to eq(4)
+      end
+      it 'understands offsets for editing rules between unnamed rules' do
+        resource = Puppet::Type.type(:firewall).new({ :name => '300 test', })
+        allow(resource.provider.class).to receive(:instances).and_return(providers)
+        expect(resource.provider.insert_order).to eq(3)
+      end
+      it 'understands offsets for adding rules after unnamed rules' do
+        resource = Puppet::Type.type(:firewall).new({ :name => '501 test', })
+        allow(resource.provider.class).to receive(:instances).and_return(providers)
+        expect(resource.provider.insert_order).to eq(6)
+      end
+      it 'understands offsets for editing rules after unnamed rules' do
+        resource = Puppet::Type.type(:firewall).new({ :name => '500 test', })
+        allow(resource.provider.class).to receive(:instances).and_return(providers)
+        expect(resource.provider.insert_order).to eq(5)
+      end
+    end
+  end
+
   # Load in ruby hash for test fixtures.
   load 'spec/fixtures/iptables/conversion_hash.rb'