Skip to content

Commit 9f4b8aa

Browse files
committed
(CAT-1260) Addition of tests for public methods of firewall provider
- Includes slight reorganization of providers - Updates `dport`/`sport` section of `insync?` to allow for more accurate comparisons.
1 parent 51834d6 commit 9f4b8aa

File tree

3 files changed

+390
-132
lines changed

3 files changed

+390
-132
lines changed

.rubocop_todo.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ Metrics/BlockLength:
3131
# Offense count: 3
3232
# Configuration parameters: CountComments, CountAsOne.
3333
Metrics/ClassLength:
34-
Max: 752
34+
Max: 763
3535

3636
# Offense count: 16
3737
# Configuration parameters: AllowedMethods, AllowedPatterns.

lib/puppet/provider/firewall/firewall.rb

Lines changed: 152 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
# Implementation for the iptables type using the Resource API.
66
class Puppet::Provider::Firewall::Firewall
7+
###### GLOBAL VARIABLES ######
8+
79
# Command to list all chains and rules
810
# $list_command = 'iptables-save'
911
$list_command = {
@@ -253,6 +255,8 @@ class Puppet::Provider::Firewall::Firewall
253255
:bytecode, :ipvs, :helper, :zone, :cgroup, :rpfilter, :condition, :name, :notrack
254256
]
255257

258+
###### PUBLIC METHODS ######
259+
256260
# Raw data is retrieved via `iptables -L` and then regexed to retrieve the different Chains and whether they have a set Policy
257261
def get(context)
258262
# Call the private method which returns the rules
@@ -319,6 +323,153 @@ def delete(context, name, is)
319323
PuppetX::Firewall::Utility.persist_iptables(context, name, is[:protocol])
320324
end
321325

