9c6c51
From f89135129d722dca4e5eb7dbcc6845ab757f2e08 Mon Sep 17 00:00:00 2001
9c6c51
Message-Id: <f89135129d722dca4e5eb7dbcc6845ab757f2e08@dist-git>
9c6c51
From: Laine Stump <laine@laine.org>
9c6c51
Date: Fri, 1 Feb 2019 20:29:30 -0500
9c6c51
Subject: [PATCH] configure: selectively install a firewalld 'libvirt' zone
9c6c51
MIME-Version: 1.0
9c6c51
Content-Type: text/plain; charset=UTF-8
9c6c51
Content-Transfer-Encoding: 8bit
9c6c51
9c6c51
In the past (when both libvirt and firewalld used iptables), if either
9c6c51
libvirt's rules *OR* firewalld's rules accepted a packet, it would
9c6c51
be accepted. This was because libvirt and firewalld rules were
9c6c51
processed during the same kernel hook, and a single ACCEPT result
9c6c51
would terminate the rule traversal and cause the packet to be
9c6c51
accepted.
9c6c51
9c6c51
But now firewalld can use nftables for its backend, while libvirt's
9c6c51
firewall rules are still using iptables; iptables rules are still
9c6c51
processed, but at a different time during packet processing
9c6c51
(i.e. during a different hook) than the firewalld nftables rules. The
9c6c51
result is that a packet must be accepted by *BOTH* the libvirt
9c6c51
iptables rules *AND* the firewalld nftable rules in order to be
9c6c51
accepted.
9c6c51
9c6c51
This causes pain because
9c6c51
9c6c51
1) libvirt always adds rules to permit DNS and DHCP (and sometimes
9c6c51
TFTP) from guests to the host network's bridge interface. But
9c6c51
libvirt's bridges are in firewalld's "default" zone (which is usually
9c6c51
the zone called "public"). The public zone allows ssh, but doesn't
9c6c51
allow DNS, DHCP, or TFTP. So even though libvirt's rules allow the
9c6c51
DHCP and DNS traffic, the firewalld rules (now processed during a
9c6c51
different hook) dont, thus guests connected to libvirt's bridges can't
9c6c51
acquire an IP address from DHCP, nor can they make DNS queries to the
9c6c51
DNS server libvirt has setup on the host. (This could be solved by
9c6c51
modifying the default firewalld zone to allow DNS and DHCP, but that
9c6c51
would open *all* interfaces in the default zone to those services,
9c6c51
which is most likely not what the host's admin wants.)
9c6c51
9c6c51
2) Even though libvirt adds iptables rules to allow forwarded traffic
9c6c51
to pass the iptables hook, firewalld's higher level "rich rules" don't
9c6c51
yet have the ability to configure the acceptance of forwarded traffic
9c6c51
(traffic that is going somewhere beyond the host), so any traffic that
9c6c51
needs to be forwarded from guests to the network beyond the host is
9c6c51
rejected during the nftables hook by the default zone's "default
9c6c51
reject" policy (which rejects all traffic in the zone not specifically
9c6c51
allowed by the rules in the zone, whether that traffic is destined to
9c6c51
be forwarded or locally received by the host).
9c6c51
9c6c51
libvirt can't send "direct" nftables rules (firewalld only supports
9c6c51
direct/passthrough rules for iptables), so we can't solve this problem
9c6c51
by just sending explicit nftables rules instead of explicit iptables
9c6c51
rules (which, if it could be done, would place libvirt's rules in the
9c6c51
same hook as firewalld's native rules, and thus eliminate the need for
9c6c51
packets to be accepted by both libvirt's and firewalld's own rules).
9c6c51
9c6c51
However, we can take advantage of a quirk in firewalld zones that have
9c6c51
a default policy of "accept" (meaning any packet that doesn't match a
9c6c51
specific rule in the zone will be *accepted*) - this default accept will
9c6c51
also accept forwarded traffic (not just traffic destined for the host).
9c6c51
9c6c51
Of course we don't want to modify firewalld's default zone in that
9c6c51
way, because that would affect the filtering of traffic coming into
9c6c51
the host from other interfaces using that zone. Instead, we will
9c6c51
create a new zone called "libvirt". The libvirt zone will have a
9c6c51
default policy of accept so that forwarded traffic can pass and list
9c6c51
specific services that will be allowed into the host from guests (DNS,
9c6c51
DHCP, SSH, and TFTP).
9c6c51
9c6c51
But the same default accept policy that fixes forwarded traffic also
9c6c51
causes *all* traffic from guest to host to be accepted. To close this
9c6c51
new hole, the libvirt zone can take advantage of a new feature in
9c6c51
firewalld (currently slated for firewalld-0.7.0) - priorities for rich
9c6c51
rules - to add a low priority rule that rejects all local traffic (but
9c6c51
leaves alone all forwarded traffic).
9c6c51
9c6c51
So, our new zone will start with a list of services that are allowed
9c6c51
(dhcp, dns, tftp, and ssh to start, but configurable via any firewalld
9c6c51
management application, or direct editing of the zone file in
9c6c51
/etc/firewalld/zones/libvirt.xml), followed by a low priority
9c6c51
<reject/> rule (to reject all other traffic from guest to host), and
9c6c51
finally with a default policy of accept (to allow forwarded traffic).
9c6c51
9c6c51
This patch only creates the zonefile for the new zone, and implements
9c6c51
a configure.ac option to selectively enable/disable installation of
9c6c51
the new zone. A separate patch contains the necessary code to actually
9c6c51
place bridge interfaces in the libvirt zone.
9c6c51
9c6c51
Why do we need a configure option to disable installation of the new
9c6c51
libvirt zone? It uses a new firewalld attribute that sets the priority
9c6c51
of a rich rule; this feature first appears in firewalld-0.7.0 (unless
9c6c51
it has been backported to am earlier firewalld by a downstream
9c6c51
maintainer). If the file were installed on a system with firewalld
9c6c51
that didn't support rule priorities, firewalld would log an error
9c6c51
every time it restarted, causing confusion and lots of extra bug
9c6c51
reports.
9c6c51
9c6c51
So we add two new configure.ac switches to avoid polluting the system
9c6c51
logs with this error on systems that don't support rule priorities -
9c6c51
"--with-firewalld-zone" and "--without-firewalld-zone". A package
9c6c51
builder can use these to include/exclude the libvirt zone file in the
9c6c51
installation. If firewalld is enabled (--with-firewalld), the default
9c6c51
is --with-firewalld-zone, but it can be disabled during configure
9c6c51
(using --without-firewalld-zone). Targets that are using a firewalld
9c6c51
version too old to support the rule priority setting in the libvirt
9c6c51
zone file can simply add --without-firewalld-zone to their configure
9c6c51
commandline.
9c6c51
9c6c51
These switches only affect whether or not the libvirt zone file is
9c6c51
*installed* in /usr/lib/firewalld/zones, but have no effect on whether
9c6c51
or not libvirt looks for a zone called libvirt and tries to use it.
9c6c51
9c6c51
NB: firewalld zones can only be added to the permanent config of
9c6c51
firewalld, and won't be loaded/enabled until firewalld is restarted,
9c6c51
so at package install/upgrade time we have to restart firewalld. For
9c6c51
rpm-based distros, this is done in the libvirt.spec file by calling
9c6c51
the %firewalld_restart rpm macro, which is a part of the
9c6c51
firewalld-filesystem package. (For distros that don't use rpm
9c6c51
packages, the command "firewalld-cmd --reload" will have the same
9c6c51
effect).
9c6c51
9c6c51
Signed-off-by: Laine Stump <laine@laine.org>
9c6c51
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
9c6c51
(cherry picked from commit 3b71f2e42dc6c5453d09136578bfb868874da088)
9c6c51
9c6c51
https://bugzilla.redhat.com/1650320
9c6c51
9c6c51
Reviewed-by: Ján Tomko <jtomko@redhat.com>
9c6c51
---
9c6c51
 configure.ac                |  3 +++
