Skip to main content

Simple Sunshine Autostart on Headless Linux

·1210 words·6 mins
Linux

Motivation
#

The combination of Sunshine and Moonlight provides means to remotely connect to a PC hosting the Sunshine server. This set of tools has been specifically engineered and optimized for the sake of low-latency and high-performance game streaming.

While a regular setup allows for streaming of one’s PC to devices in the home network, the addition of Tailscale (and, if desired, Headscale) facilitates streaming from any arbitrary internet-connected location in the world. This is especially useful if one has a powerful PC at home but is currently limited to a comparatively less powerful notebook.

However, a problem arises if the PC at home is powered off and no one is present to turn it on and log in with the user’s credentials. In the optimal case, it should be possible to achieve this from any remote location.

Unfortunately, this idea is hindered by the fact that on Linux, the Sunshine server only starts after entering the user’s credentials. Therefore, a methodology must be devised to sign in a user and start the Sunshine server before using it for its intended game streaming purposes.

Existing Approaches
#

Since the aforementioned problem is not unique, a plethora of users have faced similar issues before. The following discusses some approaches and why they are not suitable for my likings.

Autologin
#

The easiest way to achieve automatic startup of Sunshine after a headless Linux boot is to skip the password authentication and automatically log into the desktop environment.

Warning! This is insecure and not recommended!

To achieve this, an additional file /etc/systemd/system/getty@tty1.service.d/override.conf with the following content has to be created.

[Service]
ExecStart=
ExecStart=-/sbin/agetty --autologin <USERNAME HERE> --noclear %I $TERM
Type=idle

The biggest drawback of this method lies in its inherent security flaws. Removing password authentication allows unauthorized parties on-premise to access sensitive data and should therefore be avoided.

Separate Sunshine Login Session
#

The article I initially encountered (Autostart Sunshine on Boot without Auto-Login) proposes a solution with two separate consecutive Sunshine sessions. The first Sunshine session is initiated by an external SSH connection and is solely aimed at allowing the user to log in. The second Sunshine session then represents the regular game streaming session.

Multiple Sunshine sessions introduce complexity, which I did not want to tackle. This involves creating custom systemd units that replace the original Sunshine autostart unit. Additional polkit rules and the added systemd complexity led me to a search for more simplistic alternatives.

Concept
#

As in the “Autostart Sunshine on Boot without Auto-Login” article I initially considered, Wake-on-LAN is the easiest method to trigger the boot process of the Sunshine host system.

Attention: Make sure to enable the Wake-on-LAN capabilities in the host system BIOS

Then either Moonlight’s built-in Wake-on-LAN feature or an external tool can be used to power on the Sunshine host.

Subsequently, an SSH connection should be utilized to trigger the system login or directly trigger the desktop environment. This might either be achieved by remotely typing the password on the local TTY hosting the login manager or by directly spawning the desktop environment from the authenticated SSH session.

Realization
#

The following requires a working SSH server running on the Sunshine host system. In order to prevent unauthorized SSH access to this host machine, prefer SSH-key authentication and make sure the system is not exposed to the public internet.

To begin with, I explored the option to directly spawn the desktop environment from the SSH session. The Linux tool chvt allows to directly manipulate the host system’s TTY and might be used to exec the desktop manager.

sudo chvt -l -s -- bash -c "XDG_RUNTIME_DIR='/run/user/1000' exec Hyprland"

In the case of my current desktop environment — Hyprland, a Wayland compositor — I was not able to establish a usable Hyprland session via SSH. The former command leads to a Hyprland crash, which needs to be investigated in the future.

A simpler method is to emulate physical keyboard interaction and start the desktop environment using the login manager generally used to start a desktop environment in presence.

ReimuNotMoe/ydotool

Generic command-line automation tool (no X!)

C
1669
92

The Linux application ydotool, an alternative to the Xorg-specific xdotool, provides an easy means of remotely controlling keyboard input.

sequenceDiagram autonumber Client ->> Server: Wake-on-LAN request Note right of Server: Server boots to
login manager Client ->> Server: SSH Connection: Login using ydotool Note right of Server: Desktop environment
running Client ->>+ Server: Moonlight: Initialize Stream Server -->>- Client: Sunshine: Stream Desktop

In the case of my login manager, GNOME Display Manager (GDM), the following sequence of interactions is required to complete the login screen:

  1. Press the ENTER key
  2. Type the password
  3. Press the ENTER key once again to submit the password

This sequence is relatively easy to replicate with ydotool, after starting the required ydotoold daemon. After some experiments, additional time delays between keyboard interactions with sleep seemed to be required to allow the key presses to fully take effect.

1
2
3
4
5
6
7
8
#! /usr/bin/env bash
ydotoold& # start daemon
sleep 1
ydotool key 28:1 28:0 # press enter key
sleep 1
ydotool type -f password.txt # type out 
sleep 1
pkill ydotoold # kill daemon

Key codes such as for the ENTER key can be relatively easily generated by the ydotool composer.

The content of a text file, for instance, password.txt is fully typed out with a minor caveat. ydotool type currently does not respect keyboard layouts, which means one must either utilize ydtool key instead or already transform the to-be-typed text into the representation one would type on an English keyboard. For example, Foo-Bar on a German keyboard requires Foo/Bar as the input to ydotool.

This leaves the problem of plain-text passwords on the system hard drive. While there are many key stores and secrets managers, I use sops to keep secrets safe while exposing them to the system using secrets files.

getsops/sops

Simple and flexible tool for managing secrets

Go
18086
919

Using this application allows ydotool to access the system password in a file on a temporary RAM file system, such as /run/secrets/sunshine/login_pw_seq. This secret file then can be utilized in place of the aforementioned passwords.txt.

Discussion
#

Using the here proposed shell script, it is possible to directly start the Sunshine server after a single additional SSH interaction without any additional modification to the system. Its simplicity and speed are the biggest advantages compared to the presented alternative approaches.

However, there are still many problems with this method, which may or may not be solvable in the future.

The biggest flaw lies in its high speed caused by automation. The keyboard interactions via ydotool are not login manager agnostic and need to be customized. This might also cause future breakage in case the overall interaction procedure changes.

The lack of keyboard layout support by ydotool will most likely be fixed in the future, and currently does not represent a dramatic issue. Yet, the general usage of the here proposed approach is slightly detrimental in its usability.

Lastly, there exists the risk of plain-text passwords being stored on the hard drive, caused by the convenience for the user. Nevertheless, a security-minded user should be able to address this issue by applying key vaults, such as SOPS.