URGENT: sshd -t failing for a missing /run/sshd is reported as 'sshd rejects the merged config' — bootstrap blocked, stderr discarded #92
Labels
No labels
attention
blocked
blocker:ci-red
blocker:conflict
blocker:drill-pending
blocker:unrequested
bug
claimed
documentation
enhancement
epic
merge-next
needs-ruling
needs-triage
offsite
post-merge
ready
release
scope:bootstrap
scope:coolify
scope:db
scope:docs
scope:drill
scope:installer
scope:labels
scope:platform
scope:runner
scope:users
stale
state:addressing
state:bots-reviewing
state:building
state:needs-human
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: heavy-duty/rig#92
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
harden_sshd()treats any non-zero exit fromsshd -tas "the merged config is invalid", and discards the stderr that says otherwise. Whensshd -tfails for an environmental reason — most commonly a missing/run/sshdprivilege-separation directory — bootstrap aborts with a diagnosis that is simply false, and sends the operator to audit config files that were never broken.There is no bad file.
sshd -tnever reached a parse verdict.Cause
commands/lib/sshd.sh:45(and the identicalcommands/users-close-root.sh:273):sshd -tchecks for the privsep directory in addition to parsing the config, and exits non-zero if it is absent.2>/dev/nullthrows away the single line that distinguishes the two failures, so the exit code alone drives a message that asserts a cause the code never established.The follow-up hint compounds it:
Run 'sshd -t' to see which file is bad— the operator runs it, getsMissing privilege separation directory, and now has an error naming no file at all.rig never creates
/run/sshdand never mentions it (grep -rn 'run/sshd'over the tree: no hits).Why the directory is missing
/runis a tmpfs. On Debian/Ubuntu,/run/sshdis created by systemd fromRuntimeDirectory=sshdinssh.service— and removed when that unit stops. So it is legitimately absent wheneverssh.serviceis not the running form of sshd, notably under socket activation (ssh.socket, the default on Ubuntu 22.10+ and Debian 13), where per-connectionssh@.serviceinstances serve the door and no long-livedssh.serviceholds the directory open.This is not an exotic state: it is reachable on a stock, fully-working box where SSH is serving connections normally. The operator is SSHed in through sshd at the moment rig declares sshd's config broken.
Observed in the wild on a box mid-bootstrap immediately after an
openssh-serverupgrade (needrestart reportingsshd-sessionon an outdated binary).Impact
/etc/ssh/sshd_config.d/and finds nothing.rig users close-rootcarries the same defect atusers-close-root.sh:273, on the more dangerous path: it is the command that shuts the root door.Suggested fix
Distinguish "cannot test" from "config is bad". Roughly:
Two things worth taking regardless of the mechanism chosen:
$errin the die message. The current text asserts a cause and then withholds the evidence. Every variant of this bug is self-diagnosing the moment stderr is surfaced.sshd -G(OpenSSH 9.4+) for validation — it parses and dumps the effective config without the privsep-directory precondition, which is exactly the narrower question rig is asking here. Worth confirming against the oldest OpenSSH rig supports before relying on it.Fix both call sites; they are the same three lines.
Workaround
Note the directory lives on tmpfs and will not survive a reboot — but by then
ssh.servicerecreates it, so this is a one-shot unblock for the bootstrap run, not something to persist.Marked urgent: it hard-blocks bootstrap on a default configuration of current Debian/Ubuntu, and the error text actively misdirects the diagnosis.
rig users close-rootshares the flaw.@heavy-duty/agents — please review.