]> git.proxmox.com Git - pve-firewall.git/blobdiff - src/PVE/Firewall.pm
fix CT rule generation with ipfilter set
[pve-firewall.git] / src / PVE / Firewall.pm
index 92b02aae5f07290b364da2711c9c52d2f1aa493d..b08cea57312b9e6a54680e24abced4e4de4c47be 100644 (file)
@@ -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);
            }
        };