I have the peculiarity that I dislike laptops. While they are great for on the go, I still prefer a standard desktop tower machine for my regular work. However, as I’m traveling to my girlfriend each weekend, I won’t be able to work behind my tower. Next, being the chaotic person I am, I actually tend to forget to move files or sometimes I need functionality only available on my own machine. So I was investigating the most convenient way of reaching my machine remotely.

x11vnc

I’ve already worked with x11vnc in the past. Mostly to assist people remotely without requiring a closed-source propriety piece of software like TeamViewer. It works great and would help get into my desktop. I was already aware of security implications (VNC not being encrypted) so generally I was doing:

ssh dsonck@desktop -L5900:localhost:5900
x11vnc -localhost -nopw

And next VNC’ing in

vncviewer localhost:5900

This works, but still requires me to type in x11vnc.

systemd

While there is a lot of debate whether systemd is a good or bad thing. However, I personally appreciate all its features it provides. While I understand separation of concerns and realize that exploding systemd back into init, inetd, cron might improve stability, I don’t really experience that much issues from this complexity.

I recently learned about user services, so I thought of having that as basis to autostart x11vnc:

# /etc/systemd/user/xvnc.service
[Unit]
Description=XVNC Daemon

[Service]
ExecStart=/usr/bin/x11vnc -display :0 -bg -forever -shared

[Install]
WantedBy=multi-user.target

Next enabling it:

systemctl --user enable xvnc
systemctl --user start xvnc

This will remove the need to launch x11vnc .... But, it’s not ideal. Because now x11vnc will always run in the background.

systemd.socket

systemd again to the rescue (together with a feature from x11vnc). In the olden days, people used inetd or xinetd. It would allow one daemon which accepts connections and then spawn the respective program to handle the actual protocol. x11vnc supports that with its -inetd flag. Before starting (x)inetd, systemd already supports sockets on its own. So, by creating a new config file and slightly tweaking the existing one, we can turn it into an on-demand service:

# /etc/systemd/user/xvnc.socket
[Unit]
Description=XVNC Server

[Socket]
ListenStream=5900
Accept=yes

[Install]
WantedBy=sockets.target

Next we should rename xvnc.service to xvnc@.service as required by socket activation, and edit it:

# /etc/systemd/user/xvnc@.socket
[Unit]
Description=XVNC Per-Connection Daemon

[Service]
ExecStart=/usr/bin/x11vnc -display :0 -bg -forever -shared -inetd
StandardInput=socket
StandardError=syslog

Now, systemd will automatically spawn x11vnc when necessary, and bind the socket to its standard input and output. However, localhost may be used by anyone with access on this host. Now, for me this isn’t an issue as I’m the sole user, but as I like to reuse my stuff sometimes, I want to be on the safe side. Thankfully this is very easy.

Unix sockets and SSH

Many might not realize it (I didn’t) but since OpenSSH 6.7, SSH can forward unix domain sockets. This can even translate between regular TCP sockets. So it’s possible to forward local port 5900 to a remote unix-socket! So with a small adaptation:

# /etc/systemd/user/xvnc@.socket
[Unit]
Description=XVNC Server

[Socket]
ListenStream=%h/.xvnc
Accept=yes

[Install]
WantedBy=sockets.target

This will create a /home/dsonck/.xvnc socket (based on the username). This actually gives a few benefits:

  • Access of .xvnc can be limited to the owning user (only allow me to connect to my desktop
  • Multiple users each have their own private .xvnc file whereas the old solution hardcoded 5900 as port

It does require a slight adaption of the ssh command:

ssh dsonck@desktop -L5900:/home/dsonck/.xvnc

Which, to me, even looks cleaner: I want port 5900 to forward xvnc.

Conclusion

So, with the addition of two systemd configuration files in /etc/systemd/user, it’s possible to have x11vnc automatically launch when necessary. Together with unix domain sockets it’s possible to add permissions to this socket to limit others from interfering. And OpenSSH allows us to connect our local vncviewer via a port to this remote unix domain socket.