From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id 452DC68C7C for ; Fri, 10 Sep 2021 17:35:03 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 33F4016F1D for ; Fri, 10 Sep 2021 17:34:33 +0200 (CEST) Received: from kvmformation3.odiso.net (globalOdiso.M6Lille.odiso.net [89.248.211.242]) by firstgate.proxmox.com (Proxmox) with ESMTP id 3B3CD16F12 for ; Fri, 10 Sep 2021 17:34:31 +0200 (CEST) Received: by kvmformation3.odiso.net (Postfix, from userid 0) id DB8E15717; Fri, 10 Sep 2021 17:34:30 +0200 (CEST) From: Alexandre Derumier To: pve-devel@lists.proxmox.com Date: Fri, 10 Sep 2021 17:34:29 +0200 Message-Id: <20210910153430.4045276-1-aderumier@odiso.com> X-Mailer: git-send-email 2.30.2 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL 0.877 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% HEADER_FROM_DIFFERENT_DOMAINS 0.25 From and EnvelopeFrom 2nd level mail domains are different KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment KAM_LAZY_DOMAIN_SECURITY 1 Sending domain does not have any anti-forgery methods KHOP_HELO_FCRDNS 0.399 Relay HELO differs from its IP's reverse DNS NO_DNS_FOR_FROM 0.001 Envelope sender has no MX or A DNS records SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_NONE 0.001 SPF: sender does not publish an SPF Record URIBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [spinics.net, firewall.pm] Subject: [pve-devel] [PATCH V2 pve-firewall] add cluster ebtables_dst_macfilter option. X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 10 Sep 2021 15:35:03 -0000 This new option allow filtering of destination macs for ingress traffic. This is a protection from bad/hosting networks (like hetzner) flooding traffic with non-hosted mac. To be fast, one rule, this use the "--among-dst mac,mac,mac,mac," syntax. broadcast mac ff:ff:ff:ff:ff:ff is always allowed currently, ebtables-restore segfault if too many are defined https://www.spinics.net/lists/netfilter/msg55995.html So, I'm using "--among-dst-file", loading macs from an external file. Note that "ebtables-save" still show the syntax with "--among-dst mac,mac,mac," (with a comma at the end), so I compile the full mac list with --among-dst to compare, and if update is needed, I'm writing the dst file in /var/lib/pve-firewall/chain-macfilter, and replace among-dst syntax by among-dst-file Changelog v2: - as we use one rule for performance, add all vms/ct macaddress when vm firewall is enabled. (even if vm macfilter option is disabled). Signed-off-by: Alexandre Derumier --- src/PVE/Firewall.pm | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/src/PVE/Firewall.pm b/src/PVE/Firewall.pm index edc5336..8277ee0 100644 --- a/src/PVE/Firewall.pm +++ b/src/PVE/Firewall.pm @@ -1221,6 +1221,11 @@ our $cluster_option_properties = { default => 1, optional => 1, }, + ebtables_dst_macfilter => { + description => "Filtering VM/CT destination mac for ingress traffic.", + type => 'boolean', + optional => 1, + }, policy_in => { description => "Input policy.", type => 'string', @@ -2867,7 +2872,7 @@ sub parse_clusterfw_option { if (($value > 1) && ((time() - $value) > 60)) { $value = 0 } - } elsif ($line =~ m/^(ebtables):\s*(0|1)\s*$/i) { + } elsif ($line =~ m/^(ebtables|ebtables_dst_macfilter):\s*(0|1)\s*$/i) { $opt = lc($1); $value = int($2); } elsif ($line =~ m/^(policy_(in|out)):\s*(ACCEPT|DROP|REJECT)\s*$/i) { @@ -3948,11 +3953,19 @@ sub compile_ebtables_filter { ruleset_create_chain($ruleset, "PVEFW-FORWARD"); ruleset_create_chain($ruleset, "PVEFW-FWBR-OUT"); + + if ($cluster_conf->{options}->{ebtables_dst_macfilter}) { + #filtering destination mac for ipv4/ipv6 + ruleset_create_chain($ruleset, "PVEFW-FWBR-IN"); + ruleset_addrule($ruleset, 'PVEFW-FORWARD', '-i fwln+', '-j PVEFW-FWBR-IN'); + } + #for ipv4 and ipv6, check macaddress in iptables, so we use conntrack 'ESTABLISHED', to speedup rules ruleset_addrule($ruleset, 'PVEFW-FORWARD', '-p IPv4', '-j ACCEPT'); ruleset_addrule($ruleset, 'PVEFW-FORWARD', '-p IPv6', '-j ACCEPT'); ruleset_addrule($ruleset, 'PVEFW-FORWARD', '-o fwln+', '-j PVEFW-FWBR-OUT'); + my $maclist = []; # generate firewall rules for QEMU VMs foreach my $vmid (sort keys %{$vmdata->{qemu}}) { eval { @@ -3975,7 +3988,7 @@ sub compile_ebtables_filter { push(@$arpfilter, $ip); } } - generate_tap_layer2filter($ruleset, $iface, $macaddr, $vmfw_conf, $vmid, $arpfilter); + generate_tap_layer2filter($ruleset, $iface, $macaddr, $vmfw_conf, $vmid, $arpfilter, $maclist); } }; warn $@ if $@; # just to be sure - should not happen @@ -4012,17 +4025,23 @@ sub compile_ebtables_filter { push @$arpfilter, $ip; } } - generate_tap_layer2filter($ruleset, $iface, $macaddr, $vmfw_conf, $vmid, $arpfilter); + generate_tap_layer2filter($ruleset, $iface, $macaddr, $vmfw_conf, $vmid, $arpfilter, $maclist); } }; warn $@ if $@; # just to be sure - should not happen } + if ($cluster_conf->{options}->{'ebtables_dst_macfilter'} && @$maclist > 0) { + push @$maclist, 'ff:ff:ff:ff:ff:ff'; #allow broadcast mac + my $maclist_str = join ',',sort(@$maclist); + ruleset_addrule($ruleset, 'PVEFW-FWBR-IN', "--among-dst ! $maclist_str,", '-j DROP'); + } + return $ruleset; } sub generate_tap_layer2filter { - my ($ruleset, $iface, $macaddr, $vmfw_conf, $vmid, $arpfilter) = @_; + my ($ruleset, $iface, $macaddr, $vmfw_conf, $vmid, $arpfilter, $maclist) = @_; my $options = $vmfw_conf->{options}; my $tapchain = $iface."-OUT"; @@ -4037,6 +4056,10 @@ sub generate_tap_layer2filter { ruleset_addrule($ruleset, $tapchain, "-s ! $macaddr", '-j DROP'); } + if (defined($macaddr)) { + push @$maclist, $macaddr; + } + if (@$arpfilter){ my $arpchain = $tapchain."-ARP"; ruleset_addrule($ruleset, $tapchain, "-p ARP", "-j $arpchain"); @@ -4225,6 +4248,15 @@ sub get_ebtables_cmdlist { next if ! $pve_include; $pve_include = 0; } + + if ($cmd =~ m/^-A (\S+) --among-dst ! (\S+) -j DROP/) { + my $chain = $1; + my $maclist_raw = $2."\n"; + my $filename = "$pve_fw_status_dir/ebtables_macfilter-$chain"; + PVE::Tools::file_set_contents($filename, $maclist_raw); + $cmd = "-A $1 --among-dst-file ! $filename -j DROP"; + } + $cmdlist .= "$cmd\n"; } } -- 2.30.2