[LON-CAPA-cvs] cvs: loncom /configuration Firewall.pm

raeburn raeburn@source.lon-capa.org
Thu, 25 Mar 2010 01:47:45 -0000


This is a MIME encoded message

--raeburn1269481665
Content-Type: text/plain

raeburn		Thu Mar 25 01:47:45 2010 EDT

  Modified files:              
    /loncom/configuration	Firewall.pm 
  Log:
  - get_fw_chain() renamed get_fw_chains()
    - return array of chains in use
  - Reference to this array of chains is second arg for:
      &firewall_close_port() and &firewall_open_port()
  - Replace backticks with opened pipe to extract information about chains.
  - Streamline code in firewall_is_port_open() to eliminate loop for each 
    iptables line. 
  - When using &firewall_open_port() check if entry already exists for specific IP for LON-CAPA port before calling system command to add one.
  
  
--raeburn1269481665
Content-Type: text/plain
Content-Disposition: attachment; filename="raeburn-20100325014745.txt"

Index: loncom/configuration/Firewall.pm
diff -u loncom/configuration/Firewall.pm:1.5 loncom/configuration/Firewall.pm:1.6
--- loncom/configuration/Firewall.pm:1.5	Fri Jul 17 00:15:49 2009
+++ loncom/configuration/Firewall.pm	Thu Mar 25 01:47:45 2010
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # Firewall configuration to allow internal LON-CAPA communication between servers   
 #
-# $Id: Firewall.pm,v 1.5 2009/07/17 00:15:49 raeburn Exp $
+# $Id: Firewall.pm,v 1.6 2010/03/25 01:47:45 raeburn Exp $
 #
 # The LearningOnline Network with CAPA
 #
@@ -36,23 +36,21 @@
 use lib '/home/httpd/perl/lib';
 use LONCAPA::Configuration;
 
