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

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