ssh2-rs

Safe Rust bindings to libssh2 for SSH clients, exec, SFTP, SCP, and port forwarding

SDK
Cargo
v0.9.6
565stars
Apache License 2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
59/100Fair
Development Activity56
Maintenance24
Community76
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
68/100Good
Architecture78
Code Quality68
Innovation65
Learning Curve62

ssh2 is a Rust crate that wraps libssh2, the C SSH client library, in a safe, idiomatic API. It gives Rust programs everything needed to act as an SSH client: opening a session over any TcpStream, authenticating via password, public key, ssh-agent, or keyboard-interactive challenge, then running remote commands, transferring files over SFTP or SCP, allocating a PTY, and forwarding local or remote ports through the encrypted channel.

Because it only supports the client role of SSH protocol v2 (never server mode, never v1), the crate stays narrowly focused: Session for the connection and authentication lifecycle, Channel for command execution and PTY interaction, and Sftp/File for the SFTP subsystem. It links against the vendored or system libssh2 C library via the companion libssh2-sys crate, giving Rust code the same battle-tested SSH implementation used by countless other tools without leaving the type-safe Rust world.

What You Get

  • A Session type that wraps a TcpStream (or any type implementing the right traits) and drives the SSH handshake and authentication
  • Multiple authentication methods: password, public key from file or memory, ssh-agent, and keyboard-interactive with a pluggable KeyboardInteractivePrompt trait
  • A Channel type implementing Read/Write for executing remote commands, opening shells, requesting PTYs with custom terminal modes, and reading exit status/signals
  • Full SFTP subsystem support through Sftp/File — open, read, write, stat, rename, and remove remote files with standard Rust I/O traits
  • SCP file upload/download via Session::scp_send and Session::scp_recv
  • Local and remote TCP port forwarding through Listener and Channel::direct_tcpip
  • Known-hosts management (KnownHosts) for verifying and persisting server host keys, matching ~/.ssh/known_hosts semantics
  • ssh-agent inspection via the Agent type for listing and using identities already loaded in a running agent

Common Use Cases

  • Executing remote shell commands from a Rust CLI or automation tool and capturing stdout/stderr/exit status
  • Building deployment or provisioning tooling that uploads build artifacts to remote servers over SFTP or SCP
  • Writing infrastructure automation that needs to verify host keys against a known-hosts file before connecting, guarding against MITM attacks
  • Implementing a Rust-native bastion/jump-host client that forwards local ports through an SSH tunnel to reach internal services
  • Embedding SSH connectivity into a larger Rust application (backup tool, orchestration agent, network appliance CLI) without shelling out to the system ssh binary

Under The Hood

Architecture — ssh2 is a thin safety layer over the libssh2-sys FFI crate, which in turn statically or dynamically links the C libssh2 library (vendored as a git submodule under libssh2-sys/libssh2). The crate is organized around one owning type per libssh2 concept: Session (src/session.rs, ~1200 lines) holds the raw LIBSSH2_SESSION pointer behind a parking_lot::Mutex and drives the handshake, authentication, and channel/SFTP/agent construction; Channel (src/channel.rs) wraps a LIBSSH2_CHANNEL pointer and implements Read/Write for exec/shell/subsystem I/O plus PTY and extended-data (stderr) stream handling; Sftp/File (src/sftp.rs, ~900 lines) wrap the SFTP subsystem; KnownHosts, Agent, and Listener round out host-key verification, ssh-agent inspection, and port forwarding. Every wrapper type holds an Arc-shared handle back to its parent SessionInner so channels and SFTP handles cannot outlive the session that created them, which is how the crate gets memory safety out of an inherently unsafe C library without a garbage collector.

Tech Stack — Pure Rust wrapper crate (edition-agnostic, no edition bump visible in Cargo.toml) with four runtime dependencies: bitflags 2.x for flag enums like TraceFlags, libc 0.2 for C type bridging, parking_lot 0.12 for the session mutex, and the sibling libssh2-sys 0.3.2 workspace member for the actual FFI bindings and libssh2 C build. libssh2-sys itself depends on openssl-sys (system OpenSSL, required for the crypto primitives libssh2 needs) and libz-sys for zlib compression, with optional vendored-openssl and zlib-ng-compat features for statically-linked builds. Dev-dependencies are minimal — just tempfile for the test suite.

Code Quality — The crate enforces #![deny(missing_docs, unused_results)] at the crate root, so every public item is required to carry doc comments, and result values can’t be silently dropped — a meaningful quality bar for a crate whose entire surface is FFI-adjacent Result-returning calls. The tests/all/ directory has real integration tests (9 in session.rs, 16 in channel.rs, plus sftp/knownhosts/agent coverage) that spin up actual SSH connections rather than mocking libssh2, giving high confidence but requiring a real SSH server in CI. Internally the crate is heavy on unsafe (over 100 combined occurrences across session.rs/channel.rs/sftp.rs), which is expected for an FFI wrapper but places real weight on the safety invariants documented (or left implicit) around raw pointer lifetimes and the Arc<Mutex<SessionInner>> pattern.

API Design — The public API reads like a natural Rust SSH client: Session::new(), set_tcp_stream(), handshake(), userauth_agent()/userauth_password(), then channel_session() returning something you exec() and read_to_string() from — all demonstrated directly in the crate-level doc examples for agent auth, password auth, command exec, SCP upload/download, and even a full NETCONF session. That said, session setup is multi-step and imperative (construct session, attach stream, handshake, authenticate, then open a channel) rather than builder-style, and several return types (ExitSignal, ScpFileStat, KeyboardInteractivePrompt) require reading the docs to use correctly since they mirror libssh2’s C-level shapes closely.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search