[libvirt] [PATCH 0/5] network: fix networking for firewalld+nftables

The detailed explanation of this is in Patch 4/5. Basically, when firewalld enables their new nftables backend, libvirt virtual networks lose all ability to forward packets from guests out to the physical network, and can only communicate with the host itself as much as firewalld's "public" zone will allow (which isn't much, and doesn't include DHCP or DNS). Laine Stump (5): docs: add forgotten mentions of forward mode "open" util: move all firewalld-specific stuff into its own file util: new function virFirewallDInterfaceSetZone() network: regain guest network connectivity after firewalld switch to nftables network: allow configuring firewalld zone for virtual network bridge device docs/formatnetwork.html.in | 21 ++- docs/news.xml | 40 ++++++ docs/schemas/basictypes.rng | 6 + docs/schemas/network.rng | 6 + include/libvirt/virterror.h | 1 + libvirt.spec.in | 16 +++ src/conf/network_conf.c | 14 +- src/conf/network_conf.h | 1 + src/libvirt_private.syms | 4 + src/network/Makefile.inc.am | 10 +- src/network/bridge_driver_linux.c | 25 ++++ src/network/libvirt.zone | 14 ++ src/util/Makefile.inc.am | 2 + src/util/virerror.c | 1 + src/util/virfirewall.c | 86 +----------- src/util/virfirewalld.c | 151 +++++++++++++++++++++ src/util/virfirewalld.h | 36 +++++ src/util/virfirewallpriv.h | 2 - tests/networkxml2xmlin/routed-network.xml | 2 +- tests/networkxml2xmlout/routed-network.xml | 2 +- tests/virfirewalltest.c | 1 + 21 files changed, 350 insertions(+), 91 deletions(-) create mode 100644 src/network/libvirt.zone create mode 100644 src/util/virfirewalld.c create mode 100644 src/util/virfirewalld.h -- 2.20.1

A couple places in the docs didn't get updated when the forward mode "open" was added. Signed-off-by: Laine Stump <laine@laine.org> --- docs/formatnetwork.html.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/formatnetwork.html.in b/docs/formatnetwork.html.in index 363a72bbc9..156cfae4ec 100644 --- a/docs/formatnetwork.html.in +++ b/docs/formatnetwork.html.in @@ -107,13 +107,13 @@ may also be connected to the LAN. When defining a new network with a <code><forward></code> mode of - "nat" or "route" (or an isolated network with + "nat", "route", or "open" (or an isolated network with no <code><forward></code> element), libvirt will automatically generate a unique name for the bridge device if none is given, and this name will be permanently stored in the network configuration so that that the same name will be used every time the network is started. For these types of networks - (nat, routed, and isolated), a bridge name beginning with the + (nat, route, open, and isolated), a bridge name beginning with the prefix "virbr" is recommended (and that is what is auto-generated), but not enforced. Attribute <code>stp</code> specifies if Spanning Tree Protocol -- 2.20.1

On Wed, Jan 09, 2019 at 09:57:33PM -0500, Laine Stump wrote:
A couple places in the docs didn't get updated when the forward mode "open" was added.
Signed-off-by: Laine Stump <laine@laine.org> --- docs/formatnetwork.html.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/formatnetwork.html.in b/docs/formatnetwork.html.in index 363a72bbc9..156cfae4ec 100644 --- a/docs/formatnetwork.html.in +++ b/docs/formatnetwork.html.in @@ -107,13 +107,13 @@ may also be connected to the LAN. When defining a new network with a <code><forward></code> mode of
- "nat" or "route" (or an isolated network with + "nat", "route", or "open" (or an isolated network with no <code><forward></code> element), libvirt will automatically generate a unique name for the bridge device if none is given, and this name will be permanently stored in the network configuration so that that the same name will be used every time the network is started. For these types of networks - (nat, routed, and isolated), a bridge name beginning with the + (nat, route, open, and isolated), a bridge name beginning with the prefix "virbr" is recommended (and that is what is auto-generated), but not enforced. Attribute <code>stp</code> specifies if Spanning Tree Protocol
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

Since I'm going to be adding at least one more firewalld-specific function, this seems like a good time to separate the code that's unique to firewalld from the more-generic "firewall" file. Signed-off-by: Laine Stump <laine@laine.org> --- include/libvirt/virterror.h | 1 + src/libvirt_private.syms | 3 + src/util/Makefile.inc.am | 2 + src/util/virerror.c | 1 + src/util/virfirewall.c | 86 ++---------------------- src/util/virfirewalld.c | 128 ++++++++++++++++++++++++++++++++++++ src/util/virfirewalld.h | 33 ++++++++++ src/util/virfirewallpriv.h | 2 - tests/virfirewalltest.c | 1 + 9 files changed, 173 insertions(+), 84 deletions(-) create mode 100644 src/util/virfirewalld.c create mode 100644 src/util/virfirewalld.h diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index fbbe2d5624..3c19ff5e2e 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -131,6 +131,7 @@ typedef enum { VIR_FROM_PERF = 65, /* Error from perf */ VIR_FROM_LIBSSH = 66, /* Error from libssh connection transport */ VIR_FROM_RESCTRL = 67, /* Error from resource control */ + VIR_FROM_FIREWALLD = 68, /* Error from firewalld */ # ifdef VIR_ENUM_SENTINELS VIR_ERR_DOMAIN_LAST diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index c3d6306809..583868f422 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1918,6 +1918,9 @@ virFirewallSetLockOverride; virFirewallStartRollback; virFirewallStartTransaction; +# util/virfirewalld.h +virFirewallDApplyRule; +virFirewallDStatus; # util/virfirmware.h virFirmwareFreeList; diff --git a/src/util/Makefile.inc.am b/src/util/Makefile.inc.am index 4295babac3..0295a1c7d0 100644 --- a/src/util/Makefile.inc.am +++ b/src/util/Makefile.inc.am @@ -64,6 +64,8 @@ UTIL_SOURCES = \ util/virfirewall.c \ util/virfirewall.h \ util/virfirewallpriv.h \ + util/virfirewalld.c \ + util/virfirewalld.h \ util/virfirmware.c \ util/virfirmware.h \ util/virgettext.c \ diff --git a/src/util/virerror.c b/src/util/virerror.c index 61b47d2be0..ae1efa72d8 100644 --- a/src/util/virerror.c +++ b/src/util/virerror.c @@ -138,6 +138,7 @@ VIR_ENUM_IMPL(virErrorDomain, VIR_ERR_DOMAIN_LAST, "Perf", /* 65 */ "Libssh transport layer", "Resource control", + "FirewallD", ) diff --git a/src/util/virfirewall.c b/src/util/virfirewall.c index 5a0cf95a44..d5d647fcc4 100644 --- a/src/util/virfirewall.c +++ b/src/util/virfirewall.c @@ -24,12 +24,12 @@ #define LIBVIRT_VIRFIREWALLPRIV_H_ALLOW #include "virfirewallpriv.h" +#include "virfirewalld.h" #include "virerror.h" #include "virutil.h" #include "virstring.h" #include "vircommand.h" #include "virlog.h" -#include "virdbus.h" #include "virfile.h" #include "virthread.h" @@ -46,11 +46,6 @@ VIR_ENUM_IMPL(virFirewallLayerCommand, VIR_FIREWALL_LAYER_LAST, IPTABLES_PATH, IP6TABLES_PATH); -VIR_ENUM_DECL(virFirewallLayerFirewallD) -VIR_ENUM_IMPL(virFirewallLayerFirewallD, VIR_FIREWALL_LAYER_LAST, - "eb", "ipv4", "ipv6") - - struct _virFirewallRule { virFirewallLayer layer; @@ -152,7 +147,7 @@ virFirewallValidateBackend(virFirewallBackend backend) VIR_DEBUG("Validating backend %d", backend); if (backend == VIR_FIREWALL_BACKEND_AUTOMATIC || backend == VIR_FIREWALL_BACKEND_FIREWALLD) { - int rv = virDBusIsServiceRegistered(VIR_FIREWALL_FIREWALLD_SERVICE); + int rv = virFirewallDStatus(); VIR_DEBUG("Firewalld is registered ? %d", rv); if (rv < 0) { @@ -712,81 +707,8 @@ virFirewallApplyRuleFirewallD(virFirewallRulePtr rule, bool ignoreErrors, char **output) { - const char *ipv = virFirewallLayerFirewallDTypeToString(rule->layer); - DBusConnection *sysbus = virDBusGetSystemBus(); - DBusMessage *reply = NULL; - virError error; - int ret = -1; - - if (!sysbus) - return -1; - - memset(&error, 0, sizeof(error)); - - if (!ipv) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Unknown firewall layer %d"), - rule->layer); - goto cleanup; - } - - if (virDBusCallMethod(sysbus, - &reply, - &error, - VIR_FIREWALL_FIREWALLD_SERVICE, - "/org/fedoraproject/FirewallD1", - "org.fedoraproject.FirewallD1.direct", - "passthrough", - "sa&s", - ipv, - (int)rule->argsLen, - rule->args) < 0) - goto cleanup; - - if (error.level == VIR_ERR_ERROR) { - /* - * As of firewalld-0.3.9.3-1.fc20.noarch the name and - * message fields in the error look like - * - * name="org.freedesktop.DBus.Python.dbus.exceptions.DBusException" - * message="COMMAND_FAILED: '/sbin/iptables --table filter --delete - * INPUT --in-interface virbr0 --protocol udp --destination-port 53 - * --jump ACCEPT' failed: iptables: Bad rule (does a matching rule - * exist in that chain?)." - * - * We'd like to only ignore DBus errors precisely related to the failure - * of iptables/ebtables commands. A well designed DBus interface would - * return specific named exceptions not the top level generic python dbus - * exception name. With this current scheme our only option is todo a - * sub-string match for 'COMMAND_FAILED' on the message. eg like - * - * if (ignoreErrors && - * STREQ(error.name, - * "org.freedesktop.DBus.Python.dbus.exceptions.DBusException") && - * STRPREFIX(error.message, "COMMAND_FAILED")) - * ... - * - * But this risks our error detecting code being broken if firewalld changes - * ever alter the message string, so we're avoiding doing that. - */ - if (ignoreErrors) { - VIR_DEBUG("Ignoring error '%s': '%s'", - error.str1, error.message); - } else { - virReportErrorObject(&error); - goto cleanup; - } - } else { - if (virDBusMessageRead(reply, "s", output) < 0) - goto cleanup; - } - - ret = 0; - - cleanup: - virResetError(&error); - virDBusMessageUnref(reply); - return ret; + /* wrapper necessary because virFirewallRule is a private struct */ + return virFirewallDApplyRule(rule->layer, rule->args, rule->argsLen, ignoreErrors, output); } static int diff --git a/src/util/virfirewalld.c b/src/util/virfirewalld.c new file mode 100644 index 0000000000..0dc2b3de08 --- /dev/null +++ b/src/util/virfirewalld.c @@ -0,0 +1,128 @@ +/* + * virfirewalld.c: support for firewalld (https://firewalld.org) + * + * Copyright (C) 2019 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <stdarg.h> + +#include "virfirewall.h" +#include "virfirewalld.h" +#include "virerror.h" +#include "virutil.h" +#include "virlog.h" +#include "virdbus.h" + +#define VIR_FROM_THIS VIR_FROM_FIREWALLD + +VIR_LOG_INIT("util.firewalld"); + +VIR_ENUM_DECL(virFirewallLayerFirewallD) +VIR_ENUM_IMPL(virFirewallLayerFirewallD, VIR_FIREWALL_LAYER_LAST, + "eb", "ipv4", "ipv6") + +int +virFirewallDStatus(void) +{ + return virDBusIsServiceRegistered(VIR_FIREWALL_FIREWALLD_SERVICE); +} + + +int +virFirewallDApplyRule(virFirewallLayer layer, + char **args, size_t argsLen, + bool ignoreErrors, + char **output) +{ + const char *ipv = virFirewallLayerFirewallDTypeToString(layer); + DBusConnection *sysbus = virDBusGetSystemBus(); + DBusMessage *reply = NULL; + virError error; + int ret = -1; + + if (!sysbus) + return -1; + + memset(&error, 0, sizeof(error)); + + if (!ipv) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unknown firewall layer %d"), + layer); + goto cleanup; + } + + if (virDBusCallMethod(sysbus, + &reply, + &error, + VIR_FIREWALL_FIREWALLD_SERVICE, + "/org/fedoraproject/FirewallD1", + "org.fedoraproject.FirewallD1.direct", + "passthrough", + "sa&s", + ipv, + (int)argsLen, + args) < 0) + goto cleanup; + + if (error.level == VIR_ERR_ERROR) { + /* + * As of firewalld-0.3.9.3-1.fc20.noarch the name and + * message fields in the error look like + * + * name="org.freedesktop.DBus.Python.dbus.exceptions.DBusException" + * message="COMMAND_FAILED: '/sbin/iptables --table filter --delete + * INPUT --in-interface virbr0 --protocol udp --destination-port 53 + * --jump ACCEPT' failed: iptables: Bad rule (does a matching rule + * exist in that chain?)." + * + * We'd like to only ignore DBus errors precisely related to the failure + * of iptables/ebtables commands. A well designed DBus interface would + * return specific named exceptions not the top level generic python dbus + * exception name. With this current scheme our only option is todo a + * sub-string match for 'COMMAND_FAILED' on the message. eg like + * + * if (ignoreErrors && + * STREQ(error.name, + * "org.freedesktop.DBus.Python.dbus.exceptions.DBusException") && + * STRPREFIX(error.message, "COMMAND_FAILED")) + * ... + * + * But this risks our error detecting code being broken if firewalld changes + * ever alter the message string, so we're avoiding doing that. + */ + if (ignoreErrors) { + VIR_DEBUG("Ignoring error '%s': '%s'", + error.str1, error.message); + } else { + virReportErrorObject(&error); + goto cleanup; + } + } else { + if (virDBusMessageRead(reply, "s", output) < 0) + goto cleanup; + } + + ret = 0; + + cleanup: + virResetError(&error); + virDBusMessageUnref(reply); + return ret; +} diff --git a/src/util/virfirewalld.h b/src/util/virfirewalld.h new file mode 100644 index 0000000000..c1c929399a --- /dev/null +++ b/src/util/virfirewalld.h @@ -0,0 +1,33 @@ +/* + * virfirewalld.h: support for firewalld (https://firewalld.org) + * + * Copyright (C) 2019 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ + +#ifndef LIBVIRT_VIRFIREWALLD_H +# define LIBVIRT_VIRFIREWALLD_H + +# define VIR_FIREWALL_FIREWALLD_SERVICE "org.fedoraproject.FirewallD1" + +int virFirewallDStatus(void); + +int virFirewallDApplyRule(virFirewallLayer layer, + char **args, size_t argsLen, + bool ignoreErrors, + char **output); + +#endif /* LIBVIRT_VIRFIREWALLD_H */ diff --git a/src/util/virfirewallpriv.h b/src/util/virfirewallpriv.h index efa94a7da4..7c31d0680d 100644 --- a/src/util/virfirewallpriv.h +++ b/src/util/virfirewallpriv.h @@ -27,8 +27,6 @@ # include "virfirewall.h" -# define VIR_FIREWALL_FIREWALLD_SERVICE "org.fedoraproject.FirewallD1" - typedef enum { VIR_FIREWALL_BACKEND_AUTOMATIC, VIR_FIREWALL_BACKEND_DIRECT, diff --git a/tests/virfirewalltest.c b/tests/virfirewalltest.c index 63b9ced820..573ab1f9cd 100644 --- a/tests/virfirewalltest.c +++ b/tests/virfirewalltest.c @@ -27,6 +27,7 @@ # include "vircommandpriv.h" # define LIBVIRT_VIRFIREWALLPRIV_H_ALLOW # include "virfirewallpriv.h" +# include "virfirewalld.h" # include "virmock.h" # define LIBVIRT_VIRDBUSPRIV_H_ALLOW # include "virdbuspriv.h" -- 2.20.1

On Wed, Jan 09, 2019 at 09:57:34PM -0500, Laine Stump wrote:
Since I'm going to be adding at least one more firewalld-specific function, this seems like a good time to separate the code that's unique to firewalld from the more-generic "firewall" file.
Signed-off-by: Laine Stump <laine@laine.org> --- include/libvirt/virterror.h | 1 + src/libvirt_private.syms | 3 + src/util/Makefile.inc.am | 2 + src/util/virerror.c | 1 + src/util/virfirewall.c | 86 ++---------------------- src/util/virfirewalld.c | 128 ++++++++++++++++++++++++++++++++++++ src/util/virfirewalld.h | 33 ++++++++++ src/util/virfirewallpriv.h | 2 - tests/virfirewalltest.c | 1 + 9 files changed, 173 insertions(+), 84 deletions(-) create mode 100644 src/util/virfirewalld.c create mode 100644 src/util/virfirewalld.h
+ if (error.level == VIR_ERR_ERROR) { + /* + * As of firewalld-0.3.9.3-1.fc20.noarch the name and + * message fields in the error look like + * + * name="org.freedesktop.DBus.Python.dbus.exceptions.DBusException" + * message="COMMAND_FAILED: '/sbin/iptables --table filter --delete + * INPUT --in-interface virbr0 --protocol udp --destination-port 53 + * --jump ACCEPT' failed: iptables: Bad rule (does a matching rule + * exist in that chain?)." + * + * We'd like to only ignore DBus errors precisely related to the failure + * of iptables/ebtables commands. A well designed DBus interface would + * return specific named exceptions not the top level generic python dbus + * exception name. With this current scheme our only option is todo a + * sub-string match for 'COMMAND_FAILED' on the message. eg like + * + * if (ignoreErrors && + * STREQ(error.name, + * "org.freedesktop.DBus.Python.dbus.exceptions.DBusException") && + * STRPREFIX(error.message, "COMMAND_FAILED")) + * ... + * + * But this risks our error detecting code being broken if firewalld changes + * ever alter the message string, so we're avoiding doing that. + */
We really ought to ask the firewalld guys to improve this since we have good communication with them now :-)
+ if (ignoreErrors) { + VIR_DEBUG("Ignoring error '%s': '%s'", + error.str1, error.message); + } else { + virReportErrorObject(&error); + goto cleanup; + } + } else { + if (virDBusMessageRead(reply, "s", output) < 0) + goto cleanup; + } + + ret = 0; + + cleanup: + virResetError(&error); + virDBusMessageUnref(reply); + return ret; +}
diff --git a/src/util/virfirewalld.h b/src/util/virfirewalld.h new file mode 100644 index 0000000000..c1c929399a --- /dev/null +++ b/src/util/virfirewalld.h @@ -0,0 +1,33 @@
+#ifndef LIBVIRT_VIRFIREWALLD_H +# define LIBVIRT_VIRFIREWALLD_H + +# define VIR_FIREWALL_FIREWALLD_SERVICE "org.fedoraproject.FirewallD1"
This is only needed for tests so should go in a priv.h header as it was before.
+ +int virFirewallDStatus(void);
Perhaps virFirewallDIsRegistered() ? Could you put API docs for this since it has an unusal tri-state return value.
+ +int virFirewallDApplyRule(virFirewallLayer layer, + char **args, size_t argsLen, + bool ignoreErrors, + char **output);
Would be nice to have API docs for this too
+ +#endif /* LIBVIRT_VIRFIREWALLD_H */ diff --git a/src/util/virfirewallpriv.h b/src/util/virfirewallpriv.h index efa94a7da4..7c31d0680d 100644 --- a/src/util/virfirewallpriv.h +++ b/src/util/virfirewallpriv.h @@ -27,8 +27,6 @@
# include "virfirewall.h"
-# define VIR_FIREWALL_FIREWALLD_SERVICE "org.fedoraproject.FirewallD1" - typedef enum { VIR_FIREWALL_BACKEND_AUTOMATIC, VIR_FIREWALL_BACKEND_DIRECT, diff --git a/tests/virfirewalltest.c b/tests/virfirewalltest.c index 63b9ced820..573ab1f9cd 100644 --- a/tests/virfirewalltest.c +++ b/tests/virfirewalltest.c @@ -27,6 +27,7 @@ # include "vircommandpriv.h" # define LIBVIRT_VIRFIREWALLPRIV_H_ALLOW # include "virfirewallpriv.h" +# include "virfirewalld.h"
Should be virfirewalldpriv.h Assuming those small changes are made Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

