rustnetconf
A Rust network automation platform: async NETCONF client library, YANG code generation, vendor profiles, connection pooling, and a Terraform-like CLI for declarative network config management.
Built on tokio, russh, and rustls — pure Rust, no OpenSSL, no libssh2.
Workspace
| Crate | Description |
|---|---|
| rustnetconf | Async NETCONF 1.0/1.1 client library |
| rustnetconf-yang | YANG model code generation (compile-time config validation) |
| rustnetconf-cli | Terraform-like CLI tool (netconf binary) |
RFC Support
| RFC | Feature | Status |
|---|---|---|
| RFC 6241 | Network Configuration Protocol (NETCONF) | ✅ supported |
| RFC 6242 | NETCONF over SSH | ✅ supported |
| RFC 7589 | NETCONF over TLS | ✅ supported (feature flag tls) — needs physical SRX or non-vSRX for TLS test |
| RFC 5277 | Event Notifications | ✅ supported — tested on Junos 24.4 vSRX (subscription + capability; interleave limited by device) |
| RFC 5717 | Partial Lock RPC | 💡 planned |
| RFC 8071 | NETCONF Call Home | 💡 planned |
| RFC 6243 | With-defaults Capability | 💡 planned |
| RFC 6022 | YANG Module for NETCONF Monitoring | 💡 planned |
| RFC 8526 | NETCONF Extensions for NMDA | 💡 planned |
| RFC 6470 | NETCONF Base Notifications | 💡 planned |
| RFC 8040 | RESTCONF | 💡 planned |
CLI Tool — netconf
Declarative network config management. Write desired state as XML files, the CLI diffs against the device and applies changes with confirmed-commit safety.
Project Structure
my-network/
├── inventory.toml # Device connection details
├── desired/
│ └── spine-01/
│ ├── interfaces.xml # Desired interface config
│ └── system.xml # Desired system config
└── .netconf/state/ # Rollback snapshots (auto-managed)
inventory.toml
[]
= 60
[]
= "10.0.0.1:830"
= "admin"
= "~/.ssh/id_ed25519"
# vendor auto-detected from device hello
Library — Quick Start
[]
= { = "https://github.com/fastrevmd-lab/rustnetconf.git" }
= { = "1", = ["full"] }
For TLS transport (RFC 7589), enable the tls feature:
[]
= { = "https://github.com/fastrevmd-lab/rustnetconf.git", = ["tls"] }
Fetch running config
use ;
async
Edit config (full round trip)
use ;
async
Connect over TLS (RFC 7589)
Note: vSRX 24.4 has a known TLS handshake issue where the PKI engine cannot present a self-signed certificate chain. TLS testing requires a physical SRX, MX, or EX device with a CA-signed certificate. The code compiles and passes unit tests but has not been validated against a live TLS-capable device.
use ;
async
Event notifications (RFC 5277)
use ;
async
Note: Some devices (e.g., Junos vSRX 24.4) advertise
:interleavebut do not respond to RPCs on a session with an active subscription. On these devices, use a dedicated session for notifications and a separate session for RPCs. Notifications arriving during RPCs on interleave-capable devices are automatically buffered and available viadrain_notifications().
Connection pooling
use ;
use SshAuth;
use Datastore;
let pool = builder
.max_connections
.add_device
.build;
let mut conn = pool.checkout.await?;
let config = conn.get_config.await?;
// connection auto-returned to pool on drop
Features
NETCONF Client
- Async-first — tokio-based, push config to 500 devices concurrently
- SSH + TLS transports — SSH (RFC 6242) by default, TLS (RFC 7589) via
tlsfeature flag - NETCONF 1.0 + 1.1 — EOM and chunked framing with auto-negotiation
- All core RPCs — get, get-config, edit-config, lock/unlock, commit, validate, close/kill-session, discard-changes
- Confirmed commit — auto-rollback safety net (RFC 6241 §8.4)
- Event notifications —
create-subscription, inline notification demux, buffered drain/recv API (RFC 5277) - CommitUnknown detection — distinguishes "commit failed" from "maybe committed, connection lost"
- Stale lock recovery —
lock_or_kill_stale()kills crashed sessions holding locks - Framing mismatch detection — catches firmware bugs where devices send wrong framing
Vendor Profiles
- Auto-detection from device
<hello>capabilities - Junos — config wrapping, namespace normalization, discard-before-close
- Generic — standard RFC 6241 for any compliant device
- Extensible — implement
VendorProfiletrait for custom vendors
Connection Pool
- Tokio semaphore-based concurrency limiting
- Checkout with timeout (no blocking forever)
- Auto-checkin on drop, broken connections discarded
- Connection reuse from idle pool
YANG Code Generation
- Build-time generation from
.yangmodel files via libyang2 - Typed Rust structs with serde Serialize/Deserialize
- Correct type mapping (string, bool, uint32, etc.)
- Bundled IETF models: ietf-interfaces, ietf-ip, ietf-yang-types, ietf-inet-types
Authentication
| Method | Transport | Builder API |
|---|---|---|
| Password | SSH | .password("secret") |
| Key file | SSH | .key_file("~/.ssh/id_ed25519") |
| SSH agent | SSH | .ssh_agent() |
| Server-only TLS | TLS | TlsConfig { ca_cert, .. } |
| Mutual TLS (mTLS) | TLS | TlsConfig { client_cert, client_key, .. } |
Error Handling
Layered errors matching the protocol stack:
match result
Supported Operations
| Operation | RFC 6241 | Status |
|---|---|---|
get |
§7.7 | Done |
get-config |
§7.1 | Done |
edit-config |
§7.2 | Done |
lock / unlock |
§7.4-7.5 | Done |
close-session |
§7.8 | Done |
kill-session |
§7.9 | Done |
commit |
§8.4 | Done |
confirmed-commit |
§8.4 | Done |
validate |
§8.6 | Done |
discard-changes |
§8.3 | Done |
Testing
140+ tests across the workspace:
- Unit tests — framing, RPC serialization, capability parsing, vendor profiles, diff engine, inventory parsing
- Mock transport tests — session state machine, CommitUnknown detection, lock recovery
- Integration tests — 32 tests against a live Juniper vSRX including full edit-config round trips, vendor auto-detection, connection pooling, and concurrent sessions
SKIP_INTEGRATION=1
Security
Known Issues
-
RSA timing sidechannel (RUSTSEC-2023-0071) — The
rsacrate (transitive dependency viarussh → internal-russh-forked-ssh-key → rsa) has a known timing sidechannel that could theoretically allow RSA key recovery. No upstream fix is available. Mitigation: Use Ed25519 or ECDSA keys instead of RSA for SSH authentication. -
Credentials not zeroized in memory — Passwords and key passphrases are stored as
String, which is not securely zeroed on drop. Credentials may persist in process memory until overwritten. Mitigation: Prefer SSH agent authentication (ssh_agent()) over inline passwords/passphrases, and avoid core dumps in production. -
Debug logs may contain file paths — When SSH key file loading fails, the key file path is included in
tracing::debug!output. This is not exposed at info/warn/error levels. Mitigation: Disable debug-level logging in production, or filterrustnetconf::transportlogs.
Security Features
- SSH host key verification — Use
host_key_verification(HostKeyVerification::Fingerprint("SHA256:..."))to pin a device's host key and prevent MITM attacks. Default isAcceptAll(with a logged warning), consistent with most network automation tools. - XML attribute escaping — All message-id values are escaped to prevent XML attribute injection.
- Read buffer limits — Session read buffers are capped at 100 MB to prevent memory exhaustion from malformed device responses.
- Typed error hierarchy — Structured error types (
ChannelClosed,SessionExpired,MessageIdMismatch) enable precise error handling without string matching. - No unsafe code — The entire codebase uses safe Rust.
Security Best Practices
- Use Ed25519 SSH keys (not RSA) for device authentication
- Set
host_key_verification(HostKeyVerification::Fingerprint(...))in production - Prefer SSH agent auth over inline passwords
- Store credentials in inventory.toml with restricted file permissions (
chmod 600) - Run the CLI on trusted management networks with direct device connectivity
- Use
confirmed-commit(the default fornetconf apply) so the device auto-reverts if something goes wrong - Disable debug-level logging in production environments
To report a security vulnerability, please open an issue on GitHub.
License
MIT OR Apache-2.0
Contributing
Contributions welcome! See ARCHITECTURE.md for the codebase design and TODOS.md for tracked work items.