X-Git-Url: https://git.proxmox.com/?p=pve-firewall.git;a=blobdiff_plain;f=src%2FPVE%2FFirewall.pm;h=b08cea57312b9e6a54680e24abced4e4de4c47be;hp=92b02aae5f07290b364da2711c9c52d2f1aa493d;hb=0398480886f82189988edcd00fa20683cbbed4ce;hpb=40af93c4067341115877fc082a900be215122f0a diff --git a/src/PVE/Firewall.pm b/src/PVE/Firewall.pm index 92b02aa..b08cea5 100644 --- a/src/PVE/Firewall.pm +++ b/src/PVE/Firewall.pm @@ -2,27 +2,27 @@ package PVE::Firewall; use warnings; use strict; -use POSIX; -use Data::Dumper; + use Digest::SHA; +use Encode; +use File::Basename; +use File::Path; +use IO::File; +use Net::IP; +use POSIX; use Socket qw(AF_INET6 inet_ntop inet_pton); -use PVE::INotify; +use Storable qw(dclone); + +use PVE::Cluster; use PVE::Exception qw(raise raise_param_exc); +use PVE::INotify; use PVE::JSONSchema qw(register_standard_option get_standard_option); -use PVE::Cluster; -use PVE::ProcFSTools; -use PVE::Tools qw($IPV4RE $IPV6RE); use PVE::Network; +use PVE::ProcFSTools; use PVE::SafeSyslog; -use File::Basename; -use File::Path; -use IO::File; -use Net::IP; +use PVE::Tools qw($IPV4RE $IPV6RE); use PVE::Tools qw(run_command lock_file dir_glob_foreach); -use Encode; -use Storable qw(dclone); -my $hostfw_conf_filename = "/etc/pve/local/host.fw"; my $pvefw_conf_dir = "/etc/pve/firewall"; my $clusterfw_conf_filename = "$pvefw_conf_dir/cluster.fw"; @@ -41,7 +41,6 @@ eval { $have_lxc = 1; }; - my $pve_fw_status_dir = "/var/lib/pve-firewall"; mkdir $pve_fw_status_dir; # make sure this exists @@ -127,6 +126,7 @@ eval { }; my $nodename = PVE::INotify::nodename(); +my $hostfw_conf_filename = "/etc/pve/nodes/$nodename/host.fw"; my $pve_fw_lock_filename = "/var/lock/pvefw.lck"; @@ -666,14 +666,10 @@ $pve_std_chains_conf->{6} = { #{ action => 'DROP', dest => '224.0.0.0/4' }, ], 'PVEFW-reject' => [ - # same as shorewall 'reject' - #{ action => 'DROP', dsttype => 'BROADCAST' }, - #{ action => 'DROP', source => '224.0.0.0/4' }, { action => 'DROP', proto => 'icmpv6' }, { match => '-p tcp', target => '-j REJECT --reject-with tcp-reset' }, - #"-p udp -j REJECT --reject-with icmp-port-unreachable", - #"-p icmp -j REJECT --reject-with icmp-host-unreachable", - #"-j REJECT --reject-with icmp-host-prohibited", + { match => '-p udp', target => '-j REJECT --reject-with icmp6-port-unreachable' }, + { target => '-j REJECT --reject-with icmp6-adm-prohibited' }, ], 'PVEFW-Drop' => [ # same as shorewall 'Drop', which is equal to DROP, @@ -2391,10 +2387,10 @@ sub generate_tap_rules_direction { # plug the tap chain to bridge chain if ($direction eq 'IN') { ruleset_addrule($ruleset, "PVEFW-FWBR-IN", - "-m physdev --physdev-is-bridged --physdev-out $iface", "-j $tapchain", $loglevel, 'FWBR-IN: ', $vmid); + "-m physdev --physdev-is-bridged --physdev-out $iface", "-j $tapchain"); } else { ruleset_addrule($ruleset, "PVEFW-FWBR-OUT", - "-m physdev --physdev-is-bridged --physdev-in $iface", "-j $tapchain", $loglevel, 'FWBR-OUT: ', $vmid); + "-m physdev --physdev-is-bridged --physdev-in $iface", "-j $tapchain"); } } @@ -2799,20 +2795,29 @@ sub parse_alias { } sub generic_fw_config_parser { - my ($filename, $fh, $cluster_conf, $empty_conf, $rule_env) = @_; + my ($filename, $cluster_conf, $empty_conf, $rule_env) = @_; my $section; my $group; my $res = $empty_conf; - while (defined(my $line = <$fh>)) { + my $raw; + if ($filename =~ m!^/etc/pve/(.*)$!) { + $raw = PVE::Cluster::get_config($1); + } else { + $raw = eval { PVE::Tools::file_get_contents($filename) }; # ignore errors + } + return {} if !$raw; + + my $linenr = 0; + while ($raw =~ /^\h*(.*?)\h*$/gm) { + my $line = $1; + $linenr++; next if $line =~ m/^#/; next if $line =~ m/^\s*$/; - chomp $line; - my $linenr = $fh->input_line_number(); my $prefix = "$filename (line $linenr)"; if ($empty_conf->{options} && ($line =~ m/^\[options\]$/i)) { @@ -2961,47 +2966,6 @@ sub generic_fw_config_parser { return $res; } -sub parse_hostfw_config { - my ($filename, $fh, $cluster_conf) = @_; - - my $empty_conf = { rules => [], options => {}}; - - return generic_fw_config_parser($filename, $fh, $cluster_conf, $empty_conf, 'host'); -} - -sub parse_vmfw_config { - my ($filename, $fh, $cluster_conf, $rule_env) = @_; - - my $empty_conf = { - rules => [], - options => {}, - aliases => {}, - ipset => {} , - ipset_comments => {}, - }; - - return generic_fw_config_parser($filename, $fh, $cluster_conf, $empty_conf, $rule_env); -} - -sub parse_clusterfw_config { - my ($filename, $fh) = @_; - - my $section; - my $group; - - my $empty_conf = { - rules => [], - options => {}, - aliases => {}, - groups => {}, - group_comments => {}, - ipset => {} , - ipset_comments => {}, - }; - - return generic_fw_config_parser($filename, $fh, $empty_conf, $empty_conf, 'cluster'); -} - sub run_locked { my ($code, @param) = @_; @@ -3053,15 +3017,19 @@ sub read_local_vm_config { sub load_vmfw_conf { my ($cluster_conf, $rule_env, $vmid, $dir) = @_; - my $vmfw_conf = {}; - $dir = $pvefw_conf_dir if !defined($dir); - my $filename = "$dir/$vmid.fw"; - if (my $fh = IO::File->new($filename, O_RDONLY)) { - $vmfw_conf = parse_vmfw_config($filename, $fh, $cluster_conf, $rule_env); - $vmfw_conf->{vmid} = $vmid; - } + + my $empty_conf = { + rules => [], + options => {}, + aliases => {}, + ipset => {} , + ipset_comments => {}, + }; + + my $vmfw_conf = generic_fw_config_parser($filename, $cluster_conf, $empty_conf, $rule_env); + $vmfw_conf->{vmid} = $vmid; return $vmfw_conf; } @@ -3397,13 +3365,18 @@ sub load_clusterfw_conf { my ($filename) = @_; $filename = $clusterfw_conf_filename if !defined($filename); + my $empty_conf = { + rules => [], + options => {}, + aliases => {}, + groups => {}, + group_comments => {}, + ipset => {} , + ipset_comments => {}, + }; - my $cluster_conf = {}; - if (my $fh = IO::File->new($filename, O_RDONLY)) { - $cluster_conf = parse_clusterfw_config($filename, $fh); - - $set_global_log_ratelimit->($cluster_conf->{options}); - } + my $cluster_conf = generic_fw_config_parser($filename, $empty_conf, $empty_conf, 'cluster'); + $set_global_log_ratelimit->($cluster_conf->{options}); return $cluster_conf; } @@ -3456,11 +3429,8 @@ sub load_hostfw_conf { $filename = $hostfw_conf_filename if !defined($filename); - my $hostfw_conf = {}; - if (my $fh = IO::File->new($filename, O_RDONLY)) { - $hostfw_conf = parse_hostfw_config($filename, $fh, $cluster_conf); - } - return $hostfw_conf; + my $empty_conf = { rules => [], options => {}}; + return generic_fw_config_parser($filename, $cluster_conf, $empty_conf, 'host'); } sub save_hostfw_conf { @@ -3812,7 +3782,12 @@ sub compile_ebtables_filter { push(@$arpfilter, $ip); } } - push(@$arpfilter, $net->{ip}) if $net->{ip} && $vmfw_conf->{options}->{ipfilter}; + if (defined(my $ip = $net->{ip}) && $vmfw_conf->{options}->{ipfilter}) { + # ebtables changes this to a .0/MASK network but we just + # want the address here, no network - see #2193 + $ip =~ s|/(\d+)$||; + push @$arpfilter, $ip; + } generate_tap_layer2filter($ruleset, $iface, $macaddr, $vmfw_conf, $vmid, $arpfilter); } };