Preparing for a GUI
Out of the box, Alpine Linux runs with a Command Line Interface (CLI), which is suitable for containers and servers. On desktop systems, we usually prefer to have a Graphical User Interface (GUI), which allows for wallpaper, a file manager, windowed applications, and the usual things we expect on a desktop. In order to prepare an Alpine Linux system to switch from a CLI to a GUI, several steps are needed.
D-Bus
D-Bus is a system message bus that allows programs to communicate with one another. D-Bus is frequently used as an interface to system services, allowing desktop users to control networking and other hardware. There are two messages buses in use: the system bus and the session bus. The system bus allows system-wide services to listen for client connections and send events whenever hardware or system-level services change. We start the system bus at boot time with an OpenRC service.
In contrast, the session bus is started on a per-user basis whenever a user wants to run a desktop environment or other software that uses D-Bus to communicate between components. On Alpine Linux, the session bus is started by a graphical login manager whenever one is used. However, the session bus can also be started on the command line or in a script when using the regular console login.
While D-Bus has its criticisms, it provides a number of benefits without creating too many vulnerabilities. Its main fault is probably design by committee, as a result of which it can be cumbersome for developers to utilize. However, from the user and system administrator perspectives, it tends not to cause too many issues. The main concerns are that the system bus gets started at boot time and that the session bus is started before starting a desktop environment that relies on it.
To install D-Bus on Alpine Linux, run:
doas apk add dbus dbus-x11
Start the system bus and set it to start automatically by running:
doas rc-service dbus start
doas rc-update add dbus
udev
There are several system services that can monitor for hardware changes on the system and ensure that device nodes are created properly in the /dev file system. Out of the box, Alpine Linux uses BusyBox mdev to perform this task. While mdev is lightweight and is relatively simple to configure, most desktop environments require something with more features.
Historically, udev was a standalone service that was the Linux standard interface for managing device nodes. The udev project was absorbed by systemd. For distributions that do not use systemd, including Alpine, a separate eudev implementation is maintained as a standalone service.
Switching to eudev on Alpine Linux is extremely simple, thanks to an included script provided by the distribution:
doas setup-devd udev
Seat Management
You’re probably aware that Linux systems use Discretionary Access Controls (DAC) that determine what a user can do based on things like file permissions, file ownership, and whether or not the user is the root user. Some of the things we need to do on a desktop system involve directly talking to the hardware. For example, rendering your screen to an output display requires sending data to the graphics card. In the case of the Linux console (the command line interface), it’s pretty easy to send data to the graphics card without needing direct access to the video memory or low-level graphics components, since we’re just sending a stream of text. Things change when we want to use high performance 3D graphics.
Of course, we could just give desktop users direct access to the video card. We can do exactly that by adding a user to the “video” group on the system. On a home desktop computer with only one human user, this approach is fine, since that user probably also has the root password and is the system administrator. However, Linux systems are fundamentally designed to support multiple users. Think of a shared workstation in an office. One user might be sitting at the keyboard, while another user could be logged into the same system over a Secure Shell (SSH) or other kind of remote connection. It makes sense for the user sitting at the keyboard to be able to send stuff directly to the graphics card. However, the user logged in remotely isn’t looking directly at the monitor attached to the computer anyway, so why do they need that permission? If their account gets hacked remotely, we really don’t want to give an attacker direct access to the hardware.
The standard solution to this problem is to use a seat manager, which is a layer of software that allows users to access hardware devices based on conditional rules. Instead of trying to speak to hardware devices directly, software applications that can use a seat manager instead send their data to the seat manager, which uses rules to determine whether or not the user running that program can send data to the requested hardware. There are two main seat managers in existence: logind, which is – surprise, surprise – part of systemd, and seatd. Of the two, seatd has the smaller code size and the smaller attack surface. Unfortunately, the big desktop environments (GNOME and KDE) have chosen to rely on logind. As a consequence, some developers have split the logind code out of systemd and have created Elogind, which is what we have available in Alpine Linux.
Elogind uses a separate component, officially called polkit but still often called its original name PolicyKit, for determining the rules governing access by different users. Users who are sitting at the physical seat (the keyboard plugged directly into the system) typically get more access than users who are logged into the system remotely.
To install elogind and polkit, run:
doas apk add elogind polkit-elogind
After installation, start and enable the elogind and polkit services:
doas rc-service elogind start
doas rc-service polkit start
doas rc-update add elogind
doas rc-update add polkit
PAM
On our system, we log in using a user name and a password. The user name and user information is stored in the /etc/passwd file, which is world-readable, while the hash of the password goes in the /etc/shadow file and is readable only by the root user. Out of the box, Alpine Linux uses BusyBox to manage its login process, and this version of BusyBox simply hashes the password you type at the login prompt, compares the hash to the /etc/shadow file, and then lets you log in if they match. Technically, the way this process works is that your login information is sent to the /bin/login command, which runs as root and checks that your hashed password matches the stored hash. If it matches, then the login program forks, or creates a copy of itself. After forking, the copy changes its running privileges to your user account, performs a few other cleanup tasks, and replaces itself with (or execs) your login shell set in /etc/passwd.
This approach works quite well whenever user account information is stored locally in these two files. But, what happens if we want to have centralized user accounts using something like Microsoft Active Directory, which implements the Kerberos authentication mechanism and uses a Lightweight Directory Access Protocol (LDAP) server to get account information? Well, we have two choices. Either we need a login program that can also handle Kerberos+LDAP and fall back to the local files if the account isn’t found in the centralized server, or we need to add another software layer to do these sorts of operations for us.
Pluggable Authentication Modules (PAM) provide this layer. Instead of trying to figure out how to write a login program that can handle each and every different way of managing user accounts that someone might invent, PAM can centralize some of the common parts of this code and let plugins handle the details. PAM also provides mechanisms to set some security limits at login time, which makes it the favored approach when using seat management. PAM is optional on Alpine Linux, but we need to enable it on our system as part of our graphical desktop preparation.
Installing PAM is quite simple on Alpine Linux. We simply need to install the linux-pam package to get PAM itself and the util-linux-login package to replace BusyBox’s /bin/login with one that is designed to work with PAM:
doas apk add linux-pam util-linux-login
Our system will use PAM the next time it boots.
Sound
In the above section about seat management, I mentioned the video card is one of the devices that we need to access. The sound card is another one of these devices, and it’s a little bit trickier than the video card. Normally, we only have one process – either X11 or a Wayland compositor (which we’ll discuss in the next assignment) – that accesses the video card. However, multiple programs often want to play sound at the same time. At a low level, the sound card is just taking in a stream of bits and making noises based on that stream. If we throw a bunch of different bit streams at it, we would wind up with static if more than one program tries to play sound at once.
Thus, we first need to mix all our sounds together, which is the function of the mixer component. The Advanced Linux Sound Architecture (ALSA) system built into the Linux kernel has a mixer, but it has some serious limitations. Among these is that it is difficult to route audio to and from different devices. For example, if you’re using your desktop speakers but want to switch to a Bluetooth headset, changing the sound routing and mixing at the kernel level is quite tedious. In addition, keeping sound synchronized with video playback is yet another challenge.
For these reasons, we normally add a software layer to make managing our sound system a bit easier. Pipewire and its companion session and policy manager WirePlumber are the current recommendations. PipeWire replaces an older system called PulseAudio that was originally developed by the same person who created systemd. Although someone else took it over and rewrote a lot of the code (which was considered a mess at the time), it was still limited by original design decisions. PipeWire changed some of these decisions and added a compatibility layer for applications that expect a PulseAudio interface.
Install pipewire, its wireplumber session manager, and some useful compatibility interfaces by running:
doas apk add pipewire wireplumber pipewire-pulse pipewire-jack pipewire-alsa
Bluetooth
You probably have one or more Bluetooth devices lying around, and most systems today have Bluetooth adapters. Bluetooth is actually a stack of network protocols for wireless communication between devices, with a focus on Personal Area Networks (PANs), or short-range communication. On Linux, these protocols are split between kernel drivers and some userspace components provided by the BlueZ layer. We need to install this layer to be able to connect Bluetooth devices to our system.
doas apk add bluez bluez-openrc pipewire-spa-bluez
doas rc-service bluetooth start
doas rc-update add bluetooth