fix(ssh): switch CI SSH to permissive mode (no known_hosts)

Root cause: ssh-keygen -R prints 'Host X not found in F:\CI\State\known_hosts'
to stderr when the IP has no prior entry. In PS5.1 with ErrorActionPreference=Stop,
native stderr is converted to ErrorRecords before 2>$null can discard them,
making the command a terminating error caught by the job's catch block.

This caused all 4 concurrent burn-in jobs to fail after ~15s with:
  Error: Host 192.168.79.XXX not found in F:\CI\State\known_hosts

Additionally, 4 concurrent SSH connections writing to the same known_hosts file
with no advisory locking risk file corruption under concurrent load.

Fix: set SshKnownHostsFile default to '' in Invoke-CIJob, Invoke-RemoteBuild,
and Get-BuildArtifacts. When KnownHostsFile='' _Transport.psm1 uses
StrictHostKeyChecking=no + UserKnownHostsFile=NUL (permissive mode), consistent
with Wait-VMReady and Prepare-LinuxBuild2404.ps1. Ephemeral CI VMs on a
local NAT network have no meaningful MITM risk.

Remove the ssh-keygen purge block from Invoke-CIJob.ps1 (no longer needed).

AGENTS.md: document PS5.1 native stderr / 2>$null interaction (error #12).
This commit is contained in:
Simone
2026-05-12 22:51:20 +02:00
parent 985e546f2d
commit fe4439b95f
4 changed files with 9 additions and 20 deletions
+2 -2
View File
@@ -64,8 +64,8 @@ param(
[string] $SshUser = 'ci_build',
# Persistent known_hosts file for SSH calls (CI jobs).
# Pass an empty string to disable host-key checking (template provisioning only).
[string] $SshKnownHostsFile = 'F:\CI\State\known_hosts'
# Empty string (default) = permissive mode (StrictHostKeyChecking=no, UserKnownHostsFile=NUL).
[string] $SshKnownHostsFile = ''
)
Set-StrictMode -Version Latest
+4 -16
View File
@@ -147,8 +147,10 @@ param(
[string] $SshUser = 'ci_build',
# Persistent known_hosts file used for CI-job SSH calls.
# Set to empty string to disable host-key checking (template provisioning only).
[string] $SshKnownHostsFile = 'F:\CI\State\known_hosts',
# Empty string (default) = permissive mode (StrictHostKeyChecking=no, UserKnownHostsFile=NUL).
# Ephemeral CI VMs on a local NAT network do not need strict host-key verification.
# Pass a file path only if you need accept-new mode for a specific deployment.
[string] $SshKnownHostsFile = '',
# Extra environment variables injected into the guest VM before the build command (§6.5).
# Keys must be valid environment variable names. Values must be plain strings.
@@ -438,20 +440,6 @@ try {
Write-Host "[Invoke-CIJob] Could not detect GuestOS from VMX — defaulting to Windows"
}
}
# ── Purge stale known_hosts entry for this IP (Linux only) ───────────
# Each clone gets a new SSH host key. Remove any previous entry for this
# IP from the CI-local known_hosts file before the first SSH connection,
# so ssh.exe uses accept-new instead of refusing a changed key.
if ($GuestOS -eq 'Linux' -and $SshKnownHostsFile -ne '') {
$khDir = Split-Path $SshKnownHostsFile -Parent
if (-not (Test-Path $khDir)) {
New-Item -ItemType Directory -Path $khDir -Force | Out-Null
}
if (Test-Path $SshKnownHostsFile) {
& ssh-keygen -R $VMIPAddress -f $SshKnownHostsFile 2>$null | Out-Null
Write-Host "[Invoke-CIJob] Purged known_hosts entry for $VMIPAddress (fresh clone)."
}
}
# ── Phase 4: Wait for readiness ───────────────────────────────────────
Write-Host "`n[Phase 4/6] Waiting for VM readiness..."
Write-JobEvent -Phase 'phase4.wait-ready' -Status 'start' -Data @{ ip = $VMIPAddress }
+2 -2
View File
@@ -141,8 +141,8 @@ param(
[string] $SshUser = 'ci_build',
# Persistent known_hosts file for SSH calls (CI jobs).
# Pass an empty string to disable host-key checking (template provisioning only).
[string] $SshKnownHostsFile = 'F:\CI\State\known_hosts',
# Empty string (default) = permissive mode (StrictHostKeyChecking=no, UserKnownHostsFile=NUL).
[string] $SshKnownHostsFile = '',
# Work directory inside the Linux guest.
[string] $GuestLinuxWorkDir = '/opt/ci/build',