Network Gymnastics with VirtualBox, CoRD and socat

Posted on Thu, 14 May 2009

I’ve been meaning to write this post for a while, but I only found the right sort of excuse to do it today. At work I was playing with VirtualBox’s RDP (Remote Desktop Protocol: like VNC but much better) and wanted to do some work on it from home on my old Mac laptop, which is a PowerPC G4 and therefore basically useless when it comes to anything regarding virtualisation, so I needed to connect to TCP 127.0.0.1:3389 on my offic machine where VirtualBox (actually, I had started it with VBoxHeadless). But to get there, I first have to SSH into a gateway, and then from the gateway, SSH into my office machine… this calls for some gymastics.

Now, it shouldn’t be too hard to tell that what I probably wanted was some magic invocation of ‘ssh -L …’ to do the job, but it’s more complicated than that because I needed to go through multiple hops, and I don’t relish the idea of trying to work around SSH’s port forwarding mechanism to do that. Alternatively, I could have created some (excrutiating) hybrid of SSH+PPP to form some sort of VPN, but that requires administrative privileges and is not suited to ad-hoc usage.

I have previously used SSH in a tunneled form quite often in the past to perform similar gymnastics and had even written a wrapper-script called ssh-multihop, which was able of adding the appropriate level of escapping to commands that you wanted to run, so I wanted to use a multi-hop invocation of ‘ssh’ to act as a pipe. I then needed to terminate both ends of this pipe with a TCP socket, one active (making the connection) the other passive (listening for a connection). The active end will be remote, it is going to connect to the RDP session on the remote computer; the passive end will be listening locally for the connection from CoRD (a free RDP client for Mac OS X which does a pretty good job).

Enter ‘socat’, which is a tool like the very useful ‘netcat’, but much more powerful; indeed it advertises itself as ‘netcat on steroids’, and its not a bad claim, as it is wildly capable. This is the sort of thing I want to accomplish:

   +-------------+                       +------------+
   | CoRD        |                       | VirtualBox |
   +------+------+                       +-----^------+
          |                                    |
    TCP connect()                         TCP connect()
          |                                    |
   +------V------+                       +-----+------+
   | socat ???   |                       | socat ???  |
   +------+------+                       +-----^------+
          |                                    |
   EXEC office-rdp                       PIPE (stdin/out)
          |                                    |
   +------V------+    SSH connection     +-----+------+
   | ssh         |---------------------->| sshd       |
   +-------------+                       +------------+

Basically, I need to formulate a command which calls socat in such a way that it listens for a connection to 127.0.0.1:3389 (I’m using 3389 as it the same port that is being for the RDP server run by VirtualBox, although this is not a requirement at the CoRD end), executes some command which calls ssh in such a way to create a bidirectional pipe to to office machine (remember, TCP has semantics identical to a bidirectional pipe), and then the ssh invocation eventually needs to run another invocation of socat (which I have already installed on my office machine) in order to proxy its stdin/out to a TCP connection it creates to the VirtualBox instance. Whew, got that?

There is one slight workaround: ‘socat’ version 1.7.0.0 seems to look at its EXEC argument too much and gets confused when the command to run is another socat invocation, so the command I pass to EXEC is instead a script, which I should really parameterise so I can tell it which port to end up proxying. Here is the script:

#!/bin/sh
# filename: office-rdp
#
# Connect to the VRDP session on my office Mac
#
# It is NOT meant to be run by itself, but rather as the EXEC: argument to
# the 'socat' tool.
#
#    socat TCP4-LISTEN:3389,reuseaddr EXEC:office-rdp
#
# TODO: parameterise port

ssh -CA user@ssh-gateway.example.com ssh -A user@office-machine.example.com \
    socat - TCP4:127.0.0.1:3389

Some explanation as to what is happening here: the first ssh is logging into my account on the SSH remote access server at work, the -CA is enabling compression (this hop is across the Internet, so compression is often useful, though perhaps not so for this particular use) and also enables authentication agent forwarding, so the second ssh (launched from the ssh-gateway machine) ends up talking to the ssh-agent on my local machine. The second ssh is logging into my office machine from the ssh-gateway and running the socat command.

The socat command is proxying data from stdin/out (that’s what the ‘-’ means) to a TCP4 active connection to port 3389 on the office-machine’s loopback address, which is the port that VirtualBox’s RDP server is listening on.

Now, ensuring that you have public-key authentication set up and running, and that socat is installed on my office-machine and in my PATH, I can now run the following command to create the local part of the protocol stack in the figure above:

local$ socat TCP4-LISTEN:3389,reuseaddr EXEC:office-rdp

The SSH connection will only be made when it receives a connection on the local machine’s 3338 port (which doesn’t have to be the same as the remote port, I just did this for convenience). When CoRD is told to connect to 127.0.0.1:3389, the office-rdp script will be run (which runs ssh) and the stdin/out of the commad will be proxied to and from the TCP endpoint.

So that is how you can use socat and ssh to forward ports across multi-hop connections. To set up VirtualBox, you need to enable the VRDP server (this is not available in the OSE version). Consult the VirtualBox documentation for how to do this (it’s pretty easy, but you will want to become aware of the authentication options).

Oh, one other thing: you won’t want to do this very regularly: all the hops in-between make it much more likely that a packet-drop event will occur, which will incur a timeout with TCP… this really affects interactivity quite badly. Ensure that you drop the resolution and colour depth down. Ensuring a solid-colour desktop background is also highly recommended. If you’re using RDP to a Windows machine this will all be done for you already, but you’ll need to configure this yourself under Linux systems. Setting the theme to something without shadows and gradients can also be quite useful.

In my, admittedly brief, experience I have found RDP to be a much better performer compared to VNC. I can play games such as Mahjongg with ease, menus are fairly snappy, even running ‘top’ in a Gnome terminal isn’t painful.