Blame SOURCES/libvirt-util-synchronize-with-firewalld-before-we-start-calling-iptables-directly.patch

7548c0
From dc8cf11686c075166a3029e974a6caeefe521d75 Mon Sep 17 00:00:00 2001
7548c0
Message-Id: <dc8cf11686c075166a3029e974a6caeefe521d75@dist-git>
7548c0
From: Laine Stump <laine@redhat.com>
7548c0
Date: Fri, 15 Jan 2021 22:51:50 -0500
7548c0
Subject: [PATCH] util: synchronize with firewalld before we start calling
7548c0
 iptables directly
7548c0
7548c0
When it is starting up, firewalld will delete all existing iptables
7548c0
rules and chains before adding its own rules. If libvirtd were to try
7548c0
to directly add iptables rules during the time before firewalld has
7548c0
finished initializing, firewalld would end up deleting the rules that
7548c0
libvirtd has just added.
7548c0
7548c0
Currently this isn't a problem, since libvirtd only adds iptables
7548c0
rules via the firewalld "passthrough command" API, and so firewalld is
7548c0
able to properly serialize everything. However, we will soon be
7548c0
changing libvirtd to add its iptables and ebtables rules by directly
7548c0
calling iptables/ebtables rather than via firewalld, thus removing the
7548c0
serialization of libvirtd adding rules vs. firewalld deleting rules.
7548c0
7548c0
This will especially apparent (if we don't fix it in advance, as this
7548c0
patch does) when libvirtd is responding to the dbus NameOwnerChanged
7548c0
event, which is used to learn when firewalld has been restarted. In
7548c0
that case, dbus sends the event before firewalld has been able to
7548c0
complete its initialization, so when libvirt responds to the event by
7548c0
adding back its iptables rules (with direct calls to
7548c0
/usr/bin/iptables), some of those rules are added before firewalld has
7548c0
a chance to do its "remove everything" startup protocol. The usual
7548c0
result of this is that libvirt will successfully add its private
7548c0
chains (e.g. LIBVIRT_INP, etc), but then fail when it tries to add a
7548c0
rule jumping to one of those chains (because in the interim, firewalld
7548c0
has deleted the new chains).
7548c0
7548c0
The solution is for libvirt to preface it's direct calling to iptables
7548c0
with a iptables command sent via firewalld's passthrough command
7548c0
API. Since commands sent to firewalld are completed synchronously, and
7548c0
since firewalld won't service them until it has completed its own
7548c0
initialization, this will assure that by the time libvirt starts
7548c0
calling iptables to add rules, that firewalld will not be following up
7548c0
by deleting any of those rules.
7548c0
7548c0
To minimize the amount of extra overhead, we request the simplest
7548c0
iptables command possible: "iptables -V" (and aside from logging a
7548c0
debug message, we ignore the result, for good measure).
7548c0
7548c0
(This patch is being done *before* the patch that switches to calling
7548c0
iptables directly, so that everything will function properly with any
7548c0
fractional part of the series applied).
7548c0
7548c0
https://bugzilla.redhat.com/1607929
7548c0
7548c0
Signed-off-by: Laine Stump <laine@redhat.com>
7548c0
Reviewed-by: Daniel Henrique Barboza <danielhb413@gmail.com>
7548c0
(cherry picked from commit 070690538a1ed301b004c542d94b13ee9bffc9d6)
7548c0
7548c0
Conflicts: src/util/viriptables.c:
7548c0
    one line of code in context moved during g_autoptr conversion.
7548c0
Signed-off-by: Laine Stump <laine@redhat.com>
7548c0
Message-Id: <20210116035151.1066734-8-laine@redhat.com>
7548c0
Reviewed-by: Jiri Denemark <jdenemar@redhat.com>
7548c0
---
7548c0
 src/libvirt_private.syms |  1 +
7548c0
 src/util/virfirewall.c   | 30 ++++++++++++++++++++++++++++++
7548c0
 src/util/virfirewall.h   |  2 ++
7548c0
 src/util/viriptables.c   |  7 +++++++
7548c0
 4 files changed, 40 insertions(+)
