June 6, 2019

VNC - an introduction

I originally wrote this when I was running Fedora 24. Now I am on Fedora 30, but not much seems to have changed.

VNC (Virtual Network Computing) is a way to control a desktop on one computer from another one. It is platform independent (as long as someone has written the server and clients needed for the hosts involved). It is also amazingly efficient for long and slow links. It is the next best thing to being there.

The first thing is to install the necessary packages:

dnf install tigervnc
dnf install tigervnc-server

VNC password

Another d***d password! VNC has its own password, which is always an infernal nuisance since I use VNC so infrequently that I never remember what it is. The password gets stored on the server side (naturally) in the file ~/.vnc/passwd. You get to set it using a command called vncpasswd. So if you forget the password, just set it using vncpasswd. This overwrites any prior password and gets the job done.

X11 port forwarding as an alternative

I used to avoid using VNC because I could get by using port forwarding for X11 based GUI's that I wanted to run or debug. Port forwarding for X like this is wonderful, if it works for you.

I was forced to abandon X11 port forwarding when we clamped down our firewall rules so that the only open port was the ssh port, requiring even VNC to be tunneled over ssh.

Note that X11 port forwarding requires ports 5901 through 5909 to be open.

Also (although this was never much of a problem for me) X11 port forwarding doesn't work well over slow links where VNC will work just fine.

VNC over an SSH tunnel

What I do now is to tunnel VNC over an ssh connection. This offers the additional benefit of encryption, which I don't usually care about, which may be important to some people.

The VNC server

