--- loncom/configuration/Firewall.pm 2014/03/17 14:47:46 1.14 +++ loncom/configuration/Firewall.pm 2018/10/24 02:08:04 1.15 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Firewall configuration to allow internal LON-CAPA communication between servers # -# $Id: Firewall.pm,v 1.14 2014/03/17 14:47:46 bisitz Exp $ +# $Id: Firewall.pm,v 1.15 2018/10/24 02:08:04 raeburn Exp $ # # The LearningOnline Network with CAPA # @@ -37,6 +37,46 @@ use lib '/home/httpd/perl/lib'; use LONCAPA::Configuration; use LONCAPA; +sub uses_firewalld { + my ($distro) = @_; + if ($distro eq '') { + $distro = &get_distro(); + } + my ($inuse, $checkfirewalld); + if ($distro =~ /^(suse|sles)([\d\.]+)$/) { + if (($1 eq 'sles') && ($2 >= 15)) { + $checkfirewalld = 1; + } + } elsif ($distro =~ /^fedora(\d+)$/) { + if ($1 >= 18) { + $checkfirewalld = 1; + } + } elsif ($distro =~ /^(?:centos|rhes|scientific)(\d+)/) { + if ($1 >= 7) { + $checkfirewalld = 1; + } + } + if ($checkfirewalld) { + my ($loaded,$active); + if (open(PIPE,"systemctl status firewalld |")) { + while () { + chomp(); + if (/^\s*Loaded:\s+(\w+)/) { + $loaded = $1; + } + if (/^\s*Active\s+(\w+)/) { + $active = $1; + } + } + close(PIPE); + } + if (($loaded eq 'loaded') || ($active eq 'active')) { + $inuse = 1; + } + } + return $inuse; +} + sub firewall_open_port { my ($iptables,$fw_chains,$lond_port,$iphost,$ports) = @_; return 'inactive firewall' if (!&firewall_is_active()); @@ -56,6 +96,7 @@ sub firewall_open_port { if (ref($ports) ne 'ARRAY') { return 'List of ports to open needed.'; } + my $firewalld = &uses_firewalld(); foreach my $portnum (@{$ports}) { my $port = ''; if ($portnum =~ /^(\d+)$/) { @@ -95,19 +136,36 @@ sub firewall_open_port { 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)) { + if ($firewalld) { + my $cmd = 'firewall-cmd --zone=public --add-rich-rule \'rule family="ipv4" source address="'.$ip.'/32" port port="'.$port.'" protocol="tcp" accept\''; + if (open(PIPE,"$cmd |")) { + my $result = ; + chomp($result); + close(PIPE); + if ($result eq 'success') { + push(@lond_port_open,$ip); + last; + } else { + push (@port_error,$ip); + } + } else { push (@port_error,$ip); } - } elsif ($return_status == 2) { - push(@{$command_error{$fw_chain}},$ip); - } elsif ($return_status == 0) { - push(@lond_port_open,$ip); - last; + } else { + 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; + } } } } @@ -147,28 +205,45 @@ sub firewall_open_port { } else { 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; + if ($firewalld) { + my $cmd = 'firewall-cmd --zone=public --add-rich-rule \'rule family="ipv4" port port="'.$port.'" protocol="tcp" accept\''; + if (open(PIPE,"$cmd |")) { + my $result = ; + chomp($result); + close(PIPE); + if ($result eq 'success') { + push(@opened,$port); + last; + } else { + push(@port_errors,$fw_chain); + } + } else { + push(@port_errors,$fw_chain); + } + } 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) { + 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"; + 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"; + } } } } @@ -217,11 +292,20 @@ sub firewall_is_port_open { } sub firewall_is_active { + my $status = 0; if (-e '/proc/net/ip_tables_names') { - return 1; - } else { - return 0; + if (open(PIPE,'cat /proc/net/ip_tables_names |')) { + while() { + chomp(); + if (/^filter$/) { + $status = 1; + last; + } + } + close(PIPE); + } } + return $status; } sub firewall_close_port { @@ -243,6 +327,7 @@ sub firewall_close_port { if (ref($ports) ne 'ARRAY') { return 'List of ports to close needed.'; } + my $firewalld = &uses_firewalld(); foreach my $portnum (@{$ports}) { my $port = ''; if ($portnum =~ /^(\d+)$/) { @@ -260,7 +345,7 @@ sub firewall_close_port { if (open(PIPE, "$iptables -n -L $fw_chain |")) { while () { chomp(); - next unless (/dpt:\Q$port\E\s*$/); + next unless (/dpt:\Q$port\E/); if (/^ACCEPT\s+tcp\s+\-{2}\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+/) { my $ip = $1; my $keepopen = 0; @@ -278,16 +363,32 @@ sub firewall_close_port { } 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 ($firewalld) { + my $cmd = 'firewall-cmd --zone=public --remove-rich-rule \'rule family="ipv4" source address="'.$ip.'/32" port port="'.$port.'" protocol="tcp" accept\''; + if (open(PIPE,"$cmd |")) { + my $result = ; + chomp($result); + close(PIPE); + if ($result eq 'success') { + push(@lond_port_close,$ip); + } else { + push(@port_error,$ip); + } + } else { + push(@port_error,$ip); + } + } else { + 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); + } } } } @@ -315,25 +416,41 @@ sub firewall_close_port { if (open(PIPE, "$iptables -n -L $fw_chain |")) { while () { chomp(); - next unless (/dpt:\Q$port\E\s*$/); + next unless (/dpt:\Q$port\E/); $to_close = 1; } close(PIPE); } if ($to_close) { - 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 for chain: $fw_chain.\n"; - } elsif ($return_status == 2) { - # Bad command - print "Bad command error closing port. Command was\n". - " ".$firewall_command."\n"; + if ($firewalld) { + my $cmd = 'firewall-cmd --zone=public --remove-rich-rule \'rule family="ipv4" port port="'.$port.'" protocol="tcp" accept\''; + if (open(PIPE,"$cmd|")) { + my $result = ; + chomp($result); + close(PIPE); + if ($result eq 'success') { + print "Port closed for chain $fw_chain.\n"; + } else { + print "Error closing port for chain: $fw_chain.\n"; + } + } else { + print "Error closing port for chain: $fw_chain.\n"; + } } else { - print "Port closed for chain $fw_chain.\n"; + 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 for chain: $fw_chain.\n"; + } elsif ($return_status == 2) { + # Bad command + print "Bad command error closing port. Command was\n". + " ".$firewall_command."\n"; + } else { + print "Port closed for chain $fw_chain.\n"; + } } } } @@ -344,21 +461,38 @@ sub firewall_close_port { sub firewall_close_anywhere { my ($iptables,$fw_chain,$port) = @_; + my $firewalld = &uses_firewalld(); if (open(PIPE, "$iptables --line-numbers -n -L $fw_chain |")) { while () { 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"; + if ($firewalld) { + my $cmd = 'firewall-cmd --remove-port='.$port.'/tcp'; + if (open(PIPE,"$cmd |")) { + my $result = ; + chomp($result); + close(PIPE); + if ($result eq 'success') { + print 'Port '.$port.' closed for source "anywhere"'."\n"; + } else { + print 'Error closing port '.$port.' for source "anywhere".'."\n"; + } + } else { + print 'Error closing port '.$port.' for source "anywhere".'."\n"; + } } else { - print 'Port '.$port.' closed for source "anywhere"'."\n"; + 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"; + } } } } @@ -381,12 +515,16 @@ sub get_lond_port { } sub get_fw_chains { - my ($iptables) = @_; - my $distro = &LONCAPA::distro(); + my ($iptables,$distro) = @_; + if ($distro eq '') { + $distro = &get_distro(); + } my @fw_chains; my $suse_config = "/etc/sysconfig/SuSEfirewall2"; my $ubuntu_config = "/etc/ufw/ufw.conf"; - if (-e $suse_config) { + if (&uses_firewalld($distro)) { + push(@fw_chains,'IN_public_allow'); + } elsif (-e $suse_config) { push(@fw_chains,'input_ext'); } else { my @posschains; @@ -395,8 +533,12 @@ sub get_fw_chains { } else { if ($distro =~ /^(debian|ubuntu|suse|sles)/) { @posschains = ('INPUT'); - } else { - @posschains = ('RH-Firewall-1-INPUT','INPUT'); + } elsif ($distro =~ /^(fedora|rhes|centos|scientific)(\d+)$/) { + if ((($1 eq 'fedora') && ($2 > 15)) || (($1 ne 'fedora') && ($2 >= 7))) { + @posschains = ('INPUT'); + } else { + @posschains = ('RH-Firewall-1-INPUT','INPUT'); + } } if (!-e '/etc/sysconfig/iptables') { if (!-e '/var/lib/iptables') { @@ -404,8 +546,10 @@ sub get_fw_chains { print("Unable to find iptables file containing static definitions.\n"); } } - if ($distro =~ /^(fedora|rhes|centos|scientific)/) { - push(@fw_chains,'RH-Firewall-1-INPUT'); + if ($distro =~ /^(fedora|rhes|centos|scientific)(\d+)$/) { + unless ((($1 eq 'fedora') && ($2 > 15)) || (($1 ne 'fedora') && ($2 >= 7))) { + push(@fw_chains,'RH-Firewall-1-INPUT'); + } } } } @@ -446,6 +590,15 @@ sub get_pathto_iptables { return $iptables; } +sub get_distro { + my $distro; + if (open(PIPE,"/home/httpd/perl/distprobe |")) { + $distro = ; + close(PIPE); + } + return $distro; +} + 1; __END__ @@ -460,6 +613,7 @@ B - dynamic opening/c use lib '/home/httpd/lib/perl/'; use LONCAPA::Firewall; + LONCAPA::Firewall::uses_firewalld(); LONCAPA::Firewall::firewall_open_port(); LONCAPA::Firewall::firewall_close_port(); LONCAPA::Firewall::firewall_is_port_open(); @@ -479,6 +633,12 @@ The following methods are available: =over 4 +=item LONCAPA::Firewall::uses_firewalld( $distro ); + +=back + +=over 4 + =item LONCAPA::Firewall::firewall_open_port( $iptables,$fw_chains,$lond_port,$iphost,$port ); =back @@ -515,7 +675,7 @@ The following methods are available: =over 4 -=item LONCAPA::Firewall::get_fw_chains(); +=item LONCAPA::Firewall::get_fw_chains( $iptables,$distro ); =back @@ -523,6 +683,13 @@ The following methods are available: =item LONCAPA::Firewall::get_pathto_iptables(); +=back + +=over 4 + +=item LONCAPA::Firewall::get_distro(); + +=back =head1 AUTHORS