]> git.proxmox.com Git - pve-firewall.git/blobdiff - src/pve-firewall
bump version to 5.0.3
[pve-firewall.git] / src / pve-firewall
index 7de7355e8140244d6a2ad1f33c0492307cae0f05..5b62430fc6e7b6070f69e7cd88bdfb30c28c4509 100755 (executable)
 use strict;
 use warnings;
 use PVE::SafeSyslog;
-use POSIX ":sys_wait_h";
-use Fcntl ':flock';
-use Getopt::Long;
-use Time::HiRes qw (gettimeofday);
-use PVE::Tools qw(dir_glob_foreach file_read_firstline);
-use PVE::INotify;
-use PVE::Cluster qw(cfs_read_file);
-use PVE::RPCEnvironment;
-use PVE::CLIHandler;
-use PVE::Firewall;
-
-use base qw(PVE::CLIHandler);
-
-my $pve_firewall_pidfile = "/var/run/pve-firewall.pid";
+use PVE::Service::pve_firewall;
 
 $SIG{'__WARN__'} = sub {
     my $err = $@;
     my $t = $_[0];
     chomp $t;
-    print "$t\n";
-    syslog('warning', "WARNING: %s", $t);
+    print STDERR "$t\n";
+    syslog('warning', "%s", $t);
     $@ = $err;
 };
 
