Jun 06

SSH tip: Automatic Reverse Tunnels for Workflow Simplification

I came up with a trick today to simplify the workflow of certain tasks with SSH. The problem this solves is largely driven by a particular annoyance I had with remote SSH file management. Consider the following workflow for locating a remote file and copying it back to the local machine:

# Find the remote file:
local:~$ ssh remote
remote:~$ find . -name 'file.tar.gz'

# Ah, there it is! Now I want to copy it back to my local machine.
# Pop open a new shell to my local machine in a separate tab and copy the file with scp:
local:~$ scp remote:/home/cody/somewhere/deep/file.tar.gz .

The reason I have to run scp on my local machine is because it is behind a firewall, so the remote machine can't see it directly. Ideally, I could just run "scp file.tar.gz local:" from the remote machine, but SSH wouldn't be able to resolve my home computer's IP address or be able to get to the port, as I am almost always behind a firewall. This workflow also breaks down when you need to run "sudo" on the remote machine to access the files. In this case, you might feel forced to create a temporary tarball remotely using sudo before copying it to the local machine.

There is a way to get around this limitation with DNS/firewalls, and the answer is by using SSH reverse tunnels to bind your local SSH daemon port to a remote SSH alias. This can be accomplished in a simple manner by modifying your ~/.ssh/config on both the local and remote computers.

On the local computer, the SSH configuration entry for the remote host should be given a RemoteForward option, specifying that localhost:22 (the local ssh daemon) should be tunnelled to remote port 8022:

Host remote
HostName remote.example.com
RemoteForward 8022 localhost:22

On the remote computer, an alias to the local computer is created, binding itself to the local SSH daemon:

Host local
Port 8022

Now, the local machine is transparently accessible from the remote machine, replacing the above workflow with something a little smoother:

# Find the remote file:
local:~$ ssh remote
remote:~$ find . -name 'file.tar.gz'

# Don't go anywhere. Just copy it:
remote:~$ scp /home/cody/somewhere/deep/file.tar.gz local:

Update (6-8-2010): Some of the commenters have expressed security concerns regarding using SSH tunnels in this way. Please see the comments for more info (particularly this one). Thanks to those who point out when I'm wrong!