Fix SSH auth with id keys (#347)

I fixed the id_rsa/id_ed25519 SSH auth issue on Mac and now termscp should respect the key-based authentication, just like the regular ssh user@hostname, without the need for any ssh agents.

---

Co-authored-by: Lucas Czekaj <lukasz@czekaj.us>
This commit is contained in:
Christian Visintin
2025-06-08 18:34:59 +02:00
committed by GitHub
parent 783da22ca2
commit 81ae0035c3
4 changed files with 40 additions and 8 deletions

View File

@@ -233,8 +233,12 @@ impl RemoteFsBuilder {
debug!("no username was provided, using current username");
opts = opts.username(whoami::username());
}
// For SSH protocols, only set password if explicitly provided and non-empty.
// This allows the SSH library to prioritize key-based and agent authentication.
if let Some(password) = params.password {
opts = opts.password(password);
if !password.is_empty() {
opts = opts.password(password);
}
}
if let Some(config_path) = config_client.get_ssh_config() {
opts = opts.config_file(

View File

@@ -50,6 +50,23 @@ impl SshKeyStorage {
.and_then(|x| x.first().cloned())
})
}
/// Get default SSH identity files that SSH would normally try
/// This mirrors the behavior of OpenSSH client
fn get_default_identity_files(&self) -> Vec<PathBuf> {
let Some(home_dir) = dirs::home_dir() else {
return Vec::new();
};
let ssh_dir = home_dir.join(".ssh");
// Standard SSH identity files in order of preference (matches OpenSSH)
["id_ed25519", "id_ecdsa", "id_rsa", "id_dsa"]
.iter()
.map(|key_name| ssh_dir.join(key_name))
.filter(|key_path| key_path.exists())
.collect()
}
}
impl SshKeyStorageTrait for SshKeyStorage {
@@ -63,9 +80,13 @@ impl SshKeyStorageTrait for SshKeyStorage {
username, host
);
// otherwise search in configuration
let key = self.resolve_host_in_ssh2_configuration(host)?;
debug!("Found key in SSH config for {host}: {}", key.display());
Some(key)
if let Some(key) = self.resolve_host_in_ssh2_configuration(host) {
debug!("Found key in SSH config for {host}: {}", key.display());
return Some(key);
}
// As a final fallback, try default SSH identity files (like regular ssh does)
self.get_default_identity_files().into_iter().next()
}
}
@@ -134,8 +155,14 @@ mod tests {
*storage.resolve("192.168.1.31", "pi").unwrap(),
exp_key_path
);
// Verify unexisting key
assert!(storage.resolve("deskichup", "veeso").is_none());
// Verify key is a default key or none
let default_keys: Vec<PathBuf> = storage.get_default_identity_files().into_iter().collect();
if let Some(key) = storage.resolve("deskichup", "veeso") {
assert!(default_keys.contains(&key));
} else {
assert!(default_keys.is_empty());
}
}
#[test]