]> git.proxmox.com Git - pve-firewall.git/blobdiff - src/PVE/Firewall.pm
fix 901: encode unicode characters in sha digest
[pve-firewall.git] / src / PVE / Firewall.pm
index a39cf6d46503dcded67f047387a8d50a3369fb36..1e412cef3da16ace2c5565b11b478ed7543eaad4 100644 (file)
@@ -139,6 +139,7 @@ my $pve_ipv6fw_macros = {
     ],
     'NeighborDiscovery' => [
        "IPv6 neighbor solicitation, neighbor and router advertisement",
+       { action => 'PARAM', proto => 'icmpv6', dport => 'router-solicitation' },
        { action => 'PARAM', proto => 'icmpv6', dport => 'router-advertisement' },
        { action => 'PARAM', proto => 'icmpv6', dport => 'neighbor-solicitation' },
        { action => 'PARAM', proto => 'icmpv6', dport => 'neighbor-advertisement' },
@@ -506,6 +507,7 @@ my $pve_fw_macros = {
 
 my $pve_fw_parsed_macros;
 my $pve_fw_macro_descr;
+my $pve_fw_macro_ipversion = {};
 my $pve_fw_preferred_macro_names = {};
 
 my $pve_std_chains = {};
@@ -755,14 +757,32 @@ sub init_firewall_macros {
 
     $pve_fw_parsed_macros = {};
 
-    foreach my $k (keys %$pve_fw_macros) {
+    my $parse = sub {
+       my ($k, $macro) = @_;
        my $lc_name = lc($k);
-       my $macro = $pve_fw_macros->{$k};
-       if (!ref($macro->[0])) {
-           $pve_fw_macro_descr->{$k} = shift @$macro;
+       $pve_fw_macro_ipversion->{$k} = 0;
+       while (!ref($macro->[0])) {
+           my $desc = shift @$macro;
+           if ($desc eq 'ipv4only') {
+               $pve_fw_macro_ipversion->{$k} = 4;
+           } elsif ($desc eq 'ipv6only') {
+               $pve_fw_macro_ipversion->{$k} = 6;
+           } else {
+               $pve_fw_macro_descr->{$k} = $desc;
+           }
        }
        $pve_fw_preferred_macro_names->{$lc_name} = $k;
        $pve_fw_parsed_macros->{$k} = $macro;
+    };
+
+    foreach my $k (keys %$pve_fw_macros) {
+       &$parse($k, $pve_fw_macros->{$k});
+    }
+
+    foreach my $k (keys %$pve_ipv6fw_macros) {
+       next if $pve_fw_parsed_macros->{$k};
+       &$parse($k, $pve_ipv6fw_macros->{$k});
+       $pve_fw_macro_ipversion->{$k} = 6;
     }
 }
 
@@ -1063,7 +1083,10 @@ sub copy_list_with_digest {
            next if !defined($v);
            $data->{$k} = $v;
            # Note: digest ignores refs ($rule->{errors})
-           $sha->add($k, ':', $v, "\n") if !ref($v); ;
+           # since Digest::SHA expects a series of bytes,
+           #  we have to encode the value here to prevent errors when
+           #  using utf8 characters (eg. in comments)
+           $sha->add($k, ':', encode_utf8($v), "\n") if !ref($v); ;
        }
        push @$res, $data;
     }
@@ -1170,6 +1193,11 @@ my $apply_macro = sub {
        $macro_rules = $pve_ipv6fw_macros->{$macro_name};
     }
 
+    # skip macros which are specific to another ipversion
+    if ($ipversion && (my $required = $pve_fw_macro_ipversion->{$macro_name})) {
+       return if $ipversion != $required;
+    }
+
     my $rules = [];
 
     foreach my $templ (@$macro_rules) {
@@ -1839,6 +1867,16 @@ sub ruleset_add_chain_policy {
     }
 }
 
+sub ruleset_chain_add_ndp {
+    my ($ruleset, $chain, $ipversion, $options) = @_;
+    return if $ipversion != 6 || (defined($options->{ndp}) && !$options->{ndp});
+
+    ruleset_addrule($ruleset, $chain, "-p icmpv6 --icmpv6-type router-solicitation -j ACCEPT");
+    ruleset_addrule($ruleset, $chain, "-p icmpv6 --icmpv6-type router-advertisement -j ACCEPT");
+    ruleset_addrule($ruleset, $chain, "-p icmpv6 --icmpv6-type neighbor-solicitation -j ACCEPT");
+    ruleset_addrule($ruleset, $chain, "-p icmpv6 --icmpv6-type neighbor-advertisement -j ACCEPT");
+}
+
 sub ruleset_chain_add_conn_filters {
     my ($ruleset, $chain, $accept) = @_;
 
@@ -1888,6 +1926,8 @@ sub ruleset_create_vm_chain {
        }
     }
 
+    ruleset_chain_add_ndp($ruleset, $chain, $ipversion, $options);
+
     if ($direction eq 'OUT') {
        if (defined($macaddr) && !(defined($options->{macfilter}) && $options->{macfilter} == 0)) {
            ruleset_addrule($ruleset, $chain, "-m mac ! --mac-source $macaddr -j DROP");
@@ -2094,6 +2134,7 @@ sub enable_host_firewall {
 
     ruleset_addrule($ruleset, $chain, "-i lo -j ACCEPT");
 
+    ruleset_chain_add_ndp($ruleset, $chain, $ipversion, $options);
     ruleset_chain_add_conn_filters($ruleset, $chain, 'ACCEPT');
     ruleset_chain_add_input_filters($ruleset, $chain, $ipversion, $options, $cluster_conf, $loglevel);
 
@@ -2152,6 +2193,7 @@ sub enable_host_firewall {
 
     ruleset_addrule($ruleset, $chain, "-o lo -j ACCEPT");
 
+    ruleset_chain_add_ndp($ruleset, $chain, $ipversion, $options);
     ruleset_chain_add_conn_filters($ruleset, $chain, 'ACCEPT');
 
     # we use RETURN because we may want to check other thigs later
@@ -2326,7 +2368,7 @@ sub parse_vmfw_option {
 
     my $loglevels = "emerg|alert|crit|err|warning|notice|info|debug|nolog";
 
-    if ($line =~ m/^(enable|dhcp|macfilter|ips):\s*(0|1)\s*$/i) {
+    if ($line =~ m/^(enable|dhcp|ndp|macfilter|ips):\s*(0|1)\s*$/i) {
        $opt = lc($1);
        $value = int($2);
     } elsif ($line =~ m/^(log_level_in|log_level_out):\s*(($loglevels)\s*)?$/i) {
@@ -2352,7 +2394,7 @@ sub parse_hostfw_option {
 
     my $loglevels = "emerg|alert|crit|err|warning|notice|info|debug|nolog";
 
-    if ($line =~ m/^(enable|nosmurfs|tcpflags):\s*(0|1)\s*$/i) {
+    if ($line =~ m/^(enable|nosmurfs|tcpflags|ndp):\s*(0|1)\s*$/i) {
        $opt = lc($1);
        $value = int($2);
     } elsif ($line =~ m/^(log_level_in|log_level_out|tcp_flags_log_level|smurf_log_level):\s*(($loglevels)\s*)?$/i) {