SSH Tunneling – Using Multi-Hop SSH Tunnel to Reach a Database

linuxnetworkingport-forwardingsshssh-tunnel

We have some remoteservers which are only accessible through a jumphost. On this remoteservers run vms which host the databases i want to reach locally. Each box is a linux system. I have only su rights on my local pc, on the remotehost and its vms.

That means our setup looks like:

My local pc --> jumphost --> remotehost --> vm with mysql on 127.0.0.1:3306

Normally i would ssh three times and then use the mysql cli.

Now i need to setup a tunnel from my local pc to the box hosting the database.

Ulitmatly i want to use something like the python lib ssh-tunnel and its class SSHTunnelForwarder. But i want to start with the bash commands to setup this kind of proxy.

The ssh authentification is password based and each hop requires a different user. Idealy the solution would not require changes to ~/.ssh/config

I have found this post, and i think options 3 is pretty close to what i need.

An example to make it clear. Let's say jumphost is: user1@192.168.11.29, my remotehost is user2@172.1.1.1 and the vm with database is user3@10.100.1.1

what i would try is:

ssh -L 9998:172.1.1.1:22 -N user1@192.168.11.29

tunnel from my local pc port 9998 to port 22 on the remote host

Here starts my problem, how would i specify the user used for the next to ssh tunnels. Could any guide me into the right direction?

ssh -L 9999:10.100.1.1:22-N -p 9998 user2@localhost

tunnel from my local pc port 9999 through the first tunnel to port 22 on the remote host

ssh -L 10000:locahost:3306 -N -p 9999 user3@localhost

Tunnel from local pc port 10000 through the tunnel on port 9999 to the port 3306 on the vm.

After that i should be able o reach the database on the vm with 127.0.0.1:3306 from my local pc as far as i understand.

I hope that makes sense, i am thankful for every answer.

Best Answer

I was able to solve it, thanks to this post

class TunnelNetwork(object):
    def __init__(self, tunnel_info, target_ip, target_port):
        self.tunnel_info = tunnel_info
        self.target_ip = target_ip
        self.target_port = target_port


    def start_tunnels(self):
        self.tunnels = []
        for idx, info in enumerate(self.tunnel_info):
            # if we're not the first element, set the bastion to the local port of the previous tunnel
            if idx > 0:
                info['ssh_address_or_host'] = ('localhost', self.tunnels[-1].local_bind_port)
            # if we are the last element, the target is the real target
            if idx == len(self.tunnel_info) - 1:
                target = (self.target_ip, self.target_port)
            # else, the target is the next bastion
            else:
                if isinstance(self.tunnel_info[idx+1]['ssh_address_or_host'], tuple):
                    target = self.tunnel_info[idx+1]['ssh_address_or_host']
                else:
                    target = (self.tunnel_info[idx+1]['ssh_address_or_host'], 22)

            self.tunnels.append(SSHTunnelForwarder(remote_bind_address=target, **info))
            try:
                self.tunnels[idx].start()
            except:
                return False

        return True

    def stop_tunnels(self):
        for tunnel in reversed(self.tunnels):
            tunnel.stop()    


    def are_tunnels_active(self):
        return self.tunnels and all([t.is_active for t in self.tunnels])


    def get_local_connect_port(self):
        if self.tunnels:
            return self.tunnels[-1].local_bind_port
        else:
            return None

And use it with:

tunnel_i = [
    {"ssh_address_or_host": "192.168.11.29",
     "ssh_username": "user1",
     "ssh_password": "",
    },
    {"ssh_address_or_host": "172.1.1.1 ",
     "ssh_username": "user2",
     "ssh_password": "",
    },
    {"ssh_address_or_host": "10.100.1.1",
     "ssh_username": "user3",
     "ssh_password": "",
    }
]

target_ip = "127.0.0.1"
target_port = 3306

tn = TunnelNetwork(tunnel_i, target_ip, target_port)

tn.start_tunnels()

Make sure AllowTcpForwarding is set to On at each hop you are using.