SSH behind firewall using simple ncat proxy and unmodified intermediary server

Sometimes we want to SSH to machines that are behind restrictive firewalls or just because the IP keeps changing. Here I discuss a way to proxy using any publicly accessible SSH server without modification to proxy between two machines that are not directly accessible. For example you can SSH from your laptop in a coffee shop to your home machine which has a dynamic IP or your work machine which is behind a firewall.

Solutions exist for making the connection but they use other peoples servers like Google or TeamViewer and most likely don’t work with SSH. There are also some systems such a puppet and chef that will allow controlling machines but not realtime like SSH and also require special code on servers. It’s desired to preserve the beauty of SCP for copying files and SSH (-D or -L) port forwarding so elegantly on the command line. Trying to write something special to do this is not necessary because:

We can just script it with ncat!


So lets talk about the setup for this to work.

  • The intermediary server that can connect to both. You do not need to modify the intermediary server or leave anything running on this machine but you must be able to listen on at least one port. You need to dedicate a port per SSH server. The server only needs nc or ncat to listen and pkill to remove old connections. It’s better to expect nc because ncat is almost ever installed.
  • The SSH server needs a crontab entry installed (or something similar) that connects to the intermediary on a port by status polling to see if a user is trying to connect. Having the SSH server status poll instead of some more sophisticated method makes everything less prone to break. It needs ncat to connect to the intermediary and then execute and relay an SSH connection.
  • The SSH client only needs to handle the ProxyCommand option. Which is almost all SSH clients.

On the SSH server enter the following crontab entry. Here ever minute ncat connects to the intermediary server port 12300. If there is no socket listening then it will exit. If there is a socket listening it will launch another ncat process and connect to the local SSH server. Use the full path for ncat both times, you can look it up by running which ncat. The “2> /dev/null” is just to prevent emails every minute when the server connection is rejected.

crontab -e

* * * * * /usr/bin/ncat intermediary 12300 -e '/usr/bin/ncat localhost 22' 2> /dev/null

To setup the SSH Client edit the file ~/.ssh/config file to add some Host entries. Each entry runs during the SSH session automatically. Here we SSH to the intermediary server and clean out any old sessions using pkill by searching for the exact (-x) match (-f) of the string “nc -l 12300” which is what well will launch next. “nc -l 12300” starts listening on port 12300 which corresponds to the port specified on the SSH server above. The name proxy.home will correspond to how you can refer to it later. By echoing to STDERR (>&2) you can leave yourself notes about each machine to read when you connect.


Host proxy.home
     ProxyCommand ssh user@intermediary 'pkill -x -f "nc -l 12300";nc -l 12300'

Host proxy.laptop
     ProxyCommand ssh user@intermediary 'pkill -x -f "nc -l 12301";nc -l 12301'

Host proxy.laptop2
     ProxyCommand ssh user@intermediary 'echo "This is laptop2" >&2;pkill -x -f "nc -l 12301";nc -l 12301'

If you don’t want to use the ~/.ssh/config file or cannot you can also specify the proxy on the command line. You need to specify some hostname for the SSH client to save public keys under.

$ssh -o "ProxyCommand ssh user@intermediary 'pkill -x -f \"nc -l 12300\";nc -l 12300'" anything

Now you can connect to this machine as if you were directly connecting! Note because the cron job only runs every minute there is a lag for the initial connection between 0 and 60 seconds. After this everything should work fast.

$ssh user@proxy.home

$ssh user@proxy.home alpine #read some mail!

$ssh user@proxy.laptop CoreLocationCLI --once #read location of laptop!

$ssh user@proxy.laptop say "I am a laptop" #mac only

$ssh-copy-id user@proxy.home #copy your ssh keys for quick login

$scp importantfiles proxy.home:/personalfiles # send files home

$scp proxy.home:/forgotfiles workfolder # get files from home

$ssh -D 9050 user@proxy.home #proxy your internet using a SOCKS proxy

More info on SSH tunneling can be found here Configure SSH for automatic tunneling – CERN, here ssh_config man page – OpenBSD, and here Neat Tricks with ncat –