On 1/9/19 9:57 PM, Laine Stump wrote:
Since I'm going to be adding at least one more firewalld-specific function, this seems like a good time to separate the code that's unique to firewalld from the more-generic "firewall" file.
Signed-off-by: Laine Stump <laine@laine.org> --- include/libvirt/virterror.h | 1 + src/libvirt_private.syms | 3 + src/util/Makefile.inc.am | 2 + src/util/virerror.c | 1 + src/util/virfirewall.c | 86 ++---------------------- src/util/virfirewalld.c | 128 ++++++++++++++++++++++++++++++++++++ src/util/virfirewalld.h | 33 ++++++++++ src/util/virfirewallpriv.h | 2 - tests/virfirewalltest.c | 1 + 9 files changed, 173 insertions(+), 84 deletions(-) create mode 100644 src/util/virfirewalld.c create mode 100644 src/util/virfirewalld.h
[...] I was also looking as a learning opportunity, but I see Dan is reviewing as well... Still I'll point out what I have in any case. oddly enough I had similar thoughts regarding that moved hunk with the (error.level == VIR_ERR_ERROR) checking...
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index c3d6306809..583868f422 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1918,6 +1918,9 @@ virFirewallSetLockOverride; virFirewallStartRollback; virFirewallStartTransaction;
+# util/virfirewalld.h +virFirewallDApplyRule; +virFirewallDStatus;
Just a syntax/spacing NIT here about 2 blank lines seems to be the norm between groupings. So need one before and one after.
# util/virfirmware.h virFirmwareFreeList;
[...] John

Sets the firewalld zone of the given interface. This function assumes that you've already called virFirewallDIsActive(), and relies on virDBusCallMethod's standard error reporting to log any errors. Signed-off-by: Laine Stump <laine@laine.org> --- src/libvirt_private.syms | 1 + src/util/virfirewalld.c | 23 +++++++++++++++++++++++ src/util/virfirewalld.h | 3 +++ 3 files changed, 27 insertions(+) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 583868f422..346e17f535 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1920,6 +1920,7 @@ virFirewallStartTransaction; # util/virfirewalld.h virFirewallDApplyRule; +virFirewallDInterfaceSetZone; virFirewallDStatus; # util/virfirmware.h diff --git a/src/util/virfirewalld.c b/src/util/virfirewalld.c index 0dc2b3de08..7c5b37a5b2 100644 --- a/src/util/virfirewalld.c +++ b/src/util/virfirewalld.c @@ -126,3 +126,26 @@ virFirewallDApplyRule(virFirewallLayer layer, virDBusMessageUnref(reply); return ret; } + + +int +virFirewallDInterfaceSetZone(const char *iface, + const char *zone) +{ + DBusConnection *sysbus = virDBusGetSystemBus(); + DBusMessage *reply = NULL; + + if (!sysbus) + return -1; + + return virDBusCallMethod(sysbus, + &reply, + NULL, + VIR_FIREWALL_FIREWALLD_SERVICE, + "/org/fedoraproject/FirewallD1", + "org.fedoraproject.FirewallD1.zone", + "changeZoneOfInterface", + "ss", + zone, + iface); +} diff --git a/src/util/virfirewalld.h b/src/util/virfirewalld.h index c1c929399a..471176d652 100644 --- a/src/util/virfirewalld.h +++ b/src/util/virfirewalld.h @@ -30,4 +30,7 @@ int virFirewallDApplyRule(virFirewallLayer layer, bool ignoreErrors, char **output); +int virFirewallDInterfaceSetZone(const char *iface, + const char *zone); + #endif /* LIBVIRT_VIRFIREWALLD_H */ -- 2.20.1

On Wed, Jan 09, 2019 at 09:57:35PM -0500, Laine Stump wrote:
Sets the firewalld zone of the given interface. This function assumes that you've already called virFirewallDIsActive(), and relies on virDBusCallMethod's standard error reporting to log any errors.
virFirewallDIsActive method doesn't exist. Presumably you mean virFirewallDStatus though i suggested a rename for that too :-)
Signed-off-by: Laine Stump <laine@laine.org> --- src/libvirt_private.syms | 1 + src/util/virfirewalld.c | 23 +++++++++++++++++++++++ src/util/virfirewalld.h | 3 +++ 3 files changed, 27 insertions(+)
Assuming commit message typo is fixed Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

or more simply "util: Introduce virFirewallDInterfaceSetZone" On 1/9/19 9:57 PM, Laine Stump wrote:
Sets the firewalld zone of the given interface. This function assumes that you've already called virFirewallDIsActive(), and relies on virDBusCallMethod's standard error reporting to log any errors.
Signed-off-by: Laine Stump <laine@laine.org> --- src/libvirt_private.syms | 1 + src/util/virfirewalld.c | 23 +++++++++++++++++++++++ src/util/virfirewalld.h | 3 +++ 3 files changed, 27 insertions(+)
Caveat - not my area of knowledge, but from a learning PoV for me... Any concerns over with this is a "compute intensive" type operation for firewalld and whether it's felt it would be useful to getZoneOfInterface first and compare vs. the passed zone before calling the changeZoneOfInterface? My thoughts here are along the lines of other (somewhat) recent upstream patches related to performance issues that I've seen from Nikolay and Dan. John

On 1/15/19 12:19 PM, John Ferlan wrote:
or more simply "util: Introduce virFirewallDInterfaceSetZone"
On 1/9/19 9:57 PM, Laine Stump wrote:
Sets the firewalld zone of the given interface. This function assumes that you've already called virFirewallDIsActive(), and relies on virDBusCallMethod's standard error reporting to log any errors.
Signed-off-by: Laine Stump <laine@laine.org> --- src/libvirt_private.syms | 1 + src/util/virfirewalld.c | 23 +++++++++++++++++++++++ src/util/virfirewalld.h | 3 +++ 3 files changed, 27 insertions(+)
Caveat - not my area of knowledge, but from a learning PoV for me...
Any concerns over with this is a "compute intensive" type operation for firewalld and whether it's felt it would be useful to getZoneOfInterface first and compare vs. the passed zone before calling the changeZoneOfInterface?
Any time we try to set a zone that doesn't exist, there should be an error message, so we can either go to the extra trouble to figure out if the zone setting will fail beforehand, or we can just try it. The one exception is in the case of setting the zone to "libvirt", but I think I've covered that in my response to Daniel's review of 4/5
My thoughts here are along the lines of other (somewhat) recent upstream patches related to performance issues that I've seen from Nikolay and Dan.
Well, getting a list of zones is something that only needs to be done once for each time firewalld or libvirtd is restarted, since zones can't be added/removed without restarting firewalld. I suppose attempting to set the zone multiple times when it doesn't exist could net a performance penalty, but on the other hand that would only be temporary until firewalld was updated. But again, there's a proposal in my response to Daniel's review of 4/5 that would eliminate that as well.

