Display reboot required message on Debian

You can use MOTD (message of the day) to let you know if a Debian server requires reboot and why upon login.

Create a new file named /etc/update-motd.d/98-reboot-required and add to it the following lines:

#!/bin/sh -e
#
# helper for update-motd

if [ -f /var/run/reboot-required ]; then
	echo "*** System restart required ***"
        cat /var/run/reboot-required.pkgs
fi

Make the file executable:

$ sudo chmod +x /etc/update-motd.d/98-reboot-required

Now, you can test the new MOTD script using:

$ run-parts --lsbsysinit /etc/update-motd.d

If you have any installed updates that require reboot, you will get a message stating so, with a list of the packages that require the reboot.

*** System restart required ***
linux-image-5.10.0-19-cloud-amd64

Signing kernel modules for Secure Boot

Some time ago, I needed to use the v4l2loopback module. It can be installed via:

$ sudo apt install v4l2loopback-dkms

Normally, after installing a module, you can just modprobe it, and it will load. However, due to Secure Boot, it will fail.

$ sudo modprobe v4l2loopback 
modprobe: ERROR: could not insert 'v4l2loopback': Operation not permitted

The problem is that the v4l2loopback isn’t signed. For example, compare the output of:

$ /usr/sbin/modinfo -F signer v4l2loopback

which is empty, versus

$ /usr/sbin/modinfo -F signer xor
Debian Secure Boot CA

The solution would be to sign the v4l2loopback module ourselves.

Creating a key

The update-secureboot-policy script available in Ubuntu’s shim-signed package is able to generate Machine Owner Keys (MOK) by itself. However, the currently available in Debian Unstable doesn’t have the key generation functionality. We can either fetch the Ubuntu version or generate the keys ourselves.

$ wget https://git.launchpad.net/ubuntu/+source/shim-signed/plain/update-secureboot-policy
$ chmod +x ./update-secureboot-policy
$ sudo ./update-secureboot-policy --new-key

Or through generating the keys ourselves:

$ sudo mkdir -p /var/lib/shim-signed/mok
$ cd /var/lib/shim-signed/mok/
$ sudo openssl genrsa -aes256 -out MOK.priv
$ sudo openssl req \
        -subj "/CN=`hostname -s | cut -b1-31` Secure Boot Module Signature key" \
        -new -x509 -nodes -days 36500 -outform DER \
        -key MOK.priv \
        -out MOK.der

Write down the passphrase for your private key. You will need it whenever you want to sign drivers.

Now we enroll the newly created key:

$ sudo mokutil --import MOK.der

You will be prompted for a password. This password will be required after reboot in order to complete the key enrollment, you will not need it afterwards.

After reboot, check that your key was indeed enrolled:

$ mokutil --list-enrolled

Signing the module

We need to put the passphrase for the private key in the KBUILD_SIGN_PIN env variable:

$ read -s KBUILD_SIGN_PIN
$ export KBUILD_SIGN_PIN

Now we can do the actual signing:

$ cd /usr/lib/modules/$(uname -r)/updates/dkms
$ sudo --preserve-env=KBUILD_SIGN_PIN /usr/lib/linux-kbuild-$(uname -r | cut -d. -f1-2)/scripts/sign-file sha256 /var/lib/shim-signed/mok/MOK{.priv,.der} v4l2loopback.ko

You will need to repeat this step for every new kernel that you install.

Rewriting EXIF tags in JPEGs

Some out-of-camera JPEGs have bad metadata that digiKam doesn’t parse correctly. The problematic photos have the same characteristics:

  1. In the Properties tab, there is no Photograph Properties section, instead digiKam has a digiKam Properties section with the caption set to None.
  2. In the Metadata tab, not metadata is shown under Exif. However, full details are available under Exiftool.

The solution is to rewrite the EXIF tags using exiftool. This fixes the bad metadata and allows digiKam to properly read the photo’s metadata.

exiftool -overwrite_original -all= -tagsfromfile @ -all:all *.JPG

For the files with bad EXIF metadata, the command will report the following warning:

Warning: [minor] Error reading PreviewImage from file - DSC06635.JPG

Setting up WireGuard on Debian

WireGuard is a modern VPN solution, which is much easier to configure than OpenVPN and its likes. In this tutorial, we assume a simple setup where we want to route all clients network traffic through the VPN, exiting through the server.

Server configuration

$ sudo apt install wireguard
$ PRIVATE_KEY=$(wg genkey)

Now we create the configuration file for our tunnel (wg0).

$ cat <<EOF | sudo tee /etc/wireguard/wg0.conf
[Interface]
PrivateKey = $PRIVATE_KEY
Address = 10.8.0.1/24, fd0d:d325:45c0::1/64
ListenPort = 51820
SaveConfig = true

PostUp = iptables -t nat -I POSTROUTING -o eth0 -j MASQUERADE
PostUp = ip6tables -t nat -I POSTROUTING -o eth0 -j MASQUERADE
PreDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
PreDown = ip6tables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
EOF

If you have a firewall, you’ll need to open up UDP port 51820 (or whatever configured as the ListenPort).

Enable IPv4 and IPv6 forwarding, so we can route all the client traffic through the server (and reload sysctl configuration)

$ echo net.ipv4.ip_forward=1 | sudo tee /etc/sysctl.d/40-wireguard.conf
$ echo net.ipv6.conf.all.forwarding=1 | sudo tee -a /etc/sysctl.d/40-wireguard.conf
$ sudo sysctl --system 

Enable and start the WireGuard service:

$ sudo systemctl enable --now wg-quick@wg0.service

Finally, take note of the server public key (it will be a short base64 encoded string):

$ echo $PRIVATE_KEY | wg pubkey

You’ll need it in the next step.

