]> git.proxmox.com Git - pve-firewall.git/blobdiff - PVE/Firewall.pm
accept traffic to unmanaged bridge ports
[pve-firewall.git] / PVE / Firewall.pm
index 54f9c97d585de6b0624a932964a1fe31a4b808e7..568b531ded09bc7000b7bc12f27a281ad9817552 100644 (file)
@@ -352,6 +352,97 @@ my $pve_std_chains = {
     'PVEFW-SET-ACCEPT-MARK' => [
        "-j MARK --set-mark 1",
     ],
+    'PVEFW-DropBroadcast' => [
+       # same as shorewall 'Broadcast'
+       # simply DROP BROADCAST/MULTICAST/ANYCAST
+       # we can use this to reduce logging
+       { action => 'DROP', dsttype => 'BROADCAST' },
+       { action => 'DROP', dsttype => 'MULTICAST' },
+       { action => 'DROP', dsttype => 'ANYCAST' },
+       { 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 => 'icmp' },
+       "-p tcp -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",
+    ],
+    'PVEFW-Drop' => [
+       # same as shorewall 'Drop', which is equal to DROP, 
+       # but REJECT/DROP some packages to reduce logging,
+       # and ACCEPT critical ICMP types
+       { action => 'PVEFW-reject',  proto => 'tcp', dport => '43' }, # REJECT 'auth'
+       # we are not interested in BROADCAST/MULTICAST/ANYCAST
+       { action => 'PVEFW-DropBroadcast' },
+       # ACCEPT critical ICMP types
+       { action => 'ACCEPT', proto => 'icmp', dport => 'fragmentation-needed' },
+       { action => 'ACCEPT', proto => 'icmp', dport => 'time-exceeded' },
+       # Drop packets with INVALID state
+       "-m conntrack --ctstate INVALID -j DROP",
+       # Drop Microsoft SMB noise
+       { action => 'DROP', proto => 'udp', dport => '135,445', nbdport => 2 },
+       { action => 'DROP', proto => 'udp', dport => '137:139'},
+       { action => 'DROP', proto => 'udp', dport => '1024:65535', sport => 137 },
+       { action => 'DROP', proto => 'tcp', dport => '135,139,445', nbdport => 3 },
+       { action => 'DROP', proto => 'udp', dport => 1900 }, # UPnP
+       # Drop new/NotSyn traffic so that it doesn't get logged
+       "-p tcp -m tcp ! --tcp-flags FIN,SYN,RST,ACK SYN -j DROP",
+       # Drop DNS replies
+       { action => 'DROP', proto => 'udp', sport => 53 },
+    ],
+    'PVEFW-Reject' => [
+       # same as shorewall 'Reject', which is equal to Reject, 
+       # but REJECT/DROP some packages to reduce logging,
+       # and ACCEPT critical ICMP types
+       { action => 'PVEFW-reject',  proto => 'tcp', dport => '43' }, # REJECT 'auth'
+       # we are not interested in BROADCAST/MULTICAST/ANYCAST
+       { action => 'PVEFW-DropBroadcast' },
+       # ACCEPT critical ICMP types
+       { action => 'ACCEPT', proto => 'icmp', dport => 'fragmentation-needed' },
+       { action => 'ACCEPT', proto => 'icmp', dport => 'time-exceeded' },
+       # Drop packets with INVALID state
+       "-m conntrack --ctstate INVALID -j DROP",
+       # Drop Microsoft SMB noise
+       { action => 'PVEFW-reject', proto => 'udp', dport => '135,445', nbdport => 2 },
+       { action => 'PVEFW-reject', proto => 'udp', dport => '137:139'},
+       { action => 'PVEFW-reject', proto => 'udp', dport => '1024:65535', sport => 137 },
+       { action => 'PVEFW-reject', proto => 'tcp', dport => '135,139,445', nbdport => 3 },
+       { action => 'DROP', proto => 'udp', dport => 1900 }, # UPnP
+       # Drop new/NotSyn traffic so that it doesn't get logged
+       "-p tcp -m tcp ! --tcp-flags FIN,SYN,RST,ACK SYN -j DROP",
+       # Drop DNS replies
+       { action => 'DROP', proto => 'udp', sport => 53 },
+    ],
+    'PVEFW-logflags' => [
+       # same as shorewall logflags action. (fixme: enable/disable logging)
+       "-j LOG --log-prefix \"logflags-dropped:\" --log-level 4 --log-ip-options",
+       "-j DROP",
+    ],
+    'PVEFW-tcpflags' => [
+       # same as shorewall tcpflags action.
+       # Packets arriving on this interface are checked for som illegal combinations of TCP flags
+       "-p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG FIN,PSH,URG -g PVEFW-logflags",
+       "-p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG NONE -g PVEFW-logflags",
+       "-p tcp -m tcp --tcp-flags SYN,RST SYN,RST -g PVEFW-logflags",
+       "-p tcp -m tcp --tcp-flags FIN,SYN FIN,SYN -g PVEFW-logflags",
+       "-p tcp -m tcp --sport 0 --tcp-flags FIN,SYN,RST,ACK SYN -g PVEFW-logflags",
+    ],
+    'PVEFW-smurflog' => [
+       # same as shorewall smurflog. (fixme: enable/disable logging)
+       "-j LOG --log-prefix \"smurfs-dropped\" --log-level 4",
+       "-j DROP",
+    ],
+    'PVEFW-smurfs' => [
+       # same as shorewall smurfs action
+       # Filter packets for smurfs (packets with a broadcast address as the source).
+       "-s 0.0.0.0/32 -j RETURN",
+       "-m addrtype --src-type BROADCAST -g PVEFW-smurflog",
+       "-s 224.0.0.0/4 -g PVEFW-smurflog",
+    ],
 };
 
 # iptables -p icmp -h
