setup-lan-access: Include ssh-lan.conf even if it is created after start
The Include for ~/.config/devbox-shell/ssh-lan.conf was written only if the
file was readable at container start. A file created afterwards was visible in
the container and silently ignored. Hit on EMB-3HXGNXJ0 on 2026-10-18: the
tor-ms22 override was skipped, ssh fell through to the Mac's passphrase key,
and BatchMode got "Permission denied (publickey,...)" until the script was
rerun by hand.
The line is now always written. Measured with this image's OpenSSH 10.0p2,
expectations written before each run:
- file missing, and also its parent directory missing: Include skipped, rc 0
- file created after the config was written: read on the next ssh, no rerun
- a directory at that path: ignored, rc 0
- file present but unreadable: ssh aborts EVERY connection ("Can't open user
config file"). So that case is still left out, now with a warning on stderr.
- world-writable file: "Bad owner or permissions". The old code Included that
case too, so nothing changes there.
When the file exists at start, the generated config is byte-identical to
v1.12.0's. When it is absent, the only difference is the added Include block.
scripts/smoke-test.sh: two new checks, "Included even if created after start"
and "unreadable left out". Run here, the way exec_test runs them: against the
v1.12.0 script the first FAILs and the second PASSes; against this one both
PASS. README "Naming LAN peers": creating the file no longer needs a restart.
CHANGELOG: Unreleased > Fixed.
lint-shell: clean. check-doc-drift still fails, as it already did on HEAD:
pi-toolkit 3760d90->7c05901 and pi-studio 76db01f->4b4abd1 have moved upstream
since v1.12.0 and are not named in the CHANGELOG. That is release prep and is
not addressed here.
This commit is contained in:
@@ -11,6 +11,30 @@ Pre-v1.0.0 tags followed the pi npm version (`v{pi_version}[letter]`).
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **`~/.config/devbox-shell/ssh-lan.conf` is Included even if it is created
|
||||||
|
after the container started.** `setup-lan-access.sh` wrote the
|
||||||
|
`Include ~/.config/devbox-shell/ssh-lan.conf` line only if the file was
|
||||||
|
readable at container start. A file created afterwards was visible in the
|
||||||
|
container and silently ignored, so ssh fell through to `~/.ssh/config`. Hit on
|
||||||
|
EMB-3HXGNXJ0 on 2026-10-18: its `tor-ms22` override (container key, no
|
||||||
|
passphrase) was skipped. ssh then used the Mac's passphrase-protected key, and
|
||||||
|
`BatchMode` failed with `Permission denied (publickey,…)`. Rerunning the script
|
||||||
|
inside the container fixed it. The line is now written whether or not the file
|
||||||
|
exists. With this image's OpenSSH 10.0p2, an `Include` whose file (or parent
|
||||||
|
directory) is missing is skipped. A file created later is read on the next
|
||||||
|
connection, and a directory at that path is ignored. A file that **exists but
|
||||||
|
is unreadable** makes ssh abort every connection (`Can't open user config
|
||||||
|
file`), so that one case is still left out, now with a warning on stderr.
|
||||||
|
Output is byte-identical to v1.12.0 when the file exists at start.
|
||||||
|
`scripts/smoke-test.sh` gains two checks: Included when created after start,
|
||||||
|
and left out when unreadable. Against the v1.12.0 script the first fails and
|
||||||
|
the second passes. README *Naming LAN peers* no longer says that creating the
|
||||||
|
file needs a restart.
|
||||||
|
|
||||||
## v1.12.0 — 2026-10-18
|
## v1.12.0 — 2026-10-18
|
||||||
|
|
||||||
Minor release. Headlines: **pi 1.3.0**, **mempalace 3.11.0**, **pi-atelier
|
Minor release. Headlines: **pi 1.3.0**, **mempalace 3.11.0**, **pi-atelier
|
||||||
|
|||||||
@@ -1174,12 +1174,19 @@ published image (they are a fact about your LAN, not the image). Alternatively,
|
|||||||
set `DEVBOX_LAN_AUTOJUMP_PRIVATE=1` to ProxyJump *any* RFC1918 address through
|
set `DEVBOX_LAN_AUTOJUMP_PRIVATE=1` to ProxyJump *any* RFC1918 address through
|
||||||
the host without naming peers (see `.env.example`).
|
the host without naming peers (see `.env.example`).
|
||||||
|
|
||||||
Once the file exists it is re-read on every connection, so *edits* take effect
|
ssh reads the file on every connection, so creating it and editing it both take
|
||||||
immediately — no container or session restart. **Creating it for the first time
|
effect immediately — no container or session restart. `setup-lan-access.sh`
|
||||||
does need one restart**, because `setup-lan-access.sh` only emits the
|
writes the `Include ~/.config/devbox-shell/ssh-lan.conf` line whether or not the
|
||||||
`Include ~/.config/devbox-shell/ssh-lan.conf` line when the file is already
|
file exists yet (ssh skips an `Include` whose file is missing). Images up to and
|
||||||
readable at container start (`if [ -r "$SSH_LAN_CONF" ]`). Until then ssh never
|
including v1.12.0 wrote that line only if the file was readable at container
|
||||||
looks at it — which reads exactly like "my override is being ignored".
|
start. There, a file created afterwards is ignored until a restart, or until you
|
||||||
|
run `bash /usr/local/lib/pi-devbox/setup-lan-access.sh` in the container.
|
||||||
|
That looks exactly like "my override is being ignored". One case still needs
|
||||||
|
action: a file that exists but the container cannot read is left out, because
|
||||||
|
ssh would otherwise refuse every connection. The script then prints a warning at
|
||||||
|
start. Two other permission traps apply to this file as to any ssh config.
|
||||||
|
ssh rejects it if it is world-writable (`Bad owner or permissions`). And on native
|
||||||
|
Linux, the host user's uid must match the container's `developer` (1000).
|
||||||
|
|
||||||
#### Giving the container its own key for a peer
|
#### Giving the container its own key for a peer
|
||||||
|
|
||||||
|
|||||||
@@ -51,8 +51,8 @@
|
|||||||
# HOST-OWNED PEER POLICY (portable; keeps this image generic)
|
# HOST-OWNED PEER POLICY (portable; keeps this image generic)
|
||||||
# Named LAN peers are facts about a *specific* host's network, not about the
|
# Named LAN peers are facts about a *specific* host's network, not about the
|
||||||
# image — a roaming laptop sees different LANs. So we never bake peer names
|
# image — a roaming laptop sees different LANs. So we never bake peer names
|
||||||
# here. Instead, if the host bind-mounts ~/.config/devbox-shell/ssh-lan.conf
|
# here. Instead we Include ~/.config/devbox-shell/ssh-lan.conf (the host
|
||||||
# (the same devbox-shell bridge dir used for shared aliases), we Include it
|
# bind-mounts the devbox-shell bridge dir, also used for shared aliases)
|
||||||
# *before* ~/.ssh/config. That file holds the host's own jump overrides, e.g.
|
# *before* ~/.ssh/config. That file holds the host's own jump overrides, e.g.
|
||||||
# Host pve pve-2 pbs-vm
|
# Host pve pve-2 pbs-vm
|
||||||
# ProxyJump host
|
# ProxyJump host
|
||||||
@@ -151,8 +151,22 @@ EOF
|
|||||||
|
|
||||||
# Optional host-owned named-peer jump overrides (portable: lives on the host,
|
# Optional host-owned named-peer jump overrides (portable: lives on the host,
|
||||||
# not in the image). Included BEFORE ~/.ssh/config so its ProxyJump wins.
|
# not in the image). Included BEFORE ~/.ssh/config so its ProxyJump wins.
|
||||||
|
#
|
||||||
|
# Included even when the file does not exist yet. ssh skips an Include whose
|
||||||
|
# file is missing, and reads it on the next connection once it appears.
|
||||||
|
# Through v1.12.0 the Include was written only if the file was readable at
|
||||||
|
# container start. A file created later was then visible in the container but
|
||||||
|
# ignored, until a restart (EMB-3HXGNXJ0, 2026-10-18: its tor-ms22 override
|
||||||
|
# was skipped, so ssh fell back to a passphrase key and BatchMode failed).
|
||||||
|
# Only a file that EXISTS but cannot be read is left out. ssh aborts every
|
||||||
|
# connection on one ("Can't open user config file"), which would break all
|
||||||
|
# ssh, git included. A directory at that path is harmless (ssh ignores it).
|
||||||
|
# Measured with this image's OpenSSH 10.0p2; scripts/smoke-test.sh checks
|
||||||
|
# both cases.
|
||||||
SSH_LAN_CONF="${HOME}/.config/devbox-shell/ssh-lan.conf"
|
SSH_LAN_CONF="${HOME}/.config/devbox-shell/ssh-lan.conf"
|
||||||
if [ -r "$SSH_LAN_CONF" ]; then
|
if [ -e "$SSH_LAN_CONF" ] && [ ! -r "$SSH_LAN_CONF" ]; then
|
||||||
|
echo "setup-lan-access: $SSH_LAN_CONF exists but is not readable, so it is not Included (ssh would refuse every connection). Fix its permissions, then rerun this script or restart the container." >&2
|
||||||
|
else
|
||||||
LAN_CONF_BLOCK=$(cat <<'EOF'
|
LAN_CONF_BLOCK=$(cat <<'EOF'
|
||||||
|
|
||||||
# Host-owned named-peer jump overrides (bind-mounted; edit on the host).
|
# Host-owned named-peer jump overrides (bind-mounted; edit on the host).
|
||||||
|
|||||||
@@ -696,6 +696,29 @@ exec_test "git core.sshCommand matches sidecar presence" '
|
|||||||
else
|
else
|
||||||
[ -z "$cmd" ] || exit 1; echo "no sidecar, correctly unset"
|
[ -z "$cmd" ] || exit 1; echo "no sidecar, correctly unset"
|
||||||
fi'
|
fi'
|
||||||
|
# ssh-lan.conf is Included whether or not it exists at container start. Until
|
||||||
|
# v1.12.0 the Include was written only if the file was readable at start, so a
|
||||||
|
# file created later was silently ignored (EMB-3HXGNXJ0, 2026-10-18). ssh skips
|
||||||
|
# an Include whose file is missing, and picks the file up once it appears, with no
|
||||||
|
# restart. The exception is a file that exists but cannot be read: ssh then
|
||||||
|
# aborts every connection, so that case is still left out. 644 is set
|
||||||
|
# explicitly because ssh rejects a world-writable config file.
|
||||||
|
exec_test "ssh-lan.conf Included even if created after start" '
|
||||||
|
T=$(mktemp -d) && mkdir -p $T/.config/devbox-shell &&
|
||||||
|
HOME=$T DEVBOX_LAN_ACCESS=jump bash /usr/local/lib/pi-devbox/setup-lan-access.sh >/dev/null 2>&1 &&
|
||||||
|
grep -qx "Include ~/.config/devbox-shell/ssh-lan.conf" $T/.ssh-local/config &&
|
||||||
|
HOME=$T ssh -F $T/.ssh-local/config -G lanpeer 2>/dev/null | grep -qx "hostname lanpeer" &&
|
||||||
|
printf "Host lanpeer\n HostName 10.9.9.9\n" > $T/.config/devbox-shell/ssh-lan.conf &&
|
||||||
|
chmod 644 $T/.config/devbox-shell/ssh-lan.conf &&
|
||||||
|
HOME=$T ssh -F $T/.ssh-local/config -G lanpeer 2>/dev/null | grep -qx "hostname 10.9.9.9"
|
||||||
|
r=$?; rm -rf $T; exit $r'
|
||||||
|
exec_test "unreadable ssh-lan.conf left out (ssh would abort)" '
|
||||||
|
T=$(mktemp -d) && mkdir -p $T/.config/devbox-shell && : > $T/.config/devbox-shell/ssh-lan.conf &&
|
||||||
|
chmod 000 $T/.config/devbox-shell/ssh-lan.conf &&
|
||||||
|
HOME=$T DEVBOX_LAN_ACCESS=jump bash /usr/local/lib/pi-devbox/setup-lan-access.sh >/dev/null 2>&1 &&
|
||||||
|
! grep -q "ssh-lan.conf$" $T/.ssh-local/config &&
|
||||||
|
HOME=$T ssh -F $T/.ssh-local/config -G lanpeer >/dev/null 2>&1
|
||||||
|
r=$?; rm -rf $T; exit $r'
|
||||||
# The vendored mempalace snapshot is refreshed MANUALLY per release (see
|
# The vendored mempalace snapshot is refreshed MANUALLY per release (see
|
||||||
# rootfs/usr/local/share/pi-devbox/skills/VENDORED.md). Through v1.8.4 it also
|
# rootfs/usr/local/share/pi-devbox/skills/VENDORED.md). Through v1.8.4 it also
|
||||||
# silently SHADOWED the live skillset copy, so staleness was invisible — and the
|
# silently SHADOWED the live skillset copy, so staleness was invisible — and the
|
||||||
|
|||||||
Reference in New Issue
Block a user