render / rpms / libvirt

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