You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@vcl.apache.org by ar...@apache.org on 2017/05/23 23:01:10 UTC

svn commit: r1795977 - in /vcl/trunk/managementnode/lib/VCL: Module/OS/Linux/firewall/firewalld.pm Module/OS/Linux/firewall/iptables.pm utils.pm

Author: arkurth
Date: Tue May 23 23:01:09 2017
New Revision: 1795977

URL: http://svn.apache.org/viewvc?rev=1795977&view=rev
Log:
VCL-1049
Added subroutines:
* iptables.pm::nat_delete_orphaned_reservation_chains
* iptables.pm::get_table_chain_names
* utils.pm::get_all_reservation_ids

VCL-1031
Updated regex's in iptables.pm::get_table_info to detect exclamation marks enclosed in single quotes, as may be returned by 'firewall-cmd --permanent --direct --get-all-rules'. The quotes were throwing off the detection of a MASQERADE rule when a CentOS 7/firewalld host is used as a NAT host.

Commented out some notify messages in iptables.pm and firewalld.pm which were generating a lot of noise.

Modified:
    vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/firewall/firewalld.pm
    vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/firewall/iptables.pm
    vcl/trunk/managementnode/lib/VCL/utils.pm

Modified: vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/firewall/firewalld.pm
URL: http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/firewall/firewalld.pm?rev=1795977&r1=1795976&r2=1795977&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/firewall/firewalld.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/firewall/firewalld.pm Tue May 23 23:01:09 2017
@@ -174,7 +174,7 @@ sub get_all_direct_rules {
 	# ipv4 filter INPUT 0 --jump vcl-pre_capture --match comment --comment 'VCL: jump to rules added during the pre-capture stage (2017-04-07 17:19:21)'
 	my @rules = grep(/^(ipv4|ipv6|eb)/, @$output);
 	
-	notify($ERRORS{'DEBUG'}, 0, "retrieved all firewalld direct rules defined on $computer_name:\n" . join("\n", @rules));
+	#notify($ERRORS{'DEBUG'}, 0, "retrieved all firewalld direct rules defined on $computer_name:\n" . join("\n", @rules));
 	return @rules;
 }
 

