Skip to content

Commit 45228a5

Browse files
authored
Merge pull request #1030 from puppetlabs/SEC-944-handle_duplicate_rule_comments_v2
(SEC-944) Handle duplicate system rules
2 parents dc4d38c + 30db99b commit 45228a5

File tree

5 files changed

+139
-1
lines changed

5 files changed

+139
-1
lines changed

README.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@
1414
4. [Usage - Configuration and customization options](#usage)
1515
* [Default rules - Setting up general configurations for all firewalls](#default-rules)
1616
* [Application-specific rules - Options for configuring and managing firewalls across applications](#application-specific-rules)
17-
* [Additional ses for the firewall module](#other-rules)
17+
* [Rule inversion](#rule-inversion)
18+
* [Additional uses for the firewall module](#additional-uses-for-the-firewal-module)
19+
* [Duplicate rule behaviour](#duplicate-rule-behaviour)
20+
* [Additional information](#additional-information)
1821
5. [Reference - An under-the-hood peek at what the module is doing](#reference)
1922
6. [Limitations - OS compatibility, etc.](#limitations)
2023
7. [Firewall_multi - Arrays for certain parameters](#firewall_multi)
@@ -387,6 +390,21 @@ firewall {'666 for NFLOG':
387390
}
388391
```
389392

393+
### Duplicate rule behaviour
394+
395+
It is possible for an unmanaged rule to exist on the target system that has the same comment as the rule specified in the manifest. This configuration is not supported by the firewall module.
396+
397+
In the event of a duplicate rule, the module will by default display a warning message notifying the user that it has found a duplicate but will continue to update the resource.
398+
399+
This behaviour is configurable via the `onduplicaterulebehaviour` parameter. Users can choose from the following behaviours:
400+
401+
* `ignore` - The duplicate rule is ignored and any updates to the resource will continue unaffected.
402+
* `warn` - The duplicate rule is logged as a warning and any updates to the resource will continue unaffected.
403+
* `error` - The duplicate rule is logged as an error and any updates to the resource will be skipped.
404+
405+
With either the `ignore` or `warn` (default) behaviour, Puppet may create another duplicate rule.
406+
To prevent this behavior and report the resource as failing during the Puppet run, specify the `error` behaviour.
407+
390408
### Additional information
391409

392410
Access the inline documentation:

lib/puppet/provider/firewall/iptables.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,9 +401,29 @@ def delete
401401
end
402402

403403
def exists?
404+
if duplicate_rule?(@property_hash[:name])
405+
core_message = "Duplicate rule found for #{@property_hash[:name]}."
406+
case @resource.parameters[:onduplicaterulebehaviour].value
407+
when :error
408+
raise "#{core_message} Skipping update."
409+
when :warn
410+
warning "#{core_message}. This may add an additional rule to the system."
411+
when :ignore
412+
debug "#{core_message}. This may add an additional rule to the system."
413+
end
414+
end
415+
404416
properties[:ensure] != :absent
405417
end
406418

419+
def duplicate_rule?(rule)
420+
rules = self.class.instances
421+
system_rule_count = Hash.new(0)
422+
rules.each { |r| system_rule_count[r.name] += 1 }
423+
duplicate_rules = rules.select { |r| system_rule_count[r.name] > 1 }
424+
duplicate_rules.select { |r| r.name == rule }.any?
425+
end
426+
407427
# Flush the property hash once done.
408428
def flush
409429
debug('[flush]')

lib/puppet/type/firewall.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,24 @@
234234
newvalues(%r{^\d+[[:graph:][:space:]]+$})
235235
end
236236

237+
newparam(:onduplicaterulebehaviour) do
238+
desc <<-PUPPETCODE
239+
In certain situations it is possible for an unmanaged rule to exist
240+
on the target system that has the same comment as the rule
241+
specified in the manifest.
242+
243+
This setting determines what happens when such a duplicate is found.
244+
245+
It offers three options:
246+
247+
* ignore - The duplicate rule is ignored and any updates to the resource will continue unaffected.
248+
* warn - The duplicate rule is logged as a warning and any updates to the resource will continue unaffected.
249+
* error - The duplicate rule is logged as an error and any updates to the resource will be skipped.
250+
PUPPETCODE
251+
newvalues(:ignore, :warn, :error)
252+
defaultto :warn
253+
end
254+
237255
newproperty(:action) do
238256
desc <<-PUPPETCODE
239257
This is the action to perform on a match. Can be one of:
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper_acceptance'
4+
5+
def make_manifest(behaviour)
6+
pp = <<-PUPPETCODE
7+
class { 'firewall': }
8+
resources { 'firewall':
9+
purge => true,
10+
}
11+
12+
firewall { '550 destination':
13+
proto => tcp,
14+
dport => '550',
15+
action => accept,
16+
destination => '192.168.2.0/24',
17+
onduplicaterulebehaviour => #{behaviour}
18+
}
19+
PUPPETCODE
20+
21+
pp
22+
end
23+
24+
describe 'firewall - duplicate comments' do
25+
before(:all) do
26+
if os[:family] == 'ubuntu' || os[:family] == 'debian'
27+
update_profile_file
28+
end
29+
end
30+
31+
before(:each) do
32+
run_shell('iptables -I INPUT -m state --state NEW -m tcp -p tcp --dport 551 -j ACCEPT -m comment --comment "550 destination"')
33+
end
34+
35+
after(:each) do
36+
iptables_flush_all_tables
37+
end
38+
39+
context 'when onduplicateerrorhevent is set to error' do
40+
it 'raises an error' do
41+
run_shell('iptables -I INPUT -m state --state NEW -m tcp -p tcp --dport 551 -j ACCEPT -m comment --comment "550 destination"')
42+
pp = make_manifest('error')
43+
44+
apply_manifest(pp) do |r|
45+
expect(r.stderr).to include('Error: /Stage[main]/Main/Firewall[550 destination]: Could not evaluate: Duplicate rule found for 550 destination. Skipping update.')
46+
end
47+
end
48+
end
49+
50+
context 'when onduplicateerrorhevent is set to warn' do
51+
run_shell('iptables -I INPUT -m state --state NEW -m tcp -p tcp --dport 551 -j ACCEPT -m comment --comment "550 destination"')
52+
53+
it 'warns and continues' do
54+
run_shell('iptables -I INPUT -m state --state NEW -m tcp -p tcp --dport 551 -j ACCEPT -m comment --comment "550 destination"')
55+
pp = make_manifest('warn')
56+
57+
apply_manifest(pp) do |r|
58+
expect(r.stderr).to include('Warning: Firewall[550 destination](provider=iptables): Duplicate rule found for 550 destination.. This may add an additional rule to the system.')
59+
end
60+
end
61+
end
62+
63+
context 'when onduplicateerrorhevent is set to ignore' do
64+
run_shell('iptables -I INPUT -m state --state NEW -m tcp -p tcp --dport 551 -j ACCEPT -m comment --comment "550 destination"')
65+
66+
it 'continues silently' do
67+
run_shell('iptables -I INPUT -m state --state NEW -m tcp -p tcp --dport 551 -j ACCEPT -m comment --comment "550 destination"')
68+
pp = make_manifest('ignore')
69+
70+
apply_manifest(pp) do |r|
71+
expect(r.stderr).to be_empty
72+
end
73+
end
74+
end
75+
end

spec/spec_helper_acceptance_local.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,5 +111,12 @@ def fetch_os_name
111111
}
112112
PUPPETCODE
113113
LitmusHelper.instance.apply_manifest(pp)
114+
115+
# Ensure that policycoreutils is present. In the future we could probably refactor
116+
# this so that policycoreutils is installed on platform where the os.family fact
117+
# is set to 'redhat'
118+
if ['almalinux-8', 'rocky-8'].include?("#{fetch_os_name}-#{os[:release].to_i}")
119+
LitmusHelper.instance.run_shell('yum install policycoreutils -y')
120+
end
114121
end
115122
end

0 commit comments

Comments
 (0)