2023-10-19 - Remote Tech Support

(updated 2024-02-23)

I occasionally need to do remote tech support for people I know. Of
course, there are commercial programs like TeamViewer that allow easy
remote desktop access, but I wanted a free software alternative. There
seem to be a lot of remote desktop programs, but most of them never
really worked properly for me. In the end, I settled on a VNC server
together with two SSH tunnels. Two SSH tunnels are necessary because
generally, both computers will be behind firewalls that don't allow
direct access, so the connection needs to be routed through a separate
server. This would probably also be possible with something like
WireGuard, but I've never used that, so I just stuck with SSH for now.

Terminology: In this guide, local will refer to my own computer, remote
will refer to the computer of the person I'm doing tech support for,
and server will refer to the server I tunnel the connection through.

First, an unprivileged user account needs to be created on the server
for the person you're doing tech support for. This user does not need
to have a real shell set. On OpenBSD, I set the shell to /sbin/nologin
to lock down the account a bit. On other systems, this may be located
at /usr/sbin/nologin. The SSH configuration can also be locked down for
this user by adding the following section to the end of
/etc/ssh/sshd_config (where the user account is called 'unprivileged'):

Match User unprivileged
 PasswordAuthentication no
 AllowTcpForwarding yes
 X11Forwarding no
 PermitTTY no
 AllowAgentForwarding no
 PermitOpen localhost:5900
 ForceCommand echo nologin

5900 is the port that will be used by the VNC server, so we only need
to allow that for the port forwarding. I'm not sure if there are still
other things that could be done to lock the account down.

Next, the remote computer needs to be prepared. I used TigerVNC[0], but
a different VNC server could also be used. The nice thing about
TigerVNC is that it has a mode for sharing the current X session
instead of creating a new one, so it supports true screen sharing where
both people see the same screen and can control it at the same time.
Apparently, x11vnc also supports this, but I haven't tried it yet.  On
OpenBSD, the package 'tigervnc' can be installed, on Debian-based
systems, 'tigervnc-scraping-server' needs to be installed. Note that
the VNC commands may be slightly different depending on the system
you're using. Debian-based systems usually use update-alternatives to
link generic commands like 'vncviewer' to a command from a specific
package.

UPDATE (2024-02-23): I found that on some Debian-based systems, it is
also necessary to install tigervnc-common to get the
'vncpasswd' (or 'tigervncpasswd', depending on the system) command.

It is also a good idea to install autossh to make sure the ssh session
doesn't die. To make setting up the SSH tunnel convenient, create a
password-less SSH key with ssh-keygen and add the public key to
~/.ssh/authorized_keys in the unprivileged user account on the server.
Since the server is locked down pretty well, this *probably* shouldn't
cause any major security issues.

Before a VNC server can be run, a password needs to be configured. To
do this, simply run 'vncpasswd' and give a password (it also asks if
you want to create a view-only password, but you don't need that). 
When all this is done the following script can be created:

#!/bin/sh
x0vncserver -localhost yes -rfbauth ~/.vnc/passwd
autossh -Nf -R 5900:localhost:5900 unprivileged@server.name
read tmp
x0vncserver -kill :*
pkill -x autossh

UPDATE (2024-02-23): The script originally said 'pkill -x ssh', which
only kills the actual ssh client, which is then restarted by autossh.

x0vncserver is the special VNC server that shares the current X session
instead of creating a new one. This should usually run on port 5900
(5900 + number of current X display, which normally is 0). If this is
different for some reason, the port will need to be changed in all
other commands that use it. If '-localhost yes' is given, x0vncserver
only accepts connections from localhost, so other computers should only
be able to connect through the SSH tunnel, although there have
apparently been some exploits that spoofed packets from localhost in
the past[1]. This is actually the default behavior, but I guess it
doesn't hurt to specify it explicitly. I suppose there also isn't
really any added security benefit by using the default VNC
authentication when the connection is tunneled through SSH anyways, so
it might make more sense to just add '-SecurityTypes none' and use it
without a password.

When the VNC server has started, an SSH reverse tunnel to the server is
created, where 'unprivileged' is the unprivileged user account, and
'server.name' is the address of the server. The line with 'read' is
just there to block execution until enter is pressed, after which
everything is killed again. I'm not sure if there's a better way to
kill ssh than 'pkill -x ssh'.  Maybe it would be possible to remove the
'-f' flag, use & to start the process in the background, and then use
$! to get the exact pid of the process, but that seems somewhat ugly as
well.

Most desktop environments should allow you to create some sort of
launcher for running a script in a terminal. Alternatively, another
script could be created that opens a terminal and runs the script. With
either one of these options, the user just has to run that
launcher/script, and then wait for the tech support person to connect
to the VNC server.

Make sure to run the script at least once while setting it up,
otherwise the user will have to accept the public key of the server
when the SSH tunnel is set up the first time.  Also, if the public key
of the server changes for some reason, you will need to find a way to
update ~/.known_hosts on the remote computer so the user doesn't get a
scary warning when trying to run the script.

That brings us to our last step, setting up the local computer. This is
really the easiest part. Once the script on the remote computer is
running, another tunnel must be created from the local computer to the
server:

autossh -NL 5900:localhost:5900 user@server.name

In this case, 'user' is whatever user account you use to log in to the
server.  Then, you can connect to the VNC server with the following
command (on OpenBSD, this command is part of the package 'tigervnc', on
Debian-based systems, it is included in 'tigervnc-viewer'):

vncviewer localhost:0

When this asks for a password, the password that was given to
'vncpasswd' on the remote computer needs to be entered. After that, you
should see the screen of the remote computer. To stop the VNC server,
the user of the remote computer just needs to press enter in the
terminal window running the script (this can even be done by the
tech support person since the connection is only terminated once enter
has been pressed).

Feel free to contact me if you have any better suggestions or think
that there are glaring security problems with this setup.

[0] https://tigervnc.org
[1] https://serverfault.com/questions/411658/can-localhost-be-spoofed/752076#752076