397dc2
From 4d8a10886f4dffd08fcf6a93694e12f76a2afd66 Mon Sep 17 00:00:00 2001
397dc2
Message-Id: <4d8a10886f4dffd08fcf6a93694e12f76a2afd66@dist-git>
397dc2
From: Laine Stump <laine@redhat.com>
397dc2
Date: Fri, 15 Jan 2021 22:51:51 -0500
397dc2
Subject: [PATCH] util: call iptables directly rather than via firewalld
397dc2
397dc2
When libvirt added support for firewalld, we were unable to use
397dc2
firewalld's higher level rules, because they weren't detailed enough
397dc2
and could not be applied to the iptables FORWARD or OUTPUT chains
397dc2
(only to the INPUT chain). Instead we changed our code so that rather
397dc2
than running the iptables/ip6tables/ebtables binaries ourselves, we
397dc2
would send these commands to firewalld as "passthrough commands", and
397dc2
firewalld would run the appropriate program on our behalf.
397dc2
397dc2
This was done under the assumption that firewalld was somehow tracking
397dc2
all these rules, and that this tracking was benefitting proper
397dc2
operation of firewalld and the system in general.
397dc2
397dc2
Several years later this came up in a discussion on IRC, and we
397dc2
learned from the firewalld developers that, in fact, adding iptables
397dc2
and ebtables rules with firewalld's passthrough commands actually has
397dc2
*no* advantage; firewalld doesn't keep track of these rules in any
397dc2
way, and doesn't use them to tailor the construction of its own rules.
397dc2
397dc2
Meanwhile, users have been complaining for some time that whenever
397dc2
firewalld is restarted on a system with libvirt virtual networks
397dc2
and/or nwfilter rules active, the system logs would be flooded with
397dc2
warning messages whining that [lots of different rules] could not be
397dc2
deleted because they didn't exist. For example:
397dc2
397dc2
firewalld[3536040]: WARNING: COMMAND_FAILED:
397dc2
  '/usr/sbin/iptables -w10 -w --table filter --delete LIBVIRT_OUT
397dc2
  --out-interface virbr4 --protocol udp --destination-port 68
397dc2
  --jump ACCEPT' failed: iptables: Bad rule
397dc2
  (does a matching rule exist in that chain?).
397dc2
397dc2
See:
397dc2
397dc2
  https://bugzilla.redhat.com/1607929 (RHEL8)
397dc2
  https://bugzilla.redhat.com/1790837 (RHEL8-AV)
397dc2
397dc2
for many more examples and a discussion)
397dc2
397dc2
Note that these messages are created by iptables, but are logged by
397dc2
firewalld - when an iptables/ebtables command fails, firewalld grabs
397dc2
whatever is in stderr of the program, and spits it out to the system
397dc2
log as a warning. We've requested that firewalld not do this (and
397dc2
instead leave it up to the calling application to do the appropriate
397dc2
logging), but this request has been respectfully denied.
397dc2
397dc2
But combining the two problems above ( 1) firewalld doesn't do
397dc2
anything useful when you use it as a proxy to add/remove iptables
397dc2
rules, 2) firewalld often insists on logging lots of
397dc2
annoying/misleading/useless "error" messages when you use it as a
397dc2
proxy to remove iptables rules that don't already exist), leads to a
397dc2
solution - simply stop using firewalld to add and remove iptables
397dc2
rules. Instead, exec iptables/ip6tables/ebtables directly in the same
397dc2
way we do when firewalld isn't active.
397dc2
397dc2
We still need to keep track of whether or not firewalld is active, as
397dc2
there are some things that must be done, e.g. we need to add some
397dc2
actual firewalld rules in the firewalld "libvirt" zone, and we need to
397dc2
take notice when firewalld restarts, so that we can reload all our
397dc2
rules.
397dc2
397dc2
This patch doesn't remove the infrastructure that allows having
397dc2
different firewall backends that perform their functions in different
397dc2
ways, as that will very possibly come in handy in the future when we
397dc2
want to have an nftables direct backend, and possibly a "pure"
397dc2
firewalld backend (now that firewalld supports more complex rules, and
397dc2
can add those rules to the FORWARD and OUTPUT chains). Instead, it
397dc2
just changes the action when the selected backend is "firewalld" so
397dc2
that it adds rules directly rather than through firewalld, while
397dc2
leaving as much of the existing code intact as possible.
397dc2
397dc2
In order for tests to still pass, virfirewalltest also had to be
397dc2
modified to behave in a different way (i.e. by capturing the generated
397dc2
commandline as it does for the DIRECT backend, rather than capturing
397dc2
dbus messages using a mocked dbus API).
397dc2
397dc2
Signed-off-by: Laine Stump <laine@redhat.com>
397dc2
Reviewed-by: Daniel Henrique Barboza <danielhb413@gmail.com>
397dc2
(cherry picked from commit b19863640d10b47b7c4a7cbadb21f196d61d96a2)
397dc2
Message-Id: <20210116035151.1066734-9-laine@redhat.com>
397dc2
Reviewed-by: Jiri Denemark <jdenemar@redhat.com>
397dc2
---
397dc2
 src/util/virfirewall.c  | 13 +++++++++++--
