Raising the Shields Part 13: Secure Remote VNC Desktop with a Raspberry Pi SSH Bridge

I do a lot of remote computing support for my family members and so far used VNC remote screen viewing over an unencrypted connection for the purpose. This is obviously far from perfect from a security point of view but until recently I didn't find anything that is more secure, as simple to use and that doesn't require a third party service that probably decrypts the session in the middle as well. After my recent exploration of ssh (see my posts on ssh login certificates and ssh SOCKS proxying) I thought of a solution with ssh to protect my VNC sessions as well.

Another shortcoming of my old VNC solution was that changing locations of me and the supported parties required reconfiguration of the DSL router at home. Sometimes I am at home behind my home NAT while the other party is behind another NAT. At other times the person to be supported is behind the home NAT and I'm on the other side of the planet. And finally, there are times when both parties are not at home and there still needs to be a way to get connected without using a third party service in the middle. In the past, I've figured out different approaches to do this, such as the VNC server establishing a connection to the client in some scenarios, the VNC client contacting the server in others and by reconfiguring the router at home. Yes, that was complicated.

The solution I have now found fixes both issues and works as follows: To be location independent there needs to be one secure anchor point that is reachable from home and also when one or both parties are not at home and behind other NATs. This secure anchor point is a Raspberry Pi in my home network to which both parties can establish an ssh tunnel through a port that is forwarded from my ADSL router to the Pi.

The side that wants to export the screen establishes the ssh tunnel with the following command that forwards the VNCs server port (TCP 5900) to which client viewers can connect over the ssh tunnel to the Raspbery Pi. On an PC running Ubuntu the commands for this look as follows (for the Windows /Putty version have a look here:

x11vnc -localhost -usepw -forever -display :0
ssh -N -R 17934:localhost:5900 -p 9212 username@domain.com

The first command launches the vnc server and the '-localhost' ensures the server port is only accessible to applications running on the PC and not to the outside world. The ssh command that follows uses the '-N' option in order not to open a remote shell window and the -R option to forward the local server port 5900 to port 17934 on the Raspberry Pi. The '-p 9212' option is used to instruct the ssh client to use tcp port 9212 to connect to the Raspbery Pi instead of the default port 22 for ssh. While this doesn't add a lot of security it at least keeps the authentication log clean as that port is not found by automated bots looking for vulnerable ssh servers on port 22. The final parameter is the username and the domain name of my home network connection and a dynamic DNS service keeps that updated with the IP address that changes once a day. One thing that comes quite handy at this point is that I use certificates for ssh authentication rather than passwords (see my post here), so no password needs to be typed in.

On the side that wants to view the screen, an ssh tunnel is established with a slightly different ssh command that pulls port 17934 from the Raspberry Pi to the same local port number. Notice the use of the '-L' option compared to the the '-R' option as this tunnel does exactly the opposite:

ssh -N -L 17934:localhost:17934 -p 9212 username@domain.com

And that's pretty much it. Once both tunnels are in place any VNC viewer such as Remmina can be used to connect to the VNC server over the two ssh tunnels. Remmina even has the capability to establish the ssh tunnel as part of a connection profile. A nice side effect is that there is no order in which the two ssh tunnels have to be established. A Raspberry Pi, forwarding a TCP port on the home router and 3 shell commands is all it takes. Quite amazing.

One shortcomming that 3 shell commands approach has is that this solution is only suitable for supporting trused relatives aand friends as the ssh tunnel also gives the supported party access to a command shell on the Raspberry Pi. This can be fixed with a little extra time and effort as described here.

(P.S. And in case you wonder about part 1 – 12 of 'Raising the Shields' have a look here)