use strict;
use warnings;
-use PVE::SafeSyslog;
-use PVE::Daemon;
-use Time::HiRes qw (gettimeofday);
-use PVE::Tools qw(dir_glob_foreach file_read_firstline);
-use PVE::ProcFSTools;
-use PVE::INotify;
+use Data::Dumper;
+use Time::HiRes qw (gettimeofday usleep);
+
+use PVE::CLIHandler;
use PVE::Cluster qw(cfs_read_file);
+use PVE::Corosync;
+use PVE::Daemon;
+use PVE::INotify;
+use PVE::ProcFSTools;
use PVE::RPCEnvironment;
-use PVE::CLIHandler;
+use PVE::SafeSyslog;
+use PVE::Tools qw(dir_glob_foreach file_read_firstline);
+
use PVE::Firewall;
use PVE::FirewallSimulator;
-use Data::Dumper;
+use PVE::FirewallSimulator qw($bridge_interface_pattern);
use base qw(PVE::Daemon);
my $nodename = PVE::INotify::nodename();
sub init {
-
PVE::Cluster::cfs_update();
-
+
PVE::Firewall::init();
}
-my $restart_request = 0;
-my $next_update = 0;
-
-my $cycle = 0;
+my ($next_update, $cycle, $restart_request) = (0, 0, 0);
my $updatetime = 10;
my $initial_memory_usage;
sub shutdown {
my ($self) = @_;
- syslog('info' , "server closing");
+ syslog('info' , "server shutting down");
# wait for children
1 while (waitpid(-1, POSIX::WNOHANG()) > 0);
-
- syslog('info' , "clear firewall rules");
+
+ syslog('info' , "clear PVE-generated firewall rules");
eval { PVE::Firewall::remove_pvefw_chains(); };
warn $@ if $@;
local $SIG{'__WARN__'} = 'IGNORE'; # do not fill up logs
for (;;) { # forever
-
$next_update = time() + $updatetime;
my ($ccsec, $cusec) = gettimeofday ();
PVE::Cluster::cfs_update();
PVE::Firewall::update();
};
- my $err = $@;
-
- if ($err) {
+ if (my $err = $@) {
syslog('err', "status update error: $err");
}
- my ($ccsec_end, $cusec_end) = gettimeofday ();
+ my ($ccsec_end, $cusec_end) = gettimeofday();
my $cptime = ($ccsec_end-$ccsec) + ($cusec_end - $cusec)/1000000;
syslog('info', sprintf("firewall update time (%.3f seconds)", $cptime))
$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) {
+ if ($diff > 5 * 1024 * 1024) {
syslog ('info', "restarting server after $cycle cycles to " .
"reduce memory usage (free $mem->{resident} ($diff) bytes)");
$self->restart_daemon();
}
my $wcount = 0;
- while ((time() < $next_update) &&
+ while ((time() < $next_update) &&
($wcount < $updatetime) && # protect against time wrap
!$restart_request) { $wcount++; sleep (1); };
-
+
$self->restart_daemon() if $restart_request;
}
}
$daemon->register_start_command("Start the Proxmox VE firewall service.");
$daemon->register_restart_command(1, "Restart the Proxmox VE firewall service.");
-$daemon->register_stop_command("Stop firewall. This removes all Proxmox VE " .
- "related iptable rules. " .
- "The host is unprotected afterwards.");
+$daemon->register_stop_command(
+ "Stop the Proxmox VE firewall service. Note, stopping actively removes all Proxmox VE related"
+ ." iptable rules rendering the host potentially unprotected."
+);
__PACKAGE__->register_method ({
name => 'status',
method => 'GET',
description => "Get firewall status.",
parameters => {
- additionalProperties => 0,
+ additionalProperties => 0,
properties => {},
},
- returns => {
+ returns => {
type => 'object',
additionalProperties => 0,
properties => {
my $res = { status => $status };
- my $verbose = 1; # show syntax errors
- my $cluster_conf = PVE::Firewall::load_clusterfw_conf(undef, $verbose);
+ PVE::Firewall::set_verbose(1); # show syntax errors
+
+ my $cluster_conf = PVE::Firewall::load_clusterfw_conf(undef);
$res->{enable} = $cluster_conf->{options}->{enable} ? 1 : 0;
if ($status eq 'running') {
-
- my ($ruleset, $ipset_ruleset, $rulesetv6) = PVE::Firewall::compile($cluster_conf, undef, undef, $verbose);
- $verbose = 0; # do not show iptables details
- my (undef, undef, $ipset_changes) = PVE::Firewall::get_ipset_cmdlist($ipset_ruleset, $verbose);
- my ($test, $ruleset_changes) = PVE::Firewall::get_ruleset_cmdlist($ruleset, $verbose);
- my (undef, $ruleset_changesv6) = PVE::Firewall::get_ruleset_cmdlist($rulesetv6, $verbose, "ip6tables");
+ my ($ruleset, $ipset_ruleset, $rulesetv6, $ebtables_ruleset) = PVE::Firewall::compile($cluster_conf, undef, undef);
- $res->{changes} = ($ipset_changes || $ruleset_changes || $ruleset_changesv6) ? 1 : 0;
+ PVE::Firewall::set_verbose(0); # do not show iptables details
+ my (undef, undef, $ipset_changes) = PVE::Firewall::get_ipset_cmdlist($ipset_ruleset);
+ my ($test, $ruleset_changes) = PVE::Firewall::get_ruleset_cmdlist($ruleset->{filter});
+ my (undef, $ruleset_changesv6) = PVE::Firewall::get_ruleset_cmdlist($rulesetv6->{filter}, "ip6tables");
+ my (undef, $ruleset_changes_raw) = PVE::Firewall::get_ruleset_cmdlist($ruleset->{raw}, undef, 'raw');
+ my (undef, $ruleset_changesv6_raw) = PVE::Firewall::get_ruleset_cmdlist($rulesetv6->{raw}, "ip6tables", 'raw');
+ my (undef, $ebtables_changes) = PVE::Firewall::get_ebtables_cmdlist($ebtables_ruleset);
+
+ $res->{changes} = ($ipset_changes || $ruleset_changes || $ruleset_changesv6 || $ebtables_changes || $ruleset_changes_raw || $ruleset_changesv6_raw) ? 1 : 0;
}
return $res;
method => 'GET',
description => "Compile and print firewall rules. This is useful for testing.",
parameters => {
- additionalProperties => 0,
+ additionalProperties => 0,
properties => {},
},
returns => { type => 'null' },
my $code = sub {
- my $verbose = 1;
+ PVE::Firewall::set_verbose(1);
- my $cluster_conf = PVE::Firewall::load_clusterfw_conf(undef, $verbose);
- my ($ruleset, $ipset_ruleset, $rulesetv6) = PVE::Firewall::compile($cluster_conf, undef, undef, $verbose);
+ my $cluster_conf = PVE::Firewall::load_clusterfw_conf(undef);
+ my ($ruleset, $ipset_ruleset, $rulesetv6, $ebtables_ruleset) = PVE::Firewall::compile($cluster_conf, undef, undef);
print "ipset cmdlist:\n";
- my (undef, undef, $ipset_changes) = PVE::Firewall::get_ipset_cmdlist($ipset_ruleset, $verbose);
+ my (undef, undef, $ipset_changes) = PVE::Firewall::get_ipset_cmdlist($ipset_ruleset);
print "\niptables cmdlist:\n";
- my (undef, $ruleset_changes) = PVE::Firewall::get_ruleset_cmdlist($ruleset, $verbose);
+ my (undef, $ruleset_changes) = PVE::Firewall::get_ruleset_cmdlist($ruleset->{filter});
print "\nip6tables cmdlist:\n";
- my (undef, $ruleset_changesv6) = PVE::Firewall::get_ruleset_cmdlist($rulesetv6, $verbose, "ip6tables");
+ my (undef, $ruleset_changesv6) = PVE::Firewall::get_ruleset_cmdlist($rulesetv6->{filter}, "ip6tables");
+
+ print "\nebtables cmdlist:\n";
+ my (undef, $ebtables_changes) = PVE::Firewall::get_ebtables_cmdlist($ebtables_ruleset);
- if ($ipset_changes || $ruleset_changes || $ruleset_changesv6) {
+ print "\niptables table raw cmdlist:\n";
+ my (undef, $ruleset_changes_raw) = PVE::Firewall::get_ruleset_cmdlist($ruleset->{raw}, undef, 'raw');
+
+ print "\nip6tables table raw cmdlist:\n";
+ my (undef, $ruleset_changesv6_raw) = PVE::Firewall::get_ruleset_cmdlist($rulesetv6->{raw}, "ip6tables", 'raw');
+
+
+ if ($ipset_changes || $ruleset_changes || $ruleset_changesv6 || $ebtables_changes || $ruleset_changes_raw || $ruleset_changesv6_raw) {
print "detected changes\n";
} else {
print "no changes\n";
if (!$cluster_conf->{options}->{enable}) {
print "firewall disabled\n";
}
-
};
PVE::Firewall::run_locked($code);
method => 'GET',
description => "Print information about local network.",
parameters => {
- additionalProperties => 0,
+ additionalProperties => 0,
properties => {},
},
returns => { type => 'null' },
print "local IP address: $ip\n";
my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
-
+
my $localnet = PVE::Firewall::local_network() || '127.0.0.0/8';
print "network auto detect: $localnet\n";
- if ($cluster_conf->{aliases}->{local_network}) {
- print "using user defined local_network: $cluster_conf->{aliases}->{local_network}->{cidr}\n";
+ if (my $local_network = $cluster_conf->{aliases}->{local_network}) {
+ print "using user defined local_network: $local_network->{cidr}\n";
} else {
print "using detected local_network: $localnet\n";
}
+ if (PVE::Corosync::check_conf_exists(1)) {
+ my $corosync_conf = PVE::Cluster::cfs_read_file("corosync.conf");
+ my $corosync_node_found = 0;
+
+ print "\naccepting corosync traffic from/to:\n";
+
+ PVE::Corosync::for_all_corosync_addresses($corosync_conf, undef, sub {
+ my ($curr_node_name, $curr_node_ip, undef, $key) = @_;
+
+ return if $curr_node_name eq $nodename;
+
+ $corosync_node_found = 1;
+
+ $key =~ m/(?:ring|link)(\d+)_addr/;
+ print " - $curr_node_name: $curr_node_ip (link: $1)\n";
+ });
+
+ if (!$corosync_node_found) {
+ print " - no nodes found\n";
+ }
+ }
+
return undef;
}});
name => 'simulate',
path => 'simulate',
method => 'GET',
- description => "Simulate firewall rules. This does not simulate kernel 'routing' table. Instead, this simply assumes that routing from source zone to destination zone is possible.",
+ description => "Simulate firewall rules. This does not simulates the kernel 'routing' table,"
+ ." but simply assumes that routing from source zone to destination zone is possible.",
parameters => {
- additionalProperties => 0,
+ additionalProperties => 0,
properties => {
verbose => {
description => "Verbose output.",
from => {
description => "Source zone.",
type => 'string',
- pattern => '(host|outside|vm\d+|ct\d+|vmbr\d+/\S+)',
+ pattern => "(host|outside|vm\\d+|ct\\d+|$bridge_interface_pattern)",
optional => 1,
default => 'outside',
},
to => {
description => "Destination zone.",
type => 'string',
- pattern => '(host|outside|vm\d+|ct\d+|vmbr\d+/\S+)',
+ pattern => "(host|outside|vm\\d+|ct\\d+|$bridge_interface_pattern)",
optional => 1,
default => 'host',
},
local $SIG{'__WARN__'} = 'DEFAULT'; # do not fill up syslog
- my ($ruleset, $ipset_ruleset, $rulesetv6) = PVE::Firewall::compile(undef, undef, undef, $param->{verbose});
+ PVE::Firewall::set_verbose($param->{verbose});
+
+ my ($ruleset, $ipset_ruleset, $rulesetv6, $ebtables_ruleset) = PVE::Firewall::compile();
+
+ PVE::FirewallSimulator::debug();
- PVE::FirewallSimulator::debug($param->{verbose} || 0);
-
my $host_ip = PVE::Cluster::remote_node_ip($nodename);
PVE::FirewallSimulator::reset_trace();
- print Dumper($ruleset) if $param->{verbose};
+ print Dumper($ruleset->{filter}) if $param->{verbose};
+ print Dumper($ruleset->{raw}) if $param->{verbose};
my $test = {
from => $param->{from},
if (!defined($test->{to})) {
$test->{to} = 'host';
- PVE::FirewallSimulator::add_trace("Set Zone: to => '$test->{to}'\n");
- }
+ PVE::FirewallSimulator::add_trace("Set Zone: to => '$test->{to}'\n");
+ }
if (!defined($test->{from})) {
$test->{from} = 'outside',
- PVE::FirewallSimulator::add_trace("Set Zone: from => '$test->{from}'\n");
+ PVE::FirewallSimulator::add_trace("Set Zone: from => '$test->{from}'\n");
}
my $vmdata = PVE::Firewall::read_local_vm_config();
$test->{action} = 'QUERY';
- my $res = PVE::FirewallSimulator::simulate_firewall($ruleset, $ipset_ruleset,
- $host_ip, $vmdata, $test);
-
+ my $res = PVE::FirewallSimulator::simulate_firewall(
+ $ruleset->{filter}, $ipset_ruleset, $host_ip, $vmdata, $test);
+
print "ACTION: $res\n";
return undef;
status => [ __PACKAGE__, 'status', [], undef, sub {
my $res = shift;
my $status = ($res->{enable} ? "enabled" : "disabled") . '/' . $res->{status};
-
+
if ($res->{changes}) {
print "Status: $status (pending changes)\n";
} else {
};
1;
-
-__END__
-
-=head1 NAME
-
-pve-firewall - PVE Firewall Daemon
-
-=head1 SYNOPSIS
-
-=include synopsis
-
-=head1 DESCRIPTION
-
-This service updates iptables rules periodically.
-
-=include pve_copyright