Peer configuration

$ sudo apt install wireguard
$ PRIVATE_KEY=$(wg genkey)
$ PUBLIC_KEY=$(echo $PRIVATE_KEY | wg pubkey)
$ SERVER_PUBLIC_KEY=6TDw+U2WFhkaKUy/xXrCRtuZvB2m2SFN7URZA5AkGis=
$ SERVER_IP=8.8.8.8

Replace the value of SERVER_PUBLIC_KEY with the public key of your server, and SERVER_IP with the correct IP address of your server.

Edit /etc/wireguard/wg0.conf

$ cat <<EOF | sudo tee /etc/wireguard/wg0.conf
[Interface]
PrivateKey = $PRIVATE_KEY
Address = 10.8.0.2/24, fd0d:d325:45c0::2/64

[Peer]
PublicKey = $SERVER_PUBLIC_KEY
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = $SERVER_IP:51820
EOF

Setting AllowedIPs = 0.0.0.0/0, ::/0 will route all traffic through the VPN connection. If you don’t want to do that, edit the configuration file, and set AllowedIPs = 10.8.0.0/24, fd0d:d325:45c0::0/64.

We need to make the server aware of the peer. The following command should be executed on the server.

$ sudo wg set wg0 peer $PUBLIC_KEY allowed-ips 10.8.0.2,fd0d:d325:45c0::2

where PUBLIC_KEY is the value of the client’s public key (stored in the $PUBLIC_KEY environment variable).

The /etc/wireguard/wg0.conf will be updated according and make the added peer configuration persistent due to the SaveConfig = true. The configuration update will take place the next time the WireGuard interface goes down.

You can also import the profile into NetworkManager and easily enable/disable the new interface via GNOME:

$ sudo nmcli connection import type wireguard file /etc/wireguard/wg0.conf

Android Peer

It is also useful to have WireGuard on the phone. WireGuard supports both iOS and Android, and the setup should be similar in both cases. Start by installing WireGuard from the Play Store. The next step is to generate the required configuration. It can be done directly on the phone, or by creating a configuration file on your computer and transferring it, which I find simpler.

$ PRIVATE_KEY=$(wg genkey)
$ PUBLIC_KEY=$(echo $PRIVATE_KEY | wg pubkey)
$ SERVER_PUBLIC_KEY=6TDw+U2WFhkaKUy/xXrCRtuZvB2m2SFN7URZA5AkGis=
$ SERVER_IP=8.8.8.8
$ cat <<EOF > wg0.conf
[Interface]
PrivateKey = $PRIVATE_KEY
Address = 10.8.0.3/24, fd0d:d325:45c0::3/64

[Peer]
PublicKey = $SERVER_PUBLIC_KEY
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = $SERVER_IP:51820
EOF

Again, make the server aware of the client by running the following command on the server:

$ sudo wg set wg0 peer $PUBLIC_KEY allowed-ips 10.8.0.3,fd0d:d325:45c0::3

Transfer the configuration file wg0.conf to the phone and load it using the WireGuard app.

Update 2023-11-30: Added IPv6 configuration.

Connecting to WP2 Enterprise network with EAP-TLS authentication

Recently, I had to connect to a hidden WiFi network with an EAP-TLS authentication. When configured via the NetworkManager UI on Ubuntu, it would work. However, on Debian Unstable running Gnome 42 and on Arch, the same process didn’t work. The problem seems to be an empty configuration line for domain-suffix-match that gets created. To solve it, you can remove the domain-suffix-match using nmcli:

$ nmcli connection modify CorpSSID 802-1x.domain-suffix-match ""

Alternatively, you can configure the WiFi network directly with nmcli without setting the problematic property>:

nmcli connection add type wifi ifname wlp0s20f3 \
  con-name CorpSSID \
  802-11-wireless.ssid CorpSSID \
  802-11-wireless-security.key-mgmt wpa-eap \
  802-1x.eap tls \
  802-1x.identity guyru \
  802-1x.client-cert /absolute/path/wifi-certs/signed-certificate.cer \
  802-1x.private-key /absolute/path/wifi-certs/private.key 

It’s important to have absolute paths to both the client certificate and the private key.

GIO can’t mount SMB

When trying to mount SMB share using gio you might encounter the following error:

$ gio mount smb://ptnas1.cellebrite.local
gio: smb://nas.corp.local/: Location is not mountable

This error might be due to a missing gvfs backend. The smb backend should be located in /usr/share/gvfs/mounts/smb.mount. If it is missing, you should install the gvfs-backends package and it should resolve the mounting issue.

If the mount was successful, but you can’t see it under $XDG_SESSION_DESKTOP/gvfss/, you are probably missing the FUSE server that makes the mounted filesystem available to all applications and not only to GIO aware applications. You can install the FUSE server by installing the gvfs-fuse package. The server will automatically run after reboot. If you want to start the server immediately, you can start it manually:

$ /usr/lib/gvfs/gvfsd-fuse /run/user/1000/gvfs

PipeWire showing only dummy output

After the latest PipeWire upgrade on Debian, sound stopped working for. The bluetooth headset would not connect, and in the output options I had only one device labeled dummy output.

This was caused by a recent upgrade of the pipewire-media-session package to version 0.4.1-3. Debian decided that media-session is deprecated in favor of WirePlumber. As part of the package installation, the /usr/share/pipewire/media-session.d/with-pulseaudio file, signalling media-session it should handle audio, gets removed. As I didn’t have WirePlumber installed, nothing managed the audio configuration. The solution is to recreate the file and restart the relevant PipeWire servcies.

$ sudo touch /usr/share/pipewire/media-session.d/with-pulseaudio 
$ systemctl restart --user pipewire pipewire-media-session pipewire-pulse