This blog describes different approaches to SSH tunneling along with practicle examples. Something that is often not understood or remembered – hopefully you find it helpful.
If you would like to know more about how to implement modern data and cloud technologies into to your business, we at Digitalis do it all: from cloud and Kubernetes migration to fully managed services, we can help you modernize your operations, data, and applications – on-premises, in the cloud and hybrid.
We provide consulting and managed services on wide variety of technologies. Contact us today for more information or to learn more about each of our services.
Tunnelling with SSH
In the world of server administration SSH is the de-facto standard tool for securely logging into servers and getting command-line access but with a bit of imagination there is much more you can do with it. This article assumes that you are already familiar with using SSH to login to machines but there are more features outlined here that you may not be aware of.
Basic port forwarding
This simplest form is to use the -L option when opening an SSH session, for example:
ssh -L 8080:127.0.0.1:8080 [email protected]
This approach can be extended to forward a port to another machine that lives behind the remote server, for example:
ssh -L 8080:10.0.0.10:8080 [email protected]
By default the local port is opened on the loopback address (127.0.0.1) of your local machine but you can override this behaviour using one of two methods. The -g option tells SSH to listen on all interfaces so other hosts on your network can connect to your forwarded port, or you can specify a local listen address at the start of the -L arguments, for example:
ssh -L 192.168.0.1:8080:10.0.0.10:8080
ssh -R 8080:127.0.0.1:8080 [email protected]
ssh -R 8080:192.168.0.1:8080 [email protected]
Finally you can also tell the remote server to listen on a different address by specifying one of the server’s IP addresses at the start of the arguments:
ssh -R 10.0.0.1:8080:127.0.0.1:8080
SSH Over SSH
You could of course just login to jump.example.com then SSH from there to the machine behind it, for example:
laptop:~$ ssh jump.example.com jump:~$ ssh 10.0.0.2 10.0.0.2:~$
ssh -L2222:10.0.0.2:22 jump.example.com ssh -p2222 127.0.0.1
Both of these methods are cumbersome because they require you to manually open a connection to the jump host and then open another connection to the machine you want to access. Luckily there are ways to simplify the process a little.
A single command like this will open a connection to the jump host and automatically use it to tunnel your connection.
ssh -J jump.example.com 10.0.0.2
If this is a connection you are likely to use regularly then you can save yourself some typing by adding the jump server to your ~/.ssh/config file so it is used automatically when you connect to this server, for example:
Host 10.0.0.2 ProxyJump jump.example.com
There are many more things that can be done by editing your ~/.ssh/config file, check out the documentation (https://man.openbsd.org/ssh_config) for more information.
So far we have seen how to use SSH to securely tunnel specific TCP ports over the connection but what if we need to forward multiple connections to different hosts on the remote network? And what if we don’t know in advance what ports we will need to forward? Luckily SSH has you covered there too.
SSH has a SOCKS5 proxy built in which can be used by the client to access anything that the server can reach. This is enabled by passing the -D option to the ssh command along with a port number and optional listen address:
ssh -D 127.0.0.1:6000 [email protected]
This command will open a normal SSH shell session and start a SOCKS5 proxy listening at 127.0.0.1:6000 on your local machine. You can now use this with any software that supports SOCKS5 proxies and gain access to services behind the remote server. A common use for this is to connect to web services that are behind the firewall in the remote network, for example internal management tools. Combining this with a browser plugin such as FoxyProxy makes a great solution that in some cases can completely replace a VPN.
First start the proxy:
ssh -D 127.0.0.1:6000 jump.example.com
ssh "-oProxyCommand /usr/bin/nc -X 5 -x 127.0.0.1:6000 %h %p" 10.0.0.2
Host 10.0.0.2 ProxyCommand /usr/bin/nc -X 5 -x 127.0.0.1:6000 %h %p
ssh -R 127.0.0.1:6000 [email protected]
Forwarding without starting a shell session
Putting these together we can do something like this:
ssh -f -N -L 8080:127.0.0.1:8080 [email protected]
When starting a tunnel in the background there is a risk that the process will start, connect to the SSH server and then fail to establish the port forward. With a simple command like above this would leave you with an SSH process in the background but the port forward will not work. To make this easier to manage it is often useful to add the ExitOnForwardFailure option to your SSH command (or your ~/.ssh/config file), which tells the SSH client to disconnect and exit if it cannot establish the requested port forward(s). Adding this to the command above gives us:
ssh -oExitOnForwardFailure=yes -f -N -L 8080:127.0.0.1:8080 [email protected]
Keeping sessions alive
Unfortunately this is an area that does not have a built-in solution within the SSH client. There are various third-party tools that can manage SSH tunnels and automatically reconnect when needed, however a simple while loop in bash can do the job well enough in many cases, for example:
while true; do ssh -N -oExitOnForwardFailure=yes -L 8080:127.0.0.1:8080 [email protected] sleep 1 done
As you can see above there are many different ways you can use SSH to create secure tunnels to remote environments but there are more tunneling features built into SSH that were not covered, for example you can forward UNIX sockets, or you can create an ad-hoc VPN with the -w argument, or one of the most common uses is to forward X11 connections with the -X or -Y arguments.
The examples I have shown are just the basics, in reality they barely scratch the surface of what can be done with a bit of creative thinking.
There are many ways these features can be combined or nested to create tunnels and bypass firewall rules for both beneficial and malicious purposes. It is definitely something to bear in mind when designing your network’s security!
If you want to understand how to easily ingest data from Kafka topics into Cassandra than this blog can show you how with the DataStax Kafka Connector.
Do you want to know how to build a GitOps CI and CD pipeline for Kubernetes with Fleet and Rancher? This blog introduces you to the tools to get you there!
Do you want to know securely deploy k3s kubernetes for production? Have a read of this blog and accompanying Ansible project for you to run.