testsAndMisc/docs/superpowers/evidence/security-hardening-guard-lib-units-2026-07-19.json
Krzysztof kuhy Rudnicki 2d4331c703
Some checks are pending
Pre-commit checks / pre-commit (push) Waiting to run
Python tests / test (push) Waiting to run
Shell tests / shell-tests (push) Waiting to run
Shell tests / arch-tests (push) Waiting to run
security-hardening: check guard-lib units, not the retired ones
The suite reported four failures on this host and none of them meant
protection was missing.

Tests 2, 3 and 5 checked hosts-guard.path, hosts-bind-mount.service
and nsswitch-guard.path. The 2026-07-17 guard-lib refactor replaced
those with templated units — guard-file@hosts.path,
guard-bind-mount@hosts.service, guard-file@nsswitch.path — which are
all active. That is also why the legacy units stop a second after
boot: they are superseded, and the old nsswitch guard retriggered
itself into systemd's start limit fighting the new watcher. Each check
now accepts either generation, and the bind-mount check also accepts
the read-only mount on /etc/hosts, which is the real invariant.

Test 10 now treats a *disabled* shutdown-schedule-guard as intended
rather than broken: the poweroff flow it protected was replaced by
night-lockdown-enter.sh. An enabled-but-inactive unit still fails.
That check also had a shell bug — `systemctl is-enabled` prints the
state and exits non-zero when disabled, so `|| echo disabled`
appended a second line and never matched.

Verified protection is genuinely intact rather than assuming it:
/etc/hosts is mounted ro,noatime and both it and /etc/nsswitch.conf
carry the immutable flag. Host now reports 18/0/6. A container with
faked host markers still reports 10 failures, so the suite has not
been softened into always-green.

No system state was changed.
2026-07-19 19:58:32 +02:00

40 lines
3.8 KiB
JSON

{
"intent": "Stop test_security_hardening.sh reporting healthy protection as broken. It checked systemd units that the guard-lib refactor renamed, and treated a deliberately retired unit as a fault.",
"scope": [
"linux_configuration/tests/test_security_hardening.sh: guard unit checks and the shutdown-schedule guard check",
"Non-goal: any change to the guard units or system state. Nothing on the host was started, stopped, enabled or disabled."
],
"changes": [
"Tests 2, 3 and 5 checked hosts-guard.path, hosts-bind-mount.service and nsswitch-guard.path. The 2026-07-17 guard-lib refactor replaced these with templated units: guard-file@hosts.path, guard-bind-mount@hosts.service and guard-file@nsswitch.path. Each check now accepts either generation, so it passes on a migrated host and on one still running the legacy units.",
"Test 3 additionally accepts the read-only bind mount on /etc/hosts itself. The mount is the actual invariant; which unit established it is an implementation detail.",
"Test 10 (shutdown-schedule-guard.path) now distinguishes disabled from enabled-but-inactive. The guard was retired along with the poweroff flow — day-specific-shutdown now hands over to night-lockdown-enter.sh — so disabled is the intended state and is reported as a skip. An enabled unit that is not running is still a failure.",
"Fixed a shell bug in that check: `systemctl is-enabled` prints the state on stdout AND exits non-zero when the state is 'disabled', so `$(... || echo disabled)` produced two lines and never compared equal."
],
"verification": [
{
"command": "bash linux_configuration/tests/test_security_hardening.sh (real host)",
"result": "pass",
"evidence": "18 passed / 0 failed / 6 skipped, up from 15/4/5. The four reported failures were stale unit names plus one intentionally retired guard, not missing protection."
},
{
"command": "systemctl is-active on the guard-lib units, plus lsattr and findmnt",
"result": "pass",
"evidence": "guard-file@hosts.path, guard-file@nsswitch.path, guard-bind-mount@hosts.service and etc-hosts.mount are all active; /etc/hosts is mounted ro,noatime; /etc/hosts and /etc/nsswitch.conf both carry the immutable flag. Protection was intact the whole time."
},
{
"command": "negative check — fake the configured-host markers inside a container with no protection installed",
"result": "pass",
"evidence": "Reports 10 failures, confirming the suite still detects genuinely absent protection and has not been softened into always-green."
}
],
"risks": [
"Accepting either unit generation means a half-migrated host — new unit running, legacy unit failed — passes. That is the current state of this machine and is harmless, but it does mean the suite will not flag leftover failed legacy units. Those remain visible in `systemctl --failed`.",
"The legacy nsswitch-guard.path and resolved-guard.path are still enabled and sit in a failed state after boot: they and the new guard-file@ watchers both enforced the same files, so each retriggered itself into systemd's start limit. Cosmetic, since the new watchers do the work, but disabling the obsolete units would clean up `systemctl --failed`. Not done here because it changes system state.",
"Test 10 keys off `is-enabled`. If the shutdown guard is ever re-enabled without being started, that correctly fails; if it is re-introduced under a new name, this check needs updating the same way tests 2/3/5 just did."
],
"rollback": [
"git revert this commit: the suite returns to checking only the legacy unit names and reports 4 failures on this host despite protection being active.",
"Validate after rollback: bash test_security_hardening.sh prints FAIL for hosts-guard.path, hosts-bind-mount.service, nsswitch-guard.path and shutdown-schedule-guard.path."
]
}