Skip to main content

SSH to network gear you can't reach directly — legacy crypto + jump-host support over Paramiko.

Project description

reachssh

SSH to network gear you can't reach directly.

A thin Paramiko wrapper built for the awkward realities of network-device SSH — the two problems plain Paramiko makes you solve yourself, solved once:

  1. Legacy crypto. Old IOS/NX-OS/Junos/ASA gear negotiates ancient KEX, ciphers, and host-key types (DH group14/group1-SHA1, 3DES-CBC, ssh-rsa with SHA-1) that modern OpenSSH and recent Paramiko disable by default. reachssh registers every algorithm handler the installed Paramiko ships and runs a two-pass connect (force ssh-rsa/SHA-1 first, fall back to rsa-sha2 for hardened devices) so both crusty and modern gear just work.

  2. Jump hosts / bastions. Paramiko reads ProxyJump from nowhere — not from ~/.ssh/config, not from anywhere. reachssh opens a direct-tcpip channel through the bastion and tunnels the device connection over it, with the channel re-opened per connect attempt so the legacy/​modern retry still works through the jump.

Everything else it does is the network-CLI plumbing you'd otherwise rewrite per tool: interactive invoke_shell sessions, prompt detection, vendor- agnostic pagination disable, and deterministic command-output stripping so config snapshots diff cleanly run-to-run.

reachssh depends only on Paramiko.

Install

pip install reachssh

Basic use

from reachssh import SSHClient, SSHClientConfig

cfg = SSHClientConfig(
    host="172.16.1.2",
    username="cisco",
    password="cisco",
    legacy_mode=True,      # register + prefer legacy algorithms
)

with SSHClient(cfg) as c:
    c.find_prompt()
    c.disable_pagination()
    print(c.execute_command("show ip interface brief"))

Through a jump host

A single bastion hop — build a JumpSpec and hand it to the config:

from reachssh import SSHClient, SSHClientConfig, JumpSpec, JumpHop

jump = JumpSpec(hops=[JumpHop(
    name="thinkstation",
    host="10.0.0.108",
    username="speterman",
    key_content=open("/home/me/.ssh/id_rsa").read(),   # or password=...
)])

cfg = SSHClientConfig(
    host="172.16.1.2", username="cisco", password="cisco",
    legacy_mode=True, jump=jump,
)

with SSHClient(cfg) as c:
    c.find_prompt()
    print(c.execute_command("show version"))

Rule-based jump selection (many devices, different bastions)

Route-map / ACL ordering — rules are evaluated top-to-bottom, first match wins, so a special-case device rule sits above the broad site rule and wins by position:

from reachssh import build_proxy_resolver

proxy_config = {
    "jump_hosts": {
        "iad-bastion": {"host": "10.0.0.108", "port": 22, "credential": "lab-jump"},
    },
    "proxy_rules": [
        {"match": {"devices": ["bastion-*"]}, "jump": "direct"},   # the bastion itself
        {"match": {"site": "iad-lab"}, "jump": "iad-bastion"},
    ],
}

# reachssh never touches secrets at rest. YOU supply a resolver mapping a
# credential *name* to a tuple: (user, password) or (user, password, key_pem).
creds = {"lab-jump": ("speterman", None, open("/home/me/.ssh/id_rsa").read())}
resolver = build_proxy_resolver(proxy_config, credential_resolver=creds.get)

spec = resolver.resolve({"name": "wan-core-1", "site_slug": "iad-lab"})
# -> JumpSpec through iad-bastion; pass as SSHClientConfig(jump=spec)

The credential_resolver is the only place bastion secrets enter the library. Pass a vault lookup, a dict .get, a lambda reading env vars — whatever you already use.

Migrating a tool off its own SSH client

If you have a tool carrying its own copy of this client (this code originated inside such tools), replace the vendored ssh/client.py import with:

from reachssh import SSHClient, SSHClientConfig

Tools that never had jump support gain it for free — populate SSHClientConfig.jump and connections route through the bastion with no other change.

Paramiko version note

reachssh registers whatever algorithm handlers the installed Paramiko ships and silently skips ones that aren't present — it never crashes on a missing handler. Consequence: on Paramiko 5.x, the kex_group1 module was removed, so diffie-hellman-group1-sha1 cannot be offered. group14-sha1, 3des-cbc, and ssh-rsa (SHA-1) are still available and cover the large majority of legacy gear. If you must talk to a device that only speaks diffie-hellman-group1-sha1, pin Paramiko ≤ 4.x, where that handler still ships.

Emulation (optional)

For testing without live gear, enable_emulation("ip_lookup.json") transparently redirects connections to a local mock-device server. Inert unless enabled.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

reachssh-0.1.0.tar.gz (39.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

reachssh-0.1.0-py3-none-any.whl (38.0 kB view details)

Uploaded Python 3

File details

Details for the file reachssh-0.1.0.tar.gz.

File metadata

  • Download URL: reachssh-0.1.0.tar.gz
  • Upload date:
  • Size: 39.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for reachssh-0.1.0.tar.gz
Algorithm Hash digest
SHA256 eb6bc00939226b806310a4c85daa10bb622575af83d2e1d4e52f6c032641ac6d
MD5 2d4e4dc531f818094f04ba8e6d2840da
BLAKE2b-256 1783e48384e1b812e3792307525fafea97920c3132b3a5e2d0fcfe5997aa50ca

See more details on using hashes here.

File details

Details for the file reachssh-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: reachssh-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 38.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for reachssh-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 490a4528220673ff576fc13cf6bb729d6049c07731c6da01b2d508fc1a2d7c67
MD5 78c8d14b69333f042e8c8b84b6be2f4a
BLAKE2b-256 eb4bb017f2af5f86fabd59d83762674d7c43cb60af8c4382f1fdc1a3afd2eb30

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page