On 10/26/21 2:27 PM, Eric Garver wrote:
+ Laine, from the libvirt project
On Tue, Oct 26, 2021 at 05:37:41AM -0000, David Geise wrote:
> Hi,
>
> Can someone provide the recipes for firewalling libvirt guests? I see
> talk about it but no clear answers how to correctly implement it.
I can try.
Firewalld v0.9.0+ has policy objects which allow forward filtering. I
think that will help in many areas you point to below.
This blog may also be useful:
https://firewalld.org/2020/09/policy-objects-filtering-container-and-vm-t...
> I would like to be able to enable the following typical scenarios:
> 1. Guest restricted to connecting to existing vpn
> (
privateinternetaccess.com) established by PIA app on a given
> branch. No in or out connections to LAN resources. No connections
> to other guests.
Newer libvirt uses a "libvirt" zone. You should put your VPN interface
into a separate zone, e.g. "vpn". Then you can create one policy to
allow forwarding to the VPN and second policy to deny everything else.
A policy to allow forward from libvirt --> VPN would look like this
(untested):
# firewall-cmd --permanent --new-policy libvirtToVpn
# firewall-cmd --permanent --policy libvirtToVpn --priority -100
# firewall-cmd --permanent --policy libvirtToVpn \
--add-ingress-zone libvirt
# firewall-cmd --permanent --policy libvirtToVpn \
--add-egress-zone vpn
# firewall-cmd --permanent --policy libvirtToVpn \
--set-target ACCEPT
A policy to deny forward from libvirt --> world would look like this
(untested):
# firewall-cmd --permanent --new-policy libvirtToWorld
# firewall-cmd --permanent --policy libvirtToWorld --priority -99
# firewall-cmd --permanent --policy libvirtToWorld \
--add-ingress-zone libvirt
# firewall-cmd --permanent --policy libvirtToWorld \
--add-egress-zone ANY
# firewall-cmd --permanent --policy libvirtToWorld \
--set-target REJECT
But if the VPN interface was intended to be the default route for all
traffic from VMs, and the "normal" (non-VPN) ethernet on the host was
intended to be the default route for traffic from the host (and maybe
from some other VMs), then you'd need some sort of policy *routing*,
which would result in a different next-hop for traffic coming from the
VM's tap device / IP address. Is there policy-based routing underneath
the --add-egress-zone/--policy options in your example?
Normally, once the packets come out of the VM's tap device and into the
host networking stack, they will be routed according to their
destination address and nothing else, so if the default route on the
host was via enp2s0, then both host and VM traffic would go out that
interface.
Note that libvirt does have a *very* primitive method of limiting
traffic outbound from one of its virtual networks to the outside - in
the <forward> element for the network, you can set the "dev" attribute,
and that will limit outbound/inbound traffic for any VM connected to
that network to using the specified interface. So for example, if your
libvirt network definition had
<forward mode='nat' dev='vpn0'/>
then all traffic from guests that ended up being routed out a different
interface from "vpn0" would be rejected. But this suffers from the
problem I mention above - it doesn't magically cause all the traffic to
egress via vpn0, it just rejects all the traffic that doesn't end up
doing that (it's still up to the IP routing on the host to figure out
that the traffic needs to egress via vpn0).
Note: libvirtToWorld uses a lower precedence priority value so it will
execute _after_ libvirtToVpn.
Note 2: Since firewalld is stateful the return traffic will implicitly
be allowed.
> 2. Direct connection to WAN via gateway. Bypasses VPN. No
> connections to LAN. No connections to other guests.
Similar to above, but put your gateway into a separate zone, e.g.
"external".
This implies to me that there *is* policy-based routing being setup by
firewalld, otherwise I don't understand how it could work...
> 3. Guest connection to resources provided by VM host only.
I don't understand what you mean by this.
I think he's asking for the equivalent of what libvirt calls and
"isolated" network - the guests on the network can communicate with each
other and with the host, but nothing else. This can easily be done in
libvirt by defining an isolated network and connecting to that, as
outlined here:
https://libvirt.org/formatnetwork.html#examplesPrivate
(you can additionally prevent the VMs connected to this network from
communicating with each other by adding <port isolated='yes'/> to the
network definition - this sets a bit for each port on the bridge device.
so doesn't even involve iptables).
> I know this is a big ask, but I've been reading posts here & elsewhere and
its a bit confusing.
No problem. This is why the mailing list exists. :)
> Previously I had this kluged together through a series of iptables scripts but it was
an awful hack and I'd like to do better this time.
> This is on kubuntu 20.10 (firewalld 0.9.3, libvirt 7.6.0, QEMU 6.0.0), pretty close
to stock setup.
Hopefully the above points you in the correct direction.
Note that all of my comments point in a completely *different*
direction, since I'm coming from the PoV of libvirt. Eric's suggestions
are likely more modern and flexible. You may find that some of the
things I suggest are simpler, and could be adequate for certain cases,
but may not do everything you need (in particular, the facilities built
into libvirt have no way to do policy routing so that some VMs have a
default route pointing to the VPN and some to the normal local network's
default router).
Eric.