Starting systemd Services when Encrypted Device is plugged in
Tech linux · systemd
Problem: You want to start a service only once an encrypted harddrive is plugged in.
On our embedded iconnect box, we run an mpd whose music collection is
stored on an external, encrypted hdd. The mpd should only start when the
device is plugged in, since otherwise it would rescan its collection
every time the hdd is not present. To achieve that, we can use systemd’s
ability to define dependencies.
In general, it works like that:
- Plugging in the hdd triggers an udev rule
- The udev rule starts a service which decrypts the device
- After decryption, the device is mounted by another service
- Once mount is finished, mpd is started
Udev Rule
The hdd is connected via USB as /dev/sdb. We call it media which also corresponds to the filesystem label of the only partition. You can find out the devices’ serial number with
# udevadm info --path /sys/class/block/sdb1 | grep SERIAL_SHORT
E: ID_SERIAL_SHORT=222254634873
The resulting udev rule in /etc/udev/rules.d/45-media.rules looks like that:
ACTION=="add", KERNEL=="sd?1", SUBSYSTEMS=="usb", ATTRS{serial}=="222254634873", TAG+="systemd", ENV{SYSTEMD_WANTS}+="systemd-cryptsetup@media.service"
It matches the serial and triggers the execution of the systemd-cryptsetup@media.service unit
Cryptsetup
The unit for decryption is stored in /etc/systemd/system/systemd-cryptsetup@media.service.
[Unit]
Description=Cryptography Setup for %I
Documentation=man:systemd-cryptsetup@.service(8) man:crypttab(5)
SourcePath=/etc/crypttab
Conflicts=umount.target
DefaultDependencies=no
IgnoreOnIsolate=true
RequiresMountsFor=/root/.iconnect.key
BindsTo=dev-disk-by\x2duuid-6bf06a61\x2d16c7\x2d44c9\x2d98e0\x2d6ad295a19ab5.device
Wants=mnt-media.mount
Before=mnt-media.mount
[Service]
Type=oneshot
RemainAfterExit=yes
TimeoutSec=0
ExecStart=/usr/lib/systemd/systemd-cryptsetup attach 'media' '/dev/disk/by-uuid/6bf06a61-16c7-44c9-98e0-6ad295a19ab5' '/root/.iconnect.key' 'noauto'
ExecStop=/usr/lib/systemd/systemd-cryptsetup detach 'media'
Such a unit is automatically created by systemd out of /etc/crypttab during boot. Anyways, these cannot be used in our scenario, so we have to generate it manually. These are the steps I applied to create it
-
Add an entry to /etc/crypttab:
media UUID=6bf06a61-16c7-44c9-98e0-6ad295a19ab5 /root/.iconnect.key noauto
-
Execute /usr/lib/systemd/system-generators/systemd-cryptsetup-generator, which will store the auto-generated service file in /tmp
-
Remove the following lines
BindsTo=dev-mapper-%i.device After=systemd-readahead-collect.service systemd-readahead-replay.service Before=cryptsetup.target Before=umount.target
-
Add the following lines to introduce a dependency on a unit which mounts the device
Wants=mnt-media.mount Before=mnt-media.mount
Mounting
The unit which is responsible for mounting the decrypted device node can be found in /etc/systemd/system/mnt-media.mount
[Unit]
Description = Media Disk
Before=mpd.service
Wants=mpd.service
Wants=systemd-cryptsetup@media.service
After=systemd-cryptsetup@media.service
[Mount]
What = /dev/disk/by-uuid/1f16ad25-7aa4-4064-a1b9-05f4f1cd246b
Where = /mnt/media
Type = ext4
The UUID is different from the first we saw. It is the one of the
decrypted filesystem, obtained by ls -l /dev/disk/by-uuid | grep
media. It will mount to /mnt/media. Note that you have to change the
filename if your mount point is different.
As you can see, this unit will start mpd.service. It can also start
another daemon if desired.
The main advantage of this approach is its flexibility when it comes to inter-process dependencies. The mpd itself may also depend on other services being started beforehead, such as pulseaudio. It seems rather complex, but the whole setup will guarantee, that mpd is only started when the hdd is plugged in. By giving pulseaudio as a dependency for mpd, it is also ensured, that sound output is set up properly and no user interaction is involved to listen to music.