-# Firewall code is based on the code in FC2 /etc/init.d/ntpd
-
 sub firewall_open_port {
-    my ($iptables,$fw_chain,$lond_port,$iphost,$ports) = @_;
+    my ($iptables,$fw_chains,$lond_port,$iphost,$ports) = @_;
     return 'inactive firewall' if (!&firewall_is_active());
     return 'port number unknown' if !$lond_port;
-    my @opened;
-    if (! `$iptables -L -n 2>/dev/null | grep $fw_chain | wc -l`) {
-        return 'Expected chain "'.$fw_chain.'" missing from iptables'."\n";
+    return 'invalid firewall chain' unless (ref($fw_chains) eq 'ARRAY');
+    my (@opened,@chains,@badchains,@okchains);
+    foreach my $chain (@{$fw_chains}) {
+        if ($chain =~ /^([\w\-]+)$/) {
+            push(@okchains,$1);
+        } else {
+            push(@badchains,$chain);
+        }
     }
-    #
-    # iptables is running with expected chain
-    #
-    if ($fw_chain =~ /^([\w\-]+)$/) {
-        $fw_chain = $1;
-    } else {
-        return 'Chain name has unexpected format'."\n";
+    if (!@okchains) {
+        return 'None of the chain names has the expected format'."\n";
     }
     if (ref($ports) ne 'ARRAY') {
         return 'List of ports to open needed.';
@@ -70,10 +68,15 @@
         if ($port eq $lond_port) {
             # For lond port, restrict the servers allowed to attempt to communicate
             # to include only source IPs in the LON-CAPA cluster.
-            my (@port_error,@command_error,@lond_port_open);
+            my (@port_error,%command_error,@lond_port_open,
+                @lond_port_curropen);
             if (ref($iphost) eq 'HASH') {
-                if (keys(%{$iphost}) > 0) { 
-                    &firewall_close_anywhere($iptables,$fw_chain,$port);
+                if (keys(%{$iphost}) > 0) {
+                    my %curropen;
+                    foreach my $fw_chain (@okchains) {
+                        &firewall_close_anywhere($iptables,$fw_chain,$port);
+                        my $current = &firewall_is_port_open($iptables,$fw_chain,$port,$lond_port,$iphost,\%curropen);
+                    }
                     foreach my $key (keys(%{$iphost})) {
                         my $ip = '';
                         if ($key =~ /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/) {
@@ -85,46 +88,82 @@
                         } else {
                             next;
                         }
-                        my $firewall_command =
-                            "$iptables -I $fw_chain -p tcp -s $ip -d 0/0 --dport $port -j ACCEPT";
-                        system($firewall_command);
-                        my $return_status = $?>>8;
-                        if ($return_status == 1) {
-                            push (@port_error,$ip);
-                        } elsif ($return_status == 2) {
-                            push(@command_error,$ip);
-                        } elsif ($return_status == 0) {
-                            push(@lond_port_open,$ip);
+                        if ($curropen{$ip}) {
+                            push(@lond_port_curropen,$ip);
+                        } else {
+                            foreach my $fw_chain (@okchains) {
+                                my $firewall_command =
+                                    "$iptables -I $fw_chain -p tcp -s $ip -d 0/0 --dport $port -j ACCEPT";
+                                system($firewall_command);
+                                my $return_status = $?>>8;
+                                if ($return_status == 1) {
+                                    unless(grep(/^\Q$ip\E$/,@port_error)) {
+                                        push (@port_error,$ip);
+                                    }
+                                } elsif ($return_status == 2) {
+                                    push(@{$command_error{$fw_chain}},$ip);
+                                } elsif ($return_status == 0) {
+                                    push(@lond_port_open,$ip);
+                                    last;
+                                }
+                            }
                         }
                     }
                 }
             }
+            if (@lond_port_curropen) {
+                unless (grep(/^\Q$port\E$/,@opened)) {
+                    push(@opened,$port);
+                }
+                print "Port already open for ".scalar(@lond_port_curropen)." IP addresses\n";
+            }
             if (@lond_port_open) {
-                push(@opened,$port);
-                print "Port $port opened for ".scalar(@lond_port_open)." IP addresses\n";
+                unless (grep(/^\Q$port\E$/,@opened)) {   
+                    push(@opened,$port);
+                }
+                print "Port opened for ".scalar(@lond_port_open)." IP addresses\n";
             }
             if (@port_error) {
-                print "Error opening port $port for following IP addresses: ".join(', ',@port_error)."\n";
+                print "Error opening port for following IP addresses: ".join(', ',@port_error)."\n";
             }
-            if (@command_error) {
-                print "Bad command error opening port for following IP addresses: ".
-                      join(', ',@command_error)."\n".
-                      'Command was: "'."$iptables -I $fw_chain -p tcp -s ".'$ip'." -d 0/0 --dport $port -j ACCEPT".'", where $ip is IP address'."\n";
+            if (keys(%command_error) > 0) {
+                foreach my $chain (sort(keys(%command_error))) {
+                    if (ref($command_error{$chain}) eq 'ARRAY') {
+                        if (@{$command_error{$chain}}) {
+                            print "Bad command error opening port for following IP addresses: ".
+                                  join(', ',@{$command_error{$chain}})."\n".
+                                 'Command was: "'."$iptables -I $chain -p tcp -s ".'$ip'." -d 0/0 --dport $port -j ACCEPT".'", where $ip is IP address'."\n";
+                        }
+                    }
+                }
             }
         } else {
-            my $firewall_command =
-               "$iptables -I $fw_chain -p tcp -d 0/0 --dport $port -j ACCEPT";
-            system($firewall_command);
-            my $return_status = $?>>8;
-            if ($return_status == 1) {
-                # Error
-                print "Error opening port.\n";
-            } elsif ($return_status == 2) {
-                # Bad command
-                print "Bad command error opening port.  Command was\n".
-                      "  ".$firewall_command."\n";
-            } elsif ($return_status == 0) {
-                push(@opened,$port);
+            my (@port_errors,%command_errors);
+            foreach my $fw_chain (@okchains) {
+                my $firewall_command =
+                    "$iptables -I $fw_chain -p tcp -d 0/0 --dport $port -j ACCEPT";
+                system($firewall_command);
+                my $return_status = $?>>8;
+                if ($return_status == 1) {
+                    push(@port_errors,$fw_chain);
+                } elsif ($return_status == 2) {
+                    $command_errors{$fw_chain} = $firewall_command;
+                } elsif ($return_status == 0) {
+                    push(@opened,$port);
+                    last;
+                }
+            }
+            unless (grep(/^\Q$port\E$/,@opened)) {
+                if (@port_errors) {
+                    print "Error opening port for chains: ".
+                          join(', ',@port_errors).".\n";
+                }
+                if (keys(%command_errors)) {
+                    foreach my $fw_chain (sort(keys(%command_errors))) {
+                        print "Bad command error opening port for chain: $fw_chain.  Command was\n".
+                          "  ".$command_errors{$fw_chain}."\n";
+                    }
+                }
             }
         }
     }
@@ -137,33 +176,37 @@
 }
 
 sub firewall_is_port_open {
-    my ($iptables,$fw_chain,$port,$lond_port,$iphost) = @_;
+    my ($iptables,$fw_chain,$port,$lond_port,$iphost,$curropen) = @_;
     # for lond port returns number of source IPs for which firewall port is open
     # for other ports returns 1 if the firewall port is open, 0 if not.
     #
     # check if firewall is active or installed
     return if (! &firewall_is_active());
-    if ($port eq $lond_port) {
-        my $count ++;
-        if (ref($iphost) eq 'HASH') {
-            if (keys(%{$iphost}) > 0) {
-                foreach my $ip (keys(%{$iphost})) {
-                    open(PIPE,"$iptables -L $fw_chain -n 2>/dev/null");
-                    while(<PIPE>) {
-                        $count++ if (/^ACCEPT\s+tcp\s+\-{2}\s+\Q$ip\E\s+/);
+    my $count = 0;
+    if (open(PIPE,"$iptables -L $fw_chain -n 2>/dev/null |")) {
+        while(<PIPE>) {
+            if ($port eq $lond_port) {
+                if (ref($iphost) eq 'HASH') {
+                    if (/^ACCEPT\s+tcp\s+\-{2}\s+([\S]+)\s+/) {
+                        my $ip = $1;
+                        if ($iphost->{$ip}) {
+                            $count ++;
+                            if (ref($curropen) eq 'HASH') {
+                                $curropen->{$ip} ++;
+                            }
+                        }
                     }
-                    close(PIPE);
+                }
+            } else {
+                if (/tcp dpt\:\Q$port\E/) {
+                    $count ++;
+                    last;
                 }
             }
         }
-        return $count;
-    } else {
-        if (`$iptables -L -n 2>/dev/null | grep "tcp dpt:$port"`) {
-            return 1;
-        } else {
-            return 0;
-        }
+        close(PIPE);
     }
+    return $count;
 }
 
 sub firewall_is_active {
@@ -175,20 +218,24 @@
 }
 
 sub firewall_close_port {
-    my ($iptables,$fw_chain,$lond_port,$ports) = @_;
+    my ($iptables,$fw_chains,$lond_port,$ports) = @_;
     return 'inactive firewall' if (!&firewall_is_active());
     return 'port number unknown' if !$lond_port;
-    if (! `$iptables -L -n 2>/dev/null | grep $fw_chain | wc -l`) {
-        return 'Expected chain "'.$fw_chain.'" missing from iptables'."\n";
+    return 'invalid firewall chain' unless (ref($fw_chains) eq 'ARRAY');
+    my (@opened,@chains,@badchains,@okchains);
+    foreach my $chain (@{$fw_chains}) {
+        if ($chain =~ /^([\w\-]+)$/) {
+            push(@okchains,$1);
+        } else {
+            push(@badchains,$chain);
+        }
+    }
+    if (!@okchains) {
+        return 'None of the chain names has the expected format'."\n";
     }
     if (ref($ports) ne 'ARRAY') {
         return 'List of ports to close needed.';
     }
-    if ($fw_chain =~ /^([\w\-]+)$/) {
-        $fw_chain = $1;
-    } else {
-        return 'Chain name has unexpected format'."\n";
-    }
     foreach my $portnum (@{$ports}) {
         my $port = '';
         if ($portnum =~ /^(\d+)$/) {
@@ -199,58 +246,75 @@
         }
         print "Closing firewall access on port $port\n";
         if (($port ne '') && ($port eq $lond_port)) {
-            my (@port_error,@command_error,@lond_port_close);
-            my %to_close;
-            open(PIPE, "$iptables -n -L $fw_chain |");
-            while (<PIPE>) {
-                chomp();
-                next unless (/dpt:\Q$port\E\s*$/);
-                if (/^ACCEPT\s+tcp\s+\-{2}\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+/) {
-                    $to_close{$1} = $port;
+            foreach my $fw_chain (@okchains) {
+                my (@port_error,@command_error,@lond_port_close);
+                my %to_close;
+                if (open(PIPE, "$iptables -n -L $fw_chain |")) {
+                    while (<PIPE>) {
+                        chomp();
+                        next unless (/dpt:\Q$port\E\s*$/);
+                        if (/^ACCEPT\s+tcp\s+\-{2}\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+/) {
+                            $to_close{$1} = $port;
+                        }
+                    }
+                    close(PIPE);
+                }
+                if (keys(%to_close) > 0) {
+                    foreach my $ip (keys(%to_close)) {
+                        my $firewall_command =
+                            "$iptables -D $fw_chain -p tcp -s $ip -d 0/0 --dport $port -j ACCEPT";
+                        system($firewall_command);
+                        my $return_status = $?>>8;
+                        if ($return_status == 1) {
+                            push (@port_error,$ip);
+                        } elsif ($return_status == 2) {
+                            push(@command_error,$ip);
+                        } elsif ($return_status == 0) {
+                            push(@lond_port_close,$ip);
+                        }
+                    }
+                }
+                if (@lond_port_close) {
+                    print "Port closed for ".scalar(@lond_port_close)." IP addresses\n";
+                }
+                if (@port_error) {
+                    print "Error closing port for following IP addresses: ".join(', ',@port_error)."\n";
+                }
+                if (@command_error) {
+                    print "Bad command error opening port for following IP addresses: ".
+                          join(', ',@command_error)."\n".
+                          'Command was: "'."$iptables -D $fw_chain -p tcp -s ".'$ip'." -d 0/0 --dport $port -j ACCEPT".'", where $ip is IP address'."\n";
                 }
             }
-            close(PIPE);
-            if (keys(%to_close) > 0) {
-                foreach my $ip (keys(%to_close)) {
+        } else {
+            foreach my $fw_chain (@okchains) {
+                my (@port_error,@command_error,@lond_port_close);
+                my $to_close;
+                if (open(PIPE, "$iptables -n -L $fw_chain |")) {
+                    while (<PIPE>) {
+                        chomp();
+                        next unless (/dpt:\Q$port\E\s*$/);
+                        $to_close = 1;
+                    }
+                    close(PIPE);
+                }
+                if ($to_close) {
                     my $firewall_command =
-                        "$iptables -D $fw_chain -p tcp -s $ip -d 0/0 --dport $port -j ACCEPT";
+                        "$iptables -D $fw_chain -p tcp -d 0/0 --dport $port -j ACCEPT";
                     system($firewall_command);
                     my $return_status = $?>>8;
                     if ($return_status == 1) {
-                        push (@port_error,$ip);
+                        # Error
+                        print "Error closing port for chain: $fw_chain.\n";
                     } elsif ($return_status == 2) {
-                        push(@command_error,$ip);
-                    } elsif ($return_status == 0) {
-                        push(@lond_port_close,$ip);
+                        # Bad command
+                        print "Bad command error closing port.  Command was\n".
+                              "  ".$firewall_command."\n";
+                    } else {
+                        print "Port closed for chain $fw_chain.\n";
                     }
                 }
             }
-            if (@lond_port_close) {
-                print "Port $port closed for ".scalar(@lond_port_close)." IP addresses\n";
-            }
-            if (@port_error) {
-                print "Error closing port $port for following IP addresses: ".join(', ',@port_error)."\n";
-            }
-            if (@command_error) {
-                print "Bad command error opening port for following IP addresses: ".
-                      join(', ',@command_error)."\n".
-                      'Command was: "'."$iptables -D $fw_chain -p tcp -s ".'$ip'." -d 0/0 --dport $port -j ACCEPT".'", where $ip is IP address'."\n";
-            }
-        } else {
-            my $firewall_command =
-                "$iptables -D $fw_chain -p tcp -d 0/0 --dport $port -j ACCEPT";
-            system($firewall_command);
-            my $return_status = $?>>8;
-            if ($return_status == 1) {
-                # Error
-                print "Error closing port.\n";
-            } elsif ($return_status == 2) {
-                # Bad command
-                print "Bad command error closing port.  Command was\n".
-                      "  ".$firewall_command."\n";
-            } else {
-                print "Port closed.\n";
-            }
         }
     }
     return;
@@ -258,25 +322,26 @@
 
 sub firewall_close_anywhere {
     my ($iptables,$fw_chain,$port) = @_;
-    open(PIPE, "$iptables --line-numbers -n -L $fw_chain |");
-    while (<PIPE>) {
-        next unless (/dpt:\Q$port\E/);
-        chomp();
-        if (/^(\d+)\s+ACCEPT\s+tcp\s+\-{2}\s+0\.0\.0\.0\/0\s+0\.0\.0\.0\/0/) {
-            my $firewall_command = "$iptables -D $fw_chain $1";
-            system($firewall_command);
-            my $return_status = $?>>8;
-            if ($return_status == 1) {
-                print 'Error closing port '.$port.' for source "anywhere"'."\n";
-            } elsif ($return_status == 2) {
-                print 'Bad command error closing port '.$port.' for source "anywhere".  Command was'."\n".
-                      ' '.$firewall_command."\n";
-            } else {
-                print 'Port '.$port.' closed for source "anywhere"'."\n";
+    if (open(PIPE, "$iptables --line-numbers -n -L $fw_chain |")) {
+        while (<PIPE>) {
+            next unless (/dpt:\Q$port\E/);
+            chomp();
+            if (/^(\d+)\s+ACCEPT\s+tcp\s+\-{2}\s+0\.0\.0\.0\/0\s+0\.0\.0\.0\/0/) {
+                my $firewall_command = "$iptables -D $fw_chain $1";
+                system($firewall_command);
+                my $return_status = $?>>8;
+                if ($return_status == 1) {
+                    print 'Error closing port '.$port.' for source "anywhere"'."\n";
+                } elsif ($return_status == 2) {
+                    print 'Bad command error closing port '.$port.' for source "anywhere".  Command was'."\n".
+                          ' '.$firewall_command."\n";
+                } else {
+                    print 'Port '.$port.' closed for source "anywhere"'."\n";
+                }
             }
         }
+        close(PIPE);
     }
-    close(PIPE);
 }
 
 sub get_lond_port {
@@ -293,28 +358,41 @@
     return $lond_port;
 }
 
-sub get_fw_chain {
+sub get_fw_chains {
     my ($iptables) = @_;
-    my $fw_chain = 'RH-Firewall-1-INPUT';
+    my @fw_chains;
     my $suse_config = "/etc/sysconfig/SuSEfirewall2";
     if (-e $suse_config) {
-        $fw_chain = 'input_ext';
+        push(@fw_chains,'input_ext');
     } else {
         if (!-e '/etc/sysconfig/iptables') {
             if (!-e '/var/lib/iptables') {
                 print("Unable to find iptables file containing static definitions\n");
             }
+            push(@fw_chains,'RH-Firewall-1-INPUT'); 
         }
         if ($iptables eq '') {
             $iptables = &get_pathto_iptables();
         }
-        my $count = `$iptables -L -n 2>/dev/null |grep $fw_chain |wc -l`;
-        chomp($count);
-        if (!$count) {
-            $fw_chain ='INPUT';
+        my %counts;
+        my @posschains = ('RH-Firewall-1-INPUT','INPUT');
+        if (open(PIPE,"$iptables -L -n |")) {
+            while(<PIPE>) {
+                foreach my $chain (@posschains) {
+                    if (/(\Q$chain\E)/) {
+                        $counts{$1} ++;
+                    }
+                }
+            }
+            close(PIPE);
+        }
+        foreach my $fw_chain (@posschains) {
+            if ($counts{$fw_chain}) {
+                push(@fw_chains,$fw_chain);
+            }
         }
     }
-    return $fw_chain;
+    return @fw_chains;
 }
 
 sub get_pathto_iptables {
@@ -362,19 +440,19 @@
 
 =over 4
 
-=item LONCAPA::Firewall::firewall_open_port( $iptables,$fw_chain,$lond_port,$iphost,$port );
+=item LONCAPA::Firewall::firewall_open_port( $iptables,$fw_chains,$lond_port,$iphost,$port );
 
 =back
 
 =over 4
 
-=item LONCAPA::Firewall::firewall_close_port( $iptables,$fw_chain,$lond_port,$ports );
+=item LONCAPA::Firewall::firewall_close_port( $iptables,$fw_chains,$lond_port,$ports );
 
 =back
 
 =over 4
 
-=item LONCAPA::Firewall::firewall_is_port_open( $iptables,$fw_chain,$port,$lond_port,$iphost );
+=item LONCAPA::Firewall::firewall_is_port_open( $iptables,$fw_chain,$port,$lond_port,$iphost,$curropen );
 
 =back
 
@@ -398,7 +476,7 @@
 
 =over 4
 
-=item LONCAPA::Firewall::get_fw_chain();
+=item LONCAPA::Firewall::get_fw_chains();
 
 =back
 

--raeburn1269481665--