6 use POSIX
":sys_wait_h";
9 use Time
::HiRes qw
(gettimeofday
);
10 use PVE
::Tools
qw(dir_glob_foreach file_read_firstline);
12 use PVE
::Cluster
qw(cfs_read_file);
13 use PVE
::RPCEnvironment
;
17 use base
qw(PVE::CLIHandler);
19 my $pve_firewall_pidfile = "/var/run/pve-firewall.pid";
21 $SIG{'__WARN__'} = sub {
26 syslog
('warning', "WARNING: %s", $t);
30 initlog
('pve-firewall');
32 $ENV{'PATH'} = '/sbin:/bin:/usr/sbin:/usr/bin';
34 die "please run as root\n" if $> != 0;
36 PVE
::INotify
::inotify_init
();
38 my $rpcenv = PVE
::RPCEnvironment-
>init('cli');
40 $rpcenv->init_request();
41 $rpcenv->set_language($ENV{LANG
});
42 $rpcenv->set_user('root@pam');
44 my $commandline = [$0, @ARGV];
51 syslog
('info', "server shutdown (restart)");
53 $ENV{RESTART_PVE_FIREWALL
} = 1;
55 sleep($waittime) if $waittime; # avoid high server load due to restarts
58 exit (-1); # never reached?
62 unlink "$pve_firewall_pidfile.lock";
63 unlink $pve_firewall_pidfile;
68 my $lkfn = "$pidfile.lock";
70 if (!open (FLCK
, ">>$lkfn")) {
71 my $msg = "can't aquire lock on file '$lkfn' - $!";
76 if (!flock (FLCK
, LOCK_EX
|LOCK_NB
)) {
78 my $msg = "can't aquire lock '$lkfn' - $!";
87 if (!open (PIDFH
, ">$pidfile")) {
88 my $msg = "can't open pid file '$pidfile' - $!";
96 my $restart_request = 0;
102 my $initial_memory_usage;
107 # try to get the lock
108 lockpidfile
($pve_firewall_pidfile);
113 my $restart = $ENV{RESTART_PVE_FIREWALL
};
115 delete $ENV{RESTART_PVE_FIREWALL
};
117 if (!$param->{debug
}) {
118 open STDIN
, '</dev/null' || die "can't read /dev/null";
119 open STDOUT
, '>/dev/null' || die "can't write /dev/null";
122 if (!$restart && !$param->{debug
}) {
124 if (!defined ($spid)) {
125 my $msg = "can't put server into background - fork failed";
128 } elsif ($spid) { # parent
133 writepidfile
($pve_firewall_pidfile);
135 open STDERR
, '>&STDOUT' || die "can't close STDERR\n";
137 $SIG{INT
} = $SIG{TERM
} = $SIG{QUIT
} = sub {
138 syslog
('info' , "server closing");
140 $SIG{INT
} = 'DEFAULT';
143 1 while (waitpid(-1, POSIX
::WNOHANG
()) > 0);
145 syslog
('info' , "clear firewall rules");
146 eval { PVE
::Firewall
::remove_pvefw_chains
(); die "STOP";};
155 # wake up process, so this forces an immediate firewall rules update
156 syslog
('info' , "received signal HUP (restart)");
157 $restart_request = 1;
161 syslog
('info' , "restarting server");
163 syslog
('info' , "starting server");
170 local $SIG{'__WARN__'} = 'IGNORE'; # do not fill up logs
172 $next_update = time() + $updatetime;
174 my ($ccsec, $cusec) = gettimeofday
();
176 PVE
::Cluster
::cfs_update
();
177 PVE
::Firewall
::update
();
182 syslog
('err', "status update error: $err");
185 my ($ccsec_end, $cusec_end) = gettimeofday
();
186 my $cptime = ($ccsec_end-$ccsec) + ($cusec_end - $cusec)/1000000;
188 syslog
('info', sprintf("firewall update time (%.3f seconds)", $cptime))
193 my $mem = PVE
::ProcFSTools
::read_memory_usage
();
195 if (!defined($initial_memory_usage) || ($cycle < 10)) {
196 $initial_memory_usage = $mem->{resident
};
198 my $diff = $mem->{resident
} - $initial_memory_usage;
199 if ($diff > 5*1024*1024) {
200 syslog
('info', "restarting server after $cycle cycles to " .
201 "reduce memory usage (free $mem->{resident} ($diff) bytes)");
207 while ((time() < $next_update) &&
208 ($wcount < $updatetime) && # protect against time wrap
209 !$restart_request) { $wcount++; sleep (1); };
211 restart_server
() if $restart_request;
217 syslog
('err', "ERROR: $err");
224 __PACKAGE__-
>register_method ({
228 description
=> "Start the Proxmox VE firewall service.",
230 additionalProperties
=> 0,
233 description
=> "Debug mode - stay in foreground",
240 returns
=> { type
=> 'null' },
250 __PACKAGE__-
>register_method ({
254 description
=> "Stop firewall. This will remove all rules installed by this script. The host is unprotected afterwards.",
256 additionalProperties
=> 0,
259 returns
=> { type
=> 'null' },
264 my $pid = int(PVE
::Tools
::file_read_firstline
($pve_firewall_pidfile) || 0);
267 if (PVE
::ProcFSTools
::check_process_running
($pid)) {
268 kill(15, $pid); # send TERM signal
269 # give max 5 seconds to shut down
270 for (my $i = 0; $i < 5; $i++) {
271 last if !PVE
::ProcFSTools
::check_process_running
($pid);
279 if (-f
$pve_firewall_pidfile) {
280 # try to get the lock
281 lockpidfile
($pve_firewall_pidfile);
289 __PACKAGE__-
>register_method ({
293 description
=> "Get firewall status.",
295 additionalProperties
=> 0,
300 additionalProperties
=> 0,
304 enum
=> ['unknown', 'stopped', 'active'],
307 description
=> "Set when there are pending changes.",
316 local $SIG{'__WARN__'} = 'DEFAULT'; # do not fill up syslog
320 my $pid = int(PVE
::Tools
::file_read_firstline
($pve_firewall_pidfile) || 0);
321 my $running = PVE
::ProcFSTools
::check_process_running
($pid);
323 my $status = $running ?
'active' : 'stopped';
325 my $res = { status
=> $status };
326 if ($status eq 'active') {
327 my ($ruleset, $ipset_ruleset) = PVE
::Firewall
::compile
();
329 my (undef, undef, $ipset_changes) = PVE
::Firewall
::get_ipset_cmdlist
($ipset_ruleset);
330 my (undef, $ruleset_changes) = PVE
::Firewall
::get_ruleset_cmdlist
($ruleset);
332 $res->{changes
} = ($ipset_changes || $ruleset_changes) ?
1 : 0;
338 return PVE
::Firewall
::run_locked
($code);
341 __PACKAGE__-
>register_method ({
345 description
=> "Compile amd print firewall rules. This is only for testing.",
347 additionalProperties
=> 0,
350 returns
=> { type
=> 'null' },
355 local $SIG{'__WARN__'} = 'DEFAULT'; # do not fill up syslog
358 my ($ruleset, $ipset_ruleset) = PVE
::Firewall
::compile
();
360 my (undef, undef, $ipset_changes) = PVE
::Firewall
::get_ipset_cmdlist
($ipset_ruleset, 1);
361 my (undef, $ruleset_changes) = PVE
::Firewall
::get_ruleset_cmdlist
($ruleset, 1);
362 if ($ipset_changes || $ruleset_changes) {
363 print "detected changes\n";
365 print "no changes\n";
369 PVE
::Firewall
::run_locked
($code);
374 my $nodename = PVE
::INotify
::nodename
();
377 start
=> [ __PACKAGE__
, 'start', []],
378 stop
=> [ __PACKAGE__
, 'stop', []],
379 compile
=> [ __PACKAGE__
, 'compile', []],
380 status
=> [ __PACKAGE__
, 'status', [], undef, sub {
382 if ($res->{changes
}) {
383 print "Status: $res->{status} (pending changes)\n";
385 print "Status: $res->{status}\n";
392 PVE
::CLIHandler
::handle_cmd
($cmddef, $0, $cmd, \
@ARGV, undef, $0);
400 pvestatd - PVE Firewall Daemon
408 This service updates iptables rules periodically.