From 8fe0b72a042e10587d5c910bb688732b7d60e20b Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 20 Dec 2025 21:06:21 +0100 Subject: [PATCH] fix: accept new ssh host keys --- .../Sources/Clawdis/GeneralSettings.swift | 18 +++++++++++++++--- .../Sources/Clawdis/RemotePortTunnel.swift | 2 ++ apps/macos/Sources/Clawdis/Utilities.swift | 7 ++++++- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/apps/macos/Sources/Clawdis/GeneralSettings.swift b/apps/macos/Sources/Clawdis/GeneralSettings.swift index def57e70a..2f3f85106 100644 --- a/apps/macos/Sources/Clawdis/GeneralSettings.swift +++ b/apps/macos/Sources/Clawdis/GeneralSettings.swift @@ -526,7 +526,7 @@ extension GeneralSettings { timeout: 8) guard sshResult.ok else { - self.remoteStatus = .failed(self.formatSSHFailure(sshResult)) + self.remoteStatus = .failed(self.formatSSHFailure(sshResult, target: settings.target)) return } @@ -558,7 +558,13 @@ extension GeneralSettings { } private static func sshCheckCommand(target: String, identity: String) -> [String] { - var args: [String] = ["/usr/bin/ssh", "-o", "BatchMode=yes", "-o", "ConnectTimeout=5"] + var args: [String] = [ + "/usr/bin/ssh", + "-o", "BatchMode=yes", + "-o", "ConnectTimeout=5", + "-o", "StrictHostKeyChecking=accept-new", + "-o", "UpdateHostKeys=yes", + ] if !identity.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { args.append(contentsOf: ["-i", identity]) } @@ -567,12 +573,18 @@ extension GeneralSettings { return args } - private func formatSSHFailure(_ response: Response) -> String { + private func formatSSHFailure(_ response: Response, target: String) -> String { let payload = response.payload.flatMap { String(data: $0, encoding: .utf8) } let trimmed = payload? .trimmingCharacters(in: .whitespacesAndNewlines) .split(whereSeparator: \.isNewline) .joined(separator: " ") + if let trimmed, + trimmed.localizedCaseInsensitiveContains("host key verification failed") + { + let host = CommandResolver.parseSSHTarget(target)?.host ?? target + return "SSH check failed: Host key verification failed. Remove the old key with `ssh-keygen -R \(host)` and try again." + } if let trimmed, !trimmed.isEmpty { if let message = response.message, message.hasPrefix("exit ") { return "SSH check failed: \(trimmed) (\(message))" diff --git a/apps/macos/Sources/Clawdis/RemotePortTunnel.swift b/apps/macos/Sources/Clawdis/RemotePortTunnel.swift index 8d3694fd8..b84d4953c 100644 --- a/apps/macos/Sources/Clawdis/RemotePortTunnel.swift +++ b/apps/macos/Sources/Clawdis/RemotePortTunnel.swift @@ -52,6 +52,8 @@ final class RemotePortTunnel { "-o", "BatchMode=yes", "-o", "IdentitiesOnly=yes", "-o", "ExitOnForwardFailure=yes", + "-o", "StrictHostKeyChecking=accept-new", + "-o", "UpdateHostKeys=yes", "-o", "ServerAliveInterval=15", "-o", "ServerAliveCountMax=3", "-o", "TCPKeepAlive=yes", diff --git a/apps/macos/Sources/Clawdis/Utilities.swift b/apps/macos/Sources/Clawdis/Utilities.swift index be6d1cca2..e863aef7f 100644 --- a/apps/macos/Sources/Clawdis/Utilities.swift +++ b/apps/macos/Sources/Clawdis/Utilities.swift @@ -469,7 +469,12 @@ enum CommandResolver { guard !settings.target.isEmpty else { return nil } guard let parsed = self.parseSSHTarget(settings.target) else { return nil } - var args: [String] = ["-o", "BatchMode=yes", "-o", "IdentitiesOnly=yes"] + var args: [String] = [ + "-o", "BatchMode=yes", + "-o", "IdentitiesOnly=yes", + "-o", "StrictHostKeyChecking=accept-new", + "-o", "UpdateHostKeys=yes", + ] if parsed.port > 0 { args.append(contentsOf: ["-p", String(parsed.port)]) } if !settings.identity.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { args.append(contentsOf: ["-i", settings.identity])