9c6c51
 libvirt.spec.in             | 31 +++++++++++++++++++++++++
9c6c51
 m4/virt-firewalld-zone.m4   | 45 +++++++++++++++++++++++++++++++++++++
9c6c51
 src/network/Makefile.inc.am | 10 ++++++++-
9c6c51
 src/network/libvirt.zone    | 23 +++++++++++++++++++
9c6c51
 5 files changed, 111 insertions(+), 1 deletion(-)
9c6c51
 create mode 100644 m4/virt-firewalld-zone.m4
9c6c51
 create mode 100644 src/network/libvirt.zone
9c6c51
9c6c51
diff --git a/configure.ac b/configure.ac
9c6c51
index e25bf0a6ec..3da26484d0 100644
9c6c51
--- a/configure.ac
9c6c51
+++ b/configure.ac
9c6c51
@@ -247,6 +247,7 @@ LIBVIRT_ARG_CAPNG
9c6c51
 LIBVIRT_ARG_CURL
9c6c51
 LIBVIRT_ARG_DBUS
9c6c51
 LIBVIRT_ARG_FIREWALLD
9c6c51
+LIBVIRT_ARG_FIREWALLD_ZONE
9c6c51
 LIBVIRT_ARG_FUSE
9c6c51
 LIBVIRT_ARG_GLUSTER
