GithubHelp home page GithubHelp logo

andrewschenck / paramiko-jump Goto Github PK

View Code? Open in Web Editor NEW
32.0 2.0 9.0 22 KB

Paramiko SSH + jump host + multi-factor authentication made simple.

License: Other

Python 100.00%
paramiko ssh-client ssh python3 jumphost jumpserver multifactor-authentication two-factor-authentication duo mobilepass

paramiko-jump's Introduction

The Problem

"How do I use paramiko to SSH to a remote host while proxying through a jump host? Also, my jump host requires two-factor authentication!"

This seems to be a surprisingly common problem with a lot of not-very-working solutions. I figured I'd share my attempt with the world.

The Solution

A simple class, SSHJumpClient, which derives from paramiko.SSHClient and implements two additional features:

  1. Easy chaining of SSH connections, supported through object injection. This enables the programmer to build a 'stack' of proxied SSH sessions, and tunnel commands through infrastructure as-needed.
  2. Easy authentication scheme override, forcing a keyboard-interactive authentication approach to be used. This should support most 2FA / MFA infrastructure approaches to SSH authentication. The keyboard-interactive authentication handler is injected, permitting easy integration with more advanced use cases.

Usage Example 1: Connect to a single target through a Jump Host

In this example, we use keyboard-interactive authentication on the Jump Host, and we tell Paramiko to 'auto add' (and accept) unknown Host Keys. (What could possibly go wrong?)

import paramiko
from paramiko_jump import SSHJumpClient, simple_auth_handler

# My Jump Host requires keyboard-interactive multi-factor
# authentication, so I use auth_handler=. Otherwise, I could
# use paramiko.SSHClient here.
with SSHJumpClient(auth_handler=simple_auth_handler) as jumper:
    jumper.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    jumper.connect(
        hostname='jump-host',
        username='jump-user',
    )

    # Now I instantiate a session for the Jump Host <-> Target
    # Host connection, and inject the jump_session to use for
    # proxying.
    target = SSHJumpClient(jump_session=jumper)
    target.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    target.connect(
        hostname='target-host',
        username='target-user',
        password='target-password',
        look_for_keys=False,
        allow_agent=False,
    )
    _, stdout, _ = target.exec_command('sh ip int br')
    print(stdout.read().decode())
    target.close()

Usage Example 2: Open one Jump Channel, connect to multiple targets

from getpass import getpass

import paramiko
from paramiko_jump import SSHJumpClient, simple_auth_handler

with SSHJumpClient(auth_handler=simple_auth_handler) as jumper:
    jumper.connect(
        hostname='jump-host',
        username='jump-user',
    )

    target1 = SSHJumpClient(jump_session=jumper)
    target1.connect(
        hostname='target-host1',
        username='username',
        password='password',
        look_for_keys=False,
        allow_agent=False,
    )
    _, stdout, _ = target1.exec_command('sh ver')
    print(stdout.read().decode())
    target1.close()

    target2 = SSHJumpClient(jump_session=jumper)
    target2.connect(
        hostname='target-host2',
        username='username',
        password='password',
        look_for_keys=False,
        allow_agent=False,
    )
    _, stdout, _ = target2.exec_command('sh ip int br')
    print(stdout.read().decode())
    target2.close()

Usage Example 3: Multiple-Hop SSH "Virtual Circuit"

circuit = []

hop1 = SSHJumpClient()
hop1.connect('host')
circuit.append(hop1)

hop2 = SSHJumpClient(jump_session=hop1)
hop2.connect('host')
circuit.append(hop2)

hop3 = SSHJumpClient(jump_session=hop2)
hop3.connect('host')
circuit.append(hop3)

hop4 = SSHJumpClient(jump_session=hop3)
hop4.connect('host')
circuit.append(hop4)

target = SSHJumpClient(jump_session=hop4)
target.connect('host')
circuit.append(target)

target.exec_command('uptime')

for session in reversed(circuit):
    session.close()

A Note on Authentication

In order to successfully authenticate with infrastructure requiring keyboard-interactive multi-factor authentication, you will probably want to explicitly pass in auth_handler= during client construction. A basic handler callable is included, and should work for most use cases:

from paramiko_jump import simple_auth_handler

When troubleshooting authentication failures, remember that Paramiko will be authenticating as a client on each 'hop', and that it has strong preferences over which authentication scheme it will be using. You can control authentication behavior by passing various parameters to the `connect()` call. Read `paramiko.SSHClient._auth` for more insight into how this works.

paramiko-jump's People

Contributors

andrewschenck avatar craighrowland avatar prokopto-dev avatar tybruno avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

paramiko-jump's Issues

modulerror

i am trying this to login to switch and get some show version. there is a jump host in between. that is 2 factor. first it takes a password then i have to type 1 then some authentication comes in cell phone i have to approve then i can login to jump host.

i am getting this modulerror.

ModuleNotFoundError: No module named 'paramiko_jump'

any idea how to resolve this?

Regarding _Host

In the client.py file, there is a variable _Host:

_Host = Union[AnyStr, Tuple[AnyStr, int]]
_Prompt = Tuple[AnyStr, bool]

It's used like its friend, _Prompt, as a representation of a complex type, in this case a Union between either an AnyStr or a tuple of AnyStr and a bool.

I understand, implicitly, what it is:

It represents either a hostname/IP, OR a hostname/IP combined with the SSH_PORT you expect to connect over on that particular host.

Obviously it's not used anywhere right now;

I'd be happy to write up an implementation for it, but I'm curious where it was intended to be used, or if it is a deprecated variable.

Inherited Import Causing Typing and Validation Errors

Hey all;

Just a small nit-pick, but when using packages like mypy, pylance, and pyright, the SSH_PORT variable grabbed from paramiko.client throws an error.

Line in question is here:

from paramiko.client import SSH_PORT, SSHClient

The resolution is basically just to throw in another import line from the original source within paramiko.config:

from paramiko.client import SSHClient
from paramiko.config import SSH_PORT

Will attach a PR to this for the resolution.

Unable to successfully connect

Apologies I do not know how to contact you but I only have a question rather than an issue.

So I followed your example 1 and got the following error

paramiko.ssh_exception.ChannelException: ChannelException(1, 'Administratively prohibited')

on the router only the subnet of the jumphost server is allowed to SSH into the router, could my source IP be the IP of my laptop and not the jumphost server when I logged into the router?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.