From 7400d367a04c2e49cff6db27a1343381a107acbc Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Thu, 7 May 2026 07:21:38 +0200 Subject: [PATCH] [PR #11994/a43006c7 backport][stable-12] seport: fix idempotency when port is covered by an existing range (#12004) seport: fix idempotency when port is covered by an existing range (#11994) * fix(seport): handle port overlap with existing ranges Fixes idempotency when a requested port is already covered by an existing range registered for the same setype/proto. Also improves the error message when libsemanage raises FileNotFoundError on a port overlap validation failure. Fixes #10105 * chore(seport): add changelog fragment for #11994 --------- (cherry picked from commit a43006c7cb9400e1f4cce554cd315b936ebf7ca5) Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 --- .../fragments/11994-seport-port-overlap.yml | 2 ++ plugins/modules/seport.py | 30 ++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 changelogs/fragments/11994-seport-port-overlap.yml diff --git a/changelogs/fragments/11994-seport-port-overlap.yml b/changelogs/fragments/11994-seport-port-overlap.yml new file mode 100644 index 0000000000..200704e084 --- /dev/null +++ b/changelogs/fragments/11994-seport-port-overlap.yml @@ -0,0 +1,2 @@ +bugfixes: + - seport - fix idempotency when a requested port is already covered by an existing range registered for the same setype (https://github.com/ansible-collections/community.general/issues/10105, https://github.com/ansible-collections/community.general/pull/11994). diff --git a/plugins/modules/seport.py b/plugins/modules/seport.py index 888af2bfab..c88a083a8b 100644 --- a/plugins/modules/seport.py +++ b/plugins/modules/seport.py @@ -158,6 +158,24 @@ def semanage_port_get_ports(seport, setype, proto, local): return [] +def _parse_port_range(port): + """Return (low, high) integers for a port or port range string.""" + parts = str(port).split("-", 1) + low = int(parts[0]) + high = int(parts[1]) if len(parts) == 2 else low + return low, high + + +def _port_is_covered(port, existing_ports): + """Return True if port (or range) is fully covered by an existing port entry.""" + req_low, req_high = _parse_port_range(port) + for entry in existing_ports: + entry_low, entry_high = _parse_port_range(entry) + if entry_low <= req_low and req_high <= entry_high: + return True + return False + + def semanage_port_get_type(seport, port, proto): """Get the SELinux type of the specified port. @@ -218,7 +236,7 @@ def semanage_port_add(module, ports, proto, setype, do_reload, serange="s0", ses seport.set_reload(do_reload) ports_by_type = semanage_port_get_ports(seport, setype, proto, local) for port in ports: - if port in ports_by_type: + if _port_is_covered(port, ports_by_type): continue change = True @@ -230,6 +248,11 @@ def semanage_port_add(module, ports, proto, setype, do_reload, serange="s0", ses else: seport.modify(port, proto, serange, setype) + except FileNotFoundError as e: + module.fail_json( + msg=f"Failed to modify SELinux port policy, possibly due to a port overlap with an existing range: {e}\n", + exception=traceback.format_exc(), + ) except (ValueError, OSError, KeyError, RuntimeError) as e: module.fail_json(msg=f"{e.__class__.__name__}: {e}\n", exception=traceback.format_exc()) @@ -271,6 +294,11 @@ def semanage_port_del(module, ports, proto, setype, do_reload, sestore="", local if not module.check_mode: seport.delete(port, proto) + except FileNotFoundError as e: + module.fail_json( + msg=f"Failed to modify SELinux port policy, possibly due to a port overlap with an existing range: {e}\n", + exception=traceback.format_exc(), + ) except (ValueError, OSError, KeyError, RuntimeError) as e: module.fail_json(msg=f"{e.__class__.__name__}: {e}\n", exception=traceback.format_exc())