Modified: vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/firewall/iptables.pm
URL: http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/firewall/iptables.pm?rev=1795977&r1=1795976&r2=1795977&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/firewall/iptables.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/firewall/iptables.pm Tue May 23 23:01:09 2017
@@ -1380,6 +1380,37 @@ sub chain_exists {
 
 #//////////////////////////////////////////////////////////////////////////////
 
+=head2 get_table_chain_names
+
+ Parameters  : $table_name
+ Returns     : array
+ Description : Returns an array containing the chain names defined for a table.
+
+=cut
+
+sub get_table_chain_names {
+	my $self = shift;
+	if (ref($self) !~ /VCL::Module/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return 0;
+	}
+	
+	my ($table_name) = @_;
+	if (!defined($table_name)) {
+		notify($ERRORS{'WARNING'}, 0, "table name argument was not specified");
+		return;
+	}
+	
+	my $computer_name = $self->data->get_computer_hostname();
+	
+	my $table_info = $self->get_table_info($table_name, 1) || return;
+	my @table_chain_names = sort keys %$table_info;
+	notify($ERRORS{'DEBUG'}, 0, "retrieved chain names defined in $table_name table on $computer_name:\n" . join("\n", @table_chain_names));
+	return @table_chain_names;
+}
+
+#//////////////////////////////////////////////////////////////////////////////
+
 =head2 nat_sanitize_reservation
 
  Parameters  : $reservation_id (optional)
@@ -1564,6 +1595,7 @@ sub get_table_info {
 	if ($self->can('get_all_direct_rules')) {
 		# Convert:
 		#    ipv4 filter vcl-pre_capture 0 --jump ACCEPT --protocol tcp --match comment --comment 'VCL: ...' --match tcp --destination-port 22
+		#    ipv4 nat POSTROUTING 0 '!' --destination 10.0.0.0/20 --jump MASQUERADE --out-interface eth1 --match comment --comment 'blah... blah'
 		# To:
 		#    -A vcl-pre_capture -p tcp -m comment --comment "VCL: ..." -m tcp --dport 22 -j ACCEPT
 		DIRECT_RULE: for my $direct_rule ($self->get_all_direct_rules()) {
@@ -1581,15 +1613,15 @@ sub get_table_info {
 				next DIRECT_RULE;
 			}
 			elsif ($rule_table ne $table_name) {
-				notify($ERRORS{'DEBUG'}, 0, "ignoring rule, table does not match '$table_name': $direct_rule");
+				#notify($ERRORS{'DEBUG'}, 0, "ignoring rule, table does not match '$table_name': $direct_rule");
 				next DIRECT_RULE;
 			}
 			
 			my $converted_rule = "-A $rule_chain $rule_specification";
-			notify($ERRORS{'DEBUG'}, 0, "converted iptables direct rule to iptables format:\n" .
-				"direct rule     : $direct_rule\n" .
-				"iptables format : $converted_rule"
-			);
+			#notify($ERRORS{'DEBUG'}, 0, "converted iptables direct rule to iptables format:\n" .
+			#	"direct rule     : $direct_rule\n" .
+			#	"iptables format : $converted_rule"
+			#);
 			push @lines, $converted_rule;
 		}
 	}
@@ -1629,6 +1661,13 @@ sub get_table_info {
 		# Remove spaces from end of rule specification
 		$rule_specification_string =~ s/\s+$//;
 		
+		#notify($ERRORS{'DEBUG'}, 0, "split iptables line:\n" .
+		#	"line          : '$line'\n" .
+		#	"command       : '$iptables_command'\n" .
+		#	"chain         : '$chain_name'\n" .
+		#	"specification : '$rule_specification_string'"
+		#);
+		
 		if ($iptables_command =~ /^(-P|--policy)/) {
 			# -P, --policy chain target (Set  the policy for the chain to the given target)
 			$table_info->{$chain_name}{policy} = $rule_specification_string;
@@ -1648,13 +1687,15 @@ sub get_table_info {
 			$rule->{rule_specification} = $rule_specification_string;
 			
 			# Parse the rule parameters
+			# Be sure to check for ! enclosed in quotes:
+			#    -A POSTROUTING '!' --destination 10.10.0.0/20 --jump MASQUERADE
 			my $parameters = {
-				'protocol'      => '\s*(\!?)\s*(-p|--protocol)\s+([^\s]+)',
-				'source'        => '\s*(\!?)\s*(-s|--source)\s+([\d\.\/]+)',
-				'destination'   => '\s*(\!?)\s*(-d|--destination)\s+([\d\.\/]+)',
-				'in-interface'  => '\s*(\!?)\s*(-i|--in-interface)\s+([^\s]+)',
-				'out-interface' => '\s*(\!?)\s*(-o|--out-interface)\s+([^\s]+)',
-				'fragment'      => '\s*(\!?)\s*(-f|--fragment)',
+				'protocol'      => '\s*\'?(\!?)\'?\s*(-p|--protocol)\s+([^\s]+)',
+				'source'        => '\s*\'?(\!?)\'?\s*(-s|--source)\s+([\d\.\/]+)',
+				'destination'   => '\s*\'?(\!?)\'?\s*(-d|--destination)\s+([\d\.\/]+)',
+				'in-interface'  => '\s*\'?(\!?)\'?\s*(-i|--in-interface)\s+([^\s]+)',
+				'out-interface' => '\s*\'?(\!?)\'?\s*(-o|--out-interface)\s+([^\s]+)',
+				'fragment'      => '\s*\'?(\!?)\'?\s*(-f|--fragment)',
 			};
 			
 			PARAMETER: for my $parameter (keys %$parameters) {
@@ -1724,7 +1765,7 @@ sub get_table_info {
 					}
 					elsif (!$target_extension_option_name) {
 						# If here, the section should be a target extension option value
-						notify($ERRORS{'WARNING'}, 0, "failed to parse iptables rule, target extension option name was not detected before this section: '$target_extension_option_section'\n" .
+						notify($ERRORS{'WARNING'}, 0, "failed to parse iptables rule on $computer_name, target extension option name was not detected before this section: '$target_extension_option_section'\n" .
 							"output line: $line\n" .
 							"preceeding target parameter: $target_parameter\n" .
 							"target value: $target"
@@ -1799,9 +1840,11 @@ sub get_table_info {
 						next MATCH_EXTENSION_SECTION;
 					}
 					else {
-						notify($ERRORS{'WARNING'}, 0, "failed to parse iptables rule, match extension module name was not detected before this section: '$match_extension_section'\n" .
-							"iptables rule specification: $rule_specification_string\n" .
-							"iptables command: $line"
+						notify($ERRORS{'WARNING'}, 0, "failed to parse iptables rule in $table_name table on $computer_name\n" .
+							"match extension module name was not detected before this section:\n" .
+							"match extension section:\n$match_extension_section\n" .
+							"iptables rule specification:\n$rule_specification_string\n" .
+							"iptables command: \n$line"
 						);
 						next LINE;
 					}
@@ -2067,6 +2110,7 @@ sub nat_configure_host {
 		return;
 	}
 	
+	$self->save_configuration();
 	notify($ERRORS{'DEBUG'}, 0, "successfully configured NAT on $computer_name");
 	return 1;
 }
@@ -2237,6 +2281,7 @@ sub nat_add_port_forward {
 		}
 	)) {
 		notify($ERRORS{'OK'}, 0, "added NAT port forward on $computer_name: $public_interface_name:$source_port --> $destination_ip_address:$destination_port");
+		$self->save_configuration();
 		return 1;
 	}
 	else {
@@ -2410,6 +2455,80 @@ sub get_cluster_chain_name {
 }
 
 #//////////////////////////////////////////////////////////////////////////////
+
+=head2 nat_delete_orphaned_reservation_chains
+
+ Parameters  : none
+ Returns     : boolean
+ Description : Checks all of the chains that exist in the nat table on a NAT
+               host. Chains which don't begin with the vcld process name
+               followed by a hyphen (ex. 'vcld-') are ignored. Retrieves list of
+               all reservation IDs currently in the database. If a chain exists
+               but a corresponding reservation does not, the chain is deleted.
+
+=cut
+
+sub nat_delete_orphaned_reservation_chains {
+	my $self = shift;
+	if (ref($self) !~ /VCL::Module/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return 0;
+	}
+	
+	my $computer_name = $self->data->get_computer_hostname();
+	
+	my @reservation_ids = get_all_reservation_ids();
+	if (!@reservation_ids) {
+		notify($ERRORS{'WARNING'}, 0, "not deleting orphaned reservation chains on $computer_name, failed to retrieve all reservation IDs from the database");
+		return;
+	}
+	my %reservation_id_hash = map { $_ => 1 } @reservation_ids;
+	
+	my @chain_names = $self->get_table_chain_names('nat');
+	my @chains_ignored;
+	my @chains_with_reservation;
+	my @chains_deleted;
+	
+	for my $chain_name (@chain_names) {
+		if ($chain_name !~ /^$PROCESSNAME-/) {
+			notify($ERRORS{'DEBUG'}, 0, "ignoring chain in nat table on $computer_name: $chain_name, name does not begin with '$PROCESSNAME-'");
+			push @chains_ignored, $chain_name;
+			next;
+		}
+		my ($chain_reservation_id) = $chain_name =~ /^$PROCESSNAME-(\d+)$/;
+		if (!defined($chain_reservation_id)) {
+			notify($ERRORS{'DEBUG'}, 0, "ignoring chain in nat table on $computer_name: $chain_name, reservation ID could not be determined from chain name, pattern: '$PROCESSNAME-<reservation ID>'");
+			push @chains_ignored, $chain_name;
+			next;
+		}
+		elsif (defined($reservation_id_hash{$chain_reservation_id})) {
+			notify($ERRORS{'DEBUG'}, 0, "ignoring chain in nat table on $computer_name: $chain_name, reservation $chain_reservation_id exists");
+			push @chains_with_reservation, $chain_name;
+			next;
+		}
+		
+		notify($ERRORS{'OK'}, 0, "deleting orphaned chain in nat table on $computer_name: $chain_name, reservation $chain_reservation_id does NOT exist");
+		if ($self->delete_chain('nat', $chain_name)) {
+			push @chains_deleted, $chain_name;
+		}
+		else {
+			return;
+		}
+	}
+	
+	if (scalar(@chains_deleted)) {
+		$self->save_configuration();
+	}
+	
+	notify($ERRORS{'DEBUG'}, 0, "checked for orphaned reservation chains on NAT host $computer_name:\n" .
+		"chains ignored (" . scalar(@chains_ignored) . "): " . join(', ', @chains_ignored) . "\n" .
+		"chains with a current reservation (" . scalar(@chains_with_reservation) . "): " . join(', ', @chains_with_reservation) . "\n" .
+		"chains deleted (" . scalar(@chains_deleted) . "): " . join(', ', @chains_deleted)
+	);
+	return 1;
+}
+
+#//////////////////////////////////////////////////////////////////////////////
 
 =head2 DESTROY
 

Modified: vcl/trunk/managementnode/lib/VCL/utils.pm
URL: http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/utils.pm?rev=1795977&r1=1795976&r2=1795977&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/utils.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/utils.pm Tue May 23 23:01:09 2017
@@ -119,6 +119,7 @@ our @EXPORT = qw(
 	format_hash_keys
 	format_number
 	get_active_directory_domain_credentials
+	get_all_reservation_ids
 	get_affiliation_info
 	get_array_intersection
 	get_block_request_image_info
@@ -8234,6 +8235,24 @@ sub get_reservation_request_id {
 }
 
 #//////////////////////////////////////////////////////////////////////////////
+
+=head2 get_all_reservation_ids
+
+ Parameters  : none
+ Returns     : array
+ Description : Retrieves the IDs of all reservations regardless of state or any
+               other attributes.
+
+=cut
+
+sub get_all_reservation_ids {
+	my @rows = database_select('SELECT id FROM reservation');
+	my @reservation_ids = sort {$a <=> $b} map { $_->{id} } @rows;
+	notify($ERRORS{'DEBUG'}, 0, "retrieved all reservation IDs currently defined in the database: " . join(', ', @reservation_ids));
+	return @reservation_ids;
+}
+
+#//////////////////////////////////////////////////////////////////////////////
 
 =head2 get_reservation_request_info