Project: sshtunnel

Pure python SSH tunnels

Project Details

Latest version
0.4.0
Home Page
https://github.com/pahaz/sshtunnel
PyPI Page
https://pypi.org/project/sshtunnel/

Project Popularity

PageRank
0.004310301774608408
Number of downloads
6784604

|CircleCI| |AppVeyor| |readthedocs| |coveralls| |version|

|pyversions| |license|

Author: Pahaz_

Repo: https://github.com/pahaz/sshtunnel/

Inspired by https://github.com/jmagnusson/bgtunnel, which doesn't work on Windows.

See also: https://github.com/paramiko/paramiko/blob/master/demos/forward.py

Requirements

  • paramiko_

Installation

sshtunnel_ is on PyPI, so simply run:

::

pip install sshtunnel

or ::

easy_install sshtunnel

or ::

conda install -c conda-forge sshtunnel

to have it installed in your environment.

For installing from source, clone the repo <https://github.com/pahaz/sshtunnel>_ and run::

python setup.py install

Testing the package

In order to run the tests you first need tox <https://testrun.org/tox/latest/>_ and run::

python setup.py test

Usage scenarios

One of the typical scenarios where sshtunnel is helpful is depicted in the figure below. User may need to connect a port of a remote server (i.e. 8080) where only SSH port (usually port 22) is reachable. ::

----------------------------------------------------------------------

                            |
-------------+              |    +----------+
    LOCAL    |              |    |  REMOTE  | :22 SSH
    CLIENT   | <== SSH ========> |  SERVER  | :8080 web service
-------------+              |    +----------+
                            |
                         FIREWALL (only port 22 is open)

----------------------------------------------------------------------

Fig1: How to connect to a service blocked by a firewall through SSH tunnel.

