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

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@, my remotehost is user2@ and the vm with database is user3@

what i would try is:

ssh -L 9998: -N user1@

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: -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 from my local pc as far as i understand.

I hope that makes sense, i am thankful for every 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
                if isinstance(self.tunnel_info[idx+1]['ssh_address_or_host'], tuple):
                    target = self.tunnel_info[idx+1]['ssh_address_or_host']
                    target = (self.tunnel_info[idx+1]['ssh_address_or_host'], 22)

            self.tunnels.append(SSHTunnelForwarder(remote_bind_address=target, **info))
                return False

        return True

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

    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
            return None

And use it with:

tunnel_i = [
    {"ssh_address_or_host": "",
     "ssh_username": "user1",
     "ssh_password": "",
    {"ssh_address_or_host": " ",
     "ssh_username": "user2",
     "ssh_password": "",
    {"ssh_address_or_host": "",
     "ssh_username": "user3",
     "ssh_password": "",

target_ip = ""
target_port = 3306

tn = TunnelNetwork(tunnel_i, target_ip, target_port)


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


I don't know about python, but modern ssh has the ProxyJump directive.

Here's a snippet with some anonymisation, from one of my ~/.ssh/config files.

host bastion
    user sitaram
    # external IP of bastion host

host inside
    user not-sitaram
    # IP of "inside" as seen from "bastion" server
    proxyjump bastion

After this, I simply say ssh inside (or scp inside:foo bar, or rsync, or git, or whatever), and it "just works" (TM)! Ssh automatically connects to bastion first, establishing -- not a TCP port forward, but a tunnel based on STDIN/STDOUT -- to the inside server, then connects over that tunnel to the inside server.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.