Blocking DHCP servers and router advertisements with nftables

Posted on 2020-08-27 by ungleich

Motivation

Here at ungleich we are providing a variety of hosting services in the Data Center Light. One of the workloads we offer is VM hosting and we need to take some security measures to prevent one customer abusing another customer.

The problem

The virtual machines in our next generation uncloud hosting will be using standard DHCP and IPv6 address assignments. Currently we are still using the OpenNebula contextualisation scripts that read the networking information from an attached ISO.

While this makes it easier to create VM images and VMs behave even more like regular computers, this exposes the VMs to attacks where one customer runs a DHCP server or IPv6 router advertisement daemon and tricks the other VMs into sending traffic to it.

The architecture

VMs are connected to a single shared network in which they get their IP addresses (in uncloud usually only IPv6) and then they can retrieve more information from a metadata server. So the main protection that is required is preventing to trick other customers into using a wrong IP address or route.

Also, if the network is IPv6 only, another customer should not be able to trick someone else into using IPv4.

Fixing it

So the easiest thing to do is to disallow IPv6 router advertisements and IPv4 DHCP server answers. However as all the interfaces are put into one bridge, we will need to filter on bridge and not ip level:

table bridge filter {
    chain prerouting {
        type filter hook prerouting priority 0;
        policy accept;
    }

Next we create a chain to drop the packets we dislike:

    chain drop_ra_dhcp {
        # Blocks: router advertisements, dhcpv6, dhcpv4
        icmpv6 type nd-router-advert drop
        ip6 version 6 udp sport 547 drop
        ip  version 4 udp sport 67 drop
    }

Now the only thing left is to correctly classify the traffic. For this lets take some real world assumptions:

  • Let's assume the bridge is named br100
  • Let's assume the upstream interface that should allow RA/DHCP is named vxlan100

Then we can connect the chains together:

table bridge filter {
    chain prerouting {
        type filter hook prerouting priority 0;
        policy accept;

        iifname != vxlan100 meta ibrname br100 jump drop_ra_dhcp
    }

    chain drop_ra_dhcp {
        # Blocks: router advertisements, dhcpv6, dhcpv4
        icmpv6 type nd-router-advert drop
        ip6 version 6 udp sport 547 drop
        ip  version 4 udp sport 67 drop
    }
}

This way we have a very simple filter to prevent router advertisements or dhcp answers to come from customer VMs.

We hope you enjoyed reading it, but if something does not make sense, you can ask on our open chat or consult the nftables reference.