From 6b9f68a244fd14d37a0c801acce57aeb1fd23ef4 Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Fri, 28 Feb 2014 10:50:44 +0100 Subject: [PATCH] use a file to store firewall status persistently. Start/stop saves state into a file. So the firewall remembers that status even if the host is rebooted. Also added helpers to update firewall rules and get current status. --- PVE/Firewall.pm | 64 ++++++++++++++++++++++++++++++--- pvefw | 96 +++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 150 insertions(+), 10 deletions(-) diff --git a/PVE/Firewall.pm b/PVE/Firewall.pm index 6309b81..2ed65e8 100644 --- a/PVE/Firewall.pm +++ b/PVE/Firewall.pm @@ -6,6 +6,7 @@ use Data::Dumper; use Digest::SHA; use PVE::Tools; use PVE::QemuServer; +use File::Basename; use File::Path; use IO::File; use Net::IP; @@ -14,6 +15,7 @@ use PVE::Tools qw(run_command lock_file); 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 = { @@ -1353,6 +1355,30 @@ sub generate_std_chains { } } +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); @@ -1474,11 +1500,9 @@ sub print_sig_rule { 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); @@ -1539,12 +1563,22 @@ sub apply_ruleset { $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) { @@ -1558,4 +1592,26 @@ sub apply_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; diff --git a/pvefw b/pvefw index 30be559..1eff5ca 100755 --- a/pvefw +++ b/pvefw @@ -64,11 +64,62 @@ __PACKAGE__->register_method ({ 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 => { @@ -85,12 +136,33 @@ __PACKAGE__->register_method ({ 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; }}); @@ -110,6 +182,7 @@ __PACKAGE__->register_method ({ my ($param) = @_; my $code = sub { + my $chash = PVE::Firewall::iptables_get_chains(); my $cmdlist = "*filter\n"; my $rule = "INPUT -j PVEFW-INPUT"; @@ -135,6 +208,8 @@ __PACKAGE__->register_method ({ $cmdlist .= "COMMIT\n"; PVE::Firewall::iptables_restore_cmdlist($cmdlist); + + PVE::Firewall::save_pvefw_status('stopped'); }; PVE::Firewall::run_locked($code); @@ -147,6 +222,15 @@ my $nodename = PVE::INotify::nodename(); 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', []], }; -- 2.39.2