Remote access to your VNC server via modern browsers

I think I am not the only one who runs a graphical Linux desktop environment somewhere on a server 24/7.
I hear you ask: why would anyone want that?

For me the answer is simple: I work for a company that runs Linux in its datacenters but offers only Windows 10 on its user desktops and workstations. Sometimes you need to test work related stuff on an Internet-connected Linux box. But even more importantly: when I travel, I may need access to my tools – for instance to fix issues with my repositories, my blog, my build server, or build a new package for you guys.

This article documents how I created and run such a 24/7 graphical desktop session and how I made it easily accessible from any location, without being restricted by firewalls or operating systems not under my control. For instance, my company’s firewall/proxy only allows access to HTTP(S) Internet locations; for me a browser-based access to my remote desktop is a must-have.

How to run & access a graphical Linux desktop 24/7 ?

Here’s how: we will use Apache, VNC and noVNC.

VNC or Virtual Network Computing is a way to remotely access another computer using the RFB (Remote Frame Buffer) protocol. The original VNC implementation is open source. In due time, lots of cross-platform clients and servers have been developed for most if not all operating systems. Slackware ships with an optimized implementation which makes it possible to work in a remote desktop even over low-bandwidth connections.

There’s  one thing you should know about VNC. It is a full-blown display server. Many people configure a VNC server to share their primary, physical desktop (your Linux Xserver based desktop or a MS Windows desktop) with remote VNC clients.
VNC is also how IT Help Desks sometimes offer remote assistance by taking control over your mouse, keyboard and screen.
But I want to run a VNC server ‘headless‘. This means, that my VNC server will not connect to a physical keyboard, mouse and monitor. Instead, it will offer virtual access to these peripherals. Quite similar to X.Org, which also provides what is essentially a network protocol for transmitting key-presses, mouse movements and graphics updates. VNC builds on top of X.Org and thus inherits these network protocol qualities.

Start your engines

Get a computer (or two)

First, you will have to have a spare desktop computer which does not consume too much power and which you can keep running all day without bothering the other members of your family (the cooling fans of a desktop computer in your bedroom will keep people awake).

Next, install Slackware Linux on it (any Linux distro will do; but I am biased of course). You can omit all of the KDE packages if you want. We will be running XFCE. Also install ‘tigervnc’ and ‘fltk’ packages from Slackware’s ‘extra’ directory. The tigervnc package installs both a VNC client and a VNC server.

You can store this server somewhere in a cupboard or in the attic, or in the basement: you will not have a need to access the machine locally. It does not even have to have a keyboard/mouse/monitor attached after you have finished implementing all the instructions in this article.

You may of course want to use a second computer – this one would then act as the client computer from which you will access the server.

In my LAN, the server will be configured with the hostname “darkstar.example.net”. This hostname will be used a lot in the examples and instructions below. You can of course pick and choose any hostname you like when creating your own server.
I also use the Internet domain “lalalalala.org” in the example at the end where I show how to connect to your XFCE desktop from anywhere on the Internet. I do not own that domain, it’s used for demonstrative purposes only, so be gentle with it.

Run the XFCE desktop

Let’s start a VNC server and prepare to run a XFCE graphical desktop inside.

alien@darkstar:~$ vncserver -noxstartup

You will require a password to access your desktops.

Password:
Verify:
Would you like to enter a view-only password (y/n)? n

New 'darkstar.example.net:1 (alien)' desktop is darkstar.example.net:1

Creating default config /home/alien/.vnc/config
Log file is /home/alien/.vnc/darkstar.example.net:1.log

What we did here was to allow the VNC server to create the necessary directories and files but I prevented its default behavior to start a “Twm” graphical desktop. I do not want that pre-historic desktop, I want to run XFCE.

You can check that there’s a VNC server running now on “darkstar.example.net:1”, but still without a graphical desktop environment inside. Start a VNC client and connect it to the VNC server address (highlighted in red above):

alien@darkstar:~$ vncviewer darkstar.example.net:1 

TigerVNC Viewer 64-bit v1.10.1 
Built on: 2019-12-20 22:09 
Copyright (C) 1999-2019 TigerVNC Team and many others (see README.rst) 
See https://www.tigervnc.org for information on TigerVNC.
...

