VNC connection

Introduction

Virtual Network Computing (VNC) is a graphical desktop sharing system that uses the Remote Frame Buffer protocol (RFB) to remotely control another computer. VNC is platform independent – there are clients and servers for many GUI-based operating systems. There are a number of branches of VNC which offer their particular functionality and improvements. Many, including RealVNC, VNC tight, UltraVNC, x11vnc, and TigerVNC are still full backwards compatible, at least as far as their basic remote control functionality is concerned.

Here, TigerVNC is used because it is included on the RHEL7 DVD and uses the systemd system management daemon for its configuration. TigerVNC uses Xvnc to create virtual displays (X desktops) to run separate parallel sessions enabling X-11 access to headless servers. (For VNC connection to real X11 displays, x0vncserver or x11vnc could be used).

Installation

A VNC connection exists of a client and a server part. For installation of the VNC server software, use the following command:

# yum install tigervnc-server

To install the VNC client software, use:

# yum install tigervnc

Configuration

There are different approaches for the VNC server configuration. The basic approach is to start a fixed number of VNC servers. Each server is given its own (unique) display number. Clients can connect to a specific server through this display number. The major drawback is the clients can’t see which VNC server is already in use.

With the on-demand-activation approach, a VNC server is started for each connecting client and stopped on disconnect. Each client connects to a standard port which results in a connection to a newly started VNC server. As all clients are independent of each other, no specific server can be selected, it is not possible to resume a previous stopped session.

Fixed display configuration

The basic configuration starts by copying a default systemd service file:

# cp /usr/lib/systemd/system/vncserver@.service /etc/systemd/system/

This vncserver service unit file contains the command to start a VNC server:

ExecStart=/usr/sbin/runuser -l <USER> -c "/usr/bin/vncserver %i"

The command vncserver is a script which simplifies the process of starting an Xvnc server. It runs Xvnc with appropriate options and starts a window manager on the VNC desktop. It has a few options of its own, but also accepts all Xvnc parameters. Parameters can be turned on with -param or off with -param=0. Some of the options are:

  • -SecurityTypes None
    Release the client from entering the users password (as set with vncpasswd).
  • -SendCutText=0 -AcceptCutText=0
    Disable copy-paste from/to the clipboard.
  • -NeverShared -DisconnectClients=0
    Prevent a client from viewing the same window as another client or taking it over.
  • -localhost
    Only allow connections from the same machine. This is useful when using SSH and stop non-SSH connections from any other hosts.

Edit the file. Replace <USER> with the actual user name. For this user, a .vnc directory with a (plain text) password file has to be created:

# su - <user>
$ vncpasswd

To make the changes in the systemd service file take effect immediately, enable the service so it started at boot and to start it right now:

# systemctl daemon-reload
# systemctl enable vncserver@:5.service
# systemctl start vncserver@:5.service

This starts a server for display number 5. Multiple servers can be started by using other (unique) display numbers. They all can use the same service file because systemd automatically creates a appropriately named instance in memory on demand, replacing ‘%i’ in the service file by the display number.

When vncserver is run the file ~<USER>/.vnc/xstartup is, if it does not exist, created. By default it executes /etc/X11/xinit/xinitrc. It can be edited, for example to start a gnome-terminal and /usr/bin/startkde instead of xinitrc. This will open the desktop of user <USER>. In the same ~<USER>/.vnc/ directory, a log file (one per server) is created. As the user is specified in the service file, all servers that use the file use the same user. To use more than one user, create multiple service files.

A client can connect to the server with:

# vncviewer <hostname>:<display-number>

Instead of the display number, the port number of the corresponding VNC server (5905) can be used. Note that the display number determines the VNC server connected to, and thereby the user that is used.

On demand activation

The disadvantage of the fixed display configuration (as described in the previous section) is that it requires the client to specify a display number. If the display is already in use, it depends on the server settings what will happen (refuse, share or take over). Another disadvantage is that a vncserver uses a specific user. With the on demand activation approach, a VNC server is started for each connecting client without using a specific user account.

Starting the vncserver on demand looks like functionality provided by inetd (internet service daemon). The problem with inetd is that it has security limitations which would require a waver in OpenSCAP. Therefore systemd socket activation is used instead. This starts by creating the file /etc/systemd/system/vnc-server.socket:

[Unit]
Description=VNC server for Per-Connection Servers
[Socket]
ListenStream=127.0.0.1:5901
Accept=true
MaxConnections=10
[Install]
WantedBy=graphical.target

The address to listen on for a stream is limited to port 5901 on localhost via IPv4. This is needed to allow SSH only. (To release this constraint, use “0.0.0.0:5901”. For IPv6 use “[::1]:5901” or “5901” and check the value of BindIPv6Only=).

The “Accept=true” corresponds to inetd nowait. This configures the service for the “On-demand socket activation for per-connection service instances” scheme. The inetd wait (Accept=false) is for singleton services.

The socket service is started with:

# systemctl daemon-reload
# systemctl enable vnc-server.socket
# systemctl start vnc-server.socket

The status can be checked with:

# systemctl status vnc-server.socket
# netstat -anpt | grep 590

For the on demand activation two systemd unit files are needed. The socket file is described above, the matching service file is /etc/systemd/system/vnc-server@.service:

[Unit]
Description=Remote desktop service (VNC)
After=syslog.target network.target
[Service]
Type=simple
ExecStart=/usr/bin/Xvnc -inetd -query localhost -once securitytypes=none -SendCutText=0 -AcceptCutText=0 -IdleTimeout=600
StandardInput=socket
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=graphical.target

The “Type=simple” indicates that the process configured with “ExecStart=” is the main process of the service. Communication channels should be installed before the daemon is started up (e.g. sockets set up by systemd, via socket activation).

