]> git.proxmox.com Git - pve-firewall.git/blobdiff - PVE/Firewall.pm
add some useful chains
[pve-firewall.git] / PVE / Firewall.pm
index ce9b801c717623d8b32d8593a911907d5e8b8541..081b35098b3b7c4947391962fb9ff472f3d9f248 100644 (file)
@@ -348,6 +348,118 @@ my $pve_fw_macros = {
 my $pve_fw_parsed_macros;
 my $pve_fw_preferred_macro_names = {};
 
+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 },
+    ],
+};
+
+# iptables -p icmp -h
+my $icmp_type_names = {
+    any => 1,
+    'echo-reply' => 1,
+    'destination-unreachable' => 1,
+    'network-unreachable' => 1,
+    'host-unreachable' => 1,
+    'protocol-unreachable' => 1,
+    'port-unreachable' => 1,
+    'fragmentation-needed' => 1,
+    'source-route-failed' => 1,
+    'network-unknown' => 1,
+    'host-unknown' => 1,
+    'network-prohibited' => 1,
+    'host-prohibited' => 1,
+    'TOS-network-unreachable' => 1,
+    'TOS-host-unreachable' => 1,
+    'communication-prohibited' => 1,
+    'host-precedence-violation' => 1,
+    'precedence-cutoff' => 1,
+    'source-quench' => 1,
+    'redirect' => 1,
+    'network-redirect' => 1,
+    'host-redirect' => 1,
+    'TOS-network-redirect' => 1,
+    'TOS-host-redirect' => 1,
+    'echo-request' => 1,
+    'router-advertisement' => 1,
+    'router-solicitation' => 1,
+    'time-exceeded' => 1,
+    'ttl-zero-during-transit' => 1,
+    'ttl-zero-during-reassembly' => 1,
+    'parameter-problem' => 1,
+    'ip-header-bad' => 1,
+    'required-option-missing' => 1,
+    'timestamp-request' => 1,
+    'timestamp-reply' => 1,
+    'address-mask-request' => 1,
+    'address-mask-reply' => 1,
+};
+
 sub get_firewall_macros {
 
     return $pve_fw_parsed_macros if $pve_fw_parsed_macros;
@@ -458,6 +570,7 @@ sub parse_port_name_number_or_range {
     foreach my $item (split(/,/, $str)) {
        my $portlist = "";
        my $oldpon = undef;
+       $nbports++;
        foreach my $pon (split(':', $item, 2)) {
            $pon = $services->{byname}->{$pon}->{port} if $services->{byname}->{$pon}->{port};
            if ($pon =~ m/^\d+$/){
@@ -467,7 +580,6 @@ sub parse_port_name_number_or_range {
            }else{
                die "invalid port $services->{byname}->{$pon}\n" if !$services->{byname}->{$pon};
            }
-           $nbports++;
        }
     }
 
@@ -583,10 +695,35 @@ sub ruleset_generate_rule {
     $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};
-    $cmd .= "  --match multiport" if $rule->{nbdport} && $rule->{nbdport} > 1;
-    $cmd .= " --dport $rule->{dport}" if $rule->{dport};
-    $cmd .= "  --match multiport" if $rule->{nbsport} && $rule->{nbsport} > 1;
-    $cmd .= " --sport $rule->{sport}" if $rule->{sport};
+
+    if (($rule->{nbdport} && $rule->{nbdport} > 1) ||
+       ($rule->{nbsport} && $rule->{nbsport} > 1)) {
+       $cmd .= " --match multiport" 
+    }
+
+    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}";
+           } else {
+               $cmd .= " --dport $rule->{dport}";
+           }
+       }
+    }
+
+    if ($rule->{sport}) {
+       if ($rule->{nbsport} && $rule->{nbsport} > 1) {
+           $cmd .= " --sports $rule->{sport}";
+       } else {
+           $cmd .= " --sport $rule->{sport}";
+       }
+    }
+
+    $cmd .= " -m addrtype --dst-type $rule->{dsttype}" if $rule->{dsttype};
 
     if (my $action = $rule->{action}) {
        $goto = 1 if !defined($goto) && $action eq 'PVEFW-SET-ACCEPT-MARK';
@@ -1120,6 +1257,21 @@ sub read_vm_firewall_rules {
     return $rules;
 }
 
+sub generate_std_chains {
+    my ($ruleset) = @_;
+
+    foreach my $chain (keys %$pve_std_chains) {
+       ruleset_create_chain($ruleset, $chain);
+       foreach my $rule (@{$pve_std_chains->{$chain}}) {
+           if (ref($rule)) {
+               ruleset_generate_rule($ruleset, $chain, $rule);
+           } else {
+               ruleset_addrule($ruleset, $chain, $rule);
+           }
+       }
+    }
+}
+
 sub compile {
     my $vmdata = read_local_vm_config();
     my $rules = read_vm_firewall_rules($vmdata);
@@ -1138,8 +1290,7 @@ sub compile {
     ruleset_create_chain($ruleset, "PVEFW-OUTPUT");
     ruleset_create_chain($ruleset, "PVEFW-FORWARD");
 
-    ruleset_create_chain($ruleset, "PVEFW-SET-ACCEPT-MARK");
-    ruleset_addrule($ruleset, "PVEFW-SET-ACCEPT-MARK", "-j MARK --set-mark 1");
+    generate_std_chains($ruleset);
 
     my $enable_hostfw = 0;
     $filename = "/etc/pve/local/host.fw";