fix(hosts): stop chattr +i'ing install.sh and generate_hosts_file.sh sources
Some checks are pending
Pre-commit checks / pre-commit (push) Waiting to run

Immutable git-tracked files break git tooling. The pre-push ci-mirror gate
runs `pre-commit run --all-files`; end-of-file-fixer opens every file rb+
and dies with "PermissionError: Operation not permitted", so no push from a
normal checkout could ever succeed — every push had to be routed through a
throwaway worktree (a fresh checkout gets new, non-immutable inodes). The
same immutability also breaks pre-commit's unstaged-changes stash: the
`git checkout -- .` unlink fails, the restore never runs, and unrelated
unstaged edits are silently reverted.

Enforcement does not depend on these sources being locked: /etc/hosts is
itself chattr +i, hosts-guard.path watches and re-enforces it via
enforce-hosts.sh against the locked-hosts canonical snapshot, and
hosts-bind-mount.service pins it. Editing the sources changes nothing until
install.sh is re-run as root, which regenerates the guarded artifacts.

Companion to 6e3d61d (same fix for setup_midnight_shutdown.sh).

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:06:46 +02:00
parent 6e3d61daa2
commit daa712a122
2 changed files with 41 additions and 13 deletions

View File

@ -0,0 +1,27 @@
{
"intent": "Stop hosts/install.sh from chattr +i'ing its own git-tracked source and generate_hosts_file.sh. Immutable tracked files break git tooling: the pre-push ci-mirror gate runs `pre-commit run --all-files`, whose end-of-file-fixer opens every file rb+ and dies with 'PermissionError: [Errno 1] Operation not permitted', so no push from a normal checkout can succeed (every push had to be routed through a throwaway git worktree, where a fresh checkout has new, non-immutable inodes). The same immutability also breaks pre-commit's unstaged-changes stash and silently reverts unrelated edits.",
"scope": [
"linux_configuration/scripts/periodic_background/hosts/install.sh (remove the self-lock block only)",
"Cleared the immutable bit on hosts/install.sh and hosts/generate_hosts_file.sh so no tracked repo file is immutable",
"Non-goals: any change to hosts blocklist content, generation logic, or the runtime guard stack"
],
"changes": [
"Removed the `chattr +i` self-lock of install.sh and generate_hosts_file.sh, replaced with a comment explaining why it must not be reintroduced.",
"Runtime hosts enforcement is unaffected and independent: /etc/hosts is itself chattr +i, hosts-guard.path watches and re-enforces it via enforce-hosts.sh against the /usr/local/share/locked-hosts canonical snapshot, and hosts-bind-mount.service pins it. Editing the sources changes nothing until install.sh is re-run as root, which regenerates the guarded artifacts.",
"Companion to the same fix in setup_midnight_shutdown.sh (commit 6e3d61d)."
],
"verification": [
{
"command": "bash -n + shellcheck --severity=warning on hosts/install.sh; git ls-files | xargs lsattr; git push origin main from the normal checkout",
"result": "pass",
"evidence": "parse OK; shellcheck CLEAN; no removed vars (INSTALL_SCRIPT/GEN_SCRIPT/SCRIPT_DIR) referenced later; no tracked repo file reports the immutable bit; push from main succeeds without a worktree and pre-commit logs 'Restored changes from ...' (the restore step that previously never ran)."
}
],
"risks": [
"Removes one speed bump: the hosts source scripts can now be edited without first running `sudo chattr -i`. The guarded /etc/hosts, the path watcher, the canonical snapshot and the bind mount remain the substantive controls."
],
"rollback": [
"git revert the commit and re-run `sudo bash linux_configuration/scripts/periodic_background/hosts/install.sh` to restore the self-locks.",
"Validate after rollback: lsattr shows the 'i' bit on both scripts; note that pushes from main will again fail the ci-mirror gate and require a worktree."
]
}

View File

@ -890,17 +890,18 @@ else
fi
# ============================================================================
# LOCK THIS SCRIPT AND generate_hosts_file.sh AGAINST SILENT EDITS
# NOTE: this used to `chattr +i` its own source and generate_hosts_file.sh
# ("lock against silent edits"). Do NOT reintroduce that: making a git-tracked
# file immutable breaks git tooling. `pre-commit run --all-files` (which the
# pre-push ci-mirror gate runs) opens every file rb+ via end-of-file-fixer and
# dies with "PermissionError: Operation not permitted", so no push from a normal
# checkout can ever succeed. It also breaks pre-commit's stash of unstaged
# changes (`git checkout -- .` cannot unlink an immutable file), which silently
# reverts unrelated unstaged edits.
#
# Enforcement does not depend on these sources being immutable: /etc/hosts
# itself is chattr +i, hosts-guard.path watches and re-enforces it via
# enforce-hosts.sh against the /usr/local/share/locked-hosts canonical snapshot,
# and hosts-bind-mount.service pins it. Editing these sources changes nothing
# until install.sh is re-run as root, which regenerates the guarded artifacts.
# ============================================================================
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
INSTALL_SCRIPT="$(readlink -f "$0")"
chattr +i "$INSTALL_SCRIPT" 2>/dev/null \
|| echo "⚠ Warning: Could not set immutable attribute on install.sh"
echo "✓ install.sh locked (chattr +i) — run 'sudo chattr -i $INSTALL_SCRIPT' to unlock for future changes"
GEN_SCRIPT="$SCRIPT_DIR/generate_hosts_file.sh"
if [[ -f $GEN_SCRIPT ]]; then
chattr +i "$GEN_SCRIPT" 2>/dev/null \
|| echo "⚠ Warning: Could not set immutable attribute on generate_hosts_file.sh"
echo "✓ generate_hosts_file.sh locked (chattr +i)"
fi