Arch: Automount encrypted sdcard – udev + systemd

Einen wundervollen Nachmittag,

in diesem Blogpost möchte ich beschreiben wie man mit Hilfe einer udev-Regel und Systemd eine mit luks verschlüsselte SD-Karte automatisch mounten lassen kann.

Vorwort: SD-Karte verschlüsseln

Damit man die SD-Karte automatisch entschlüsseln lassen kann, bietet es sich an, diese mit einem Keyfile zu verschlüsseln. Dazu erstellt man zunächst ein Keyfile:

sudo dd bs=512 count=4 if=/dev/urandom of=/etc/sdkey iflag=fullblock

Damit nicht jeder diesen Schlüssel lesen kann, verpasst man ihm noch die korrekten Rechte und einen Änderungsschutz:

sudo chmown root:root /etc/sdkey
sudo chmod 500 /etc/sdkey
sudo chattr +i /etc/sdkey

Danach kann man auch schon mit cryptsetup die SD-Karte verschlüsseln:

sudo cryptsetup luksFormat /dev/mmcblk0p1 /etc/sdkey
  • /dev/mmcblk0p1 ist die zu verschlüsselnde Partition auf der SD-Karte
  • /etc/sdkey ist das Keyfile, dass zur Verschlüsselung genutzt wird

Zuletzt öffnet man die SD-Karte und erstellt ein Dateisystem:

sudo cryptsetup --key-file=/etc/sdkey luksOpen /dev/mmcblk0p1 sdcard
sudo mkfs.ext4 /dev/mapper/sdcard

Fertig.

Automatische Entschlüsselung und Einbindung

Zum automatischen Entschlüsseln und Mounten der SD-Karte müssen wir eine neue udev-Regel und ein passendes Systemd Unitfile schreiben.

Systemd Unitfile

Wir beginnen mit dem Systemd Unitfile, da dieses relativ straight forward ist. Man legt eine neue Datei an: ‚/etc/systemd/system/sdcardencrypt.service‘. Der Inhalt kann so aussehen:

[Unit]
Description=Automount encrypted sdcard

[Service]
Type=oneshot
ExecStart=/usr/bin/cryptsetup --key-file=/etc/sdkey luksOpen /dev/sdcard sdcard
ExecStart=/usr/bin/mount /dev/mapper/sdcard /sdcard

Möchte man in dem Unitfile mehere ‚ExecStart‘-Kommandos ausführen, so ist ‚Type=oneshot‘ zu setzen.

Das erste Kommando ist bereits von oben bekannt und öffnet den luks-Container. Neu ist hier, dass als Device ‚/dev/sdcard‘ statt ‚/dev/mmcblk0p1‘ angegeben wird. Das erklärt sich aber später mit unserer udev-Regel.

Der zweite Befehl mountet den luks-Container in ‚/sdcard‘. Der Mountpoint muss einmalig davor erstellt werden und/oder geändert werden.

Damit Systemd das neue Unitfile korrekt ausführt, müssen wir einen kurzen Reload durchführen:

sudo systemctl daemon-reload

Udev-Regel

Als nächstes machen wir uns an die udev-Regel, welche den Symlink zu ‚/dev/sdcard‘ erstellt und das unser Unitfile aufruft. Dazu erstellen wir eine Datei in ‚/etc/udev/rules.d/‘ mit dem Namen ’10-sdcard.rules‘ und folgendem Inhalt:

ACTION=="add", KERNEL=="mmcblk0p1", SUBSYSTEM=="block", ATTR{size}=="124702720", ATTR{start}=="32768", SYMLINK+="sdcard", ENV{SYSTEMD_WANTS}="sdcardencrypt.service"

Hier passieren mehrere Dinge:

  • ACTION: Nur wenn das neue Gerät eingesteckt wird, soll die Regel ausgeführt werden
  • KERNEL, SUBSYSTEM, ATTR: Verundete Kriterien; Nur wenn alle matchen, wird die Regel ausgeführt
  • SYMLINK: Erzeuge ein Symlink von der gematchten Partition ‚/dev/mmcblk0p1‘ zu ‚/dev/sdcard‘
  • ENV: Führe danach das ’sdcardencrypt.service‘ Unitfile aus.

Das einzige, was ihr an der Regel ändern müsst, sind die Werte hinter KERNEL, ATTR{size}, ATTR{start}. Da wir hier von einer SD-Karte reden, wird SUBSYSTEM wahrscheinlich den Wert beibehalten. Alle anderen Werte nutze ich, um meine verschlüsselte SD-Karte (eindeutig) zu identifizieren.

An die entsprechenden Attribute gelangt man die SD-Karte einsteckt und danach das folgende Kommando ausführt:

udevadm info -a -p (udevadm info -q path -n /dev/mmcblk0p1)

Dabei ersetzt man ‚/dev/mmcblk0p1‘ mit der entsprechenden Partition der SD-Karte. Die Ausgabe kann so ähnlich aussehen:

