Netdev Archive on lore.kernel.org
 help / color / Atom feed
From: Etienne Champetier <champetier.etienne@gmail.com>
To: netdev@vger.kernel.org, netfilter-devel@vger.kernel.org
Subject: bridge firewall "bypass" using VLAN 0 stacking
Date: Thu, 20 Aug 2020 17:17:56 +0200
Message-ID: <CAOdf3grDKBkYmt54ZAzG1zZ6zz1JXeoHSv67_Fc9-nRiY662mQ@mail.gmail.com> (raw)

Hello Linux network folks,

On the input path, the kernel will remove any number of VLAN 0 headers
(priority tagging)
On the bridge firewalling path (iptables filter FORWARD or nftable
bridge table),
the kernel only looks at the current layer, except with
bridge-nf-filter-vlan-tagged=1 where it goes 1 layer deep.

This difference in behaviour makes any project using bridge
firewalling possibly vulnerable.

This was fixed in LXD via
https://github.com/lxc/lxd/commit/7599ff5834c4e0fedb3870a35ff457d342b2d1d8
Openstack Team just made their issue public:
https://bugs.launchpad.net/neutron/+bug/1884341

I haven't checked much more projects (libvirt has good rules but not
enabled by default), but I'm wondering if the current Linux behaviour
could be improved to be less surprising.

If we take the example of LXD (before the fix), enabling anti spoofing options
(security.ipv*_filtering) gives the following nft rules:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
table bridge lxd {
    chain in.u1.eth0 {
        type filter hook input priority filter; policy accept;
        iifname "vethececeb08" ether saddr != 00:16:3e:14:12:03 drop
        iifname "vethececeb08" arp saddr ether != 00:16:3e:14:12:03 drop
        iifname "vethececeb08" icmpv6 type nd-neighbor-advert
@nh,528,48 != 95530783235 drop
        iifname "vethececeb08" arp saddr ip != 10.242.217.2 drop
        iifname "vethececeb08" ip saddr 0.0.0.0 ip daddr
255.255.255.255 udp dport 67 accept
        iifname "vethececeb08" ip saddr != 10.242.217.2 drop
        iifname "vethececeb08" ip6 saddr fe80::/10 ip6 daddr ff02::1:2
udp dport 547 accept
        iifname "vethececeb08" ip6 saddr fe80::/10 ip6 daddr ff02::2
icmpv6 type nd-router-solicit accept
        iifname "vethececeb08" icmpv6 type nd-neighbor-advert
@nh,384,128 != 336637795894033326396171405568628756995 drop
        iifname "vethececeb08" ip6 saddr !=
fd42:14c6:68bf:9774:216:3eff:fe14:1203 drop
        iifname "vethececeb08" icmpv6 type nd-router-advert drop
    }

    chain fwd.u1.eth0 {
        type filter hook forward priority filter; policy accept;
        iifname "vethececeb08" ether saddr != 00:16:3e:14:12:03 drop
        iifname "vethececeb08" arp saddr ether != 00:16:3e:14:12:03 drop
        iifname "vethececeb08" icmpv6 type nd-neighbor-advert
@nh,528,48 != 95530783235 drop
        iifname "vethececeb08" arp saddr ip != 10.242.217.2 drop
        iifname "vethececeb08" ip saddr != 10.242.217.2 drop
        iifname "vethececeb08" ip6 saddr !=
fd42:14c6:68bf:9774:216:3eff:fe14:1203 drop
        iifname "vethececeb08" icmpv6 type nd-neighbor-advert
@nh,384,128 != 336637795894033326396171405568628756995 drop
        iifname "vethececeb08" icmpv6 type nd-router-advert drop
    }
...
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Some (many) people expect those rules to prevent ARP/IPv4/IPv6
spoofing, but they don't protect either the host or the Linux
neighbours.

Here is a simple scapy script to send router advertisements
encapsulated in 2 VLAN 0 headers. This will bypass all those rules but
be accepted:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ra  = Ether()/Dot1Q(vlan=0)/Dot1Q(vlan=0)
ra /= IPv6(dst='ff02::1')
ra /= ICMPv6ND_RA(chlim=64, prf='High', routerlifetime=1800)
ra /= ICMPv6NDOptSrcLLAddr(lladdr=get_if_hwaddr('eth0'))
ra /= ICMPv6NDOptPrefixInfo(prefix="2001:db8:1::", prefixlen=64,
validlifetime=1810, preferredlifetime=1800)
sendp(ra)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(this also works with 'Dot1AD(vlan=0)')

Some server NICs (at least Cisco UCS) use priority tagging by default
so just dropping VLAN 0 traffic is not an option, but here are some
questions / thoughts:

- should the input path drop packets with stacked VLAN 0 ?

- should there be a sysctl to not remove VLAN 0 on the input path ?
VLAN 0 stacking might allow to bypass anti spoofing / DHCP & NDP guard
on some switches in some configuration,
If your network doesn't use priority tagging there is no reason to
accept such traffic.

- should the bridge path have the same behaviour as the input path and
"remove" all the VLAN 0 headers by default ? (that would fix all
projects using bridge firewalling at once)

- are there any other headers that are automatically removed on the
input path that would allow a similar "bypass" ?

Best
Etienne Champetier

                 reply index

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CAOdf3grDKBkYmt54ZAzG1zZ6zz1JXeoHSv67_Fc9-nRiY662mQ@mail.gmail.com \
    --to=champetier.etienne@gmail.com \
    --cc=netdev@vger.kernel.org \
    --cc=netfilter-devel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

Netdev Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/netdev/0 netdev/git/0.git
	git clone --mirror https://lore.kernel.org/netdev/1 netdev/git/1.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 netdev netdev/ https://lore.kernel.org/netdev \
		netdev@vger.kernel.org
	public-inbox-index netdev

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.netdev


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git