]> git.proxmox.com Git - pve-firewall.git/blobdiff - PVE/Firewall.pm
ignoreZ source/destination port if no protocol specified
[pve-firewall.git] / PVE / Firewall.pm
index 568b531ded09bc7000b7bc12f27a281ad9817552..6309b81e0223aa3ad674d20ce926ad813e59ff94 100644 (file)
@@ -224,7 +224,7 @@ my $pve_fw_macros = {
        { action => 'PARAM', proto => 'tcp', dport => '1723' },
     ],
     'Ping' => [
-       { action => 'PARAM', proto => 'icmp', dport => '8' },
+       { action => 'PARAM', proto => 'icmp', dport => 'echo-request' },
     ],
     'PostgreSQL' => [
        { action => 'PARAM', proto => 'tcp', dport => '5432' },
@@ -322,7 +322,7 @@ my $pve_fw_macros = {
     ],
     'Trcrt' => [
        { action => 'PARAM', proto => 'udp', dport => '33434:33524' },
-       { action => 'PARAM', proto => 'icmp', dport => '8' },
+       { action => 'PARAM', proto => 'icmp', dport => 'echo-request' },
     ],
     'VNC' => [
        { action => 'PARAM', proto => 'tcp', dport => '5900:5909' },
@@ -712,7 +712,9 @@ sub iptables_rule_exist {
 }
 
 sub ruleset_generate_rule {
-    my ($ruleset, $chain, $rule, $goto) = @_;
+    my ($ruleset, $chain, $rule, $actions, $goto) = @_;
+
+    return if $rule->{disable};
 
     my $cmd = '';
 
@@ -720,41 +722,56 @@ sub ruleset_generate_rule {
     $cmd .= " -s $rule->{source}" if $rule->{source};
     $cmd .= " -m iprange --dst-range" if $rule->{nbdest} && $rule->{nbdest} > 1;
     $cmd .= " -d $rule->{dest}" if $rule->{dest};
-    $cmd .= " -p $rule->{proto}" if $rule->{proto};
 
-    if (($rule->{nbdport} && $rule->{nbdport} > 1) ||
-       ($rule->{nbsport} && $rule->{nbsport} > 1)) {
-       $cmd .= " --match multiport" 
-    }
+    if ($rule->{proto}) {
+       $cmd .= " -p $rule->{proto}";
 
-    if ($rule->{dport}) {
-       if ($rule->{proto} && $rule->{proto} eq 'icmp') {
-           # Note: we use dport to store --icmp-type
-           die "unknown icmp-type\n" if !$icmp_type_names->{$rule->{dport}};
-           $cmd .= " -m icmp --icmp-type $rule->{dport}";
-       } else {
-           if ($rule->{nbdport} && $rule->{nbdport} > 1) {
-               $cmd .= " --dports $rule->{dport}";
+       my $multiport = 0;
+       $multiport++ if $rule->{nbdport} && ($rule->{nbdport} > 1);
+       $multiport++ if $rule->{nbsport} && ($rule->{nbsport} > 1);
+
+       $cmd .= " --match multiport" if $multiport;
+
+       die "multiport: option '--sports' cannot be used together with '--dports'\n" 
+           if ($multiport == 2) && ($rule->{dport} ne $rule->{sport});
+
+       if ($rule->{dport}) {
+           if ($rule->{proto} && $rule->{proto} eq 'icmp') {
+               # Note: we use dport to store --icmp-type
+               die "unknown icmp-type '$rule->{dport}'\n" if !defined($icmp_type_names->{$rule->{dport}});
+               $cmd .= " -m icmp --icmp-type $rule->{dport}";
            } else {
-               $cmd .= " --dport $rule->{dport}";
+               if ($rule->{nbdport} && $rule->{nbdport} > 1) {
+                   if ($multiport == 2) {
+                       $cmd .= " --ports $rule->{dport}";
+                   } else {
+                       $cmd .= " --dports $rule->{dport}";
+                   }
+               } else {
+                   $cmd .= " --dport $rule->{dport}";
+               }
            }
        }
-    }
 
-    if ($rule->{sport}) {
-       if ($rule->{nbsport} && $rule->{nbsport} > 1) {
-           $cmd .= " --sports $rule->{sport}";
-       } else {
-           $cmd .= " --sport $rule->{sport}";
+       if ($rule->{sport}) {
+           if ($rule->{nbsport} && $rule->{nbsport} > 1) {
+               $cmd .= " --sports $rule->{sport}" if $multiport != 2;
+           } else {
+               $cmd .= " --sport $rule->{sport}";
+           }
        }
+    } elsif ($rule->{dport} || $rule->{sport}) {
+       warn "ignoring destination port '$rule->{dport}' - no protocol specified\n" if $rule->{dport};
+       warn "ignoring source port '$rule->{sport}' - no protocol specified\n" if $rule->{sport};
     }
 
     $cmd .= " -m addrtype --dst-type $rule->{dsttype}" if $rule->{dsttype};
 
     if (my $action = $rule->{action}) {
+       $action = $actions->{$action} if defined($actions->{$action}); 
        $goto = 1 if !defined($goto) && $action eq 'PVEFW-SET-ACCEPT-MARK';
        $cmd .= $goto ? " -g $action" : " -j $action";
-    };
+    }
 
     ruleset_addrule($ruleset, $chain, $cmd) if $cmd;
 }
@@ -835,6 +852,10 @@ sub generate_tap_rules_direction {
        ruleset_addrule($ruleset, $tapchain, "-m conntrack --ctstate INVALID,NEW -j PVEFW-smurfs");
     }
 
+    if (!(defined($options->{dhcp}) && $options->{dhcp} == 0)) {
+       ruleset_addrule($ruleset, $tapchain, "-p udp -m udp --dport 67:68 -j ACCEPT");
+    }
+
     if ($options->{tcpflags}) {
        ruleset_addrule($ruleset, $tapchain, "-p tcp -j PVEFW-tcpflags");
     }
@@ -847,7 +868,6 @@ sub generate_tap_rules_direction {
        ruleset_addrule($ruleset, $tapchain, "-m mac ! --mac-source $macaddr -j DROP");
     }
 
-
     if ($rules) {
         foreach my $rule (@$rules) {
            next if $rule->{iface} && $rule->{iface} ne $netid;
@@ -862,8 +882,12 @@ sub generate_tap_rules_direction {
                ruleset_addrule($ruleset, $tapchain, "-m mark --mark 1 -j RETURN")
                    if $direction eq 'OUT';
            } else {
-               $rule->{action} = "PVEFW-SET-ACCEPT-MARK" if $rule->{action} eq 'ACCEPT' && $direction eq 'OUT';
-               ruleset_generate_rule($ruleset, $tapchain, $rule);
+               if ($direction eq 'OUT') {
+                   ruleset_generate_rule($ruleset, $tapchain, $rule, 
+                                         { ACCEPT => "PVEFW-SET-ACCEPT-MARK", REJECT => "PVEFW-reject" });
+               } else {
+                   ruleset_generate_rule($ruleset, $tapchain, $rule, { REJECT => "PVEFW-reject" });
+               }
            }
        }
     }
@@ -927,8 +951,7 @@ sub enablehostfw {
     if ($rules->{in}) {
         foreach my $rule (@{$rules->{in}}) {
             # we use RETURN because we need to check also tap rules
-            $rule->{action} = 'RETURN' if $rule->{action} eq 'ACCEPT';
-            ruleset_generate_rule($ruleset, $chain, $rule);
+            ruleset_generate_rule($ruleset, $chain, $rule, { ACCEPT => 'RETURN', REJECT => "PVEFW-reject" });
         }
     }
 
@@ -949,8 +972,7 @@ sub enablehostfw {
     if ($rules->{out}) {
         foreach my $rule (@{$rules->{out}}) {
             # we use RETURN because we need to check also tap rules
-            $rule->{action} = 'RETURN' if $rule->{action} eq 'ACCEPT';
-            ruleset_generate_rule($ruleset, $chain, $rule);
+            ruleset_generate_rule($ruleset, $chain, $rule, { ACCEPT => 'RETURN', REJECT => "PVEFW-reject" });
         }
     }
 
@@ -974,7 +996,7 @@ sub generate_group_rules {
 
     if ($rules->{in}) {
         foreach my $rule (@{$rules->{in}}) {
-           ruleset_generate_rule($ruleset, $chain, $rule);
+           ruleset_generate_rule($ruleset, $chain, $rule, { REJECT => "PVEFW-reject" });
         }
     }
 
@@ -985,11 +1007,10 @@ sub generate_group_rules {
 
     if ($rules->{out}) {
         foreach my $rule (@{$rules->{out}}) {
-            # we go the PVEFW-SET-ACCEPT-MARK Instead of ACCEPT) because we need to
-           # check also other tap rules (and group rules can be set on any bridge,
-           # so we can't go to VMBRXX-IN)
-            $rule->{action} = 'PVEFW-SET-ACCEPT-MARK' if $rule->{action} eq 'ACCEPT';
-            ruleset_generate_rule($ruleset, $chain, $rule);
+            # we use PVEFW-SET-ACCEPT-MARK (Instead of ACCEPT) because we need to
+           # check also other tap rules later
+            ruleset_generate_rule($ruleset, $chain, $rule, 
+                                 { ACCEPT => 'PVEFW-SET-ACCEPT-MARK', REJECT => "PVEFW-reject" });
         }
     }
 }
@@ -1008,7 +1029,11 @@ sub parse_fw_rule {
 
     my ($action, $iface, $source, $dest, $proto, $dport, $sport);
 
-    $line =~ s/#.*$//;
+    # we can add single line comments to the end of the rule
+    my $comment = $1 if $line =~ s/#\s*(.*?)\s*$//;
+
+    # we can disable a rule when prefixed with '|'
+    my $disable = 1 if  $line =~ s/^\|//;
 
     my @data = split(/\s+/, $line);
     my $expected_elements = $need_iface ? 7 : 6;
@@ -1067,6 +1092,8 @@ sub parse_fw_rule {
     my $rules = [];
 
     my $param = {
+       disable => $disable,
+       comment => $comment,
        action => $action,
        iface => $iface,
        source => $source,
@@ -1129,7 +1156,7 @@ sub parse_fw_option {
 
     my ($opt, $value);
 
-    if ($line =~ m/^(enable|macfilter|nosmurfs|tcpflags):\s*(0|1)\s*$/i) {
+    if ($line =~ m/^(enable|dhcp|macfilter|nosmurfs|tcpflags):\s*(0|1)\s*$/i) {
        $opt = lc($1);
        $value = int($2);
     } elsif ($line =~ m/^(policy-(in|out)):\s*(ACCEPT|DROP|REJECT)\s*$/i) {