The argument “-inetd” changes Xvnc’s behaviour so that it can be launched from inetd. “-query localhost” tells the VNC X server to check with localhost for XDMCP authentication and “-once” that the server should terminate after one session.

The purpose of the X Display Manager Control Protocol (XDMCP) is to provide a uniform mechanism for an autonomous display to request login service from a remote host. The Gnome Display Manager (GDM) support XDMCP, but to use it modify /etc/gdm/custom.conf to include:

[xdmcp]
Enable=true

Then restart the GDM:

# systemctl restart gdm

With the above configuration, up to 10 local VNC clients can connect through port 5901. To connect from a remote node, an SSH tunnel must be created.

SSH connection

VNC is an unencrypted protocol. To create a secure connection between a local computer and a remote machine through which VNC can be relayed, port forwarding via SSH (SSH tunneling) is very useful. To make a connection from the local computer to another server, local port forwarding is to be used. The synopsis is:

ssh [-Nf] -L <local-port>:<remote-host>:<remote-port> -l <user> <hostname>
ParameterDescription
-NDo not execute a remote command. This is useful for just forwarding ports. By default, a login shell is executed.
-fRequests ssh to go to background just before command execution
-L <local-port>:<remote-host>:<remote-port>
Specifies that the given port on the local (client) host (i.e. <local-port>) is to be forwarded to the given host and port on the remote side (i.e. : <remote-host>:<remote-port>). Note that <remote-host> is relative to the host ssh connects and logs into, i.e. <hostname>. If they are the same, <remote-host> can be “localhost”.
-l <user>
Specifies the user to log in as on the remote machine.

Creating a SSH tunnel and starting a VNC viewer could look like this:

$ ssh -Nf -L 5901:localhost:5901 -l <user> <server>
$ vncviewer localhost:5901

The local and remote port don’t have to be the same. Remote-port 5901 maps to display number 1 in a fixed display configuration or to the port the socket service is listening to in on demand configuration. The local port (the first port) is the one the local vncviewer should connect to. Once the tunnel is opened, any user can use the tunnel. It is not limited to the user who created the tunnel or a user with privileges. Any user can start vncviewer using the tunnel.

Note: to prevent direct remote access to the VNC server (so ssh must be used), the VNC server should only accept local connections.

TigerVNC has a specific option to automatically create an encrypted TCP tunnel. With this option, a client can connect to a VNC server with:

$ vncviewer localhost:1 -via <server>

Note that the current user is used to setup the ssh connection. To use another user for the ssh connection, needed in case the current user is not known at the VNC server, set the environment variable VNC_VIA_CMD:

$ export VNC_VIA_CMD='/usr/bin/ssh -f -L "$L":"$H":"$R" -l <user> "$G"'

Idle timeout

For secure VNC connections, there are actually two connections: the SSH connection and the VNC connection. The VNC connection idle timeout can set with the “-IdleTimeout” option of Xvnc. The default is 0, which means that idle connections will never be dropped. To have the VNC connection be dropped after 600 seconds, edit the file /etc/systemd/system/vnc-server@.service and add “-IdleTimeout=600”:

ExecStart=/usr/bin/Xvnc -inetd -query localhost -once securitytypes=none -SendCutText=0 -AcceptCutText=0 -IdleTimeout=600

The new timeout is immediately effective for every new VNC connection.

As long as at least one VNC client is connected, the SSH connection is not idle. Once the SSH connection is idle, two timeout settings apply to the SSH connection: one at the client side and one at the server side. On both sides, an alive interval and an alive count max can be specified. The interval sets a timeout interval in seconds after which, if no data has been received, an alive message will be send. Note that an alive message resets the idle time on both sides. If the alive interval is 0, no alive messages will be send and this side will never drop the connection.

The alive count max sets the number of alive messages which may be sent without receiving any messages back, before the connection is dropped. If set to 0, no alive message is send and this side will drop the connection after interval time. To summarize:

AliveInterval=0, AliveCountMax>0 --> Never disconnect
AliveInterval>0, AliveCountMax=0 --> Disconnect if idle for Interval time
AliveInterval>0, AliveCountMax>0 --> Disconnect if no response for Interval*CountMax time

The server side timeout is set in the file /etc/ssh/sshd_config. To have it drop a client connection after being idle for 600 seconds it should contain:

ClientAliveInterval 600
ClientAliveCountMax 0

This are the default values. When the values are changes, the SSH daemon needs to be restarted:

# systemctl restart sshd

On the client side, the idle timeout can be set as options to the ssh command:

$ ssh -Nf -L 5901:localhost:5901 -o ServerAliveInterval=60 -o ServerAliveCountMax=0 -l <user> <server>

In this case, the ssh command will drop the connection after being idle for 60 seconds. If ServerAliveInterval was set to 900, the connection would be dropped by the server after 600 seconds. The default value of ServerAliveInterval is 0, indicating that the client will never disconnect. The client can force the connection to be open by sending periodic alive messages:

$ ssh -Nf -L 5901:localhost:5901 -o ServerAliveInterval=60 -o ServerAliveCountMax=3 -l <user><server>

Instead of passing the options to the ssh command, a user can also specify the options (per node) in ~/.ssh/config.

Logging

Log information on the systemd socket activation can be retrieved with:

# journalctl -u vnc-server.socket

It reports information on Listening, Starting, Stopping and Close of the socket activation as well as exceeding the maximum number of concurrent connections (the exceeding can also be viewed in /var/log/messages). It does not say anything about VNC client connections.

The file /var/log/secure contains information about:

  • open SSH tunnel
  • vncviewer XDMCP connect
  • vncviewer login (both failed and successful)
  • vncviewer logout
  • close SSH tunnel

References