From: Laine Stump <laine@redhat.com> In the past (when both libvirt and firewalld used iptables), if either libvirt's rules *OR* firewalld's rules accepted a packet, it would be accepted. This was because libvirt and firewalld rules were processed by the same kernel hook. But now firewalld can use nftables for its backend, while libvirt's firewall rules are still using iptables; iptables rules are still processed, but at a different time during packet processing (i.e. during a different hook) than the firewalld nftables rules. The result is that a packet must be accepted by *BOTH* the libvirt iptables rules *AND* the firewalld nftable rules in order to be accepted. This causes pain because 1) libvirt always adds rules to permit DNS and DHCP (and sometimes TFTP) from guests to the local host. But libvirt's bridges are in firewalld's "default" zone (which is usually the zone called "public"). The public zone allows ssh, but doesn't allow DNS, DHCP, or TFTP. So even though libvirt's rules allow the DHCP and DNS traffic, the firewalld rules dont, thus guests connected to libvirt's bridges can't acquire an IP address from DHCP, nor can they make DNS queries to the DNS server libvirt has setup on the host. 2) firewalld's higher level "rich rules" don't yet have the ability to configure the acceptance of forwarded traffic (traffic that is going somewhere beyond the host), so any traffic that needs to be forwarded is rejected by the public zone's default "reject" policy (which rejects all traffic in the zone not specifically allowed by the rules in the zone, whether that traffic is destined to be forwarded or locally received by the host). libvirt can't send "direct" nftables rules (firewalld only supports that for iptables), so we can't solve this problem by just sending explicit nftables rules instead of explicit iptables rules (which, if it could be done, would place libvirt's rules in the same hook as firewalld's native rules, and thus eliminate the need for packets to be accepted by both libvirt's and firewalld's own rules). However, we can take advantage of a quirk in firewalld zones that have a default policy of "accept" (meaning any packet that doesn't match a specific rule in the zone will be *accepted*) - this default accept will also accept forwarded traffic (not just traffic destined for the host). Putting each network's bridge in a new zone called "libvirt" which has a default policy of accept will allow the forwarded traffic to pass, but the same default accept policy that fixes forwarded traffic also causes *all* traffic from guest to host to be accepted. To solve this new problem, we can take advantage of a new feature in firewalld (currently slated for firewalld-0.7.0) - priorities for rich rules - to add a low priority rule that rejects all local traffic (but leaves alone all forwarded traffic). So, our new zone will start with a list of services that are allowed (dhcp, dns, tftp, and ssh to start, but configurable via any firewalld management application, or direct editing of the zone file in /etc/firewalld/zones/libvirt.xml), followed by a low priority <reject/> rule (to reject all other traffic from guest to host), and finally with a default policy of accept (to allow forwarded traffic) After this patch, any network created by libvirt (when firewalld is enabled) will be added to the zone called "libvirt". HOWEVER, even this could be problematic - since the libvirt zone uses a very new feature in firewalld which might not yet be present in the firewalld package on the host. The best we can do is put the zone file in place, and let firewalld try to load it - if firewalld doesn't support rule priorities, it will fail to load the zone file and log an error. Since libvirtd will also be attempting to set the zone of every new interface to "libvirt", if the libvirt zone failed to load, then the call to set the zone of an interface will also fail; this is acceptable because it's a transient problem, and the failure will help alert the user that they need to also update their firewalld package. NB: This 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.0 or above. Although it's unfortunate that the behavior has to 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). NB2: This patch does not check whether the firewalld backend is nftables or iptables; it behaves identically in either case, which is much less confusing than getting different behavior based on the configuration of some other package (firewalld). NB3: firewalld zones can't normally be added to the runtime config of firewalld, so at package install/upgrade time we have to reload all of the firewalld permanent config for the new zone to be recognized. This is done with a call to "firewall-cmd --reload" during postinstall and postuninstall (for rpm-based distros; non-rpm distros will need to figure out a different method of triggering the reload). In the case that firewalld is inactive, firewall-cmd exits without doing anything (i.e. it doesn't start up firewalld.service if it's not already started). Resolves: https://bugzilla.redhat.com/1638342 Creates-and-Resolves: https://bugzilla.redhat.com/1650320 Signed-off-by: Laine Stump <laine@laine.org> --- libvirt.spec.in | 16 ++++++++++++++++ src/network/Makefile.inc.am | 10 +++++++++- src/network/bridge_driver_linux.c | 9 +++++++++ src/network/libvirt.zone | 14 ++++++++++++++ 4 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 src/network/libvirt.zone diff --git a/libvirt.spec.in b/libvirt.spec.in index b04cf53eb8..5217fee6ce 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -389,6 +389,8 @@ BuildRequires: rpcgen BuildRequires: libtirpc-devel %endif +BuildRequires: firewalld-filesystem + Provides: bundled(gnulib) %description @@ -1352,6 +1354,16 @@ if [ -f %{_localstatedir}/lib/rpm-state/libvirt/restart ]; then fi rm -rf %{_localstatedir}/lib/rpm-state/libvirt || : +%post daemon-driver-network +%if %{with_firewalld} + %firewalld_reload +%endif + +%postun daemon-driver-network +%if %{with_firewalld} + %firewalld_reload +%endif + %post daemon-config-network if test $1 -eq 1 && test ! -f %{_sysconfdir}/libvirt/qemu/networks/default.xml ; then # see if the network used by default network creates a conflict, @@ -1590,6 +1602,10 @@ exit 0 %attr(0755, root, root) %{_libexecdir}/libvirt_leaseshelper %{_libdir}/%{name}/connection-driver/libvirt_driver_network.so +%if %{with_firewalld} +%{_prefix}/lib/firewalld/zones/libvirt.xml +%endif + %files daemon-driver-nodedev %{_libdir}/%{name}/connection-driver/libvirt_driver_nodedev.so diff --git a/src/network/Makefile.inc.am b/src/network/Makefile.inc.am index 508c8c0422..20d899e699 100644 --- a/src/network/Makefile.inc.am +++ b/src/network/Makefile.inc.am @@ -87,6 +87,11 @@ install-data-network: ( cd $(DESTDIR)$(confdir)/qemu/networks/autostart && \ rm -f default.xml && \ $(LN_S) ../default.xml default.xml ) +if HAVE_FIREWALLD + $(MKDIR_P) "$(DESTDIR)$(prefix)/lib/firewalld/zones" + $(INSTALL_DATA) $(srcdir)/network/libvirt.zone \ + $(DESTDIR)$(prefix)/lib/firewalld/zones/libvirt.xml +endif HAVE_FIREWALLD uninstall-data-network: rm -f $(DESTDIR)$(confdir)/qemu/networks/autostart/default.xml @@ -95,10 +100,13 @@ uninstall-data-network: rmdir "$(DESTDIR)$(confdir)/qemu/networks" || : rmdir "$(DESTDIR)$(localstatedir)/lib/libvirt/network" ||: rmdir "$(DESTDIR)$(localstatedir)/run/libvirt/network" ||: +if HAVE_FIREWALLD + rm -f $(DESTDIR)$(prefix)/lib/firewalld/zones/libvirt.xml +endif HAVE_FIREWALLD endif WITH_NETWORK -EXTRA_DIST += network/default.xml +EXTRA_DIST += network/default.xml network/libvirt.zone .PHONY: \ install-data-network \ diff --git a/src/network/bridge_driver_linux.c b/src/network/bridge_driver_linux.c index dd08222653..a32f19bcf0 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 @@ -638,6 +639,14 @@ int networkAddFirewallRules(virNetworkDefPtr def) virFirewallPtr fw = NULL; int ret = -1; + + /* if firewalld is active, try to set the default "libvirt" zone, + * but ignore failure, since the version of firewalld on the host + * may have failed to load the libvirt zone + */ + if (virFirewallDStatus() >= 0) + ignore_value(virFirewallDInterfaceSetZone(def->bridge, "libvirt")); + fw = virFirewallNew(); virFirewallStartTransaction(fw, 0); diff --git a/src/network/libvirt.zone b/src/network/libvirt.zone new file mode 100644 index 0000000000..1750ba2f06 --- /dev/null +++ b/src/network/libvirt.zone @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<zone target="ACCEPT"> + <short>libvirt</short> + <description>The default policy of "ACCEPT" allows all packets to/from interfaces in the zone to be forwarded, while the (*low priority*) reject rule blocks any traffic destined for the host, except those services explicitly listed (that list can be modified as required by the local admin). This zone is intended to be used only by libvirt virtual networks - libvirt will add the bridge devices for all new virtual networks to this zone by default.</description> + +<rule priority='127'> + <reject/> +</rule> +<service name='dhcp'/> +<service name='dhcpv6'/> +<service name='dns'/> +<service name='ssh'/> +<service name='tftp'/> +</zone> -- 2.20.1

On Wed, Jan 09, 2019 at 09:57:36PM -0500, Laine Stump wrote:
From: Laine Stump <laine@redhat.com> [..] diff --git a/src/network/libvirt.zone b/src/network/libvirt.zone new file mode 100644 index 0000000000..1750ba2f06 --- /dev/null +++ b/src/network/libvirt.zone @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<zone target="ACCEPT"> + <short>libvirt</short> + <description>The default policy of "ACCEPT" allows all packets to/from interfaces in the zone to be forwarded, while the (*low priority*) reject rule blocks any traffic destined for the host, except those services explicitly listed (that list can be modified as required by the local admin). This zone is intended to be used only by libvirt virtual networks - libvirt will add the bridge devices for all new virtual networks to this zone by default.</description> + +<rule priority='127'>
The valid priority range is [-32768, 32767]. You may want to change this to 32767 to make sure it's the lowest precedence possible. Although, since libvirt completely controls this zone it won't matter unless libvirt or the user adds other rich rules.
+ <reject/> +</rule> +<service name='dhcp'/> +<service name='dhcpv6'/> +<service name='dns'/> +<service name='ssh'/> +<service name='tftp'/> +</zone> -- 2.20.1

On Wed, Jan 09, 2019 at 09:57:36PM -0500, Laine Stump wrote:
From: Laine Stump <laine@redhat.com>
In the past (when both libvirt and firewalld used iptables), if either libvirt's rules *OR* firewalld's rules accepted a packet, it would be accepted. This was because libvirt and firewalld rules were processed by the same kernel hook.
But now firewalld can use nftables for its backend, while libvirt's firewall rules are still using iptables; iptables rules are still processed, but at a different time during packet processing (i.e. during a different hook) than the firewalld nftables rules. The result is that a packet must be accepted by *BOTH* the libvirt iptables rules *AND* the firewalld nftable rules in order to be accepted.
This causes pain because
1) libvirt always adds rules to permit DNS and DHCP (and sometimes TFTP) from guests to the local host. But libvirt's bridges are in firewalld's "default" zone (which is usually the zone called "public"). The public zone allows ssh, but doesn't allow DNS, DHCP, or TFTP. So even though libvirt's rules allow the DHCP and DNS traffic, the firewalld rules dont, thus guests connected to libvirt's bridges can't acquire an IP address from DHCP, nor can they make DNS queries to the DNS server libvirt has setup on the host.
2) firewalld's higher level "rich rules" don't yet have the ability to configure the acceptance of forwarded traffic (traffic that is going somewhere beyond the host), so any traffic that needs to be forwarded is rejected by the public zone's default "reject" policy (which rejects all traffic in the zone not specifically allowed by the rules in the zone, whether that traffic is destined to be forwarded or locally received by the host).
libvirt can't send "direct" nftables rules (firewalld only supports that for iptables), so we can't solve this problem by just sending explicit nftables rules instead of explicit iptables rules (which, if it could be done, would place libvirt's rules in the same hook as firewalld's native rules, and thus eliminate the need for packets to be accepted by both libvirt's and firewalld's own rules).
However, we can take advantage of a quirk in firewalld zones that have a default policy of "accept" (meaning any packet that doesn't match a specific rule in the zone will be *accepted*) - this default accept will also accept forwarded traffic (not just traffic destined for the host).
Putting each network's bridge in a new zone called "libvirt" which has a default policy of accept will allow the forwarded traffic to pass, but the same default accept policy that fixes forwarded traffic also causes *all* traffic from guest to host to be accepted. To solve this new problem, we can take advantage of a new feature in firewalld (currently slated for firewalld-0.7.0) - priorities for rich rules - to add a low priority rule that rejects all local traffic (but leaves alone all forwarded traffic).
Ok, so we depend on newer firewalld. Any older version using nftables will still be broken. I think it would be quite desirable if we queried the firewalld version and backend and reported a fatal error to the user if they try to start a virtual network when we know it will be broken. You can get version from /org/fedoraproject/FirewallD1 org.freedesktop.Properties.Get("org.fedoraproject.FirewallD1", "version") And backend from /org/fedoraproject/FirewallD1/config org.freedesktop.Properties.Get("org.fedoraproject.FirewallD1.config", "FirewallBackend")
diff --git a/src/network/bridge_driver_linux.c b/src/network/bridge_driver_linux.c index dd08222653..a32f19bcf0 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
@@ -638,6 +639,14 @@ int networkAddFirewallRules(virNetworkDefPtr def) virFirewallPtr fw = NULL; int ret = -1;
+ + /* if firewalld is active, try to set the default "libvirt" zone, + * but ignore failure, since the version of firewalld on the host + * may have failed to load the libvirt zone + */
Ah, I was wondering when you checked the version. This is the place where I think we ought to raise an error
+ if (virFirewallDStatus() >= 0) + ignore_value(virFirewallDInterfaceSetZone(def->bridge, "libvirt")); + fw = virFirewallNew();
virFirewallStartTransaction(fw, 0); diff --git a/src/network/libvirt.zone b/src/network/libvirt.zone new file mode 100644 index 0000000000..1750ba2f06 --- /dev/null +++ b/src/network/libvirt.zone @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<zone target="ACCEPT"> + <short>libvirt</short> + <description>The default policy of "ACCEPT" allows all packets to/from interfaces in the zone to be forwarded, while the (*low priority*) reject rule blocks any traffic destined for the host, except those services explicitly listed (that list can be modified as required by the local admin). This zone is intended to be used only by libvirt virtual networks - libvirt will add the bridge devices for all new virtual networks to this zone by default.</description>
Line wrapping :-)
+ +<rule priority='127'> + <reject/> +</rule> +<service name='dhcp'/> +<service name='dhcpv6'/> +<service name='dns'/> +<service name='ssh'/> +<service name='tftp'/> +</zone>
I wonder if we should have an configure time check so that we do *not* install this file if built agains an old firewalld version. By unconditionally installing it we're probably causing firewalld to emit an error message in syslog which some sysadmin is almost certainly to report a bug about Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

