Skip to content

fix(ci): rebuild cluster host keys per run so a node rebuild self-heals - #247

Merged
FumingPower3925 merged 1 commit into
mainfrom
fix/cluster-host-key-refresh
Aug 26, 2026
Merged

fix(ci): rebuild cluster host keys per run so a node rebuild self-heals#247
FumingPower3925 merged 1 commit into
mainfrom
fix/cluster-host-key-refresh

Conversation

@FumingPower3925

Copy link
Copy Markdown
Contributor

What broke

msa2-client was reinstalled onto a new SSD and presents a new SSH host key. StrictHostKeyChecking=accept-new accepts unknown hosts but correctly refuses changed ones, so every scheduled run after the rebuild died:

fatal: [msa2-client]: UNREACHABLE!
Host key verification failed.
msa2-server : ok=277   msr1 : ok=48   msa2-client : unreachable=1

That took out both Nightly Validation and the weekly rated Benchmark Tier.

The symptom is badly misleading — a healthy machine (19 h uptime, .195 up on the bond, on the tailnet) looks dead. The stale entry sits in the persistent ~/.ssh/known_hosts of whichever host ran the job, and Ubuntu hashes known_hosts by default, so grep finds nothing. Only ssh-keygen -F reveals it.

Fix

Scan the cluster fresh into a per-run known_hosts under the runner root (/tmp, tmpfs, gone on reboot) and point ansible at it with UserKnownHostsFile.

  • a rebuilt node is picked up automatically
  • host-key checking still applies within the run
  • the operator's persistent known_hosts is never mutated
  • unset outside CI → normal ~/.ssh/known_hosts, dev-machine behaviour unchanged

Host list comes from ansible/inventory.yml, so topology has one source of truth and adding a node needs no workflow edit.

Verified against the live cluster

test result
empty known_hosts all 3 hosts SUCCESS, 3 keys learned
well-formed wrong key planted for .195 UNREACHABLE, REMOTE HOST IDENTIFICATION HAS CHANGED
correct scanned keys SUCCESS

The negative test is the important one — it proves the file is used for verification, not merely written.

Script is bash-3.2 compatible (no mapfile, no nested heredocs) so it stays testable on the dev Mac, not only on the Linux runners.

Non-obvious finding

ControlMaster multiplexing bypasses host-key verification entirely. My first two negative tests passed misleadingly because ansible reused an already-open socket. The control sockets have to be cleared to test this at all — worth knowing for any future host-key debugging.

Security note

This is trust-on-first-use per run rather than persistent pinning. For a private-LAN bench cluster that's the right trade: the alternative is that any node rebuild silently wedges every scheduled run, and presents as hardware failure.

msa2-client was reinstalled onto a new SSD on 2026-08-18 and so presents a new
SSH host key. The inventory sets StrictHostKeyChecking=accept-new, which accepts
UNKNOWN hosts but correctly REFUSES CHANGED ones, so every subsequent scheduled
run died:

    fatal: [msa2-client]: UNREACHABLE!
    Host key verification failed.
    msa2-server : ok=277   msr1 : ok=48   msa2-client : unreachable=1

That took out both the Nightly Validation and the weekly rated Benchmark Tier.
The symptom is badly misleading: a perfectly healthy machine (19h uptime, .195
up on the bond, on the tailnet) looks dead. The stale entry lives in the
persistent ~/.ssh/known_hosts of whichever host ran the job, and because Ubuntu
hashes known_hosts by default `grep` finds nothing — only `ssh-keygen -F`
reveals it, which cost real time during diagnosis.

Fix: scan the cluster fresh into a per-run known_hosts under the runner root
(on /tmp, i.e. tmpfs, discarded on reboot) and point ansible at it via
UserKnownHostsFile. A rebuilt node is picked up automatically; host-key checking
still applies WITHIN the run; and the operator's persistent known_hosts is never
mutated. Unset outside CI, so dev-machine behaviour is unchanged.

The host list comes from ansible/inventory.yml, keeping one source of truth for
topology — adding a node needs no workflow edit.

Verified end-to-end against the live cluster:
  - empty known_hosts   -> all 3 hosts SUCCESS, 3 keys learned into the file
  - well-formed WRONG key planted for .195 -> UNREACHABLE, "REMOTE HOST
    IDENTIFICATION HAS CHANGED" (proves the file is used for verification,
    not merely written)
  - correct scanned keys -> SUCCESS
  - script runs on bash 3.2 (no mapfile, no nested heredocs) so it stays
    testable on the dev Mac, not just the Linux runners

Non-obvious detail found while testing: ControlMaster multiplexing BYPASSES
host-key verification entirely, so a negative test against an already-open
connection passes misleadingly. The sockets must be cleared to test this.
@FumingPower3925
FumingPower3925 merged commit 8535ceb into main Aug 26, 2026
11 of 12 checks passed
@FumingPower3925
FumingPower3925 deleted the fix/cluster-host-key-refresh branch August 26, 2026 07:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant