Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com>
Co-authored-by: Aditya Tripathi <aditya@climactic.co>
This commit is contained in:
Andras Bacsai
2026-11-06 14:25:02 +01:00
committed by GitHub
co-authored by Claude Opus 5.5 peaklabs-dev Aditya Tripathi
parent b7dc2e435b
commit 033f0c84da
581 changed files with 58626 additions and 2456 deletions
+34
View File
@@ -49,6 +49,11 @@ class ServerConnectionCheckJob implements ShouldBeEncrypted, ShouldQueue
/** Owner marker that the SSH probe read from the host; empty when the host has none. */
private ?string $ownerMarker = null;
/** The server sent a host key that does not match the pinned key. */
private bool $hostKeyChanged = false;
private string $sshErrorOutput = '';
public function __construct(
public Server $server
) {}
@@ -84,9 +89,30 @@ class ServerConnectionCheckJob implements ShouldBeEncrypted, ShouldQueue
return;
}
// Enforce mode cannot connect without the key, so the check reads the key also of an unreachable server.
if (SshMultiplexingHelper::hostKeyCheckingMode($this->server) === 'enforce') {
SshMultiplexingHelper::learnHostKey($this->server, evenIfUnreachable: true);
}
// Check basic connectivity first
$isReachable = $this->checkConnection();
if ($this->hostKeyChanged) {
// A failed host key check with the pinned key (an unreadable known_hosts file) is a normal connection error.
$this->hostKeyChanged = $this->server->recordHostKeyMismatch($this->sshErrorOutput);
}
if (! $isReachable && $this->hostKeyChanged && SshMultiplexingHelper::hostKeyCheckingMode($this->server) === 'enforce') {
// In enforce mode a changed host key blocks every connection: stop at once, without the retry threshold.
$this->server->settings->update([
'is_reachable' => false,
'is_usable' => false,
]);
$this->logConnectionStateChange($wasReachable, $wasUsable, false, false, 'ssh_host_key_changed');
return;
}
if (! $isReachable) {
$this->server->increment('unreachable_count');
@@ -116,6 +142,9 @@ class ServerConnectionCheckJob implements ShouldBeEncrypted, ShouldQueue
return;
}
// Warn mode connects without a pinned key. Commands do not scan an unreachable server, so learn the key now.
SshMultiplexingHelper::learnHostKey($this->server, evenIfUnreachable: true);
// Server is reachable, check if Docker is available
$isUsable = $this->checkDockerAvailability();
@@ -217,6 +246,9 @@ class ServerConnectionCheckJob implements ShouldBeEncrypted, ShouldQueue
if ($this->attemptConnection()) {
return true;
}
if ($this->hostKeyChanged) {
return false;
}
}
return false;
@@ -226,6 +258,8 @@ class ServerConnectionCheckJob implements ShouldBeEncrypted, ShouldQueue
{
try {
$process = $this->runSsh(self::probeCommand(), self::SSH_TIMEOUT_SECONDS, disableMultiplexing: true);
$this->sshErrorOutput = $process->errorOutput();
$this->hostKeyChanged = SshMultiplexingHelper::isHostKeyMismatch($this->sshErrorOutput);
if ($process->exitCode() !== 0) {
$this->connectionError = 'ssh exit '.$process->exitCode().': '.Str::limit(trim($process->errorOutput()), 300);
} else {