mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-10 20:27:02 +01:00
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.
This commit is contained in:
parent
ee740bf92e
commit
2d4331c703
@ -0,0 +1,39 @@
|
||||
{
|
||||
"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."
|
||||
]
|
||||
}
|
||||
@ -93,18 +93,30 @@ else
|
||||
test_result "/etc/hosts is immutable" "skip" "File not found"
|
||||
fi
|
||||
|
||||
# Test 2: hosts-guard.path is active
|
||||
if systemctl is-active --quiet hosts-guard.path 2>/dev/null; then
|
||||
test_result "hosts-guard.path is active" "pass"
|
||||
# Test 2: the hosts file watcher is active
|
||||
# The 2026-07-17 guard-lib refactor replaced the standalone hosts-guard.path
|
||||
# with the templated guard-file@hosts.path. Accept either, so this passes on a
|
||||
# migrated host and on one still running the legacy unit. Checking only the old
|
||||
# name reported the protection as down when it was simply renamed.
|
||||
if systemctl is-active --quiet guard-file@hosts.path 2>/dev/null ||
|
||||
systemctl is-active --quiet hosts-guard.path 2>/dev/null; then
|
||||
test_result "hosts file watcher is active" "pass"
|
||||
else
|
||||
test_result "hosts-guard.path is active" "fail" "Service not running"
|
||||
test_result "hosts file watcher is active" "fail" \
|
||||
"neither guard-file@hosts.path nor hosts-guard.path is running"
|
||||
fi
|
||||
|
||||
# Test 3: hosts-bind-mount.service is active
|
||||
if systemctl is-active --quiet hosts-bind-mount.service 2>/dev/null; then
|
||||
test_result "hosts-bind-mount.service is active" "pass"
|
||||
# Test 3: /etc/hosts is bind-mounted read-only
|
||||
# guard-bind-mount@hosts.service superseded hosts-bind-mount.service. The mount
|
||||
# itself is the thing that matters, so check that too — it is the real
|
||||
# invariant, independent of which unit established it.
|
||||
if systemctl is-active --quiet guard-bind-mount@hosts.service 2>/dev/null ||
|
||||
systemctl is-active --quiet hosts-bind-mount.service 2>/dev/null ||
|
||||
findmnt -no OPTIONS /etc/hosts 2>/dev/null | grep -q '\bro\b'; then
|
||||
test_result "/etc/hosts bind-mounted read-only" "pass"
|
||||
else
|
||||
test_result "hosts-bind-mount.service is active" "fail" "Service not running"
|
||||
test_result "/etc/hosts bind-mounted read-only" "fail" \
|
||||
"no bind-mount unit active and /etc/hosts is not mounted read-only"
|
||||
fi
|
||||
|
||||
# Test 4: Canonical hosts copy exists
|
||||
@ -114,11 +126,17 @@ else
|
||||
test_result "Canonical hosts copy exists" "fail" "Not found at /usr/local/share/locked-hosts"
|
||||
fi
|
||||
|
||||
# Test 5: nsswitch-guard.path is active (NEW)
|
||||
if systemctl is-active --quiet nsswitch-guard.path 2>/dev/null; then
|
||||
test_result "nsswitch-guard.path is active" "pass"
|
||||
# Test 5: the nsswitch watcher is active
|
||||
# guard-file@nsswitch.path superseded nsswitch-guard.path. The legacy unit is
|
||||
# left in a failed state on migrated hosts (it and the new watcher both
|
||||
# enforced the same file, so it retriggered itself into systemd's start
|
||||
# limit) — that is obsolete-unit noise, not a lapse in protection.
|
||||
if systemctl is-active --quiet guard-file@nsswitch.path 2>/dev/null ||
|
||||
systemctl is-active --quiet nsswitch-guard.path 2>/dev/null; then
|
||||
test_result "nsswitch watcher is active" "pass"
|
||||
else
|
||||
test_result "nsswitch-guard.path is active" "fail" "Service not running"
|
||||
test_result "nsswitch watcher is active" "fail" \
|
||||
"neither guard-file@nsswitch.path nor nsswitch-guard.path is running"
|
||||
fi
|
||||
|
||||
# Test 6: /etc/nsswitch.conf is immutable (NEW)
|
||||
@ -171,11 +189,26 @@ else
|
||||
test_result "day-specific-shutdown.timer is active" "fail" "Timer not running"
|
||||
fi
|
||||
|
||||
# Test 10: shutdown schedule guard is active
|
||||
# Test 10: shutdown schedule guard
|
||||
# The shutdown flow no longer powers the machine off — day-specific-shutdown
|
||||
# now hands over to night-lockdown-enter.sh, which tears down the GUI and masks
|
||||
# the TTY login while leaving the servers up. This guard was retired with the
|
||||
# old poweroff path, so a *disabled* unit is the intended state, not a fault.
|
||||
# An enabled-but-inactive unit would still mean something is wrong, so those
|
||||
# two cases are distinguished rather than both being tolerated.
|
||||
# `systemctl is-enabled` prints the state on stdout *and* exits non-zero when
|
||||
# that state is "disabled", so `|| echo disabled` would append a second line
|
||||
# and never compare equal. Capture stdout and discard the exit status instead.
|
||||
shutdown_guard_enabled=$(systemctl is-enabled shutdown-schedule-guard.path 2>/dev/null || true)
|
||||
[[ -n "$shutdown_guard_enabled" ]] || shutdown_guard_enabled=unknown
|
||||
if systemctl is-active --quiet shutdown-schedule-guard.path 2>/dev/null; then
|
||||
test_result "shutdown-schedule-guard.path is active" "pass"
|
||||
elif [[ "$shutdown_guard_enabled" == "disabled" ]]; then
|
||||
test_result "shutdown-schedule-guard.path is active" "skip" \
|
||||
"intentionally retired with the old poweroff flow (night lockdown replaced it)"
|
||||
else
|
||||
test_result "shutdown-schedule-guard.path is active" "fail" "Guard not running"
|
||||
test_result "shutdown-schedule-guard.path is active" "fail" \
|
||||
"unit is $shutdown_guard_enabled but not running"
|
||||
fi
|
||||
|
||||
# Test 11: Unlock script has obscure name (no helpful path)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user