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