Skip to content

Commit f952013

Browse files
committed
(CAT-376) Update treatment of array values
- Allow certain array values to be negated either by negating their first value, or all their values.
1 parent 2b94c3c commit f952013

File tree

4 files changed

+53
-19
lines changed

4 files changed

+53
-19
lines changed

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -272,10 +272,13 @@ class profile::apache {
272272

273273
### Rule inversion
274274

275-
Firewall rules may be inverted by prefixing the value of a parameter by "! ". If the value is an array, then the first value of the array must be prefixed in order to invert them all.
275+
Firewall rules may be inverted by prefixing the value of a parameter by "! ".
276276

277277
Parameters that understand inversion are: connmark, ctstate, destination, dport, dst\_range, dst\_type, iniface, outiface, port, proto, source, sport, src\_range and src\_type.
278278

279+
If the value is an array, then either the first value of the array, or all of its values must be prefixed in order to invert them all.
280+
For most array attributes it is not possible to invert only one passed value.
281+
279282
Examples:
280283

281284
```puppet
@@ -295,7 +298,7 @@ firewall { '002 drop NEW external website packets with FIN/RST/ACK set and SYN u
295298
}
296299
```
297300

298-
There are exceptions to this however, with attributes such as src\_type, dst\_type and ipset allowing the user to negate any passed values seperately.
301+
There are exceptions to this however, with attributes such as src\_type, dst\_type and ipset allowing the user to negate each passed values seperately.
299302

300303
Examples:
301304

lib/puppet/provider/firewall/firewall.rb

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -227,9 +227,8 @@ class Puppet::Provider::Firewall::Firewall
227227
cgroup: [:cgroup]
228228
}
229229