326+
# Custom insync method
327+
# Needed for uid and gid
328+
def insync?(context, _name, property_name, is_hash, should_hash)
329+
context.debug("Checking whether '#{property_name}' is out of sync")
330+
331+
# If either value is nil, no custom logic is required
332+
return nil if is_hash[property_name].nil? || should_hash[property_name].nil?
333+
334+
case property_name
335+
when :protocol
336+
is = is_hash[property_name]
337+
should = should_hash[property_name]
338+
339+
# Ensure the should value accurately matches the is
340+
should = 'IPv4' if should == 'iptables'
341+
should = 'IPv6' if should == 'ip6tables'
342+
343+
is == should
344+
when :source, :destination
345+
# Ensure source/destination has it's valid mask before you compare it
346+
is_hash[property_name] == PuppetX::Firewall::Utility.host_to_mask(should_hash[property_name], should_hash[:protocol])
347+
when :tcp_option, :ctproto, :hop_limit
348+
# Ensure that the values are compared as strings
349+
is_hash[property_name] == should_hash[property_name].to_s
350+
when :tcp_flags
351+
# Custom logic to account for `ALL` being returned as `FIN,SYN,RST,PSH,ACK,URG`
352+
is = is_hash[property_name].split
353+
should = should_hash[property_name].split
354+
355+
is = is.map { |x| (x == 'FIN,SYN,RST,PSH,ACK,URG') ? 'ALL' : x }
356+
should = should.map { |x| (x == 'FIN,SYN,RST,PSH,ACK,URG') ? 'ALL' : x }
357+
358+
is.join(' ') == should.join(' ')
359+
when :uid, :gid
360+
require 'etc'
361+
# The following code allow us to take into consideration unix mappings
362+
# between string usernames and UIDs (integers). We also need to ignore
363+
# spaces as they are irrelevant with respect to rule sync.
364+
365+
# Remove whitespace
366+
is = is_hash[property_name].to_s.gsub(%r{\s+}, '')
367+
should = should_hash[property_name].to_s.gsub(%r{\s+}, '')
368+
369+
# Keep track of negation, but remove the '!'
370+
is_negate = ''
371+
should_negate = ''
372+
if is.start_with?('!')
373+
is = is.gsub(%r{^!}, '')
374+
is_negate = '!'
375+
end
376+
if should.start_with?('!')
377+
should = should.gsub(%r{^!}, '')
378+
should_negate = '!'
379+
end
380+
381+
# If 'is' or 'should' contain anything other than digits or digit range,
382+
# we assume that we have to do a lookup to convert to UID
383+
is = Etc.getpwnam(is).uid unless is[%r{[0-9]+(-[0-9]+)?}] == is
384+
should = Etc.getpwnam(should).uid unless should[%r{[0-9]+(-[0-9]+)?}] == should
385+
386+
"#{is_negate}#{is}" == "#{should_negate}#{should}"
387+
when :mac_source
388+
# Value of mac_source may be downcased when returned on certain OS, i.e. Debian 11 / Ubuntu 22.04 / SLES 15
389+
is_hash[property_name].casecmp(should_hash[property_name]).zero?
390+
when :state, :ctstate, :ctstatus
391+
# Ensure that if both is and should are array values, they are correctly compared in order
392+
is = is_hash[property_name]
393+
should = should_hash[property_name]
394+
return nil unless is.is_a?(Array) && should.is_a?(Array)
395+
396+
if is[0].start_with?('!')
397+
is.append('!')
398+
is[0] = is[0].gsub(%r{^!\s?}, '')
399+
end
400+
if should[0].start_with?('!')
401+
should.append('!')
402+
should[0] = should[0].gsub(%r{^!\s?}, '')
403+
end
404+
is.sort == should.sort
405+
when :icmp
406+
# Ensure that the values are compared to each other as icmp code numbers
407+
is = PuppetX::Firewall::Utility.icmp_name_to_number(is_hash[property_name], is_hash[:protocol])
408+
should = PuppetX::Firewall::Utility.icmp_name_to_number(should_hash[property_name], should_hash[:protocol])
409+
is == should
410+
when :log_level
411+
# Ensure that the values are compared to each other as log level numbers
412+
is = PuppetX::Firewall::Utility.log_level_name_to_number(is_hash[property_name])
413+
should = PuppetX::Firewall::Utility.log_level_name_to_number(should_hash[property_name])
414+
is == should
415+
when :set_mark
416+
# Ensure that the values are compared to eachother in hexidecimal format
417+
is = PuppetX::Firewall::Utility.mark_mask_to_hex(is_hash[property_name])
418+
should = PuppetX::Firewall::Utility.mark_mask_to_hex(should_hash[property_name])
419+
is == should
420+
when :match_mark, :connmark
421+
# Ensure that the values are compared to eachother in hexidecimal format
422+
is = PuppetX::Firewall::Utility.mark_to_hex(is_hash[property_name])
423+
should = PuppetX::Firewall::Utility.mark_to_hex(should_hash[property_name])
424+
is == should
425+
when :time_start, :time_stop
426+
# Ensure the values are compared in full `00:00:00` format
427+
is = is_hash[property_name]
428+
should = should_hash[property_name]
429+
430+
should = "0#{should}" if %r{^([0-9]):}.match?(should)
431+
should = "#{should}:00" if %r{^([0-9]|0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$}.match?(should)
432+
433+
is == should
434+
when :jump
435+
# Compare values as uppercase
436+
is_hash[property_name].casecmp(should_hash[property_name]).zero?
437+
when :dport, :sport
438+
is = is_hash[property_name]
439+
should = should_hash[property_name]
440+
441+
# Wrap compared values as arrays in order to simplify comparisons
442+
is = [is] unless is.is_a?(Array)
443+
should = [should] unless should.is_a?(Array)
444+
445+
# If first value includes a negation, retrieve it and set as it's own value
446+
if is[0].start_with?('!')
447+
is.append('!')
448+
is[0] = is[0].gsub(%r{^!\s?}, '')
449+
end
450+
if should[0].start_with?('!')
451+
should.append('!')
452+
should[0] = should[0].gsub(%r{^!\s?}, '')
453+
end
454+
455+
# Range can be passed as `-` but will always be set/returned as `:`
456+
# Ensure values are sorted
457+
is.sort == should.map { |port| port.to_s.tr('-', ':') }.sort
458+
when :string_hex
459+
# Compare the values with any whitespace removed
460+
is = is_hash[property_name].to_s.gsub(%r{\s+}, '')
461+
should = should_hash[property_name].to_s.gsub(%r{\s+}, '')
462+
463+
is == should
464+
else
465+
# Ensure that if both values are arrays, that they are sorted prior to comparison
466+
return nil unless is_hash[property_name].is_a?(Array) && should_hash[property_name].is_a?(Array)
467+
468+
is_hash[property_name].sort == should_hash[property_name].sort
469+
end
470+
end
471+
472+
###### PRIVATE METHODS ######
322473
###### GET ######
323474

324475
# Retrieve the rules
@@ -597,7 +748,7 @@ def self.create_absent(namevar, title)
597748
result
598749
end
599750

600-
###### SET/UPDATE ######
751+
###### SET ######
601752

602753
# Verify that the information being set is correct
603754
# @api.private
@@ -918,134 +1069,4 @@ def self.insert_order(context, name, chain, table, protocol)
9181069
sorted_rules = rules.reject { |r| r.match($unmanaged_rule_regex) }.sort
9191070
sorted_rules.index(name) + 1 + unnamed_offset
9201071
end
921-
922-
# Custom insync method
923-
# Needed for uid and gid
924-
def insync?(context, _name, property_name, is_hash, should_hash)
925-
context.debug("Checking whether #{property_name} is out of sync")
926-
927-
# If either value is nil, no custom logic is required
928-
return nil if is_hash[property_name].nil? || should_hash[property_name].nil?
929-
930-
case property_name
931-
when :protocol
932-
is = is_hash[property_name]
933-
should = should_hash[property_name]
934-
935-
# Ensure the should value accurately matches the is
936-
should = 'IPv4' if should == 'iptables'
937-
should = 'IPv6' if should == 'ip6tables'
938-
939-
is == should
940-
when :source, :destination
941-
# Ensure source/destination has it's valid mask before you compare it
942-
is_hash[property_name] == PuppetX::Firewall::Utility.host_to_mask(should_hash[property_name], should_hash[:protocol])
943-
when :tcp_option, :ctproto, :hop_limit
944-
# Ensure that the values are compared as strings
945-
is_hash[property_name] == should_hash[property_name].to_s
946-
when :tcp_flags
947-
# Custom logic to account for `ALL` being returned as `FIN,SYN,RST,PSH,ACK,URG`
948-
is = is_hash[property_name].split
949-
should = should_hash[property_name].split
950-
951-
is = is.map { |x| (x == 'FIN,SYN,RST,PSH,ACK,URG') ? 'ALL' : x }
952-
should = should.map { |x| (x == 'FIN,SYN,RST,PSH,ACK,URG') ? 'ALL' : x }
953-
954-
is.join(' ') == should.join(' ')
955-
when :uid, :gid
956-
require 'etc'
957-
# The following code allow us to take into consideration unix mappings
958-
# between string usernames and UIDs (integers). We also need to ignore
959-
# spaces as they are irrelevant with respect to rule sync.
960-
961-
# Remove whitespace
962-
is = is_hash[property_name].to_s.gsub(%r{\s+}, '')
963-
should = should_hash[property_name].to_s.gsub(%r{\s+}, '')
964-
965-
# Keep track of negation, but remove the '!'
966-
is_negate = ''
967-
should_negate = ''
968-
if is.start_with?('!')
969-
is = is.gsub(%r{^!}, '')
970-
is_negate = '!'
971-
end
972-
if should.start_with?('!')
973-
should = should.gsub(%r{^!}, '')
974-
should_negate = '!'
975-
end
976-
977-
# If 'is' or 'should' contain anything other than digits or digit range,
978-
# we assume that we have to do a lookup to convert to UID
979-
is = Etc.getpwnam(is).uid unless is[%r{[0-9]+(-[0-9]+)?}] == is
980-
should = Etc.getpwnam(should).uid unless should[%r{[0-9]+(-[0-9]+)?}] == should
981-
982-
"#{is_negate}#{is}" == "#{should_negate}#{should}"
983-
when :mac_source
984-
# Value of mac_source may be downcased when returned on certain OS, i.e. Debian 11 / Ubuntu 22.04 / SLES 15
985-
is_hash[property_name].casecmp(should_hash[property_name]).zero?
986-
when :state, :ctstate, :ctstatus
987-
# Ensure that if both is and should are array values, they are correctly compared in order
988-
is = is_hash[property_name]
989-
should = should_hash[property_name]
990-
return nil unless is.is_a?(Array) && should.is_a?(Array)
991-
992-
if is[0].start_with?('!')
993-
is.append('!')
994-
is[0] = is[0].gsub(%r{^!\s?}, '')
995-
end
996-
if should[0].start_with?('!')
997-
should.append('!')
998-
should[0] = should[0].gsub(%r{^!\s?}, '')
999-
end
1000-
is.sort == should.sort
1001-
when :icmp
1002-
# Ensure that the values are compared to each other as icmp code numbers
1003-
is = PuppetX::Firewall::Utility.icmp_name_to_number(is_hash[property_name], is_hash[:protocol])
1004-
should = PuppetX::Firewall::Utility.icmp_name_to_number(should_hash[property_name], should_hash[:protocol])
1005-
is == should
1006-
when :log_level
1007-
# Ensure that the values are compared to each other as log level numbers
1008-
is = PuppetX::Firewall::Utility.log_level_name_to_number(is_hash[property_name])
1009-
should = PuppetX::Firewall::Utility.log_level_name_to_number(should_hash[property_name])
1010-
is == should
1011-
when :set_mark
1012-
# Ensure that the values are compared to eachother in hexidecimal format
1013-
is = PuppetX::Firewall::Utility.mark_mask_to_hex(is_hash[property_name])
1014-
should = PuppetX::Firewall::Utility.mark_mask_to_hex(should_hash[property_name])
1015-
is == should
1016-
when :match_mark, :connmark
1017-
# Ensure that the values are compared to eachother in hexidecimal format
1018-
is = PuppetX::Firewall::Utility.mark_to_hex(is_hash[property_name])
1019-
should = PuppetX::Firewall::Utility.mark_to_hex(should_hash[property_name])
1020-
is == should
1021-
when :time_start, :time_stop
1022-
# Ensure the values are compared in full `00:00:00` format
1023-
is = is_hash[property_name]
1024-
should = should_hash[property_name]
1025-
1026-
should = "0#{should}" if %r{^([0-9]):}.match?(should)
1027-
should = "#{should}:00" if %r{^([0-9]|0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$}.match?(should)
1028-
1029-
is == should
1030-
when :jump
1031-
# Compare values as uppercase
1032-
is_hash[property_name].casecmp(should_hash[property_name]).zero?
1033-
when :dport, :sport
1034-
# Ensure values are compared as string
1035-
# Range can be passed as `-` but will always be set/returned as `:`
1036-
is_hash[property_name] == should_hash[property_name].to_s.tr('-', ':') unless should_hash[property_name].is_a?(Array)
1037-
is_hash[property_name] == should_hash[property_name].map { |port| port.to_s.tr('-', ':') } if should_hash[property_name].is_a?(Array)
1038-
when :string_hex
1039-
# Compare the values with any whitespace removed
1040-
is = is_hash[property_name].to_s.gsub(%r{\s+}, '')
1041-
should = should_hash[property_name].to_s.gsub(%r{\s+}, '')
1042-
1043-
is == should
1044-
else
1045-
# Ensure that if both values are arrays, that they are sorted prior to comparison
1046-
return nil unless is_hash[property_name].is_a?(Array) && should_hash[property_name].is_a?(Array)
1047-
1048-
is_hash[property_name].sort == should_hash[property_name].sort
1049-
end
1050-
end
10511072
end

0 commit comments

Comments
 (0)