The connection of the VNC client to the server was successful, but all you will see is a black screen – nothing is running inside as expected. You can exit the VNC viewer application and then kill the VNC server like this:

alien@darkstar:~$ vncserver -kill :1

The value I passed to the parameter “-kill” is “:1”. This “:1” is a pointer to your active VNC session. It’s that same “:1” which you saw in the red highlighted “darkstar.example.net:1” above. It also corresponds to a socket file in ” /tmp/.X11-unix/”. For my VNC server instance with designation “:1” the corresponding socket file is “X1”. The “kill” command above will communicate with the VNC server through that socket file.

Some more background on VNC networking follows, because it will help you understand how to configure noVNC and Apache later on.
The “:1” number does not only correspond to a socket; it translates directly to a TCP port: just add 5900 to the value behind the colon and you get the TCP port number where your VNC server is listening for client connections.
In our case, “5900 + 1” means TCP port 5901. We will use this port number later on.

If you had configured VNC server to share your physical X.Org based desktop (using the ‘x11vnc’ extension), then a VNC server would be running on “:0” meaning TCP port 5900.
And if multiple users want to run a VNC server on your computer, that is entirely possible! Every new VNC server session will get a new TCP port assigned (by default the first un-assigned TCP port above 5900).
It’s also good to know that the VNC server binds to all network interfaces, including the loop-back address. So the commands “vncviewer darkstar.example.net:1” and “vncviewer localhost:1” give identical result when you start a VNC client on the machine which is also running the VNC server.

Enough with the theory, we need that XFCE session to run! When a VNC server starts, it looks for a script file called “~/.vnc/xstartup” and executes that. This script should start your graphical desktop.

So let’s create a ‘xstartup’ script for VNC, based on Slackware’s default XFCE init script:

alien@darkstar:~$ cp /etc/X11/xinit/xinitrc.xfce ~/.vnc/xstartup
alien@darkstar:~$ vi ~/.vnc/xstartup

Edit that ‘xstartup’ script and add the following lines. They should be the first lines to be executed, so add them directly before the section “Merge in defaults and keymaps“:

vncconfig -iconic &
unset SESSION_MANAGER
unset DBUS_SESSION_BUS_ADDRESS

Add the following lines immediately before the section “Start xfce Desktop Environment” to ensure that your desktop is locked from the start and no-one can hi-jack your unprotected VNC session before you have a chance to connect to it:

# Ensure that we start with a locked session:
xscreensaver -no-splash &
xscreensaver-command -lock &

In order for these commands to actually work, you may have to run “xscreensaver-demo” once, the first time you login to your desktop in VNC, and configure the screensaver properly.

Into the future

This ends the preparations. From now on, you will start your VNC server (hopefully once per reboot) with the following simple command without any parameters:

alien@darkstar:~$ vncserver

That’s it. You have a XFCE desktop running 24/7 or until you kill the VNC server. You can connect to that VNC server with a VNC client (on Slackware that’s the vncviewer program) just like I showed before, and configure your XFCE desktop to your heart’s content. You can close your VNC client at any time and reconnect to the VNC server at a later time – and in the meantime your XFCE desktop will happily keep on running undisturbed.
You can  connect to your VNC server from any computer as long as that computer has a VNC client installed and there’s a network connection between server and client.

How to make that graphical desktop available remotely ?

So now we have a VNC server running with a XFCE desktop environment inside it. You can connect to it from within the LAN using your distro’s VNC viewer application. And now we will take it to the next level: make this Linux XFCE desktop available remotely using a browser based VNC client.

We will require NoVNC software and a correctly configured Apache httpd server. Apache is part of Slackware, all we need to do is give it the proper config, but NoVNC is something we need to download and configure first.

noVNC

The noVNC software is a JavaScript based VNC client application which uses HTML5 WebSockets and Canvas elements. It requires a fairly modern browser like Chromium, Firefox, and also mobile browsers (Android and i/OS based) will work just fine.
With your browser, you connect to the noVNC client URL. Apache’s reverse proxy connects the noVNC client to a WebSocket and that WebSocket in turn connects to your VNC server port.

Some VNC servers like x11vnc and libvncserver contain the WebSockets support that noVNC needs, but our TigerVNC based server does not have WebSocket support. Therefore we will have to add a WebSockets-to-TCP proxy to our noVNC installation. Luckily, the noVNC site offers such an add-on.

