running a desktop with vnc over openvpn
Posted by pj on Wed 9 Jan 2013 at 21:50
I'm going to describe how you can display and control your primary desktop remotely from a secondary so that it everything works really well. To do that, we are going to bolt together several commonly used linux components. Along the way I'll pass on some personal drivel about my own experience in doing this. The idea behind this being that you get a feel of how things hang together.
Now, I said display and control your primary desktop remotely from a secondary. By primary desktop, I want you to think of, for example, your desktop at work. Normally, you run a GUI under X on it, so that it shows applications like x-terminals, menus, screensavers, wallpapers, and so on. You've usually set these up just the way you like it on your primary desktop, so that you can work super efficiently. Every other desktop you configure elsewhere is normally a way to approximate the same stuff that you do on the primary.
By display and control your primary desktop remotely I mean something like this example: One day, you want to work from home, from your home machine, in your bunny slippers. The display and control bit means that you can not only see stuff that is on display on the work machine, but also control stuff on that work machine like you would if you were actually at the desktop at work. So, ideally you want to be able to do it from the home desktop so that it feels just like you are at the work desktop.
Note that I don't mean replicating the desktop. Replicating would mean something like copying the underlying disk drive from the work machine to the machine at home and constantly keeping stuff in sync. That requires too much thinking and maintenance. The display and control bit actually says it quite precisely. What I mean is viewing what is at work (display multiplexing), but being able to work on the desktop running at work too (remote control), all without replication of the underlying filesystem data from the remote to the local.
The tools I use for this are:
- xrandr: deals with a dual monitor set up. Xinerama is sometimes needed instead.
- vnc: does the multiplexing display serving thing (serve the display from the work machine to the home machine).
- xtightvncviewer: does the multiplexing display viewing (and keystrokes/mouse input) thing (lets you view/control the remote machine on the local machine)
- openvpn:- used to connect securely from point to point as if the home and work machine are on the same LAN.
xrandr: X Resize, Rotate and Reflect Extension (RandR). Version 1.3+ (released in 2009) works with dual head setups that use one big virtual screen across both heads.
Actually it works with more than just dual heads, but I just work with dual heads, so that's what I'll consider.
At work the machine I have is a reasonably powerful dual core box, with a single video card ("lspci | grep VGA" shows the chipset as ATI Technologies Inc RS880 [Radeon HD 4200]). With this card is one output for DVI and one for VGA. One output is plugged into one 22" Samsung monitor each, giving me a dual head. Having two outputs from a single video card like this is quite common for current (2012) video cards. The VGA analogue output is not distinguishable in quality from the DVI output on the hardware I use, at least for these 22" monitors running at a resolution of 1920x1200.
Back at home, my desktop set up is rather more humble. Actually, forget humble, it's downright obsolete --- the home box is a P-4 single core machine, with a single built-in VGA out, to which an oldish 1280x1024 LCD monitor is plugged in. To this I added some additional junk --- and I mean junk quite literally. As in, I added a ridiculously old 8MB PCI voodoo 3dfx card from internet prehistory, together with an equally ancient 19" Ilyama CRT monitor. Both of these were junked freebies. However, in the end, I had a home box with a gigantic CRT monitor stood beside an oldish LCD monitor that the box was already using.
Aha!, you may exclaim, the power consumption of the CRT will eat up any savings from the freebieness! Aha yourself! --- I use the CRT about once a week. So it works out to about 6 years before I spend in power the amount I would on a new LCD monitor the same size, without the power consumption. With energy saving modes enabled on the CRT it'd be a few years more. So I'm okay there on green credentials. Not so okay on the exasperation-from-the-significant-other front, but like she says, I'm thick-skinned. Plus I quite like the display on the Ilyama, since it was a fantastically overengineered monitor in its heyday.
So, there I am with two monitors and two video cards. The kernel detects both cards just fine, and Debian's xserver-xorg installs a suite of drivers for X11 for the xserver. The PCI card needs to made to boot first in the BIOS, or it refuses to do X. It seems to be a common quirk for those kind of vintage machines. The voodoo and built-in video are detected just fine after the BIOS tweak.
From the gentoo wiki, the BIOS options can be tweaked like this:
- In AWARD BIOS (v4.51PG), menu: Integrated Peripherals -> Init Display First -> [PCI Slot / AGP]
- In AMIBIOS (v 08.00.08), menu: PCIPnP -> PCI Bus Scan Order -> [Ascent/Descent]
- In other BIOSs it's called: Bios First Boot -> [AGP/PCI]
LCD Monitors can run at lots of resolutions, but the optimal ones are the ones matching the hardware pixel layout (resolution figures like 1920x1200, or 1280x1024). CRT monitors that are 17" or greater can generally handle 1280x1024, while the Ilyama 19" beast I have can cope with 1600x1200, for example. A resolution of 1280x1024 with a depth of 24 is fine for work, and is something even the Voodoo card copes with.
- To run a single monitor on the built-in AGP card, I make sure the video card is enabled in the BIOS, and everything runs as usual, without having to configure X for it. You can generate an xorg.conf (xserver configuration) file for it, by running Xorg -configure, and copying the resulting xorg.conf.new file over to /etc/X11/xorg.conf if you like, but in most cases it isn't needed. Indeed, by default, a recent distribution (squeeze onwards, I think) with a modern X doesn't even bother with an xorg.conf.
- Or instead of the AGP card, I can enable PCI video in the BIOS, so that the voodoo card runs instead. Again, the xorg.conf file isn't needed here either, but can be generated the same way.
- To run both cards, ie to run it as a so-called dual head, I have to enable the PCI video card (voodoo) setting. The xorg.conf for a dual head can be generated either as before, or it can be built up a bit more manually by using the two preceding xorg.conf files as a guide to making one big xorg.conf file to cover both cards. You'll have to use some judgement, but the man page for xorg.conf can be used for some guidance. The result, if you set things up right, is that when the machine boots, the monitor connected to the PCI card starts up first, the machine marches its way towards X, and then the xserver goes through the configuration file and eventually both monitors end up displaying something. Exactly what that "something" is depends on the way X is running.
- The xrandr way
The recommended way of doing multihead is xrandr, especially if you have a single card with dual (or more) outputs. Only if you have multiple cards, and you can't get them all running with xrandr, should you go the older xinerama way, as described later.
By default, on a single card with multiple outputs, x shows up on all displays the same way (a clone view). Thus, the display manager login (xdm/kdm/gdm, whatever) will show up on all the screens. Only when xrandr kicks in will the screens display separately, as in, you typically have one monitor showing the left hand side, and the other monitor showing the right hand side, of a large "logical" screen.
Getting xrandr to run is easy. First run a query with:
See what outputs there are, then build a command that you can run from X as a regular user. For example, you might build scripts like the following:
peej@rotwang:~$ cat singlescreen #!/bin/bash xrandr -s 1280x1024 peej@rotwang:~$ cat dualscreen #!/bin/bash xrandr --output DVI-0 --primary --mode 1280x1024 --left-of VGA-0 --output VGA-0 --mode 1280x1024
You can add the xrandr command to the xsession start up script (.xsessionrc) so that it launches after the display manager login has been carried out.
- The xinerama way
The older way of doing multihead is with xinerama. There, when xdm/kdm/gdm starts up you get one screen showing an xdm/kdm/gdm display manager. I found specifying depth and maybe busID was needed, eg, in /etc/X11/xdm/Xservers:
:0 local /usr/bin/X :0 vt7 -depth 24 -nolisten tcp -isolateDevice PCI:0:9:0
About busIDs: AGP busIDs also use that kind of format (eg: PCI:1:0:0), but start with PCI:1 instead.
The addendum to this article shows a sample xorg.conf file that works for me across two cards and two monitors with xinerama.
- The multiple xserver sessions way?
A probable third way to do multihead, but which I haven't yet been able to beat into submission on my rig, is to run an xserver for each video card. The bit I haven't figured out is how to make both displays show at the same time. If I switch to one with a ctrl alt f8, the other goes dark. You'll have to live with the displays being pretty independent too---that is, not being 1 big logical display like you get with xinerama and xrandr.
To run a different xserver for both, if you are using xdm as your display manager, have a look at the /etc/X11/xdm/Xservers file. It has examples on how to do that near the end. I found that setting -depth explicitly as an option to the xservers there let my display show up properly for some reason as standard multiple x-servers per card, as well as with xinerama over cards.
A way with multiple xserver sessions I've used in the past has been to use a desktop machine with a single display locally and a laptop with a single display locally, to show one screen each of the remote. Not the most efficient way to do stuff, but it works. Of course, most laptops typically have a resolution of 1280x800 so it isn't going to be seamless across monitors unless you're using that per monitor remotely too (which would be pretty unusual). Also, most laptops have an external display port too, so you should be able to do the xrandr or xinerama way anyway.
VNC is awesome! I'll only be going through a part of its capabilities though, because if I go on about it in depth, you will be blinded by its extreme awesomeness. And then you won't be able to read this article properly.
VNC was covered earlier on this website. Basically, if you have been living under a rock for the last 15 years, VNC can display the GUI on the remote (work desktop) to your local (home) display. Not only that, but mouse and keyboard stuff you do locally (at home) gets converted to remote equivalents at the work machine. The result is that it's like you are at the work desktop, except from home. Which is obviously getting us on the right track if we want to work from home.
Set this VNC visual display up right, and you don't have to replicate the file structure on your work machine that makes the remote desktop. This is because, well, you have the remote desktop display itself on display in front of you. So, that means, no copying of .bashrc, /home/, your customized menu, whatever. Your work desktop is just there, in front of your naked, steaming eyes! And "there" then becomes wherever you can get a connection to the work box. If you follow the current IT fashion for cloud-speak, then your workbox is your truly personal cloud!
Most VNC implementations run as a client/server setup. That is, a VNC server runs on the remote, and you use a VNC client for viewing locally. The VNC protocol has a common core standard, so that you can mix different clients and servers. The x11vnc server is designed to send the X11 display to clients, while vino/vinagre is good for gnome. I find x11vnc server works well for me with the xtightvncviewer client. The "tight" in xtightvncviewer refers to clever compression extensions to the original VNC protocol. These extensions make the exchange between client and server take fewer round trips and generally make things snappier. All modern VNC implementations have something similar --- everyone likes responsiveness after all!
The snappiness caused by the compression extensions is remarkable. Trying the same trick of viewing a desktop with standard ssh -X (as described in in http://narnia.cs.ttu.edu/drupal/node/132, or http://www.novell.com/communities/node/6669/rdp-linux-managing-gui-displays-remotely) is painfully, teeth-clenchingly, laggy --- at least over a connection to anywhere beyond a local LAN. Using a compressed VNC client makes the connection miraculously unlaggy.
The VNC server
Installing the VNC server is easy:
work:~# apt-get x11vnc
Then, store a password with (as an ordinary user):
peej@work:~$ x11vnc -storepasswd secret12345
Using a password is a fairly weak precaution (let alone the pathetic password used here), considering there is no encryption during a default VNC connection. You can crank up the connection security to any level you like, including using ssl, piggybacking over ssh, or only allowing access from particular IP addresses. Read the man page if you want to know more on this, but I found my eyes glazing over when I tried reading it. So I used openvpn instead. I'm going to discuss using openvpn later on.
Having got the VNC server ready, we can start it up, as an ordinary user, with:
peej@work:~$ x11vnc -display :0 -usepw -forever
Tip: If you like screen or tmux, you may want to start it up under that at boot from, say cron:
@reboot /usr/bin/screen -d -m -S "x11vncserver-d" /usr/bin/x11vnc -display :0 -usepw -forever
The VNC client
Once the server is running, you can run the client from the home machine. I've set up a mindless shortcut that does something like this:
pkill xtightvncviewer; echo "secret12345" | xtightvncviewer -autopass -fullscreen 192.168.32.2:0
- VNC client gotchas: encryption
One gotcha about VNC is that the default connection configuration is typically done without encryption, even though the option is available. You could say that this is simply the old unix philosophy of having one standard tool doing one special thing incredibly well, and leaving other functionality to other standard tools. So, while the pretty standard tool VNC has as its special thing the snappy replication of a display really well, we can use the functionality of another pretty standard tool for a secure connection, that tool being openvpn. Like I've said, I'll cover using OpenVPN with VNC later.
Anyway, an important point is to be aware of the lack of default encryption, so that you do not use an unencrypted VNC over untrusted networks.
- VNC client gotchas: escaping keystrokes
After you run xtightvncviewer, you get a screen replicating the remote. If you've used the -fullscreen option, and your X resolution locally matches the remote, and you have the same number of monitors in both places, then you should find you have a snappy replica of the remote, that lets you interact with it like you're sitting at the remote. To break out of it, the default key is the f8 key.
Now, I use a lot of special keystrokes---the window manager I use icewm is pretty inspiring that way. So, like a maniacal Emacs user I go about sprinkling ctrl-escs liberally, with nary a thought. Not having to think about the fact I'm doing all this remotely makes me pretty productive. The trouble is that the default xtightVNCviewer implementation hiccups over these keystrokes, and then I suddenly find I can no longer type on the remote. Which is obviously a setback to my productivity. To pass through these kind of keystrokes, you have to change a configuration in the default vncviewer settings file: /etc/X11/app-defaults/Vncviewer. In this file, you uncomment the line:
After that, the only special key that will work is the F8 key. If by chance you need that key for something else, or maybe you've drooled over your f8 key and it doesn't work any more, then you can modify the choice of key in the settings file too.
Like I said earlier in the VNC section, we need to do VNC in a secure way. One way that works for me is to tunnel between the client and server within OpenVPN on the big bad internet.
I covered tunneling securely over OpenVPN in in detail earlier. If you aren't familiar with OpenVPN, you should find that a pretty good guide.
The way I run OpenVPN between two points is something like:
- On the remote primary you can run:
# openvpn --script-security 2 --proto tcp-client --remote 220.127.116.11 \ --tls-client --remote-cert-tls server --ca ca.crt --cert pjworkbox_vpn.crt \ --key pjworkbox_vpn.key --dev tun1 --ifconfig 192.168.32.1 192.168.32.2 \ --verb 4 --port 80 --user nobody --group nogroup --persist-tun --persist-key
The client script repeats indefinitely every 5 seconds or so if it gets no connection, so it is a bit like a service anyway. The IP address 18.104.22.168 specified in it is the internet address of the local machine at home. You can maybe use a dyndns service for that, for convenience. Also, like the x11vnc service, you can run it in a screen session at boot.
- On the local machine at home, you can run:
# openvpn --script-security 2 --proto tcp-server --tls-server --ca ca.crt \ --dh dh2048.pem --cert serverbox_vpn.crt --key serverbox_vpn.key --dev \ tun1 --ifconfig 192.168.32.2 192.168.32.1 --verb 4 --port 80 --user nobody \ --group nogroup --persist-tun --persist-key
I use port 80 for my tunnel to 22.214.171.124---that's the least likely outgoing port to be blocked by a firewall at work. As far as VNC is concerned, it will look like a private LAN connection from 192.168.32.1 to 192.168.32.2.
Once the tunnel is connected up from both ends, I just run that vncviewer script from earlier:
pkill xtightvncviewer echo "secret12345" | xtightvncviewer -autopass -fullscreen 192.168.32.2:0
If all goes well, my snappy remote X session starts, and no one can snoop on the contents.
This way of working remotely can be done in many other ways, including:
- nxserver/nxclient: An optimized non-free way of connecting xclients with their display servers, using the compressed NX protocol. This is one I've tried out before, and it is probably the speediest of the lot, though I've never bothered benchmarking it.
- vino/vinagre: Another VNC client/server combination. I don't know how neatly this works. It's probably quite slick.
- teamviewer Very user-friendly. Uses a wine layer. Works and installs well, especially for Aunt Tilly (http://www.catb.org/jargon/html/A/Aunt-Tillie.html). Free for use for private individuals, but larger organizations have to pay. Uses a MITM server, so a cryptanalyst can't trust its security from that point of view.
- xrdp/rdesktop: RDP is the Remote Desktop Protocol from Microsoft, for doing things in a compressed VNC-like way. I don't know anything about RDP clients and servers. I'm just adding it here so that you know that they exist in Linux too, just in case the boss forces you to use RDP.
- x11vnc -ssl/ssvnc: I said earlier in this article that the details on securing x11vnc with the built-in options made my eyes glaze over. Inspired by a comment, I've added a note in the comment section on how to get started with that.
- xpra: (Gotta love the comments section for reminding me of this) xpra is like screen for X, which sounds totally cool. I haven't tried it yet myself but there is a debian package for it so it won't be long before I do.
My preference for the xv11vnc/xtightvncviewer way over openVPN was based on the parts being free (as in freedom and open standards), without MITM worries, familiarity, and fast enough on the cheapest broadband connection I could get. Addendum: sample xorg.conf file that handles xinerama over two video cards:
Section "ServerLayout" Identifier "X.org Configured" Screen 0 "Screen0" 0 0 Screen 1 "Screen1" RightOf "Screen0" InputDevice "Mouse0" "CorePointer" InputDevice "Keyboard0" "CoreKeyboard" EndSection Section "ServerFlags" Option "Xinerama" "True" EndSection Section "Files" ModulePath "/usr/lib/xorg/modules" FontPath "/usr/share/fonts/X11/misc" FontPath "/usr/share/fonts/X11/cyrillic" FontPath "/usr/share/fonts/X11/100dpi/:unscaled" FontPath "/usr/share/fonts/X11/75dpi/:unscaled" FontPath "/usr/share/fonts/X11/Type1" FontPath "/usr/share/fonts/X11/100dpi" FontPath "/usr/share/fonts/X11/75dpi" FontPath "/var/lib/defoma/x-ttcidfont-conf.d/dirs/TrueType" FontPath "built-ins" EndSection Section "Module" Load "extmod" Load "dri2" Load "dbe" Load "glx" Load "dri" Load "record" EndSection Section "InputDevice" Identifier "Keyboard0" Driver "kbd" EndSection Section "InputDevice" Identifier "Mouse0" Driver "mouse" Option "Protocol" "auto" Option "Device" "/dev/input/mice" Option "ZAxisMapping" "4 5 6 7" EndSection Section "Monitor" Identifier "Monitor0" VendorName "Monitor Vendor" ModelName "Monitor Model" EndSection Section "Monitor" Identifier "Monitor1" VendorName "Monitor Vendor" ModelName "Monitor Model" EndSection Section "Device" Identifier "Card0" Driver "tdfx" BusID "PCI:0:9:0" EndSection Section "Device" Identifier "Card1" Driver "nouveau" BusID "PCI:1:0:0" EndSection Section "Screen" Identifier "Screen0" Device "Card0" Monitor "Monitor0" SubSection "Display" Viewport 0 0 Depth 24 EndSubSection EndSection Section "Screen" Identifier "Screen1" Device "Card1" Monitor "Monitor1" SubSection "Display" Viewport 0 0 Depth 24 EndSubSection EndSection