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
}
}