397dc2
 tests/virfirewalltest.c | 30 ++++++++++++++++++++----------
397dc2
 2 files changed, 31 insertions(+), 12 deletions(-)
397dc2
397dc2
diff --git a/src/util/virfirewall.c b/src/util/virfirewall.c
397dc2
index 66d20d3f17..2ea821ec17 100644
397dc2
--- a/src/util/virfirewall.c
397dc2
+++ b/src/util/virfirewall.c
397dc2
@@ -644,7 +644,7 @@ virFirewallApplyRuleDirect(virFirewallRulePtr rule,
397dc2
 }
397dc2
 
397dc2
 
397dc2
-static int
397dc2
+static int G_GNUC_UNUSED
397dc2
 virFirewallApplyRuleFirewallD(virFirewallRulePtr rule,
397dc2
                               bool ignoreErrors,
397dc2
                               char **output)
397dc2
@@ -702,7 +702,16 @@ virFirewallApplyRule(virFirewallPtr firewall,
397dc2
             return -1;
397dc2
         break;
397dc2
     case VIR_FIREWALL_BACKEND_FIREWALLD:
397dc2
-        if (virFirewallApplyRuleFirewallD(rule, ignoreErrors, &output) < 0)
397dc2
+        /* Since we are using raw iptables rules, there is no
397dc2
+         * advantage to going through firewalld, so instead just add
397dc2
+         * them directly rather that via dbus calls to firewalld. This
397dc2
+         * has the useful side effect of eliminating extra unwanted
397dc2
+         * warning messages in the system logs when trying to delete
397dc2
+         * rules that don't exist (which is something that happens
397dc2
+         * often when libvirtd is started, and *always* when firewalld
397dc2
+         * is restarted)
397dc2
+         */
397dc2
+        if (virFirewallApplyRuleDirect(rule, ignoreErrors, &output) < 0)
397dc2
             return -1;
397dc2
         break;
397dc2
 
397dc2
diff --git a/tests/virfirewalltest.c b/tests/virfirewalltest.c
397dc2
index 40e7f4f00b..1036353579 100644
397dc2
--- a/tests/virfirewalltest.c
397dc2
+++ b/tests/virfirewalltest.c
397dc2
@@ -214,7 +214,8 @@ testFirewallSingleGroup(const void *opaque)
397dc2
     if (virFirewallSetBackend(data->tryBackend) < 0)
397dc2
         goto cleanup;
397dc2
 
397dc2
-    if (data->expectBackend == VIR_FIREWALL_BACKEND_DIRECT)
397dc2
+    if (data->expectBackend == VIR_FIREWALL_BACKEND_DIRECT ||
397dc2
+        data->expectBackend == VIR_FIREWALL_BACKEND_FIREWALLD)
397dc2
         virCommandSetDryRun(&cmdbuf, NULL, NULL);
397dc2
     else
397dc2
         fwBuf = &cmdbuf;
397dc2
@@ -271,7 +272,8 @@ testFirewallRemoveRule(const void *opaque)
397dc2
     if (virFirewallSetBackend(data->tryBackend) < 0)
397dc2
         goto cleanup;
397dc2
 
397dc2
-    if (data->expectBackend == VIR_FIREWALL_BACKEND_DIRECT)
397dc2
+    if (data->expectBackend == VIR_FIREWALL_BACKEND_DIRECT ||
397dc2
+        data->expectBackend == VIR_FIREWALL_BACKEND_FIREWALLD)
397dc2
         virCommandSetDryRun(&cmdbuf, NULL, NULL);
397dc2
     else
397dc2
         fwBuf = &cmdbuf;
397dc2
@@ -335,7 +337,8 @@ testFirewallManyGroups(const void *opaque G_GNUC_UNUSED)
397dc2
     if (virFirewallSetBackend(data->tryBackend) < 0)
397dc2
         goto cleanup;
397dc2
 
397dc2
-    if (data->expectBackend == VIR_FIREWALL_BACKEND_DIRECT)
397dc2
+    if (data->expectBackend == VIR_FIREWALL_BACKEND_DIRECT ||
397dc2
+        data->expectBackend == VIR_FIREWALL_BACKEND_FIREWALLD)
397dc2
         virCommandSetDryRun(&cmdbuf, NULL, NULL);
397dc2
     else
397dc2
         fwBuf = &cmdbuf;
397dc2
@@ -426,7 +429,8 @@ testFirewallIgnoreFailGroup(const void *opaque G_GNUC_UNUSED)
397dc2
     if (virFirewallSetBackend(data->tryBackend) < 0)
397dc2
         goto cleanup;
397dc2
 
397dc2
-    if (data->expectBackend == VIR_FIREWALL_BACKEND_DIRECT) {
397dc2
+    if (data->expectBackend == VIR_FIREWALL_BACKEND_DIRECT ||
397dc2
+        data->expectBackend == VIR_FIREWALL_BACKEND_FIREWALLD) {
397dc2
         virCommandSetDryRun(&cmdbuf, testFirewallRollbackHook, NULL);
397dc2
     } else {
397dc2
         fwBuf = &cmdbuf;
397dc2
@@ -498,7 +502,8 @@ testFirewallIgnoreFailRule(const void *opaque G_GNUC_UNUSED)
397dc2
     if (virFirewallSetBackend(data->tryBackend) < 0)
397dc2
         goto cleanup;
397dc2
 
397dc2
-    if (data->expectBackend == VIR_FIREWALL_BACKEND_DIRECT) {
397dc2
+    if (data->expectBackend == VIR_FIREWALL_BACKEND_DIRECT ||
397dc2
+        data->expectBackend == VIR_FIREWALL_BACKEND_FIREWALLD) {
397dc2
         virCommandSetDryRun(&cmdbuf, testFirewallRollbackHook, NULL);
397dc2
     } else {
397dc2
         fwBuf = &cmdbuf;
397dc2
@@ -567,7 +572,8 @@ testFirewallNoRollback(const void *opaque G_GNUC_UNUSED)
397dc2
     if (virFirewallSetBackend(data->tryBackend) < 0)
397dc2
         goto cleanup;
397dc2
 
397dc2
-    if (data->expectBackend == VIR_FIREWALL_BACKEND_DIRECT) {
397dc2
+    if (data->expectBackend == VIR_FIREWALL_BACKEND_DIRECT ||
397dc2
+        data->expectBackend == VIR_FIREWALL_BACKEND_FIREWALLD) {
397dc2
         virCommandSetDryRun(&cmdbuf, testFirewallRollbackHook, NULL);
397dc2
     } else {
397dc2
         fwBuf = &cmdbuf;
397dc2
@@ -634,7 +640,8 @@ testFirewallSingleRollback(const void *opaque G_GNUC_UNUSED)
397dc2
     if (virFirewallSetBackend(data->tryBackend) < 0)
397dc2
         goto cleanup;
397dc2
 
397dc2
-    if (data->expectBackend == VIR_FIREWALL_BACKEND_DIRECT) {
397dc2
+    if (data->expectBackend == VIR_FIREWALL_BACKEND_DIRECT ||
397dc2
+        data->expectBackend == VIR_FIREWALL_BACKEND_FIREWALLD) {
397dc2
         virCommandSetDryRun(&cmdbuf, testFirewallRollbackHook, NULL);
397dc2
     } else {
397dc2
         fwError = true;
397dc2
@@ -717,7 +724,8 @@ testFirewallManyRollback(const void *opaque G_GNUC_UNUSED)
397dc2
     if (virFirewallSetBackend(data->tryBackend) < 0)
397dc2
         goto cleanup;
397dc2
 
397dc2
-    if (data->expectBackend == VIR_FIREWALL_BACKEND_DIRECT) {
397dc2
+    if (data->expectBackend == VIR_FIREWALL_BACKEND_DIRECT ||
397dc2
+        data->expectBackend == VIR_FIREWALL_BACKEND_FIREWALLD) {
397dc2
         virCommandSetDryRun(&cmdbuf, testFirewallRollbackHook, NULL);
397dc2
     } else {
397dc2
         fwBuf = &cmdbuf;
397dc2
@@ -808,7 +816,8 @@ testFirewallChainedRollback(const void *opaque G_GNUC_UNUSED)
397dc2
     if (virFirewallSetBackend(data->tryBackend) < 0)
397dc2
         goto cleanup;
397dc2
 
397dc2
-    if (data->expectBackend == VIR_FIREWALL_BACKEND_DIRECT) {
397dc2
+    if (data->expectBackend == VIR_FIREWALL_BACKEND_DIRECT ||
397dc2
+        data->expectBackend == VIR_FIREWALL_BACKEND_FIREWALLD) {
397dc2
         virCommandSetDryRun(&cmdbuf, testFirewallRollbackHook, NULL);
397dc2
     } else {
397dc2
         fwBuf = &cmdbuf;
397dc2
@@ -1007,7 +1016,8 @@ testFirewallQuery(const void *opaque G_GNUC_UNUSED)
397dc2
     if (virFirewallSetBackend(data->tryBackend) < 0)
397dc2
         goto cleanup;
397dc2
 
397dc2
-    if (data->expectBackend == VIR_FIREWALL_BACKEND_DIRECT) {
397dc2
+    if (data->expectBackend == VIR_FIREWALL_BACKEND_DIRECT ||
397dc2
+        data->expectBackend == VIR_FIREWALL_BACKEND_FIREWALLD) {
397dc2
         virCommandSetDryRun(&cmdbuf, testFirewallQueryHook, NULL);
397dc2
     } else {
397dc2
         fwBuf = &cmdbuf;
397dc2
-- 
397dc2
2.30.0
397dc2