From 7d7315079b44d3628c6e81093f54298e0fe9d135 Mon Sep 17 00:00:00 2001 From: weibeu Date: Tue, 9 Jun 2026 20:09:55 +0530 Subject: [PATCH] fix: don't let ssh-keyscan abort SSH git clones MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cloneGitRepository runs `ssh-keyscan >> known_hosts` as one step of a `set -e` script. Hosts whose SSH endpoint waits for the client's identification string first — Hugging Face's hf.co among them — never complete the keyscan handshake, so it exits 1 and `set -e` aborts the deploy before `git clone` ever runs. Make ssh-keyscan non-fatal and let the real ssh client record the host key on first connect (StrictHostKeyChecking=accept-new), which reaches hosts ssh-keyscan can't scan. Same TOFU trust model, so no regression; GitHub/GitLab/Gitea still pre-seed and verify known_hosts as before. --- packages/server/src/utils/providers/git.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/server/src/utils/providers/git.ts b/packages/server/src/utils/providers/git.ts index 4c06109217..8e7c718397 100644 --- a/packages/server/src/utils/providers/git.ts +++ b/packages/server/src/utils/providers/git.ts @@ -73,7 +73,7 @@ export const cloneGitRepository = async ({ if (customGitSSHKeyId) { const sshKey = await findSSHKeyById(customGitSSHKeyId); const { port } = sanitizeRepoPathSSH(customGitUrl); - const gitSshCommand = `ssh -i /tmp/id_rsa${port ? ` -p ${port}` : ""} -o UserKnownHostsFile=${knownHostsPath}`; + const gitSshCommand = `ssh -i /tmp/id_rsa${port ? ` -p ${port}` : ""} -o UserKnownHostsFile=${knownHostsPath} -o StrictHostKeyChecking=accept-new`; command += `echo "${sshKey.privateKey}" > /tmp/id_rsa;`; command += "chmod 600 /tmp/id_rsa;"; command += `export GIT_SSH_COMMAND="${gitSshCommand}";`; @@ -111,7 +111,10 @@ const addHostToKnownHostsCommand = (repositoryURL: string) => { const { domain, port } = sanitizeRepoPathSSH(repositoryURL); const knownHostsPath = path.join(SSH_PATH, "known_hosts"); - return `ssh-keyscan -p ${port} ${domain} >> ${knownHostsPath};`; + // ssh-keyscan is best-effort: some Git hosts (e.g. Hugging Face) never answer + // it, and its exit code must not abort the clone under `set -e`. The clone's + // own host-key check (StrictHostKeyChecking=accept-new) is the real boundary. + return `ssh-keyscan -p ${port} ${domain} >> ${knownHostsPath} || true;`; }; const sanitizeRepoPathSSH = (input: string) => { const SSH_PATH_RE = new RegExp(