udevadm info -a -p (udevadm info -q path -n /dev/mmcblk0p1)

Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.

 looking at device '/devices/pci0000:00/0000:00:1c.0/0000:02:00.1/mmc_host/mmc0/mmc0:aaaa/block/mmcblk0/mmcblk0p1':
 KERNEL=="mmcblk0p1"
 SUBSYSTEM=="block"
 DRIVER==""
 ATTR{alignment_offset}=="0"
 ATTR{discard_alignment}=="8388608"
 ATTR{inflight}==" 0 0"
 ATTR{partition}=="1"
 ATTR{ro}=="0"
 ATTR{size}=="124702720"
 ATTR{start}=="32768"
 ATTR{stat}==" 38 0 1099 136 0 0 0 0 0 56 136"

 looking at parent device '/devices/pci0000:00/0000:00:1c.0/0000:02:00.1/mmc_host/mmc0/mmc0:aaaa/block/mmcblk0':
 KERNELS=="mmcblk0"
 SUBSYSTEMS=="block"
 DRIVERS==""
 ATTRS{alignment_offset}=="0"
 ATTRS{capability}=="10"
 ATTRS{discard_alignment}=="0"
 ATTRS{ext_range}=="8"
 ATTRS{force_ro}=="0"
 ATTRS{inflight}==" 0 0"
 ATTRS{range}=="8"
 ATTRS{removable}=="0"
 ATTRS{ro}=="0"
 ATTRS{size}=="124735488"
 ATTRS{stat}==" 52 0 2155 206 0 0 0 0 0 96 206"

 looking at parent device '/devices/pci0000:00/0000:00:1c.0/0000:02:00.1/mmc_host/mmc0/mmc0:aaaa':
 KERNELS=="mmc0:aaaa"
 SUBSYSTEMS=="mmc"
 DRIVERS=="mmcblk"
 ATTRS{cid}=="035344534c36344780295762d200f800"
 ATTRS{csd}=="400e00325b590001dbd37f800a404000"
 ATTRS{date}=="08/2015"
 ATTRS{erase_size}=="512"
 ATTRS{fwrev}=="0x0"
 ATTRS{hwrev}=="0x8"
 ATTRS{manfid}=="0x000003"
 ATTRS{name}=="SL64G"
 ATTRS{oemid}=="0x5344"
 ATTRS{preferred_erase_size}=="25165824"
 ATTRS{scr}=="0245800300000000"
 ATTRS{serial}=="0x295762d2"
 ATTRS{type}=="SD"

 looking at parent device '/devices/pci0000:00/0000:00:1c.0/0000:02:00.1/mmc_host/mmc0':
 KERNELS=="mmc0"
 SUBSYSTEMS=="mmc_host"
 DRIVERS==""

 looking at parent device '/devices/pci0000:00/0000:00:1c.0/0000:02:00.1':
 KERNELS=="0000:02:00.1"
 SUBSYSTEMS=="pci"
 DRIVERS=="sdhci-pci"
 ATTRS{broken_parity_status}=="0"
 ATTRS{class}=="0x080501"
 ATTRS{consistent_dma_mask_bits}=="32"
 ATTRS{d3cold_allowed}=="1"
 ATTRS{device}=="0x16bc"
 ATTRS{dma_mask_bits}=="64"
 ATTRS{driver_override}=="(null)"
 ATTRS{enable}=="1"
 ATTRS{irq}=="17"
 ATTRS{local_cpulist}=="0-3"
 ATTRS{local_cpus}=="0f"
 ATTRS{msi_bus}=="1"
 ATTRS{numa_node}=="-1"
 ATTRS{subsystem_device}=="0x0504"
 ATTRS{subsystem_vendor}=="0x1025"
 ATTRS{vendor}=="0x14e4"

 looking at parent device '/devices/pci0000:00/0000:00:1c.0':
 KERNELS=="0000:00:1c.0"
 SUBSYSTEMS=="pci"
 DRIVERS=="pcieport"
 ATTRS{broken_parity_status}=="0"
 ATTRS{class}=="0x060400"
 ATTRS{consistent_dma_mask_bits}=="32"
 ATTRS{d3cold_allowed}=="0"
 ATTRS{device}=="0x1c10"
 ATTRS{dma_mask_bits}=="32"
 ATTRS{driver_override}=="(null)"
 ATTRS{enable}=="1"
 ATTRS{irq}=="17"
 ATTRS{local_cpulist}=="0-3"
 ATTRS{local_cpus}=="0f"
 ATTRS{msi_bus}=="1"
 ATTRS{numa_node}=="-1"
 ATTRS{subsystem_device}=="0x0504"
 ATTRS{subsystem_vendor}=="0x1025"
 ATTRS{vendor}=="0x8086"

 looking at parent device '/devices/pci0000:00':
 KERNELS=="pci0000:00"
 SUBSYSTEMS==""
 DRIVERS==""

Am Besten nimmt man die Attribute von weiter oben. Es stellt allerdings auch kein Problem dar, wenn man Attribute nutzt, die weiter unten stehen, nur sind diese etwas genereller und könnten gegebenenfalls auf andere SD-Karten matchen. Allerdings darf man nur Attribute von einem ‚Parent‘ des Devices nutzen. Für bessere Robustheit könnte man noch zusätzliche folgende Attribute einführen:

  • ATTRS{manfid}==“0x000003″
  • ATTRS{name}==“SL64G“
  • ATTRS{oemid}==“0x5344″
  • ATTRS{serial}==“0x295762d2″
  • ATTRS{type}==“SD“

Zuletzte sollte man noch die Regeln neu einlesen. Das klappt einfach mit einem:

sudo udevadm trigger

Fazit

Es braucht nicht viel Bastelei, um eine verschlüsselte SD-Karte automatisch einzubinden. Möglicherweise möchte man sich aber noch Gedanken darüber machen, dass der Mountpoint bzw. Luks-Container geschlossen wird, wenn die SD-Karte ausgehängt wird. Da ich das nicht häufig/wirklich benötige, ist dies dem interessierten Leser überlassen.

~ Sebastian