Skip to content

juliankahlert/simple_ssh

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

62 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

simple_ssh

Rust Report Card

simple_ssh is a lightweight, asynchronous Rust library that simplifies SSH operations such as executing remote commands, transferring files via SCP, and interactive PTY sessions. Built on top of the russh and russh-keys crates, it offers a streamlined API for secure and efficient SSH interactions.

Features

  • Asynchronous SSH client operations using tokio
  • Execute remote shell commands (cmd, exec, system)
  • Transfer files securely using the SCP protocol
  • Interactive PTY shell sessions with raw mode and auto-resize
  • Programmatic PTY sessions via PtyHandle for embedding in TUIs
  • Terminal multiplexer support (1x2, 2x1, 2x2 layouts)
  • IPv6 link-local address support with scope ID
  • Authentication modes: public key, password, and none
  • SSH certificate support for key authentication
  • Minimalistic and focused API design

Installation

As a Library

Add simple_ssh to your project's Cargo.toml:

[dependencies]
simple_ssh = "0.1.3"

CLI Tools

To install the CLI binaries (simple-ssh and simple-scp), use the cli feature:

cargo install simple_ssh --features cli

This installs both simple-ssh and simple-scp binaries to your cargo bin directory.

Usage

Executing Remote Commands

use simple_ssh::Session;
use anyhow::Result;

#[tokio::main]
async fn main() -> Result<()> {
    let session = Session::init()
        .with_port(22)
        .with_host("192.168.0.100")
        .with_user("root")
        .with_passwd("toor")
        .build()?;

    let mut ssh = session.connect().await?;
    println!("Connected");

    // Execute a shell command
    let code = ssh.cmd("ls -la").await?;
    println!("Exitcode: {:?}", code);

    // Execute with arguments (properly escaped)
    let code = ssh.exec(&["ls", "-la", "/tmp"].iter().map(|s| s.to_string()).collect()).await?;
    println!("Exitcode: {code}");

    // Execute via sh -c (like system())
    let code = ssh.system("echo $HOME && ls -la").await?;
    println!("Exitcode: {:?}", code);

    ssh.close().await?;
    Ok(())
}

Transferring Files via SCP

use simple_ssh::Session;
use anyhow::Result;

#[tokio::main]
async fn main() -> Result<()> {
    let session = Session::init()
        .with_port(22)
        .with_host("192.168.0.100")
        .with_user("root")
        .with_passwd("toor")
        .build()?;

    let mut ssh = session.connect().await?;
    println!("Connected");

    ssh.scp("local_file.txt", "/remote/path/remote_file.txt").await?;
    println!("File transferred successfully.");
    Ok(())
}

IPv6 Link-Local Addresses

For IPv6 link-local addresses, use the --scope option to specify the network interface:

use simple_ssh::Session;
use anyhow::Result;

#[tokio::main]
async fn main() -> Result<()> {
    let session = Session::init()
        .with_host("fe80::1")
        .with_scope("eth0")  // Network interface name or index
        .with_user("root")
        .with_passwd("password")
        .build()?;

    let mut ssh = session.connect().await?;
    // ...
    Ok(())
}

Programmatic PTY Sessions

For non-interactive PTY sessions where you control the I/O (e.g., embedding in a TUI):

use simple_ssh::Session;
use anyhow::Result;

#[tokio::main]
async fn main() -> Result<()> {
    let session = Session::init()
        .with_host("192.168.0.100")
        .with_user("root")
        .with_passwd("toor")
        .build()?;

    let mut ssh = session.connect().await?;

    // Open a PTY handle for programmatic control
    let mut handle = ssh
        .pty_builder()
        .with_term("xterm-256color")
        .with_size(80, 24)
        .open()
        .await?;

    // Send input
    handle.write(b"ls -la\n").await?;

    // Exit the shell so handle.read() returns None and handle.wait() completes
    handle.write(b"exit\n").await?;

    // Read output - loop ends when shell exits and handle.read() returns None
    while let Some(data) = handle.read().await {
        println!("Received: {:?}", data);
    }

    // Wait for exit status
    let status = handle.wait().await?;
    println!("Exit status: {:?}", status.code());

    Ok(())
}

CLI Tools

Two example binaries are provided for command-line use:

simple-ssh

Interactive SSH client with PTY support and terminal multiplexing:

# Interactive shell
simple-ssh -H 192.168.1.1 -u root -P password

# Execute command
simple-ssh -H server.example.com -u admin -P secret "uname -a"

# With private key
simple-ssh -H server.example.com -i /path/to/key

# IPv6 link-local with scope
simple-ssh -H fe80::1%eth0 -u root -P password

# Terminal multiplexer (2 panes stacked vertically)
simple-ssh -H 192.168.1.1 -u root -P password --mux 1x2

# Terminal multiplexer (2 panes side by side)
simple-ssh -H 192.168.1.1 -u root -P password --mux 2x1

# Terminal multiplexer (4 panes in 2x2 grid)
simple-ssh -H 192.168.1.1 -u root -P password --mux 2x2

simple-scp

File transfer utility:

# Transfer file using password authentication
simple-scp -H 192.168.1.1 -u root -P password /local/file.txt /remote/path.txt

# Transfer file using private key
simple-scp -H server.example.com -i /path/to/key /local/file.txt /remote/path.txt

# With custom port
simple-scp -H 192.168.1.1 -p 2222 -u admin -P secret /local/file.txt /remote/path.txt

CLI Options

simple-ssh Options

Option Description
-H, --host <HOST> SSH host to connect to (required)
-u, --user <USER> SSH username (default: root)
-P, --passwd <PASSWD> SSH password
-i, --key <KEY> Path to private key file
-p, --port <PORT> SSH port (default: 22)
--scope <SCOPE> IPv6 scope ID (e.g., interface name or number)
-a, --auth <AUTH> Authentication method (password, key, none)
--mux <MODE> Terminal multiplexer mode: 1x2, 2x1, or 2x2

simple-scp Options

Option Description
-H, --host <HOST> SSH host to connect to (required)
-u, --user <USER> SSH username (default: root)
-P, --passwd <PASSWD> SSH password
-i, --key <KEY> Path to private key file
-p, --port <PORT> SSH port (default: 22)
--scope <SCOPE> IPv6 scope ID (e.g., interface name or number)
-a, --auth <AUTH> Authentication method (password, key, none)
<LOCAL> Local file path to upload
<REMOTE> Remote destination path

Building

# Development build
cargo build

# Release build
cargo build --release

# Build CLI examples
cargo build --bin simple-ssh --features cli
cargo build --bin simple-scp --features cli

# Static musl build (for distribution)
cargo build --release --target x86_64-unknown-linux-musl

The musl build produces a fully static binary with no dynamic library dependencies, suitable for deployment in containerized or minimal environments.

Testing

# Run all tests
cargo test

# Run specific test
cargo test test_session_builder

# Run with output
cargo test -- --nocapture

Development Status

simple_ssh is actively developed and provides a stable API for common SSH operations. The core functionalities including command execution, SCP file transfer, and PTY sessions are fully implemented and tested. Contributions, issues, and feature requests are welcome!

License

This project is licensed under the MIT License. See the LICENSE file for details.

Acknowledgements

russh - The underlying SSH library used for client and server implementations.

About

Crate for simple ssh

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors