File:  [LON-CAPA] / loncom / configuration / Firewall.pm
Revision 1.24: download - view: text, annotated - select for diffs
Tue Dec 21 13:57:47 2021 UTC (2 years, 5 months ago) by raeburn
Branches: MAIN
CVS tags: HEAD
- Support CentOS Stream 8 and 9.

    1: # The LearningOnline Network with CAPA
    2: # Firewall configuration to allow internal LON-CAPA communication between servers   
    3: #
    4: # $Id: Firewall.pm,v 1.24 2021/12/21 13:57:47 raeburn Exp $
    5: #
    6: # The LearningOnline Network with CAPA
    7: #
    8: # Copyright Michigan State University Board of Trustees
    9: #
   10: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
   11: #
   12: # LON-CAPA is free software; you can redistribute it and/or modify
   13: # it under the terms of the GNU General Public License as published by
   14: # the Free Software Foundation; either version 2 of the License, or
   15: # (at your option) any later version.
   16: #
   17: # LON-CAPA is distributed in the hope that it will be useful,
   18: # but WITHOUT ANY WARRANTY; without even the implied warranty of
   19: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   20: # GNU General Public License for more details.
   21: #
   22: # You should have received a copy of the GNU General Public License
   23: # along with LON-CAPA; if not, write to the Free Software
   24: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   25: #
   26: # /home/httpd/html/adm/gpl.txt
   27: #
   28: # http://www.lon-capa.org/
   29: #
   30: # Startup script for the LON-CAPA network processes
   31: #
   32: 
   33: package LONCAPA::Firewall;
   34: 
   35: use strict;
   36: use lib '/home/httpd/perl/lib';
   37: use LONCAPA::Configuration;
   38: use LONCAPA;
   39: 
   40: sub uses_firewalld {
   41:     my ($distro) = @_;
   42:     if ($distro eq '') {
   43:         $distro = &get_distro();
   44:     }
   45:     my ($inuse,$checkfirewalld);
   46:     if ($distro =~ /^(suse|sles)([\d\.]+)$/) {
   47:         if (($1 eq 'sles') && ($2 >= 15)) {
   48:             $checkfirewalld = 1;
   49:         }
   50:     } elsif ($distro =~ /^fedora(\d+)$/) {
   51:         if ($1 >= 18) {
   52:             $checkfirewalld = 1;
   53:         }
   54:     } elsif ($distro =~ /^(?:centos|rhes|scientific|oracle|rocky|alma)(\d+)/) {
   55:         if ($1 >= 7) {
   56:             $checkfirewalld = 1;
   57:         }
   58:     }
   59:     if ($checkfirewalld) {
   60:         my ($loaded,$active);
   61:         if (open(PIPE,"systemctl status firewalld 2>&1 |")) {
   62:             while (<PIPE>) {
   63:                 chomp();
   64:                 if (/^\s*Loaded:\s+(\w+)/) {
   65:                     $loaded = $1;
   66:                 }
   67:                 if (/^\s*Active\s+(\w+)/) {
   68:                     $active = $1;
   69:                 }
   70:             }
   71:             close(PIPE);
   72:         }
   73:         if (($loaded eq 'loaded') || ($active eq 'active')) {
   74:             $inuse = 1;
   75:         }
   76:     }
   77:     return $inuse;
   78: }
   79: 
   80: sub firewall_open_port {
   81:     my ($iptables,$fw_chains,$lond_port,$iphost,$ports,$firewalld) = @_;
   82:     return 'inactive firewall' if (!&firewall_is_active());
   83:     return 'port number unknown' if !$lond_port;
   84:     return 'invalid firewall chain' unless (ref($fw_chains) eq 'ARRAY');
   85:     my (@opened,@okchains,$zone);
   86:     if ($firewalld) {
   87:         $zone = &get_default_zone();
   88:         return 'invalid zone' if ($zone eq '');
   89:     } else {
   90:         my @badchains;
   91:         foreach my $chain (@{$fw_chains}) {
   92:             if ($chain =~ /^([\w\-]+)$/) {
   93:                 push(@okchains,$1);
   94:             } else {
   95:                 push(@badchains,$chain);
   96:             }
   97:         }
   98:         if (!@okchains) {
   99:             return 'None of the chain names has the expected format.'."\n";
  100:         }
  101:     }
  102:     if (ref($ports) ne 'ARRAY') {
  103:         return 'List of ports to open needed.';
  104:     }
  105:     foreach my $portnum (@{$ports}) {
  106:         my $port = '';
  107:         if ($portnum =~ /^(\d+)$/) {
  108:             $port = $1;
  109:         } else {
  110:             print "Skipped non-numeric port: $portnum.\n";
  111:             next;
  112:         }
  113:         print "Opening firewall access on port $port.\n";
  114:         my $result;
  115:         if ($port eq $lond_port) {
  116:             # For lond port, restrict the servers allowed to attempt to communicate
  117:             # to include only source IPs in the LON-CAPA cluster.
  118:             my (@port_error,%command_error,@lond_port_open,
  119:                 @lond_port_curropen);
  120:             if (ref($iphost) eq 'HASH') {
  121:                 if (keys(%{$iphost}) > 0) {
  122:                     my $count = scalar(keys(%{$iphost}));
  123:                     if ($count > 1) {
  124:                         print "Please be patient. Checking $count IPs.\n";
  125:                     }
  126:                     my %curropen;
  127:                     if ($firewalld) {
  128:                         &firewall_close_anywhere($iptables,$zone,$port,$firewalld);
  129:                         my $current = &firewall_is_port_open($iptables,$zone,$port,
  130:                                                              $lond_port,$iphost,\%curropen,
  131:                                                              $firewalld);
  132:                     } else {
  133:                         foreach my $fw_chain (@okchains) {
  134:                             &firewall_close_anywhere($iptables,$fw_chain,$port);
  135:                             my $current = &firewall_is_port_open($iptables,$fw_chain,$port,
  136:                                                                  $lond_port,$iphost,\%curropen);
  137:                         }
  138:                     }
  139:                     my $countok = 0;
  140:                     foreach my $key (keys(%{$iphost})) {
  141:                         my $ip = '';
  142:                         if ($key =~ /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/) {
  143:                             if (($1<=255) && ($2<=255) && ($3<=255) && ($4<=255)) {
  144:                                 $ip = "$1.$2.$3.$4";
  145:                             } else {
  146:                                 print "IP address: $key does not have expected format.\n";
  147:                                 next;
  148:                             }
  149:                         } else {
  150:                             print "IP address: $key does not have expected format.\n";
  151:                             next;
  152:                         }
  153:                         if ($curropen{$ip}) {
  154:                             push(@lond_port_curropen,$ip);
  155:                         } else {
  156:                             if ($firewalld) {
  157:                                 my $cmd = 'firewall-cmd --zone='.$zone.' --add-rich-rule \'rule family="ipv4" source address="'.$ip.'/32" port port="'.$port.'" protocol="tcp" accept\'';
  158:                                 if (open(PIPE,"$cmd |")) {
  159:                                     my $result = <PIPE>;
  160:                                     chomp($result);
  161:                                     close(PIPE);
  162:                                     if ($result eq 'success') {
  163:                                         push(@lond_port_open,$ip);
  164:                                     } else {
  165:                                         push(@port_error,$ip);
  166:                                     }
  167: 				}	 
  168:                             } else {
  169:                                 foreach my $fw_chain (@okchains) {
  170:                                     my $firewall_command =
  171:                                         "$iptables -I $fw_chain -p tcp -s $ip -d 0/0 --dport $port -j ACCEPT";
  172:                                     system($firewall_command);
  173:                                     my $return_status = $?>>8;
  174:                                     if ($return_status == 1) {
  175:                                         unless(grep(/^\Q$ip\E$/,@port_error)) {
  176:                                             push(@port_error,$ip);
  177:                                         }
  178:                                     } elsif ($return_status == 2) {
  179:                                         push(@{$command_error{$fw_chain}},$ip);
  180:                                     } elsif ($return_status == 0) {
  181:                                         push(@lond_port_open,$ip);
  182:                                         last;
  183:                                     }
  184:                                 }
  185:                             }
  186:                         }
  187:                         if ($count > 1) {
  188:                             $countok ++;
  189:                             print '.';
  190:                             if ($countok%40 == 0) {
  191:                                 print "\n";
  192:                             }
  193:                         }
  194:                     }
  195:                     if ($count > 1) {
  196:                         if ($countok%40) { 
  197:                             print "\n"; 
  198:                         }
  199:                     }
  200:                 } else {
  201:                     print "no key found in \$iphost hash ref.\n".
  202:                           "Domain Name Service (DNS) may not be available.\n".
  203:                           "If this LON-CAPA node is standalone, then you can fix this issue by modifying /etc/hosts.\n".
  204:                           "Use a text editor to add: IPaddress Hostname\n";
  205:                 }
  206:             } else {
  207:                 print "\$iphost is not a reference to a hash\n";
  208:             }
  209:             if (@lond_port_curropen) {
  210:                 unless (grep(/^\Q$port\E$/,@opened)) {
  211:                     push(@opened,$port);
  212:                 }
  213:                 print "Port already open for ".scalar(@lond_port_curropen)." IP addresses.\n";
  214:             }
  215:             if (@lond_port_open) {
  216:                 unless (grep(/^\Q$port\E$/,@opened)) {   
  217:                     push(@opened,$port);
  218:                 }
  219:                 print "Port opened for ".scalar(@lond_port_open)." IP addresses.\n";
  220:             }
  221:             if (@port_error) {
  222:                 print "Error opening port for following IP addresses: ".join(', ',@port_error)."\n";
  223:             }
  224:             if (keys(%command_error) > 0) {
  225:                 foreach my $chain (sort(keys(%command_error))) {
  226:                     if (ref($command_error{$chain}) eq 'ARRAY') {
  227:                         if (@{$command_error{$chain}}) {
  228:                             print "Bad command error opening port for following IP addresses: ".
  229:                                   join(', ',@{$command_error{$chain}})."\n".
  230:                                  'Command was: "'."$iptables -I $chain -p tcp -s ".'$ip'." -d 0/0 --dport $port -j ACCEPT".'", where $ip is IP address'."\n";
  231:                         }
  232:                     }
  233:                 }
  234:             }
  235:         } else {
  236:             if ($firewalld) {
  237:                 my ($port_error);
  238:                 my $cmd = 'firewall-cmd --zone='.$zone.' --add-rich-rule \'rule family="ipv4" port port="'.$port.'" protocol="tcp" accept\'';
  239:                 if (open(PIPE,"$cmd |")) {
  240:                     my $result = <PIPE>;
  241:                     chomp($result);
  242:                     close(PIPE);
  243:                     if ($result eq 'success') {
  244:                         push(@opened,$port);
  245:                     } else {
  246:                         $port_error = $port;
  247:                     }
  248:                 } else {
  249:                     $port_error = $port;
  250:                 }
  251:                 if ($port_error) {
  252:                     print "Error opening port: $port\n";
  253:                 }
  254:             } else {
  255:                 my (@port_errors,%command_errors);
  256:                 foreach my $fw_chain (@okchains) {
  257:                     my $firewall_command =
  258:                         "$iptables -I $fw_chain -p tcp -d 0/0 --dport $port -j ACCEPT";
  259:                     system($firewall_command);
  260:                     my $return_status = $?>>8;
  261:                     if ($return_status == 1) {
  262:                         push(@port_errors,$fw_chain);
  263:                     } elsif ($return_status == 2) {
  264:                         $command_errors{$fw_chain} = $firewall_command;
  265:                     } elsif ($return_status == 0) {
  266:                         push(@opened,$port);
  267:                         last;
  268:                     }
  269:                 }
  270:                 unless (grep(/^\Q$port\E$/,@opened)) {
  271:                     if (@port_errors) {
  272:                         print "Error opening port for chains: ".
  273:                               join(', ',@port_errors).".\n";
  274:                     }
  275:                     if (keys(%command_errors)) {
  276:                         foreach my $fw_chain (sort(keys(%command_errors))) {
  277:                             print "Bad command error opening port for chain: $fw_chain.  Command was\n".
  278:                                   "  ".$command_errors{$fw_chain}."\n";
  279:                         }
  280:                     }
  281:                 }
  282:             }
  283:         }
  284:     }
  285:     foreach my $port (@{$ports}) {
  286:         if (!grep(/^\Q$port\E$/,@opened)) {
  287:             return 'Required port not open: '.$port."\n";
  288:         }
  289:     }
  290:     return 'ok';
  291: }
  292: 
  293: sub firewall_is_port_open {
  294:     my ($iptables,$fw_chain,$port,$lond_port,$iphost,$curropen,$firewalld) = @_;
  295:     # for lond port returns number of source IPs for which firewall port is open
  296:     # for other ports returns 1 if the firewall port is open, 0 if not.
  297:     # if firewalld is in use, checks for rich rules only.
  298:     my $count = 0;
  299:     # check if firewall is active or installed
  300:     return $count if (! &firewall_is_active());
  301:     if ($firewalld) {
  302:         my $zone = &get_default_zone();
  303:         return $count if ($zone eq ''); 
  304:         if ($port eq $lond_port) {
  305:             if (open(PIPE,"firewall-cmd --zone=$zone --list-rich-rules |")) {
  306:                 while(<PIPE>) {
  307:                     chomp();
  308:                     if (/\Qrule family="ipv4" source address="\E([\d.]+)\Q\/32" port port="$port" protocol="tcp" accept\E/) {
  309:                         my $ip = $1;
  310:                         if ($iphost->{$ip}) {
  311:                             $count ++;
  312:                             if (ref($curropen) eq 'HASH') {
  313:                                 $curropen->{$ip} ++;
  314:                             }
  315:                         }
  316:                     }
  317:                 }
  318:                 close(PIPE);
  319:             }
  320:         } else {
  321:             if (open(PIPE,"firewall-cmd --zone=$zone --list-rich-rules |")) {
  322:                 while(<PIPE>) {
  323:                     if (/\Qrule family="ipv4" port port="$port" protocol="tcp" accept\E/) {
  324:                         $count ++;
  325:                         last;
  326:                     }
  327:                 }
  328:                 close(PIPE);
  329:             }
  330:         }
  331:     } elsif (($fw_chain =~ /^[\w-]+$/) && (open(PIPE,"$iptables -L $fw_chain -n |"))) {
  332:         while(<PIPE>) {
  333:             if ($port eq $lond_port) {
  334:                 if (ref($iphost) eq 'HASH') {
  335:                     if (/^ACCEPT\s+tcp\s+\-{2}\s+(\S+)\s+\S+\s+tcp\s+dpt\:\Q$port\E/) {
  336:                         my $ip = $1;
  337:                         if ($iphost->{$ip}) {
  338:                             $count ++;
  339:                             if (ref($curropen) eq 'HASH') {
  340:                                 $curropen->{$ip} ++;
  341:                             }
  342:                         }
  343:                     }
  344:                 }
  345:             } elsif (/tcp dpt\:\Q$port\E/) {
  346:                 $count ++;
  347:                 last;
  348:             }
  349:         }
  350:         close(PIPE);
  351:     }
  352:     return $count;
  353: }
  354: 
  355: sub firewall_is_active {
  356:     my $status = 0;
  357:     if (-e '/proc/net/ip_tables_names') {
  358:         if (open(PIPE,'cat /proc/net/ip_tables_names |')) {
  359:             while(<PIPE>) {
  360:                 chomp();
  361:                 if (/^filter$/) {
  362:                     $status = 1;
  363:                     last;
  364:                 }
  365:             }
  366:             close(PIPE);
  367:         }
  368:     }
  369:     unless ($status) {
  370:         $status = &uses_firewalld();
  371:     }
  372:     return $status;
  373: }
  374: 
  375: sub firewall_close_port {
  376:     my ($iptables,$fw_chains,$lond_port,$iphost,$ports,$firewalld) = @_;
  377:     return 'inactive firewall' if (!&firewall_is_active());
  378:     return 'port number unknown' if !$lond_port;
  379:     return 'invalid firewall chain' unless (ref($fw_chains) eq 'ARRAY');
  380:     my (@okchains,$zone);
  381:     if ($firewalld) {
  382:         $zone = &get_default_zone();
  383:         return 'no default zone' if ($zone eq '');
  384:     } else {
  385:         my @badchains;
  386:         foreach my $chain (@{$fw_chains}) {
  387:             if ($chain =~ /^([\w\-]+)$/) {
  388:                 push(@okchains,$1);
  389:             } else {
  390:                 push(@badchains,$chain);
  391:             }
  392:         }
  393:         if (!@okchains) {
  394:             return 'None of the chain names has the expected format.'."\n";
  395:         }
  396:     }
  397:     if (ref($ports) ne 'ARRAY') {
  398:         return 'List of ports to close needed.';
  399:     }
  400:     foreach my $portnum (@{$ports}) {
  401:         my $port = '';
  402:         if ($portnum =~ /^(\d+)$/) {
  403:             $port = $1;
  404:         } else {
  405:             print "Skipped non-numeric port: $portnum\n"; 
  406:             next;
  407:         }
  408:         print "Closing firewall access on port $port.\n";
  409:         if (($port ne '') && ($port eq $lond_port)) {
  410:             my $output;
  411:             if ($firewalld) {
  412:                 my (%to_close,@port_error,@lond_port_close);
  413:                 my $cmd = 'firewall-cmd --list-rich-rules';
  414:                 if (open(PIPE,"$cmd |")) {
  415:                     while(<PIPE>) {
  416:                         if (/\Qrule family="ipv4" source address="\E([\d.]+)\Q\/32" port port="$port" protocol="tcp" accept\E/) {
  417:                             my $ip = $1;
  418:                             my $keepopen = 0;
  419:                             if (ref($iphost) eq 'HASH') {
  420:                                 if (exists($iphost->{$ip})) {
  421:                                     $keepopen = 1;
  422:                                 }
  423:                             }
  424:                             unless ($keepopen) {
  425:                                 $to_close{$ip} = $port;
  426:                             }
  427:                         }
  428:                     }
  429:                     close(PIPE);
  430:                 }
  431:                 if (keys(%to_close) > 0) {
  432:                     foreach my $ip (sort(keys(%to_close))) {
  433:                         my $cmd = 'firewall-cmd --zone='.$zone.' --remove-rich-rule \'rule family="ipv4" source address="'.$ip.'/32" port port="'.$port.'" protocol="tcp" accept\'';
  434:                         if (open(PIPE,"$cmd |")) {
  435:                             my $result = <PIPE>;
  436:                             chomp($result);
  437:                             close(PIPE);
  438:                             if ($result eq 'success') {
  439:                                 push(@lond_port_close,$ip);
  440:                             } else {
  441:                                 push(@port_error,$ip);
  442:                             }
  443:                         } else {
  444:                             push(@port_error,$ip);
  445:                         }
  446:                     }
  447:                 }
  448:                 if (@lond_port_close) {
  449:                     $output .= "Port closed for ".scalar(@lond_port_close)." IP addresses.\n";
  450:                 }
  451:                 if (@port_error) {
  452:                     $output .= "Error closing port for following IP addresses: ".join(', ',@port_error)."\n";
  453:                 }
  454:             } else {
  455:                 foreach my $fw_chain (@okchains) {
  456:                     my (%to_close,@port_error,@command_error,@lond_port_close);
  457:                     if (open(PIPE, "$iptables -n -L $fw_chain |")) {
  458:                         while (<PIPE>) {
  459:                             chomp();
  460:                             next unless (/dpt:\Q$port\E/);
  461:                             if (/^ACCEPT\s+tcp\s+\-{2}\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+/) {
  462:                                 my $ip = $1;
  463:                                 my $keepopen = 0;
  464:                                 if (ref($iphost) eq 'HASH') {
  465:                                     if (exists($iphost->{$ip})) {
  466:                                         $keepopen = 1; 
  467:                                     }
  468:                                 }
  469:                                 unless ($keepopen) {
  470:                                     $to_close{$ip} = $port;
  471:                                 }
  472:                             }
  473:                         }
  474:                         close(PIPE);
  475:                     }
  476:                     if (keys(%to_close) > 0) {
  477:                         foreach my $ip (keys(%to_close)) {
  478:                             my $firewall_command =
  479:                                 "$iptables -D $fw_chain -p tcp -s $ip -d 0/0 --dport $port -j ACCEPT";
  480:                             system($firewall_command);
  481:                             my $return_status = $?>>8;
  482:                             if ($return_status == 1) {
  483:                                 push(@port_error,$ip);
  484:                             } elsif ($return_status == 2) {
  485:                                 push(@command_error,$ip);
  486:                             } elsif ($return_status == 0) {
  487:                                 push(@lond_port_close,$ip);
  488:                             }
  489:                         }
  490:                     }
  491:                     if (@lond_port_close) {
  492:                         $output .= "Port closed for ".scalar(@lond_port_close)." IP addresses.\n";
  493:                     }
  494:                     if (@port_error) {
  495:                         $output .= "Error closing port for following IP addresses: ".join(', ',@port_error)."\n";
  496:                     }
  497:                     if (@command_error) {
  498:                         $output .= "Bad command error opening port for following IP addresses: ".
  499:                               join(', ',@command_error)."\n".
  500:                               'Command was: "'."$iptables -D $fw_chain -p tcp -s ".'$ip'." -d 0/0 --dport $port -j ACCEPT".'", where $ip is IP address'."\n";
  501:                     }
  502:                 }
  503:             }
  504:             if ($output) {
  505:                  print $output;
  506:             } else {
  507:                 print "No IP addresses required discontinuation of access.\n";
  508:             }
  509:         } else {
  510:             if ($firewalld) {
  511:                 my $to_close;
  512:                 if (open(PIPE,"firewall-cmd --list-rich-rules |")) {
  513:                     while(<PIPE>) {
  514:                         next unless (/\Qrule family="ipv4" port port="$port" protocol="tcp" accept\E/);
  515:                         $to_close = 1;
  516:                         last;
  517:                     }
  518:                     close(PIPE);
  519:                 }
  520:                 if ($to_close) {
  521:                     my $cmd = 'firewall-cmd --zone='.$zone.' --remove-rich-rule \'rule family="ipv4" port port="'.$port.'" protocol="tcp" accept\'';
  522:                     if (open(PIPE,"$cmd|")) {
  523:                         my $result = <PIPE>;
  524:                         chomp($result);
  525:                         close(PIPE);
  526:                         if ($result eq 'success') {
  527:                             print "Port: $port closed in zone: $zone.\n";
  528:                         } else {
  529:                             print "Error closing port: $port in zone: $zone.\n";
  530:                         }
  531:                     } else {
  532:                         print "Error closing port: $port in zone: $zone.\n";
  533:                     }
  534:                 }
  535:             } else {
  536:                 foreach my $fw_chain (@okchains) {
  537:                     my (@port_error,@command_error,@lond_port_close);
  538:                     my $to_close;
  539:                     if (open(PIPE, "$iptables -n -L $fw_chain |")) {
  540:                         while (<PIPE>) {
  541:                             chomp();
  542:                             next unless (/dpt:\Q$port\E/);
  543:                             $to_close = 1;
  544:                             last;
  545:                         }
  546:                         close(PIPE);
  547:                     }
  548:                     if ($to_close) {
  549:                         my $firewall_command =
  550:                             "$iptables -D $fw_chain -p tcp -d 0/0 --dport $port -j ACCEPT";
  551:                         system($firewall_command);
  552:                         my $return_status = $?>>8;
  553:                         if ($return_status == 1) {
  554:                             # Error
  555:                             print "Error closing port: $port for chain: $fw_chain.\n";
  556:                         } elsif ($return_status == 2) {
  557:                             # Bad command
  558:                             print "Bad command error closing port.  Command was\n".
  559:                                   "  ".$firewall_command."\n";
  560:                         } else {
  561:                             print "Port closed for chain $fw_chain.\n";
  562:                         }
  563:                     }
  564:                 }
  565:             }
  566:         }
  567:     }
  568:     return;
  569: }
  570: 
  571: sub firewall_close_anywhere {
  572:     my ($iptables,$fw_chain,$port,$firewalld) = @_;
  573:     my $zone; 
  574:     if ($firewalld) {
  575:         $zone = &get_default_zone();
  576:         if ($zone eq '') {
  577:             print 'no default zone';
  578: 	    return;
  579:         }
  580:     } else {
  581:         unless ($fw_chain =~ /^([\w\-]+)$/) {
  582:             print 'invalid chain';
  583: 	    return;
  584:         }
  585:     }
  586:     if ($firewalld) { 
  587:         my $to_close;
  588:         my $cmd = 'firewall-cmd --list-ports';
  589:         if (open(PIPE,"$cmd |")) {
  590:             my $currports = <PIPE>;
  591:             close(PIPE);
  592:             chomp($currports);
  593:             if (grep(/^\Q$port\E\/tcp/,split(/\s+/,$currports))) {
  594:                 $to_close = 1;
  595:             }
  596:         }
  597:         if ($to_close) {
  598:             my $cmd = 'firewall-cmd --zone='.$zone.' --remove-port='.$port.'/tcp';
  599:             if (open(PIPE,"$cmd |")) {
  600:                 my $result = <PIPE>;
  601:                 chomp($result);
  602:                 close(PIPE);
  603:                 if ($result eq 'success') {
  604:                     print 'Port '.$port.' closed for source "anywhere"'."\n";
  605:                 } else {
  606:                     print 'Error closing port '.$port.' for source "anywhere".'."\n";
  607:                 }
  608:             } else {
  609:                 print 'Error closing port '.$port.' for source "anywhere".'."\n";
  610:             }
  611:         }    
  612:     } elsif (open(PIPE, "$iptables --line-numbers -n -L $fw_chain |")) {
  613:         while (<PIPE>) {
  614:             next unless (/dpt:\Q$port\E/);
  615:             chomp();
  616:             if (/^(\d+)\s+ACCEPT\s+tcp\s+\-{2}\s+0\.0\.0\.0\/0\s+0\.0\.0\.0\/0/) {
  617:                 my $firewall_command = "$iptables -D $fw_chain $1";
  618:                 system($firewall_command);
  619:                 my $return_status = $?>>8;
  620:                 if ($return_status == 1) {
  621:                     print 'Error closing port '.$port.' for source "anywhere".'."\n";
  622:                 } elsif ($return_status == 2) {
  623:                     print 'Bad command error closing port '.$port.' for source "anywhere".  Command was'."\n".
  624:                           ' '.$firewall_command."\n";
  625:                 } else {
  626:                     print 'Port '.$port.' closed for source "anywhere"'."\n";
  627:                 }
  628:             }
  629:         }
  630:         close(PIPE);
  631:     }
  632: }
  633: 
  634: sub get_lond_port {
  635:     my $perlvarref=&LONCAPA::Configuration::read_conf();
  636:     my $lond_port;
  637:     if (ref($perlvarref) eq 'HASH') {
  638:         if (defined($perlvarref->{'londPort'})) {
  639:             $lond_port = $perlvarref->{'londPort'};
  640:         }
  641:     }
  642:     if (!$lond_port) {
  643:         print("Unable to determine lond port number from LON-CAPA configuration.\n");
  644:     }
  645:     return $lond_port;
  646: }
  647: 
  648: sub get_fw_chains {
  649:     my ($iptables,$distro) = @_;
  650:     if ($distro eq '') {
  651:         $distro = &get_distro();
  652:     }
  653:     my @fw_chains;
  654:     my $suse_config = "/etc/sysconfig/SuSEfirewall2";
  655:     my $ubuntu_config = "/etc/ufw/ufw.conf";
  656:     my $firewalld = &uses_firewalld($distro);
  657:     if ($firewalld) {
  658:         my ($dist,$version) = ($distro =~ /^([\D]+)(\d+)(?:|\-stream)$/);
  659:         if (((($dist eq 'rhes') || ($dist eq 'centos') || ($dist eq 'rocky') || ($dist eq 'alma')) &&
  660:              ($version >= 8)) || (($dist eq 'oracle') && ($version >= 7))) {
  661:             push(@fw_chains,'INPUT');
  662:         } else {
  663:             my $zone = &get_default_zone();
  664: 	    if ($zone ne '') {
  665:                 push(@fw_chains,'IN_'.$zone.'_allow');
  666:             } else {
  667:                 push(@fw_chains,'IN_public_allow');
  668:             }
  669:         }  
  670:     } elsif (-e $suse_config) {
  671:         push(@fw_chains,'input_ext');
  672:     } else {
  673:         my @posschains;
  674:         if (-e $ubuntu_config) {
  675:             @posschains = ('ufw-user-input','INPUT');
  676:         } else {
  677:             if ($distro =~ /^(debian|ubuntu|suse|sles)/) {
  678:                 @posschains = ('INPUT'); 
  679:             } elsif ($distro =~ /^(fedora|rhes|centos|scientific|oracle|rocky|alma)(\d+)(?:|\-stream)$/) {
  680:                 if ((($1 eq 'fedora') && ($2 > 15)) || (($1 ne 'fedora') && ($2 >= 7))) {
  681:                     @posschains = ('INPUT');
  682:                 } else {
  683:                     @posschains = ('RH-Firewall-1-INPUT','INPUT');
  684:                 }
  685:             }
  686:             if (!-e '/etc/sysconfig/iptables') {
  687:                 if (!-e '/var/lib/iptables') {
  688:                     unless ($distro =~ /^(debian|ubuntu)/) {
  689:                         print("Unable to find iptables file containing static definitions.\n");
  690:                     }
  691:                 }
  692:                 if ($distro =~ /^(fedora|rhes|centos|scientific|oracle|rocky|alma)(\d+){?:|\-stream)$/) {
  693:                     unless ((($1 eq 'fedora') && ($2 > 15)) || (($1 ne 'fedora') && ($2 >= 7))) {
  694:                         push(@fw_chains,'RH-Firewall-1-INPUT');
  695:                     }
  696:                 }
  697:             }
  698:         }
  699:         if ($iptables eq '') {
  700:             $iptables = &get_pathto_iptables();
  701:         }
  702:         my %counts;
  703:         if (open(PIPE,"$iptables -L -n |")) {
  704:             while(<PIPE>) {
  705:                 foreach my $chain (@posschains) {
  706:                     if (/(\Q$chain\E)/) {
  707:                         $counts{$1} ++;
  708:                     }
  709:                 }
  710:             }
  711:             close(PIPE);
  712:         }
  713:         foreach my $fw_chain (@posschains) {
  714:             if ($counts{$fw_chain}) {
  715:                 unless(grep(/^\Q$fw_chain\E$/,@fw_chains)) {
  716:                     push(@fw_chains,$fw_chain);
  717:                 }
  718:             }
  719:         }
  720:     }
  721:     return @fw_chains;
  722: }
  723: 
  724: sub get_default_zone {
  725:     my $cmd = 'firewall-cmd --get-default-zone';
  726:     my $zone;
  727:     if (open(PIPE,"$cmd |")) {
  728:         my $result = <PIPE>;
  729:         chomp($result);
  730:         close(PIPE);
  731:         ($zone) = ($result =~ /^(\w+)$/);
  732:     }
  733:     return $zone;
  734: }
  735: 
  736: sub get_pathto_iptables {
  737:     my $iptables;
  738:     if (-e '/sbin/iptables') {
  739:         $iptables = '/sbin/iptables';
  740:     } elsif (-e '/usr/sbin/iptables') {
  741:         $iptables = '/usr/sbin/iptables';
  742:     } else {
  743:         print("Unable to find iptables command.\n");
  744:     }
  745:     return $iptables;
  746: }
  747: 
  748: sub get_distro {
  749:     my $distro;
  750:     if (open(PIPE,"/home/httpd/perl/distprobe |")) {
  751:         $distro = <PIPE>;
  752:         close(PIPE);
  753:     }
  754:     return $distro;
  755: }
  756: 
  757: 1;
  758: __END__
  759: 
  760: =pod
  761: 
  762: =head1 NAME
  763: 
  764: B<LONCAPA::Firewall> - dynamic opening/closing of firewall ports
  765: 
  766: =head1 SYNOPSIS
  767: 
  768:  use lib '/home/httpd/lib/perl/';
  769:  use LONCAPA::Firewall;
  770: 
  771:  LONCAPA::Firewall::uses_firewalld();
  772:  LONCAPA::Firewall::firewall_open_port();
  773:  LONCAPA::Firewall::firewall_close_port();
  774:  LONCAPA::Firewall::firewall_is_port_open();
  775:  LONCAPA::Firewall::firewall_is_active();
  776:  LONCAPA::Firewall::firewall_close_anywhere();
  777: 
  778: =head1 DESCRIPTION
  779: 
  780: The scripts: /etc/init.d/loncontrol, used to stop or start LON-CAPA services, 
  781: as well as the setuid script /home/httpd/perl/lciptables, called by loncron 
  782: for housekeeping tasks, make use of the methods provided by this module to 
  783: open and close firewall ports (currently the default port: 5663), used
  784: for socket-based communication between LON-CAPA servers in the cluster
  785: of networked servers to which the server belongs. 
  786: 
  787: The following methods are available:
  788: 
  789: =over 4
  790: 
  791: =item LONCAPA::Firewall::uses_firewalld( $distro );
  792: 
  793: =back
  794: 
  795: =over 4
  796: 
  797: =item LONCAPA::Firewall::firewall_open_port( $iptables,$fw_chains,$lond_port,$iphost,$ports,$firewalld );
  798: 
  799: =back
  800: 
  801: =over 4
  802: 
  803: =item LONCAPA::Firewall::firewall_close_port( $iptables,$fw_chains,$lond_port,$iphost,$ports,$firewalld );
  804: 
  805: =back
  806: 
  807: =over 4
  808: 
  809: =item LONCAPA::Firewall::firewall_is_port_open( $iptables,$fw_chain,$port,$lond_port,$iphost,$curropen,$firewalld );
  810: 
  811: =back
  812: 
  813: =over 4
  814: 
  815: =item LONCAPA::Firewall::firewall_is_active();
  816: 
  817: =back
  818: 
  819: =over 4
  820: 
  821: =item LONCAPA::Firewall::firewall_close_anywhere( $iptables,$fw_chain,$port,$firewalld );
  822: 
  823: =back
  824: 
  825: =over 4
  826: 
  827: =item LONCAPA::Firewall::get_lond_port();
  828: 
  829: =back
  830: 
  831: =over 4
  832: 
  833: =item LONCAPA::Firewall::get_fw_chains( $iptables,$distro );
  834: 
  835: =back
  836: 
  837: =over 4
  838: 
  839: =item LONCAPA::Firewall::get_pathto_iptables();
  840: 
  841: =back
  842: 
  843: =over 4
  844: 
  845: =item LONCAPA::Firewall::get_distro();
  846: 
  847: =back
  848: 
  849: =head1 AUTHORS
  850: 
  851: This library is free software; you can redistribute it and/or
  852: modify it under the same terms as LON-CAPA itself.
  853: 
  854: =cut
  855: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>
500 Internal Server Error

Internal Server Error

The server encountered an internal error or misconfiguration and was unable to complete your request.

Please contact the server administrator at root@localhost to inform them of the time this error occurred, and the actions you performed just before this error.

More information about this error may be available in the server error log.