In our previous blog post “Tunnelling with SSH: The Basics” we have explained, how to set up OpenSSH1 a port forwarding, e.g. to conveniently access a PostgreSQL database in a test environment, even though it is not directly accessible from local network.

In earlier examples, we have seen that a SSH connection to the target system often has to be made through one “jump host” or “jump server”:

Advanced example of local-port-forwarding with SSH

In such cases, you can establish the connection to the database with the command:

ssh -L 54321:db.example.com:5432 jump-host.example.com

In this article, we show how to conveniently set up a port forwarding across more than one jump host using the ~/.ssh/config configuration file. In addition, we see that the SSH configuration helps to reduce typing when writing the SSH command. Finally, we introduce our open-source library ssh-proxy, which can also be used to programmatically establish a port forwarding with multiple jump hosts.

Port forwardings with multiple jump hosts

In contrast to the simple scenario from the introduction, in some cases even the connection to a jump host must be made via another jump host.

Example of a local port forwarding with two jump hosts

In the example shown, a SSH connection from client to jumphost1 is possible. You can also connect from jumphost1 to jumphost2 and finally from jumphost2 to the target system, the database. Note that in this example, neither a direct connection from client to jumphost2 nor from jumphost1 to target system with the database is possible.

If you try to set up a port forwarding to the database with the command

ssh -L 54321:db.example.com:5432 jumphost2.example.com

the SSH command would fail because the connection to jumphost2 cannot be established.

OpenSSH configuration files

Via configuration files OpenSSH provides an elegant way to declaratively specify how a connection to a remote system can be established.

A detailed documentation of OpenSSH configuration files can be found on the ssh_config man page.

For our example, we configure OpenSSH to establish a connection to jumphost2 via jumphost1. To achieve this we add the following entry to ~/.ssh/config:

Host jumphost2.example.com
    ProxyJump jumphost1.example.com

If necessary, the file ~/.ssh/config must be created beforehand.

This enables a port forwarding to the database with the command:

ssh -L 54321:db.example.com:5432 jumphost2.example.com

By the entry in ~/. ssh/config OpenSSH knows that jumphost2 can be reached via jumphost1 and establishes the necessary additional SSH connection transparently in the background.

To shorten the command ssh jumphost2.example.com to ssh jumphost2 we configure an alias for both jumphost1 and jumphost2:

Host jumphost1
    HostName jumphost1.example.com

Host jumphost2
    HostName jumphost2.example.com
    ProxyJump jumphost1

This shortens the port forwarding command to:

ssh -L 54321:db.example.com:5432 jumphost2

To ensure that the connection can be made conveniently and, above all, without having to enter the password multiple times, authentication should, if possible, take place via a generated public/private key pair. At this point we would like to refer to an external tutorial on handling SSH keys.

Any number of jump hosts

In our example, we have seen how to configure port forwardings when two hops are necessary. However since the configuration is declarative, the example can be easily extended to even more complex scenarios that require more jumps.

Generally speaking, let’s assume we want to establish a connection via n jump hosts:

client → jumphost 1 → jumphost 2 → [...] → jumphost n → target

To do so, we would simply have to specify how to get from jump host n-1 to jump host n:

Host jumphost1
    HostName jumphost1.example.com

Host jumphost2
    HostName jumphost2.example.com
    ProxyJump jumphost1

Host jumphost3
    HostName jumphost3.example.com
    ProxyJump jumphost2

[…]

Host jumphost_N
    HostName jumphost_N.example.com
    ProxyJump jumphost_N-1

For more examples and explanations on how to configure jump hosts, we would like to refer to the article OpenSSH/Cookbook/Proxies and Jump Hosts.

Java library “ssh-proxy”

To create a port forwarding programmatically, cronn GmbH provides the Java library ssh-proxy available under an open-source license. The library is based on the widely used Java SSH implementation JSch. This SSH implementation is extended by ssh-proxy to better support SSH configuration files. Directives such as ProxyJump are understood and considered when establishing a connection, just as OpenSSH itself would do.

Example of use:

try (SshProxy sshProxy = new SshProxy()) {
    int targetPort = 5432;
    int randomLocalPort = sshProxy.connect("jumphost2", "db.example.com", targetPort);
    // Connect to the database via localhost:randomLocalPort or inject it is JDBC URL in the Spring context
}
// Note: All port forwardings get closed when the sshProxy instance is closed

For example, the library can be used in an integration test to automatically perform (temporary) a port forwarding to the test database or a service in the test environment, so that a developer or tester can conveniently run or debug the test locally on a laptop.

Coming up

In this article, we have seen how to set up a port forwarding using the SSH configuration file even in more complex scenarios.

In another article we will show how to set up port forwardings on Linux with systemd user services and autossh, which remains permanently and is automatically reestablished, if the network connection has been interrupted.

  1. For conciseness, we use “SSH” and “OpenSSH” synonymously in this article, although strictly speaking SSH stands for the concept for encrypted network connections and OpenSSH is an implementation of it. All examples in the article are OpenSSH-specific.