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:
Krzysztof Rudnicki 2026-07-17 08:25:51 +02:00
parent 62158b0137
commit fab9d6ecf8

View File

@ -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