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
ncatto listen and
pkillto remove old connections. It’s better to expect
ncatis 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
ncatto 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.
~/.ssh/config 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 firstname.lastname@example.org $ssh email@example.com alpine #read some mail! $ssh firstname.lastname@example.org CoreLocationCLI --once #read location of laptop! $ssh email@example.com say "I am a laptop" #mac only $ssh-copy-id firstname.lastname@example.org #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 email@example.com #proxy your internet using a SOCKS proxy