|
4 | 4 |
|
5 | 5 | # Implementation for the iptables type using the Resource API. |
6 | 6 | class Puppet::Provider::Firewall::Firewall |
| 7 | + ###### GLOBAL VARIABLES ###### |
| 8 | + |
7 | 9 | # Command to list all chains and rules |
8 | 10 | # $list_command = 'iptables-save' |
9 | 11 | $list_command = { |
@@ -253,6 +255,8 @@ class Puppet::Provider::Firewall::Firewall |
253 | 255 | :bytecode, :ipvs, :helper, :zone, :cgroup, :rpfilter, :condition, :name, :notrack |
254 | 256 | ] |
255 | 257 |
|
| 258 | + ###### PUBLIC METHODS ###### |
| 259 | + |
256 | 260 | # Raw data is retrieved via `iptables -L` and then regexed to retrieve the different Chains and whether they have a set Policy |
257 | 261 | def get(context) |
258 | 262 | # Call the private method which returns the rules |
@@ -319,6 +323,153 @@ def delete(context, name, is) |
319 | 323 | PuppetX::Firewall::Utility.persist_iptables(context, name, is[:protocol]) |
320 | 324 | end |
321 | 325 |
|
| 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 ###### |
322 | 473 | ###### GET ###### |
323 | 474 |
|
324 | 475 | # Retrieve the rules |
@@ -597,7 +748,7 @@ def self.create_absent(namevar, title) |
597 | 748 | result |
598 | 749 | end |
599 | 750 |
|
600 | | - ###### SET/UPDATE ###### |
| 751 | + ###### SET ###### |
601 | 752 |
|
602 | 753 | # Verify that the information being set is correct |
603 | 754 | # @api.private |
@@ -918,134 +1069,4 @@ def self.insert_order(context, name, chain, table, protocol) |
918 | 1069 | sorted_rules = rules.reject { |r| r.match($unmanaged_rule_regex) }.sort |
919 | 1070 | sorted_rules.index(name) + 1 + unnamed_offset |
920 | 1071 | 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 |
1051 | 1072 | end |
0 commit comments