Welcome to sshtunnel's documentation!
*************************************

[image: CircleCI][image] [image: AppVeyor][image] [image:
Documentation Status][image] [image: coveralls][image] [image:
version][image]

[image: pyversions][image] [image: license][image]

**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 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(
       '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**):

   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:

   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:

   (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.

   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


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.

sshtunnel.SSH_TIMEOUT = 0.1

   Timeout (seconds) for transport socket ("socket.settimeout")

sshtunnel.TUNNEL_TIMEOUT = 10.0

   Timeout (seconds) for tunnel connection (open_channel timeout)

sshtunnel.DEFAULT_LOGLEVEL = 40

   default level if no logger passed (ERROR)

sshtunnel.DEFAULT_SSH_DIRECTORY = '~/.ssh'

   Path of optional ssh configuration file

sshtunnel.check_address(address)

   Check if the format of the address is correct

   Arguments:
      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

   Arguments:
      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

   Arguments:

      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

   Return:
      "logging.Logger"

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=5.0, threaded=True, compression=None, allow_agent=True, host_pkey_directories=None, *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.

      host_pkey_directories (list):
         Look for pkeys in folders on this list, for example
         ['~/.ssh'].

         Default: "None" (disabled)

         New in version 0.1.4.

      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. You can set to 0.0 for
         disable keepalive.

         Default: 5.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)

   Attributes:

      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.

   daemon_forward_servers = True

      flag tunnel threads in daemon mode

   daemon_transport = True

      flag SSH transport thread in daemon mode

   local_is_up(target)

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

      Arguments:
         target (tuple):
            tuple of type ("str", "int") indicating the listen IP
            address and port

      Return:
         boolean

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

   check_tunnels()

      Check that if all tunnels are established and populates
      "tunnel_is_up"

   static get_agent_keys(logger=None)

      Load public keys from any available SSH agent

      Arguments:
         logger (Optional[logging.Logger])

      Return:
         list

   static get_keys(logger=None, host_pkey_directories=None, allow_agent=False)

      Load public keys from any available SSH agent or local .ssh
      directory.

      Arguments:
         logger (Optional[logging.Logger])

         host_pkey_directories (Optional[list[str]]):
            List of local directories where host SSH pkeys in the
            format "id_*" are searched. For example, ['~/.ssh']

            New in version 0.1.0.

         allow_agent (Optional[boolean]):
            Whether or not load keys from agent

            Default: False

      Return:
         list

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

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

      Arguments:
         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])

      Return:
         paramiko.Pkey

   start()

      Start the SSH tunnels

   stop(force=False)

      Shut the tunnel down. By default we are always waiting until
      closing all connections. You can use *force=True* to force close
      connections

      Keyword Arguments:
         force (bool):
            Force close current connections

            Default: False

            New in version 0.2.2.

      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

   close()

      Stop the an active tunnel, alias to "stop()"

   restart()

      Restart connection to the gateway and tunnels

   property local_bind_ports

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

   property local_bind_hosts

      Return a list containing the IP addresses listening for the
      tunnels

   property local_bind_addresses

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

   property tunnel_bindings

      Return a dictionary containing the active local<>remote
      tunnel_bindings

   property is_active

      Return True if the underlying SSH transport is up

sshtunnel.open_tunnel(*args, **kwargs)

   Open an SSH Tunnel, wrapper for "SSHTunnelForwarder"

   Arguments:
      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)


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)


License
*******

Copyright (c) 2014-2019 Pahaz White

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.
