use PVE::RPCEnvironment;
use PVE::CLIHandler;
use PVE::Firewall;
+use PVE::FirewallSimulator;
+use Data::Dumper;
use base qw(PVE::CLIHandler);
$rpcenv->set_language($ENV{LANG});
$rpcenv->set_user('root@pam');
+my $nodename = PVE::INotify::nodename();
+
my $commandline = [$0, @ARGV];
$0 = "pve-firewall";
sleep($waittime) if $waittime; # avoid high server load due to restarts
+ PVE::INotify::inotify_close();
+
exec (@$commandline);
exit (-1); # never reached?
}
properties => {
status => {
type => 'string',
- enum => ['unknown', 'stopped', 'active'],
+ enum => ['unknown', 'stopped', 'running'],
+ },
+ enable => {
+ description => "Firewall is enabled (in 'cluster.fw')",
+ type => 'boolean',
},
changes => {
description => "Set when there are pending changes.",
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 $status = $running ? 'running' : '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;
+ my $verbose = 1; # show syntax errors
+ my $cluster_conf = PVE::Firewall::load_clusterfw_conf(undef, $verbose);
+ $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");
+
+ $res->{changes} = ($ipset_changes || $ruleset_changes || $ruleset_changesv6) ? 1 : 0;
}
return $res;
__PACKAGE__->register_method ({
name => 'compile',
path => 'compile',
- method => 'POST',
+ method => 'GET',
description => "Compile and print firewall rules. This is useful for testing.",
parameters => {
additionalProperties => 0,
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) {
+ my $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);
+
+ print "ipset cmdlist:\n";
+ my (undef, undef, $ipset_changes) = PVE::Firewall::get_ipset_cmdlist($ipset_ruleset, $verbose);
+
+ print "\niptables cmdlist:\n";
+ my (undef, $ruleset_changes) = PVE::Firewall::get_ruleset_cmdlist($ruleset, $verbose);
+
+ print "\nip6tables cmdlist:\n";
+ my (undef, $ruleset_changesv6) = PVE::Firewall::get_ruleset_cmdlist($rulesetv6, $verbose, "ip6tables");
+
+ if ($ipset_changes || $ruleset_changes || $ruleset_changesv6) {
print "detected changes\n";
} else {
print "no changes\n";
}
+ if (!$cluster_conf->{options}->{enable}) {
+ print "firewall disabled\n";
+ }
+
};
PVE::Firewall::run_locked($code);
return undef;
}});
-my $nodename = PVE::INotify::nodename();
+__PACKAGE__->register_method ({
+ name => 'localnet',
+ path => 'localnet',
+ method => 'GET',
+ description => "Print information about local network.",
+ parameters => {
+ additionalProperties => 0,
+ properties => {},
+ },
+ returns => { type => 'null' },
+ code => sub {
+ my ($param) = @_;
+
+ local $SIG{'__WARN__'} = 'DEFAULT'; # do not fill up syslog
+
+ my $nodename = PVE::INotify::nodename();
+ print "local hostname: $nodename\n";
+
+ my $ip = PVE::Cluster::remote_node_ip($nodename);
+ 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";
+ } else {
+ print "using detected local_network: $localnet\n";
+ }
+
+ return undef;
+ }});
+
+__PACKAGE__->register_method ({
+ 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.",
+ parameters => {
+ additionalProperties => 0,
+ properties => {
+ verbose => {
+ description => "Verbose output.",
+ type => 'boolean',
+ optional => 1,
+ default => 0,
+ },
+ from => {
+ description => "Source zone.",
+ type => 'string',
+ pattern => '(host|outside|vm\d+|ct\d+|vmbr\d+/\S+)',
+ optional => 1,
+ default => 'outside',
+ },
+ to => {
+ description => "Destination zone.",
+ type => 'string',
+ pattern => '(host|outside|vm\d+|ct\d+|vmbr\d+/\S+)',
+ optional => 1,
+ default => 'host',
+ },
+ protocol => {
+ description => "Protocol.",
+ type => 'string',
+ pattern => '(tcp|udp)',
+ optional => 1,
+ default => 'tcp',
+ },
+ dport => {
+ description => "Destination port.",
+ type => 'integer',
+ minValue => 1,
+ maxValue => 65535,
+ optional => 1,
+ },
+ sport => {
+ description => "Source port.",
+ type => 'integer',
+ minValue => 1,
+ maxValue => 65535,
+ optional => 1,
+ },
+ source => {
+ description => "Source IP address.",
+ type => 'string', format => 'ipv4',
+ optional => 1,
+ },
+ dest => {
+ description => "Destination IP address.",
+ type => 'string', format => 'ipv4',
+ optional => 1,
+ },
+ },
+ },
+ returns => { type => 'null' },
+ code => sub {
+ my ($param) = @_;
+
+ local $SIG{'__WARN__'} = 'DEFAULT'; # do not fill up syslog
+
+ my ($ruleset, $ipset_ruleset, $rulesetv6) = PVE::Firewall::compile(undef, undef, undef, $param->{verbose});
+
+ 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};
+
+ my $test = {
+ from => $param->{from},
+ to => $param->{to},
+ proto => $param->{protocol} || 'tcp',
+ source => $param->{source},
+ dest => $param->{dest},
+ dport => $param->{dport},
+ sport => $param->{sport},
+ };
+
+ if (!defined($test->{to})) {
+ $test->{to} = 'host';
+ 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");
+ }
+
+ my $vmdata = PVE::Firewall::read_local_vm_config();
+
+ print "Test packet:\n";
+
+ foreach my $k (qw(from to proto source dest dport sport)) {
+ printf(" %-8s: %s\n", $k, $test->{$k}) if defined($test->{$k});
+ }
+
+ $test->{action} = 'QUERY';
+
+ my $res = PVE::FirewallSimulator::simulate_firewall($ruleset, $ipset_ruleset,
+ $host_ip, $vmdata, $test);
+
+ print "ACTION: $res\n";
+
+ return undef;
+ }});
my $cmddef = {
start => [ __PACKAGE__, 'start', []],
stop => [ __PACKAGE__, 'stop', []],
compile => [ __PACKAGE__, 'compile', []],
+ simulate => [ __PACKAGE__, 'simulate', []],
+ localnet => [ __PACKAGE__, 'localnet', []],
status => [ __PACKAGE__, 'status', [], undef, sub {
my $res = shift;
+ my $status = ($res->{enable} ? "enabled" : "disabled") . '/' . $res->{status};
+
if ($res->{changes}) {
- print "Status: $res->{status} (pending changes)\n";
+ print "Status: $status (pending changes)\n";
} else {
- print "Status: $res->{status}\n";
+ print "Status: $status\n";
}
}],
};