9c6c51
 LIBVIRT_ARG_HAL
9c6c51
@@ -286,6 +287,7 @@ LIBVIRT_CHECK_DBUS
9c6c51
 LIBVIRT_CHECK_DEVMAPPER
9c6c51
 LIBVIRT_CHECK_DLOPEN
9c6c51
 LIBVIRT_CHECK_FIREWALLD
9c6c51
+LIBVIRT_CHECK_FIREWALLD_ZONE
9c6c51
 LIBVIRT_CHECK_FUSE
9c6c51
 LIBVIRT_CHECK_GLUSTER
9c6c51
 LIBVIRT_CHECK_GNUTLS
9c6c51
@@ -959,6 +961,7 @@ LIBVIRT_RESULT_CURL
9c6c51
 LIBVIRT_RESULT_DBUS
9c6c51
 LIBVIRT_RESULT_DLOPEN
9c6c51
 LIBVIRT_RESULT_FIREWALLD
9c6c51
+LIBVIRT_RESULT_FIREWALLD_ZONE
9c6c51
 LIBVIRT_RESULT_FUSE
9c6c51
 LIBVIRT_RESULT_GLUSTER
9c6c51
 LIBVIRT_RESULT_GNUTLS
9c6c51
diff --git a/m4/virt-firewalld-zone.m4 b/m4/virt-firewalld-zone.m4
9c6c51
new file mode 100644
9c6c51
index 0000000000..b67d1a0b2f
9c6c51
--- /dev/null
9c6c51
+++ b/m4/virt-firewalld-zone.m4
9c6c51
@@ -0,0 +1,45 @@
9c6c51
+dnl firewalld_zone check - whether or not to install the firewall "libvirt" zone
9c6c51
+dnl
9c6c51
+dnl Copyright (C) 2019 Red Hat, Inc.
9c6c51
+dnl
9c6c51
+dnl This library is free software; you can redistribute it and/or
9c6c51
+dnl modify it under the terms of the GNU Lesser General Public
9c6c51
+dnl License as published by the Free Software Foundation; either
9c6c51
+dnl version 2.1 of the License, or (at your option) any later version.
9c6c51
+dnl
9c6c51
+dnl This library is distributed in the hope that it will be useful,
9c6c51
+dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
9c6c51
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
9c6c51
+dnl Lesser General Public License for more details.
9c6c51
+dnl
9c6c51
+dnl You should have received a copy of the GNU Lesser General Public
9c6c51
+dnl License along with this library.  If not, see
9c6c51
+dnl <http://www.gnu.org/licenses/>.
9c6c51
+dnl
9c6c51
+
9c6c51
+AC_DEFUN([LIBVIRT_ARG_FIREWALLD_ZONE], [
9c6c51
+  LIBVIRT_ARG_WITH([FIREWALLD_ZONE], [Whether to install firewalld libvirt zone], [check])
9c6c51
+])
9c6c51
+
9c6c51
+AC_DEFUN([LIBVIRT_CHECK_FIREWALLD_ZONE], [
9c6c51
+  AC_REQUIRE([LIBVIRT_CHECK_FIREWALLD])
9c6c51
+  AC_MSG_CHECKING([for whether to install firewalld libvirt zone])
9c6c51
+
9c6c51
+  if test "x$with_firewalld_zone" = "xcheck" ; then
9c6c51
+    with_firewalld_zone=$with_firewalld
9c6c51
+  fi
9c6c51
+
9c6c51
+  if test "x$with_firewalld_zone" = "xyes" ; then
9c6c51
+    if test "x$with_firewalld" != "xyes" ; then
9c6c51
+      AC_MSG_ERROR([You must have firewalld support enabled to enable firewalld-zone])
9c6c51
+    fi
9c6c51
+    AC_DEFINE_UNQUOTED([WITH_FIREWALLD_ZONE], [1], [whether firewalld libvirt zone is installed])
9c6c51
+  fi
9c6c51
+
9c6c51
+  AM_CONDITIONAL([WITH_FIREWALLD_ZONE], [test "x$with_firewalld_zone" != "xno"])
9c6c51
+  AC_MSG_RESULT($with_firewalld_zone)
9c6c51
+])
9c6c51
+
9c6c51
+AC_DEFUN([LIBVIRT_RESULT_FIREWALLD_ZONE], [
9c6c51
+  LIBVIRT_RESULT([firewalld-zone], [$with_firewalld_zone])
9c6c51
+])
9c6c51
diff --git a/src/network/Makefile.inc.am b/src/network/Makefile.inc.am
9c6c51
index 508c8c0422..cbaaa7ea68 100644
9c6c51
--- a/src/network/Makefile.inc.am
9c6c51
+++ b/src/network/Makefile.inc.am
9c6c51
@@ -87,6 +87,11 @@ install-data-network:
9c6c51
 	( cd $(DESTDIR)$(confdir)/qemu/networks/autostart && \
9c6c51
 	  rm -f default.xml && \
9c6c51
 	  $(LN_S) ../default.xml default.xml )
