QEMU/networkd/nftables
Remember this post when I was all enthusiastic about using a local server with QEMU and some apparently simple network setup using systemd-networkd? Remember when you were like Oh!, I've gotta try this out! But wait — what about forwarding ports?
There was supposed to be a fourth section going something like "nftables — Now, let's add some port-forwarding!". Later, it became something like "Oh, it's a little more complicated — let's just complete it once I've got all the informations!". It then became "God, this is awfully fucked up", before finally becoming "Oh, it's actually pretty logic - I'm just really dumb."
Well, welcome to that follow-up.
systemd-networkd
As stated, I decided to use nftables for port-forwarding. Yet packets didn't seem to even enter my prerouting chain, which was rather unfortunate, as it is there where things like "this packet will get redirected to this host" should happen.
After asking on #netfilter
and getting friendly troubleshooting advice (and
involuntary invoking a pro-systemd vs. anti-systemd debate when I stated that
the IPMasquerade
option in my systemd-networkd setup might perhaps have had
some bad influence on the matter), I decided to tear my network and its
configuration down and restart from scratch (while people on #netfilter
were
ripping each other's head off about the purpose of systemd-networkd and its
apparent non-conformity to some RFC).
When everything was back up, my setup looked almost the same, except for the
missing IPMasquerade
:
# /etc/systemd/network/qemu0.netdev
[NetDev]
Name=qemu0
Kind=bridge
# /etc/systemd/network/qemu0.network
[Match]
Name=qemu0
[Network]
172.16.10.1/24
IPForward=yes
nftables: masquerade
nftables is an effort to replace the existing netfilter/iptables framework. Although it is not production-ready, I decided to bleed and replace my existing iptables setup with nftables, since in the long run, learning the Ins and Outs of a filtering system that has a hard-to-parse syntax and that will soon get replaced didn't seem very lucrative to me.
Hence, the masquerading takes place in /etc/nftables.conf
:
#!/usr/sbin/nft -f
table ip nat {
chain prerouting {
type nat hook prerouting priority 0;
}
chain postrouting {
type nat hook postrouting priority 0;
ip saddr 172.16.10.1/24 oifname {"wlp3s0", "enp0s25"} masquerade
}
}
Looks sane enough, and works.
nftables: port-forwarding
Packet filtering is a topic that could fill a new article on its own — I usually stick to this mental picture (I may stand corrected):
- Packet enters pre-routing chain, where we may alter it before it enters routing
- if Packet is for the system itself:
- Packet enters input chain, where we decide whether we let it in
- Packet ends/starts here
- Packet enters output chain, where we decide whether we let it out
- else if Packet is for a different system:
- Packet enters forward chain, where we can influence the routing
- Packet enters post-routing chain, where we may alter it before it leaves the system
Alright! So let's add some classic rule: one that forwards connections to the host's port 2222 to the guest's port 22:
#!/usr/sbin/nft -f
table ip nat {
chain prerouting {
type nat hook prerouting priority 0;
tcp dport 2222 dnat 172.16.10.2:22
}
chain postrouting {
type nat hook postrouting priority 0;
ip saddr 172.16.10.1/24 oifname {"wlp3s0", "enp0s25"} masquerade
}
}
The Troubleshooting
Although everything looks fine, we've got the above-stated problem: Packets
don't enter nftables' prerouting
chain. Packets not entering the prerouting
chain apparently ate an entire Friday afternoon away from me.
Let's prevent that for you:
As already stated in the original article,
systemd-networkd provides the options IPForward
and IPMasquerade
. The tricky
thing about IPForward
is that if you don't specify it, it will set the sysctl
option net.ipv4.conf.<interface>.forwarding
to 0 — regardless of whether
you set net.ipv4.ip_forward
to 1 in /etc/sysctl.d/99-ip_forward.conf
.
But we already knew that.
The other tricky thing is that this is actually wrong:
Also,
IPMasquerade
replaces iptables/nftables rules for masquerading in this simple case.
This is incorrect because systemd will not handle IP masquerading on its own, but use iptables. It's actually good, because it doesn't reinvent the wheel, but delegate the filtering to an appropriate tool: the firewall. It also makes sense that if we remove the option, no iptables rules for masquerading will be added.
Yet … in my nftables setup, while packets seemed to enter the
postrouting
chain, they refused to do the same for prerouting
.
At this point, I reread all documentation I could find on creating a NAT with port forwarding for nftables, before stumbling over this:
You cannot use iptables and nft to perform NAT at the same time. So make sure that the iptable_nat module is unloaded:
rmmod iptable_nat
Friday evening. Weekend. It's all fine.
read more
2019
2015
- Waiter! There's a VLA in my C!
- Anarcho DHCP
- QEMU/networkd/nftables
- QEMU/networkd
- Dynamic Waltz