]>
git.proxmox.com Git - pve-firewall.git/blob - src/pve-firewall
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
;
16 use PVE
::FirewallSimulator
;
19 use base
qw(PVE::CLIHandler);
21 my $pve_firewall_pidfile = "/var/run/pve-firewall.pid";
23 $SIG{'__WARN__'} = sub {
28 syslog
('warning', "WARNING: %s", $t);
32 initlog
('pve-firewall');
34 $ENV{'PATH'} = '/sbin:/bin:/usr/sbin:/usr/bin';
36 die "please run as root\n" if $> != 0;
38 PVE
::INotify
::inotify_init
();
40 my $rpcenv = PVE
::RPCEnvironment-
>init('cli');
42 $rpcenv->init_request();
43 $rpcenv->set_language($ENV{LANG
});
44 $rpcenv->set_user('root@pam');
46 my $nodename = PVE
::INotify
::nodename
();
48 my $commandline = [$0, @ARGV];
55 syslog
('info', "server shutdown (restart)");
57 $ENV{RESTART_PVE_FIREWALL
} = 1;
59 sleep($waittime) if $waittime; # avoid high server load due to restarts
62 exit (-1); # never reached?
66 unlink "$pve_firewall_pidfile.lock";
67 unlink $pve_firewall_pidfile;
72 my $lkfn = "$pidfile.lock";
74 if (!open (FLCK
, ">>$lkfn")) {
75 my $msg = "can't aquire lock on file '$lkfn' - $!";
80 if (!flock (FLCK
, LOCK_EX
|LOCK_NB
)) {
82 my $msg = "can't aquire lock '$lkfn' - $!";
91 if (!open (PIDFH
, ">$pidfile")) {
92 my $msg = "can't open pid file '$pidfile' - $!";
100 my $restart_request = 0;
106 my $initial_memory_usage;
111 # try to get the lock
112 lockpidfile
($pve_firewall_pidfile);
117 my $restart = $ENV{RESTART_PVE_FIREWALL
};
119 delete $ENV{RESTART_PVE_FIREWALL
};
121 PVE
::Cluster
::cfs_update
();
123 PVE
::Firewall
::init
();
125 if (!$param->{debug
}) {
126 open STDIN
, '</dev/null' || die "can't read /dev/null";
127 open STDOUT
, '>/dev/null' || die "can't write /dev/null";
130 if (!$restart && !$param->{debug
}) {
132 if (!defined ($spid)) {
133 my $msg = "can't put server into background - fork failed";
136 } elsif ($spid) { # parent
141 writepidfile
($pve_firewall_pidfile);
143 open STDERR
, '>&STDOUT' || die "can't close STDERR\n";
145 $SIG{INT
} = $SIG{TERM
} = $SIG{QUIT
} = sub {
146 syslog
('info' , "server closing");
148 $SIG{INT
} = 'DEFAULT';
151 1 while (waitpid(-1, POSIX
::WNOHANG
()) > 0);
153 syslog
('info' , "clear firewall rules");
154 eval { PVE
::Firewall
::remove_pvefw_chains
(); die "STOP";};
163 # wake up process, so this forces an immediate firewall rules update
164 syslog
('info' , "received signal HUP (restart)");
165 $restart_request = 1;
169 syslog
('info' , "restarting server");
171 syslog
('info' , "starting server");
178 local $SIG{'__WARN__'} = 'IGNORE'; # do not fill up logs
180 $next_update = time() + $updatetime;
182 my ($ccsec, $cusec) = gettimeofday
();
184 PVE
::Cluster
::cfs_update
();
185 PVE
::Firewall
::update
();
190 syslog
('err', "status update error: $err");
193 my ($ccsec_end, $cusec_end) = gettimeofday
();
194 my $cptime = ($ccsec_end-$ccsec) + ($cusec_end - $cusec)/1000000;
196 syslog
('info', sprintf("firewall update time (%.3f seconds)", $cptime))
201 my $mem = PVE
::ProcFSTools
::read_memory_usage
();
203 if (!defined($initial_memory_usage) || ($cycle < 10)) {
204 $initial_memory_usage = $mem->{resident
};
206 my $diff = $mem->{resident
} - $initial_memory_usage;
207 if ($diff > 5*1024*1024) {
208 syslog
('info', "restarting server after $cycle cycles to " .
209 "reduce memory usage (free $mem->{resident} ($diff) bytes)");
215 while ((time() < $next_update) &&
216 ($wcount < $updatetime) && # protect against time wrap
217 !$restart_request) { $wcount++; sleep (1); };
219 restart_server
() if $restart_request;
225 syslog
('err', "ERROR: $err");
232 __PACKAGE__-
>register_method ({
236 description
=> "Start the Proxmox VE firewall service.",
238 additionalProperties
=> 0,
241 description
=> "Debug mode - stay in foreground",
248 returns
=> { type
=> 'null' },
258 __PACKAGE__-
>register_method ({
262 description
=> "Stop firewall. This removes all Proxmox VE related iptable rules. The host is unprotected afterwards.",
264 additionalProperties
=> 0,
267 returns
=> { type
=> 'null' },
272 my $pid = int(PVE
::Tools
::file_read_firstline
($pve_firewall_pidfile) || 0);
275 if (PVE
::ProcFSTools
::check_process_running
($pid)) {
276 kill(15, $pid); # send TERM signal
277 # give max 5 seconds to shut down
278 for (my $i = 0; $i < 5; $i++) {
279 last if !PVE
::ProcFSTools
::check_process_running
($pid);
287 if (-f
$pve_firewall_pidfile) {
288 # try to get the lock
289 lockpidfile
($pve_firewall_pidfile);
297 __PACKAGE__-
>register_method ({
301 description
=> "Get firewall status.",
303 additionalProperties
=> 0,
308 additionalProperties
=> 0,
312 enum
=> ['unknown', 'stopped', 'active'],
315 description
=> "Set when there are pending changes.",
324 local $SIG{'__WARN__'} = 'DEFAULT'; # do not fill up syslog
328 my $pid = int(PVE
::Tools
::file_read_firstline
($pve_firewall_pidfile) || 0);
329 my $running = PVE
::ProcFSTools
::check_process_running
($pid);
331 my $status = $running ?
'active' : 'stopped';
333 my $res = { status
=> $status };
334 if ($status eq 'active') {
335 my ($ruleset, $ipset_ruleset) = PVE
::Firewall
::compile
();
337 my (undef, undef, $ipset_changes) = PVE
::Firewall
::get_ipset_cmdlist
($ipset_ruleset);
338 my (undef, $ruleset_changes) = PVE
::Firewall
::get_ruleset_cmdlist
($ruleset);
340 $res->{changes
} = ($ipset_changes || $ruleset_changes) ?
1 : 0;
346 return PVE
::Firewall
::run_locked
($code);
349 __PACKAGE__-
>register_method ({
353 description
=> "Compile and print firewall rules. This is useful for testing.",
355 additionalProperties
=> 0,
358 returns
=> { type
=> 'null' },
363 local $SIG{'__WARN__'} = 'DEFAULT'; # do not fill up syslog
366 my ($ruleset, $ipset_ruleset) = PVE
::Firewall
::compile
();
368 my (undef, undef, $ipset_changes) = PVE
::Firewall
::get_ipset_cmdlist
($ipset_ruleset, 1);
369 my (undef, $ruleset_changes) = PVE
::Firewall
::get_ruleset_cmdlist
($ruleset, 1);
370 if ($ipset_changes || $ruleset_changes) {
371 print "detected changes\n";
373 print "no changes\n";
377 PVE
::Firewall
::run_locked
($code);
382 __PACKAGE__-
>register_method ({
386 description
=> "Simulate firewall rules.",
388 additionalProperties
=> 0,
391 description
=> "Verbose output.",
397 description
=> "Source zone.",
399 pattern
=> '(host|outside|vm\d+|ct\d+|vmbr\d+/\S+)',
401 default => 'outside',
404 description
=> "Destination zone.",
406 pattern
=> '(host|outside|vm\d+|ct\d+|vmbr\d+/\S+)',
411 description
=> "Protocol.",
413 pattern
=> '(tcp|udp)',
418 description
=> "Destination port.",
425 description
=> "Source port.",
432 description
=> "Source IP address.",
433 type
=> 'string', format
=> 'ipv4',
437 description
=> "Destination IP address.",
438 type
=> 'string', format
=> 'ipv4',
443 returns
=> { type
=> 'null' },
447 my ($ruleset, $ipset_ruleset) = PVE
::Firewall
::compile
();
449 PVE
::FirewallSimulator
::debug
($param->{verbose
} || 0);
451 my $host_ip = PVE
::Cluster
::remote_node_ip
($nodename);
453 PVE
::FirewallSimulator
::reset_trace
();
454 print Dumper
($ruleset) if $param->{verbose
};
457 from
=> $param->{from
},
459 proto
=> $param->{protocol
} || 'tcp',
460 source
=> $param->{source
},
461 dest
=> $param->{dest
},
462 dport
=> $param->{dport
},
463 sport
=> $param->{sport
},
466 if (!defined($test->{to
})) {
467 $test->{to
} = 'host';
468 PVE
::FirewallSimulator
::add_trace
("Set Zone: to => '$test->{to}'\n");
470 if (!defined($test->{from
})) {
471 $test->{from
} = 'outside',
472 PVE
::FirewallSimulator
::add_trace
("Set Zone: from => '$test->{from}'\n");
475 my $vmdata = PVE
::Firewall
::read_local_vm_config
();
477 print "Test packet:\n";
479 foreach my $k (qw(from to proto source dest dport sport)) {
480 printf(" %-8s: %s\n", $k, $test->{$k}) if defined($test->{$k});
483 $test->{action
} = 'QUERY';
485 my $res = PVE
::FirewallSimulator
::simulate_firewall
($ruleset, $ipset_ruleset,
486 $host_ip, $vmdata, $test);
488 print "ACTION: $res\n";
494 start
=> [ __PACKAGE__
, 'start', []],
495 stop
=> [ __PACKAGE__
, 'stop', []],
496 compile
=> [ __PACKAGE__
, 'compile', []],
497 simulate
=> [ __PACKAGE__
, 'simulate', []],
498 status
=> [ __PACKAGE__
, 'status', [], undef, sub {
500 if ($res->{changes
}) {
501 print "Status: $res->{status} (pending changes)\n";
503 print "Status: $res->{status}\n";
510 PVE
::CLIHandler
::handle_cmd
($cmddef, $0, $cmd, \
@ARGV, undef, $0);
518 pve-firewall - PVE Firewall Daemon
526 This service updates iptables rules periodically.
528 =include pve_copyright