Access remote DB via ssh tunnel (Python 3)

Vladimir Kolenov picture Vladimir Kolenov · Jul 20, 2017 · Viewed 7.8k times · Source

I can not understand (even after reading few articles dedicated to ssh tunelling) what parameters of CLI ssh command where in this script. Basically I have to connect to some server (I called it 'ssh_tunnel_host:22'), than connect to db_host using this tunnel.

with SSHTunnelForwarder(
    ('ssh_tunnel_host', 22),
    ssh_username="ssh_username",
    ssh_pkey="/somepath/id_rsa",
    local_bind_address=('0.0.0.0', 1234),
    remote_bind_address=('127.0.0.1', 3306)
) as tunnel:
    client = paramiko.SSHClient()
    client.load_system_host_keys()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    client.connect('127.0.0.1', 1234)
    db_connection = pymysql.connect(host=db_host, port=3306, db='mysql', user='user',
                                    password='password', charset='utf8mb4')

Could someone explain to me:

  1. What is local_bind_address - is it my local address or ssh_tunnel_host local adderss? Where can I learn IP and port?
  2. Depends on previous question - what is remote_bind_address - is it address of ssh_tunnel_host or db_host? Where can I learn these IP and port?
  3. Where should I connect with client.connect()? Local or remote bind?

(I did try to read the docs but it is still messing)

Answer

danny picture danny · Jul 20, 2017

Think of it like a proxy connection. You connect to ssh_tunnel_host:22 and you tell it to proxy connections from its <db host>:3306, meaning port 3306 on db_host accessed by ssh_tunnel_host to you, the client.

You can either specify the local (to you) ip:port you want the proxy connection to be available on, or let the client choose a free one. Omitting local_bind_address does the latter.

You then connect to your local port which is actually a proxy to remote_bind_address:3306.

local_bind_address <-> ssh_tunnel_host <-> remote_bind_address

Code should be:

db_host = '<address reachable only by ssh_tunnel_host>'
with SSHTunnelForwarder(
    ('ssh_tunnel_host', 22),
    ssh_username="ssh_username",
    ssh_pkey="/somepath/id_rsa",
    remote_bind_address=(db_host, 3306)
) as tunnel:
    port = tunnel.local_bind_port
    db_connection = pymysql.connect(
        host='127.0.0.1', port=port, db='mysql', user='user',
        password='password', charset='utf8mb4')
  1. Local to client address. Either set one, or let client choose and find its port from tunnel.local_bind_port.

  2. The address you want ssh_tunnel_host to proxy back to you. If it's a local to the server service, the IP will be 127.0.0.1 and port of the service. It could well be any other IP or an IP in ssh_tunnel_host's network that is not visible outside SSH tunnel host.

  3. Nowhere. The tunnel provides a local ip:port that proxies the remote connection. Once the tunnel is up, no other client is needed. Just connect to the local ip:port.