This is the easy part. Somehow (probably using ssh) log in to the remote machine as yourself (not root) and start up a VNC server. This is typically done using the command:
vncserver
This starts an instance of Xvnc on the next available desktop (usually :1). It will tell you which desktop it decided to use. This will stay running forever (i.e. until you explicitly kill it, or you reboot the machine.

To get rid of that server, issue the following command on the machine hosting the server:

vncserver -kill :1
The first time ever that you do this, it will prompt you to set a password. It encrypts that password and places it in ~/.vnc/passwd. When you forget that password, you can either just delete that file and start the whole process over, or reset it using "vncpasswd".

The VNC viewer

In the simple case where you are on the same LAN and don't have firewalls getting in your way, you just type:

vncviewer remote:1
and a small GUI should appear asking for your password. After you enter it you have the session you want.

Life has never been that easy for me, but making the vncviewer set up forwarding to use an SSH tunnel is so common that doing so is built into the vncviwer program. You just type this instead:

vncviewer -via cholla cholla:1

Tricky SSH tunneling

All kinds of weird setups can be arranged to use ssh tunneling. For example you can set up a tunnel to a middle man machine and hop from there to the machine that runs the vncserver via:
vncviewer -via middle_host final_host:1
Why would you want to do this? Who knows! I have a vague memory of finding it useful once when I couldn't get a simpler approach to work. Note that both hosts must be on the same network and free to use whatever ports they want for this to work.

Setting up an SSH tunnel by hand

Here is what the documentation (for vncviewer) says that the "-via" option to vncviewer does:
Automatically create encrypted TCP tunnel to the gateway machine before connection, connect to the host through that tunnel (TigerVNC-specific). By default, this option invokes SSH local port forwarding, assuming that SSH client binary can be accessed as /usr/bin/ssh. Note that when using the -via option, the host machine name should be specified as known to the gateway machine, e.g. "localhost" denotes the gateway, not the machine where vncviewer was launched. The environment variable VNC_VIA_CMD can override the default tunnel command of /usr/bin/ssh -f -L "$L":"$H":"$R" "$G" sleep 20. The tunnel command is executed with the environment variables L, H, R, and G taken the values of the local port number, the remote host, the port number on the remote host, and the gateway machine respectively.
All of this is clear as mud, but using ps -alx on a machine where this is actually set up and working shows the following processes running.
vncviewer -via cholla cholla:1
/usr/bin/ssh -f -L 38151:cholla:5901 cholla sleep 20
So, in the above, we have: The description of local port forwarding (in the SSH document) is:
-L local_socket:host:hostport

Connections to the given TCP port on the local (client) host are to be forwarded to the given host and port, on the remote side. This works by allocating a socket to listen to a TCP port on the local side, optionally bound to the specified bind_address. Whenever a connection is made to the local port, the connection is forwarded over the secure channel, and a connection is made to host port hostport, from the remote machine.

So, what ssh is doing is to listen on TCP port 38151. When a connection is made, it will contact the ssh server on the gateway machine. That ssh server will then make a TCP connection to port 5901 on the remote host. Port 5901 is the port a VNC server always starts up listening on, so there is no magic there. Port 38151 is agreed upon between vncviewer and the ssh process it started up to do all this.

All this starts looking simple enough. We could start an ssh server by hand with all of these options. The question then is how to tell vncviewer to talk to port 38151, or whatever port we decide to use. As near as I can tell, this is done in the following way. Note the double colons.

vncviewer localhost::38151
In fact I created the following two line script, and it worked just fine:
ssh -f -L 38199:cholla:5901 cholla sleep 20
vncviewer localhost::38199
And the following also works. In some ways this is the clearest because "localhost:5901" gets sent to the remote machine (the "gateway") telling him who to connect to. In this case it is a process on himself.
ssh -f -L 38999:localhost:5901 cholla sleep 5
vncviewer localhost::38999
Then for the sake of experimentation, I ran an ssh server on port 4228. This then worked fine (but watch out for firewalls blocking port 4228).
ssh -p 4228 -f -L 38999:localhost:5901 trona sleep 5
vncviewer localhost::38999
Then, to get to a machine behind a port forwarding firewall with some odd ssh issues, this worked fine. Note that I am using Tiger-vnc on the server side. Not that that should matter since I am doing the tricky stuff with ssh.
ssh -oKexAlgorithms=+diffie-hellman-group1-sha1 -p 4228 -f -L 38999:localhost:5901 pickle.mit.edu sleep 20
vncviewer localhost::38999

The VNC session

Who knows what window manager and setup you will be presented with when the VNC session gets going. The easiest thing is to just deal with whatever fate tosses your way. If you make a career out of working on remote machines using VNC (I don't), you may want to take some control over what session gets started. This section may give you some starting points to get that worked out.

The nature of the session you get is controlled by the file ~/.vnc/xstartup. In particular you can fiddle with this file to select what kind of desktop your vnc session will run. By default the VNC session will have limited resolution and a small set of colors. This may serve you well by being efficient, but can also be a real pain. Often the default window manager is something from the stone age, which again may serve you well (or not) if your link is slow. On my systems, the default is a 1024x768 session with 8 bit color and running the twm window manage.

If you have plenty of bandwidth, go ahead and run gnome in 24 bit color. On my 1680x1050 display, I like what I get when I launch the server via:

vncserver -depth 15 -geometry 1650x1020
Modify the xstartup file to end like so:
exec gnome-session &
In lieu of starting some window manager.

Fedora blank screen bug

I haven't seen this for a long time. I don't know if Fedora fixed this, or if the hack below is still in place and masking the problem, but here is more than you want to know if this crops up.

Running a vncserver on one Fedora machine and then using vncviewer on another Fedora machine used to simply yield an unresponsive black screen. This was apparently a widespread problem:

I tried a variety of things, finally editing the file ~/.vnc/xstartup file. It looks like the following solved the problem:
unset SESSION_MANAGER
unset DBUS_SESSION_BUS_ADDRESS
#exec /etc/X11/xinit/xinitrc
#exec /usr/bin/gnome-session
exec /usr/bin/mate-session
Note that the regular desktop on that machine ran "mate" (for better or worse) at that time. It may be important for this to match the default desktop selection.

VNC and Fedora installs

Believe it or not, it is possible to do remote an unattended fedora installs via VNC, here are some articles on this:

VNC and your X server

It is possible to do some cool things by fiddling with /etc/X11/xorg.conf. This requires the (yum available) package vnc-server, then to start this fun, add this to the Module section:
Load "vnc"
After doing this, it is possible to remote the :0 display via VNC.
In the screen section, do this:
Option      "passwordFile" "/local/admin/.vnc/passwd"
Option      "rfbport" "5999"

Feedback? Questions? Drop me a line!

Adventures in computing / tom@mmto.org