Installing

Get the most recent noVNC ‘tar.gz’ archive (at the moment that is version 1.1.0) from here: https://github.com/novnc/noVNC/releases .
As root, extract the archive somewhere to your server’s hard disk, I suggest /usr/local/ :

# tar -C /usr/local -xvf noVNC-1.1.0.tar.gz
# ln -s noVNC-1.1.0 /usr/local/novnc

The symlink “/usr/local/novnc” allows you to create an Apache configuration which does not contain a version number, so that you can upgrade noVNC in future without having to reconfigure your Apache. See below for that Apache configuration.
We also need to download the WebSockets proxy implementation for noVNC:

# cd /usr/local/novnc/utils/
# git clone https://github.com/novnc/websockify websockify

Running

You will have to start the WebSockets software for noVNC as a non-root local account. That account can be your own user or a local account which you specifically use for noVNC. My suggestion is to start noVNC as your own user. That way, multiple users of your server would be able to start their own VNC session and noVNC acccess port.
I start the noVNC script in a ‘screen‘ session (the ‘screen’ application is a console analog of VNC) so that noVNC keeps running after I logoff.
Whether or not you use ‘screen‘, or ‘tmux‘, the actual commands to start noVNC are as follows (I added the output of these commands as well):

$ cd /usr/local/novnc/
$  ./utils/launch.sh --vnc localhost:5901

Warning: could not find self.pem
Using local websockify at /usr/local/novnc/utils/websockify/run
Starting webserver and WebSockets proxy on port 6080
websockify/websocket.py:30: UserWarning: no 'numpy' module, HyBi protocol will be slower
 warnings.warn("no 'numpy' module, HyBi protocol will be slower")
WebSocket server settings:
 - Listen on :6080
 - Web server. Web root: /usr/local/novnc
 - No SSL/TLS support (no cert file)
 - proxying from :6080 to localhost:5901

Navigate to this URL: 
   http://baxter.dyn.barrier.lan:6080/vnc.html?host=baxter.dyn.barrier.lan&port=6080
Press Ctrl-C to exit

I highlighted some of the output in red. It shows that I instruct noVNC (via the commandline argument “–vnc”) to connect to your VNC server which is running on localhost’s TCP port 5901 (remember that port number from earlier in the article when we started vncserver?) and the noVNC WebSockets proxy starts listening on port 6080 for client connection requests. We will use that port 6080 later on, in the Apache configuration.

From this moment on, you can already access your VNC session in a browser, using the URL provided in the command output. But this only works inside your LAN, and the connection is un-encrypted (using HTTP instead of HTTPS) and not secured (no way to control or limit access to the URL).
The next step is to configure Apache and provide the missing pieces.

Apache

Making your Apache work securely using HTTPS protocol (port 443) means you will have to get a SSL certificate for your server and configure ‘httpd’ to use that certificate. I wrote an article on this blog recently which explains how to obtain and configure a free SSL certificate for your Apache webserver. Go check that out first!

Once you have a local webserver running securely over HTTPS, let’s add a block to create a reverse proxy in Apache. If you are already running Apache and have VirtualHosts configured, then you should add the below block to any of your VirtualHost definitions. Otherwise, just add it to /etc/httpd/httpd.conf .

Alias /aliensdesktop /usr/local/novnc

# Route all HTTP traffic at /aliensdesktop to port 6080
ProxyRequests Off
ProxyVia on
ProxyAddHeaders On
ProxyPreserveHost On

<Proxy *>
    Require all granted
</Proxy>

# This will not work when you use encrypted web connections (https):
#<Location /websockify>
#     Require all granted
#     ProxyPass ws://localhost:6080/websockify
#     ProxyPassReverse ws://localhost:6080/websockify
#</Location>
# But this will:

# Enable the rewrite engine
# Requires modules: proxy rewrite proxy_http proxy_wstunnel
# In the rules/conditions, we use the following flags:
# [NC] == case-insensitve, [P] == proxy, [L] == stop rewriting
RewriteEngine On

# When websocket wants to initiate a WebSocket connection, it sends an
# "upgrade: websocket" request that should be transferred to ws://
RewriteCond %{HTTP:Connection} Upgrade [NC]
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule /(.*) ws://127.0.0.1:6080/$1 [P,L]

<Location /aliensdesktop/>
    Require all granted
    # Delivery of the web files
    ProxyPass http://localhost:6080/
    ProxyPassReverse http://localhost:6080/
</Location>

Again, I highlighted the bits in red which you can change to fit your local needs.

The “Alias” statement in the first line is needed to make our noVNC directory visible to web clients, since it is not inside the Apache DocumentRoot (which is “/srv/httpd/htdocs/” by default). I do not like having actual content inside the DocumentRoot directory tree (you never know when you accidentally create a hole and allow the bad guys access to your data) so using an Alias is a nice alternative approach.

You will probably already have noticed that Apache’s reverse proxy will connect to “localhost”. This means you can run a local firewall on the server which only exposes ports 80 and 443 for http(s) access. There is no need for direct remote access to port 6080 (novnc’s websocket) or 5901 (your vncserver).

Check your Apache configuration for syntax errors and restart the httpd if all is well:

# apachectl configtest
# /etc/rc.d/rc.httpd restart

Now if you connect a browser to https://darkstar.example.net/aliensdesktop/vnc.html and enter the following data into the connection box:

  • Host: darkstar.example.net
  • Port: 443
  • Password: the password string you defined for the VNC server
  • Token: leave empty

Then press “Connect”. You will get forwarded to your XFCE session running inside the VNC server. Probably the XFCE screen lock will be greeting you and if you enter your local account credentials, the desktop will unlock.

You can expand this Apache configuration with additional protection mechanisms. That is the power of hiding a simple application behind an Apache reverse proxy: the simple application does what it does best, and Apache takes care of all the rest, including data encryption and access control.
You could think of limiting access to the noVNC URL to certain IP addresses or domain names. or you can add a login dialog in front of the noVNC web page. Be creative.

Configure your ISP’s router

My assumption is that your LAN server is behind a DSL, fiber or other connection provided by a local Internet Service Provider (ISP). The ISP will have installed a modem/router in your home which connects your home’s internal network to the Internet.

So the final step is to ensure that the HTPS port (443) of your LAN server is accessible from the internet. For this, you will have to enable ‘port-forwarding’ on your Internet modem/router. The exact configuration will depend on your brand of router, but essentially you will have to forward port 443 on the router to port 443 on your LAN server’s IP address.

If you have been reading this far, I expect that you are serious about implementing noVNC. You should have control over an Internet domain and the Internet-facing interface of your ISP’s modem/router should be associated with a hostname in your domain. Let’s say, you own the domain “lalalalala.org” and the hostname “alien.lalalalala.org” points to the public IP address of your ISP’s modem. Then anyone outside of your home (and you too inside your home if the router is modern enough) can connect to: https://alien.lalalalala.org/aliensdesktop/vnc.html and enter the following data into the connection box in order to connect to your VNC session:

  • Host: alien.lalalalala.org
  • Port: 443
  • Password: the password string you defined for the VNC server
  • Token: leave empty

That’s it! I hope it was all clear. I love to hear your feedback. Also, if certain parts need clarification or are just not working for you, let me know in the comments section below.

Cheers, happy holidays, Eric

12 thoughts on “Remote access to your VNC server via modern browsers




  1. Pingback: Links 24/12/2019: Cantor 19.12, antiX 19.1, HyperbolaBSD Roadmap | Techrights

  2. Hi Eric,
    Thanks for this article. I recently changed jobs, and unlike at my previous office, I don’t have admin privileges on my office pc, and the firewall is a lot more restrictive. Therefore I am no longer able to use an openvpn client to reach my home network via the openvpn server running on my router.

    Just in time, I read your article about noVNC. First I had to learn the basics of apache and openssl, and following your “Using Let’s Encrypt to Secure your Slackware webserver with HTTPS” https://alien.slackbook.org/blog/using-letsencrypt-to-secure-your-slackware-webserver-with-https/ set up my apache server first, and eventually I was able to set up noVNC to access my desktop at home (I used the physical desktop on display :0 instead of a virtual desktop).

    I set up a password in apache, so there are three layers of protection before I can get into my desktop (passwords for apache, vncserver, and the lock screen on the desktop). My router is a mini pc with Slackware 14.2 on it, and I used your invaluable firewall creator tool, http://www.slackware.com/~alien/efg/ to set up the firewall on it. This leads to my question. It was unclear to me the reason you recommended to set up a local firewall on the desktop, and just allow ports 80 and 443 to it from the LAN. Why is this extra security necessary? My LAN should be fairly secure, being behind the firewall on the router, shouldn’t it?

    Another question I have is perhaps more related to your latest aricle, “Setting up an Outline server to bypass state firewalls” https://alien.slackbook.org/blog/setting-up-an-outline-server-to-bypass-state-firewalls/. Long time ago I set up a similar solution, using stunnel. I followed this guide, “Make a VPN Server with a Raspberry Pi, OpenVPN and Stunnel” https://www.youtube.com/redirect?event=video_description&v=nnQDiGBFIXk&q=http%3A%2F%2Fwww.farrellf.com%2Fprojects%2Fsoftware%2F2016-05-04_Running_a_VPN_Server_with_OpenVPN_and_Stunnel%2F&redir_token=eD1F2TC34DJA7qu-Aowv1jP-scZ8MTU4NDA2OTU3NUAxNTgzOTgzMTc1 , and modified it for my slackware router. With it, I could browse the internet unhindered on my mobile phone and on my laptop from abroad. Stunnel uses port 443, so I had to turn it off because now apache/noVNC uses that port. I think stunnel could be set up to use another port, but doing so may be problematic. Do you see an obvious solution that would allow stunnel and noVNC to work simultaneously?


    1. Some answers:

      > the reason you recommended to set up a local firewall on the desktop,
      > and just allow ports 80 and 443 to it from the LAN. Why is this extra security necessary?
      > My LAN should be fairly secure, being behind the firewall on the router, shouldn’t it?
      I was talking about the server there, not a desktop. You should not open up more ports on a server than necessary, and for the purpose of an NoVNC server you do not need to expose more than ports 80 and 443.
      Your own setup can of course be as insecure as you want.

      > Do you see an obvious solution that would allow stunnel and noVNC to work simultaneously?
      I think you should have a look at sslh, a ssl/ssh multiplexer which also understands openvpn and http traffic ; see http://www.rutschle.net/tech/sslh/README.html
      On slackbuilds.org you can find a script to build a package for sslh: https://slackbuilds.org/repository/14.2/network/sslh/



  3. I couldn’t clone websockify, git wants /usr/share/curl/ca-bundle.crt. So I got around that with
    mkdir /usr/share/curl
    ln -sv /usr/share/ncat/ca-bundle.crt /usr/share/curl/ca-bundle.crt
    to make git clone websockify work. The only packages with ca-bundle.crt are kdelibs and kdelibs4support from ktown which are identical, and the newer, larger one from nmap.


    1. Henry, I guess you are not fully up-to-date with your Slackware-current. I have no problems running the ‘git clone’ commandline from the article and I have curl-7.69.1-x86_64-3 installed.


      1. To prevent git form looking for ca-bundle.crt, issue this command:
        git config –global http.sslverify “false”


        1. Correction: To prevent git FROM looking for ca-bundle.crt, issue this command:
          git config –global http.sslverify “false”

          Please delete my reply dated
          March 30, 2020 at 19:06
          #comment-37225


  4. While troubleshooting a wpa_supplicant authenticate issue (router had cached a different MAC address, rtl8188ee kept changing the address during power-on), I rsync’d slackware64-current from mirrors.slackware.com and kde/deps from slackbook ktown.”find . -type f -name \*.txz | xargs upgradepkg –reinstall –install-new” to make sure fully up-to-date. /var/log/packages/curl-7.69.1-x86_64-3 does not contain /usr/share/curl, or ca-bundle.crt. “grep ca-bundle.crt /var/log/packages/*” does not contain /usr/share/curl. Git clone works fine on other github sites. The problem appears only with websockify, https://github.com/novnc/websockify.git
    Cloning into ‘websockify’…
    fatal: unable to access ‘https://github.com/novnc/websockify.git/’: error setting certificate verify locations:
    CAfile: /usr/share/curl/ca-bundle.crt
    CApath: none
    I’ve never encountered that error before


Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.