fix(bootstrap): sshd hardening never applied on cloud images #6
No reviewers
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#6
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/sshd-hardening-first-wins"
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?
The bug
rig bootstrapwrote its hardening drop-in as99-rig.conf. Cloud images ship/etc/ssh/sshd_config.d/50-cloud-init.confcarryingPasswordAuthentication yes,and
sshd_configis first-wins — "for each keyword, the first obtained valuewill be used" (
sshd_config(5)) — withIncludeexpanding its glob in lexicalorder. So
50-was read before99-, and every keyword rig set was silentlydiscarded.
Every Hetzner box rig has bootstrapped was still serving
passwordauthentication yes. Confirmed oncoolify-box(CX23) andci-runner(CX43) viasshd -T, andfrom off-tailnet via
ssh -o PreferredAuthentications=none:Root logins were never exposed (
PermitRootLoginresolved toprohibit-passwordvia Debian's stock config, not via rig), but any password-bearing non-root account
was reachable on a public port 22.
Why it shipped green
bootstrapasserted that the file existed, not whatsshdactually resolved —and the Incus rehearsal runs in a pristine Debian container that has no cloud-init
drop-in to lose to. The one gate that could have caught this was structurally
blind to it. That is the real defect; the filename is just its symptom.
Fixes
00-rig.confso it is read first and actually wins. Sweeps astale
99-rig.confon re-run, so existing boxes converge by re-runningbootstrap.sshd -T), and refuse to claim a hardened boxotherwise. Verifying the artifact instead of the effect is what let this through.
sshand checked afterwards — on a box whose only door is SSH, restarting against aconfig
sshdwon't parse leaves no listener and no way back.sshd -tnow parseseverything
sshdwould (ours, cloud-init's, any third-party file); on failure thedrop-in is rolled back and the daemon is left untouched. Verified: a bad neighbour
drop-in exits 255 and never reaches
systemctl restart.coolify-boxgreeted the operator as
root@internal-tooling. The shell prompt is the only "am Ion the right box" signal before a destructive command, and it was lying on every box
rig built — it very nearly sent a diagnostic to the wrong host while this bug was
being chased.
re-running it to pick up this fix demanded a Tailscale key it would never spend —
friction on precisely the repair path that matters.
Operator follow-up
Re-run
rig bootstraponcoolify-boxandci-runneronce this lands; both converge(no pre-auth key needed — they are already joined). Bootstrap the new prod CX33 with the
fixed rig from the start.
bash -n+shellcheckclean.🤖 Generated with Claude Code