7548c0
7548c0
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
7548c0
index edc53ce899..9d87e2a27b 100644
7548c0
--- a/src/libvirt_private.syms
7548c0
+++ b/src/libvirt_private.syms
7548c0
@@ -2080,6 +2080,7 @@ virFileCacheSetPriv;
7548c0
 # util/virfirewall.h
7548c0
 virFirewallAddRuleFull;
7548c0
 virFirewallApply;
7548c0
+virFirewallBackendSynchronize;
7548c0
 virFirewallFree;
7548c0
 virFirewallNew;
7548c0
 virFirewallRemoveRule;
7548c0
diff --git a/src/util/virfirewall.c b/src/util/virfirewall.c
7548c0
index 520d515c11..66d20d3f17 100644
7548c0
--- a/src/util/virfirewall.c
7548c0
+++ b/src/util/virfirewall.c
7548c0
@@ -653,6 +653,36 @@ virFirewallApplyRuleFirewallD(virFirewallRulePtr rule,
7548c0
     return virFirewallDApplyRule(rule->layer, rule->args, rule->argsLen, ignoreErrors, output);
7548c0
 }
7548c0
 
7548c0
+
7548c0
+void
7548c0
+virFirewallBackendSynchronize(void)
7548c0
+{
7548c0
+    const char *arg = "-V";
7548c0
+    g_autofree char *output = NULL;
7548c0
+
7548c0
+    switch (currentBackend) {
7548c0
+    case VIR_FIREWALL_BACKEND_DIRECT:
7548c0
+        /* nobody to synchronize with */
7548c0
+        break;
7548c0
+    case VIR_FIREWALL_BACKEND_FIREWALLD:
7548c0
+        /* Send a simple rule via firewalld's passthrough iptables
7548c0
+         * command so that we'll be sure firewalld has fully
7548c0
+         * initialized and caught up with its internal queue of
7548c0
+         * iptables commands. Waiting for this will prevent our own
7548c0
+         * directly-executed iptables commands from being run while
7548c0
+         * firewalld is still initializing.
7548c0
+         */
7548c0
+        ignore_value(virFirewallDApplyRule(VIR_FIREWALL_LAYER_IPV4,
7548c0
+                                           (char **)&arg, 1, true, &output));
7548c0
+        VIR_DEBUG("Result of 'iptables -V' via firewalld: %s", NULLSTR(output));
7548c0
+        break;
7548c0
+    case VIR_FIREWALL_BACKEND_AUTOMATIC:
7548c0
+    case VIR_FIREWALL_BACKEND_LAST:
7548c0
+        break;
7548c0
+    }
7548c0
+}
7548c0
+
7548c0
+
7548c0
 static int
7548c0
 virFirewallApplyRule(virFirewallPtr firewall,
7548c0
                      virFirewallRulePtr rule,
7548c0
diff --git a/src/util/virfirewall.h b/src/util/virfirewall.h
7548c0
index fda3cdec01..3db0864380 100644
7548c0
--- a/src/util/virfirewall.h
7548c0
+++ b/src/util/virfirewall.h
7548c0
@@ -111,4 +111,6 @@ void virFirewallStartRollback(virFirewallPtr firewall,
7548c0
 
7548c0
 int virFirewallApply(virFirewallPtr firewall);
7548c0
 
7548c0
+void virFirewallBackendSynchronize(void);
7548c0
+
7548c0
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(virFirewall, virFirewallFree);
7548c0
diff --git a/src/util/viriptables.c b/src/util/viriptables.c
7548c0
index 6b3a025880..41544b7f36 100644
7548c0
--- a/src/util/viriptables.c
7548c0
+++ b/src/util/viriptables.c
7548c0
@@ -154,6 +154,13 @@ iptablesSetupPrivateChains(virFirewallLayer layer)
7548c0
 
7548c0
     fw = virFirewallNew();
7548c0
 
7548c0
+    /* When the backend is firewalld, we need to make sure that
7548c0
+     * firewalld has been fully started and completed its
7548c0
+     * initialization, otherwise firewalld might delete our rules soon
7548c0
+     * after we add them!
7548c0
+     */
7548c0
+    virFirewallBackendSynchronize();
7548c0
+
7548c0
     virFirewallStartTransaction(fw, 0);
7548c0
 
7548c0
     for (i = 0; i < G_N_ELEMENTS(data); i++)
7548c0
-- 
7548c0
2.30.0
7548c0