ssh2-rs
Safe Rust bindings to libssh2 for SSH clients, exec, SFTP, SCP, and port forwarding
Repository Health
Technical Analysis
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
Sessiontype that wraps aTcpStream(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
KeyboardInteractivePrompttrait - A
Channeltype implementingRead/Writefor 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_sendandSession::scp_recv - Local and remote TCP port forwarding through
ListenerandChannel::direct_tcpip - Known-hosts management (
KnownHosts) for verifying and persisting server host keys, matching~/.ssh/known_hostssemantics - ssh-agent inspection via the
Agenttype 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
sshbinary
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.