How to use secure VNC with SSH

Securing a VNC connection with OpenSSH
I write this sitting at Maude’s Cafe in downtown Gainesville via GRU‘s public wireless network which was just deployed. I’m doing it, coincidently enough, over VNC encrypted via SSH. This ties in nicely with my recent wireless articles, including my initial adoption and compiling and configuring drivers.So, while I suck down a nice root beer float and listen to some excellent Jazz, won’t you join me as I discuss configuring VNC to be secure via OpenSSH, using some port forward magic, encryption, and an optional lite install of OpenSSH on Windows NT/2K/XP courtesy of Cygwin and Network Simplicity. (This works end to end doing Linux to Linux straight up as well, but I am on my W2K laptop right now.)

Additionally, I’m going to briefly mention configuring Keychain under Debian. It’s discussed at length by it’s author in a IBM developerWorks article. Keychain is a very slick way of maintaining RSA key passphrases in memory safely without having to type them in all the time. You can stick it in your .bash_profile or .xsession, enter the phrases once, then you’re good to go. (Just lock your desktop or logout before you get up.)

Configuration Summary Steps

First, I’m going to walk through installation of the Windows NT based OpenSSH daemon, which works basically like it does under Unix, but it’s been ported to work properly under Windows. If you’re doing Unix to Unix VNC tunneling with SSH, you can skip this item and the next one, since your SSH is good to go.

Next, I’m going to discuss getting the Windows VNC client ready to go. There are a few things we need to do prior to using it, especially on low bandwidth links.

Then, I’ll walk through SSH port fowards, compression, and encryption options. This is the most important section and will cover the two most popular ways of securing a VNC session to some other machine directly or via a SSH daemon operating as a proxy.

Next, I’ll quickly write about PuTTY, an SSH terminal client for Windows with port forwarding capabilities.

Finally, I’ll discussion a simple Keychain configuration.

OpenSSH on Windows NT/2K/XP with Cygwin

First, you need to install a copy of Network Simplicity’s OpenSSH on Windows for NT based Windows systems. Fetch the latest version there, then run the included Setup.exe file. If you already have a Cygwin environment on the box in question, stop the installation and obtain and install OpenSSH for Cygwin. OpenSSH on Windows is designed for boxes with no prior Cygwin environment as explained by the author in a post to the support forum.

Once installed, the README outlines a few steps you have to take to get your version of OpenSSH ready to roll. By default, everything is installed under C:\Program Files\NetworkSimplicity where familar Unix conventions should jump out at you. You’ll need to create a etc/passwd file with mkpasswd and a etc/group file with mkgroup as outlined in the README. Thereafter, net start opensshd should start OpenSSH. The Quick Start file was all I needed to get up and running.

For compression, I edited C:\.ssh (there’s a period in there) and added a file named config with

Compression Yes
CompressionLevel 9

(This may be undesirable if you plan on using TightVNC, which can do its own zlib compression; See below.)Next, we’ll get VNC Viewer ready to go.

Getting and Preparing VNC Viewer

You probably already have VNC Viewer for your platform, but if not, you can get it from VNC’s new home, by the original creators of VNC, at their download page.

For Windows, you’ll need to allow the viewer to make connections to the localhost. This is disabled by default and no configuration option exists to enable this. So, you will need to edit the registry to enable this feature. The option is called AllowLoopback and exists under the HKEY_LOCAL_MACHINE\Software\ORL\WinVNC3 path. At least on my machine, the ORL\WinVNC3 portion of the path did not yet exist. You can create the key manually and then add 1 as a hexadecimal base, or copy the entry below into a file with a .reg extension and double click it. (You shouldn’t need to restart.)

REGEDIT4

[HKEY_LOCAL_MACHINE\Software\ORL\WinVNC3]
"AllowLoopback"=dword:00000001

Another potentially useful, but not required option is to enable LoopbackOnly which prevents VNC Viewer from making non local connections. Useful if you don’t want to accidently make an insecure connection. (Which could be disasterous.)

REGEDIT4

[HKEY_LOCAL_MACHINE\Software\ORL\WinVNC3]
"LoopbackOnly"=dword:00000001

For local connections, VNC Viewer might want to use Raw encoding. Don’t let it. The best choice is still Hextile encoding. Next, if you don’t want to mess with your X colour depth and restart X, you can enable Restrict pixels to 8-bit as well for a speed increase on slow links.If usability on a slow link is desirably, then fetch a copy of TightVNC viewer. It’s a drop in replacement for VNC Viewer on the client side, and everything above applies, but you need the TighVNC server as well, or something that supports TightVNC compression options. I believe libvncserver supports TightVNC, and servers like KDE’s Krfb support TightVNC through it.

Encrypted Tunneling with OpenSSH

Now, it’s time for the fun part. Herein, we will summon the power of OpenSSH local port fowarding. Before getting into command line semantics, let me explain plainly how local port fowarding works.

With local port fowarding, you tell your local OpenSSH client to listen on a dummy port of your choice (which usually has logical reasoning behind it). You also tell OpenSSH that you would like to relay traffic from this dummy port to some other machine on another (or same) port. Naturally, an OpenSSH daemon running on the machine you wish to relay the traffic to (or through) is the destination, and it will be the machine you actually perform a Secure Shell login to during this operation. (You can request that no command prompt be available and that no tty get allocated; after all, you are tunneling.)

For example, here we wish to securely tunnel VNC traffic from a local machine, 1.2.3.4, to some other machine running a VNC server. The VNC server is running on port 5900 on machine 192.168.1.55. The remote OpenSSH server will be on 192.168.1.55. We use OpenSSH to locally listen on port 5901 on 1.2.3.4 and tell it we want the traffic forwarded to 192.168.1.55 on port 5900 and that we wish to use the OpenSSH daemon on 192.168.1.55 as our relay. This is one possible way of locally forwarding traffic and the corresponding ssh command arguments are:

ssh -L 5901:192.168.1.55:5900 vncuser@192.168.1.55

The command may be unclear at first glance, it was for me. The kicker is the middle argument in the colon seperated list to -L. The first item is our local port, 5901, the last is the remote port we wish to foward traffic on local port 5901 to. So what’s the middle arugment? It’s the machine we wish to actually foward the traffic to with respect to the OpenSSH server we are connecting to. In this instance it is 192.168.1.55. As you will recall in this example, 192.168.1.55 is running the VNC server we wish to relay traffic to, so this important detail may be lost. Another example.Let us imagine, instead, that we want to foward traffic again from port 5901 on the local machine, 1.2.3.4, but this time we’re on a dial up connection outside our “internal network” and wish to access port 5900 on firewalled machine 192.168.1.55. How can we do that? Well, we can’t without a little help from OpenSSH, as the address is nonroutable and we can’t, securely, access it directly. (Assuming VNC traffic is firewalled off.) So, we must instead relay the traffic through our “internal network’s” firewall machine, 192.168.1.1. It is the OpenSSH daemon on that machine that will then relay our traffic from 1.2.3.4 to 192.168.1.55 by sending our traffic on port 5900, from 192.168.1.1, to 192.168.1.55. From the perspective of 192.168.1.55 running the VNC server, the connection request actually came from 192.168.1.1. This additional machine, the firewall, changes our SSH arguments slightly.

ssh -L 5901:192.168.1.55:5900 vncuser@192.168.1.1

As you can see, the middle argument to -L is with respect to how the “internal network” is configured, and need not even be access from the outside world (and it’s not), since the OpenSSH daemon we’re connecting to is on the firewall as indicated in the last portion of the command argument.In this instance all we’re interested in is local port fowarding, so we can disable the execution of a command shell and the allocation of a tty. For that, we use -T to disable pseudo-tty allocation and -N so you do not get a shell and cannot execute commands. If your firewall is an older box with a slow CPU, like mine (P133), you could benefit from utilizing the -c option and choosing the blowfish cypher. It’s considered to be quite secure, and is less CPU intensive than using 3DES, the default. But I leave that choice to you. Thus, our final ssh command for a tunnel with encryption through a firewall would be (backslash continues the command on the next line as ‘one line’):

ssh -L 5901:192.168.1.55:5900 -N -T -c blowfish \
    vncuser@192.168.1.1

I find that works quite nicely for my wireless VNC sessions from outside the “internal network”.It’s important to note that traffic is unencrypted while it travels from your dummy port to your local ssh port on your local loopback device. It’s then unencrypted again from your relay machine, if you’re using one, across the “internal network” to the box running a VNC server.

Setting up port forwards with PuTTY

PuTTY is an excellent Open Source SSH client for Windows which replaced my aging install of Tera Term Pro with the SSH extensions. One of its unique features (at least for Open Source Windows SSH clients) is the inclusion of local and remote port forwarding capabilities. We can use these to accomplish the port fowards we configured above, but without installing OpenSSH for Windows or playing with any configuration files.

If you don’t already have a copy of PuTTY, download a copy and rejoice. Once your excitement has calmed a bit, either run the new installation program or simply copy putty.exe into a directory where you want it to live. Then, run it.

