132 lines
3.3 KiB
Rust
132 lines
3.3 KiB
Rust
use std::borrow::Cow;
|
|
use std::sync::Arc;
|
|
|
|
use async_trait::async_trait;
|
|
use log::debug;
|
|
use russh::client::Handler;
|
|
use russh::kex::DH_G1_SHA1;
|
|
use russh::kex::ECDH_SHA2_NISTP256;
|
|
use russh_keys::key::SSH_RSA;
|
|
|
|
use super::BrocadeOptions;
|
|
use super::Error;
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct SshOptions {
|
|
pub preferred_algorithms: russh::Preferred,
|
|
pub port: u16,
|
|
}
|
|
|
|
impl Default for SshOptions {
|
|
fn default() -> Self {
|
|
Self {
|
|
preferred_algorithms: Default::default(),
|
|
port: 22,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl SshOptions {
|
|
fn ecdhsa_sha2_nistp256(port: u16) -> Self {
|
|
Self {
|
|
preferred_algorithms: russh::Preferred {
|
|
kex: Cow::Borrowed(&[ECDH_SHA2_NISTP256]),
|
|
key: Cow::Borrowed(&[SSH_RSA]),
|
|
..Default::default()
|
|
},
|
|
port,
|
|
..Default::default()
|
|
}
|
|
}
|
|
|
|
fn legacy(port: u16) -> Self {
|
|
Self {
|
|
preferred_algorithms: russh::Preferred {
|
|
kex: Cow::Borrowed(&[DH_G1_SHA1]),
|
|
key: Cow::Borrowed(&[SSH_RSA]),
|
|
..Default::default()
|
|
},
|
|
port,
|
|
..Default::default()
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct Client;
|
|
|
|
#[async_trait]
|
|
impl Handler for Client {
|
|
type Error = Error;
|
|
|
|
async fn check_server_key(
|
|
&mut self,
|
|
_server_public_key: &russh_keys::key::PublicKey,
|
|
) -> Result<bool, Self::Error> {
|
|
Ok(true)
|
|
}
|
|
}
|
|
|
|
pub async fn try_init_client(
|
|
username: &str,
|
|
password: &str,
|
|
ip: &std::net::IpAddr,
|
|
base_options: BrocadeOptions,
|
|
) -> Result<BrocadeOptions, Error> {
|
|
let mut default = SshOptions::default();
|
|
default.port = base_options.ssh.port;
|
|
let ssh_options = vec![
|
|
default,
|
|
SshOptions::ecdhsa_sha2_nistp256(base_options.ssh.port),
|
|
SshOptions::legacy(base_options.ssh.port),
|
|
];
|
|
|
|
for ssh in ssh_options {
|
|
let opts = BrocadeOptions {
|
|
ssh: ssh.clone(),
|
|
..base_options.clone()
|
|
};
|
|
debug!("Creating client {ip}:{} {username}", ssh.port);
|
|
let client = create_client(*ip, ssh.port, username, password, &opts).await;
|
|
|
|
match client {
|
|
Ok(_) => {
|
|
return Ok(opts);
|
|
}
|
|
Err(e) => match e {
|
|
Error::NetworkError(e) => {
|
|
if e.contains("No common key exchange algorithm") {
|
|
continue;
|
|
} else {
|
|
return Err(Error::NetworkError(e));
|
|
}
|
|
}
|
|
_ => return Err(e),
|
|
},
|
|
}
|
|
}
|
|
|
|
Err(Error::NetworkError(
|
|
"Could not establish ssh connection: wrong key exchange algorithm)".to_string(),
|
|
))
|
|
}
|
|
|
|
pub async fn create_client(
|
|
ip: std::net::IpAddr,
|
|
port: u16,
|
|
username: &str,
|
|
password: &str,
|
|
options: &BrocadeOptions,
|
|
) -> Result<russh::client::Handle<Client>, Error> {
|
|
let config = russh::client::Config {
|
|
preferred: options.ssh.preferred_algorithms.clone(),
|
|
..Default::default()
|
|
};
|
|
let mut client = russh::client::connect(Arc::new(config), (ip, port), Client {}).await?;
|
|
if !client.authenticate_password(username, password).await? {
|
|
return Err(Error::AuthenticationError(
|
|
"ssh authentication failed".to_string(),
|
|
));
|
|
}
|
|
Ok(client)
|
|
}
|