Search code examples
pythonssh-tunnel

How to use Python SSHTunnle to forward multiple ports


I need to forward to multiple ports which are sits behind a server

server1(22) -> Server2(mysql, 3360) = local 3360
            -> Server3(http, 8080)  = local 8080
            -> Server4(oracle,1234) = local 1234

I can only access Server2,3, and 4 via server1.

I am using Python ssltunnel package https://pypi.org/project/sshtunnel/

In example1&2, I can only specify one remote&local bind address. Not sure how to connect multiple servers(2,3,4)

Example1

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

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!')

I could use any other Python package that can do the job.


Solution

  • Both examples can be modified slightly to work the way you want.

    There is the singular versions of bindings (local_bind_address & remote_bind_address) and the plural versions of bindings (local_bind_addresses & remote_bind_addresses.

    The singular verisons expects a tuple containing variables for the connections, while the plural versions expects a list of one or more tuple(s).

    Here is a modified version of your example 2:

    import paramiko
    import sshtunnel
    
    tunnels = [("172.16.0.1", 80),
               ("172.16.0.2", 22)]
    
    localPorts = [("127.0.0.1", 1180),
                  ("127.0.0.1", 10022)]
    
    with sshtunnel.open_tunnel(
        (REMOTE_SERVER_IP, 22),
        ssh_username="",
        ssh_pkey="/var/ssh/rsa_key",
        ssh_private_key_password="secret",
        remote_bind_addresses=tunnels,
        local_bind_addresses=localPorts
    ) 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()
    

    If the lengths of the lists are the same length, then the IP-addresses / ports will correspond with each other.

    In my example above, the following is happening:

    Connection: 172.16.0.1 Port: 80, Is tunneled via: 127.0.0.1 Port: 1180

    Connection: 172.16.0.2 Port: 22, Is tunneled via: 127.0.0.1 Port: 10022