Skip to content

Commit 8d5fb04

Browse files
committed
chore: Remove obsolete firewall_lib offline code
This code is very problematic: * It has not worked for years, at least not since RHEL 9 (probably earlier). If firewalld isn't running, this code already crashes in the `FirewallClient()` instantiation. * There haven't been any integration tests that cover this. The unit tests don't cover this as they mock the entire firewalld API, i.e. they test an API that simply does not exist. * `firewall.core.fw` is not supported API, but the internal CLI implementation. That can change at any time (and apparently did, assuming that the old code worked at *some* point). There is no offline API [1]. * Even in the current state the implementation was very incomplete. None of the service/ipset/zone/etc. settings are implemented, only some parts of zone handling (and not even that, e.g. setting the default zone is missing as well). Trying to implement the missing bits with the internal API would be a major piece of work and go in the wrong direction. Let's rather use the official methods (`firewall-offline-cmd` or XML editing). To spare the next person from falling into the "oh, offline is supported" pitfall, delete all the offline code. [1] https://issues.redhat.com/browse/RHEL-88425?focusedId=27062772&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-27062772
1 parent b36278f commit 8d5fb04

File tree

2 files changed

+34
-182
lines changed

2 files changed

+34
-182
lines changed

library/firewall_lib.py

Lines changed: 34 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -383,38 +383,16 @@ def create_ipset(module, fw, ipset, ipset_type):
383383

384384

385385
def handle_interface_permanent(
386-
zone, item, fw_zone, fw_settings, fw, fw_offline, module
386+
zone, item, fw_zone, fw_settings, fw, module
387387
):
388-
if fw_offline:
389-
iface_zone_objs = []
390-
for zone in fw.config.get_zones():
391-
old_zone_obj = fw.config.get_zone(zone)
392-
if item in old_zone_obj.interfaces:
393-
old_zone_settings = FirewallClientZoneSettings(
394-
fw.config.get_zone_config(old_zone_obj)
395-
)
396-
old_zone_settings.removeInterface(item)
397-
fw.config.set_zone_config(old_zone_obj, old_zone_settings.settings)
398-
iface_zone_objs.append(old_zone_obj)
399-
400-
old_zone_obj = iface_zone_objs[0]
401-
if old_zone_obj.name != zone:
402-
old_zone_settings = FirewallClientZoneSettings(
403-
fw.config.get_zone_config(old_zone_obj)
404-
)
388+
old_zone_name = fw.config().getZoneOfInterface(item)
389+
if old_zone_name != zone:
390+
if old_zone_name:
391+
old_zone_obj = fw.config().getZoneByName(old_zone_name)
392+
old_zone_settings = old_zone_obj.getSettings()
405393
old_zone_settings.removeInterface(item)
406-
fw.config.set_zone_config(old_zone_obj, old_zone_settings.settings)
407-
fw_settings.addInterface(item)
408-
fw.config.set_zone_config(fw_zone, fw_settings.settings)
409-
else:
410-
old_zone_name = fw.config().getZoneOfInterface(item)
411-
if old_zone_name != zone:
412-
if old_zone_name:
413-
old_zone_obj = fw.config().getZoneByName(old_zone_name)
414-
old_zone_settings = old_zone_obj.getSettings()
415-
old_zone_settings.removeInterface(item)
416-
old_zone_obj.update(old_zone_settings)
417-
fw_settings.addInterface(item)
394+
old_zone_obj.update(old_zone_settings)
395+
fw_settings.addInterface(item)
418396

419397

420398
pci_ids = None
@@ -993,75 +971,37 @@ def main():
993971

994972
fw = FirewallClient()
995973

996-
fw_offline = False
997-
if not fw.connected:
998-
# Firewalld is not currently running, permanent-only operations
999-
fw_offline = True
1000-
runtime = False
1001-
permanent = True
1002-
1003-
# Pre-run version checking
1004-
if lsr_parse_version(FW_VERSION) < lsr_parse_version("0.3.9"):
1005-
module.fail_json(
1006-
msg="Unsupported firewalld version %s" " requires >= 0.3.9" % FW_VERSION
1007-
)
1008-
1009-
try:
1010-
from firewall.core.fw_test import Firewall_test
1011-
1012-
fw = Firewall_test()
1013-
1014-
except ImportError:
1015-
# In firewalld version 0.7.0 this behavior changed
1016-
from firewall.core.fw import Firewall
1017-
1018-
fw = Firewall(offline=True)
1019-
1020-
fw.start()
1021-
else:
1022-
# Pre-run version checking
1023-
if lsr_parse_version(FW_VERSION) < lsr_parse_version("0.2.11"):
1024-
module.fail_json(
1025-
msg="Unsupported firewalld version %s, requires >= 0.2.11" % FW_VERSION
1026-
)
974+
# Pre-run version checking
975+
if lsr_parse_version(FW_VERSION) < lsr_parse_version("0.2.11"):
976+
module.fail_json(
977+
msg="Unsupported firewalld version %s, requires >= 0.2.11" % FW_VERSION
978+
)
1027979