9c6c51
+if WITH_FIREWALLD_ZONE
9c6c51
+	$(MKDIR_P) "$(DESTDIR)$(prefix)/lib/firewalld/zones"
9c6c51
+	$(INSTALL_DATA) $(srcdir)/network/libvirt.zone \
9c6c51
+	  $(DESTDIR)$(prefix)/lib/firewalld/zones/libvirt.xml
9c6c51
+endif WITH_FIREWALLD_ZONE
9c6c51
 
9c6c51
 uninstall-data-network:
9c6c51
 	rm -f $(DESTDIR)$(confdir)/qemu/networks/autostart/default.xml
9c6c51
@@ -95,10 +100,13 @@ uninstall-data-network:
9c6c51
 	rmdir "$(DESTDIR)$(confdir)/qemu/networks" || :
9c6c51
 	rmdir "$(DESTDIR)$(localstatedir)/lib/libvirt/network" ||:
9c6c51
 	rmdir "$(DESTDIR)$(localstatedir)/run/libvirt/network" ||:
9c6c51
+if WITH_FIREWALLD_ZONE
9c6c51
+	rm -f  $(DESTDIR)$(prefix)/lib/firewalld/zones/libvirt.xml
9c6c51
+endif WITH_FIREWALLD_ZONE
9c6c51
 
9c6c51
 endif WITH_NETWORK
9c6c51
 
9c6c51
-EXTRA_DIST += network/default.xml
9c6c51
+EXTRA_DIST += network/default.xml network/libvirt.zone
9c6c51
 
9c6c51
 .PHONY: \
9c6c51
 	install-data-network \
9c6c51
diff --git a/src/network/libvirt.zone b/src/network/libvirt.zone
9c6c51
new file mode 100644
9c6c51
index 0000000000..bf81db1b6e
9c6c51
--- /dev/null
9c6c51
+++ b/src/network/libvirt.zone
9c6c51
@@ -0,0 +1,23 @@
9c6c51
+
9c6c51
+<zone target="ACCEPT">
9c6c51
+  <short>libvirt</short>
9c6c51
+
9c6c51
+  <description>
9c6c51
+    The default policy of "ACCEPT" allows all packets to/from
9c6c51
+    interfaces in the zone to be forwarded, while the (*low priority*)
9c6c51
+    reject rule blocks any traffic destined for the host, except those
9c6c51
+    services explicitly listed (that list can be modified as required
9c6c51
+    by the local admin). This zone is intended to be used only by
9c6c51
+    libvirt virtual networks - libvirt will add the bridge devices for
9c6c51
+    all new virtual networks to this zone by default.
9c6c51
+  </description>
9c6c51
+
9c6c51
+<rule priority='32767'>
9c6c51
+  <reject/>
9c6c51
+</rule>
9c6c51
+<service name='dhcp'/>
9c6c51
+<service name='dhcpv6'/>
9c6c51
+<service name='dns'/>
9c6c51
+<service name='ssh'/>
9c6c51
+<service name='tftp'/>
9c6c51
+</zone>
9c6c51
-- 
9c6c51
2.20.1
9c6c51