mirror of
https://github.com/veeso/termscp.git
synced 2025-12-06 17:15:35 -08:00
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:
committed by
GitHub
parent
783da22ca2
commit
81ae0035c3
@@ -46,6 +46,7 @@
|
|||||||
Released on 31/07/2025
|
Released on 31/07/2025
|
||||||
|
|
||||||
- **Updated dependencies** and updated the Rust edition to `2024`
|
- **Updated dependencies** and updated the Rust edition to `2024`
|
||||||
|
- [Issue 345](https://github.com/veeso/termscp/issues/345): Default keys are used from `~/.ssh` directory if no keys are resolved for the host.
|
||||||
|
|
||||||
## 0.17.0
|
## 0.17.0
|
||||||
|
|
||||||
|
|||||||
4
dist/build/macos.sh
vendored
4
dist/build/macos.sh
vendored
@@ -81,7 +81,7 @@ fi
|
|||||||
# Build release (x86_64)
|
# Build release (x86_64)
|
||||||
X86_TARGET=""
|
X86_TARGET=""
|
||||||
X86_TARGET_DIR=""
|
X86_TARGET_DIR=""
|
||||||
if [ "$ARCH" = "aarch64" ]; then
|
if [ "$ARCH" = "x86_64" ]; then
|
||||||
X86_TARGET="--target x86_64-apple-darwin"
|
X86_TARGET="--target x86_64-apple-darwin"
|
||||||
X86_TARGET_DIR="target/x86_64-apple-darwin/release/"
|
X86_TARGET_DIR="target/x86_64-apple-darwin/release/"
|
||||||
fi
|
fi
|
||||||
@@ -92,7 +92,7 @@ RET_X86_64=$?
|
|||||||
|
|
||||||
ARM64_TARGET=""
|
ARM64_TARGET=""
|
||||||
ARM64_TARGET_DIR=""
|
ARM64_TARGET_DIR=""
|
||||||
if [ "$ARCH" = "aarch64" ]; then
|
if [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ]; then
|
||||||
ARM64_TARGET="--target aarch64-apple-darwin"
|
ARM64_TARGET="--target aarch64-apple-darwin"
|
||||||
ARM64_TARGET_DIR="target/aarch64-apple-darwin/release/"
|
ARM64_TARGET_DIR="target/aarch64-apple-darwin/release/"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -233,9 +233,13 @@ impl RemoteFsBuilder {
|
|||||||
debug!("no username was provided, using current username");
|
debug!("no username was provided, using current username");
|
||||||
opts = opts.username(whoami::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 {
|
if let Some(password) = params.password {
|
||||||
|
if !password.is_empty() {
|
||||||
opts = opts.password(password);
|
opts = opts.password(password);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if let Some(config_path) = config_client.get_ssh_config() {
|
if let Some(config_path) = config_client.get_ssh_config() {
|
||||||
opts = opts.config_file(
|
opts = opts.config_file(
|
||||||
PathBuf::from(config_path),
|
PathBuf::from(config_path),
|
||||||
|
|||||||
@@ -50,6 +50,23 @@ impl SshKeyStorage {
|
|||||||
.and_then(|x| x.first().cloned())
|
.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 {
|
impl SshKeyStorageTrait for SshKeyStorage {
|
||||||
@@ -63,9 +80,13 @@ impl SshKeyStorageTrait for SshKeyStorage {
|
|||||||
username, host
|
username, host
|
||||||
);
|
);
|
||||||
// otherwise search in configuration
|
// otherwise search in configuration
|
||||||
let key = self.resolve_host_in_ssh2_configuration(host)?;
|
if let Some(key) = self.resolve_host_in_ssh2_configuration(host) {
|
||||||
debug!("Found key in SSH config for {host}: {}", key.display());
|
debug!("Found key in SSH config for {host}: {}", key.display());
|
||||||
Some(key)
|
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(),
|
*storage.resolve("192.168.1.31", "pi").unwrap(),
|
||||||
exp_key_path
|
exp_key_path
|
||||||
);
|
);
|
||||||
// Verify unexisting key
|
// Verify key is a default key or none
|
||||||
assert!(storage.resolve("deskichup", "veeso").is_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]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user