1028-
# Set exception handler
1029-
def exception_handler(exception_message):
1030-
module.fail_json(msg=exception_message)
980+
# Set exception handler
981+
def exception_handler(exception_message):
982+
module.fail_json(msg=exception_message)
1031983

1032-
fw.setExceptionHandler(exception_handler)
984+
fw.setExceptionHandler(exception_handler)
1033985

1034986
# Get default zone, the permanent zone and settings
1035987
fw_zone = None
1036988
fw_settings = None
1037-
if fw_offline:
1038-
# if zone is None, we will use default zone which always exists
1039-
zone_exists = zone is None or zone in fw.zone.get_zones()
1040-
if not zone_exists and not zone_operation:
1041-
module.fail_json(msg="Permanent zone '%s' does not exist." % zone)
1042-
elif zone_exists:
1043-
zone = zone or fw.get_default_zone()
1044-
fw_zone = fw.config.get_zone(zone)
1045-
fw_settings = FirewallClientZoneSettings(
1046-
list(fw.config.get_zone_config(fw_zone))
1047-
)
1048-
else:
1049-
zone_exists = False
1050-
if runtime:
1051-
zone_exists = zone_exists or zone is None or zone in fw.getZones()
1052-
err_str = "Runtime"
1053-
if permanent:
1054-
zone_exists = (
1055-
zone_exists or zone is None or zone in fw.config().getZoneNames()
1056-
)
1057-
err_str = "Permanent"
989+
zone_exists = False
990+
if runtime:
991+
zone_exists = zone_exists or zone is None or zone in fw.getZones()
992+
err_str = "Runtime"
993+
if permanent:
994+
zone_exists = (
995+
zone_exists or zone is None or zone in fw.config().getZoneNames()
996+
)
997+
err_str = "Permanent"
1058998

1059-
if not zone_exists and not zone_operation:
1060-
module.fail_json(msg="%s zone '%s' does not exist." % (err_str, zone))
1061-
elif zone_exists:
1062-
zone = zone or fw.getDefaultZone()
1063-
fw_zone = fw.config().getZoneByName(zone)
1064-
fw_settings = fw_zone.getSettings()
999+
if not zone_exists and not zone_operation:
1000+
module.fail_json(msg="%s zone '%s' does not exist." % (err_str, zone))
1001+
elif zone_exists:
1002+
zone = zone or fw.getDefaultZone()
1003+
fw_zone = fw.config().getZoneByName(zone)
1004+
fw_settings = fw_zone.getSettings()
10651005

10661006
# Firewall modification starts here
10671007

@@ -1493,7 +1433,7 @@ def exception_handler(exception_message):
14931433
elif not fw_settings.queryInterface(item):
14941434
if not module.check_mode:
14951435
handle_interface_permanent(
1496-
zone, item, fw_zone, fw_settings, fw, fw_offline, module
1436+
zone, item, fw_zone, fw_settings, fw, module
14971437
)
14981438
changed = True
14991439
elif state == "disabled":
@@ -1572,10 +1512,7 @@ def exception_handler(exception_message):
15721512
# apply permanent changes
15731513
if changed and (zone_operation or permanent):
15741514
if fw_zone and fw_settings:
1575-
if fw_offline:
1576-
fw.config.set_zone_config(fw_zone, fw_settings.settings)
1577-
else:
1578-
fw_zone.update(fw_settings)
1515+
fw_zone.update(fw_settings)
15791516
if need_reload:
15801517
fw.reload()
15811518

tests/unit/test_firewall_lib.py

Lines changed: 0 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -280,60 +280,6 @@ def __call__(self, **kwargs):
280280
class FirewallInterfaceTests(unittest.TestCase):
281281
"""class to test Firewall interface tests"""
282282