-initlog('pve-firewall');
-
-$ENV{'PATH'} = '/sbin:/bin:/usr/sbin:/usr/bin';
-
-die "please run as root\n" if $> != 0;
-
-PVE::INotify::inotify_init();
-
-my $rpcenv = PVE::RPCEnvironment->init('cli');
-
-$rpcenv->init_request();
-$rpcenv->set_language($ENV{LANG});
-$rpcenv->set_user('root@pam');
-
-my $commandline = [$0, @ARGV];
-
-$0 = "pve-firewall";
-
-sub restart_server {
-    my ($waittime) = @_;
-
-    syslog('info', "server shutdown (restart)");
-
-    $ENV{RESTART_PVE_FIREWALL} = 1;
-
-    sleep($waittime) if $waittime; # avoid high server load due to restarts
-
-    exec (@$commandline);
-    exit (-1); # never reached?
-}
-
-sub cleanup {
-    unlink "$pve_firewall_pidfile.lock";
-    unlink $pve_firewall_pidfile;
-}
-
-sub lockpidfile {
-    my $pidfile = shift;
-    my $lkfn = "$pidfile.lock";
-
-    if (!open (FLCK, ">>$lkfn")) {
-       my $msg = "can't aquire lock on file '$lkfn' - $!";
-       syslog ('err', $msg);
-       die "ERROR: $msg\n";
-    }
-
-    if (!flock (FLCK, LOCK_EX|LOCK_NB)) {
-       close (FLCK);
-        my $msg = "can't aquire lock '$lkfn' - $!";
-       syslog ('err', $msg);
-       die "ERROR: $msg\n";
-    }
-}
-
-sub writepidfile {
-    my $pidfile = shift;
-
-    if (!open (PIDFH, ">$pidfile")) {
-       my $msg = "can't open pid file '$pidfile' - $!";
-       syslog ('err', $msg);
-       die "ERROR: $msg\n";
-    } 
-    print PIDFH "$$\n";
-    close (PIDFH);
-}
-
-my $restart_request = 0;
-my $next_update = 0;
-
-my $cycle = 0; 
-my $updatetime = 10;
-
-my $initial_memory_usage;
-
-sub run_server {
-    my ($param) = @_;
-
-    # try to get the lock
-    lockpidfile($pve_firewall_pidfile);
-
-    # run in background
-    my $spid;
-
-    my $restart = $ENV{RESTART_PVE_FIREWALL};
-
-    delete $ENV{RESTART_PVE_FIREWALL};
-
-    if (!$param->{debug}) {
-       open STDIN,  '</dev/null' || die "can't read /dev/null";
-       open STDOUT, '>/dev/null' || die "can't write /dev/null";
-    }
-
-    if (!$restart && !$param->{debug}) {
-       $spid = fork();
-       if (!defined ($spid)) {
-           my $msg =  "can't put server into background - fork failed";
-           syslog('err', $msg);
-           die "ERROR: $msg\n";
-       } elsif ($spid) { # parent
-           exit (0);
-       }
-    } 
-
-    writepidfile($pve_firewall_pidfile);
-
-    open STDERR, '>&STDOUT' || die "can't close STDERR\n";
-    $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = sub { 
-       syslog('info' , "server closing");
-
-       $SIG{INT} = 'DEFAULT';
-
-       # wait for children
-       1 while (waitpid(-1, POSIX::WNOHANG()) > 0);
-       
-       syslog('info' , "clear firewall rules");
-       eval { PVE::Firewall::remove_pvefw_chains(); die "STOP";};
-       warn $@ if $@;
-
-       cleanup();
-
-       exit (0);
-    };
-
-    $SIG{HUP} = sub {
-       # wake up process, so this forces an immediate firewall rules update
-       syslog('info' , "received signal HUP (restart)");
-       $restart_request = 1;
-    };
-
-    if ($restart) {
-       syslog('info' , "restarting server");
-    } else {
-       syslog('info' , "starting server");
-    }
-
-    for (;;) { # forever
-
-       eval {
-
-           local $SIG{'__WARN__'} = 'IGNORE'; # do not fill up logs
-
-           $next_update = time() + $updatetime;
-
-           my ($ccsec, $cusec) = gettimeofday ();
-           eval {
-               PVE::Cluster::cfs_update();
-               PVE::Firewall::update();
-           };
-           my $err = $@;
-
-           if ($err) {
-               syslog('err', "status update error: $err");
-           }
-
-           my ($ccsec_end, $cusec_end) = gettimeofday ();
-           my $cptime = ($ccsec_end-$ccsec) + ($cusec_end - $cusec)/1000000;
-
-           syslog('info', sprintf("firewall update time (%.3f seconds)", $cptime))
-               if ($cptime > 5);
-
-           $cycle++;
-
-           my $mem = PVE::ProcFSTools::read_memory_usage();
-
-           if (!defined($initial_memory_usage) || ($cycle < 10)) {
-               $initial_memory_usage = $mem->{resident};
-           } else {
-               my $diff = $mem->{resident} - $initial_memory_usage;
-               if ($diff > 5*1024*1024) {
-                   syslog ('info', "restarting server after $cycle cycles to " .
-                           "reduce memory usage (free $mem->{resident} ($diff) bytes)");
-                   restart_server();
-               }
-           }
-
-           my $wcount = 0;
-           while ((time() < $next_update) && 
-                  ($wcount < $updatetime) && # protect against time wrap
-                  !$restart_request) { $wcount++; sleep (1); };
-
-           restart_server() if $restart_request;
-       };
-
-       my $err = $@;
-    
-       if ($err) {
-           syslog ('err', "ERROR: $err");
-           restart_server(5);
-           exit (0);
-       }
-    }
-}
-
-__PACKAGE__->register_method ({
-    name => 'start',
-    path => 'start',
-    method => 'POST',
-    description => "Start the Proxmox VE firewall service.",
-    parameters => {
-       additionalProperties => 0,
-       properties => {
-           debug => {
-               description => "Debug mode - stay in foreground",
-               type => "boolean",
-               optional => 1,
-               default => 0,
-           },
-       },
-    },
-    returns => { type => 'null' },
-
-    code => sub {
-       my ($param) = @_;
-
-       run_server($param);
-
-       return undef;
-    }});
-
-__PACKAGE__->register_method ({
-    name => 'stop',
-    path => 'stop',
-    method => 'POST',
-    description => "Stop firewall. This will remove all rules installed by this script. The host is unprotected afterwards.",
-    parameters => {
-       additionalProperties => 0,
-       properties => {},
-    },
-    returns => { type => 'null' },
-
-    code => sub {
-       my ($param) = @_;
-
-       my $pid = int(PVE::Tools::file_read_firstline($pve_firewall_pidfile) || 0);
-
-       if ($pid) {
-           if (PVE::ProcFSTools::check_process_running($pid)) {
-               kill(15, $pid); # send TERM signal
-               # give max 5 seconds to shut down
-               for (my $i = 0; $i < 5; $i++) {
-                   last if !PVE::ProcFSTools::check_process_running($pid);
-                   sleep (1);
-               }
-       
-               # to be sure
-               kill(9, $pid); 
-               waitpid($pid, 0);
-           }
-           if (-f $pve_firewall_pidfile) {
-               # try to get the lock
-               lockpidfile($pve_firewall_pidfile);
-               cleanup();
-           }
-       }
-
-       return undef;
-    }});
-
-__PACKAGE__->register_method ({
-    name => 'status',
-    path => 'status',
-    method => 'GET',
-    description => "Get firewall status.",
-    parameters => {
-       additionalProperties => 0,
-       properties => {},
-    },
-    returns => { 
-       type => 'object',
-       additionalProperties => 0,
-       properties => {
-           status => {
-               type => 'string',
-               enum => ['unknown', 'stopped', 'active'],
-           },
-           changes => {
-               description => "Set when there are pending changes.",
-               type => 'boolean',
-               optional => 1,
-           }
-       },
-    },
-    code => sub {
-       my ($param) = @_;
-
-       local $SIG{'__WARN__'} = 'DEFAULT'; # do not fill up syslog
-
-       my $code = sub {
-
-           my $pid = int(PVE::Tools::file_read_firstline($pve_firewall_pidfile) || 0);
-           my $running = PVE::ProcFSTools::check_process_running($pid);
-
-           my $status = $running ? 'active' : 'stopped';
-
-           my $res = { status => $status };
-           if ($status eq 'active') {
-               my ($ruleset, $ipset_ruleset) = PVE::Firewall::compile();
-
-               my (undef, undef, $ipset_changes) = PVE::Firewall::get_ipset_cmdlist($ipset_ruleset);
-               my (undef, $ruleset_changes) = PVE::Firewall::get_ruleset_cmdlist($ruleset);
-             
-               $res->{changes} = ($ipset_changes || $ruleset_changes) ? 1 : 0;
-           }
-
-           return $res;
-       };
-
-       return PVE::Firewall::run_locked($code);
-    }});
-
-__PACKAGE__->register_method ({
-    name => 'compile',
-    path => 'compile',
-    method => 'POST',
-    description => "Compile amd print firewall rules. This is only for testing.",
-    parameters => {
-       additionalProperties => 0,
-       properties => {},
-    },
-    returns => { type => 'null' },
-
-    code => sub {
-       my ($param) = @_;
-
-       local $SIG{'__WARN__'} = 'DEFAULT'; # do not fill up syslog
-
-       my $code = sub {
-           my ($ruleset, $ipset_ruleset) = PVE::Firewall::compile();
-
-           my (undef, undef, $ipset_changes) = PVE::Firewall::get_ipset_cmdlist($ipset_ruleset, 1);
-           my (undef, $ruleset_changes) = PVE::Firewall::get_ruleset_cmdlist($ruleset, 1);
-           if ($ipset_changes || $ruleset_changes) {
-               print "detected changes\n";
-           } else {
-               print "no changes\n";
-           }
-       };
-
-       PVE::Firewall::run_locked($code);
-
-       return undef;
-    }});
-
-my $nodename = PVE::INotify::nodename();
-
-my $cmddef = {
-    start => [ __PACKAGE__, 'start', []],
-    stop => [ __PACKAGE__, 'stop', []],
-    compile => [ __PACKAGE__, 'compile', []],
-    status => [ __PACKAGE__, 'status', [], undef, sub {
-       my $res = shift;
-       if ($res->{changes}) {
-           print "Status: $res->{status} (pending changes)\n";
-       } else {
-           print "Status: $res->{status}\n";
-       }
-    }],
- };
-
-my $cmd = shift;
-
-PVE::CLIHandler::handle_cmd($cmddef, $0, $cmd, \@ARGV, undef, $0);
-
-exit (0);
-
-__END__
-
-=head1 NAME
-                                          
-pvestatd - PVE Firewall Daemon
-
-=head1 SYNOPSIS
-
-pve-firewall
-
-=head1 DESCRIPTION
-
-This service updates iptables rules periodically.
-
-
+my $prepare = sub {
+    my $rpcenv = PVE::RPCEnvironment->init('cli');
 
+    $rpcenv->init_request();
+    $rpcenv->set_language($ENV{LANG});
+    $rpcenv->set_user('root@pam');
+};
 
+PVE::Service::pve_firewall->run_cli_handler(prepare => $prepare);