On 1/15/19 11:39 AM, Daniel P. Berrangé wrote:
On Wed, Jan 09, 2019 at 09:57:36PM -0500, Laine Stump wrote:
From: Laine Stump <laine@redhat.com>
In the past (when both libvirt and firewalld used iptables), if either libvirt's rules *OR* firewalld's rules accepted a packet, it would be accepted. This was because libvirt and firewalld rules were processed by the same kernel hook.
But now firewalld can use nftables for its backend, while libvirt's firewall rules are still using iptables; iptables rules are still processed, but at a different time during packet processing (i.e. during a different hook) than the firewalld nftables rules. The result is that a packet must be accepted by *BOTH* the libvirt iptables rules *AND* the firewalld nftable rules in order to be accepted.
This causes pain because
1) libvirt always adds rules to permit DNS and DHCP (and sometimes TFTP) from guests to the local host. But libvirt's bridges are in firewalld's "default" zone (which is usually the zone called "public"). The public zone allows ssh, but doesn't allow DNS, DHCP, or TFTP. So even though libvirt's rules allow the DHCP and DNS traffic, the firewalld rules dont, thus guests connected to libvirt's bridges can't acquire an IP address from DHCP, nor can they make DNS queries to the DNS server libvirt has setup on the host.
2) firewalld's higher level "rich rules" don't yet have the ability to configure the acceptance of forwarded traffic (traffic that is going somewhere beyond the host), so any traffic that needs to be forwarded is rejected by the public zone's default "reject" policy (which rejects all traffic in the zone not specifically allowed by the rules in the zone, whether that traffic is destined to be forwarded or locally received by the host).
libvirt can't send "direct" nftables rules (firewalld only supports that for iptables), so we can't solve this problem by just sending explicit nftables rules instead of explicit iptables rules (which, if it could be done, would place libvirt's rules in the same hook as firewalld's native rules, and thus eliminate the need for packets to be accepted by both libvirt's and firewalld's own rules).
However, we can take advantage of a quirk in firewalld zones that have a default policy of "accept" (meaning any packet that doesn't match a specific rule in the zone will be *accepted*) - this default accept will also accept forwarded traffic (not just traffic destined for the host).
Putting each network's bridge in a new zone called "libvirt" which has a default policy of accept will allow the forwarded traffic to pass, but the same default accept policy that fixes forwarded traffic also causes *all* traffic from guest to host to be accepted. To solve this new problem, we can take advantage of a new feature in firewalld (currently slated for firewalld-0.7.0) - priorities for rich rules - to add a low priority rule that rejects all local traffic (but leaves alone all forwarded traffic). Ok, so we depend on newer firewalld. Any older version using nftables will still be broken.
I think it would be quite desirable if we queried the firewalld version and backend and reported a fatal error to the user if they try to start a virtual network when we know it will be broken.
You can get version from /org/fedoraproject/FirewallD1
org.freedesktop.Properties.Get("org.fedoraproject.FirewallD1", "version")
And backend from /org/fedoraproject/FirewallD1/config
org.freedesktop.Properties.Get("org.fedoraproject.FirewallD1.config", "FirewallBackend")
Depending on the firewalld version won't give us correct results, since the patches to support rule priorities could be / will be backported to older firewalld versions on some distros. Of course we could make failure to set the zone to "libvirt" a fatal error - that is really the most reliable proxy we have for the question "Does this firewalld support rule priorities?" That's a bit draconian though, since it means that "new" libvirt will fail to start networks on all distros that haven't yet updated their firewalld version (or backported the necessary patches). In particular, if they haven't updated firewalld, but they're still using the iptables backend anyway, we shouldn't be whining (although for consistency we should still set the zone to "libvirt" when possible, even if the backend is iptables,)
diff --git a/src/network/bridge_driver_linux.c b/src/network/bridge_driver_linux.c index dd08222653..a32f19bcf0 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
@@ -638,6 +639,14 @@ int networkAddFirewallRules(virNetworkDefPtr def) virFirewallPtr fw = NULL; int ret = -1;
+ + /* if firewalld is active, try to set the default "libvirt" zone, + * but ignore failure, since the version of firewalld on the host + * may have failed to load the libvirt zone + */ Ah, I was wondering when you checked the version. This is the place where I think we ought to raise an error
Yep,
+ if (virFirewallDStatus() >= 0) + ignore_value(virFirewallDInterfaceSetZone(def->bridge, "libvirt")); + fw = virFirewallNew();
virFirewallStartTransaction(fw, 0); diff --git a/src/network/libvirt.zone b/src/network/libvirt.zone new file mode 100644 index 0000000000..1750ba2f06 --- /dev/null +++ b/src/network/libvirt.zone @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<zone target="ACCEPT"> + <short>libvirt</short> + <description>The default policy of "ACCEPT" allows all packets to/from interfaces in the zone to be forwarded, while the (*low priority*) reject rule blocks any traffic destined for the host, except those services explicitly listed (that list can be modified as required by the local admin). This zone is intended to be used only by libvirt virtual networks - libvirt will add the bridge devices for all new virtual networks to this zone by default.</description> Line wrapping :-)
Yeah, the examples I saw had everything in a single line, and that made me think maybe something in firewalld requires it to be a single line, so I avoided breaking it up. I guess I *could* go to the horrible great trouble of actually trying it out with line breaks and see if it's accepted :-)
+ +<rule priority='127'> + <reject/> +</rule> +<service name='dhcp'/> +<service name='dhcpv6'/> +<service name='dns'/> +<service name='ssh'/> +<service name='tftp'/> +</zone> I wonder if we should have an configure time check so that we do *not* install this file if built agains an old firewalld version. By unconditionally installing it we're probably causing firewalld to emit an error message in syslog which some sysadmin is almost certainly to report a bug about
1) see the comment above about the unreliability of version checks. 2) The version / build of firewalld present at build time is really irrelevant. As a matter of fact, we currently don't require firewalld to be present *at all* at build time (or at runtime - we just use it if it's there and active, otherwise we go directly to iptables/ebtables). 3) I actually *want* the bug reported to distro maintainers, to push them to update their firewalld sooner rather than later. On the other hand, it's much nicer if the error is reported only when someone tries to configure things in a manner that won't work, i.e. "backend==nftables, but firewalld doesn't support rule priorities". So here's an idea - how about if we do this: Once when libvirtd is started: retrieve a list of zones and check for presence of the libvirt zone. Save this somewhere. Each time a network is started: if (libvirt zone doesn't exist) { if (firewalldbackend == nftables) { Error "Hey you!! Change the firewalld backend to nftables or update firewalld!!" } } else { put the bridge in the libvirt zone (regardless of nftables/iptables) } This way if firewalld is too old to support rich rule priorities, but the backend is iptables, the network silently functions as it always has (i.e. in the default zone). *AND* as long as firewalld is new enough to support rule priorities, behavior will be consistent no matter what the backend is set to. But if a distro or user tries to set the nftables backend (meaning that networking can't work without rule priorities) and firewalld doesn't support rule priorities, *then* it fails to start the network and logs an error. (in the meantime, we haven't relied on version number checks, and a system with old firewalld can still build a libvirt that supports new firewalld) Does this sound reasonable?

On Tue, Jan 15, 2019 at 12:43:05PM -0500, Laine Stump wrote:
On 1/15/19 11:39 AM, Daniel P. Berrangé wrote:
On Wed, Jan 09, 2019 at 09:57:36PM -0500, Laine Stump wrote:
From: Laine Stump <laine@redhat.com>
In the past (when both libvirt and firewalld used iptables), if either libvirt's rules *OR* firewalld's rules accepted a packet, it would be accepted. This was because libvirt and firewalld rules were processed by the same kernel hook.
But now firewalld can use nftables for its backend, while libvirt's firewall rules are still using iptables; iptables rules are still processed, but at a different time during packet processing (i.e. during a different hook) than the firewalld nftables rules. The result is that a packet must be accepted by *BOTH* the libvirt iptables rules *AND* the firewalld nftable rules in order to be accepted.
This causes pain because
1) libvirt always adds rules to permit DNS and DHCP (and sometimes TFTP) from guests to the local host. But libvirt's bridges are in firewalld's "default" zone (which is usually the zone called "public"). The public zone allows ssh, but doesn't allow DNS, DHCP, or TFTP. So even though libvirt's rules allow the DHCP and DNS traffic, the firewalld rules dont, thus guests connected to libvirt's bridges can't acquire an IP address from DHCP, nor can they make DNS queries to the DNS server libvirt has setup on the host.
2) firewalld's higher level "rich rules" don't yet have the ability to configure the acceptance of forwarded traffic (traffic that is going somewhere beyond the host), so any traffic that needs to be forwarded is rejected by the public zone's default "reject" policy (which rejects all traffic in the zone not specifically allowed by the rules in the zone, whether that traffic is destined to be forwarded or locally received by the host).
libvirt can't send "direct" nftables rules (firewalld only supports that for iptables), so we can't solve this problem by just sending explicit nftables rules instead of explicit iptables rules (which, if it could be done, would place libvirt's rules in the same hook as firewalld's native rules, and thus eliminate the need for packets to be accepted by both libvirt's and firewalld's own rules).
However, we can take advantage of a quirk in firewalld zones that have a default policy of "accept" (meaning any packet that doesn't match a specific rule in the zone will be *accepted*) - this default accept will also accept forwarded traffic (not just traffic destined for the host).
Putting each network's bridge in a new zone called "libvirt" which has a default policy of accept will allow the forwarded traffic to pass, but the same default accept policy that fixes forwarded traffic also causes *all* traffic from guest to host to be accepted. To solve this new problem, we can take advantage of a new feature in firewalld (currently slated for firewalld-0.7.0) - priorities for rich rules - to add a low priority rule that rejects all local traffic (but leaves alone all forwarded traffic). Ok, so we depend on newer firewalld. Any older version using nftables will still be broken.
I think it would be quite desirable if we queried the firewalld version and backend and reported a fatal error to the user if they try to start a virtual network when we know it will be broken.
You can get version from /org/fedoraproject/FirewallD1
org.freedesktop.Properties.Get("org.fedoraproject.FirewallD1", "version")
And backend from /org/fedoraproject/FirewallD1/config
org.freedesktop.Properties.Get("org.fedoraproject.FirewallD1.config", "FirewallBackend")
Depending on the firewalld version won't give us correct results, since the patches to support rule priorities could be / will be backported to older firewalld versions on some distros.
Of course we could make failure to set the zone to "libvirt" a fatal error - that is really the most reliable proxy we have for the question "Does this firewalld support rule priorities?" That's a bit draconian though, since it means that "new" libvirt will fail to start networks on all distros that haven't yet updated their firewalld version (or backported the necessary patches). In particular, if they haven't updated firewalld, but they're still using the iptables backend anyway, we shouldn't be whining (although for consistency we should still set the zone to "libvirt" when possible, even if the backend is iptables,)
I feel we should enforce failure to set libvirt zone as a fatal error *if* we query that FirewallBackend property == nftables. That is a situation we know will never work correctly, without opening a security hole in the libvirt rules. We should also enforce failure if setting zone failed and version >= 0.7.0, regardless of what backend is set, as we expect that to always work. Essentially only case we don't want to report an error is in version < 0.7.0 and backend is iptables, as that is safe to use. This should avoid issues with distros that choose to backport.
+<rule priority='127'> + <reject/> +</rule> +<service name='dhcp'/> +<service name='dhcpv6'/> +<service name='dns'/> +<service name='ssh'/> +<service name='tftp'/> +</zone> I wonder if we should have an configure time check so that we do *not* install this file if built agains an old firewalld version. By unconditionally installing it we're probably causing firewalld to emit an error message in syslog which some sysadmin is almost certainly to report a bug about
1) see the comment above about the unreliability of version checks.
2) The version / build of firewalld present at build time is really irrelevant. As a matter of fact, we currently don't require firewalld to be present *at all* at build time (or at runtime - we just use it if it's there and active, otherwise we go directly to iptables/ebtables).
If configure installs it by default, but provides a switch, we can use --with-firewall-zone / --without-firewalld-zone explicitly in the RPM specs. We know exactly which Fedora / RHEL versions need this in so can avoid installing it where we know if isn't wanted.
3) I actually *want* the bug reported to distro maintainers, to push them to update their firewalld sooner rather than later.
If we install it based on version where nftables supprot was first introduced in firewalld, rather than when it was fixed, we would still get useful bug reports without spamming people with errors on distros where nftables isn't even supported by firewalld.
So here's an idea - how about if we do this:
Once when libvirtd is started:
retrieve a list of zones and check for presence of the libvirt zone. Save this somewhere.
Each time a network is started:
if (libvirt zone doesn't exist) {
if (firewalldbackend == nftables) {
Error "Hey you!! Change the firewalld backend to nftables or update firewalld!!"
}
} else {
put the bridge in the libvirt zone (regardless of nftables/iptables)
}
That doesn't solve the issue with installing an invalid zone file for older firewalld which could generate syslog errors on firewalld startup.
(in the meantime, we haven't relied on version number checks, and a system with old firewalld can still build a libvirt that supports new firewalld)
As above I don't think we need to rely on version numbers for te compile time check, as long as we provide the --with/--without flags to let the distro vendors override it. Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

On 1/15/19 12:54 PM, Daniel P. Berrangé wrote: > On Tue, Jan 15, 2019 at 12:43:05PM -0500, Laine Stump wrote: >> On 1/15/19 11:39 AM, Daniel P. Berrangé wrote: >>> On Wed, Jan 09, 2019 at 09:57:36PM -0500, Laine Stump wrote: >>>> From: Laine Stump<laine@redhat.com> >>>> >>>> In the past (when both libvirt and firewalld used iptables), if either >>>> libvirt's rules *OR* firewalld's rules accepted a packet, it would be >>>> accepted. This was because libvirt and firewalld rules were processed >>>> by the same kernel hook. >>>> >>>> But now firewalld can use nftables for its backend, while libvirt's >>>> firewall rules are still using iptables; iptables rules are still >>>> processed, but at a different time during packet processing >>>> (i.e. during a different hook) than the firewalld nftables rules. The >>>> result is that a packet must be accepted by *BOTH* the libvirt >>>> iptables rules *AND* the firewalld nftable rules in order to be >>>> accepted. >>>> >>>> This causes pain because >>>> >>>> 1) libvirt always adds rules to permit DNS and DHCP (and sometimes >>>> TFTP) from guests to the local host. But libvirt's bridges are in >>>> firewalld's "default" zone (which is usually the zone called >>>> "public"). The public zone allows ssh, but doesn't allow DNS, DHCP, or >>>> TFTP. So even though libvirt's rules allow the DHCP and DNS traffic, >>>> the firewalld rules dont, thus guests connected to libvirt's bridges >>>> can't acquire an IP address from DHCP, nor can they make DNS queries >>>> to the DNS server libvirt has setup on the host. >>>> >>>> 2) firewalld's higher level "rich rules" don't yet have the ability to >>>> configure the acceptance of forwarded traffic (traffic that is going >>>> somewhere beyond the host), so any traffic that needs to be forwarded >>>> is rejected by the public zone's default "reject" policy (which >>>> rejects all traffic in the zone not specifically allowed by the rules >>>> in the zone, whether that traffic is destined to be forwarded or >>>> locally received by the host). >>>> >>>> libvirt can't send "direct" nftables rules (firewalld only supports >>>> that for iptables), so we can't solve this problem by just sending >>>> explicit nftables rules instead of explicit iptables rules (which, if >>>> it could be done, would place libvirt's rules in the same hook as >>>> firewalld's native rules, and thus eliminate the need for packets to >>>> be accepted by both libvirt's and firewalld's own rules). >>>> >>>> However, we can take advantage of a quirk in firewalld zones that have >>>> a default policy of "accept" (meaning any packet that doesn't match a >>>> specific rule in the zone will be *accepted*) - this default accept will >>>> also accept forwarded traffic (not just traffic destined for the host). >>>> >>>> Putting each network's bridge in a new zone called "libvirt" which has >>>> a default policy of accept will allow the forwarded traffic to pass, >>>> but the same default accept policy that fixes forwarded traffic also >>>> causes *all* traffic from guest to host to be accepted. To solve this >>>> new problem, we can take advantage of a new feature in firewalld >>>> (currently slated for firewalld-0.7.0) - priorities for rich rules - >>>> to add a low priority rule that rejects all local traffic (but leaves >>>> alone all forwarded traffic). >>> Ok, so we depend on newer firewalld. Any older version using nftables >>> will still be broken. >>> >>> I think it would be quite desirable if we queried the firewalld version >>> and backend and reported a fatal error to the user if they try to >>> start a virtual network when we know it will be broken. >>> >>> You can get version from /org/fedoraproject/FirewallD1 >>> >>> org.freedesktop.Properties.Get("org.fedoraproject.FirewallD1", "version") >>> >>> And backend from /org/fedoraproject/FirewallD1/config >>> >>> org.freedesktop.Properties.Get("org.fedoraproject.FirewallD1.config", "FirewallBackend") >> Depending on the firewalld version won't give us correct results, since the >> patches to support rule priorities could be / will be backported to older >> firewalld versions on some distros. >> >> >> Of course we could make failure to set the zone to "libvirt" a fatal error - >> that is really the most reliable proxy we have for the question "Does this >> firewalld support rule priorities?" That's a bit draconian though, since it >> means that "new" libvirt will fail to start networks on all distros that >> haven't yet updated their firewalld version (or backported the necessary >> patches). In particular, if they haven't updated firewalld, but they're >> still using the iptables backend anyway, we shouldn't be whining (although >> for consistency we should still set the zone to "libvirt" when possible, >> even if the backend is iptables,) > I feel we should enforce failure to set libvirt zone as a fatal > error *if* we query that FirewallBackend property == nftables. > That is a situation we know will never work correctly, without > opening a security hole in the libvirt rules. > > We should also enforce failure if setting zone failed and > version >= 0.7.0, regardless of what backend is set, as we > expect that to always work. > > Essentially only case we don't want to report an error is > in version < 0.7.0 and backend is iptables, as that is > safe to use. > > This should avoid issues with distros that choose to backport. Okay, those all make sense. > >>>> +<rule priority='127'> >>>> + <reject/> >>>> +</rule> >>>> +<service name='dhcp'/> >>>> +<service name='dhcpv6'/> >>>> +<service name='dns'/> >>>> +<service name='ssh'/> >>>> +<service name='tftp'/> >>>> +</zone> >>> I wonder if we should have an configure time check so that we do >>> *not* install this file if built agains an old firewalld version. >>> By unconditionally installing it we're probably causing firewalld >>> to emit an error message in syslog which some sysadmin is almost >>> certainly to report a bug about >> 1) see the comment above about the unreliability of version checks. >> >> >> 2) The version / build of firewalld present at build time is really >> irrelevant. As a matter of fact, we currently don't require firewalld to be >> present *at all* at build time (or at runtime - we just use it if it's there >> and active, otherwise we go directly to iptables/ebtables). > If configure installs it by default, but provides a switch, we can > use --with-firewall-zone / --without-firewalld-zone explicitly in > the RPM specs. We know exactly which Fedora / RHEL versions need > this in so can avoid installing it where we know if isn't wanted. To make sure I'm understanding properly (because I think I misunderstood the 1st time I read it) you're suggesting that we not check the version of firewalld during configure/build, but have a configure option that always defaults to "with", and can be manually set to "without"? Since the extraneous error message that you want to get rid of is one that's logged by firewalld when it fails to parse the libvirt zone file (I think we can avoid all other extraneous logs at runtime by checking the firewalld backend, and checking for existence of the libvirt zone), I guess this configure change would only be useful for indicating whether or not the libvirt zone file should be installed (and not really essential for any runtime code in libvirt), but that's something that can't really be affected by configure (aside from having it modify the libvirt.spec file, but that's only useful for RHEL/Fedora, and I don't think RHEL or Fedora will be updating libvirt without updating firewalld). So I'm not sure there's a point to supporting --without_firewalld_zone in the configure. >> 3) I actually *want* the bug reported to distro maintainers, to push them to >> update their firewalld sooner rather than later. > If we install it based on version where nftables supprot was first > introduced in firewalld, rather than when it was fixed, we would > still get useful bug reports without spamming people with errors on > distros where nftables isn't even supported by firewalld. Our aim is to avoid the error log caused by firewalld failing to parse the libvirt zone (if that failure is due to a lack of support for rule priorities). To do that, we need to make sure the libvirt zone file isn't installed on systems where firewalld lacks support for rule priorities. We can either do that by modifying the package contents to include or not include libvirt.zone, or we can do it by modifying the install-time scripts to copy/not copy libvirt.zone into its place. If we modify the package contents at build time, then updating firewalld to a version supporting nftables+priorities would necessitate a new build *and* install of the libvirt packages. If, on the other hand, we use logic in the install scripts, we wouldn't have to rebuild the libvirt packages, but it would still mean that an update of firewalld on the target host would require a reinstall of libvirt. In both cases, if you didn't rebuild+reinstall libvirt (or reinstall in the latter case) after updating firewalld to a new version with nftables+priorities, you would end up with the situation we have now, where the backend is nftables and libvirt networks are broken, but there is no error message pointing that out. I think that is worse than (temporarily, until firewalld gets updated) logging one extra error log each time firewalld is restarted. Hmm, but I guess if the *only* change when building/installing with --without_firewalld_zones was that libvirt.zone wasn't installed (i.e. we would still compile the code that logs a libvirt error if the backend is nftables but there is no libvirt zone), then I guess the failure wouldn't be silent. So I retract my statement about a silent failure, but it still would mean the libvirt packages would need to be rebuilt/reinstalled to get things working after a firewalld upgrade. >> So here's an idea - how about if we do this: >> >> >> Once when libvirtd is started: >> >> retrieve a list of zones and check for presence of the libvirt zone. Save >> this somewhere. >> >> Each time a network is started: >> >> if (libvirt zone doesn't exist) { >> >> if (firewalldbackend == nftables) { >> >> Error "Hey you!! Change the firewalld backend to nftables or update >> firewalld!!" >> >> } >> >> } else { >> >> put the bridge in the libvirt zone (regardless of nftables/iptables) >> >> } > That doesn't solve the issue with installing an invalid zone > file for older firewalld which could generate syslog errors > on firewalld startup. s/could/would/ :-) But on the positive side, it's only a single error message, logged just once per restart of firewalld. So it's got that going for it. Which is nice. </BillMurrayCaddyShack> Since it's only a transient thing (it will only be a problem for somebody who upgrades to new libvirt, but keeps their firewalld at something older than 0.7.0), I don't think we should do anything at all complicated to prevent it - we'd just end up with a bunch of obscure code that won't be used any time after 6 months from now, and will continue confusing people long into the future. I think it's better in the end to just live with a few extra (and harmless) error messages during the short period when some distros have libvirt >= 5.1.0 and firewalld < 0.7.0, rather than having silent (i.e. no error message) failures if a distro updates firewalld without rebuilding libvirt. >> (in the meantime, we haven't relied on version number checks, and a system >> with old firewalld can still build a libvirt that supports new firewalld) > As above I don't think we need to rely on version numbers for te compile > time check, as long as we provide the --with/--without flags to let the > distro vendors override it. On one hand, it could be useful to add the --with/--without_firewalld_zones configure flags. On the other hand, that permits a distro to build a package that is guaranteed to cause a silent failure when firewalld is upgraded and has backend set to nftables, which I think is a much worse problem because it is silent. That makes me lean towards not even providing that option, just for the sake of safety.

On Wed, Jan 16, 2019 at 09:43:58PM -0500, Laine Stump wrote: > On 1/15/19 12:54 PM, Daniel P. Berrangé wrote: > > On Tue, Jan 15, 2019 at 12:43:05PM -0500, Laine Stump wrote: > > > On 1/15/19 11:39 AM, Daniel P. Berrangé wrote: > > > > I wonder if we should have an configure time check so that we do > > > > *not* install this file if built agains an old firewalld version. > > > > By unconditionally installing it we're probably causing firewalld > > > > to emit an error message in syslog which some sysadmin is almost > > > > certainly to report a bug about > > > 1) see the comment above about the unreliability of version checks. > > > > > > > > > 2) The version / build of firewalld present at build time is really > > > irrelevant. As a matter of fact, we currently don't require firewalld to be > > > present *at all* at build time (or at runtime - we just use it if it's there > > > and active, otherwise we go directly to iptables/ebtables). > > If configure installs it by default, but provides a switch, we can > > use --with-firewall-zone / --without-firewalld-zone explicitly in > > the RPM specs. We know exactly which Fedora / RHEL versions need > > this in so can avoid installing it where we know if isn't wanted. > > > To make sure I'm understanding properly (because I think I misunderstood the > 1st time I read it) you're suggesting that we not check the version of > firewalld during configure/build, but have a configure option that always > defaults to "with", and can be manually set to "without"? Yes that's correct. > Since the extraneous error message that you want to get rid of is one that's > logged by firewalld when it fails to parse the libvirt zone file (I think we > can avoid all other extraneous logs at runtime by checking the firewalld > backend, and checking for existence of the libvirt zone), I guess this > configure change would only be useful for indicating whether or not the > libvirt zone file should be installed (and not really essential for any > runtime code in libvirt), but that's something that can't really be > affected by configure (aside from having it modify the libvirt.spec file, > but that's only useful for RHEL/Fedora, and I don't think RHEL or Fedora > will be updating libvirt without updating firewalld). So I'm not sure > there's a point to supporting --without_firewalld_zone in the configure. It isn't only about the versions of firewalld that support nftables, but lack priority. Firewalld has existed in RHEL/Fedora and other distros for many years now, and only the yet-to-be release 0.7.0 version supports the new priority syntax our zone file is using. So essentially every distro that exists today will see the error from our zone file if we install it unconditionally. > > > 3) I actually *want* the bug reported to distro maintainers, to push them to > > > update their firewalld sooner rather than later. > > If we install it based on version where nftables supprot was first > > introduced in firewalld, rather than when it was fixed, we would > > still get useful bug reports without spamming people with errors on > > distros where nftables isn't even supported by firewalld. > > > Our aim is to avoid the error log caused by firewalld failing to parse the > libvirt zone (if that failure is due to a lack of support for rule > priorities). To do that, we need to make sure the libvirt zone file isn't > installed on systems where firewalld lacks support for rule priorities. We > can either do that by modifying the package contents to include or not > include libvirt.zone, or we can do it by modifying the install-time scripts > to copy/not copy libvirt.zone into its place. > > > If we modify the package contents at build time, then updating firewalld to > a version supporting nftables+priorities would necessitate a new build *and* > install of the libvirt packages. I don't see that as a real problem. Very few distros will rebase firewalld in an existing stream. In a development stream, a rebuild of libvirt will happen by chance for other reasons alrady. Dynamically copying files around at time of RPM install is never very nice as it becomes content that is not tracked by RPM. > Hmm, but I guess if the *only* change when building/installing with > --without_firewalld_zones was that libvirt.zone wasn't installed (i.e. we > would still compile the code that logs a libvirt error if the backend is > nftables but there is no libvirt zone), then I guess the failure wouldn't be > silent. So I retract my statement about a silent failure, but it still would > mean the libvirt packages would need to be rebuilt/reinstalled to get things > working after a firewalld upgrade. Yes, I'd expect a rebuild of libvirt packages to take advantage of a newer firewalld. > Since it's only a transient thing (it will only be a problem for somebody > who upgrades to new libvirt, but keeps their firewalld at something older > than 0.7.0), I don't think we should do anything at all complicated to > prevent it - we'd just end up with a bunch of obscure code that won't be > used any time after 6 months from now, and will continue confusing people > long into the future. I think it's better in the end to just live with a few > extra (and harmless) error messages during the short period when some > distros have libvirt >= 5.1.0 and firewalld < 0.7.0, rather than having > silent (i.e. no error message) failures if a distro updates firewalld > without rebuilding libvirt. It isn't a transient thing though - the majority of distros will never upgrade their firewalld to 0.7.0 - they'll just wait until their next distro release. Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

On 1/9/19 9:57 PM, Laine Stump wrote:
From: Laine Stump <laine@redhat.com>
In the past (when both libvirt and firewalld used iptables), if either libvirt's rules *OR* firewalld's rules accepted a packet, it would be accepted. This was because libvirt and firewalld rules were processed by the same kernel hook.
But now firewalld can use nftables for its backend, while libvirt's firewall rules are still using iptables; iptables rules are still processed, but at a different time during packet processing (i.e. during a different hook) than the firewalld nftables rules. The result is that a packet must be accepted by *BOTH* the libvirt iptables rules *AND* the firewalld nftable rules in order to be accepted.
This causes pain because
1) libvirt always adds rules to permit DNS and DHCP (and sometimes TFTP) from guests to the local host. But libvirt's bridges are in firewalld's "default" zone (which is usually the zone called "public"). The public zone allows ssh, but doesn't allow DNS, DHCP, or TFTP. So even though libvirt's rules allow the DHCP and DNS traffic, the firewalld rules dont, thus guests connected to libvirt's bridges can't acquire an IP address from DHCP, nor can they make DNS queries to the DNS server libvirt has setup on the host.
2) firewalld's higher level "rich rules" don't yet have the ability to configure the acceptance of forwarded traffic (traffic that is going somewhere beyond the host), so any traffic that needs to be forwarded is rejected by the public zone's default "reject" policy (which rejects all traffic in the zone not specifically allowed by the rules in the zone, whether that traffic is destined to be forwarded or locally received by the host).
libvirt can't send "direct" nftables rules (firewalld only supports that for iptables), so we can't solve this problem by just sending explicit nftables rules instead of explicit iptables rules (which, if it could be done, would place libvirt's rules in the same hook as firewalld's native rules, and thus eliminate the need for packets to be accepted by both libvirt's and firewalld's own rules).
However, we can take advantage of a quirk in firewalld zones that have a default policy of "accept" (meaning any packet that doesn't match a specific rule in the zone will be *accepted*) - this default accept will also accept forwarded traffic (not just traffic destined for the host).
Putting each network's bridge in a new zone called "libvirt" which has a default policy of accept will allow the forwarded traffic to pass, but the same default accept policy that fixes forwarded traffic also causes *all* traffic from guest to host to be accepted. To solve this new problem, we can take advantage of a new feature in firewalld (currently slated for firewalld-0.7.0) - priorities for rich rules - to add a low priority rule that rejects all local traffic (but leaves alone all forwarded traffic).
So, our new zone will start with a list of services that are allowed (dhcp, dns, tftp, and ssh to start, but configurable via any firewalld management application, or direct editing of the zone file in /etc/firewalld/zones/libvirt.xml), followed by a low priority <reject/> rule (to reject all other traffic from guest to host), and finally with a default policy of accept (to allow forwarded traffic)
After this patch, any network created by libvirt (when firewalld is enabled) will be added to the zone called "libvirt".
HOWEVER, even this could be problematic - since the libvirt zone uses a very new feature in firewalld which might not yet be present in the firewalld package on the host. The best we can do is put the zone file in place, and let firewalld try to load it - if firewalld doesn't support rule priorities, it will fail to load the zone file and log an error. Since libvirtd will also be attempting to set the zone of every new interface to "libvirt", if the libvirt zone failed to load, then the call to set the zone of an interface will also fail; this is acceptable because it's a transient problem, and the failure will help alert the user that they need to also update their firewalld package.
NB: This 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.0 or above. Although it's unfortunate that the behavior has to 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).
NB2: This patch does not check whether the firewalld backend is nftables or iptables; it behaves identically in either case, which is much less confusing than getting different behavior based on the configuration of some other package (firewalld).
NB3: firewalld zones can't normally be added to the runtime config of firewalld, so at package install/upgrade time we have to reload all of the firewalld permanent config for the new zone to be recognized. This is done with a call to "firewall-cmd --reload" during postinstall and postuninstall (for rpm-based distros; non-rpm distros will need to figure out a different method of triggering the reload). In the case that firewalld is inactive, firewall-cmd exits without doing anything (i.e. it doesn't start up firewalld.service if it's not already started).
Resolves: https://bugzilla.redhat.com/1638342 Creates-and-Resolves: https://bugzilla.redhat.com/1650320 Signed-off-by: Laine Stump <laine@laine.org> --- libvirt.spec.in | 16 ++++++++++++++++ src/network/Makefile.inc.am | 10 +++++++++- src/network/bridge_driver_linux.c | 9 +++++++++ src/network/libvirt.zone | 14 ++++++++++++++ 4 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 src/network/libvirt.zone
When the commit message is longer/larger than the patch ;-) /me wonders how much of the above can or even should be described in https://libvirt.org/firewall.html. For those that really know, understand, care, etc. about firewalls getting to understand what role libvirt plays in things could be very helpful. Besides you have so much to say now and before you forget it again...
diff --git a/libvirt.spec.in b/libvirt.spec.in index b04cf53eb8..5217fee6ce 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -389,6 +389,8 @@ BuildRequires: rpcgen BuildRequires: libtirpc-devel %endif
+BuildRequires: firewalld-filesystem +
Should this have a with_firewalld like others below? Of course I find/note the conditional with_firewalld check around line 84 and then the unconditional setting around line 131 to be odd, but for consistency...
Provides: bundled(gnulib)
%description @@ -1352,6 +1354,16 @@ if [ -f %{_localstatedir}/lib/rpm-state/libvirt/restart ]; then fi rm -rf %{_localstatedir}/lib/rpm-state/libvirt || :
+%post daemon-driver-network +%if %{with_firewalld} + %firewalld_reload +%endif + +%postun daemon-driver-network +%if %{with_firewalld} + %firewalld_reload +%endif + %post daemon-config-network if test $1 -eq 1 && test ! -f %{_sysconfdir}/libvirt/qemu/networks/default.xml ; then # see if the network used by default network creates a conflict, @@ -1590,6 +1602,10 @@ exit 0 %attr(0755, root, root) %{_libexecdir}/libvirt_leaseshelper %{_libdir}/%{name}/connection-driver/libvirt_driver_network.so
+%if %{with_firewalld} +%{_prefix}/lib/firewalld/zones/libvirt.xml +%endif +
So something is causing me to wonder if we have to do 'anything' here with respect to file security like other %attr's?
%files daemon-driver-nodedev %{_libdir}/%{name}/connection-driver/libvirt_driver_nodedev.so
John [...]

