Jason’s musings

I just need a little time

Posts Tagged ‘networking hack

SSH magic

with 4 comments

I’m currently bedridden at home from a nasty dose of the flu and therefore have to use ssh to connect to my machine in the lab.

This machine is behind a firewall however, requiring the use of an intermediary SSH server. So typically you’d do:

CLIENT>  ssh GATEWAY
GATEWAY> ssh DESTINATION

While this can be satisfactory enough for most uses, it doesn’t make copying files any easier, and my usual course of action was to copy the file to the gateway machine and then copy it from there to my laptop.

Today I got a little tired of that and decided to fix it.

This exerpt from “SSH: The definitive guide” proposes that you either use a remote authorized_hosts file command from the gateway to connect to the destination or to create a manual tunnel like so:
# ~/.ssh/config on client C
host S
hostname localhost
port 2001


# Execute on client C
$ ssh -L2001:S:22 G

# Execute on client C in a different shell
$ ssh S

This works well enough, but annoying that it required the manual creation of the tunnel. As for the authorized_hosts command method, it would require creating a new key for the client to use to make each connection to the gateway in order for the gateway to know which machine to connect to. (Doesn’t matter if you don’t understand what I mean) – Too much effort.

Instead I started thinking about using ssh_config to create the tunnel automatically before the connection:

~/.ssh/config:

HOST DESTINATION
hostname GATEWAY
ControlPath=none
LocalForward 2002 DESTINATION:22
PermitLocalCommand yes
LocalCommand ssh -p 2002 localhost

However, this didn’t work, and I didn’t figure out why it couldn’t get past the key exchange.

Another solution I considered was to use something called ProxyCommand, which I already use to connect to machines using HTTPS and SOCKS proxies. So I wrote a script that will make an ssh tunnel through the gateway and connect to the remote machine using the handy ‘nc’ utility.

~/.ssh/config:

HOST [DESTINATION]
ProxyCommand ~/bin/sc 2002 %h %p [GATEWAY]

~/bin/sc:

#!/bin/sh
#$1 == local port $2 == far away host $3 == far away host port $4 == gateway host
ssh -oControlPath=~/.$2$4 -f -N -L$1:$2:$3 $4
nc localhost $1

This will always use the same tunnel to forward a connection to the ssh server on the destination machine, saving resources. There was however the issue that ssh would constantly try to create the tunnel at each invocation, and the ControlPath was a neat solution to avoid that (if there is already exists a tunnel it silently does nothing). The only problem with this technique is that I couldn’t come up with a good way to garbage collect the unused tunnels. Perhaps a file locking based technique, using a counter (similar to reference counting) would work well. But it would be a much bigger project than I wanted.

I realised that using nc on the gateway machine would also work:

~/.ssh/config

HOST [DESTINATION]
ProxyCommand ssh [GATEWAY] nc %h %p

However this unsatisfactorily left nc processes running on the gateway machine, probably due to unimplemented shutdown message handling in some part of the network stack.

Both of these solutions were nice that they allowed the client to connect directly to the ssh server on the destination, enabling the use of ssh keys, forwarded ports, and preventing man-in-the-middle attacks. However, the nc utility is not exactly meant to be used in a production setup. A proper solution to the whole problem would involve modifying the sourcecode of ssh, to allow specifying a gateway in some fashion.

Update: Looking at the  Git tips page I noticed that the following works:

Host *.foo.internal
     ProxyCommand ssh gateway.foo.com exec nc %h %p

I really should have realized that would work. This is the perfect solution.

Written by jasonmc

April 16, 2008 at 12:09 am

Posted in Computing

Tagged with