Welcome to sshtunnel’s documentation!

CircleCI AppVeyor Documentation Status coveralls version

pyversions license

Author: Pahaz Blinov

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

Inspired by https://github.com/jmagnusson/bgtunnel, but it doesn’t work on Windows.

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

Requirements

Installation

sshtunnel is on PyPI, so simply run:

pip install sshtunnel

or

easy_install sshtunnel

to have it installed in your environment.

For installing from source, clone the repo and run:

python setup.py install

Testing the package

In order to run the tests you first need tox 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.

from sshtunnel import SSHTunnelForwarder

server = SSHTunnelForwarder(
    'pahaz.urfuclub.ru',
    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):

import paramiko
from sshtunnel import SSHTunnelForwarder

with SSHTunnelForwarder(
    (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:

from sshtunnel import SSHTunnelForwarder
from time import sleep

with SSHTunnelForwarder(
    ('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:

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

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]
                 ssh_address

Pure python ssh tunnel utils

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

API

sshtunnel - Initiate SSH tunnels via a remote gateway.

sshtunnel works by opening a port forwarding SSH connection in the background, using threads.

The connection(s) are closed when explicitly calling the SSHTunnelForwarder.stop() method or using it as a context.

exception sshtunnel.BaseSSHTunnelForwarderError(*args, **kwargs)

Exception raised by SSHTunnelForwarder errors

exception sshtunnel.HandlerSSHTunnelForwarderError(*args, **kwargs)

Exception for Tunnel forwarder errors

class sshtunnel.SSHTunnelForwarder(ssh_address_or_host=None, ssh_config_file='~/.ssh/config', ssh_host_key=None, ssh_password=None, ssh_pkey=None, ssh_private_key_password=None, ssh_proxy=None, ssh_proxy_enabled=True, ssh_username=None, local_bind_address=None, local_bind_addresses=None, logger=None, mute_exceptions=False, remote_bind_address=None, remote_bind_addresses=None, set_keepalive=0.0, threaded=True, compression=None, allow_agent=True, *args, **kwargs)

SSH tunnel class

  • Initialize a SSH tunnel to a remote host according to the input arguments

  • Optionally:
    • Read an SSH configuration file (typically ~/.ssh/config)
    • Load keys from a running SSH agent (i.e. Pageant, GNOME Keyring)
Raises:
  • BaseSSHTunnelForwarderError – raised by SSHTunnelForwarder class methods

  • HandlerSSHTunnelForwarderError – raised by tunnel forwarder threads

    Note

    Attributes mute_exceptions and raise_exception_if_any_forwarder_have_a_problem (deprecated) may be used to silence most exceptions raised from this class

Keyword Arguments:
 
  • ssh_address_or_host (tuple or str) – IP or hostname of REMOTE GATEWAY. It may be a two-element tuple (str, int) representing IP and port respectively, or a str representing the IP address only

    New in version 0.0.4.

  • ssh_config_file (str) – SSH configuration file that will be read. If explicitly set to None, parsing of this configuration is omitted

    Default: SSH_CONFIG_FILE

    New in version 0.0.4.

  • ssh_host_key (str) – Representation of a line in an OpenSSH-style “known hosts” file.

    REMOTE GATEWAY‘s key fingerprint will be compared to this host key in order to prevent against SSH server spoofing. Important when using passwords in order not to accidentally do a login attempt to a wrong (perhaps an attacker’s) machine

  • ssh_username (str) – Username to authenticate as in REMOTE SERVER

    Default: current local user name

  • ssh_password (str) – Text representing the password used to connect to REMOTE SERVER or for unlocking a private key.

    Note

    Avoid coding secret password directly in the code, since this may be visible and make your service vulnerable to attacks

  • ssh_port (int) – Optional port number of the SSH service on REMOTE GATEWAY, when ssh_address_or_host` is a str representing the IP part of REMOTE GATEWAY‘s address

    Default: 22

  • ssh_pkey (str or paramiko.PKey) – Private key file name (str) to obtain the public key from or a public key (paramiko.pkey.PKey)

  • ssh_private_key_password (str) – Password for an encrypted ssh_pkey

    Note

    Avoid coding secret password directly in the code, since this may be visible and make your service vulnerable to attacks

  • ssh_proxy (socket-like object or tuple) – Proxy where all SSH traffic will be passed through. It might be for example a paramiko.proxy.ProxyCommand instance. See either the paramiko.transport.Transport‘s sock parameter documentation or ProxyCommand in ssh_config(5) for more information.

    It is also possible to specify the proxy address as a tuple of type (str, int) representing proxy’s IP and port

    Note

    Ignored if ssh_proxy_enabled is False

    New in version 0.0.5.

  • ssh_proxy_enabled (boolean) – Enable/disable SSH proxy. If True and user’s ssh_config_file contains a ProxyCommand directive that matches the specified ssh_address_or_host, a paramiko.proxy.ProxyCommand object will be created where all SSH traffic will be passed through

    Default: True

    New in version 0.0.4.

  • local_bind_address (tuple) – Local tuple in the format (str, int) representing the IP and port of the local side of the tunnel. Both elements in the tuple are optional so both ('', 8000) and ('10.0.0.1', ) are valid values

    Default: ('0.0.0.0', RANDOM_PORT)

    Changed in version 0.0.8: Added the ability to use a UNIX domain socket as local bind address

  • local_bind_addresses (list[tuple]) – In case more than one tunnel is established at once, a list of tuples (in the same format as local_bind_address) can be specified, such as [(ip1, port_1), (ip_2, port2), ...]

    Default: [local_bind_address]

    New in version 0.0.4.

  • remote_bind_address (tuple) – Remote tuple in the format (str, int) representing the IP and port of the remote side of the tunnel.

  • remote_bind_addresses (list[tuple]) – In case more than one tunnel is established at once, a list of tuples (in the same format as remote_bind_address) can be specified, such as [(ip1, port_1), (ip_2, port2), ...]

    Default: [remote_bind_address]

    New in version 0.0.4.

  • allow_agent (boolean) – Enable/disable load of keys from an SSH agent

    Default: True

    New in version 0.0.8.

  • compression (boolean) – Turn on/off transport compression. By default compression is disabled since it may negatively affect interactive sessions

    Default: False

    New in version 0.0.8.

  • logger (logging.Logger) – logging instance for sshtunnel and paramiko

    Default: logging.Logger instance with a single logging.StreamHandler handler and DEFAULT_LOGLEVEL level

    New in version 0.0.3.

  • mute_exceptions (boolean) – Allow silencing BaseSSHTunnelForwarderError or HandlerSSHTunnelForwarderError exceptions when enabled

    Default: False

    New in version 0.0.8.

  • set_keepalive (float) – Interval in seconds defining the period in which, if no data was sent over the connection, a ‘keepalive’ packet will be sent (and ignored by the remote host). This can be useful to keep connections alive over a NAT

    Default: 0.0 (no keepalive packets are sent)

    New in version 0.0.7.

  • threaded (boolean) – Allow concurrent connections over a single tunnel

    Default: True

    New in version 0.0.3.

  • ssh_address (str) – Superseded by ssh_address_or_host, tuple of type (str, int) representing the IP and port of REMOTE SERVER

    Deprecated since version 0.0.4.

  • ssh_host (str) – Superseded by ssh_address_or_host, tuple of type (str, int) representing the IP and port of REMOTE SERVER

    Deprecated since version 0.0.4.

  • ssh_private_key (str or paramiko.PKey) – Superseded by ssh_pkey, which can represent either a private key file name (str) or a public key (paramiko.pkey.PKey)

    Deprecated since version 0.0.8.

  • raise_exception_if_any_forwarder_have_a_problem (boolean) – Allow silencing BaseSSHTunnelForwarderError or HandlerSSHTunnelForwarderError exceptions when set to False

    Default: True

    New in version 0.0.4.

    Deprecated since version 0.0.8: (use mute_exceptions instead)

tunnel_is_up

dict – Describe whether or not the other side of the tunnel was reported to be up (and we must close it) or not (skip shutting down that tunnel)

Note

This attribute should not be modified

Note

When skip_tunnel_checkup is disabled or the local bind is a UNIX socket, the value will always be True

Example:

{('127.0.0.1', 55550): True,   # this tunnel is up
 ('127.0.0.1', 55551): False}  # this one isn't

where 55550 and 55551 are the local bind ports

skip_tunnel_checkup

boolean – Disable tunnel checkup (default for backwards compatibility).

New in version 0.1.0.

check_tunnels()

Check that if all tunnels are established and populates tunnel_is_up

close()

Stop the an active tunnel, alias to stop()

static get_keys(logger=None)

Load public keys from any available SSH agent

Parameters:logger (Optional[logging.Logger]) –
Returns:list
is_active

Return True if the underlying SSH transport is up

local_bind_addresses

Return a list of (IP, port) pairs for the local side of the tunnels

local_bind_hosts

Return a list containing the IP addresses listening for the tunnels

local_bind_ports

Return a list containing the ports of local side of the TCP tunnels

local_is_up(target)

Check if a tunnel is up (remote target’s host is reachable on TCP target’s port)

Parameters:target (tuple) – tuple of type (str, int) indicating the listen IP address and port
Returns:boolean

Deprecated since version 0.1.0: Replaced by check_tunnels() and tunnel_is_up

static read_private_key_file(pkey_file, pkey_password=None, logger=None)

Get SSH Public key from a private key file, given an optional password

Parameters:

pkey_file (str) – File containing a private key (RSA, DSS or ECDSA)

Keyword Arguments:
 
  • pkey_password (Optional[str]) – Password to decrypt the private key
  • logger (Optional[logging.Logger])
Returns:

paramiko.Pkey

restart()

Restart connection to the gateway and tunnels

start()

Start the SSH tunnels

stop()

Shut the tunnel down.

Note

This had to be handled with care before 0.1.0:

  • if a port redirection is opened
  • the destination is not reachable
  • we attempt a connection to that tunnel (SYN is sent and acknowledged, then a FIN packet is sent and never acknowledged... weird)
  • we try to shutdown: it will not succeed until FIN_WAIT_2 and CLOSE_WAIT time out.

Note

Handle these scenarios with tunnel_is_up: if False, server shutdown() will be skipped on that tunnel

tunnel_bindings

Return a dictionary containing the active local<>remote tunnel_bindings

sshtunnel.check_address(address)

Check if the format of the address is correct

Parameters:address (tuple) –

(str, int) representing an IP address and port, respectively

Note

alternatively a local address can be a str when working with UNIX domain sockets, if supported by the platform

Raises:ValueError – raised when address has an incorrect format

Example

>>> check_address(('127.0.0.1', 22))
sshtunnel.check_addresses(address_list, is_remote=False)

Check if the format of the addresses is correct

Parameters:
  • address_list (list[tuple]) –

    Sequence of (str, int) pairs, each representing an IP address and port respectively

    Note

    when supported by the platform, one or more of the elements in the list can be of type str, representing a valid UNIX domain socket

  • is_remote (boolean) – Whether or not the address list
Raises:
  • AssertionError – raised when address_list contains an invalid element
  • ValueError – raised when any address in the list has an incorrect format

Example

>>> check_addresses([('127.0.0.1', 22), ('127.0.0.1', 2222)])
sshtunnel.create_logger(logger=None, loglevel=None, capture_warnings=True, add_paramiko_handler=True)

Attach or create a new logger and add a console handler if not present

Parameters:
  • logger (Optional[logging.Logger]) – logging.Logger instance; a new one is created if this argument is empty
  • loglevel (Optional[str or int]) –

    logging.Logger‘s level, either as a string (i.e. ERROR) or in numeric format (10 == DEBUG)

    Note

    a value of 1 == TRACE enables Tracing mode

  • capture_warnings (boolean) –

    Enable/disable capturing the events logged by the warnings module into logger‘s handlers

    Default: True

    Note

    ignored in python 2.6

  • add_paramiko_handler (boolean) –

    Whether or not add a console handler for paramiko.transport‘s logger if no handler present

    Default: True

Returns:

logging.Logger

sshtunnel.open_tunnel(*args, **kwargs)

Open an SSH Tunnel, wrapper for SSHTunnelForwarder

Parameters:

destination (Optional[tuple]) – SSH server’s IP address and port in the format (ssh_address, ssh_port)

Keyword Arguments:
 
  • debug_level (Optional[int or str]) – log level for logging.Logger instance, i.e. DEBUG

  • skip_tunnel_checkup (boolean) – Enable/disable the local side check and populate tunnel_is_up

    Default: True

    New in version 0.1.0.

Note

A value of debug_level set to 1 == TRACE enables tracing mode

Note

See SSHTunnelForwarder for keyword arguments

Example:

from sshtunnel import open_tunnel

with open_tunnel(SERVER,
                 ssh_username=SSH_USER,
                 ssh_port=22,
                 ssh_password=SSH_PASSWORD,
                 remote_bind_address=(REMOTE_HOST, REMOTE_PORT),
                 local_bind_address=('', LOCAL_PORT)) as server:
    def do_something(port):
        pass

    print("LOCAL PORTS:", server.local_bind_port)

    do_something(server.local_bind_port)

CHANGELOG

License

Copyright (c) 2014-2016 Pahaz Blinov

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.