SSH magic
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.


















IM IN UR TUNNELZ, CONFIGURIN UR PROXYZ
Glynn
April 16, 2008 at 4:37 pm
What’s wrong with:
ssh -t gateway ’ssh destination’
along with appropriate authorized keys
sjf
April 17, 2008 at 12:03 pm
I think the problem with your solution is when you run localcommand ssh… , the command is run by the ssh, and when it ask for the password, you don’t see it. Try to upload your public key from the gateway to the destination with ssh-copy-id.
Nucc
March 6, 2009 at 11:46 pm