Author: fapeeler Date: Mon Nov 14 21:15:16 2011 New Revision: 1201909 URL: http://svn.apache.org/viewvc?rev=1201909&view=rev Log: VCL-30
rework of the process connect methods routine -- added support to Linux.pm for collect firewall configuration -- added support for better manipulation of iptables if it exists Modified: incubator/vcl/trunk/managementnode/lib/VCL/Module/OS.pm incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm incubator/vcl/trunk/managementnode/lib/VCL/inuse.pm incubator/vcl/trunk/managementnode/lib/VCL/reserved.pm Modified: incubator/vcl/trunk/managementnode/lib/VCL/Module/OS.pm URL: http://svn.apache.org/viewvc/incubator/vcl/trunk/managementnode/lib/VCL/Module/OS.pm?rev=1201909&r1=1201908&r2=1201909&view=diff ============================================================================== --- incubator/vcl/trunk/managementnode/lib/VCL/Module/OS.pm (original) +++ incubator/vcl/trunk/managementnode/lib/VCL/Module/OS.pm Mon Nov 14 21:15:16 2011 @@ -2046,14 +2046,32 @@ sub process_connect_methods { my $connect_method_info = get_connect_method_info($imagerevision_id); if (!$connect_method_info) { notify($ERRORS{'WARNING'}, 0, "no connect methods are configured for image revision $imagerevision_id"); + return; } - my $remote_ip = $self->data->get_reservation_remote_ip(); + my $remote_ip = shift; if (!$remote_ip) { - notify($ERRORS{'WARNING'}, 0, "reservation remote IP address is not defined, connect methods will be available from any IP address"); - $remote_ip = '0.0.0.0/0.0.0.0'; + notify($ERRORS{'OK'}, 0, "reservation remote IP address is not defined, connect methods will be available from any IP address"); + $remote_ip = '0.0.0.0/0'; + } + elsif ($remote_ip =~ /any/i){ + notify($ERRORS{'OK'}, 0, "reservation remote IP address is set to ANY, connect methods will be available from any IP address"); + $remote_ip = '0.0.0.0/0'; + } + else { + $remote_ip .= "/24"; } + my $overwrite = shift; + if (!$overwrite) { + notify($ERRORS{'DEBUG'}, 0, "overwrite value was not passed as an argument setting to 0"); + $overwrite = 0; + } + + my $state = $self->data->get_request_state_name(); + notify($ERRORS{'DEBUG'}, 0, "state = $state"); + + CONNECT_METHOD: for my $connect_method_id (sort keys %{$connect_method_info} ) { notify($ERRORS{'DEBUG'}, 0, "processing connect method:\n" . format_data($connect_method_info->{$connect_method_id})); @@ -2065,10 +2083,33 @@ sub process_connect_methods { my $startup_script = $connect_method_info->{$connect_method_id}{startupscript}; my $install_script = $connect_method_info->{$connect_method_id}{installscript}; my $disabled = $connect_method_info->{$connect_method_id}{connectmethodmap}{disabled}; + + if( $state =~ /deleted|timeout/) { + $disabled = 1; + } if ($disabled) { - # TODO: Add code to disable the service, close the firewall port, etc - notify($ERRORS{'OK'}, 0, "skipping '$name' connect method configuration, it should be disabled on $computer_node_name"); + if ($self->service_exists($service_name)) { + notify($ERRORS{'DEBUG'}, 0, "attempting to stop '$service_name' service for '$name' connect method on $computer_node_name"); + if ($self->stop_service($service_name)) { + notify($ERRORS{'OK'}, 0, "'$service_name' stop on $computer_node_name"); + } + else { + notify($ERRORS{'WARNING'}, 0, "failed to stop '$service_name' service for '$name' connect method on $computer_node_name"); + } + } + + #Disable firewall port + if (defined($port)) { + notify($ERRORS{'DEBUG'}, 0, "attempting to open firewall port $port on $computer_node_name for '$name' connect method"); + if ($self->disable_firewall_port($protocol, $port, $remote_ip, 1)) { + notify($ERRORS{'OK'}, 0, "closing firewall port $port on $computer_node_name for $remote_ip $name connect method"); + } + else { + notify($ERRORS{'WARNING'}, 0, "failed to close firewall port $port on $computer_node_name for $remote_ip $name connect method"); + } + } + } else { # Attempt to start and configure the connect method @@ -2088,29 +2129,6 @@ sub process_connect_methods { notify($ERRORS{'WARNING'}, 0, "failed to start '$service_name' service for '$name' connect method on $computer_node_name"); } } - #elsif ($install_script) { - # notify($ERRORS{'DEBUG'}, 0, "'$service_name' service for '$name' connect method does not exist on $computer_node_name, attempting to execute connect method install script"); - # my ($install_exit_status, $install_output) = $self->execute($install_script, 1, 600, 1); - # if (!defined($install_output)) { - # notify($ERRORS{'WARNING'}, 0, "failed to run command to execute install script for '$name' connect method on $computer_node_name, command: '$install_script'"); - # } - # else { - # notify($ERRORS{'OK'}, 0, "executed install script for '$name' connect method on $computer_node_name, command: '$install_script', exit status: $install_exit_status, output:\n" . join("\n", @$install_output)); - # - # if ($self->service_exists($service_name)) { - # if ($self->start_service($service_name)) { - # notify($ERRORS{'OK'}, 0, "'$service_name' started on $computer_node_name"); - # $service_started = 1; - # } - # else { - # notify($ERRORS{'WARNING'}, 0, "failed to start '$service_name' service after executing install script for '$name' connect method on $computer_node_name"); - # } - # } - # else { - # notify($ERRORS{'WARNING'}, 0, "'$service_name' service does NOT exist after executing install script for '$name' connect method on $computer_node_name"); - # } - # } - #} else { notify($ERRORS{'WARNING'}, 0, "'$service_name' service for '$name' connect method does NOT exist on $computer_node_name, connect method install script is not defined"); } @@ -2134,11 +2152,11 @@ sub process_connect_methods { # Open the firewall port if (defined($port)) { notify($ERRORS{'DEBUG'}, 0, "attempting to open firewall port $port on $computer_node_name for '$name' connect method"); - if ($self->enable_firewall_port($protocol, $port, "$remote_ip/24", 1)) { - notify($ERRORS{'OK'}, 0, "opened firewall port $port on $computer_node_name for '$name' connect method"); + if ($self->enable_firewall_port($protocol, $port, $remote_ip, 1)) { + notify($ERRORS{'OK'}, 0, "opened firewall port $port on $computer_node_name for $remote_ip $name connect method"); } else { - notify($ERRORS{'WARNING'}, 0, "failed to open firewall port $port on $computer_node_name for '$name' connect method"); + notify($ERRORS{'WARNING'}, 0, "failed to open firewall port $port on $computer_node_name for $remote_ip $name connect method"); } } } Modified: incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm URL: http://svn.apache.org/viewvc/incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm?rev=1201909&r1=1201908&r2=1201909&view=diff ============================================================================== --- incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm (original) +++ incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm Mon Nov 14 21:15:16 2011 @@ -54,6 +54,7 @@ use diagnostics; no warnings 'redefine'; use VCL::utils; +use Net::Netmask; ############################################################################## @@ -112,6 +113,7 @@ sub pre_capture { my $computer_node_name = $self->data->get_computer_node_name(); notify($ERRORS{'OK'}, 0, "beginning Linux-specific image capture preparation tasks"); + if (!$self->file_exists("/root/.vclcontrol/vcl_exclude_list.sample")) { notify($ERRORS{'DEBUG'}, 0, "/root/.vclcontrol/vcl_exclude_list.sample does not exists"); @@ -131,9 +133,13 @@ sub pre_capture { } #Clean up connection methods - if($self->process_connect_methods() ){ + if($self->process_connect_methods("any", 1) ){ notify($ERRORS{'OK'}, 0, "processed connection methods on $computer_node_name"); } + + if(!$self->clean_iptables()) { + return 0; + } # Try to clear /tmp if ($self->execute("/usr/sbin/tmpwatch -f 0 /tmp; /bin/cp /dev/null /var/log/wtmp")) { @@ -233,6 +239,8 @@ sub post_load { my $computer_short_name = $self->data->get_computer_short_name(); my $computer_node_name = $self->data->get_computer_node_name(); my $image_os_install_type = $self->data->get_image_os_install_type(); + my $management_node_ip = $self->data->get_management_node_ipaddress(); + my $mn_private_ip = $self->mn_os->get_private_ip_address(); notify($ERRORS{'OK'}, 0, "initiating Linux post_load: $image_name on $computer_short_name"); @@ -303,6 +311,10 @@ sub post_load { notify($ERRORS{'DEBUG'}, 0, "ran $script_path"); } + if($self->enable_firewall_port("tcp", "any", $mn_private_ip, 1) ){ + notify($ERRORS{'OK'}, 0, "added MN_Priv_IP $mn_private_ip to firewall on $computer_short_name"); + } + # Attempt to generate ifcfg-eth* files and ifup any interfaces which the file does not exist $self->activate_interfaces(); @@ -960,8 +972,8 @@ sub grant_access { } #foreach notify($ERRORS{'OK'}, 0, "started ext_sshd on $computer_node_name"); - if($self->process_connect_methods('start') ){ - notify($ERRORS{'OK'}, 0, "processed connection methods on $computer_node_name"); + if($self->process_connect_methods("", 1) ){ + notify($ERRORS{'OK'}, 0, "processed connection methods on $computer_node_name setting 0.0.0.0 for all allowed ports"); } @@ -1024,6 +1036,7 @@ sub sanitize { } my $computer_node_name = $self->data->get_computer_node_name(); + my $mn_private_ip = $self->mn_os->get_private_ip_address(); # Make sure user is not connected if ($self->is_connected()) { @@ -1040,7 +1053,7 @@ sub sanitize { } #Clean up connection methods - if($self->process_connect_methods() ){ + if($self->process_connect_methods($mn_private_ip, 1) ){ notify($ERRORS{'OK'}, 0, "processed connection methods on $computer_node_name"); } @@ -3038,10 +3051,14 @@ sub service_exists { notify($ERRORS{'DEBUG'}, 0, "'$service_name' service does not exist on $computer_node_name"); return 0; } - elsif ($exit_status == 0 || grep(/not referenced in any runlevel/i, @$output)) { + elsif (defined($exit_status) && $exit_status == 0 ) { + notify($ERRORS{'DEBUG'}, 0, "'$service_name' service exists"); + return 1; + } + elsif (defined($exit_status) && grep(/not referenced in any runlevel/i, @$output)) { # chkconfig may display the following if the service exists but has not been added: # service ext_sshd supports chkconfig, but is not referenced in any runlevel (run 'chkconfig --add ext_sshd') - notify($ERRORS{'DEBUG'}, 0, "'$service_name' service exists but is not referenced in any runlevel"); + notify($ERRORS{'DEBUG'}, 0, "'$service_name' service exists but is not referenced in any runlevel: output:\n" . join("\n", @$output)); } else { notify($ERRORS{'WARNING'}, 0, "unable to determine if '$service_name' service exists, exit status: $exit_status, output:\n" . join("\n", @$output)); @@ -3349,6 +3366,45 @@ sub get_total_memory { #///////////////////////////////////////////////////////////////////////////// +=head2 sanitize_firewall + + Parameters : $scope (optional), + Returns : boolean + Description : Removes all entries for INUPT chain and Sets iptables firewall for private management node IP + +=cut + +sub sanitize_firewall { + 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; + } + + my $scope = shift; + if(!defined($scope)) { + notify($ERRORS{'CRITICAL'}, 0, "scope variable was not passed in as an arguement"); + return; + } + + my $computer_node_name = $self->data->get_computer_node_name(); + my $mn_private_ip = $self->mn_os->get_private_ip_address(); + + my $firewall_configuration = $self->get_firewall_configuration() || return; + my $chain; + my $iptables_del_cmd; + my $INPUT_CHAIN = "INPUT"; + + for my $num (sort keys %{$firewall_configuration->{$INPUT_CHAIN}} ) { + + + } + + +} + +#///////////////////////////////////////////////////////////////////////////// + =head2 enable_firewall_port Parameters : $protocol, $port, $scope (optional), $overwrite_existing (optional), $name (optional), $description (optional) @@ -3364,6 +3420,13 @@ sub enable_firewall_port { return; } + # Check to see if this distro has iptables + # If not return 1 so it does not fail + if (!($self->service_exists("iptables"))) { + notify($ERRORS{'WARNING'}, 0, "iptables does not exist on this OS"); + return 1; + } + my ($protocol, $port, $scope_argument, $overwrite_existing, $name, $description) = @_; if (!defined($protocol) || !defined($port)) { notify($ERRORS{'WARNING'}, 0, "protocol and port arguments were not supplied"); @@ -3371,12 +3434,114 @@ sub enable_firewall_port { } my $computer_node_name = $self->data->get_computer_node_name(); + my $mn_private_ip = $self->mn_os->get_private_ip_address(); $protocol = lc($protocol); - my $command = "/sbin/iptables -I INPUT 1 -m state --state NEW,RELATED,ESTABLISHED -m $protocol -p $protocol --dport $port -j ACCEPT"; + $scope_argument = '' if (!defined($scope_argument)); + + $name = '' if !$name; + $description = '' if !$description; + + my $scope; + + my $INPUT_CHAIN = "INPUT"; + + + my $firewall_configuration = $self->get_firewall_configuration() || return; + my $chain; + my $iptables_del_cmd; + + for my $num (sort keys %{$firewall_configuration->{$INPUT_CHAIN}} ) { + my $existing_scope = $firewall_configuration->{$INPUT_CHAIN}{$num}{$protocol}{$port}{scope} || ''; + my $existing_name = $firewall_configuration->{$INPUT_CHAIN}{$num}{$protocol}{$port}{name} || ''; + my $existing_description = $firewall_configuration->{$INPUT_CHAIN}{$num}{$protocol}{$port}{name} || ''; + + if ($existing_scope) { + notify($ERRORS{'DEBUG'}, 0, " num= $num protocol= $protocol port= $port existing_scope= $existing_scope existing_name= $existing_name existing_description= $existing_description "); + + if ($overwrite_existing) { + $scope = $self->parse_firewall_scope($scope_argument); + $iptables_del_cmd = "iptables -D $INPUT_CHAIN $num"; + if (!$scope) { + notify($ERRORS{'WARNING'}, 0, "failed to parse firewall scope argument: '$scope_argument'"); + return; + } + + notify($ERRORS{'DEBUG'}, 0, "existing firewall opening on $computer_node_name will be replaced:\n" . + "name: '$existing_name'\n" . + "num: '$num'\n" . + "protocol: $protocol\n" . + "port/type: $port\n" . + "existing scope: '$existing_scope'\n" . + "new scope: $scope\n" . + "overwrite existing rule: " . ($overwrite_existing ? 'yes' : 'no') + ); + } + else { + my $parsed_existing_scope = $self->parse_firewall_scope($existing_scope); + if (!$parsed_existing_scope) { + notify($ERRORS{'WARNING'}, 0, "failed to parse existing firewall scope: '$existing_scope'"); + return; + } + + $scope = $self->parse_firewall_scope("$scope_argument,$existing_scope"); + if (!$scope) { + notify($ERRORS{'WARNING'}, 0, "failed to parse firewall scope argument appended with existing scope: '$scope_argument,$existing_scope'"); + return; + } + + if ($scope eq $parsed_existing_scope) { + notify($ERRORS{'DEBUG'}, 0, "firewall is already open on $computer_node_name, existing scope matches scope argument:\n" . + "name: '$existing_name'\n" . + "protocol: $protocol\n" . + "port/type: $port\n" . + "scope: $scope\n" . + "overwrite existing rule: " . ($overwrite_existing ? 'yes' : 'no') + ); + return 1; + } + } + } + else { + next; + } + } + + if(!$scope) { + $scope = $self->parse_firewall_scope($scope_argument); + if (!$scope) { + notify($ERRORS{'WARNING'}, 0, "failed to parse firewall scope argument: '$scope_argument'"); + return; + } + } + + + $name = "VCL: allow $protocol/$port from $scope" if !$name; + + $name = substr($name, 0, 60) . "..." if length($name) > 60; + + my $command; + + if ($iptables_del_cmd ){ + $command = "$iptables_del_cmd ; "; + + } + + $command .= "/sbin/iptables -I INPUT 1 -m state --state NEW,RELATED,ESTABLISHED -m $protocol -p $protocol -j ACCEPT"; + + if ($port =~ /\d+/){ + $command .= " --dport $port"; + } + + if ($scope_argument) { + # if($scope_argument eq '0.0.0.0') { + # $scope_argument .= "/0"; + # } + # else { + # $scope_argument .= "/24"; + # } - if ($scope_argument) { $command .= " -s $scope_argument"; } @@ -3426,36 +3591,61 @@ sub disable_firewall_port { return; } - my $port = shift; - if(!$port) { - notify($ERRORS{'CRITICAL'}, 0, "Input variable port was not passed in as an argument"); - return 0; + # Check to see if this distro has iptables + # If not return 1 so it does not fail + if (!($self->service_exists("iptables"))) { + notify($ERRORS{'WARNING'}, 0, "iptables does not exist on this OS"); + return 1; } + + my ($protocol, $port, $scope_argument, $overwrite_existing, $name, $description) = @_; + if (!defined($protocol) || !defined($port)) { + notify($ERRORS{'WARNING'}, 0, "protocol and port arguments were not supplied"); + return; + } my $computer_node_name = $self->data->get_computer_node_name(); - my $remote_ip = $self->data->get_reservation_remote_ip(); + my $mn_private_ip = $self->mn_os->get_private_ip_address(); - my $command = "sed -i -e '/.*-p tcp --dport $port -j ACCEPT$/d' /etc/sysconfig/iptables"; - my ($status, $output) = $self->execute($command); + $protocol = lc($protocol); - if (defined $status && $status == 0) { - notify($ERRORS{'DEBUG'}, 0, "executed command $command on $computer_node_name"); - } - else { - notify($ERRORS{'WARNING'}, 0, "output from iptables:" . join("\n", @$output)); - } - - #restart iptables - $command = "/etc/init.d/iptables restart"; - my ($status_iptables,$output_iptables) = $self->execute($command); - if (defined $status_iptables && $status_iptables == 0) { - notify($ERRORS{'DEBUG'}, 0, "executed command $command on $computer_node_name"); - } - else { - notify($ERRORS{'WARNING'}, 0, "output from iptables:" . join("\n", @$output_iptables)); - } + $scope_argument = '' if (!defined($scope_argument)); - return 1; + $name = '' if !$name; + $description = '' if !$description; + + my $scope; + + my $INPUT_CHAIN = "INPUT"; + + my $firewall_configuration = $self->get_firewall_configuration() || return; + my $chain; + my $command; + + for my $num (sort keys %{$firewall_configuration->{$INPUT_CHAIN}} ) { + my $existing_scope = $firewall_configuration->{$INPUT_CHAIN}{$num}{$protocol}{$port}{scope} || ''; + my $existing_name = $firewall_configuration->{$INPUT_CHAIN}{$num}{$protocol}{$port}{name} || ''; + if($existing_scope) { + $command = "iptables -D $INPUT_CHAIN $num"; + + notify($ERRORS{'DEBUG'}, 0, "attempting to execute command on $computer_node_name: '$command'"); + my ($status, $output) = $self->execute($command); + if (defined $status && $status == 0) { + notify($ERRORS{'DEBUG'}, 0, "executed command on $computer_node_name: '$command'"); + } + else { + notify($ERRORS{'WARNING'}, 0, "output from iptables:\n" . join("\n", @$output)); + } + + # Save rules to sysconfig/iptables -- incase of reboot + my $iptables_save_cmd = "/sbin/iptables-save > /etc/sysconfig/iptables"; + my ($status_save, $output_save) = $self->execute($iptables_save_cmd); + if (defined $status_save && $status_save == 0) { + notify($ERRORS{'DEBUG'}, 0, "executed command $iptables_save_cmd on $computer_node_name"); + } + } + } + return 1; } @@ -3557,9 +3747,486 @@ sub generate_vclcontrol_sample_files { return 1; } -##///////////////////////////////////////////////////////////////////////////// +=head2 get_firewall_configuration + + Parameters : none + Returns : hash reference + Description : Retrieves information about the open firewall ports on the + computer and constructs a hash. The hash keys are protocol names. + Each protocol key contains a hash reference. The keys are either + port numbers or ICMP types. + Example: + + "ICMP" => { + 8 => { + "description" => "Allow inbound echo request" + } + }, + "TCP" => { + 22 => { + "interface_names" => [ + "Local Area Connection 3" + ], + "name" => "sshd" + }, + 3389 => { + "name" => "Remote Desktop", + "scope" => "192.168.53.54/255.255.255.255" + }, + +=cut + +sub get_firewall_configuration { + my $self = shift; + if (ref($self) !~ /linux/i) { + notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); + return; + } + + my $computer_node_name = $self->data->get_computer_node_name(); + my $firewall_configuration = {}; + + # Check to see if this distro has iptables + # If not return 1 so it does not fail + if (!($self->service_exists("iptables"))) { + notify($ERRORS{'WARNING'}, 0, "iptables does not exist on this OS"); + return 1; + } + + my $port_command = "iptables -L --line-number -n"; + my ($iptables_exit_status, $output_iptables) = $self->execute($port_command); + if (!defined($output_iptables)) { + notify($ERRORS{'WARNING'}, 0, "failed to run command to show open firewall ports on $computer_node_name"); + return; + } + + #notify($ERRORS{'DEBUG'}, 0, "output from iptables:\n" . join("\n", @$output_iptables)); + + + # Execute the iptables -L --line-number -n command to retrieve firewall port openings + # Expected output: + #Chain INPUT (policy ACCEPT 0 packets, 0 bytes) + #num target prot opt source destination + #1 RH-Firewall-1-INPUT all -- 0.0.0.0/0 0.0.0.0/0 + + #Chain FORWARD (policy ACCEPT) + #num target prot opt source destination + #1 RH-Firewall-1-INPUT all -- 0.0.0.0/0 0.0.0.0/0 + + #Chain OUTPUT (policy ACCEPT) + #num target prot opt source destination + + #Chain RH-Firewall-1-INPUT (2 references) + #num target prot opt source destination + #1 ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 + #2 ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 + #3 ACCEPT icmp -- 0.0.0.0/0 0.0.0.0/0 icmp type 255 + #4 ACCEPT esp -- 0.0.0.0/0 0.0.0.0/0 + #5 ACCEPT ah -- 0.0.0.0/0 0.0.0.0/0 + #6 ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED + #7 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:22 + #8 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:3389 + #9 REJECT all -- 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited + + + my $chain; + my $previous_protocol; + my $previous_port; + + for my $line (@$output_iptables) { + if ($line =~ /^Chain\s+(\S+)\s+(.*)/ig) { + $chain = $1; + notify($ERRORS{'DEBUG'}, 0, "output Chain = $chain"); + } + elsif($line =~ /^(\d+)\s+([A-Z]*)\s+([a-z]*)\s+(--)\s+(\S+)\s+(\S+)\s+(.*)/ig ) { + + my $num = $1; + my $target = $2; + my $protocol = $3; + my $scope = $5; + my $destination =$6; + my $port_string = $7 if (defined($7)); + my $port = ''; + my $name; + + + if (defined($port_string) && ($port_string =~ /([\s(a-zA-Z)]*)(dpt:)(\d+)/ig )){ + $port = $3; + notify($ERRORS{'DEBUG'}, 0, "output rule: $num, $target, $protocol, $scope, $destination, $port "); + } + + if (!$port) { + $port = "any"; + } + + my $services_cmd = "cat /etc/services"; + my ($services_status, $service_output) = $self->execute($services_cmd); + if (!defined($service_output)) { + notify($ERRORS{'DEBUG'}, 0, "failed to get /etc/services"); + } + else { + for my $sline (@$service_output) { + if ( $sline =~ /(^[_-a-zA-Z1-9]+)\s+($port\/$protocol)\s+(.*) /ig ){ + $name = $1; + } + } + + } + + $name = $port if (!$name); + + $firewall_configuration->{$chain}->{$num}{$protocol}{$port}{name}= $name; + $firewall_configuration->{$chain}->{$num}{$protocol}{$port}{number}= $num; + $firewall_configuration->{$chain}->{$num}{$protocol}{$port}{scope}= $scope; + $firewall_configuration->{$chain}->{$num}{$protocol}{$port}{target}= $target; + $firewall_configuration->{$chain}->{$num}{$protocol}{$port}{destination}= $destination; + + + if (!defined($previous_protocol) || + !defined($previous_port) || + !defined($firewall_configuration->{$previous_protocol}) || + !defined($firewall_configuration->{$previous_protocol}{$previous_port}) + ) { + next; + } + elsif ($scope !~ /0.0.0.0\/0/) { + $firewall_configuration->{$previous_protocol}{$previous_port}{scope} = $scope; + } + } + } + + notify($ERRORS{'DEBUG'}, 0, "retrieved firewall configuration from $computer_node_name:\n" . format_data($firewall_configuration)); + return $firewall_configuration; + + +} + +#///////////////////////////////////////////////////////////////////////////// + +=head2 parse_firewall_scope + + Parameters : @scope_strings + Returns : string + Description : Parses an array of firewall scope strings and collpases them into + a simplified scope if possible. A comma-separated string is + returned. The scope string argument may be in the form: + -192.168.53.54/255.255.255.192 + -192.168.53.54/24 + -192.168.53.54 + -* + -Any + -LocalSubnet + +=cut + +sub parse_firewall_scope { + my $self = shift; + if (ref($self) !~ /linux/i) { + notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); + return; + } + + my @scope_strings = @_; + if (!@scope_strings) { + notify($ERRORS{'WARNING'}, 0, "scope array argument was not supplied"); + return; + } + + my @netmask_objects; + + for my $scope_string (@scope_strings) { + if ($scope_string =~ /(\*|Any)/i) { + my $netmask_object = new Net::Netmask('any'); + push @netmask_objects, $netmask_object; + } + + elsif ($scope_string =~ /LocalSubnet/i) { + my $network_configuration = $self->get_network_configuration() || return; + + for my $interface_name (sort keys %$network_configuration) { + for my $ip_address (keys %{$network_configuration->{$interface_name}{ip_address}}) { + my $subnet_mask = $network_configuration->{$interface_name}{ip_address}{$ip_address}; + + my $netmask_object_1 = new Net::Netmask("$ip_address/$subnet_mask"); + if ($netmask_object_1) { + push @netmask_objects, $netmask_object_1; + } + else { + notify($ERRORS{'WARNING'}, 0, "failed to create Net::Netmask object, IP address: $ip_address, subnet mask: $subnet_mask"); + return; + } + } + } + } + + elsif (my @scope_sections = split(/,/, $scope_string)) { + for my $scope_section (@scope_sections) { + + if (my ($start_address, $end_address) = $scope_section =~ /^([\d\.]+)-([\d\.]+)$/) { + my @netmask_range_objects = Net::Netmask::range2cidrlist($start_address, $end_address); + if (@netmask_range_objects) { + push @netmask_objects, @netmask_range_objects; + } + else { + notify($ERRORS{'WARNING'}, 0, "failed to call Net::Netmask::range2cidrlist to create an array of objects covering IP range: $start_address-$end_address"); + return; + } + } + + elsif (my ($ip_address, $subnet_mask) = $scope_section =~ /^([\d\.]+)\/([\d\.]+)$/) { + my $netmask_object = new Net::Netmask("$ip_address/$subnet_mask"); + if ($netmask_object) { + push @netmask_objects, $netmask_object; + } + else { + notify($ERRORS{'WARNING'}, 0, "failed to create Net::Netmask object, IP address: $ip_address, subnet mask: $subnet_mask"); + return; + } + } + + elsif (($ip_address) = $scope_section =~ /^([\d\.]+)$/) { + my $netmask_object = new Net::Netmask("$ip_address"); + if ($netmask_object) { + push @netmask_objects, $netmask_object; + } + else { + notify($ERRORS{'WARNING'}, 0, "failed to create Net::Netmask object, IP address: $ip_address"); + return; + } + } + + else { + notify($ERRORS{'WARNING'}, 0, "unable to parse '$scope_section' section of scope: '$scope_string'"); + return; + } + } + } + + else { + notify($ERRORS{'WARNING'}, 0, "unexpected scope format: '$scope_string'"); + return + } + } + + my @netmask_objects_collapsed = cidrs2cidrs(@netmask_objects); + if (@netmask_objects_collapsed) { + my $scope_result_string; + my @ip_address_ranges; + for my $netmask_object (@netmask_objects_collapsed) { + + if ($netmask_object->first() eq $netmask_object->last()) { + push @ip_address_ranges, $netmask_object->first(); + $scope_result_string .= $netmask_object->base() . ","; + } + else { + push @ip_address_ranges, $netmask_object->first() . "-" . $netmask_object->last(); + $scope_result_string .= $netmask_object->base() . "/" . $netmask_object->mask() . ","; + } + } + + $scope_result_string =~ s/,+$//; + my $argument_string = join(",", @scope_strings); + if ($argument_string ne $scope_result_string) { + notify($ERRORS{'DEBUG'}, 0, "parsed firewall scope:\n" . + "argument: '$argument_string'\n" . + "result: '$scope_result_string'\n" . + "IP address ranges:\n" . join(", ", @ip_address_ranges) + ); + } + return $scope_result_string; + } + else { + notify($ERRORS{'WARNING'}, 0, "failed to parse firewall scope: '" . join(",", @scope_strings) . "', no Net::Netmask objects were created"); + return; + } +} + + +#///////////////////////////////////////////////////////////////////////////// + +=head2 firewall_compare_update + + Parameters : @scope_strings + Returns : 0 , 1 + Description : Compare iptables for listed remote IP address in reservation + +=cut + +sub firewall_compare_update { + my $self = shift; + if (ref($self) !~ /linux/i) { + notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); + return; + } + + # Check to see if this distro has iptables + # If not return 1 so it does not fail + if (!($self->service_exists("iptables"))) { + notify($ERRORS{'WARNING'}, 0, "iptables does not exist on this OS"); + return 1; + } + + my $computer_node_name = $self->data->get_computer_node_name(); + my $imagerevision_id = $self->data->get_imagerevision_id(); + my $remote_ip = $self->data->get_reservation_remote_ip(); + + #collect connection_methods + #collect firewall_config + #For each port defined in connection_methods + #compare rule source address with remote_IP address + + # Retrieve the connect method info hash + my $connect_method_info = get_connect_method_info($imagerevision_id); + if (!$connect_method_info) { + notify($ERRORS{'WARNING'}, 0, "no connect methods are configured for image revision $imagerevision_id"); + return; + } + + # Retrieve the firewall configuration + my $INPUT_CHAIN = "INPUT"; + my $firewall_configuration = $self->get_firewall_configuration() || return; + + for my $connect_method_id (sort keys %{$connect_method_info} ) { + + my $name = $connect_method_info->{$connect_method_id}{name}; + my $description = $connect_method_info->{$connect_method_id}{description}; + my $protocol = $connect_method_info->{$connect_method_id}{protocol} || 'TCP'; + my $port = $connect_method_info->{$connect_method_id}{port}; + my $scope; + + $protocol = lc($protocol); + + for my $num (sort keys %{$firewall_configuration->{$INPUT_CHAIN}} ) { + my $existing_scope = $firewall_configuration->{$INPUT_CHAIN}{$num}{$protocol}{$port}{scope} || ''; + if(!$existing_scope ) { + } + else { + my $parsed_existing_scope = $self->parse_firewall_scope($existing_scope); + if (!$parsed_existing_scope) { + notify($ERRORS{'WARNING'}, 0, "failed to parse existing firewall scope: '$existing_scope'"); + return; + } + $scope = $self->parse_firewall_scope("$remote_ip,$existing_scope"); + if (!$scope) { + notify($ERRORS{'WARNING'}, 0, "failed to parse firewall scope argument appended with existing scope: '$remote_ip,$existing_scope'"); + return; + } + + if ($scope eq $parsed_existing_scope) { + notify($ERRORS{'DEBUG'}, 0, "firewall is already open on $computer_node_name, existing scope matches scope argument:\n" . + "name: '$name'\n" . + "protocol: $protocol\n" . + "port/type: $port\n" . + "scope: $scope\n"); + return 1; + } + else { + if ($self->enable_firewall_port($protocol, $port, "$remote_ip/24", 0)) { + notify($ERRORS{'OK'}, 0, "opened firewall port $port on $computer_node_name for $remote_ip $name connect method"); + } + } + + + } + } + } + + return 1; + +} + +#///////////////////////////////////////////////////////////////////////////// + +=head2 clean_iptables + + Parameters : + Returns : 0 , 1 + Description : Deletes rules with any leftover -s addresses + +=cut + +sub clean_iptables { + my $self = shift; + if (ref($self) !~ /linux/i) { + notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); + return; + } + + # Check to see if this distro has iptables + # If not return 1 so it does not fail + if (!($self->service_exists("iptables"))) { + notify($ERRORS{'WARNING'}, 0, "iptables does not exist on this OS"); + return 1; + } + + my $computer_node_name = $self->data->get_computer_node_name(); + my $reservation_id = $self->data->get_reservation_id(); + my $management_node_keys = $self->data->get_management_node_keys(); + + # Retrieve the firewall configuration + my $INPUT_CHAIN = "INPUT"; + + # Retrieve the iptables file to work on locally + my $tmpfile = "/tmp/" . $reservation_id . "_iptables"; + my $source_file_path = "/etc/sysconfig/iptables"; + if (run_scp_command("$computer_node_name:\"$source_file_path\"", $tmpfile, $management_node_keys)) { + my @lines; + if(open(IPTAB_TMPFILE, $tmpfile)){ + @lines = <IPTAB_TMPFILE>; + close(IPTAB_TMPFILE); + } + foreach my $line (@lines){ + if ($line =~ s/-A INPUT -s .*\n//) { + } + } + + #Rewrite array to tmpfile + if(open(IPTAB_TMPFILE, ">$tmpfile")){ + print IPTAB_TMPFILE @lines; + close (IPTAB_TMPFILE); + } + + # Copy iptables file back to node + if (run_scp_command($tmpfile, "$computer_node_name:\"$source_file_path\"", $management_node_keys)) { + notify($ERRORS{'DEBUG'}, 0, "copied $tmpfile to $computer_node_name $source_file_path"); + } + } + + + #my $command = "sed -i -e '/-A INPUT -s */d' /etc/sysconfig/iptables"; + #my ($status, $output) = $self->execute($command); + + #if (defined $status && $status == 0) { + # notify($ERRORS{'DEBUG'}, 0, "executed command $command on $computer_node_name"); + #} + #else { + # notify($ERRORS{'WARNING'}, 0, "output from iptables:" . join("\n", @$output)); + #} + + #restart iptables + my $command = "/etc/init.d/iptables restart"; + my ($status_iptables,$output_iptables) = $self->execute($command); + if (defined $status_iptables && $status_iptables == 0) { + notify($ERRORS{'DEBUG'}, 0, "executed command $command on $computer_node_name"); + } + else { + notify($ERRORS{'WARNING'}, 0, "output from iptables:" . join("\n", @$output_iptables)); + } + + if ($self->wait_for_ssh(0)) { + return 1; + } + else { + notify($ERRORS{'CRITICAL'}, 0, "not able to login via ssh after cleaning_iptables"); + return 0; + } + +} + + +##///////////////////////////////////////////////////////////////////////////// 1; __END__ Modified: incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm URL: http://svn.apache.org/viewvc/incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm?rev=1201909&r1=1201908&r2=1201909&view=diff ============================================================================== --- incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm (original) +++ incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm Mon Nov 14 21:15:16 2011 @@ -947,7 +947,7 @@ sub grant_access { # Set the $remote_ip_range variable to the string 'all' if it isn't already set (for display purposes) $remote_ip_range = 'all' if !$remote_ip_range; - if($self->process_connect_methods('start') ){ + if($self->process_connect_methods("0.0.0.0", 1) ){ notify($ERRORS{'OK'}, 0, "processed connection methods on $computer_node_name"); } Modified: incubator/vcl/trunk/managementnode/lib/VCL/inuse.pm URL: http://svn.apache.org/viewvc/incubator/vcl/trunk/managementnode/lib/VCL/inuse.pm?rev=1201909&r1=1201908&r2=1201909&view=diff ============================================================================== --- incubator/vcl/trunk/managementnode/lib/VCL/inuse.pm (original) +++ incubator/vcl/trunk/managementnode/lib/VCL/inuse.pm Mon Nov 14 21:15:16 2011 @@ -187,6 +187,12 @@ sub process { # Is this a poll or end time if ($request_checktime eq "poll") { notify($ERRORS{'OK'}, 0, "beginning to poll"); + + if ($self->os->can('firewall_compare_update')) { + if ($self->os->firewall_compare_update()) { + notify($ERRORS{'OK'}, 0, "confirmed firewall scope has been updated"); + } + } if ($image_os_type =~ /windows/) { if (firewall_compare_update($computer_nodename, $reservation_remoteip, $identity_key, $image_os_type)) { Modified: incubator/vcl/trunk/managementnode/lib/VCL/reserved.pm URL: http://svn.apache.org/viewvc/incubator/vcl/trunk/managementnode/lib/VCL/reserved.pm?rev=1201909&r1=1201908&r2=1201909&view=diff ============================================================================== --- incubator/vcl/trunk/managementnode/lib/VCL/reserved.pm (original) +++ incubator/vcl/trunk/managementnode/lib/VCL/reserved.pm Mon Nov 14 21:15:16 2011 @@ -336,6 +336,10 @@ sub process { notify($ERRORS{'OK'}, 0, "$remote_ip connected to $nodename"); insertloadlog($reservation_id, $computer_id, "connected", "reserved: user connected to remote machine"); + + if($self->os->process_connect_methods($remote_ip, 1)) { + notify($ERRORS{'OK'}, 0, "process_connect_methods return successfully $remote_ip $nodename"); + } # Update the request state to either inuse or imageinuse if (update_request_state($request_id, "inuse", "reserved")) { @@ -368,6 +372,10 @@ sub process { elsif ($retval_conn eq "conn_wrong_ip") { # does the same as above, until we make a firm decision as to how to handle this + if($self->os->process_connect_methods($remote_ip, 1)) { + notify($ERRORS{'OK'}, 0, "process_connect_methods return successfully $remote_ip $nodename"); + } + # Update the request state to inuse if (update_request_state($request_id, "inuse", "reserved")) { notify($ERRORS{'OK'}, 0, "setting request into inuse state"); @@ -391,6 +399,7 @@ sub process { else { notify($ERRORS{'CRITICAL'}, 0, "unable to update lastcheck time for reservation $reservation_id"); } + notify($ERRORS{'OK'}, 0, "exiting"); exit;