fix(firewall): a failed status probe no longer fabricates inactive

The §D matrix pinned this live: in a capability-restricted environment
(unprivileged LXC — ufw exists, is on PATH, and fails on dropped
CAP_NET_ADMIN) ufw status exits non-zero, and _check_ufw returned
active=False with no error signal — "probe failed" indistinguishable
from "firewall off", the same dishonesty class as plan 74's "not
installed", one rung down the ladder.

active/running are now tri-state: rc=0 parses a determined answer;
non-zero with a dbus/not-running signature is a determined
not-running (verified on Rocky 9: rc=36); anything else is None +
error. Not-installed stays a determined False. any_active propagates
the None, and the firewall tab renders an amber Status Unknown
instead of collapsing it to Inactive.
This commit is contained in:
Juan Denis committed 2026-08-17 22:58:18 -04:00
1 parent c242d4cab5
commit 2f002c5963
3 files changed
+45 -14

No files matched your search

+38 -12
View File
@@ -52,24 +52,38 @@ class FirewallService:
# Same separation as _check_ufw: a probe that fails must not be able to
# unsay an `installed` that was already determined correctly.
running = False
#
# running is tri-state. `firewall-cmd --state` answers rc=0 + "running"
# when up; a non-zero rc with a dbus/not-running error means the daemon
# is down — a *determined* not-running (verified on Rocky 9: rc=36,
# DBUS_ERROR on stderr). Any other failure shape is "could not check":
# None, never a fabricated inactive.
running = None if installed else False
error = None
default_zone = None
if installed:
try:
result = run_privileged(['firewall-cmd', '--state'])
running = 'running' in (result.stdout or '').lower()
if result.returncode == 0:
running = 'running' in (result.stdout or '').lower()
else:
evidence = ((result.stderr or '') + (result.stdout or '')).lower()
if 'not running' in evidence or 'dbus' in evidence:
running = False
else:
error = ((result.stderr or result.stdout or '')
.strip()[:200] or 'firewall-cmd --state failed')
if running:
result = run_privileged(['firewall-cmd', '--get-default-zone'])
default_zone = (result.stdout or '').strip()
except Exception:
running = False
default_zone = None
except Exception as e: # noqa: BLE001
error = str(e)
return {
'installed': installed,
'running': running,
'default_zone': default_zone
'default_zone': default_zone,
'error': error,
}
@classmethod
@@ -89,17 +103,29 @@ class FirewallService:
except Exception:
installed = False
active = False
# active is tri-state. `ufw status` answers rc=0 with "Status:
# active|inactive" — both determined. A non-zero rc (e.g. an
# unprivileged LXC, where ufw exists and is on PATH but fails on
# dropped CAP_NET_ADMIN) or an exception is "could not check": None,
# never a fabricated inactive — the shape plan 74 warned about,
# verified live in the §D capability-restricted container.
active = None if installed else False
error = None
if installed:
try:
result = run_privileged(['ufw', 'status'])
active = 'Status: active' in (result.stdout or '')
except Exception:
active = False
if result.returncode == 0:
active = 'Status: active' in (result.stdout or '')
else:
error = ((result.stderr or result.stdout or '')
.strip()[:200] or 'ufw status failed')
except Exception as e: # noqa: BLE001
error = str(e)
return {
'installed': installed,
'active': active
'active': active,
'error': error,
}
# ------------------------------------------------------------------
@@ -420,11 +420,11 @@ const FirewallTab = () => {
<>
<div className="firewall-header">
<div className="firewall-status-row">
<div className={`status-indicator ${isActive ? 'active' : 'inactive'}`}>
<div className={`status-indicator ${isActive == null ? 'unknown' : isActive ? 'active' : 'inactive'}`}>
<span className="sec-shield">
<Shield size={17} />
</span>
<span className="status-indicator__label">{isActive ? 'Firewall Active' : 'Firewall Inactive'}</span>
<span className="status-indicator__label">{isActive == null ? 'Firewall Status Unknown' : isActive ? 'Firewall Active' : 'Firewall Inactive'}</span>
<span className="firewall-type">{activeFirewall?.toUpperCase()}</span>
</div>
<div className="firewall-actions">
+5
View File
@@ -749,6 +749,11 @@
color: var(--red);
}
&.unknown .sec-shield {
background: var(--amber-bg);
color: var(--amber);
}
&__label {
font-size: 13.5px;
font-weight: $font-weight-semibold;