X-Git-Url: https://git.proxmox.com/?p=pve-firewall.git;a=blobdiff_plain;f=pvefw;h=1eff5caaae015a816c9836b8f97cc019f6e5eb8d;hp=1a0631b4bf9870b42d6e96de6a11bbd7d6d7b4a0;hb=6b9f68a244fd14d37a0c801acce57aeb1fd23ef4;hpb=9aab3127bdc04bc3e17bcfb0b8461e32694dc098 diff --git a/pvefw b/pvefw index 1a0631b..1eff5ca 100755 --- a/pvefw +++ b/pvefw @@ -3,15 +3,11 @@ use strict; use lib qw(.); use PVE::Firewall; -use File::Path; -use IO::File; -use Data::Dumper; use PVE::SafeSyslog; use PVE::Cluster; use PVE::INotify; use PVE::RPCEnvironment; -use PVE::QemuServer; use PVE::JSONSchema qw(get_standard_option); @@ -33,192 +29,149 @@ $rpcenv->init_request(); $rpcenv->set_language($ENV{LANG}); $rpcenv->set_user('root@pam'); +__PACKAGE__->register_method ({ + name => 'compile', + path => 'compile', + method => 'POST', + description => "Compile amd print firewall rules. This is only for testing.", + parameters => { + additionalProperties => 0, + properties => { + verbose => { + description => "Verbose output.", + type => "boolean", + optional => 1, + }, + }, + }, + returns => { type => 'null' }, -sub parse_fw_rules { - my ($filename, $fh) = @_; - - my $section; - - my $res = { in => [], out => [] }; - - my $macros = PVE::Firewall::get_shorewall_macros(); - - while (defined(my $line = <$fh>)) { - next if $line =~ m/^#/; - next if $line =~ m/^\s*$/; - - if ($line =~ m/^\[(in|out)\]\s*$/i) { - $section = lc($1); - next; - } - next if !$section; - - my ($action, $iface, $source, $dest, $proto, $dport, $sport) = - split(/\s+/, $line); - - if (!($action && $iface && $source && $dest)) { - warn "skip incomplete line\n"; - next; - } - - my $service; - if ($action =~ m/^(ACCEPT|DROP|REJECT)$/) { - # OK - } elsif ($action =~ m/^(\S+)\((ACCEPT|DROP|REJECT)\)$/) { - ($service, $action) = ($1, $2); - if (!$macros->{$service}) { - warn "unknown service '$service'\n"; - next; - } - } else { - warn "unknown action '$action'\n"; - next; - } - - if ($iface !~ m/^(all|net0|net1|net2|net3|net4|net5)$/) { - warn "unknown interface '$iface'\n"; - next; - } - - if ($proto && $proto !~ m/^(icmp|tcp|udp)$/) { - warn "unknown protokol '$proto'\n"; - next; - } + code => sub { + my ($param) = @_; - if ($source !~ m/^(any)$/) { - warn "unknown source '$source'\n"; - next; - } + my $rpcenv = PVE::RPCEnvironment::get(); - if ($dest !~ m/^(any)$/) { - warn "unknown destination '$dest'\n"; - next; - } + $param->{verbose} = 1 + if !defined($param->{verbose}) && ($rpcenv->{type} eq 'cli'); - my $rule = { - action => $action, - service => $service, - iface => $iface, - source => $source, - dest => $dest, - proto => $proto, - dport => $dport, - sport => $sport, + my $code = sub { + my $ruleset = PVE::Firewall::compile(); + PVE::Firewall::get_ruleset_status($ruleset, 1) if $param->{verbose}; }; - push @{$res->{$section}}, $rule; - } - - return $res; -} - -sub read_local_vm_config { - - my $openvz = {}; - - my $qemu = {}; - - my $list = PVE::QemuServer::config_list(); - - foreach my $vmid (keys %$list) { - my $cfspath = PVE::QemuServer::cfs_config_path($vmid); - if (my $conf = PVE::Cluster::cfs_read_file($cfspath)) { - $qemu->{$vmid} = $conf; - } - } - - my $vmdata = { openvz => $openvz, qemu => $qemu }; - - return $vmdata; -}; - -sub read_vm_firewall_rules { - my ($vmdata) = @_; - my $rules = {}; - foreach my $vmid (keys %{$vmdata->{qemu}}, keys %{$vmdata->{openvz}}) { - my $filename = "/etc/pve/firewall/$vmid.fw"; - my $fh = IO::File->new($filename, O_RDONLY); - next if !$fh; - - $rules->{$vmid} = parse_fw_rules($filename, $fh); - } + PVE::Firewall::run_locked($code); - return $rules; -} + return undef; + }}); __PACKAGE__->register_method ({ - name => 'compile', - path => 'compile', - method => 'POST', - description => "Compile firewall rules.", + name => 'status', + path => 'status', + method => 'GET', + description => "Get firewall status.", parameters => { additionalProperties => 0, properties => {}, }, - returns => { type => 'null' }, - + 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 $vmdata = read_local_vm_config(); - my $rules = read_vm_firewall_rules($vmdata); + my $rpcenv = PVE::RPCEnvironment::get(); - # print Dumper($vmdata); + $param->{verbose} = 1 + if !defined($param->{verbose}) && ($rpcenv->{type} eq 'cli'); - my $swdir = '/etc/shorewall'; - mkdir $swdir; + my $code = sub { + my $status = PVE::Firewall::read_pvefw_status(); - PVE::Firewall::compile($swdir, $vmdata, $rules); + my $res = { status => $status }; + if ($status eq 'active') { + my $ruleset = PVE::Firewall::compile(); + my $cmdlist = PVE::Firewall::get_rulset_cmdlist($ruleset); - PVE::Tools::run_command(['shorewall', 'compile']); + if ($cmdlist ne "*filter\nCOMMIT\n") { + $res->{changes} = 1; + } + } - return undef; + return $res; + }; + return PVE::Firewall::run_locked($code); }}); __PACKAGE__->register_method ({ name => 'start', path => 'start', method => 'POST', - description => "Start firewall.", + description => "Start (or simply update if already active) firewall.", parameters => { additionalProperties => 0, - properties => {}, + properties => { + verbose => { + description => "Verbose output.", + type => "boolean", + optional => 1, + default => 0, + }, + }, }, returns => { type => 'null' }, code => sub { my ($param) = @_; - PVE::Tools::run_command(['shorewall', 'start']); + PVE::Firewall::update(1, $param->{verbose}); return undef; }}); __PACKAGE__->register_method ({ - name => 'stop', - path => 'stop', + name => 'update', + path => 'update', method => 'POST', - description => "Stop firewall.", + description => "Check firewall rules. Then update the rules if the firewall is active.", parameters => { additionalProperties => 0, - properties => {}, + properties => { + verbose => { + description => "Verbose output.", + type => "boolean", + optional => 1, + default => 0, + }, + }, }, returns => { type => 'null' }, code => sub { my ($param) = @_; - PVE::Tools::run_command(['shorewall', 'stop']); + PVE::Firewall::update(0, $param->{verbose}); return undef; }}); __PACKAGE__->register_method ({ - name => 'clear', - path => 'clear', + name => 'stop', + path => 'stop', method => 'POST', - description => "Clear will remove all rules installed by this script. The host is then unprotected.", + description => "Stop firewall. This will remove all rules installed by this script. The host is then unprotected.", parameters => { additionalProperties => 0, properties => {}, @@ -228,7 +181,38 @@ __PACKAGE__->register_method ({ code => sub { my ($param) = @_; - PVE::Tools::run_command(['shorewall', 'clear']); + my $code = sub { + + my $chash = PVE::Firewall::iptables_get_chains(); + my $cmdlist = "*filter\n"; + my $rule = "INPUT -j PVEFW-INPUT"; + if (PVE::Firewall::iptables_rule_exist($rule)) { + $cmdlist .= "-D $rule\n"; + } + $rule = "OUTPUT -j PVEFW-OUTPUT"; + if (PVE::Firewall::iptables_rule_exist($rule)) { + $cmdlist .= "-D $rule\n"; + } + + $rule = "FORWARD -j PVEFW-FORWARD"; + if (PVE::Firewall::iptables_rule_exist($rule)) { + $cmdlist .= "-D $rule\n"; + } + + foreach my $chain (keys %$chash) { + $cmdlist .= "-F $chain\n"; + } + foreach my $chain (keys %$chash) { + $cmdlist .= "-X $chain\n"; + } + $cmdlist .= "COMMIT\n"; + + PVE::Firewall::iptables_restore_cmdlist($cmdlist); + + PVE::Firewall::save_pvefw_status('stopped'); + }; + + PVE::Firewall::run_locked($code); return undef; }}); @@ -238,8 +222,16 @@ 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', []], - clear => [ __PACKAGE__, 'clear', []], }; my $cmd = shift;