On 2/21/25 7:02 PM, robinleepowell(a)gmail.com wrote:
So I, like many other people, have hit problems with nftables
ordering, as has been discussed on this mailing list MANY TIMES.
This whole thing seemed ridiculous so I asked the nftables people about what one is
*supposed* to do in this situation. It turns out that the standard solution is for
libvirt's nftables rules to set a packet mark (there's a collision possibility
here but it's a 32 bit integer if you pick one at random it shouldn't be a
problem) and then the user adds a rule to exclude packets with that mark from any reject
rules they might have, or explicitly accept marked packets in their own chains, or
whatever.
Was the discussion on a public forum somewhere? I'd like to look at
exactly what they said.
It's not *as nice* as the iptables situation, but having documentation that says
"if you're using nftables make sure that packets with mark 79892 are accepted in
all your chains" is quite straightforward compared to the current situation of
"LOL good luck". (I'm not blaming anyone there!, the current situation is
impossible for libvirt to navigate and it's not anyone's fault.)
It does still require that the other utilities know this secret number,
and agree to "anti-reject" it as we've requested, though. Also doesn't
this require that libvirt's table is processed first, before the other
utilities' tables? Otherwise, if the other tables are traversed before
libvirt has a chance to mark the packet with the special number, they
won't get the signal, so they'll reject the traffic. So I we would have
set our table as a higher priority, but then what if someone else sets
their table with an *even higher* priority? e.g. firewalld has "priority
filter + 10" for its forwarding rules, so could make ours "priority
filter + 20", but what if, e.g. docker decided to make theirs "priority
filter +50"?). (yes, that's all a rhetorical question. I guess in the
end everything like this that we do will chip away a bit more at the
list of people who encounter problems; it will never reach 0, but it
will at least get closer :-))
Aside from that, libvirt's nftables rules are default accept, and it has
no rules looking at traffic that is destined for the host, only for
forwarded traffic that is going *through* the host, mainly with the
intent of rejecting stuff it doesn't like. So are you/they suggesting
that this forwarded traffic be marked with the special "libvirt code"?
Or that we should also add back rules that match input DNS/DHCP/TFTP on
the libvirt-created bridges, and have them both accept and mark those
packets?
If y'all don't like that, what's working excellently for me is adding
`iifname "virbr*" accept` to my rule chain. FWIW.
Just keep in mind that "iifname" has to fetch the name of the interface
and do a string comparison for each packet, while "iif" just does a
quick comparison of ifindex, which I think is already saved away in the
skb (of course wildcards aren't possible in that case, but if you have
just a couple of libvirt networks it's still more efficient to have a
rule using "iif" for each interface.
It was very hard to navigate through this situation because there's no documentation
that this problem even exists.
Yeah, that's my fault. When I added the nftables backend, I forgot to
update
https://libvirt.org/firewall.html (which is in docs/firewall.rst
in the libvirt sources). (also at the time I wrote the code, I I keep
remembering that I should do that, but only when I'm in the middle of
something else and somehow I haven't managed to even write it down on a
list.
My suggestion is to describe the situation at
https://libvirt.org/firewall.html and
suggest the virbr* fix, and down the road maybe look at this mark thing.
That's a kind of a broad solution though - libvirt's rules only reject
specific traffic between libvirt-created bridges (and incoming traffic
from outside a bridge's direct connects in the case of forward
mode='nat'), Anywhere they allow traffic, they allow *all* of it. The
real problematic stuff is traffic between the guests and the host (the
rules we've had for iptables that are absent in nftables are those to
allow inbound DNS, DHCP, and TFTP that are arriving on a virbr*
interface, and destined for the host). If you allow *all* traffic for
virbr*, then you're leaving the host wide open to all traffic from any
guests (since libvirt's own rules are default accept). I think the
suggestion needs to be more than just "allow all incoming on virbr*".
I'd like to help. I'm happy to write up issues for this, and
I'm happy to write the updates to the firewall docs; just tell me what you'd like
me to do.
firewall.rst should really be a shortened intro that links to the
current firewall.html for iptables (maybe renaming it
"iptables.rst/html"?), and to a new nftables.rst/html for information
about nftables (including an explanation of the "many tables, all must
resolve to 'accept' problem.)
Since I've never gotten around to it in spite of wanting it done, I'd
certainly be happy to review an update done by anyone else :-)