Thursday, December 15, 2005

Tunneling VNC over SSH

Connecting a Windows Cygwin client to a Linux SSH/VNC server across the public Internet

There are times when I need to connect to my computer at home from work, most often because I need to retrieve a file or program stored there. A common scenario is one where I can (sort of) remember the name of the file I'm after, but not its location on my home computer's hard drive. This computer is a Linux server and what I end up doing is opening a SSH shell into my home box, and then I use tools running on my home machine to locate the file I'm after. Sometimes I find myself changing directories and listing files like mad until it comes up, particularly if the slocate database is out of date. Once I find the file I'm after, I open a second Cygwin shell on the client side (at work that is), and issue SCP to retrieve it.

Sometimes I'm just not in the mood to work with shells. Recently, while in one such mood, I thought I might try perusing the files on my home machine using Gnome’s Nautilus file manager. After some fiddling around with environment variables, I did manage this via X over SSH, but the performance was dismal. I've heard people say that the X protocol is too chatty, and that you shouldn't expect good client/server performance over the Internet in most cases. I’d been advised that I would have better luck using VNC and so I decided to give it a try.

I needed to run the VNC server on the machine whose desktop I wished to view---my home computer---a scenerio that would seem to necessitate that I poke a hole in my home firewall to let the VNC traffic through. Nay, nay.

For reasons I hope are totally obvious, only a single incoming port opens into my home network, and that's SSH port 22. I lock this port down as tight as I can using configuration rules, of course. (Wow…even admitting that I have an open port feels like a security risk.) Instead of exposing the VNC server's listening port to the public Internet, I preferred tunneling VNC traffic over my SSH connection.

It took a few hours of research before I was able to pull this off. I’ll retrace my steps here for the benefit of others of similar need and configuration.

Client Requirements

As I began my research, I found a lot of information on how to accomplish VNC tunneling using the Putty SSH program for Windows on the client side. I personally use Cygwin tools, am quite happy with them, and didn’t see the sense in downloading yet another SSH client (thank you very much). And so I decided to use my existing Cygwin SSH client, one of the two tools you will need to have on your Windows client box if you intend to reproduce these steps for yourself, using my cookbook recipe.

The second of the two Windows programs you’ll need is a VNC client, and I suggest you use TightVNC. I think you can download an executable for the viewer only, but I just grabbed a download that included both the client and server. I’m sure I’ll use the server on my work box at some point down the road.

(By the way, if you google for “SSH VNC Tunneling” and find a lot written about setting an AllowLoopback value in a registry key for TightVNC---disregard it. It’s not needed for what we’re going to do.)

Server Requirements

You’ll need a running server that accepts SSH connections and that has VNC installed. Many modern Linux distributions install it by default, so you may already have it.

1. Open a SSH shell into your remote machine. If you’ve not used VNC before, you should set a password. You’ll provide this password when opening your VNC client later on.

[me@aldous ~]$ vncpasswd
Password: *****
Verify: *****

2. Start a vncserver. You should be able to just type ‘vncserver’ and use the defaults.

[me@aldous ~]$ vncserver

New 'aldous:1 (me)' desktop is aldous:1

Starting applications specified in /home/me/.vnc/xstartup
Log file is /home/me/.vnc/aldous:1.log

3. Use the tail command to peek at your log. We’re interested in which port VNC will be listening on.
[me@aldous ~]$ tail /home/me/.vnc/aldous:1.log

Xvnc version 4.0 - built Oct 6 2004 08:11:33
Underlying X server release 60801000, The X.Org Foundation

Thu Dec 15 09:17:00 2005
vncext: VNC extension running!
vncext: Listening for VNC connections on port 5901
vncext: Listening for HTTP connections on port 5801
vncext: created VNC server for screen 0

We’re interested in port 5901 for this example. That’s it for the server side of things.

4. Open a new Cygwin window on your Windows client and type something like the following, replacing USERNAME with the username you use to SSH into your remote machine, replacing with the IP address or DNS lookup-able name of your home box. Also replace the trailing 5901 with the port reported to you in step three (from the Listening for VNC connections… line).

ssh -l USERNAME -L 5900:

A little explanation here is warranted.

First, let’s see what the ssh man page says about the –L option.

-L [bind_address:]port:host:hostport
Specifies that the given port on the local (client) host is to be
forwarded to the given host and port on the remote side. This
works by allocating a socket to listen to port on the local side,
optionally bound to the specified bind_address. Whenever a con-
nection is made to this port, the connection is forwarded over
the secure channel, and a connection is made to host port
hostport from the remote machine. Port forwardings can also be
specified in the configuration file. IPv6 addresses can be spec-
ified with an alternative syntax:
[bind_address/]port/host/hostport or by enclosing the address in
square brackets. Only the superuser can forward privileged
ports. By default, the local port is bound in accordance with
the GatewayPorts setting. However, an explicit bind_address may
be used to bind the connection to a specific address. The
bind_address of ``localhost'' indicates that the listening port
be bound for local use only, while an empty address or `*' indi-
cates that the port should be available from all interfaces.

Let's look at the command once again.
ssh -l USERNAME -L 5900:
The first number after the –L specifier, 5900, is the port your local client will be listening on that will be forwarded across the tunnel to your remote box. 5900 is the port that the TightVNC client tries to contact when establishing a VNC connection by default, so this is a pretty good number to use. The second number,, is the address of the remote machine from the perspective of the remote machine. Attempting to use the public IP address of your home machine, the one that you see from your client box, probably won’t work because of firewall rules, etc. I won’t get into an explanation here…just use The third number, 5901, is the port your VNC server is listening on, on the remote machine.

Note that the above SSH command will not only create your tunnel, but it will also open a SSH command shell. To prevent this, add –T and –N to your list of command arguments.
ssh -l USERNAME -L 5900: -T -N
This will disable pseudo-tty allocation and prevent you from opening a shell. See man ssh for more information.

5. Start your TightVNC viewer (Fast compression). A dialog will pop up asking you to enter the IP address of a VNC Server. Enter You will then be prompted for the password you created for your VNC server back in step one.

If all went well, you should now be looking at your remote machine’s desktop.

To avoid confusion about the use of the IP address (we gave it to the TightVNC Viewer as the VNC server's address, and to the SSH command as the host address) , let me describe the tunnel from the perspective of the VNC client. For the TightVNC viewer you specified that the VNC server was running on In other words, you specified your localhost...the box at your physical location (a.k.a. the client). The VNC client then looked for the default port that VNC servers run on (5900) and said (or would if it could speak), “Ah ha! There is indeed a VNC server running on this same box.” But of course you fooled it. Due to the nifty SSH command and the –L option, you created a tunnel that forwarded the traffic on the 5900 port on to, through SSH, your home (or remote) machine. The VNC client was never any the wiser.

Something similar happened on the remote side. The request to speak with the VNC server came out of the tunnel on the remote side and it asked (again, if machines could talk like us), “Any VNC servers around here that want to respond to a VNC client? I’m looking for one that is running here locally…it’s addressed to”

The point I’m getting around to is that both the client and the server side of VNC think that they are talking to services running on their own machines. SSH is acting as a proxy on both sides of the tunnel. Pretty cool, eh?

And if it didn’t work for you…

Look at your vncserver log on the remote machine. If an attempt was made and failed for any reason, you’ll read about it there.

[me@aldous ~]$ tail /home/me/.vnc/aldous:1.log

Also make sure your problem is not with making the connection to your remote box. You might check the secure log.

[me@aldous ~]$ tail /var/log/secure