mirror of
https://github.com/veeso/termscp.git
synced 2025-12-06 17:15:35 -08:00
Use ssh2 config IdentityFile as fallback for key based authentication
This commit is contained in:
@@ -35,6 +35,7 @@ Released on ??
|
||||
- **Changed keybindings for BACKTAB**: backtab will now change the explorer tab
|
||||
- To active the LOG PANEL, use `P`
|
||||
- **Yes/No dialogs** are now answerable by pressing `Y` or `N` on your keyboard ([Issue 121](https://github.com/veeso/termscp/issues/121))
|
||||
- **Use ssh2 config IdentityFile** as fallback for key based authentication
|
||||
- **Bugfix**
|
||||
- Fixed [Issue 122](https://github.com/veeso/termscp/issues/122)
|
||||
- Fixed version comparison when going above 0.9
|
||||
@@ -44,6 +45,8 @@ Released on ??
|
||||
- Bump `keyring` to `1.2.0`
|
||||
- Bump `open` to `3.0.2`
|
||||
- Changed `regex` to `lazy-regex 2.3.0`
|
||||
- Bump `remotefs-ssh` to `0.1.2`
|
||||
- Bump `ssh2-config` to `0.1.3`
|
||||
- Bump `tuirealm` to `1.8.0`
|
||||
- Bump `tui-realm-stdlib` to `1.1.7`
|
||||
- Added `version-compare 0.1.0`
|
||||
|
||||
17
Cargo.lock
generated
17
Cargo.lock
generated
@@ -1719,6 +1719,12 @@ version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "498a099351efa4becc6a19c72aa9270598e8fd274ca47052e37455241c88b696"
|
||||
|
||||
[[package]]
|
||||
name = "path-slash"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42"
|
||||
|
||||
[[package]]
|
||||
name = "pathdiff"
|
||||
version = "0.2.1"
|
||||
@@ -1938,7 +1944,7 @@ checksum = "9939849e0b3895b07f258458d0fa5cd4632c78ffc517e66e1f758ddf23990542"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"log",
|
||||
"path-slash",
|
||||
"path-slash 0.1.5",
|
||||
"remotefs",
|
||||
"rust-s3",
|
||||
"thiserror",
|
||||
@@ -1952,7 +1958,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "359fd989a6ad50fa6defae771e453695412cbfdb5476f49e42d1bb1d51b5c096"
|
||||
dependencies = [
|
||||
"log",
|
||||
"path-slash",
|
||||
"path-slash 0.1.5",
|
||||
"remotefs",
|
||||
"suppaftp",
|
||||
"users",
|
||||
@@ -1960,14 +1966,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "remotefs-ssh"
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea2ac5a7f475c1b63ffd2aaab57ca4e37e6a6ed23461046d4fb1554835e6088a"
|
||||
checksum = "190106b53b839cf094172d2f1bc14e8d2846a69de06b239f11c2fc353080e9e0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"path-slash",
|
||||
"path-slash 0.2.1",
|
||||
"regex",
|
||||
"remotefs",
|
||||
"ssh2",
|
||||
@@ -2511,6 +2517,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serial_test",
|
||||
"simplelog",
|
||||
"ssh2-config",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"toml",
|
||||
|
||||
@@ -51,11 +51,12 @@ rand = "0.8.5"
|
||||
remotefs = "^0.2.0"
|
||||
remotefs-aws-s3 = "^0.2.0"
|
||||
remotefs-ftp = { version = "^0.1.0", features = [ "secure" ] }
|
||||
remotefs-ssh = { version = "^0.1.1", features = [ "ssh2-vendored" ] }
|
||||
remotefs-ssh = { version = "^0.1.2", features = [ "ssh2-vendored" ] }
|
||||
rpassword = "6.0.1"
|
||||
self_update = { version = "0.30.0", features = [ "archive-tar", "archive-zip", "compression-flate2", "compression-zip-deflate" ] }
|
||||
serde = { version = "^1.0.0", features = [ "derive" ] }
|
||||
simplelog = "0.12.0"
|
||||
ssh2-config = "^0.1.3"
|
||||
tempfile = "3.2.0"
|
||||
thiserror = "^1.0.0"
|
||||
toml = "0.5.0"
|
||||
|
||||
@@ -5,12 +5,16 @@
|
||||
// Locals
|
||||
use super::config_client::ConfigClient;
|
||||
// Ext
|
||||
use remotefs_ssh::SshKeyStorage as SshKeyStorageT;
|
||||
use remotefs_ssh::SshKeyStorage as SshKeyStorageTrait;
|
||||
use ssh2_config::SshConfig;
|
||||
use std::collections::HashMap;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
pub struct SshKeyStorage {
|
||||
hosts: HashMap<String, PathBuf>, // Association between {user}@{host} and RSA key path
|
||||
/// Association between {user}@{host} and RSA key path
|
||||
hosts: HashMap<String, PathBuf>,
|
||||
/// Ssh2 configuration
|
||||
ssh_config: Option<SshConfig>,
|
||||
}
|
||||
|
||||
impl SshKeyStorage {
|
||||
@@ -19,6 +23,7 @@ impl SshKeyStorage {
|
||||
pub fn empty() -> Self {
|
||||
SshKeyStorage {
|
||||
hosts: HashMap::new(),
|
||||
ssh_config: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,21 +39,67 @@ impl SshKeyStorage {
|
||||
let key: String = Self::make_mapkey(host, username);
|
||||
self.hosts.insert(key, p);
|
||||
}
|
||||
}
|
||||
|
||||
impl SshKeyStorageT for SshKeyStorage {
|
||||
fn resolve(&self, host: &str, username: &str) -> Option<&Path> {
|
||||
/// Parse ssh2 config
|
||||
fn parse_ssh2_config(path: &str) -> Result<SshConfig, String> {
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
|
||||
let mut reader = File::open(path)
|
||||
.map_err(|e| format!("failed to open {}: {}", path, e))
|
||||
.map(BufReader::new)?;
|
||||
SshConfig::default()
|
||||
.parse(&mut reader)
|
||||
.map_err(|e| format!("Failed to parse ssh2 config: {}", e))
|
||||
}
|
||||
|
||||
/// Resolve host via termscp ssh keys storage
|
||||
fn resolve_host_in_termscp_storage(&self, host: &str, username: &str) -> Option<&Path> {
|
||||
let key: String = Self::make_mapkey(host, username);
|
||||
self.hosts.get(&key).map(|x| x.as_path())
|
||||
}
|
||||
|
||||
/// Resolve host via ssh2 configuration
|
||||
fn resolve_host_in_ssh2_configuration(&self, host: &str) -> Option<PathBuf> {
|
||||
if let Some(config) = self.ssh_config.as_ref() {
|
||||
let params = config.query(host);
|
||||
params
|
||||
.identity_file
|
||||
.as_ref()
|
||||
.and_then(|x| x.get(0).cloned())
|
||||
} else {
|
||||
debug!("ssh2 config is not available; no key has been found");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SshKeyStorageTrait for SshKeyStorage {
|
||||
fn resolve(&self, host: &str, username: &str) -> Option<PathBuf> {
|
||||
// search in termscp keys
|
||||
if let Some(path) = self.resolve_host_in_termscp_storage(host, username) {
|
||||
return Some(path.to_path_buf());
|
||||
}
|
||||
debug!(
|
||||
"couldn't find any ssh key associated to {} at {}. Trying with ssh2 config",
|
||||
username, host
|
||||
);
|
||||
// otherwise search in configuration
|
||||
self.resolve_host_in_ssh2_configuration(host)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&ConfigClient> for SshKeyStorage {
|
||||
fn from(cfg_client: &ConfigClient) -> Self {
|
||||
// read ssh2 config
|
||||
let ssh_config = cfg_client.get_ssh_config().and_then(|x| {
|
||||
debug!("reading ssh config at {}", x);
|
||||
Self::parse_ssh2_config(x).ok()
|
||||
});
|
||||
let mut hosts: HashMap<String, PathBuf> =
|
||||
HashMap::with_capacity(cfg_client.iter_ssh_keys().count());
|
||||
debug!("Setting up SSH key storage");
|
||||
// Iterate over keys
|
||||
// Iterate over keys in storage
|
||||
for key in cfg_client.iter_ssh_keys() {
|
||||
match cfg_client.get_ssh_key(key) {
|
||||
Ok(host) => match host {
|
||||
@@ -66,7 +117,7 @@ impl From<&ConfigClient> for SshKeyStorage {
|
||||
info!("Got SSH key for {}", key);
|
||||
}
|
||||
// Return storage
|
||||
SshKeyStorage { hosts }
|
||||
SshKeyStorage { hosts, ssh_config }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +126,7 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::system::config_client::ConfigClient;
|
||||
use crate::utils::test_helpers;
|
||||
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::path::Path;
|
||||
@@ -103,6 +155,35 @@ mod tests {
|
||||
assert!(storage.resolve("deskichup", "veeso").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sould_resolve_key_from_ssh2_config() {
|
||||
let rsa_key = test_helpers::create_sample_file_with_content("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDErJhQxEI0+VvhlXVUyh+vMCm7aXfCA/g633AG8ezD/5EylwchtAr2JCoBWnxn4zV8nI9dMqOgm0jO4IsXpKOjQojv+0VOH7I+cDlBg0tk4hFlvyyS6YviDAfDDln3jYUM+5QNDfQLaZlH2WvcJ3mkDxLVlI9MBX1BAeSmChLxwAvxALp2ncImNQLzDO9eHcig3dtMrEKkzXQowRW5Y7eUzg2+vvVq4H2DOjWwUndvB5sJkhEfTUVE7ID8ZdGJo60kUb/02dZYj+IbkAnMCsqktk0cg/4XFX82hEfRYFeb1arkysFisPU1DOb6QielL/axeTebVplaouYcXY0pFdJt root@8c50fd4c345a");
|
||||
let ssh_config_file = test_helpers::create_sample_file_with_content(format!(
|
||||
r#"
|
||||
Host test
|
||||
HostName 127.0.0.1
|
||||
Port 2222
|
||||
User test
|
||||
IdentityFile {}
|
||||
StrictHostKeyChecking no
|
||||
UserKnownHostsFile /dev/null
|
||||
"#,
|
||||
rsa_key.path().display()
|
||||
));
|
||||
// make storage
|
||||
let tmp_dir: tempfile::TempDir = tempfile::TempDir::new().ok().unwrap();
|
||||
let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
|
||||
let mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path())
|
||||
.ok()
|
||||
.unwrap();
|
||||
client.set_ssh_config(Some(ssh_config_file.path().to_string_lossy().to_string()));
|
||||
let storage: SshKeyStorage = SshKeyStorage::from(&client);
|
||||
assert_eq!(
|
||||
storage.resolve("test", "pi").unwrap().as_path(),
|
||||
rsa_key.path()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_system_sshkey_storage_empty() {
|
||||
let storage: SshKeyStorage = SshKeyStorage::empty();
|
||||
|
||||
@@ -21,14 +21,15 @@ pub fn create_sample_file_entry() -> (File, NamedTempFile) {
|
||||
)
|
||||
}
|
||||
|
||||
/// Create sample file with default lorem ipsum content
|
||||
pub fn create_sample_file() -> NamedTempFile {
|
||||
// Write
|
||||
create_sample_file_with_content("Lorem ipsum dolor sit amet, consectetur adipiscing elit.Mauris ultricies consequat eros,nec scelerisque magna imperdiet metus.")
|
||||
}
|
||||
|
||||
/// Create sample file with provided content
|
||||
pub fn create_sample_file_with_content(content: impl std::fmt::Display) -> NamedTempFile {
|
||||
let mut tmpfile: tempfile::NamedTempFile = tempfile::NamedTempFile::new().unwrap();
|
||||
writeln!(
|
||||
tmpfile,
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.Mauris ultricies consequat eros,nec scelerisque magna imperdiet metus."
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(tmpfile, "{}", content).unwrap();
|
||||
tmpfile
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user