On 1/15/19 12:23 PM, John Ferlan wrote:
On 1/9/19 9:57 PM, Laine Stump wrote:
From: Laine Stump <laine@redhat.com>
In the past (when both libvirt and firewalld used iptables), if either libvirt's rules *OR* firewalld's rules accepted a packet, it would be accepted. This was because libvirt and firewalld rules were processed by the same kernel hook.
But now firewalld can use nftables for its backend, while libvirt's firewall rules are still using iptables; iptables rules are still processed, but at a different time during packet processing (i.e. during a different hook) than the firewalld nftables rules. The result is that a packet must be accepted by *BOTH* the libvirt iptables rules *AND* the firewalld nftable rules in order to be accepted.
This causes pain because
1) libvirt always adds rules to permit DNS and DHCP (and sometimes TFTP) from guests to the local host. But libvirt's bridges are in firewalld's "default" zone (which is usually the zone called "public"). The public zone allows ssh, but doesn't allow DNS, DHCP, or TFTP. So even though libvirt's rules allow the DHCP and DNS traffic, the firewalld rules dont, thus guests connected to libvirt's bridges can't acquire an IP address from DHCP, nor can they make DNS queries to the DNS server libvirt has setup on the host.
2) firewalld's higher level "rich rules" don't yet have the ability to configure the acceptance of forwarded traffic (traffic that is going somewhere beyond the host), so any traffic that needs to be forwarded is rejected by the public zone's default "reject" policy (which rejects all traffic in the zone not specifically allowed by the rules in the zone, whether that traffic is destined to be forwarded or locally received by the host).
libvirt can't send "direct" nftables rules (firewalld only supports that for iptables), so we can't solve this problem by just sending explicit nftables rules instead of explicit iptables rules (which, if it could be done, would place libvirt's rules in the same hook as firewalld's native rules, and thus eliminate the need for packets to be accepted by both libvirt's and firewalld's own rules).
However, we can take advantage of a quirk in firewalld zones that have a default policy of "accept" (meaning any packet that doesn't match a specific rule in the zone will be *accepted*) - this default accept will also accept forwarded traffic (not just traffic destined for the host).
Putting each network's bridge in a new zone called "libvirt" which has a default policy of accept will allow the forwarded traffic to pass, but the same default accept policy that fixes forwarded traffic also causes *all* traffic from guest to host to be accepted. To solve this new problem, we can take advantage of a new feature in firewalld (currently slated for firewalld-0.7.0) - priorities for rich rules - to add a low priority rule that rejects all local traffic (but leaves alone all forwarded traffic).
So, our new zone will start with a list of services that are allowed (dhcp, dns, tftp, and ssh to start, but configurable via any firewalld management application, or direct editing of the zone file in /etc/firewalld/zones/libvirt.xml), followed by a low priority <reject/> rule (to reject all other traffic from guest to host), and finally with a default policy of accept (to allow forwarded traffic)
After this patch, any network created by libvirt (when firewalld is enabled) will be added to the zone called "libvirt".
HOWEVER, even this could be problematic - since the libvirt zone uses a very new feature in firewalld which might not yet be present in the firewalld package on the host. The best we can do is put the zone file in place, and let firewalld try to load it - if firewalld doesn't support rule priorities, it will fail to load the zone file and log an error. Since libvirtd will also be attempting to set the zone of every new interface to "libvirt", if the libvirt zone failed to load, then the call to set the zone of an interface will also fail; this is acceptable because it's a transient problem, and the failure will help alert the user that they need to also update their firewalld package.
NB: This 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.0 or above. Although it's unfortunate that the behavior has to 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).
NB2: This patch does not check whether the firewalld backend is nftables or iptables; it behaves identically in either case, which is much less confusing than getting different behavior based on the configuration of some other package (firewalld).
NB3: firewalld zones can't normally be added to the runtime config of firewalld, so at package install/upgrade time we have to reload all of the firewalld permanent config for the new zone to be recognized. This is done with a call to "firewall-cmd --reload" during postinstall and postuninstall (for rpm-based distros; non-rpm distros will need to figure out a different method of triggering the reload). In the case that firewalld is inactive, firewall-cmd exits without doing anything (i.e. it doesn't start up firewalld.service if it's not already started).
Resolves: https://bugzilla.redhat.com/1638342 Creates-and-Resolves: https://bugzilla.redhat.com/1650320 Signed-off-by: Laine Stump <laine@laine.org> --- libvirt.spec.in | 16 ++++++++++++++++ src/network/Makefile.inc.am | 10 +++++++++- src/network/bridge_driver_linux.c | 9 +++++++++ src/network/libvirt.zone | 14 ++++++++++++++ 4 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 src/network/libvirt.zone
When the commit message is longer/larger than the patch ;-)
I end up with a lot of those somehow...
/me wonders how much of the above can or even should be described in https://libvirt.org/firewall.html.
Yeah, I should probably update that to include nftables and firewalld zones.
For those that really know, understand, care, etc. about firewalls getting to understand what role libvirt plays in things could be very helpful. Besides you have so much to say now and before you forget it again...
diff --git a/libvirt.spec.in b/libvirt.spec.in index b04cf53eb8..5217fee6ce 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -389,6 +389,8 @@ BuildRequires: rpcgen BuildRequires: libtirpc-devel %endif
+BuildRequires: firewalld-filesystem + Should this have a with_firewalld like others below?
Yeah, it should.
Of course I find/note the conditional with_firewalld check around line 84 and then the unconditional setting around line 131 to be odd, but for consistency...
Interesting. It looks like that came about because we used to force enable firewalld on all fedora and all RHEL >= 7, and when we dropped support for RHEL6 it just became unconditional. (The same thing happened to with_systemd and with_systemd_macros). It would maybe be better if that unconditional line was instead: %define with_firewalld 0%{!?_without_firewalld:1} so that it was possible to disable it in the build.
Provides: bundled(gnulib)
%description @@ -1352,6 +1354,16 @@ if [ -f %{_localstatedir}/lib/rpm-state/libvirt/restart ]; then fi rm -rf %{_localstatedir}/lib/rpm-state/libvirt || :
+%post daemon-driver-network +%if %{with_firewalld} + %firewalld_reload +%endif + +%postun daemon-driver-network +%if %{with_firewalld} + %firewalld_reload +%endif + %post daemon-config-network if test $1 -eq 1 && test ! -f %{_sysconfdir}/libvirt/qemu/networks/default.xml ; then # see if the network used by default network creates a conflict, @@ -1590,6 +1602,10 @@ exit 0 %attr(0755, root, root) %{_libexecdir}/libvirt_leaseshelper %{_libdir}/%{name}/connection-driver/libvirt_driver_network.so
+%if %{with_firewalld} +%{_prefix}/lib/firewalld/zones/libvirt.xml +%endif + So something is causing me to wonder if we have to do 'anything' here with respect to file security like other %attr's?
Well, according to the Fedora packaging guidelines, the default permissions for a file are 0644, and default owner:group is root:root: https://docs.fedoraproject.org/en-US/packaging-guidelines/#_file_permissions That will be the case unless defattr() is used to change the defaults, and Cole removed all use of defattr() in commit 90f9193cf. The other zone files in /usr/lib/firewalld/zones (installed by firewalld) have permissions 0644 and are owned by root:root, so I think this default is okay. (It looks like we only use attr() to set permissions/owner if it needs to be something other than 0644/root:root). (I know that may sound like I actually know what I'm talking about, but it's all the result of just looking up a few things based on a memory of defattr() being removed :-)