230-
# This is the order of resources as they appear in iptables-save output,
231-
# we need it to properly parse and apply rules, if the order of resource
232-
# changes between puppet runs, the changed rules will be re-applied again.
230+
# This is the order of resources as they appear in ip(6)tables-save output,
231+
# it is used in order to ensure that the rules are applied in the correct order.
233232
# This order can be determined by going through iptables source code or just tweaking and trying manually
234233
$resource_list = [
235234
:source, :destination, :iniface, :outiface,
@@ -257,7 +256,6 @@ class Puppet::Provider::Firewall::Firewall
257256

258257
###### PUBLIC METHODS ######
259258

260-
# Raw data is retrieved via `iptables -L` and then regexed to retrieve the different Chains and whether they have a set Policy
261259
def get(context)
262260
# Call the private method which returns the rules
263261
# The method is seperated out in this way as it is re-used later in the code
@@ -287,7 +285,6 @@ def set(context, changes)
287285
end
288286
elsif is[:ensure].to_s == 'present' && should[:ensure].to_s == 'absent'
289287
context.deleting(name) do
290-
# Delete command requires additional context
291288
delete(context, name, is)
292289
end
293290
elsif is[:ensure].to_s == 'present'
@@ -762,13 +759,15 @@ def self.validate_input(_is, should)
762759
# `stat_mode` must be set to `random` for `stat_probability` to be set
763760
raise ArgumentError, '`stat_mode` must be set to `random` for `stat_probability` to be set.' if should[:stat_probability] && should[:stat_mode] != 'random'
764761

765-
# Verify that if dport/sport/state/ctstate/ctstatus is passed as an array, that only the first value is negated
762+
# Verify that if dport/sport/state/ctstate/ctstatus is passed as an array, that any negation includes either the first value or al values
766763
[:dport, :sport, :state, :ctstate, :ctstatus].each do |key|
767764
next unless should[key].is_a?(Array)
768-
769-
should[key].each_with_index do |value, index|
770-
raise ArgumentError, "Only the first value in a `#{key}` array must be negated in order to negate the combined values." if index >= 1 && value.to_s.match(%r{^!})
765+
negated_values = 0
766+
should[key].each do |value|
767+
negated_values += 1 if value.to_s.match(%r{^!\s})
771768
end
769+
raise ArgumentError, "When negating a `#{key}` array, you must negate either the first given value only or all the given values." if (negated_values == 1 && !should[key][0].to_s.match(%r{^!\s})) ||
770+
(negated_values >= 2 && negated_values != should[key].length)
772771
end
773772
raise ArgumentError, 'Value `any` is not valid. This behaviour should be achieved by omitting or undefining the ICMP parameter.' if should[:icmp] && should[:icmp] == 'any'
774773
raise ArgumentError, '`burst` cannot be set without `limit`.' if should[:burst] && !(should[:limit])
@@ -860,6 +859,17 @@ def self.validate_input(_is, should)
860859
# Certain attributes need processed in ways that can vary between IPv4 and IPv6
861860
# @api.private
862861
def self.process_input(should)
862+
# `dport`, `sport` `state` `ctstate` and `ctstatus` arrays should only have the first value negated.
863+
[:dport, :sport, :state, :ctstate, :ctstatus].each do |key|
864+
next unless should[key].is_a?(Array)
865+
866+
negated = true if should[key][0].to_s.match(%r{^!\s})
867+
should[key].each_with_index do |value, index|
868+
should[key] = should[key].map { |value| value.to_s.tr('! ', '') }
869+
end
870+
should[key][0] = ['!', should[key][0]].join(' ') if negated
871+
end
872+
863873
# `jump` values should always be uppercase
864874
should[:jump] = should[:jump].upcase if should[:jump]
865875

lib/puppet/type/firewall.rb

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,9 @@
437437
438438
sport => ['! 54','23']
439439
440-
Note: this will negate all passed ports, it is not possible to negate a single one of the array.
440+
Note:
441+
This will negate all passed ports, it is not possible to negate a single one of the array.
442+
In order to maintain compatibility it is also possible to negate all values given in the array to achieve the same behaviour.
441443
DESC
442444
},
443445
dport: {
@@ -461,7 +463,9 @@
461463
462464
dport => ['! 54','23']
463465
464-
Note: this will negate all passed ports, it is not possible to negate a single one of the array.
466+
Note:
467+
This will negate all passed ports, it is not possible to negate a single one of the array.
468+
In order to maintain compatibility it is also possible to negate all values given in the array to achieve the same behaviour.
465469
DESC
466470
},
467471
src_type: {
@@ -584,7 +588,9 @@
584588
585589
state => ['! INVALID', 'ESTABLISHED']
586590
587-
Note: this will negate all passed states, it is not possible to negate a single one of the array.
591+
Note:
592+
This will negate all passed states, it is not possible to negate a single one of the array.
593+
In order to maintain compatibility it is also possible to negate all values given in the array to achieve the same behaviour.
588594
DESC
589595
},
590596
ctstate: {
@@ -612,7 +618,9 @@
612618
613619
ctstate => ['! INVALID', 'ESTABLISHED']
614620
615-
Note: this will negate all passed states, it is not possible to negate a single one of the array.
621+
Note:
622+
This will negate all passed states, it is not possible to negate a single one of the array.
623+
In order to maintain compatibility it is also possible to negate all values given in the array to achieve the same behaviour.
616624
DESC
617625
},
618626
ctproto: {
@@ -768,7 +776,9 @@
768776
769777
ctstatus => ['! EXPECTED', 'CONFIRMED']
770778
771-
Note: this will negate all passed states, it is not possible to negate a single one of the array.
779+
Note:
780+
This will negate all passed states, it is not possible to negate a single one of the array.
781+
In order to maintain compatibility it is also possible to negate all values given in the array to achieve the same behaviour.
772782
DESC
773783
},
774784
ctexpire: {

spec/unit/puppet/provider/firewall/firewall_private_set_spec.rb

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,15 @@
4242
},
4343
{
4444
# Covers `dport`, `sport`, `state`, `ctstate` and `ctstatus`
45-
valid: { is: {}, should: { ensure: 'present', name: '001 Test Rule', dport: ['! 54', '64'] } },
46-
invalid: { is: {}, should: { ensure: 'present', name: '001 Test Rule', dport: ['! 54', '! 64'] } },
47-
error: 'Only the first value in a `dport` array must be negated in order to negate the combined values.'
45+
valid: { is: {}, should: { ensure: 'present', name: '001 Test Rule', dport: ['! 54', '64', '74'] } },
46+
invalid: { is: {}, should: { ensure: 'present', name: '001 Test Rule', dport: ['54', '! 64', '74'] } },
47+
error: 'When negating a `dport` array, you must negate either the first given value only or all the given values.'
48+
},
49+
{
50+
# Covers `dport`, `sport`, `state`, `ctstate` and `ctstatus`
51+
valid: { is: {}, should: { ensure: 'present', name: '001 Test Rule', sport: ['! 54', '! 64', '! 74'] } },
52+
invalid: { is: {}, should: { ensure: 'present', name: '001 Test Rule', sport: ['! 54', '! 64', '74'] } },
53+
error: 'When negating a `sport` array, you must negate either the first given value only or all the given values.'
4854
},
4955
{
5056
valid: { is: {}, should: { ensure: 'present', name: '001 Test Rule' } },
@@ -250,6 +256,11 @@
250256

251257
describe 'self.process_input(should)' do
252258
[
259+
{
260+
process: '`dport`, `sport` `state` `ctstate` and `ctstatus` arrays should only have the first value negated',
261+
should: { dport: ['! 54', '! 64', '! 74'] },
262+
result: { dport: ['! 54', '64', '74'] }
263+
},
253264
{
254265
process: '`jump` values should always be uppercase',
255266
should: { jump: 'accept' },

0 commit comments

Comments
 (0)