From: Laine Stump <laine@xxxxxxxxxx> This patch restores broken guest network connectivity after a host firewalld is switched to using an nftables backend. It does this by adding libvirt networks' bridge interfaces to the new "libvirt" zone in firewalld. After this patch, the bridge interface of any network created by libvirt (when firewalld is active) will be added to the firewalld zone called "libvirt" if it exists (regardless of the firewalld backend setting). This behavior does *not* depend on whether or not libvirt has installed the libvirt zone file (set with "--with[out]-firewalld-zone" during the configure phase of the package build). If the libvirt zone doesn't exist (either because the package was configured to not install it, or possibly it was installed, but firewalld doesn't support rule priorities, resulting in a parse error), the bridge will remain in firewalld's default zone, which could be innocuous (in the case that the firewalld backend is iptables, guest networking will still function properly with the bridge in the default zone), or it could be disastrous (if the firewalld backend is nftables, we can be assured that guest networking will fail). In order to be unobtrusive in the former case, and informative in the latter, when the libvirt zone doesn't exist we then check the firewalld version to see if it's new enough to support the nftables backend, and then if the backend is actually set to nftables, before logging an error (and failing the net-start operation, since the network couldn't possibly work anyway). When the libvirt zone is used, network behavior is *slightly* different from behavior of previous libvirt. In the past, libvirt network behavior would be affected by the configuration of firewalld's default zone (usually "public"), but now it is affected only by the "libvirt" zone), and thus almost surely warrants a release note for any distro upgrading to libvirt 5.1 or above. Although it's unfortunate that we have to deal with a mandatory behavior change, the architecture of multiple hooks makes it impossible to *not* change behavior in some way, and the new behavior is arguably better (since it will now be possible to manage access to the host from virtual machines vs from public interfaces separately). Resolves: https://bugzilla.redhat.com/1638342 Creates-and-Resolves: https://bugzilla.redhat.com/1650320 Signed-off-by: Laine Stump <laine@xxxxxxxxx> --- NB: I had considered that it might be useful to cache the results of checking the list of active zones, the firewalld version, or the firewalld backend, but in my tests of restarting libvirtd with 100 active networks, the full startup time (from the beginning of "systemctl restart libvirtd.service" until successful return from a subsequent "virsh list") showed 42 seconds and a bit regardless of whether or not I made those checks. This tells me that the amount of time to be saved by caching the results of a single call vs calling once for each network are insignificant relative to everything else that is being done. Because any cached values would need to be stored in the network driver state object, and thus require acquiring the driver-wide lock to update at potentially very different times (e.g. in the response to a dbus message informing us that firewalld was restarted, as well as while starting a new network from an API call) I consider the chance of a bug in my code causing an obscure deadlock sometime in the future to be a much greater concern than maybe saving 1/10th of a second out of 42 (and lock contention might eliminate the gain anyway(), so I have left the code to retrieve the list of zones once for each network start. Changes in V2: * split off from Patch 5. This patch only sets the libvirt zone if it exists (and attempts to somewhat document the behavior in firewall.html), it doesn't install the libvirt zone. * check for existence of libvirt zone before attempting to set it. * if libvirt zone doesn't exist, only log an error in the case that the firewalld version is new enough to have an nftables backend, and the backend is actually set to nftables. docs/firewall.html.in | 33 +++++++++++++++++++++ src/network/bridge_driver_linux.c | 48 +++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) diff --git a/docs/firewall.html.in b/docs/firewall.html.in index 0a50687c26..5d584e582e 100644 --- a/docs/firewall.html.in +++ b/docs/firewall.html.in @@ -129,6 +129,39 @@ MASQUERADE all -- * * 192.168.122.0/24 !192.168.122.0/24</pre> </li> </ul> + <h3><a id="fw-firewalld-and-virtual-network-driver">firewalld and the virtual network driver</a> + </h3> + <p> + If <a href="https://firewalld.org">firewalld</a> is active on + the host, libvirt will attempt to place the bridge interface of + a libvirt virtual network into the firewalld zone named + "libvirt" (thus making all guest->host traffic on that network + subject to the rules of the "libvirt" zone). This is done + because, if firewalld is using its nftables backend (available + since firewalld 0.6.0) the default firewalld zone (which would + be used if libvirt didn't explicitly set the zone) prevents + forwarding traffic from guests through the bridge, as well as + preventing DHCP, DNS, and most other traffic from guests to + host. The zone named "libvirt" is installed into the firewalld + configuration by libvirt (not by firewalld), and allows + forwarded traffic through the bridge as well as DHCP, DNS, TFTP, + and SSH traffic to the host - depending on firewalld's backend + this will be implemented via either iptables or nftables + rules. libvirt's own rules outlined above will *always* be + iptables rules regardless of which backend is in use by + firewalld. + </p> + <p> + NB: Prior to libvirt 5.1.0, the firewalld "libvirt" zone did not + exist, and prior to firewalld 0.7.0 a feature crucial to making + the "libvirt" zone operate properly (rich rule priority + settings) was not implemented in firewalld. In cases where one + or the other of the two packages is missing the necessary + functionality, it's still possible to have functional guest + networking by setting the firewalld backend to "iptables" (in + firewalld prior to 0.6.0, this was the only backend available). + </p> + <h3><a id="fw-network-filter-driver">The network filter driver</a> </h3> <p>This driver provides a fully configurable network filtering capability diff --git a/src/network/bridge_driver_linux.c b/src/network/bridge_driver_linux.c index e5e48c90f1..9d2e6877ae 100644 --- a/src/network/bridge_driver_linux.c +++ b/src/network/bridge_driver_linux.c @@ -27,6 +27,7 @@ #include "virstring.h" #include "virlog.h" #include "virfirewall.h" +#include "virfirewalld.h" #define VIR_FROM_THIS VIR_FROM_NONE @@ -670,6 +671,53 @@ int networkAddFirewallRules(virNetworkDefPtr def) virFirewallPtr fw = NULL; int ret = -1; + /* if firewalld is active, try to set the "libvirt" zone. This is + * desirable (for consistency) if firewalld is using the iptables + * backend, but is necessary (for basic network connectivity) if + * firewalld is using the nftables backend + */ + if (virFirewallDIsRegistered() == 0) { + + /* if the "libvirt" zone exists, then set it. If not, and + * if firewalld is using the nftables backend, then we + * need to log an error because the combination of + * nftables + default zone means that traffic cannot be + * forwarded (and even DHCP and DNS from guest to host + * will probably no be permitted by the default zone + */ + if (virFirewallDZoneExists("libvirt")) { + if (virFirewallDInterfaceSetZone(def->bridge, "libvirt") < 0) + goto cleanup; + } else { + unsigned long version; + int vresult = virFirewallDGetVersion(&version); + + if (vresult < 0) + goto cleanup; + + /* Support for nftables backend was added in firewalld + * 0.6.0. Support for rule priorities (required by the + * 'libvirt' zone, which should be installed by a + * libvirt package, *not* by firewalld) was not added + * until firewalld 0.7.0 (unless it was backported). + */ + if (version >= 6000 && + virFirewallDGetBackend() == VIR_FIREWALLD_BACKEND_NFTABLES) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("firewalld is set to use the nftables " + "backend, but the required firewalld " + "'libvirt' zone is missing. Either set " + "the firewalld backend to 'iptables', or " + "ensure that firewalld has a 'libvirt' " + "zone by upgrading firewalld to a " + "version supporting rule priorities " + "(0.7.0+) and/or rebuilding " + "libvirt with --with-firewalld-zone")); + goto cleanup; + } + } + } + fw = virFirewallNew(); virFirewallStartTransaction(fw, 0); -- 2.20.1 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list