Since we're setting the zone anyway, it will be useful to allow setting a different (custom) zone for each network. This will be done by adding a "zone" attribute to the "bridge" element, e.g.: ... <bridge name='virbr0' zone='myzone'/> ... If a zone is specified in the config and it can't be honored, this will be an error. If no zone is specified and the default zone ("libvirt") can't be set, it will be ignored. (BTW, Federico Simoncelli proposed a similar patch 5 1.2 years (!!) ago, but I misunderstood its usefulness at first, and by the time I did we were both too busy to revisit it. libvirt's code has changed so much in the intervening time that it was simpler to just rewrite from scratch) Signed-off-by: Laine Stump <laine@laine.org> --- docs/formatnetwork.html.in | 17 +++++++++ docs/news.xml | 40 ++++++++++++++++++++++ docs/schemas/basictypes.rng | 6 ++++ docs/schemas/network.rng | 6 ++++ src/conf/network_conf.c | 14 ++++++-- src/conf/network_conf.h | 1 + src/network/bridge_driver_linux.c | 30 ++++++++++++---- tests/networkxml2xmlin/routed-network.xml | 2 +- tests/networkxml2xmlout/routed-network.xml | 2 +- 9 files changed, 107 insertions(+), 11 deletions(-) diff --git a/docs/formatnetwork.html.in b/docs/formatnetwork.html.in index 156cfae4ec..4aecdfe8e0 100644 --- a/docs/formatnetwork.html.in +++ b/docs/formatnetwork.html.in @@ -152,6 +152,23 @@ <span class="since">Since 1.2.11, requires kernel 3.17 or newer</span> </p> + + <p> + The optional <code>zone</code> attribute of + the <code>bridge</code> element is used to specify + the <a href="https://firewalld.org">firewalld</a> + zone for the bridge of a network with <code>forward</code> + mode of "nat", "route", "open", or one with + no <code>forward</code> specified. By default, the bridges + of all virtual networks with these forward modes are placed + in the firewalld zone named "libvirt", which permits + incoming DNS, DHCP, TFTP, and SSH to the host from guests on + the network. This behavior can be changed either by + modifying the libvirt zone (using firewalld management + tools), or by placing the network in a different zone (which + will also be managed using firewalld tools). + <span class="since">Since 5.0.0</span> + </p> </dd> <dt><code>mtu</code></dt> diff --git a/docs/news.xml b/docs/news.xml index 8c608cdc36..d894821ed5 100644 --- a/docs/news.xml +++ b/docs/news.xml @@ -58,6 +58,19 @@ trunking configuration. </description> </change> + <change> + <summary> + network: support setting a firewalld "zone" for all virtual network bridges + </summary> + <description> + All libvirt virtual networks with bridges managed by libvirt + (i.e. those with <code>forward</code> mode of "nat", + "route", "open", or no forward mode) will now be placed in a + special firewalld zone called "libvirt" by default, and the + zone of any network bridge can be changed using the <code>zone</code> + attribute of the network's <code>bridge</code> element. + </description> + </change> </section> <section title="Removed features"> <change> @@ -123,6 +136,33 @@ </change> </section> <section title="Bug fixes"> + <change> + <summary> + network: fix virtual networks on systems using firewalld+nftables + </summary> + <description> + Because of the transitional state of firewalld's new support + for nftables, not all iptables features required by libvirt + are yet available, so libvirt must continue to use iptables + for its own packet filtering rules even when the firewalld + backend is set to use nftables, but due to the way iptables + support is implemented in kernels using nftables (iptables + rules are converted to nftables rules and processed in a + separate hook from the native nftables rules), guest + networking was broken on hosts with firewalld configured to + use nftables as the backend. This has been fixed by putting + libvirt-managed bridges in their own firewalld zone, so that + guest traffic can be forwarded beyond the host, and so that + host services can be exposed to guests on the virtual + network without opening up those same services to the rest + of the physical network. This means that host access from + virtual machines is no longer controlled by the firewalld + default zone (usually "public"), but rather by the new + firewalld zone called "libvirt" (unless configured otherwise + using the new <code>zone</code> attribute of the network + <code>bridge</code> element). + </description> + </change> </section> </release> <release version="v4.10.0" date="2018-12-03"> diff --git a/docs/schemas/basictypes.rng b/docs/schemas/basictypes.rng index 9a63720ff7..9b3dcad4a5 100644 --- a/docs/schemas/basictypes.rng +++ b/docs/schemas/basictypes.rng @@ -279,6 +279,12 @@ </data> </define> + <define name="zoneName"> + <data type="string"> + <param name="pattern">[a-zA-Z0-9_\-]+</param> + </data> + </define> + <define name="filePath"> <data type="string"> <param name="pattern">.+</param> diff --git a/docs/schemas/network.rng b/docs/schemas/network.rng index f37c422bf3..2a6e3358fd 100644 --- a/docs/schemas/network.rng +++ b/docs/schemas/network.rng @@ -58,6 +58,12 @@ </attribute> </optional> + <optional> + <attribute name="zone"> + <ref name="zoneName"/> + </attribute> + </optional> + <optional> <attribute name="stp"> <ref name="virOnOff"/> diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index e035d8aba7..b09cb1dae2 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -203,6 +203,7 @@ virNetworkDefFree(virNetworkDefPtr def) VIR_FREE(def->name); VIR_FREE(def->bridge); + VIR_FREE(def->bridgeZone); VIR_FREE(def->domain); virNetworkForwardDefClear(&def->forward); @@ -1684,6 +1685,7 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt) /* Parse bridge information */ def->bridge = virXPathString("string(./bridge[1]/@name)", ctxt); + def->bridgeZone = virXPathString("string(./bridge[1]/@zone)", ctxt); stp = virXPathString("string(./bridge[1]/@stp)", ctxt); def->stp = (stp && STREQ(stp, "off")) ? false : true; @@ -1920,6 +1922,13 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt) def->name); goto error; } + if (def->bridgeZone) { + virReportError(VIR_ERR_XML_ERROR, + _("bridge zone not allowed in %s mode (network '%s')"), + virNetworkForwardTypeToString(def->forward.type), + def->name); + goto error; + } if (def->macTableManager) { virReportError(VIR_ERR_XML_ERROR, _("bridge macTableManager setting not allowed " @@ -1931,9 +1940,9 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt) ATTRIBUTE_FALLTHROUGH; case VIR_NETWORK_FORWARD_BRIDGE: - if (def->delay || stp) { + if (def->delay || stp || def->bridgeZone) { virReportError(VIR_ERR_XML_ERROR, - _("bridge delay/stp options only allowed in " + _("bridge delay/stp/zone options only allowed in " "route, nat, and isolated mode, not in %s " "(network '%s')"), virNetworkForwardTypeToString(def->forward.type), @@ -2508,6 +2517,7 @@ virNetworkDefFormatBuf(virBufferPtr buf, if (hasbridge || def->bridge || def->macTableManager) { virBufferAddLit(buf, "<bridge"); virBufferEscapeString(buf, " name='%s'", def->bridge); + virBufferEscapeString(buf, " zone='%s'", def->bridgeZone); if (hasbridge) virBufferAsprintf(buf, " stp='%s' delay='%ld'", def->stp ? "on" : "off", def->delay); diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index c630674300..69ee8d7f2a 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -235,6 +235,7 @@ struct _virNetworkDef { int connections; /* # of guest interfaces connected to this network */ char *bridge; /* Name of bridge device */ + char *bridgeZone; /* name of firewalld zone for bridge (default "libvirt" */ int macTableManager; /* enum virNetworkBridgeMACTableManager */ char *domain; int domainLocalOnly; /* enum virTristateBool: yes disables dns forwarding */ diff --git a/src/network/bridge_driver_linux.c b/src/network/bridge_driver_linux.c index a32f19bcf0..8c585594d1 100644 --- a/src/network/bridge_driver_linux.c +++ b/src/network/bridge_driver_linux.c @@ -639,13 +639,29 @@ int networkAddFirewallRules(virNetworkDefPtr def) virFirewallPtr fw = NULL; int ret = -1; - - /* if firewalld is active, try to set the default "libvirt" zone, - * but ignore failure, since the version of firewalld on the host - * may have failed to load the libvirt zone - */ - if (virFirewallDStatus() >= 0) - ignore_value(virFirewallDInterfaceSetZone(def->bridge, "libvirt")); + if (!def->bridgeZone) { + /* if firewalld is active and no zone has been explicitly set + * in the config, try to set the default "libvirt" zone, but + * ignore failure, since the version of firewalld on the host + * may have failed to load the libvirt zone + */ + if (virFirewallDStatus() >= 0) + ignore_value(virFirewallDInterfaceSetZone(def->bridge, "libvirt")); + + } else { + /* if a zone has been specified, fail/log an error if we can't + * honor it + */ + if (virFirewallDStatus() < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("zone %s requested for network %s " + "but firewalld is not active"), + def->bridgeZone, def->name); + goto cleanup; + } + if (virFirewallDInterfaceSetZone(def->bridge, def->bridgeZone) < 0) + goto cleanup; + } fw = virFirewallNew(); diff --git a/tests/networkxml2xmlin/routed-network.xml b/tests/networkxml2xmlin/routed-network.xml index ab5e15b1f6..fce01df132 100644 --- a/tests/networkxml2xmlin/routed-network.xml +++ b/tests/networkxml2xmlin/routed-network.xml @@ -1,7 +1,7 @@ <network> <name>local</name> <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid> - <bridge name="virbr1"/> + <bridge name="virbr1" zone="myzone"/> <mac address='12:34:56:78:9A:BC'/> <forward mode="route" dev="eth1"/> <ip address="192.168.122.1" netmask="255.255.255.0"> diff --git a/tests/networkxml2xmlout/routed-network.xml b/tests/networkxml2xmlout/routed-network.xml index 81abf06e9f..2e13cf4ffa 100644 --- a/tests/networkxml2xmlout/routed-network.xml +++ b/tests/networkxml2xmlout/routed-network.xml @@ -4,7 +4,7 @@ <forward dev='eth1' mode='route'> <interface dev='eth1'/> </forward> - <bridge name='virbr1' stp='on' delay='0'/> + <bridge name='virbr1' zone='myzone' stp='on' delay='0'/> <mac address='12:34:56:78:9a:bc'/> <ip address='192.168.122.1' netmask='255.255.255.0'> </ip> -- 2.20.1

On Wed, Jan 09, 2019 at 09:57:37PM -0500, Laine Stump wrote:
Since we're setting the zone anyway, it will be useful to allow setting a different (custom) zone for each network. This will be done by adding a "zone" attribute to the "bridge" element, e.g.:
... <bridge name='virbr0' zone='myzone'/> ...
If a zone is specified in the config and it can't be honored, this will be an error. If no zone is specified and the default zone ("libvirt") can't be set, it will be ignored.
(BTW, Federico Simoncelli proposed a similar patch 5 1.2 years (!!) ago, but I misunderstood its usefulness at first, and by the time I did we were both too busy to revisit it. libvirt's code has changed so much in the intervening time that it was simpler to just rewrite from scratch)
Should this paragraph go below the --- ?
Signed-off-by: Laine Stump <laine@laine.org> --- docs/formatnetwork.html.in | 17 +++++++++ docs/news.xml | 40 ++++++++++++++++++++++ docs/schemas/basictypes.rng | 6 ++++ docs/schemas/network.rng | 6 ++++ src/conf/network_conf.c | 14 ++++++-- src/conf/network_conf.h | 1 + src/network/bridge_driver_linux.c | 30 ++++++++++++---- tests/networkxml2xmlin/routed-network.xml | 2 +- tests/networkxml2xmlout/routed-network.xml | 2 +- 9 files changed, 107 insertions(+), 11 deletions(-)
diff --git a/docs/formatnetwork.html.in b/docs/formatnetwork.html.in index 156cfae4ec..4aecdfe8e0 100644 --- a/docs/formatnetwork.html.in +++ b/docs/formatnetwork.html.in @@ -152,6 +152,23 @@ <span class="since">Since 1.2.11, requires kernel 3.17 or newer</span> </p> + + <p> + The optional <code>zone</code> attribute of + the <code>bridge</code> element is used to specify + the <a href="https://firewalld.org">firewalld</a> + zone for the bridge of a network with <code>forward</code> + mode of "nat", "route", "open", or one with + no <code>forward</code> specified. By default, the bridges + of all virtual networks with these forward modes are placed + in the firewalld zone named "libvirt", which permits + incoming DNS, DHCP, TFTP, and SSH to the host from guests on + the network. This behavior can be changed either by + modifying the libvirt zone (using firewalld management + tools), or by placing the network in a different zone (which + will also be managed using firewalld tools). + <span class="since">Since 5.0.0</span> + </p> </dd>
<dt><code>mtu</code></dt> diff --git a/docs/news.xml b/docs/news.xml index 8c608cdc36..d894821ed5 100644 --- a/docs/news.xml +++ b/docs/news.xml @@ -58,6 +58,19 @@ trunking configuration. </description> </change> + <change> + <summary> + network: support setting a firewalld "zone" for all virtual network bridges + </summary> + <description> + All libvirt virtual networks with bridges managed by libvirt + (i.e. those with <code>forward</code> mode of "nat", + "route", "open", or no forward mode) will now be placed in a + special firewalld zone called "libvirt" by default, and the + zone of any network bridge can be changed using the <code>zone</code> + attribute of the network's <code>bridge</code> element. + </description> + </change> </section> <section title="Removed features"> <change> @@ -123,6 +136,33 @@ </change> </section> <section title="Bug fixes"> + <change> + <summary> + network: fix virtual networks on systems using firewalld+nftables + </summary> + <description> + Because of the transitional state of firewalld's new support + for nftables, not all iptables features required by libvirt + are yet available, so libvirt must continue to use iptables + for its own packet filtering rules even when the firewalld + backend is set to use nftables, but due to the way iptables + support is implemented in kernels using nftables (iptables + rules are converted to nftables rules and processed in a + separate hook from the native nftables rules), guest + networking was broken on hosts with firewalld configured to + use nftables as the backend. This has been fixed by putting + libvirt-managed bridges in their own firewalld zone, so that + guest traffic can be forwarded beyond the host, and so that + host services can be exposed to guests on the virtual + network without opening up those same services to the rest + of the physical network. This means that host access from + virtual machines is no longer controlled by the firewalld + default zone (usually "public"), but rather by the new + firewalld zone called "libvirt" (unless configured otherwise + using the new <code>zone</code> attribute of the network + <code>bridge</code> element). + </description> + </change>
Either this change belongs in the previous patch, or we should put both changes in a patch #6 separate from the code.
</section> </release> <release version="v4.10.0" date="2018-12-03"> diff --git a/docs/schemas/basictypes.rng b/docs/schemas/basictypes.rng index 9a63720ff7..9b3dcad4a5 100644 --- a/docs/schemas/basictypes.rng +++ b/docs/schemas/basictypes.rng @@ -279,6 +279,12 @@ </data> </define>
+ <define name="zoneName"> + <data type="string"> + <param name="pattern">[a-zA-Z0-9_\-]+</param> + </data> + </define> + <define name="filePath"> <data type="string"> <param name="pattern">.+</param> diff --git a/docs/schemas/network.rng b/docs/schemas/network.rng index f37c422bf3..2a6e3358fd 100644 --- a/docs/schemas/network.rng +++ b/docs/schemas/network.rng @@ -58,6 +58,12 @@ </attribute> </optional>
+ <optional> + <attribute name="zone"> + <ref name="zoneName"/> + </attribute> + </optional> + <optional> <attribute name="stp"> <ref name="virOnOff"/> diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index e035d8aba7..b09cb1dae2 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -203,6 +203,7 @@ virNetworkDefFree(virNetworkDefPtr def)
VIR_FREE(def->name); VIR_FREE(def->bridge); + VIR_FREE(def->bridgeZone); VIR_FREE(def->domain);
virNetworkForwardDefClear(&def->forward); @@ -1684,6 +1685,7 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt)
/* Parse bridge information */ def->bridge = virXPathString("string(./bridge[1]/@name)", ctxt); + def->bridgeZone = virXPathString("string(./bridge[1]/@zone)", ctxt); stp = virXPathString("string(./bridge[1]/@stp)", ctxt); def->stp = (stp && STREQ(stp, "off")) ? false : true;
@@ -1920,6 +1922,13 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt) def->name); goto error; } + if (def->bridgeZone) { + virReportError(VIR_ERR_XML_ERROR, + _("bridge zone not allowed in %s mode (network '%s')"), + virNetworkForwardTypeToString(def->forward.type), + def->name); + goto error; + } if (def->macTableManager) { virReportError(VIR_ERR_XML_ERROR, _("bridge macTableManager setting not allowed " @@ -1931,9 +1940,9 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt) ATTRIBUTE_FALLTHROUGH;
case VIR_NETWORK_FORWARD_BRIDGE: - if (def->delay || stp) { + if (def->delay || stp || def->bridgeZone) { virReportError(VIR_ERR_XML_ERROR, - _("bridge delay/stp options only allowed in " + _("bridge delay/stp/zone options only allowed in " "route, nat, and isolated mode, not in %s " "(network '%s')"), virNetworkForwardTypeToString(def->forward.type), @@ -2508,6 +2517,7 @@ virNetworkDefFormatBuf(virBufferPtr buf, if (hasbridge || def->bridge || def->macTableManager) { virBufferAddLit(buf, "<bridge"); virBufferEscapeString(buf, " name='%s'", def->bridge); + virBufferEscapeString(buf, " zone='%s'", def->bridgeZone); if (hasbridge) virBufferAsprintf(buf, " stp='%s' delay='%ld'", def->stp ? "on" : "off", def->delay); diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index c630674300..69ee8d7f2a 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -235,6 +235,7 @@ struct _virNetworkDef { int connections; /* # of guest interfaces connected to this network */
char *bridge; /* Name of bridge device */ + char *bridgeZone; /* name of firewalld zone for bridge (default "libvirt" */ int macTableManager; /* enum virNetworkBridgeMACTableManager */ char *domain; int domainLocalOnly; /* enum virTristateBool: yes disables dns forwarding */ diff --git a/src/network/bridge_driver_linux.c b/src/network/bridge_driver_linux.c index a32f19bcf0..8c585594d1 100644 --- a/src/network/bridge_driver_linux.c +++ b/src/network/bridge_driver_linux.c @@ -639,13 +639,29 @@ int networkAddFirewallRules(virNetworkDefPtr def) virFirewallPtr fw = NULL; int ret = -1;
- - /* if firewalld is active, try to set the default "libvirt" zone, - * but ignore failure, since the version of firewalld on the host - * may have failed to load the libvirt zone - */ - if (virFirewallDStatus() >= 0) - ignore_value(virFirewallDInterfaceSetZone(def->bridge, "libvirt")); + if (!def->bridgeZone) { + /* if firewalld is active and no zone has been explicitly set + * in the config, try to set the default "libvirt" zone, but + * ignore failure, since the version of firewalld on the host + * may have failed to load the libvirt zone + */ + if (virFirewallDStatus() >= 0) + ignore_value(virFirewallDInterfaceSetZone(def->bridge, "libvirt")); + + } else { + /* if a zone has been specified, fail/log an error if we can't + * honor it + */ + if (virFirewallDStatus() < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("zone %s requested for network %s " + "but firewalld is not active"), + def->bridgeZone, def->name); + goto cleanup; + } + if (virFirewallDInterfaceSetZone(def->bridge, def->bridgeZone) < 0) + goto cleanup; + }
fw = virFirewallNew();
diff --git a/tests/networkxml2xmlin/routed-network.xml b/tests/networkxml2xmlin/routed-network.xml index ab5e15b1f6..fce01df132 100644 --- a/tests/networkxml2xmlin/routed-network.xml +++ b/tests/networkxml2xmlin/routed-network.xml @@ -1,7 +1,7 @@ <network> <name>local</name> <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid> - <bridge name="virbr1"/> + <bridge name="virbr1" zone="myzone"/> <mac address='12:34:56:78:9A:BC'/> <forward mode="route" dev="eth1"/> <ip address="192.168.122.1" netmask="255.255.255.0"> diff --git a/tests/networkxml2xmlout/routed-network.xml b/tests/networkxml2xmlout/routed-network.xml index 81abf06e9f..2e13cf4ffa 100644 --- a/tests/networkxml2xmlout/routed-network.xml +++ b/tests/networkxml2xmlout/routed-network.xml @@ -4,7 +4,7 @@ <forward dev='eth1' mode='route'> <interface dev='eth1'/> </forward> - <bridge name='virbr1' stp='on' delay='0'/> + <bridge name='virbr1' zone='myzone' stp='on' delay='0'/> <mac address='12:34:56:78:9a:bc'/> <ip address='192.168.122.1' netmask='255.255.255.0'> </ip> -- 2.20.1
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

On 1/9/19 9:57 PM, Laine Stump wrote:
Since we're setting the zone anyway, it will be useful to allow setting a different (custom) zone for each network. This will be done by adding a "zone" attribute to the "bridge" element, e.g.:
... <bridge name='virbr0' zone='myzone'/> ...
If a zone is specified in the config and it can't be honored, this will be an error. If no zone is specified and the default zone ("libvirt") can't be set, it will be ignored.
(BTW, Federico Simoncelli proposed a similar patch 5 1.2 years (!!) ago, but I misunderstood its usefulness at first, and by the time I did we were both too busy to revisit it. libvirt's code has changed so much in the intervening time that it was simpler to just rewrite from scratch)
Signed-off-by: Laine Stump <laine@laine.org> --- docs/formatnetwork.html.in | 17 +++++++++ docs/news.xml | 40 ++++++++++++++++++++++ docs/schemas/basictypes.rng | 6 ++++ docs/schemas/network.rng | 6 ++++ src/conf/network_conf.c | 14 ++++++-- src/conf/network_conf.h | 1 + src/network/bridge_driver_linux.c | 30 ++++++++++++---- tests/networkxml2xmlin/routed-network.xml | 2 +- tests/networkxml2xmlout/routed-network.xml | 2 +- 9 files changed, 107 insertions(+), 11 deletions(-)
The news.xml would be in it's own/last patch rather than included in with the others. Whether you split out the two entries into separate patches in the "correct order" is up to you. Having them in one patch at the end is also fine. I think the splitting just makes it easier to determine at a glance whether changes are at least described in the news.xml file. Perhaps makes the end of a release search through git history easier to weed out commit series that did adjust release notes and thus easier to find those that didn't and update appropriately.
diff --git a/docs/formatnetwork.html.in b/docs/formatnetwork.html.in index 156cfae4ec..4aecdfe8e0 100644 --- a/docs/formatnetwork.html.in +++ b/docs/formatnetwork.html.in @@ -152,6 +152,23 @@ <span class="since">Since 1.2.11, requires kernel 3.17 or newer</span> </p> + + <p> + The optional <code>zone</code> attribute of + the <code>bridge</code> element is used to specify + the <a href="https://firewalld.org">firewalld</a> + zone for the bridge of a network with <code>forward</code> + mode of "nat", "route", "open", or one with + no <code>forward</code> specified. By default, the bridges + of all virtual networks with these forward modes are placed + in the firewalld zone named "libvirt", which permits + incoming DNS, DHCP, TFTP, and SSH to the host from guests on + the network. This behavior can be changed either by + modifying the libvirt zone (using firewalld management + tools), or by placing the network in a different zone (which
tools) or (e.g. no extra comma)
+ will also be managed using firewalld tools). + <span class="since">Since 5.0.0</span>
5.1.0
+ </p> </dd>
<dt><code>mtu</code></dt> diff --git a/docs/news.xml b/docs/news.xml index 8c608cdc36..d894821ed5 100644 --- a/docs/news.xml +++ b/docs/news.xml @@ -58,6 +58,19 @@ trunking configuration. </description> </change> + <change> + <summary> + network: support setting a firewalld "zone" for all virtual network bridges + </summary> + <description> + All libvirt virtual networks with bridges managed by libvirt + (i.e. those with <code>forward</code> mode of "nat", + "route", "open", or no forward mode) will now be placed in a + special firewalld zone called "libvirt" by default, and the
...by default. The ...
+ zone of any network bridge can be changed using the <code>zone</code> + attribute of the network's <code>bridge</code> element. + </description> + </change> </section> <section title="Removed features"> <change> @@ -123,6 +136,33 @@ </change> </section> <section title="Bug fixes">
Really, "just" a bug fix. It's perhaps more a new feature since we're adding a new zone file and altering libvirt.spec.in... Certainly at least an improvement to have filters working properly w/ nftables. FWIW: Rather than 3 really, long sentences...
+ <change> + <summary> + network: fix virtual networks on systems using firewalld+nftables + </summary> + <description> + Because of the transitional state of firewalld's new support + for nftables, not all iptables features required by libvirt + are yet available, so libvirt must continue to use iptables
available. So,
+ for its own packet filtering rules even when the firewalld + backend is set to use nftables, but due to the way iptables
nftables. However, due
+ support is implemented in kernels using nftables (iptables
kernel's (or due to the way the kernel implements iptables support)
+ rules are converted to nftables rules and processed in a + separate hook from the native nftables rules), guest + networking was broken on hosts with firewalld configured to + use nftables as the backend. This has been fixed by putting + libvirt-managed bridges in their own firewalld zone, so that + guest traffic can be forwarded beyond the host, and so that
host and (e.g. no need for the comma)
+ host services can be exposed to guests on the virtual + network without opening up those same services to the rest + of the physical network. This means that host access from + virtual machines is no longer controlled by the firewalld + default zone (usually "public"), but rather by the new
(usually "public"). Instead virtual machine access will now be controlled by the new "libvirt" firewalld zone, unless the virtual network bridge is configured to use a specific zone file. NB: I don't think using <code></code> around zone and bridge is necessary here since it's used above when defining/describing the virtual network bridge change.
+ firewalld zone called "libvirt" (unless configured otherwise + using the new <code>zone</code> attribute of the network + <code>bridge</code> element). + </description> + </change>
Order wise, this last part certainly exhibits why this was added with the zone attribute adjustment above.
</section> </release> <release version="v4.10.0" date="2018-12-03"> diff --git a/docs/schemas/basictypes.rng b/docs/schemas/basictypes.rng index 9a63720ff7..9b3dcad4a5 100644 --- a/docs/schemas/basictypes.rng +++ b/docs/schemas/basictypes.rng @@ -279,6 +279,12 @@ </data> </define>
+ <define name="zoneName"> + <data type="string"> + <param name="pattern">[a-zA-Z0-9_\-]+</param> + </data> + </define> + <define name="filePath"> <data type="string"> <param name="pattern">.+</param> diff --git a/docs/schemas/network.rng b/docs/schemas/network.rng index f37c422bf3..2a6e3358fd 100644 --- a/docs/schemas/network.rng +++ b/docs/schemas/network.rng @@ -58,6 +58,12 @@ </attribute> </optional>
+ <optional> + <attribute name="zone"> + <ref name="zoneName"/> + </attribute> + </optional> + <optional> <attribute name="stp"> <ref name="virOnOff"/> diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index e035d8aba7..b09cb1dae2 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -203,6 +203,7 @@ virNetworkDefFree(virNetworkDefPtr def)
VIR_FREE(def->name); VIR_FREE(def->bridge); + VIR_FREE(def->bridgeZone); VIR_FREE(def->domain);
virNetworkForwardDefClear(&def->forward); @@ -1684,6 +1685,7 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt)
/* Parse bridge information */ def->bridge = virXPathString("string(./bridge[1]/@name)", ctxt); + def->bridgeZone = virXPathString("string(./bridge[1]/@zone)", ctxt); stp = virXPathString("string(./bridge[1]/@stp)", ctxt); def->stp = (stp && STREQ(stp, "off")) ? false : true;
@@ -1920,6 +1922,13 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt) def->name); goto error; } + if (def->bridgeZone) { + virReportError(VIR_ERR_XML_ERROR, + _("bridge zone not allowed in %s mode (network '%s')"), + virNetworkForwardTypeToString(def->forward.type), + def->name); + goto error; + } if (def->macTableManager) { virReportError(VIR_ERR_XML_ERROR, _("bridge macTableManager setting not allowed " @@ -1931,9 +1940,9 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt) ATTRIBUTE_FALLTHROUGH;
case VIR_NETWORK_FORWARD_BRIDGE: - if (def->delay || stp) { + if (def->delay || stp || def->bridgeZone) { virReportError(VIR_ERR_XML_ERROR, - _("bridge delay/stp options only allowed in " + _("bridge delay/stp/zone options only allowed in " "route, nat, and isolated mode, not in %s " "(network '%s')"), virNetworkForwardTypeToString(def->forward.type), @@ -2508,6 +2517,7 @@ virNetworkDefFormatBuf(virBufferPtr buf, if (hasbridge || def->bridge || def->macTableManager) { virBufferAddLit(buf, "<bridge"); virBufferEscapeString(buf, " name='%s'", def->bridge); + virBufferEscapeString(buf, " zone='%s'", def->bridgeZone); if (hasbridge) virBufferAsprintf(buf, " stp='%s' delay='%ld'", def->stp ? "on" : "off", def->delay); diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index c630674300..69ee8d7f2a 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -235,6 +235,7 @@ struct _virNetworkDef { int connections; /* # of guest interfaces connected to this network */
char *bridge; /* Name of bridge device */ + char *bridgeZone; /* name of firewalld zone for bridge (default "libvirt" */
nit: while "libvirt" is the de facto default, it's not placed into the XML so that on output "bridge='libvirt'" gets listed.... It's an implementation detail described in formatnetwork.html.in. Still if you keep the above comment close the (
int macTableManager; /* enum virNetworkBridgeMACTableManager */ char *domain; int domainLocalOnly; /* enum virTristateBool: yes disables dns forwarding */ diff --git a/src/network/bridge_driver_linux.c b/src/network/bridge_driver_linux.c index a32f19bcf0..8c585594d1 100644 --- a/src/network/bridge_driver_linux.c +++ b/src/network/bridge_driver_linux.c @@ -639,13 +639,29 @@ int networkAddFirewallRules(virNetworkDefPtr def) virFirewallPtr fw = NULL; int ret = -1;
- - /* if firewalld is active, try to set the default "libvirt" zone, - * but ignore failure, since the version of firewalld on the host - * may have failed to load the libvirt zone - */ - if (virFirewallDStatus() >= 0) - ignore_value(virFirewallDInterfaceSetZone(def->bridge, "libvirt")); + if (!def->bridgeZone) { + /* if firewalld is active and no zone has been explicitly set + * in the config, try to set the default "libvirt" zone, but + * ignore failure, since the version of firewalld on the host + * may have failed to load the libvirt zone + */ + if (virFirewallDStatus() >= 0) + ignore_value(virFirewallDInterfaceSetZone(def->bridge, "libvirt")); + + } else { + /* if a zone has been specified, fail/log an error if we can't + * honor it + */ + if (virFirewallDStatus() < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("zone %s requested for network %s "
zone '%s' network '%s',
+ "but firewalld is not active"), + def->bridgeZone, def->name);
zone is for bridge for network right? e.g. shouldn't def->bridge be printed as well? or alternatively; otherwise, using example below it's "zone 'myzone' requested for network 'local', but firewalld is not active" John
+ goto cleanup; + } + if (virFirewallDInterfaceSetZone(def->bridge, def->bridgeZone) < 0) + goto cleanup; + }
fw = virFirewallNew();
diff --git a/tests/networkxml2xmlin/routed-network.xml b/tests/networkxml2xmlin/routed-network.xml index ab5e15b1f6..fce01df132 100644 --- a/tests/networkxml2xmlin/routed-network.xml +++ b/tests/networkxml2xmlin/routed-network.xml @@ -1,7 +1,7 @@ <network> <name>local</name> <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid> - <bridge name="virbr1"/> + <bridge name="virbr1" zone="myzone"/> <mac address='12:34:56:78:9A:BC'/> <forward mode="route" dev="eth1"/> <ip address="192.168.122.1" netmask="255.255.255.0"> diff --git a/tests/networkxml2xmlout/routed-network.xml b/tests/networkxml2xmlout/routed-network.xml index 81abf06e9f..2e13cf4ffa 100644 --- a/tests/networkxml2xmlout/routed-network.xml +++ b/tests/networkxml2xmlout/routed-network.xml @@ -4,7 +4,7 @@ <forward dev='eth1' mode='route'> <interface dev='eth1'/> </forward> - <bridge name='virbr1' stp='on' delay='0'/> + <bridge name='virbr1' zone='myzone' stp='on' delay='0'/> <mac address='12:34:56:78:9a:bc'/> <ip address='192.168.122.1' netmask='255.255.255.0'> </ip>
participants (4)
-
Daniel P. Berrangé
-
Eric Garver
-
John Ferlan
-
Laine Stump