crosup_ssh/
lib.rs

1use anyhow::Error;
2use owo_colors::OwoColorize;
3use ssh2::Session;
4use std::{
5    collections::HashMap,
6    io::{self, Read},
7    process::Command,
8};
9
10pub fn exec(sess: Session, command: &str) -> Result<(), Error> {
11    let mut channel = sess.channel_session()?;
12
13    channel.exec(command)?;
14
15    let mut buffer = [0; 1024];
16    loop {
17        match channel.read(&mut buffer) {
18            Ok(n) => {
19                if n > 0 {
20                    let chunk = String::from_utf8_lossy(&buffer[..n]);
21                    print!("{}", chunk);
22                } else {
23                    break;
24                }
25            }
26            Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => continue,
27            Err(err) => {
28                return Err(err.into());
29            }
30        }
31    }
32
33    if channel.exit_status()? != 0 {
34        let command = command.bright_green();
35        return Err(Error::msg(format!(
36            "{} exit status is not 0, exit status = {}",
37            command,
38            channel.exit_status()?
39        )));
40    }
41
42    Ok(())
43}
44
45pub fn setup_ssh_agent_var() -> Result<(), Error> {
46    println!("-> Setting up ssh-agent {}", "ssh-agent -s".bright_green());
47    let child = Command::new("ssh-agent").arg("-s").output()?;
48    let output = String::from_utf8(child.stdout)?;
49
50    let mut envs = HashMap::new();
51
52    for line in output.lines() {
53        let env = line.split(";").next();
54        if let Some(env) = env {
55            let mut env = env.split("=");
56            let key = env.next();
57            let value = env.next();
58            if let (Some(key), Some(value)) = (key, value) {
59                std::env::set_var(key, value);
60                envs.insert(key, value);
61            }
62        }
63    }
64
65    println!(
66        "-> Adding ssh key {}",
67        "ssh-add ~/.ssh/id_rsa".bright_green()
68    );
69
70    let mut child = Command::new("sh")
71        .arg("-c")
72        .arg("ssh-add ~/.ssh/id_rsa")
73        .envs(envs)
74        .spawn()?;
75
76    child.wait()?;
77
78    Ok(())
79}
80
81pub fn setup_ssh_connection(addr: &str, username: &str) -> Result<Session, Error> {
82    setup_ssh_agent_var()?;
83    let tcp = std::net::TcpStream::connect(addr)?;
84    let mut sess = Session::new()?;
85    let mut agent = sess.agent()?;
86    agent.connect()?;
87
88    sess.set_tcp_stream(tcp);
89    sess.handshake()?;
90    sess.userauth_agent(username)?;
91
92    if !sess.authenticated() {
93        return Err(Error::msg("authentication failed"));
94    }
95
96    Ok(sess)
97}
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102
103    #[test]
104    fn it_works() {
105        // let session = setup_ssh_connection("192.168.8.101:22", "tsirysandratraina").unwrap();
106        let session = setup_ssh_connection("localhost:22", "tsirysndr").unwrap();
107        exec(session.clone(), "sh -c 'PATH=/home/linuxbrew/.linuxbrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin minikube version'").unwrap();
108        assert!(true);
109    }
110}