fix(sanity): verify the ControlPath ssh RESOLVES, not the dir the image creates
recreate-sanity-check.sh asserted `/tmp/sshcm exists with mode 700` and printed
a green tick while every ssh in the container died rc=255. It was right about
what it checked: the breakage was in the directory a CONFIG named, not the one
the image creates, and the old check could not see the disagreement between them.
Found on the v1.9.2 first boot on emb-7kj4vr4g. A durable ~/.pi/ssh/config,
hand-written into the ~/.pi named volume by the previous session so it would
survive the recreate, declared `ControlPath /tmp/ssh-cm/%C` — with a hyphen.
Nothing here creates that path; the canonical spelling is /tmp/sshcm, identical
in Dockerfile.base, entrypoint-user.sh, recreate-sanity-check.sh and
smoke-test.sh. ControlMaster auto with an unusable ControlPath does not degrade
to an unmultiplexed connection — it fails hard:
unix_listener: cannot bind to path /tmp/ssh-cm/<hash>: No such file or directory
rc=255, remote command never runs.
Now: resolve the ControlPath ssh itself would use via `ssh -G` and require its
parent to exist and be writable. -G applies real config precedence (first-value-
wins, the system drop-in, Include, an -F override), so it answers "which rule
captured this host" instead of re-implementing the guess, and it never opens a
connection — 0.116s for 48 hosts.
This also puts a check under a caveat documented in prose in Dockerfile.base
("SSH client defaults") and verified nowhere: a per-host ControlPath under a
read-only bind-mounted ~/.ssh gives the identical failure, "Read-only file
system". On the machine this was built on that is 16 of 50 hosts — freeipa-1..6,
gitea.egl.lan, runner-1..3, tor-ms22 — never reported by anything before.
Severity split is deliberate. Default ssh precedence legitimately lands in the
read-only ~/.ssh for any host whose own config pins it there, and the supported
workaround (ssh -F ~/.ssh-local/config, generated every start by
setup-lan-access.sh) already exists, so that is a warn. Failing it would paint
the script red on every run of every device, and a check that fires benignly
every time is one you learn to ignore — the same reasoning that keeps
lint-shell.sh at -S error. The sidecar route is prescribed, so there it is a
hard fail. Host lists cap at six names plus a count: unreadable output is
ignored output.
Teeth proven both directions, each sabotage confirmed by diff BEFORE the result
was believed: ControlPath -> nonexistent dir => rc=1; -> existing-but-read-only
dir => rc=1; restored => rc=0 with the sidecar byte-identical.
No config was added to fix the original problem — the fix was to DELETE
~/.pi/ssh/config, a third hand-maintained copy of what setup-lan-access.sh
already generates from version control on every container start, with fewer
features and one typo. The host-owned ~/.ssh/config is left alone on purpose:
those ~/.ssh/cm paths are correct on the host, where ~/.ssh is writable.
Also lint-shell.sh: the SC2088 count in the severity-choice rationale said 19
and is now 20, with the reproduce command recorded. That command needs a `$ `
prefix — a comment whose first word is "shellcheck" is parsed as a directive,
and the malformed one tripped SC1072/SC1073 at severity error. The gate caught
it on the very commit that introduced it.
This commit is contained in:
@@ -32,10 +32,16 @@
|
||||
#
|
||||
# SEVERITY CHOICE
|
||||
# -S error is 0 findings across this repo when clean, so it is free to add.
|
||||
# -S warning is NOT free here (19x SC2088 tilde-in-quotes in
|
||||
# -S warning is NOT free here (20x SC2088 tilde-in-quotes in
|
||||
# recreate-sanity-check.sh, plus assorted SC2016 — both intentional), and a
|
||||
# noisy gate trains people to ignore it. Error-only, matching the
|
||||
# SHELLCHECK_OPTS philosophy in lint.yml.
|
||||
# Reproduce the count before editing it (the `$ ` prefix is load-bearing: a
|
||||
# comment whose first word is "shellcheck" is parsed as a DIRECTIVE, and a
|
||||
# malformed one is SC1072/SC1073 at severity error — this gate caught exactly
|
||||
# that when the line was first written without it):
|
||||
# $ shellcheck -S warning -f gcc scripts/*.sh rootfs/usr/local/bin/* \
|
||||
# entrypoint*.sh hooks/* | grep -c SC2088
|
||||
#
|
||||
# Usage: bash scripts/lint-shell.sh [root] (default root: repo top level)
|
||||
set -uo pipefail
|
||||
|
||||
Reference in New Issue
Block a user