Blob Blame History Raw
From 17470fa9deac4aa15ecf75b9c811c093bc44c019 Mon Sep 17 00:00:00 2001
From: Eric Garver <e@erig.me>
Date: Fri, 17 Aug 2018 12:26:53 -0400
Subject: [PATCH 1/2] fw: if startup fails on reload, reapply non-perm config
 that survives reload

Even if startup fails we should still re-assign the non-permanent
interfaces to zones and non-permanent direct rules.

Fixes: rhbz 1498923
(cherry picked from commit 2796edc1691f52c3655991c0be814a617cb26910)
---
 src/firewall/core/fw.py             | 121 +++++++++++++++-------------
 src/tests/regression/rhbz1498923.at |  17 ++++
 2 files changed, 80 insertions(+), 58 deletions(-)

diff --git a/src/firewall/core/fw.py b/src/firewall/core/fw.py
index 5b706d6d3e80..9079f1bbc6a4 100644
--- a/src/firewall/core/fw.py
+++ b/src/firewall/core/fw.py
@@ -910,70 +910,75 @@ class Firewall(object):
     def reload(self, stop=False):
         _panic = self._panic
 
-        try:
-            # save zone interfaces
-            _zone_interfaces = { }
-            for zone in self.zone.get_zones():
-                _zone_interfaces[zone] = self.zone.get_settings(zone)["interfaces"]
-            # save direct config
-            _direct_config = self.direct.get_runtime_config()
-            _old_dz = self.get_default_zone()
-
-            # stop
-            self.cleanup()
+        # save zone interfaces
+        _zone_interfaces = { }
+        for zone in self.zone.get_zones():
+            _zone_interfaces[zone] = self.zone.get_settings(zone)["interfaces"]
+        # save direct config
+        _direct_config = self.direct.get_runtime_config()
+        _old_dz = self.get_default_zone()
+
+        # stop
+        self.cleanup()
 
-            self.set_policy("DROP")
+        self.set_policy("DROP")
 
+        start_exception = None
+        try:
             self._start(reload=True, complete_reload=stop)
-
-            # handle interfaces in the default zone and move them to the new
-            # default zone if it changed
-            _new_dz = self.get_default_zone()
-            if _new_dz != _old_dz:
-                # if_new_dz has been introduced with the reload, we need to add it
-                # https://github.com/firewalld/firewalld/issues/53
-                if _new_dz not in _zone_interfaces:
-                    _zone_interfaces[_new_dz] = { }
-                # default zone changed. Move interfaces from old default zone to
-                # the new one.
-                for iface, settings in list(_zone_interfaces[_old_dz].items()):
-                    if settings["__default__"]:
-                        # move only those that were added to default zone
-                        # (not those that were added to specific zone same as
-                        # default)
-                        _zone_interfaces[_new_dz][iface] = \
-                            _zone_interfaces[_old_dz][iface]
-                        del _zone_interfaces[_old_dz][iface]
-
-            # add interfaces to zones again
-            for zone in self.zone.get_zones():
-                if zone in _zone_interfaces:
-                    self.zone.set_settings(zone, { "interfaces":
-                                                   _zone_interfaces[zone] })
-                    del _zone_interfaces[zone]
-                else:
-                    log.info1("New zone '%s'.", zone)
-            if len(_zone_interfaces) > 0:
-                for zone in list(_zone_interfaces.keys()):
-                    log.info1("Lost zone '%s', zone interfaces dropped.", zone)
-                    del _zone_interfaces[zone]
-            del _zone_interfaces
-
-            # restore direct config
-            self.direct.set_config(_direct_config)
-
-            # enable panic mode again if it has been enabled before or set policy
-            # to ACCEPT
-            if _panic:
-                self.enable_panic_mode()
+        except Exception as e:
+            # save the exception for later, but continue restoring interfaces,
+            # etc. We'll re-raise it at the end.
+            start_exception = e
+
+        # handle interfaces in the default zone and move them to the new
+        # default zone if it changed
+        _new_dz = self.get_default_zone()
+        if _new_dz != _old_dz:
+            # if_new_dz has been introduced with the reload, we need to add it
+            # https://github.com/firewalld/firewalld/issues/53
+            if _new_dz not in _zone_interfaces:
+                _zone_interfaces[_new_dz] = { }
+            # default zone changed. Move interfaces from old default zone to
+            # the new one.
+            for iface, settings in list(_zone_interfaces[_old_dz].items()):
+                if settings["__default__"]:
+                    # move only those that were added to default zone
+                    # (not those that were added to specific zone same as
+                    # default)
+                    _zone_interfaces[_new_dz][iface] = \
+                        _zone_interfaces[_old_dz][iface]
+                    del _zone_interfaces[_old_dz][iface]
+
+        # add interfaces to zones again
+        for zone in self.zone.get_zones():
+            if zone in _zone_interfaces:
+                self.zone.set_settings(zone, { "interfaces":
+                                               _zone_interfaces[zone] })
+                del _zone_interfaces[zone]
             else:
