From fe4439b95fea991ffdd8d0dc11d853848bc5b334 Mon Sep 17 00:00:00 2001 From: Simone Date: Tue, 12 May 2026 22:51:20 +0200 Subject: [PATCH] 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). --- AGENTS.md | 1 + scripts/Get-BuildArtifacts.ps1 | 4 ++-- scripts/Invoke-CIJob.ps1 | 20 ++++---------------- scripts/Invoke-RemoteBuild.ps1 | 4 ++-- 4 files changed, 9 insertions(+), 20 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 9d74be9..012647a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -153,3 +153,4 @@ Configurazione in `PSScriptAnalyzerSettings.psd1`. Regole principali: 9. **Snapshot template catturato con VM accesa** — produce file `*.vmem` / `*.vmsn` con memoria; `vmrun clone ... linked -snapshot=` rifiuta con `Error: The virtual machine should not be powered on. It is already running.` anche se `vmrun list` mostra 0 VM. Catturare snapshot SOLO da stato fully powered-off (verificare assenza di `.vmem` nella dir template). 10. **`vmrun getGuestIPAddress` per check "VM running"** — richiede VMware Tools attivi nel guest; in headless Tools puo' impiegare 30-60s a rispondere e il check appare bloccato. Usare `vmrun list` e cercare il path del VMX nell'output. 11. **Machine-id identico tra clone Linux** — le cloud image Ubuntu hanno un `/etc/machine-id` fisso. VMware DHCP assegna l'IP tramite DHCP client-id derivato da machine-id: se tutti i clone hanno lo stesso machine-id ottengono lo stesso IP e si bloccano per "IP collision". Fix nel template (prima di prendere lo snapshot): `sudo truncate -s 0 /etc/machine-id && sudo rm -f /var/lib/dbus/machine-id && sudo shutdown -h now`, poi ricreare lo snapshot `BaseClean-Linux` da stato powered-off. +12. **`& nativecmd 2>$null` NON sopprime stderr in PS 5.1 con `$ErrorActionPreference='Stop'`** — la stderr del processo nativo viene convertita in ErrorRecord nel pipeline di PowerShell; sotto `'Stop'` questo diventa un errore terminante PRIMA che `2>$null` possa scartarlo. Esempio: `ssh-keygen -R -f ` stampa "Host X not found in file" su stderr → eccezione terminante. Fix: usare `$ErrorActionPreference = 'SilentlyContinue'` attorno alla chiamata oppure `2>&1 | Out-Null`. Per i job CI su VM ephemere locali, usare direttamente `StrictHostKeyChecking=no UserKnownHostsFile=NUL` (default in `_Transport.psm1` con `KnownHostsFile=''`) — nessun `ssh-keygen` necessario. diff --git a/scripts/Get-BuildArtifacts.ps1 b/scripts/Get-BuildArtifacts.ps1 index e2370c7..3201380 100644 --- a/scripts/Get-BuildArtifacts.ps1 +++ b/scripts/Get-BuildArtifacts.ps1 @@ -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 diff --git a/scripts/Invoke-CIJob.ps1 b/scripts/Invoke-CIJob.ps1 index c8f3925..08f1598 100644 --- a/scripts/Invoke-CIJob.ps1 +++ b/scripts/Invoke-CIJob.ps1 @@ -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 } diff --git a/scripts/Invoke-RemoteBuild.ps1 b/scripts/Invoke-RemoteBuild.ps1 index 6c180a4..fd31f50 100644 --- a/scripts/Invoke-RemoteBuild.ps1 +++ b/scripts/Invoke-RemoteBuild.ps1 @@ -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',