ferrissh
An async SSH CLI scraper library for network device automation in Rust.
Warning: This library is EXTREMELY experimental and under active development. The API is subject to change without notice. Use in production at your own risk. And I mean really, I have not tried sending actual configuration yet!
Ferrissh provides a high-level async API for interacting with network devices over SSH, similar to Python's scrapli and netmiko libraries.
Features
- Async/Await - Built on Tokio and russh for efficient async SSH connections
- Multi-Vendor Support - Linux, Juniper JUNOS, with more coming
- Privilege Management - Automatic navigation between privilege levels
- Interactive Commands - Handle prompts requiring user input (confirmations, passwords)
- Configuration Mode - Automatic privilege escalation for config commands
- Pattern Matching - Efficient tail-search buffer matching (scrapli-style optimization)
- Output Normalization - Clean output with command echo and prompt stripping
- Error Detection - Vendor-specific failure pattern detection
- Easy Extensibility - Add custom platforms with minimal code
Installation
Add to your Cargo.toml:
[]
= "0.1"
= { = "1", = ["full"] }
Quick Start
use ;
async
Supported Platforms
| Platform | Name | Privilege Levels |
|---|---|---|
| Linux/Unix | linux |
user ($), root (#) |
| Juniper JUNOS | juniper |
exec (>), configuration (#), shell (%) |
Usage Examples
Basic Commands
use ;
let mut driver = new
.username
.password
.platform
.build?;
driver.open.await?;
// Single command
let response = driver.send_command.await?;
println!;
// Multiple commands
let responses = driver.send_commands.await?;
for response in responses
driver.close.await?;
SSH Key Authentication
use PathBuf;
let driver = new
.username
.private_key
.platform
.build?;
Configuration Mode
Automatically enter and exit configuration mode:
// send_config handles privilege escalation automatically
let responses = driver.send_config.await?;
// Check for errors
for response in &responses
Interactive Commands
Handle commands that require confirmation or input:
use ;
// Using the builder (fluent API)
let events = new
.send
.expect?
.send
.expect?
.build;
let result = driver.send_interactive.await?;
if result.failed
// With hidden input (passwords)
let events = new
.send
.expect?
.send_hidden // Won't appear in logs
.expect?
.build;
Privilege Level Management
// Check current privilege
if let Some = driver.current_privilege
// Navigate to a specific privilege level
driver.acquire_privilege.await?;
// Do configuration work...
driver.send_command.await?;
// Return to operational mode
driver.acquire_privilege.await?;
Custom Timeouts
use Duration;
let driver = new
.username
.password
.platform
.timeout // 60 second timeout
.build?;
Error Handling
let response = driver.send_command.await?; // Typo
if response.failed else
Response Structure
Parsing Output with TextFSM
For structured data extraction from CLI output, ferrissh works well with textfsm-rust - a Rust implementation of Google's TextFSM.
As dictionaries
use ;
use Template;
// Define a TextFSM template for parsing `df -h` output
const DF_TEMPLATE: &str = r#"
Value Filesystem (\S+)
Value Size (\S+)
Value Used (\S+)
Value Available (\S+)
Value UsePercent (\d+)
Value MountedOn (\S+)
Start
^Filesystem -> Continue
^${Filesystem}\s+${Size}\s+${Used}\s+${Available}\s+${UsePercent}%\s+${MountedOn} -> Record
"#;
// Run command and parse output
let response = driver.send_command.await?;
let template = parse_str?;
let mut parser = template.parser;
let records = parser.parse_text_to_dicts?;
// Access structured data
for record in records
Into typed structs (serde)
With the serde feature enabled, parse directly into strongly-typed Rust structs:
[]
= { = "0.3", = ["serde"] }
use Deserialize;
use Template;
let template = parse_str?;
let mut parser = template.parser;
let disks: = parser.parse_text_into?;
for disk in &disks
See the textfsm_parsing example for a complete demonstration with templates for Linux and Juniper commands.
Adding Custom Platforms
use ;
use Arc;
// Define privilege levels
let exec = new?;
let config = new?
.with_parent
.with_escalate
.with_deescalate;
// Create platform
let platform = new
.with_privilege
.with_privilege
.with_default_privilege
.with_failure_pattern
.with_on_open_command
.with_behavior;
// Use with driver
let driver = new
.custom_platform
.username
.password
.build?;
Running the Examples
The ferrissh/examples/ directory contains several examples demonstrating different features. All examples support both password and SSH key authentication.
Common Options
| Option | Description |
|---|---|
--host <HOST> |
Target hostname or IP (default: localhost) |
--port <PORT> |
SSH port (default: 22) |
--user <USER> |
Username (default: $USER) |
--password <PASS> |
Password authentication |
--key <PATH> |
Path to SSH private key |
--timeout <SECS> |
Connection timeout (default: 30) |
--help |
Show help message |
basic_ls - Linux Commands
Basic example connecting to a Linux host and running commands.
# With password
# With SSH key
juniper - Juniper JUNOS
Demonstrates connecting to Juniper devices and running operational/configuration commands.
# Basic operational commands
# Include configuration mode demo
interactive - Interactive Commands
Shows how to handle commands that require user input or confirmation prompts.
textfsm_parsing - Structured Output Parsing
Demonstrates using textfsm-rust to parse CLI output into structured data. Includes templates for common Linux and Juniper commands.
# Parse Linux commands (uname, df, ps)
# Parse Juniper commands (show version, show interfaces terse)
Sample output:
[
{
"filesystem": "/dev/nvme1n1p4",
"size": "853G",
"used": "278G",
"available": "532G",
"usepercent": "35",
"mountedon": "/"
}
]
Filesystems with >50% usage:
/sys/firmware/efi/efivars - 52% used (64K of 128K)
Debug Logging
Enable debug logging to see detailed SSH and parsing information:
RUST_LOG=debug
Log levels: error, warn, info, debug, trace
Planned Features
Platform Support
- Juniper JUNOS (in progress)
- Nokia SR OS
- Arista EOS
- Arrcus ArcOS
Macros & Compile-Time Safety
- Proc macro for defining custom platforms declaratively
- Compile-time privilege graph validation
- Compile-time regex verification for prompt patterns
Transport
- Feature-gated
async_ssh2_litebackend
API
- Streaming output API
Dependencies
| Crate | Purpose |
|---|---|
russh |
SSH client library |
ssh-key |
SSH key handling |
tokio |
Async runtime |
regex |
Pattern matching |
thiserror |
Error handling |
log |
Logging facade |
serde |
Serialization/deserialization |
indexmap |
Deterministic-order maps |
strip-ansi-escapes |
ANSI escape code removal |
License
MIT License - see LICENSE for details.