-                self.set_policy("ACCEPT")
+                log.info1("New zone '%s'.", zone)
+        if len(_zone_interfaces) > 0:
+            for zone in list(_zone_interfaces.keys()):
+                log.info1("Lost zone '%s', zone interfaces dropped.", zone)
+                del _zone_interfaces[zone]
+        del _zone_interfaces
+
+        # restore direct config
+        self.direct.set_config(_direct_config)
+
+        # enable panic mode again if it has been enabled before or set policy
+        # to ACCEPT
+        if _panic:
+            self.enable_panic_mode()
+        else:
+            self.set_policy("ACCEPT")
 
-            self._state = "RUNNING"
-        except Exception:
+        if start_exception:
             self._state = "FAILED"
-            self.set_policy("ACCEPT")
-            raise
+            raise start_exception
+        else:
+            self._state = "RUNNING"
 
     # STATE
 
diff --git a/src/tests/regression/rhbz1498923.at b/src/tests/regression/rhbz1498923.at
index bb0d841db2a7..9b68678180ef 100644
--- a/src/tests/regression/rhbz1498923.at
+++ b/src/tests/regression/rhbz1498923.at
@@ -1,11 +1,28 @@
 FWD_START_TEST([invalid direct rule causes reload error])
 FWD_CHECK([-q --permanent --direct --add-rule ipv4 filter INPUT 0 -p tcp --dport 8080 -j ACCEPT])
 FWD_CHECK([-q --permanent --direct --add-rule ipv4 filter INPUT 1 --a-bogus-flag])
+
+dnl add some non-permanent things that should persist a reload
+FWD_CHECK([-q --zone=public --add-interface=foobar0])
+FWD_CHECK([-q --direct --direct --add-rule ipv4 filter FORWARD 0 -p tcp -j ACCEPT])
+
 FWD_RELOAD(13, [ignore], [ignore], 251)
 FWD_CHECK([--state], 251, [ignore], [failed
 ])
 
+dnl verify the non-permanent stuff we set above remained
+FWD_CHECK([--get-zone-of-interface=foobar0], 0, [dnl
+public
+])
+FWD_CHECK([-q --direct --direct --query-rule ipv4 filter FORWARD 0 -p tcp -j ACCEPT])
+
 dnl now remove the bad rule and reload successfully
 FWD_CHECK([-q --permanent --direct --remove-rule ipv4 filter INPUT 1 --a-bogus-flag])
 FWD_RELOAD
+
+dnl verify the non-permanent stuff we set above remained
+FWD_CHECK([--get-zone-of-interface=foobar0], 0, [dnl
+public
+])
+FWD_CHECK([-q --direct --direct --query-rule ipv4 filter FORWARD 0 -p tcp -j ACCEPT])
 FWD_END_TEST([-e '/.*a-bogus-flag.*/d'])
-- 
2.18.0