If allowed by the SSH server, it is also possible to reach a private server (from the perspective of REMOTE SERVER) not directly visible from the outside (LOCAL CLIENT's perspective). ::

----------------------------------------------------------------------

                            |
-------------+              |    +----------+               +---------
    LOCAL    |              |    |  REMOTE  |               | PRIVATE
    CLIENT   | <== SSH ========> |  SERVER  | <== local ==> | SERVER
-------------+              |    +----------+               +---------
                            |
                         FIREWALL (only port 443 is open)

----------------------------------------------------------------------

Fig2: How to connect to PRIVATE SERVER through SSH tunnel.

Usage examples

API allows either initializing the tunnel and starting it or using a with context, which will take care of starting and stopping the tunnel:

Example 1

Code corresponding to Fig1 above follows, given remote server's address is pahaz.urfuclub.ru, password authentication and randomly assigned local bind port.

.. code-block:: python

from sshtunnel import SSHTunnelForwarder

server = SSHTunnelForwarder(
    'alfa.8iq.dev',
    ssh_username="pahaz",
    ssh_password="secret",
    remote_bind_address=('127.0.0.1', 8080)
)

server.start()

print(server.local_bind_port)  # show assigned local port
# work with `SECRET SERVICE` through `server.local_bind_port`.

server.stop()

Example 2

Example of a port forwarding to a private server not directly reachable, assuming password protected pkey authentication, remote server's SSH service is listening on port 443 and that port is open in the firewall (Fig2):

.. code-block:: python

import paramiko
import sshtunnel

with sshtunnel.open_tunnel(
    (REMOTE_SERVER_IP, 443),
    ssh_username="",
    ssh_pkey="/var/ssh/rsa_key",
    ssh_private_key_password="secret",
    remote_bind_address=(PRIVATE_SERVER_IP, 22),
    local_bind_address=('0.0.0.0', 10022)
) as tunnel:
    client = paramiko.SSHClient()
    client.load_system_host_keys()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    client.connect('127.0.0.1', 10022)
    # do some operations with client session
    client.close()

print('FINISH!')

Example 3

Example of a port forwarding for the Vagrant MySQL local port:

.. code-block:: python

from sshtunnel import open_tunnel
from time import sleep

with open_tunnel(
    ('localhost', 2222),
    ssh_username="vagrant",
    ssh_password="vagrant",
    remote_bind_address=('127.0.0.1', 3306)
) as server:

    print(server.local_bind_port)
    while True:
        # press Ctrl-C for stopping
        sleep(1)

print('FINISH!')

Or simply using the CLI:

.. code-block:: console

(bash)$ python -m sshtunnel -U vagrant -P vagrant -L :3306 -R 127.0.0.1:3306 -p 2222 localhost

Example 4

Opening an SSH session jumping over two tunnels. SSH transport and tunnels will be daemonised, which will not wait for the connections to stop at close time.

.. code-block:: python

import sshtunnel
from paramiko import SSHClient


with sshtunnel.open_tunnel(
    ssh_address_or_host=('GW1_ip', 20022),
    remote_bind_address=('GW2_ip', 22),
) as tunnel1:
    print('Connection to tunnel1 (GW1_ip:GW1_port) OK...')
    with sshtunnel.open_tunnel(
        ssh_address_or_host=('localhost', tunnel1.local_bind_port),
        remote_bind_address=('target_ip', 22),
        ssh_username='GW2_user',
        ssh_password='GW2_pwd',
    ) as tunnel2:
        print('Connection to tunnel2 (GW2_ip:GW2_port) OK...')
        with SSHClient() as ssh:
            ssh.connect('localhost',
                port=tunnel2.local_bind_port,
                username='target_user',
                password='target_pwd',
            )
            ssh.exec_command(...)

CLI usage

::

$ sshtunnel --help
usage: sshtunnel [-h] [-U SSH_USERNAME] [-p SSH_PORT] [-P SSH_PASSWORD] -R
                 IP:PORT [IP:PORT ...] [-L [IP:PORT [IP:PORT ...]]]
                 [-k SSH_HOST_KEY] [-K KEY_FILE] [-S KEY_PASSWORD] [-t] [-v]
                 [-V] [-x IP:PORT] [-c SSH_CONFIG_FILE] [-z] [-n]
                 [-d [FOLDER [FOLDER ...]]]
                 ssh_address

Pure python ssh tunnel utils
Version 0.4.0

positional arguments:
  ssh_address           SSH server IP address (GW for SSH tunnels)
                        set with "-- ssh_address" if immediately after -R or -L

optional arguments:
  -h, --help            show this help message and exit
  -U SSH_USERNAME, --username SSH_USERNAME
                        SSH server account username
  -p SSH_PORT, --server_port SSH_PORT
                        SSH server TCP port (default: 22)
  -P SSH_PASSWORD, --password SSH_PASSWORD
                        SSH server account password
  -R IP:PORT [IP:PORT ...], --remote_bind_address IP:PORT [IP:PORT ...]
                        Remote bind address sequence: ip_1:port_1 ip_2:port_2 ... ip_n:port_n
                        Equivalent to ssh -Lxxxx:IP_ADDRESS:PORT
                        If port is omitted, defaults to 22.
                        Example: -R 10.10.10.10: 10.10.10.10:5900
  -L [IP:PORT [IP:PORT ...]], --local_bind_address [IP:PORT [IP:PORT ...]]
                        Local bind address sequence: ip_1:port_1 ip_2:port_2 ... ip_n:port_n
                        Elements may also be valid UNIX socket domains:
                        /tmp/foo.sock /tmp/bar.sock ... /tmp/baz.sock
                        Equivalent to ssh -LPORT:xxxxxxxxx:xxxx, being the local IP address optional.
                        By default it will listen in all interfaces (0.0.0.0) and choose a random port.
                        Example: -L :40000
  -k SSH_HOST_KEY, --ssh_host_key SSH_HOST_KEY
                        Gateway's host key
  -K KEY_FILE, --private_key_file KEY_FILE
                        RSA/DSS/ECDSA private key file
  -S KEY_PASSWORD, --private_key_password KEY_PASSWORD
                        RSA/DSS/ECDSA private key password
  -t, --threaded        Allow concurrent connections to each tunnel
  -v, --verbose         Increase output verbosity (default: ERROR)
  -V, --version         Show version number and quit
  -x IP:PORT, --proxy IP:PORT
                        IP and port of SSH proxy to destination
  -c SSH_CONFIG_FILE, --config SSH_CONFIG_FILE
                        SSH configuration file, defaults to ~/.ssh/config
  -z, --compress        Request server for compression over SSH transport
  -n, --noagent         Disable looking for keys from an SSH agent
  -d [FOLDER [FOLDER ...]], --host_pkey_directories [FOLDER [FOLDER ...]]
                        List of directories where SSH pkeys (in the format `id_*`) may be found

.. _Pahaz: https://github.com/pahaz .. _sshtunnel: https://pypi.python.org/pypi/sshtunnel .. paramiko: http://www.paramiko.org/ .. |CircleCI| image:: https://circleci.com/gh/pahaz/sshtunnel.svg?style=svg :target: https://circleci.com/gh/pahaz/sshtunnel .. |AppVeyor| image:: https://ci.appveyor.com/api/projects/status/oxg1vx2ycmnw3xr9?svg=true&passingText=Windows%20-%20OK&failingText=Windows%20-%20Fail :target: https://ci.appveyor.com/project/pahaz/sshtunnel .. |readthedocs| image:: https://readthedocs.org/projects/sshtunnel/badge/?version=latest :target: http://sshtunnel.readthedocs.io/en/latest/?badge=latest :alt: Documentation Status .. |coveralls| image:: https://coveralls.io/repos/github/pahaz/sshtunnel/badge.svg?branch=master :target: https://coveralls.io/github/pahaz/sshtunnel?branch=master .. |pyversions| image:: https://img.shields.io/pypi/pyversions/sshtunnel.svg .. |version| image:: https://img.shields.io/pypi/v/sshtunnel.svg :target: sshtunnel .. |license| image:: https://img.shields.io/pypi/l/sshtunnel.svg :target: https://github.com/pahaz/sshtunnel/blob/master/LICENSE

Online documentation

Documentation may be found at readthedocs_.

.. _readthedocs: https://sshtunnel.readthedocs.org/

CONTRIBUTORS

  • Cameron Maske_
  • Gustavo Machado_
  • Colin Jermain_
  • JM Fernández_ - (big thanks!)
  • Lewis Thompson_
  • Erik Rogers_
  • Mart Sõmermaa_
  • Chronial_
  • Dan Harbin_
  • Ignacio Peluffo_
  • Niels Zeilemaker_
  • Georgy Rylov_
  • Eddie Chiang_
  • kkrasovskii_

CHANGELOG

  • v.0.4.0 (Pahaz_)

    • Change the daemon mod flag for all tunnel threads (is not fully backward compatible) to prevent unexpected hangs (#219_)
    • Add docker based end to end functinal tests for Mongo/Postgres/MySQL (#219_)
    • Add docker based end to end hangs tests (#219_)
  • v.0.3.2 (Pahaz, JM Fernández)

    • Fix host key directory detection
    • Unify default ssh config folder to ~/.ssh
  • v.0.3.1 (Pahaz_)

    • Increase open connection timeout to 10 secods
  • v.0.3.0 (Pahaz_)

    • Change default with context behavior to use .stop(force=True) on exit (is not fully backward compatible)
    • Remove useless daemon_forward_servers = True hack for hangs prevention (is not fully backward compatible)
    • Set transport keepalive to 5 second by default (disabled for version < 0.3.0)
    • Set default transport timeout to 0.1
    • Deprecate and remove block_on_close option
    • Fix "deadlocks" / "tunneling hangs" (#173, #201, #162, #211)
  • v.0.2.2 (Pahaz_)

    • Add .stop(force=True) for force close active connections (#201_)
  • v.0.2.1 (Pahaz, Eddie Chiang and kkrasovskii_)

    • Fixes bug with orphan thread for a tunnel that is DOWN (#170_)
  • v.0.2.0 (Georgy Rylov_)

    • Support IPv6 without proxy command. Use built-in paramiko create socket logic. The logic tries to use ipv6 socket family first, then ipv4 socket family.
  • v.0.1.5 (JM Fernández_)

    • Introduce block_on_close attribute
  • v.0.1.4 (Niels Zeilemaker_)

    • Allow loading pkeys from ~/.ssh
  • v.0.1.3 (Ignacio Peluffo_ and others)

    • pkey_file parameter updated to accept relative paths to user folder using ~
    • Several bugfixes
  • v.0.1.2 (JM Fernández_)

    • Fix #77
  • v.0.1.1 (JM Fernández_)

    • Fix #72
  • v.0.1.0 (JM Fernández_)

    • Add tunnel_bindings property
    • Several bugfixes (#49, #56, #57, #59, #60, #62, #64, #66, ...) (Pahaz, JM Fernández)
    • Add TRACE logging level (JM Fernández_)
    • Code and tests refactoring (JM Fernández_)
    • Drop python3.2 support
  • v.0.0.8 (JM Fernández_)

    • Merge #31: Support Unix domain socket (local) forwarding (Dan Harbin)
    • Simplify API (JM Fernández_)
    • Add sphinx-based documentation (JM Fernández_)
    • Add allow_agent (fixes #36, #46) (JM Fernández_)
    • Add compression (JM Fernández_)
    • Add __str__ method (JM Fernández_)
    • Add test functions (JM Fernández_)
    • Fix default username when not provided and ssh_config file is skipped (JM Fernández_)
    • Fix gateway IP unresolvable exception catching (JM Fernández_)
    • Minor fixes (JM Fernández_)
    • Add AppVeyor support (JM Fernández_)
  • v.0.0.7 (JM Fernández_)

    • Tunnels can now be stopped and started safely (#41) (JM Fernández)
    • Add timeout to SSH gateway and keep-alive messages (#29) (JM Fernández)
    • Allow sending a pkey directly (#43) (Chronial)
    • Add -V CLI option to show current version (JM Fernández_)
    • Add coverage (JM Fernández_)
    • Refactoring (JM Fernández_)
  • v.0.0.6 (Pahaz_)

    • add -S CLI options for ssh private key password support (Pahaz_)
  • v.0.0.5 (Pahaz_)

    • add ssh_proxy argument, as well as ssh_config(5) ProxyCommand support (Lewis Thompson_)
    • add some python 2.6 compatibility fixes (Mart Sõmermaa_)
    • paramiko.transport inherits handlers of loggers passed to SSHTunnelForwarder (JM Fernández_)
    • fix #34, #33, code style and docs (JM Fernández_)
    • add tests (Pahaz_)
    • add CI integration (Pahaz_)
    • normal packaging (Pahaz_)
    • disable check distenation socket connection by SSHTunnelForwarder.local_is_up (Pahaz_) [changed default behavior]
    • use daemon mode = False in all threads by default; detail_ (Pahaz_) [changed default behavior]
  • v.0.0.4.4 (Pahaz_)

    • fix issue #24_ - hide ssh password in logs (Pahaz_)
  • v.0.0.4.3 (Pahaz_)

    • fix default port issue #19_ (Pahaz_)
  • v.0.0.4.2 (Pahaz_)

    • fix Thread.daemon mode for Python < 3.3 #16, #21 (Lewis Thompson, Erik Rogers)
  • v.0.0.4.1 (Pahaz_)

    • fix CLI issues #13_ (Pahaz_)
  • v.0.0.4 (Pahaz_)

    • daemon mode by default for all threads (JM Fernández, Pahaz) - incompatible
    • move make_ssh_forward_server to SSHTunnelForwarder.make_ssh_forward_server (Pahaz, JM Fernández) - incompatible
    • move make_ssh_forward_handler to SSHTunnelForwarder.make_ssh_forward_handler_class (Pahaz, JM Fernández) - incompatible
    • rename open to open_tunnel (JM Fernández_) - incompatible
    • add CLI interface (JM Fernández_)
    • support opening several tunnels at once (JM Fernández_)
    • improve stability and readability (JM Fernández, Pahaz)
    • improve logging (JM Fernández, Pahaz)
    • add raise_exception_if_any_forwarder_have_a_problem argument for opening several tunnels at once (Pahaz_)
    • add ssh_config_file argument support (JM Fernández_)
    • add Python 3 support (JM Fernández, Pahaz)
  • v.0.0.3 (Pahaz_)

    • add threaded option (Cameron Maske_)
    • fix exception error message, correctly printing destination address (Gustavo Machado_)
    • fix pip install failure (Colin Jermain, Pahaz)
  • v.0.0.1 (Pahaz_)

    • SSHTunnelForwarder class (Pahaz_)
    • open function (Pahaz_)

.. _Pahaz: https://github.com/pahaz .. _Cameron Maske: https://github.com/cameronmaske .. _Gustavo Machado: https://github.com/gdmachado .. _Colin Jermain: https://github.com/cjermain .. _JM Fernández: https://github.com/fernandezcuesta .. _Lewis Thompson: https://github.com/lewisthompson .. _Erik Rogers: https://github.com/ewrogers .. _Mart Sõmermaa: https://github.com/mrts .. _Chronial: https://github.com/Chronial .. _Dan Harbin: https://github.com/RasterBurn .. _Ignacio Peluffo: https://github.com/ipeluffo .. _Niels Zeilemaker: https://github.com/NielsZeilemaker .. _Georgy Rylov: https://github.com/g0djan .. _Eddie Chiang: https://github.com/eddie-chiang .. _kkrasovskii: https://github.com/kkrasovskii .. _#13: https://github.com/pahaz/sshtunnel/issues/13 .. _#16: https://github.com/pahaz/sshtunnel/issues/16 .. _#19: https://github.com/pahaz/sshtunnel/issues/19 .. _#21: https://github.com/pahaz/sshtunnel/issues/21 .. _#24: https://github.com/pahaz/sshtunnel/issues/24 .. _#29: https://github.com/pahaz/sshtunnel/issues/29 .. _#31: https://github.com/pahaz/sshtunnel/issues/31 .. _#33: https://github.com/pahaz/sshtunnel/issues/33 .. _#34: https://github.com/pahaz/sshtunnel/issues/34 .. _#36: https://github.com/pahaz/sshtunnel/issues/36 .. _#41: https://github.com/pahaz/sshtunnel/issues/41 .. _#43: https://github.com/pahaz/sshtunnel/issues/43 .. _#46: https://github.com/pahaz/sshtunnel/issues/46 .. _#170: https://github.com/pahaz/sshtunnel/issues/170 .. _#201: https://github.com/pahaz/sshtunnel/issues/201 .. _#162: https://github.com/pahaz/sshtunnel/issues/162 .. _#173: https://github.com/pahaz/sshtunnel/issues/173 .. _#201: https://github.com/pahaz/sshtunnel/issues/201 .. _#211: https://github.com/pahaz/sshtunnel/issues/211 .. _#219: https://github.com/pahaz/sshtunnel/issues/219 .. _detail: https://github.com/pahaz/sshtunnel/commit/64af238b799b0e0057c4f9b386cda247e0006da9#diff-76bc1662a114401c2954deb92b740081R127