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("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}