PuTTY comes with an extension set of options that ought to work for even the strangest configurations, but I’m not going to discuss them here. Instead, turn your attention to the SSH option in the category tree which should be under the Connection tree item. Expend it to reveal Tunnels. Adding a new port forward is as simple as selecting the Local radio button to create a local port forward, then entering the source port, which is the port that you’ll be connecting to on the local machine, and finally entering the destination from the perspective of the SSH server you’re connecting to followed by a colon and the remote port you want to use. (You can refer to this screen capture.)

You can add as many as you want. Next, you’ll probably want to move back to the SSH tree entry and select Don’t allocate a pseudo-terminal under Protocol options if you only want to use this particular connection profile for port fowards. If you’d like to also have an active SSH session running, do not choose the aforementioned option. Finally, you may wish to look at the Encryption options section and move the Blowfish cipher to the top and it will become the most perferred cipher. (Here’s another relevant screen capture.)

Configuring and Using Keychain on a Debian GNU/Linux box

Encrypted SSH tunnels can be used quite effectively for a variety of things. For example, I use an SSH tunnel to access my IMAP server at home from the office. With port forwarding, much like the VNC example above, I connect to the local machine, which connects back to my home network over the SSH tunnel. Effective.

However, each time, you need to enter your password at the login prompt. This is, however, entirely unnecessary and vunerable to replay attacks. A far more secure and convenient (although even more secure when inconvenient) way is to setup an RSA key pair, then connection to the remote machine you wish to initiate a tunnel with via RSA key authentication. You can create an RSA key pair with any recent installation of OpenSSH.

postgres@chosen:~$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/postgres/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/postgres/.ssh/id_rsa.
Your public key has been saved in /home/postgres/.ssh/id_rsa.pub.
The key fingerprint is:
6b:1f:aa:93:17:cc:23:22:05:ff:f1:22:2e:f7:0b:6c postgres@chosen

Above an RSA key pair was created with a strong passphrase, using multiple unrelated words and special characters.To use the keys, simply copy the public key, the one with the pub extension, to the machine you wish to create the tunnel with. This target machine will expect the contents of the pubic RSA key generated above in a file called ~/.ssh/authorized_keys. One approach is to use the cat utility to append the contents of the RSA public key to the end of the authorized_keys file.

postgres@target:~$ cat id_rsa.pub >> ~/.ssh/authorized_keys

Now, when you attempt to initiate a connection (with or without tunneling) to the target machine, it should ask you for a phassphrase for your RSA key, not a password. Keychain takes this a step farther and allows you to enter your passphrase once and maintain it in memory for as long as your machine is up. Only a reboot or Keychain’s –clear option will clear it.Keychain is easy to install. If you’re running Debian GNU/Linux simply install the package called, unsurprisingly, keychain. If you will be using the X Window System, you will need to edit etc/X11/Xsession.options and comment out the use-ssh-agent option, since Keychain will take care of this for you. You will need to restart your login manager, if you’re using one, after making that change.

Finally, configure Keychain to run from your .xsession or .bash_profile. I setup mine like the following.

jasonb@chosen:~$ cat .xsession
# Let's have some fun

# Use Keychain to ask for my passphrases only once
# Source the result so correct variables are everywhere

keychain ~/.ssh/id_rsa
. ~/.keychain/`uname -n`-sh

# For ssh-askpass-x11
sleep 2

# Setup our tunnel to fetch mail from my box with needing
# to open 993 on my public IP or manually login at each boot.

ssh -N -T -L 9930:192.168.0.2:993 jasonb@target &

# Finally, start KDE as usual; ssh-askpass might pop up
# the first boot, but not again until the machine is restarted again.

exec startkde

Again, the -N and -T options to OpenSSH prevent the execution of remote commands and the allocation of a pseudo-tty respectively, allowing you to easily background the process. Our friend the -L option allows you to specify the local port, remote server, remote port triplet discussed earlier. Thanks to Keychain you will not be asked for either a password or a passphrase.Happy forwarding!

Relevant and Useful Resources

Any links herein to products or services are not meant as an endorsement of the product or service offered.

  • An excellent VNC through SSH tutorial that first got me up and running and serves as the basis for this article.
  • AT&T — which is also a wonderful phone and ethernet/ provider — which created VNC via their Cambridge Laboratories, also have a brief discussion of using VNC tunneled through SSH.
  • VNC List post discussing tunneling from one machine to another via an intermediary encrypted tunnel.
  • Secure Shell list post also describing an encrypted tunnel via an intermediary.
  • Linux Journal two part article on setting up VNC access to a running X desktop so your state is preserved for access from remote locations. (KDE 3.1’s new Krfb will allow this out of the box.) The second part discusses OpenSSH for tunneling and encryption.
  • RealVNC, home of the latest releases of VNC Viewer and Server for Windows and Unix.
  • TightVNC, home of the Tight VNC server.