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