--- loncom/configuration/Firewall.pm 2019/05/07 21:18:24 1.17 +++ loncom/configuration/Firewall.pm 2019/06/09 02:35:45 1.18 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Firewall configuration to allow internal LON-CAPA communication between servers # -# $Id: Firewall.pm,v 1.17 2019/05/07 21:18:24 raeburn Exp $ +# $Id: Firewall.pm,v 1.18 2019/06/09 02:35:45 raeburn Exp $ # # The LearningOnline Network with CAPA # @@ -42,7 +42,7 @@ sub uses_firewalld { if ($distro eq '') { $distro = &get_distro(); } - my ($inuse,$checkfirewalld,$zone); + my ($inuse,$checkfirewalld); if ($distro =~ /^(suse|sles)([\d\.]+)$/) { if (($1 eq 'sles') && ($2 >= 15)) { $checkfirewalld = 1; @@ -72,40 +72,36 @@ sub uses_firewalld { } if (($loaded eq 'loaded') || ($active eq 'active')) { $inuse = 1; - my $cmd = 'firewall-cmd --get-default-zone'; - if (open(PIPE,"$cmd |")) { - my $result = ; - chomp($result); - close(PIPE); - if ($result =~ /^\w+$/) { - $zone = $result; - } - } } } - return ($inuse,$zone); + return $inuse; } sub firewall_open_port { - my ($iptables,$fw_chains,$lond_port,$iphost,$ports) = @_; + my ($iptables,$fw_chains,$lond_port,$iphost,$ports,$firewalld) = @_; return 'inactive firewall' if (!&firewall_is_active()); return 'port number unknown' if !$lond_port; 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); + my (@opened,@okchains,$zone); + if ($firewalld) { + $zone = &get_default_zone(); + return 'invalid zone' if ($zone eq ''); + } else { + my @badchains; + 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 (!@okchains) { - return 'None of the chain names has the expected format.'."\n"; } if (ref($ports) ne 'ARRAY') { return 'List of ports to open needed.'; } - my ($firewalld,$zone) = &uses_firewalld(); foreach my $portnum (@{$ports}) { my $port = ''; if ($portnum =~ /^(\d+)$/) { @@ -124,9 +120,17 @@ sub firewall_open_port { if (ref($iphost) eq 'HASH') { 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); + if ($firewalld) { + &firewall_close_anywhere($iptables,$zone,$port,$firewalld); + my $current = &firewall_is_port_open($iptables,$zone,$port, + $lond_port,$iphost,\%curropen, + $firewalld); + } else { + 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 = ''; @@ -144,30 +148,27 @@ sub firewall_open_port { if ($curropen{$ip}) { push(@lond_port_curropen,$ip); } else { - foreach my $fw_chain (@okchains) { - if ($firewalld) { - my $cmd = 'firewall-cmd --zone='.$zone.' --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); - } + if ($firewalld) { + my $cmd = 'firewall-cmd --zone='.$zone.' --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); } else { - push (@port_error,$ip); + push(@port_error,$ip); } - } else { + } + } 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); + push(@port_error,$ip); } } elsif ($return_status == 2) { push(@{$command_error{$fw_chain}},$ip); @@ -212,24 +213,27 @@ sub firewall_open_port { } } } else { - my (@port_errors,%command_errors); - foreach my $fw_chain (@okchains) { - if ($firewalld) { - my $cmd = 'firewall-cmd --zone='.$zone.' --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); - } + if ($firewalld) { + my ($port_error); + my $cmd = 'firewall-cmd --zone='.$zone.' --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); } else { - push(@port_errors,$fw_chain); + $port_error = $port; } } else { + $port_error = $port; + } + if ($port_error) { + print "Error opening port: $port\n"; + } + } 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); @@ -267,14 +271,48 @@ sub firewall_open_port { } sub firewall_is_port_open { - my ($iptables,$fw_chain,$port,$lond_port,$iphost,$curropen) = @_; + my ($iptables,$fw_chain,$port,$lond_port,$iphost,$curropen,$firewalld) = @_; # 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 firewalld is in use, checks for rich rules only. my $count = 0; + return $count if (! &firewall_is_active()); + if ($firewalld) { + my $zone = &get_default_zone(); + return $count if ($zone eq ''); + if ($port eq $lond_port) { + if (open(PIPE,"firewall-cmd --zone=$zone --list-rich-rules |")) { + while() { + chomp(); + if (/\Qrule family="ipv4" source address="\E([\d.]+)\Q\/32" port port="$port" protocol="tcp" accept\E/) { + my $ip = $1; + if ($iphost->{$ip}) { + $count ++; + if (ref($curropen) eq 'HASH') { + $curropen->{$ip} ++; + } + } + } + } + close(PIPE); + } + } else { + if (open(PIPE,"firewall-cmd --zone=$zone --list-rich-rules |")) { + while() { + if (/\Qrule family="ipv4" port port="$port" protocol="tcp" accept\E/) { + $count ++; + last; + } + } + close(PIPE); + } + } + return $count; + } + return $count unless ($fw_chain !~ /^[\w-]+$/); if (open(PIPE,"$iptables -L $fw_chain -n |")) { + # check if firewall is active or installed + return if (! &firewall_is_active()); while() { if ($port eq $lond_port) { if (ref($iphost) eq 'HASH') { @@ -288,11 +326,9 @@ sub firewall_is_port_open { } } } - } else { - if (/tcp dpt\:\Q$port\E/) { - $count ++; - last; - } + } elsif (/tcp dpt\:\Q$port\E/) { + $count ++; + last; } } close(PIPE); @@ -314,29 +350,37 @@ sub firewall_is_active { close(PIPE); } } + unless ($status) { + $status = &uses_firewalld(); + } return $status; } sub firewall_close_port { - my ($iptables,$fw_chains,$lond_port,$iphost,$ports) = @_; + my ($iptables,$fw_chains,$lond_port,$iphost,$ports,$firewalld) = @_; return 'inactive firewall' if (!&firewall_is_active()); return 'port number unknown' if !$lond_port; 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); + my (@okchains,$zone); + if ($firewalld) { + $zone = &get_default_zone(); + return 'no default zone' if ($zone eq ''); + } else { + my @badchains; + 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 (!@okchains) { - return 'None of the chain names has the expected format.'."\n"; } if (ref($ports) ne 'ARRAY') { return 'List of ports to close needed.'; } - my ($firewalld,$zone) = &uses_firewalld(); foreach my $portnum (@{$ports}) { my $port = ''; if ($portnum =~ /^(\d+)$/) { @@ -348,19 +392,17 @@ sub firewall_close_port { print "Closing firewall access on port $port.\n"; if (($port ne '') && ($port eq $lond_port)) { my $output; - 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 () { - chomp(); - 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+/) { + if ($firewalld) { + my (%to_close,@port_error,@lond_port_close); + my $cmd = 'firewall-cmd --list-rich-rules'; + if (open(PIPE,"$cmd |")) { + while() { + if (/\Qrule family="ipv4" source address="\E([\d.]+)\Q\/32" port port="$port" protocol="tcp" accept\E/) { my $ip = $1; my $keepopen = 0; if (ref($iphost) eq 'HASH') { if (exists($iphost->{$ip})) { - $keepopen = 1; + $keepopen = 1; } } unless ($keepopen) { @@ -371,28 +413,58 @@ sub firewall_close_port { close(PIPE); } if (keys(%to_close) > 0) { - foreach my $ip (keys(%to_close)) { - if ($firewalld) { - my $cmd = 'firewall-cmd --zone='.$zone.' --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); - } + foreach my $ip (sort(keys(%to_close))) { + my $cmd = 'firewall-cmd --zone='.$zone.' --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); + } + } + } + if (@lond_port_close) { + $output .= "Port closed for ".scalar(@lond_port_close)." IP addresses.\n"; + } + if (@port_error) { + $output .= "Error closing port for following IP addresses: ".join(', ',@port_error)."\n"; + } + } else { + foreach my $fw_chain (@okchains) { + my (%to_close,@port_error,@command_error,@lond_port_close); + if (open(PIPE, "$iptables -n -L $fw_chain |")) { + while () { + chomp(); + 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; + if (ref($iphost) eq 'HASH') { + if (exists($iphost->{$ip})) { + $keepopen = 1; + } + } + unless ($keepopen) { + $to_close{$ip} = $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); + push(@port_error,$ip); } elsif ($return_status == 2) { push(@command_error,$ip); } elsif ($return_status == 0) { @@ -400,17 +472,17 @@ sub firewall_close_port { } } } - } - if (@lond_port_close) { - $output .= "Port closed for ".scalar(@lond_port_close)." IP addresses.\n"; - } - if (@port_error) { - $output .= "Error closing port for following IP addresses: ".join(', ',@port_error)."\n"; - } - if (@command_error) { - $output .= "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"; + if (@lond_port_close) { + $output .= "Port closed for ".scalar(@lond_port_close)." IP addresses.\n"; + } + if (@port_error) { + $output .= "Error closing port for following IP addresses: ".join(', ',@port_error)."\n"; + } + if (@command_error) { + $output .= "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"; + } } } if ($output) { @@ -419,40 +491,52 @@ sub firewall_close_port { print "No IP addresses required discontinuation of access.\n"; } } else { - foreach my $fw_chain (@okchains) { - my (@port_error,@command_error,@lond_port_close); + if ($firewalld) { my $to_close; - if (open(PIPE, "$iptables -n -L $fw_chain |")) { - while () { - chomp(); - next unless (/dpt:\Q$port\E/); + if (open(PIPE,"firewall-cmd --list-rich-rules |")) { + while() { + next unless (/\Qrule family="ipv4" port port="$port" protocol="tcp" accept\E/); $to_close = 1; + last; } close(PIPE); } if ($to_close) { - if ($firewalld) { - my $cmd = 'firewall-cmd --zone='.$zone.' --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"; - } + my $cmd = 'firewall-cmd --zone='.$zone.' --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: $port closed in zone: $zone.\n"; } else { - print "Error closing port for chain: $fw_chain.\n"; + print "Error closing port: $port in zone: $zone.\n"; } } else { + print "Error closing port: $port in zone: $zone.\n"; + } + } + } 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 () { + chomp(); + next unless (/dpt:\Q$port\E/); + $to_close = 1; + last; + } + 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"; + print "Error closing port: $port for chain: $fw_chain.\n"; } elsif ($return_status == 2) { # Bad command print "Bad command error closing port. Command was\n". @@ -469,39 +553,61 @@ sub firewall_close_port { } sub firewall_close_anywhere { - my ($iptables,$fw_chain,$port) = @_; - my ($firewalld,$zone) = &uses_firewalld(); - if (open(PIPE, "$iptables --line-numbers -n -L $fw_chain |")) { + my ($iptables,$fw_chain,$port,$firewalld) = @_; + my $zone; + if ($firewalld) { + $zone = &get_default_zone(); + if ($zone eq '') { + print 'no default zone'; + return; + } + } else { + unless ($fw_chain =~ /^([\w\-]+)$/) { + print 'invalid chain'; + return; + } + } + if ($firewalld) { + my $to_close; + my $cmd = 'firewall-cmd --list-ports'; + if (open(PIPE,"$cmd |")) { + my $currports = ; + close(PIPE); + chomp($currports); + if (grep(/^\Q$port\E\/tcp/,split(/\s+/,$currports))) { + $to_close = 1; + } + } + if ($to_close) { + my $cmd = 'firewall-cmd --zone='.$zone.' --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"; + } + } + } elsif (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/) { - 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"; - } + 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 { - 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"; - } + print 'Port '.$port.' closed for source "anywhere"'."\n"; } } } @@ -531,13 +637,20 @@ sub get_fw_chains { my @fw_chains; my $suse_config = "/etc/sysconfig/SuSEfirewall2"; my $ubuntu_config = "/etc/ufw/ufw.conf"; - my ($firewalld,$zone) = &uses_firewalld($distro); + my $firewalld = &uses_firewalld($distro); if ($firewalld) { - if ($zone ne '') { - push(@fw_chains,'IN_'.$zone.'_allow'); + my ($dist,$version) = ($distro =~ /^([\D]+)(\d+)$/); + if ((($dist eq 'rhes') || ($dist eq 'centos')) && + ($version >= 8)) { + push(@fw_chains,'INPUT'); } else { - push(@fw_chains,'IN_public_allow'); - } + my $zone = &get_default_zone(); + if ($zone ne '') { + push(@fw_chains,'IN_'.$zone.'_allow'); + } else { + push(@fw_chains,'IN_public_allow'); + } + } } elsif (-e $suse_config) { push(@fw_chains,'input_ext'); } else { @@ -592,6 +705,18 @@ sub get_fw_chains { return @fw_chains; } +sub get_default_zone { + my $cmd = 'firewall-cmd --get-default-zone'; + my $zone; + if (open(PIPE,"$cmd |")) { + my $result = ; + chomp($result); + close(PIPE); + ($zone) = ($result =~ /^(\w+)$/); + } + return $zone; +} + sub get_pathto_iptables { my $iptables; if (-e '/sbin/iptables') { @@ -653,19 +778,19 @@ The following methods are available: =over 4 -=item LONCAPA::Firewall::firewall_open_port( $iptables,$fw_chains,$lond_port,$iphost,$port ); +=item LONCAPA::Firewall::firewall_open_port( $iptables,$fw_chains,$lond_port,$iphost,$ports,$firewalld ); =back =over 4 -=item LONCAPA::Firewall::firewall_close_port( $iptables,$fw_chains,$lond_port,$iphost,$ports ); +=item LONCAPA::Firewall::firewall_close_port( $iptables,$fw_chains,$lond_port,$iphost,$ports,$firewalld ); =back =over 4 -=item LONCAPA::Firewall::firewall_is_port_open( $iptables,$fw_chain,$port,$lond_port,$iphost,$curropen ); +=item LONCAPA::Firewall::firewall_is_port_open( $iptables,$fw_chain,$port,$lond_port,$iphost,$curropen,$firewalld ); =back @@ -677,7 +802,7 @@ The following methods are available: =over 4 -=item LONCAPA::Firewall::firewall_close_anywhere( $iptables,$fw_chain,$port ); +=item LONCAPA::Firewall::firewall_close_anywhere( $iptables,$fw_chain,$port,$firewalld ); =back