2

I am using Arch linux and I have set up a bridge with bridge-utils. Now I would like to firewall it. I would like to drop some packets going through that bridge while allowing this machine to freely communicate with the one behind the bridge. I usually handle such cases by matching iifname lo, but unfortunately it does not match my packets.

again, hopefully more clearly

  • I have two machines, master and slave.
  • master has two ethernet card. One connected to the rest of my network, the other one to slave
  • I want master to be able to send anything to slave
  • I want to filter traffic from the rest of my network to slave on master

I would like to do it using nftables. Do you have any ideas?

Vojtech Kane
  • 143
  • 1
  • 5

1 Answers1

1

The lo interface is not an ethernet interface. It doesn't have a link layer address and can certainly not be part of a bridge. So there's no way iifname lo will ever match.

The bridge layout is still organised similarly to the ip layout, but working at layer 2: ethernet frames that are switched (instead of routed) will enter the forward chain. Ethernet frames that are for the host or coming from the host won't traverse any chain (they weren't created below since they are useless in this use case).

Let's suppose the network is like this:

LAN <--------> eth0 master eth1 <--------> slave
                   (bridge0)

with eth0 and eth1 enslaved to bridge0.

So with one unique bridge on the host, this "empty" nft ruleset is enough to isolate slave, while allowing master unrestricted traffic with LAN and slave, just by using a drop policy with the forward chain.

#!/usr/sbin/nft -f

flush ruleset

table bridge forward {
    chain forward {
        type filter hook forward priority 0; policy drop;
    }
}

It's certainly normal to expect ARP working both ways (else slave won't be able to do much at the IP level):

nft add rule bridge filter forward ether type arp accept

Now if LAN is allowed to ping slave, but not the opposite:

nft add rule bridge filter forward oif eth1 ip protocol icmp icmp type echo-request accept
nft add rule bridge filter forward iif eth1 ip protocol icmp icmp type echo-reply accept

Note that using nftables at the bridge level is still somewhat limited (but cleaner) compared to iptables, because there's still no conntrack integration as of today. So there as far as I know no stateful firewalling available.

So some usually easy tasks may seem awkward, eg: allow ssh from (possibly remote) IP 203.0.113.3. It turns into: allow traffic both ways, except for the initial syn not allowed from slave to LAN:

nft add rule bridge filter forward oif eth1 ip saddr 203.0.113.3 ip protocol tcp tcp dport 22 accept
nft add rule bridge filter forward iif eth1 ip daddr 203.0.113.3 ip protocol tcp tcp sport 22 tcp flags != syn accept

If there is more than one bridge on master, then the rules and/or default policies might have to be adapted.


Stateful firewalling at the bridge layer

UPDATE: kernel 5.3 brought the module nf_conntrack_bridge to have conntrack available at the bridge layer, allowing stateful firewalling. Warning, interactions with older br_netfilter, often loaded in Docker environments, might produce unexpected results.

With such a kernel and a recent enough nftables, replacing the rules above by this ruleset below will allow ARP, and will allow incoming ping or incoming ssh only from IP address 203.0.113.3 to reach slave in a stateful manner: their replies are automatically allowed. slave is still barred from initiating any communication except ARP. The setctzone chain is completely optional: it allows to track separately the bridge conntrack entries from other conntrack entries (eg: generated at the IP routing layer) by assigning them a different conntrack zone id, to avoid clashing entries in very exotic setups.

table bridge isolateslave
delete table bridge isolateslave

table bridge isolateslave {
        chain setctzone {
                type filter hook prerouting priority -300; policy accept;
                ether type ip ct zone set 10
        }

        chain forward {
                type filter hook forward priority 0; policy drop;
                ether type arp accept
                ct state established,related accept
                ct state invalid drop
                oif "eth1" ip saddr 203.0.113.3 tcp dport 22 accept
                oif "eth1" icmp type echo-request accept
        }
}
A.B
  • 9,037
  • 2
  • 19
  • 37