Secure VNC from Anywhere

A few years ago, my brother had running on his computer what is called a VNC server. A VNC server is a Linux program that sends graphics to a port on the computer. That way, another computer can access that port in a VNC client and see whatever graphics you put there. Usually, the VNC server gives a whole desktop session. Now I have my own desktop computer that I like to access remotely, but I don’t like the idea that anyone on the network can access it.

I was using vnc4server, and I put a password on it, but the password is limited to between 6 and 8 characters. That’s just not secure enough for me. I barely have anything on that computer, but still: I like to be secure when it’s free.

The VNC client I use (ssvnc) sends the connection through an SSH tunnel. That’s great, but that’s the client end. Nobody else is forced to do that, so that doesn’t protect the server from a simple brute force attack. This vulnerability did not please me.

I thought of an idea. The SSH tunnel means that any connection to the local port goes through the tunnel and goes to the port on the remote computer. Since it gets to the remote port from an SSH program running on that computer, it’s coming from the inside. That is, it’s accessing localhost, not 0.0.0.0. It’s similar to how ringing the doorbell  to talk to the man of the house is different from calling the man’s son to talk to him. Even though I’m on the outside, the request comes from the inside. Therefore, if I simply limit VNC access to those who come from the inside, I am forcing an SSH tunnel to be used.

It was around this time that I realized the difference between vnc4server and x11vnc. If you want to see what the computer’s monitor sees, use x11vnc. If you want your own graphics session, use vnc4server. It gave me a waive of relief when I finally discovered how to see what the monitor sees. I just couldn’t get that Xstartup file to do it for me. Well, anyway, x11vnc was easy to change. I simply added the -localhost option.

My SSH configuration does not allow password authentication, so a brute force attack would need to be sending public keys … no, I don’t think anybody would be stupid enough to try that. In addition to needing the public key, a private key and the password to that private key are required to decrypt the response. Now I was satisfied; well, almost satisfied.

My brother and I were listening to a lecture about TOR and hidden services. I knew that he had set up a hidden service before, so I asked how it was done. He explained that a configuration file tells the tor daemon what port to be forwarded to the onion address (multiple port-address options can be specified). I then thought, why not use the SSH port? Since an onion address is accessed by a connection that both the client and the server make, neither IP address is necessary, so no router port-forwarding is required for the server. Therefore, I can have SSH access to the server even if I’m on the other side of the world (granted, it would be very slow). Since VNC is accessible through SSH, I can do that too.

That also was very easy to set up. I installed the tor daemon (sudo apt-get install tor), and then edited /etc/tor/torrc. In that file, I just added two lines:

HiddenServiceDir /var/lib/tor/ssh
HiddenServicePort 22 127.0.0.1:22

The hidden service directory can be whatever you want. That is the folder used for storing information about the onion address. The hidden service address gets two arguments: the port on the onion address that is being forwarded, and where to forward it. I used 22 for the first because that’s what every SSH client will be asking for, and localhost:22 for where to forward it because that is where the SSH daemon is listening. I then restarted the daemon (sudo service tor restart), and boom; I had it running. I then looked in /var/lib/tor/ssh/hostname to find the onion address so that I would know where to find it on the client.

On the client, I expected it to be complicated. After all, it doesn’t use TOR by default, so it won’t know how to connect. I looked it up, and I found something surprisingly easy. All I did was add the host to my ~/.ssh/config with one more line than I had before:

proxyCommand ncat --proxy 127.0.0.1:9050 --proxy-type socks5 %h %p

That makes the whole host entry look like this:

Host nameformyserver
    HostName myonionaddress.onion
    User myusername
    proxyCommand ncat --proxy 127.0.0.1:9050 --proxy-type socks5 %h %p

This works because I have tor running all the time, but it might not be that easy for everyone. To get tor to run all the time, first install it with sudo apt-get install tor. Next, you’ll need to get it going. I’ve never done that manually, but if sudo service tor start doesn’t work, try tor --runasdaemon 1.

Now that the onion address was set up and working, I wanted to make that forced. Simple enough: just edit /etc/ssh/sshd_config. Find where it says ListenAddress 0.0.0.0, and change it to ListenAddress 127.0.0.1. If that line has an octothorpe (#) at the beginning, take it out. Once that’s done, it can be restarted with sudo service sshd restart. If that doesn’t work, use sudo service ssh restart.

Now I’m satisfied. To access my computer with VNC, all this is required:

  • The onion address
  • A tor connection
  • My SSH public key
  • My SSH private key
  • The password to my SSH private key
  • The password to VNC

Well, okay. It isn’t quite that hard necessarily. You don’t need the VNC password because you already have SSH access if you’ve gotten that far. With that, you can simply spawn a new VNC session that has no password. Hm, maybe I need more security…

Written on March 13, 2017