283-
@patch("firewall_lib.FirewallClientZoneSettings", create=True)
284-
@patch("firewall_lib.FirewallClient", create=True)
285-
@patch("firewall_lib.HAS_FIREWALLD", True)
286-
@patch("firewall_lib.FW_VERSION", "0.3.8", create=True)
287-
def test_handle_interface_offline_true(self, zone_settings, firewall_class):
288-
module = Mock()
289-
zone = "dmz"
290-
item = "eth2"
291-
fw = firewall_class.return_value
292-
fw_config = Mock()
293-
fw.config.return_value = fw_config
294-
fw_zone = Mock()
295-
fw.config.getZoneByName.return_value = fw_zone
296-
fw_settings = Mock()
297-
fw.config.getZoneByName.getSettings.return_value = fw_settings
298-
fw_offline = True
299-
fw.config.get_zones.return_value = ["dmz"]
300-
fw_zone_two = Mock()
301-
fw.config.get_zone.return_value = fw_zone_two
302-
fw_zone_two.interfaces = ["eth2"]
303-
304-
firewall_lib.handle_interface_permanent(
305-
zone, item, fw_zone, fw_settings, fw, fw_offline, module
306-
)
307-
called_mock = getattr(fw_settings, "addInterface")
308-
assert [call("eth2")] == called_mock.call_args_list
309-
310-
@patch("firewall_lib.FirewallClientZoneSettings", create=True)
311-
@patch("firewall_lib.FirewallClient", create=True)
312-
@patch("firewall_lib.HAS_FIREWALLD", True)
313-
@patch("firewall_lib.FW_VERSION", "0.3.8", create=True)
314-
def test_handle_interface_offline_false(self, zone_settings, firewall_class):
315-
module = Mock()
316-
zone = "dmz"
317-
item = "eth2"
318-
fw = firewall_class.return_value
319-
fw_config = Mock()
320-
fw.config.return_value = fw_config
321-
fw_zone = Mock()
322-
fw.config.getZoneByName.return_value = fw_zone
323-
fw_settings = Mock()
324-
fw.config.getZoneByName.getSettings.return_value = fw_settings
325-
fw_offline = False
326-
fw.config.get_zones.return_value = ["dmz"]
327-
fw_zone_two = Mock()
328-
fw.config.get_zone.return_value = fw_zone_two
329-
fw_zone_two.interfaces = ["eth2"]
330-
331-
firewall_lib.handle_interface_permanent(
332-
zone, item, fw_zone, fw_settings, fw, fw_offline, module
333-
)
334-
called_mock = getattr(fw_settings, "addInterface")
335-
assert [call("eth2")] == called_mock.call_args_list
336-
337283
@patch("firewall_lib.nm_get_connection_of_interface", create=True)
338284
def test_try_get_connection_of_interface(self, nm_get_connection_of_interface):
339285
nm_get_connection_of_interface.return_value = Mock()
@@ -593,20 +539,6 @@ def test_main_error_add_service_permanent_false(self, am_class):
593539
msg="permanent must be enabled for service configuration. Additionally, service runtime configuration is not possible"
594540
)
595541

596-
@patch("firewall_lib.HAS_FIREWALLD", True)
597-
def test_permanent_runtime_offline(self, am_class):
598-
am = am_class.return_value
599-
am.params = {
600-
"icmp_block_inversion": True,
601-
"permanent": False,
602-
"runtime": False,
603-
}
604-
with self.assertRaises(MockException):
605-
firewall_lib.main()
606-
am.fail_json.assert_called_with(
607-
msg="One of permanent, runtime needs to be enabled"
608-
)
609-
610542
@patch("firewall_lib.HAS_FIREWALLD", True)
611543
def test_timeout_with_disabled_state(self, am_class):
612544
am = am_class.return_value
@@ -663,23 +595,6 @@ def test_main_error_timeout_target(self, am_class):
663595
firewall_lib.main()
664596
am.fail_json.assert_called_with(msg="timeout can not be used with target only")
665597

666-
@patch("firewall_lib.FirewallClient", create=True)
667-
@patch("firewall_lib.HAS_FIREWALLD", True)
668-
@patch("firewall_lib.FW_VERSION", "0.3.8", create=True)
669-
def test_firewalld_offline_version_disconnected(self, firewall_class, am_class):
670-
am = am_class.return_value
671-
am.params = {
672-
"icmp_block_inversion": True,
673-
"permanent": True,
674-
}
675-
fw = firewall_class.return_value
676-
fw.connected = False
677-
with self.assertRaises(MockException):
678-
firewall_lib.main()
679-
am.fail_json.assert_called_with(
680-
msg="Unsupported firewalld version 0.3.8 requires >= 0.3.9"
681-
)
682-
683598
@patch("firewall_lib.FirewallClient", create=True)
684599
@patch("firewall_lib.HAS_FIREWALLD", True)
685600
@patch("firewall_lib.FW_VERSION", "0.2.8", create=True)

0 commit comments

Comments
 (0)