@@ -725,6 +816,8 @@ sub generate_bridge_chains {
        ruleset_create_chain($ruleset, "$bridge-IN");
        ruleset_addrule($ruleset, "$bridge-FW", "-m physdev --physdev-is-bridged --physdev-is-out -j $bridge-IN");
        ruleset_addrule($ruleset, "$bridge-FW", "-m mark --mark 1 -j ACCEPT");
+       # accept traffic to unmanaged bridge ports
+       ruleset_addrule($ruleset, "$bridge-FW", "-m physdev --physdev-is-bridged --physdev-is-out -j ACCEPT ");
     }
 }
 
@@ -738,13 +831,23 @@ sub generate_tap_rules_direction {
 
     ruleset_create_chain($ruleset, $tapchain);
 
+    if (!(defined($options->{nosmurfs}) && $options->{nosmurfs} == 0)) {
+       ruleset_addrule($ruleset, $tapchain, "-m conntrack --ctstate INVALID,NEW -j PVEFW-smurfs");
+    }
+
+    if ($options->{tcpflags}) {
+       ruleset_addrule($ruleset, $tapchain, "-p tcp -j PVEFW-tcpflags");
+    }
+
     ruleset_addrule($ruleset, $tapchain, "-m conntrack --ctstate INVALID -j DROP");
     ruleset_addrule($ruleset, $tapchain, "-m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT");
 
-    if ($direction eq 'OUT' && defined($macaddr)) {
+    if ($direction eq 'OUT' && defined($macaddr) && 
+       !(defined($options->{macfilter}) && $options->{macfilter} == 0)) {
        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;
@@ -781,11 +884,13 @@ sub generate_tap_rules_direction {
            ruleset_addrule($ruleset, $tapchain, "-j ACCEPT");
        }
     } elsif ($policy eq 'DROP') {
+       ruleset_addrule($ruleset, $tapchain, "-j PVEFW-Drop");
        ruleset_addrule($ruleset, $tapchain, "-j LOG --log-prefix \"$tapchain-dropped: \" --log-level 4");
        ruleset_addrule($ruleset, $tapchain, "-j DROP");
     } elsif ($policy eq 'REJECT') {
+       ruleset_addrule($ruleset, $tapchain, "-j PVEFW-Reject");
        ruleset_addrule($ruleset, $tapchain, "-j LOG --log-prefix \"$tapchain-reject: \" --log-level 4");
-       ruleset_addrule($ruleset, $tapchain, "-j REJECT");
+       ruleset_addrule($ruleset, $tapchain, "-g PVEFW-reject");
     } else {
        # should not happen
        die "internal error: unknown policy '$policy'";
@@ -976,19 +1081,33 @@ sub parse_fw_rule {
     if ($macro) {
        foreach my $templ (@$macro) {
            my $rule = {};
+           my $param_used = {};
            foreach my $k (keys %$templ) {
                my $v = $templ->{$k};
                if ($v eq 'PARAM') {
                    $v = $param->{$k};
+                   $param_used->{$k} = 1;
                } elsif ($v eq 'DEST') {
                    $v = $param->{dest};
+                   $param_used->{dest} = 1;
                } elsif ($v eq 'SOURCE') {
                    $v = $param->{source};
+                   $param_used->{source} = 1;
                }
 
                die "missing parameter '$k' in macro '$macro_name'\n" if !defined($v);
                $rule->{$k} = $v;
            }
+           foreach my $k (keys %$param) {
+               next if !defined($param->{$k});
+               next if $param_used->{$k};
+               if (defined($rule->{$k})) {
+                   die "parameter '$k' already define in macro (value = '$rule->{$k}')\n"
+                       if $rule->{$k} ne $param->{$k};
+               } else {
+                   $rule->{$k} = $param->{$k};
+               }
+           }
            push @$rules, $rule;
        }
     } else {
@@ -1010,13 +1129,13 @@ sub parse_fw_option {
 
     my ($opt, $value);
 
-    if ($line =~ m/^enable:\s*(0|1)\s*$/i) {
-       $opt = 'enable';
-       $value = int($1);
+    if ($line =~ m/^(enable|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) {
        $opt = lc($1);
        $value = uc($3);
-     } else {
+    } else {
        chomp $line;
        die "can't parse option '$line'\n"
     }