mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-10 20:07:04 +01:00
fix(hosts-guard): stop setup aborting silently, so the guards actually enable
setup_hosts_guard.sh died partway on every run under `set -euo pipefail`, which is why hosts-guard.path / hosts-bind-mount.service / nsswitch-guard.path sat installed-but-disabled since 2026-02-02 and resolved-guard.path was never installed at all — /etc/hosts had no watcher to restore it if tampered. Two aborts, both silent: 1. `local_dot=$(grep -E '^\s*DNSOverTLS\s*=' ... | tail -1 | sed ...)` — grep exits 1 when the key is absent, pipefail propagates, the assignment returns 1, set -e kills the script. DNSOverTLS is absent from a default resolved.conf, so this fired on EVERY run, right before resolved-guard.path was enabled. The fix-if-missing branch below it was unreachable because detecting "missing" aborted first. Same trap on the ReadEtcHosts probe. check_and_enable_services.sh already guarded its copy with `|| true`. 2. `systemctl restart systemd-resolved` — resolved is MASKED here (dnsmasq serves DNS), so the restart failed and aborted the script on its last step, after everything was already configured. Now skipped with a note. Before: exit 1, resolved-guard disabled, nsswitch-guard start-limit-hit. After: exit 0; hosts-guard.path, hosts-bind-mount.service, nsswitch-guard.path and resolved-guard.path all enabled+active. DNS still resolves; getent ahosts on doubleclick/googlesyndication/facebook returns 0.0.0.0; /etc/hosts still immutable at 176888 lines. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JsCs1GUyiinA7bg68YvAh1
This commit is contained in:
parent
aba288d418
commit
779ace0c87
@ -0,0 +1,26 @@
|
||||
{
|
||||
"intent": "setup_hosts_guard.sh aborted partway on every run under `set -euo pipefail`, silently leaving the hosts guards half-configured or disabled. hosts-guard.path, hosts-bind-mount.service and nsswitch-guard.path were installed 2026-02-02 but disabled, and resolved-guard.path/enforce-resolved.sh were never installed at all — so /etc/hosts had no watcher to restore it if tampered. Make the installer run to completion so all four guards actually get enabled.",
|
||||
"scope": [
|
||||
"linux_configuration/scripts/periodic_background/hosts/guard/setup_hosts_guard.sh",
|
||||
"Non-goals: blocklist content, dnsmasq config, the other drifted services (thesis_tracker, focus_mode, guest_mode_removal, workout_locker) which the author will repair separately"
|
||||
],
|
||||
"changes": [
|
||||
"Added `|| true` to the ReadEtcHosts and DNSOverTLS detection assignments. grep exits 1 when a key is absent; with set -euo pipefail that aborted the whole script — silently skipping the very fix-if-missing branch below it. DNSOverTLS is absent from a default resolved.conf, so the run died there on EVERY invocation, right before resolved-guard.path was enabled. (check_and_enable_services.sh already guarded its copy of the same grep with `|| true`; this brings the installer in line.)",
|
||||
"Guarded the final `systemctl restart systemd-resolved`: systemd-resolved is MASKED on this host (dnsmasq serves DNS), so restarting it failed and set -e aborted the script on its last step — after everything was already configured, making a successful run report failure. Now skips with a note when resolved is masked/disabled."
|
||||
],
|
||||
"verification": [
|
||||
{
|
||||
"command": "sudo bash setup_hosts_guard.sh; systemctl is-enabled/is-active for the four guard units; getent ahosts on blocked domains; bash -n + shellcheck --severity=warning",
|
||||
"result": "pass",
|
||||
"evidence": "Before: exit 1, resolved-guard.path disabled/inactive, nsswitch-guard.path failed (unit-start-limit-hit). After: exit 0, and hosts-guard.path / hosts-bind-mount.service / nsswitch-guard.path / resolved-guard.path are all enabled+active. DNS still resolves (dnsmasq untouched); getent ahosts doubleclick.net / googlesyndication.com / facebook.com all return 0.0.0.0; /etc/hosts still immutable with 176888 lines. parse OK, shellcheck clean."
|
||||
}
|
||||
],
|
||||
"risks": [
|
||||
"The guards are now actually live: /etc/hosts has a read-only bind mount and a path watcher that reverts edits, and nsswitch.conf/resolved.conf are enforced against canonical snapshots. Editing those files now requires the documented unlock path (sudo unlock-hosts) or `setup_hosts_guard.sh --uninstall`.",
|
||||
"nsswitch-guard hit 'unit-start-limit-hit' once during the first run (the enforcement rewrites the file it watches, re-triggering itself). It settled to active, but a rapid drift/restore cycle could trip systemd's start limit again."
|
||||
],
|
||||
"rollback": [
|
||||
"sudo bash setup_hosts_guard.sh --uninstall (disables the units and removes them; canonical snapshots are left in place).",
|
||||
"Validate after rollback: the four units are gone/disabled, /etc/hosts is no longer bind-mounted (findmnt /etc/hosts is empty), and DNS still resolves."
|
||||
]
|
||||
}
|
||||
@ -456,8 +456,13 @@ if [[ $ENABLE_RESOLVED -eq 1 ]]; then
|
||||
|
||||
# Ensure ReadEtcHosts=yes in resolved.conf before snapshotting
|
||||
if [[ -f "$RESOLVED_CONF" ]]; then
|
||||
# `|| true` is REQUIRED: grep exits 1 when the key is absent, and under
|
||||
# `set -euo pipefail` that aborts the whole script — silently skipping the
|
||||
# very fix below that exists to handle the absent case. Without it the
|
||||
# "key missing" branch is unreachable. (check_and_enable_services.sh
|
||||
# already guards its copy of this grep the same way.)
|
||||
local_read_hosts=$(grep -E '^\s*ReadEtcHosts\s*=' "$RESOLVED_CONF" 2>/dev/null |
|
||||
tail -1 | sed 's/.*=\s*//' | tr -d '[:space:]')
|
||||
tail -1 | sed 's/.*=\s*//' | tr -d '[:space:]') || true
|
||||
if [[ "$local_read_hosts" != "yes" ]]; then
|
||||
msg "Fixing ReadEtcHosts in resolved.conf (was: '$local_read_hosts')"
|
||||
chattr -i "$RESOLVED_CONF" 2>/dev/null || true
|
||||
@ -472,8 +477,12 @@ if [[ $ENABLE_RESOLVED -eq 1 ]]; then
|
||||
fi
|
||||
|
||||
# Ensure DNSOverTLS is not set to yes or opportunistic
|
||||
# `|| true`: same trap as above. DNSOverTLS is absent from a default
|
||||
# resolved.conf, so without this the script aborted here on every run —
|
||||
# right before resolved-guard.path was enabled, which is why that guard
|
||||
# was never installed.
|
||||
local_dot=$(grep -E '^\s*DNSOverTLS\s*=' "$RESOLVED_CONF" 2>/dev/null |
|
||||
tail -1 | sed 's/.*=\s*//' | tr -d '[:space:]')
|
||||
tail -1 | sed 's/.*=\s*//' | tr -d '[:space:]') || true
|
||||
if [[ -n "$local_dot" && "$local_dot" != "no" ]]; then
|
||||
msg "Disabling DNSOverTLS in resolved.conf (was: '$local_dot')"
|
||||
chattr -i "$RESOLVED_CONF" 2>/dev/null || true
|
||||
@ -514,8 +523,16 @@ if [[ $ENABLE_RESOLVED -eq 1 ]]; then
|
||||
"$INSTALL_ENFORCE_RESOLVED" || warn "resolved enforcement returned non-zero"
|
||||
fi
|
||||
|
||||
# Restart resolved to pick up corrected config
|
||||
run systemctl restart systemd-resolved
|
||||
# Restart resolved to pick up corrected config.
|
||||
# systemd-resolved is not necessarily the resolver: on this host it is MASKED
|
||||
# and dnsmasq serves DNS. Restarting a masked unit fails, and under `set -e`
|
||||
# that aborted the script on its very last step — after everything was already
|
||||
# configured, so the run reported failure despite having succeeded.
|
||||
if systemctl is-enabled systemd-resolved.service &>/dev/null; then
|
||||
run systemctl restart systemd-resolved || warn "Failed to restart systemd-resolved"
|
||||
else
|
||||
note "systemd-resolved is masked/disabled — skipping restart (DNS is served elsewhere, e.g. dnsmasq)"
|
||||
fi
|
||||
else
|
||||
note "Skipping resolved.conf protection (--skip-resolved)"
|
||||
fi
|
||||
|
||||
Loading…
Reference in New Issue
Block a user