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”:
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.
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.