pijul nest
guest [sign in]

Executing a shell command example

[?]
Feb 1, 2021, 7:51 PM
KGIUIQYIBBUEGBOQIFYJJNZGGSPV3KERBPYTKKCOBKZZ5CHIJ24AC

Dependencies

Change contents

  • file addition: remote_shell_call.rs (-xw-x--x--)
    [2.11]
    use anyhow::Result;
    use std::io::Write;
    use std::sync::Arc;
    use thrussh::*;
    use thrussh_keys::*;
    #[tokio::main]
    async fn main() -> Result<()> {
    let pem = std::fs::read("./my-aws-key.pem")?;
    let mut ssh = Session::connect(&pem, "ubuntu", "35.158.158.35:22").await?;
    let r = ssh.call("whoami").await?;
    assert!(r.success());
    assert_eq!(r.output(), "ubuntu\n");
    ssh.close().await?;
    Ok(())
    }
    struct Client {}
    impl client::Handler for Client {
    type Error = thrussh::Error;
    type FutureUnit = futures::future::Ready<Result<(Self, client::Session), Self::Error>>;
    type FutureBool = futures::future::Ready<Result<(Self, bool), Self::Error>>;
    fn finished_bool(self, b: bool) -> Self::FutureBool {
    futures::future::ready(Ok((self, b)))
    }
    fn finished(self, session: client::Session) -> Self::FutureUnit {
    futures::future::ready(Ok((self, session)))
    }
    fn check_server_key(self, _server_public_key: &key::PublicKey) -> Self::FutureBool {
    self.finished_bool(true)
    }
    }
    pub struct Session {
    session: client::Handle<Client>,
    }
    impl Session {
    async fn connect(
    pem: &[u8],
    user: impl Into<String>,
    addr: impl std::net::ToSocketAddrs,
    ) -> Result<Self> {
    let key_pair = key::KeyPair::RSA {
    key: openssl::rsa::Rsa::private_key_from_pem(pem)?,
    hash: key::SignatureHash::SHA2_512,
    };
    let config = client::Config::default();
    let config = Arc::new(config);
    let sh = Client {};
    let mut agent = agent::client::AgentClient::connect_env().await?;
    agent.add_identity(&key_pair, &[]).await?;
    let mut identities = agent.request_identities().await?;
    let mut session = client::connect(config, addr, sh).await?;
    let pubkey = identities.pop().unwrap();
    let (_, auth_res) = session.authenticate_future(user, pubkey, agent).await;
    let _auth_res = auth_res?;
    Ok(Self { session })
    }
    async fn call(&mut self, command: &str) -> Result<CommandResult> {
    let mut channel = self.session.channel_open_session().await?;
    channel.exec(true, command).await?;
    let mut output = Vec::new();
    let mut code = None;
    while let Some(msg) = channel.wait().await {
    match msg {
    thrussh::ChannelMsg::Data { ref data } => {
    output.write_all(&data).unwrap();
    }
    thrussh::ChannelMsg::ExitStatus { exit_status } => {
    code = Some(exit_status);
    }
    _ => {}
    }
    }
    Ok(CommandResult { output, code })
    }
    async fn close(&mut self) -> Result<()> {
    self.session
    .disconnect(Disconnect::ByApplication, "", "English")
    .await?;
    Ok(())
    }
    }
    struct CommandResult {
    output: Vec<u8>,
    code: Option<u32>,
    }
    impl CommandResult {
    fn output(&self) -> String {
    String::from_utf8_lossy(&self.output).into()
    }
    fn success(&self) -> bool {
    self.code == Some(0)
    }
    }