use Digest::SHA;
use PVE::Tools;
use PVE::QemuServer;
+use File::Basename;
use File::Path;
use IO::File;
use Net::IP;
use Data::Dumper;
my $pve_fw_lock_filename = "/var/lock/pvefw.lck";
+my $pve_fw_status_filename = "/var/lib/pve-firewall/pvefw.status";
# imported/converted from: /usr/share/shorewall/macro.*
my $pve_fw_macros = {
}
}
+sub save_pvefw_status {
+ my ($status) = @_;
+
+ die "unknown status '$status' - internal error"
+ if $status !~ m/^(stopped|active)$/;
+
+ mkdir dirname($pve_fw_status_filename);
+ PVE::Tools::file_set_contents($pve_fw_status_filename, $status);
+}
+
+sub read_pvefw_status {
+
+ my $status = 'unknown';
+
+ return 'stopped' if ! -f $pve_fw_status_filename;
+
+ eval {
+ $status = PVE::Tools::file_get_contents($pve_fw_status_filename);
+ };
+ warn $@ if $@;
+
+ return $status;
+}
+
sub compile {
my $vmdata = read_local_vm_config();
my $rules = read_vm_firewall_rules($vmdata);
return "-A $chain -m comment --comment \"PVESIG:$sig\"\n";
}
-sub apply_ruleset {
+sub get_rulset_cmdlist {
my ($ruleset, $verbose) = @_;
- enable_bridge_firewall();
-
my $cmdlist = "*filter\n"; # we pass this to iptables-restore;
my $statushash = get_ruleset_status($ruleset, $verbose);
$cmdlist .= "COMMIT\n";
+ return $cmdlist;
+}
+
+sub apply_ruleset {
+ my ($ruleset, $verbose) = @_;
+
+ enable_bridge_firewall();
+
+ my $cmdlist = get_rulset_cmdlist($ruleset, $verbose);
+
print $cmdlist if $verbose;
iptables_restore_cmdlist($cmdlist);
# test: re-read status and check if everything is up to date
- $statushash = get_ruleset_status($ruleset);
+ my $statushash = get_ruleset_status($ruleset);
my $errors;
foreach my $chain (sort keys %$ruleset) {
die "unable to apply firewall changes\n" if $errors;
}
+sub update {
+ my ($start, $verbose) = @_;
+
+ my $code = sub {
+ my $status = read_pvefw_status();
+
+ my $ruleset = PVE::Firewall::compile();
+
+ if ($start || $status eq 'active') {
+
+ save_pvefw_status('active') if ($status ne 'active');
+
+ PVE::Firewall::apply_ruleset($ruleset, $verbose);
+ } else {
+ print "Firewall not active (status = $status)\n" if $verbose;
+ }
+ };
+
+ run_locked($code);
+}
+
+
1;
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) = @_;
+
+ my $rpcenv = PVE::RPCEnvironment::get();
+
+ $param->{verbose} = 1
+ if !defined($param->{verbose}) && ($rpcenv->{type} eq 'cli');
+
+ my $code = sub {
+ my $status = PVE::Firewall::read_pvefw_status();
+
+ my $res = { status => $status };
+ if ($status eq 'active') {
+ my $ruleset = PVE::Firewall::compile();
+ my $cmdlist = PVE::Firewall::get_rulset_cmdlist($ruleset);
+
+ if ($cmdlist ne "*filter\nCOMMIT\n") {
+ $res->{changes} = 1;
+ }
+ }
+
+ return $res;
+ };
+
+ return PVE::Firewall::run_locked($code);
+ }});
+
__PACKAGE__->register_method ({
name => 'start',
path => 'start',
method => 'POST',
- description => "Start (or restart if already active) firewall.",
+ description => "Start (or simply update if already active) firewall.",
parameters => {
additionalProperties => 0,
properties => {
code => sub {
my ($param) = @_;
- my $code = sub {
- my $ruleset = PVE::Firewall::compile();
- PVE::Firewall::apply_ruleset($ruleset, $param->{verbose});
- };
+ PVE::Firewall::update(1, $param->{verbose});
- PVE::Firewall::run_locked($code);
+ return undef;
+ }});
+
+__PACKAGE__->register_method ({
+ name => 'update',
+ path => 'update',
+ method => 'POST',
+ description => "Check firewall rules. Then update the rules if the firewall is active.",
+ parameters => {
+ additionalProperties => 0,
+ properties => {
+ verbose => {
+ description => "Verbose output.",
+ type => "boolean",
+ optional => 1,
+ default => 0,
+ },
+ },
+ },
+ returns => { type => 'null' },
+
+ code => sub {
+ my ($param) = @_;
+
+ PVE::Firewall::update(0, $param->{verbose});
return undef;
}});
my ($param) = @_;
my $code = sub {
+
my $chash = PVE::Firewall::iptables_get_chains();
my $cmdlist = "*filter\n";
my $rule = "INPUT -j PVEFW-INPUT";
$cmdlist .= "COMMIT\n";
PVE::Firewall::iptables_restore_cmdlist($cmdlist);
+
+ PVE::Firewall::save_pvefw_status('stopped');
};
PVE::Firewall::run_locked($code);
my $cmddef = {
compile => [ __PACKAGE__, 'compile', []],
start => [ __PACKAGE__, 'start', []],
+ update => [ __PACKAGE__, 'update', []],
+ status => [ __PACKAGE__, 'status', [], undef, sub {
+ my $res = shift;
+ if ($res->{changes}) {
+ print "Status: $res->{status} (pending changes)\n";
+ } else {
+ print "Status: $res->{status}\n";
+ }
+ }],
stop => [ __PACKAGE__, 'stop', []],
};