Blame SOURCES/0003-fw-if-failure-occurs-during-startup-set-state-to-FAI.patch

4d71d0
From 9e4bf24e1e0a5d54398d2220f0a5217eff0704a7 Mon Sep 17 00:00:00 2001
4d71d0
From: Eric Garver <e@erig.me>
4d71d0
Date: Mon, 13 Aug 2018 16:53:46 -0400
4d71d0
Subject: [PATCH 3/4] fw: if failure occurs during startup set state to FAILED
4d71d0
4d71d0
Previously if a failure occurred at startup firewalld would get stuck in
4d71d0
INIT state and the policy would remain as "DROP". This commit changes
4d71d0
that behavior and introduces a new state "FAILED", which means a failure
4d71d0
occurred and we're running in a failed state. Policy is set to "ACCEPT"
4d71d0
so as to hopefully prevent locking out an admin.
4d71d0
4d71d0
(cherry picked from commit f475bd2293b7ba01ad4b56b68bef1b61d01526f0)
4d71d0
---
4d71d0
 doc/xml/firewall-cmd.xml.in         |   2 +-
4d71d0
 doc/xml/firewalld.dbus.xml          |   2 +-
4d71d0
 src/firewall-cmd                    |   2 +
4d71d0
 src/firewall/core/fw.py             | 131 +++++++++++++++-------------
4d71d0
 src/firewall/errors.py              |   1 +
4d71d0
 src/tests/regression/rhbz1498923.at |   8 +-
4d71d0
 6 files changed, 83 insertions(+), 63 deletions(-)
4d71d0
4d71d0
diff --git a/doc/xml/firewall-cmd.xml.in b/doc/xml/firewall-cmd.xml.in
4d71d0
index 32c89591db86..c2606553e549 100644
4d71d0
--- a/doc/xml/firewall-cmd.xml.in
4d71d0
+++ b/doc/xml/firewall-cmd.xml.in
4d71d0
@@ -118,7 +118,7 @@
4d71d0
 	  <term><option>--state</option></term>
4d71d0
 	  <listitem>
4d71d0
 	    <para>
4d71d0
-	      Check whether the firewalld daemon is active (i.e. running). Returns an exit code 0 if it is active, <replaceable>NOT_RUNNING</replaceable> otherwise (see <xref linkend="exit_codes"/>). This will also print the state to <replaceable>STDOUT</replaceable>.
4d71d0
+	      Check whether the firewalld daemon is active (i.e. running). Returns an exit code 0 if it is active, <replaceable>RUNNING_BUT_FAILED</replaceable> if failure occurred on startup, <replaceable>NOT_RUNNING</replaceable> otherwise. See <xref linkend="exit_codes"/>. This will also print the state to <replaceable>STDOUT</replaceable>.
4d71d0
 	    </para>
4d71d0
 	  </listitem>
4d71d0
 	</varlistentry>
4d71d0
diff --git a/doc/xml/firewalld.dbus.xml b/doc/xml/firewalld.dbus.xml
4d71d0
index acdbb5fd6e00..ec82d4cad077 100644
4d71d0
--- a/doc/xml/firewalld.dbus.xml
4d71d0
+++ b/doc/xml/firewalld.dbus.xml
4d71d0
@@ -488,7 +488,7 @@
4d71d0
           </varlistentry>
4d71d0
           <varlistentry id="FirewallD1.Properties.state">
4d71d0
             <term><parameter>state</parameter> - s - (ro)</term>
4d71d0
-            <listitem><para>firewalld state. This can be either <literal>INIT</literal> or <literal>RUNNING</literal>. In <literal>INIT</literal> state, firewalld is starting up and initializing.</para></listitem>
4d71d0
+            <listitem><para>firewalld state. This can be either <literal>INIT</literal>, <literal>FAILED</literal>, or <literal>RUNNING</literal>. In <literal>INIT</literal> state, firewalld is starting up and initializing. In <literal>FAILED</literal> state, firewalld completely started but experienced a failure.</para></listitem>
4d71d0
           </varlistentry>
4d71d0
           <varlistentry id="FirewallD1.Properties.version">
4d71d0
             <term><parameter>version</parameter> - s - (ro)</term>
4d71d0
diff --git a/src/firewall-cmd b/src/firewall-cmd
4d71d0
index b80115564e1b..12e18bb88a54 100755
4d71d0
--- a/src/firewall-cmd
4d71d0
+++ b/src/firewall-cmd
4d71d0
@@ -2022,6 +2022,8 @@ elif a.state:
4d71d0
     state = fw.get_property("state")
4d71d0
     if state == "RUNNING":
4d71d0
         cmd.print_and_exit ("running")
4d71d0
+    elif state == "FAILED":
4d71d0
+        cmd.print_and_exit("failed", errors.RUNNING_BUT_FAILED)
4d71d0
     else:
4d71d0
         cmd.print_and_exit ("not running", errors.NOT_RUNNING)
4d71d0
 elif a.get_log_denied:
4d71d0
diff --git a/src/firewall/core/fw.py b/src/firewall/core/fw.py
4d71d0
index 1ff36f18cd99..5b706d6d3e80 100644
4d71d0
--- a/src/firewall/core/fw.py
4d71d0
+++ b/src/firewall/core/fw.py
4d71d0
@@ -451,11 +451,16 @@ class Firewall(object):
4d71d0
             tm2 = time.time()
4d71d0
             log.debug2("Flushing and applying took %f seconds" % (tm2 - tm1))
4d71d0
 
4d71d0
-        self._state = "RUNNING"
4d71d0
-
4d71d0
     def start(self):
4d71d0
-        self._start()
4d71d0
-        self.set_policy("ACCEPT")
4d71d0
+        try:
4d71d0
+            self._start()
4d71d0
+        except Exception:
4d71d0
+            self._state = "FAILED"
4d71d0
+            self.set_policy("ACCEPT")
4d71d0
+            raise
4d71d0
+        else:
4d71d0
+            self._state = "RUNNING"
4d71d0
+            self.set_policy("ACCEPT")
4d71d0
 
4d71d0
     def _loader(self, path, reader_type, combine=False):
4d71d0
         # combine: several zone files are getting combined into one obj
4d71d0
@@ -905,64 +910,70 @@ class Firewall(object):
4d71d0
     def reload(self, stop=False):
4d71d0
         _panic = self._panic
4d71d0
 
4d71d0
-        # save zone interfaces
4d71d0
-        _zone_interfaces = { }
4d71d0
-        for zone in self.zone.get_zones():
4d71d0
-            _zone_interfaces[zone] = self.zone.get_settings(zone)["interfaces"]
4d71d0
-        # save direct config
4d71d0
-        _direct_config = self.direct.get_runtime_config()
4d71d0
-        _old_dz = self.get_default_zone()
4d71d0
-
4d71d0
-        # stop
4d71d0
-        self.cleanup()
4d71d0
-
4d71d0
-        self.set_policy("DROP")
4d71d0
-
4d71d0
-        # start
4d71d0
-        self._start(reload=True, complete_reload=stop)
4d71d0
-
4d71d0
-        # handle interfaces in the default zone and move them to the new
4d71d0
-        # default zone if it changed
4d71d0
-        _new_dz = self.get_default_zone()
4d71d0
-        if _new_dz != _old_dz:
4d71d0
-            # if_new_dz has been introduced with the reload, we need to add it
4d71d0
-            # https://github.com/firewalld/firewalld/issues/53
4d71d0
-            if _new_dz not in _zone_interfaces:
4d71d0
-                _zone_interfaces[_new_dz] = { }
4d71d0
-            # default zone changed. Move interfaces from old default zone to
4d71d0
-            # the new one.
4d71d0
-            for iface, settings in list(_zone_interfaces[_old_dz].items()):
4d71d0
-                if settings["__default__"]:
4d71d0
-                    # move only those that were added to default zone
4d71d0
-                    # (not those that were added to specific zone same as
4d71d0
-                    # default)
4d71d0
-                    _zone_interfaces[_new_dz][iface] = \
4d71d0
-                        _zone_interfaces[_old_dz][iface]
4d71d0
-                    del _zone_interfaces[_old_dz][iface]
4d71d0
-
4d71d0
-        # add interfaces to zones again
4d71d0
-        for zone in self.zone.get_zones():
4d71d0
-            if zone in _zone_interfaces:
4d71d0
-                self.zone.set_settings(zone, { "interfaces":
4d71d0
-                                               _zone_interfaces[zone] })
4d71d0
-                del _zone_interfaces[zone]
4d71d0
+        try:
4d71d0
+            # save zone interfaces
4d71d0
+            _zone_interfaces = { }
4d71d0
+            for zone in self.zone.get_zones():
4d71d0
+                _zone_interfaces[zone] = self.zone.get_settings(zone)["interfaces"]
4d71d0
+            # save direct config
4d71d0
+            _direct_config = self.direct.get_runtime_config()
4d71d0
+            _old_dz = self.get_default_zone()
4d71d0
+
4d71d0
+            # stop
4d71d0
+            self.cleanup()
4d71d0
+
4d71d0
+            self.set_policy("DROP")
4d71d0
+
4d71d0
+            self._start(reload=True, complete_reload=stop)
4d71d0
+
4d71d0
+            # handle interfaces in the default zone and move them to the new
4d71d0
+            # default zone if it changed
4d71d0
+            _new_dz = self.get_default_zone()
4d71d0
+            if _new_dz != _old_dz:
4d71d0
+                # if_new_dz has been introduced with the reload, we need to add it
4d71d0
+                # https://github.com/firewalld/firewalld/issues/53
4d71d0
+                if _new_dz not in _zone_interfaces:
4d71d0
+                    _zone_interfaces[_new_dz] = { }
4d71d0
+                # default zone changed. Move interfaces from old default zone to
4d71d0
+                # the new one.
4d71d0
+                for iface, settings in list(_zone_interfaces[_old_dz].items()):
4d71d0
+                    if settings["__default__"]:
4d71d0
+                        # move only those that were added to default zone
4d71d0
+                        # (not those that were added to specific zone same as
4d71d0
+                        # default)
4d71d0
+                        _zone_interfaces[_new_dz][iface] = \
4d71d0
+                            _zone_interfaces[_old_dz][iface]
4d71d0
+                        del _zone_interfaces[_old_dz][iface]
4d71d0
+
4d71d0
+            # add interfaces to zones again
4d71d0
+            for zone in self.zone.get_zones():
4d71d0
+                if zone in _zone_interfaces:
4d71d0
+                    self.zone.set_settings(zone, { "interfaces":
4d71d0
+                                                   _zone_interfaces[zone] })
4d71d0
+                    del _zone_interfaces[zone]
4d71d0
+                else:
4d71d0
+                    log.info1("New zone '%s'.", zone)
4d71d0
+            if len(_zone_interfaces) > 0:
4d71d0
+                for zone in list(_zone_interfaces.keys()):
4d71d0
+                    log.info1("Lost zone '%s', zone interfaces dropped.", zone)
4d71d0
+                    del _zone_interfaces[zone]
4d71d0
+            del _zone_interfaces
4d71d0
+
4d71d0
+            # restore direct config
4d71d0
+            self.direct.set_config(_direct_config)
4d71d0
+
4d71d0
+            # enable panic mode again if it has been enabled before or set policy
4d71d0
+            # to ACCEPT
4d71d0
+            if _panic:
4d71d0
+                self.enable_panic_mode()
4d71d0
             else:
4d71d0
-                log.info1("New zone '%s'.", zone)
4d71d0
-        if len(_zone_interfaces) > 0:
4d71d0
-            for zone in list(_zone_interfaces.keys()):
4d71d0
-                log.info1("Lost zone '%s', zone interfaces dropped.", zone)
4d71d0
-                del _zone_interfaces[zone]
4d71d0
-        del _zone_interfaces
4d71d0
-
4d71d0
-        # restore direct config
4d71d0
-        self.direct.set_config(_direct_config)
4d71d0
-
4d71d0
-        # enable panic mode again if it has been enabled before or set policy
4d71d0
-        # to ACCEPT
4d71d0
-        if _panic:
4d71d0
-            self.enable_panic_mode()
4d71d0
-        else:
4d71d0
+                self.set_policy("ACCEPT")
4d71d0
+
4d71d0
+            self._state = "RUNNING"
4d71d0
+        except Exception:
4d71d0
+            self._state = "FAILED"
4d71d0
             self.set_policy("ACCEPT")
4d71d0
+            raise
4d71d0
 
4d71d0
     # STATE
4d71d0
 
4d71d0
diff --git a/src/firewall/errors.py b/src/firewall/errors.py
4d71d0
index 1cd604884c99..63d007191ffa 100644
4d71d0
--- a/src/firewall/errors.py
4d71d0
+++ b/src/firewall/errors.py
4d71d0
@@ -97,6 +97,7 @@ MISSING_NAME        =  205
4d71d0
 MISSING_SETTING     =  206
4d71d0
 MISSING_FAMILY      =  207
4d71d0
 
4d71d0
+RUNNING_BUT_FAILED  =  251
4d71d0
 NOT_RUNNING         =  252
4d71d0
 NOT_AUTHORIZED      =  253
4d71d0
 UNKNOWN_ERROR       =  254
4d71d0
diff --git a/src/tests/regression/rhbz1498923.at b/src/tests/regression/rhbz1498923.at
4d71d0
index 505a523d5cc4..bb0d841db2a7 100644
4d71d0
--- a/src/tests/regression/rhbz1498923.at
4d71d0
+++ b/src/tests/regression/rhbz1498923.at
4d71d0
@@ -1,5 +1,11 @@
4d71d0
 FWD_START_TEST([invalid direct rule causes reload error])
4d71d0
 FWD_CHECK([-q --permanent --direct --add-rule ipv4 filter INPUT 0 -p tcp --dport 8080 -j ACCEPT])
4d71d0
 FWD_CHECK([-q --permanent --direct --add-rule ipv4 filter INPUT 1 --a-bogus-flag])
4d71d0
-FWD_RELOAD(13, [ignore], [ignore])
4d71d0
+FWD_RELOAD(13, [ignore], [ignore], 251)
4d71d0
+FWD_CHECK([--state], 251, [ignore], [failed
4d71d0
+])
4d71d0
+
4d71d0
+dnl now remove the bad rule and reload successfully
4d71d0
+FWD_CHECK([-q --permanent --direct --remove-rule ipv4 filter INPUT 1 --a-bogus-flag])
4d71d0
+FWD_RELOAD
4d71d0
 FWD_END_TEST([-e '/.*a-bogus-flag.*/d'])
4d71d0
-- 
4d71d0
2.18.0
4d71d0