tl;dr: In diesem Beitrag erfahrt ihr wie ihr
- unter Archlinux einer bereits vollverschlüsselten Partition ein Keyfile hinzufügt, welches auf einem USB-Stick liegt. Dieser wird während des Bootvorgangs die Partition entschlüsseln. Der USB-Stick hat dabei keine Partitionen, der Key wird direkt vom Blockdevice (USB-Stick) an beliebiger Stelle gelesen.
- was umprogrammiert werden muss, dass im initramfs keine “sensiblen” Daten (Offset und Länge des Schlüssels) mehr stehen
Der Bootvorgang / die Entschlüsselung mittels Keyfile auf einem USB-Stick sieht am Ende so aus:
Einleitung. In folgendem Beitrag will ich etwas ausführlicher beschreiben, wie man einer existierenden Vollverschlüsselung ein Keyfile hinzufügt, welches die Parition anschließend automatisch beim booten entschlüsselt – die Eingabe eines Passworts besteht weiterhin als Alternative. Es gibt zwei Möglichkeiten: Man könnte den Schlüssel in ein Dateisystem auf den USB-Stick legen und dieses beim booten mounten. Diese Möglichkeit wurde im Archlinux-Wiki schon beschrieben und funktioniert super. Die andere Möglichkeit ist die, dass man sich seinen USB-Key mit Zufallsdaten vollschreibt, Offset und Länge in Byte angibt und als Schlüssel die Bytes nimmt, welche an diesem Offset auf dem USB-Key gespeichert sind. Diese Möglichkeit will ich in folgendem beschreiben.
Ich gehe davon aus, dass eine vollverschlüsselte Partition mit LVM vorliegt, in dem alle Paritionen liegen außer /boot. Weitere Vereinbarungen:
- /dev/sda1 => /boot
- /dev/sda5 => mit dm-crypt verschlüsselte Partition, die ein LVM enthält.
- Der USB-Stick wird über eine Regel in udev unter /dev/udev/rules.d/50-usbstick.rules verwaltet und an die Stelle /dev/usbstick gemapped. Eine tolle Anleitung dafür gibt es im Archlinux Wiki.
- Der USB-Stick wurde mit Zufallsdaten überschrieben. Beispielsweise mit dd if=/dev/urandom of=/dev/sdb
Schlüssel suchen. Wir haben einen USB-Stick vorliegen, der mit Zufallsdaten vollgeschrieben wurde. Nun müssen wir uns einen Offset und eine Schlüssellänge überlegen. Der Offset dient dazu den Schlüssel irgendwo mitten auf dem USB-Stick zu verstecken – es werden also $offset Byte am Anfang übersprungen und dann ein Schlüssel entsprechender Länge gelesen (ich nehme mal 1000 Byte). Dieses Keyfile müssen wir zunächst dem Container hinzufügen. Dazu muss der Key als Datei vorliegen. Also kopieren wir uns die Byte, die als Keyfile dienen sollen, mittels dd if=/dev/usbstick of=/home/user/keyfile bs=1 skip=OFFSET count=1000 vom USB-Stick in die Datei keyfile. Offset und Länge merken - die Werte brauchen wir später noch.
Tipp: Generiert euch den Offset zufällig auf Basis der Größe eures USB-Sticks.
Schlüssel hinzufügen. Mittels cryptsetup luksAddKey /dev/sdb5 /home/user/keyfile wird der Schlüssel dem verschlüsselten Volume hinzugefügt. Dabei bleibt das schon eingetragene Passwort bestehen - /dev/sda5 kann also in Zukunft über das Passwort oder das Keyfile entschlüsselt werden. Vielleicht auch interessant: Die beiden Keyslots können über cryptsetup luksDump /dev/sda5 angezeigt werden.
initramfs umbauen. Die Einstellungen für das initramfs werden in /etc/mkinitcpio.conf getroffen:
- FILES-Array anpassen: FILES=”/dev/udev/rules.d/50-usbstick.rules” Damit wird die Konfigurationsdatei für udev in das initramfs eingebunden. Der USB-Stick erscheint schon beim botten unter /dev/usbstick
- HOOKS-Array überprüfen: HOOKS=”base udev autodetect pata scsi sata usb usbinput keymap encrypt lvm2 filesystems” Die Hooks werden vom init-Prozess in dieser Reihenfolge ausgeführt: Damit ist auch klar warum usb vor encrypt, lvm2 nach encrypt und filesystems nach lvm stehen muss, oder?
Da ich davon ausgehe, dass euer System bereits vollverschlüsselt ist, sollte bei den Hooks nicht mehr viel zu machen sein. Wahrscheinlich war höchstens der usb-Hook einzufügen.
Nun noch das initramfs neu bauen: mkinitcpio -p linux (seit Kernel 3.0, davor mkinitcpio -p kernel2.6).
Kernelparameter anpassen. Die Kernelparameter werden unter /boot/grub/menu.lst eingetragen. Für die Entschlüsselung von dm-crypt-Volumes mittels Keyfiles gibt es bei Archlinux den Kernelparameter cryptkey. Ein Beispiel:
title Arch Linux
root (hd0,2)
kernel /vmlinuz-linux cryptdevice=/dev/sda5:vg root=/dev/mapper/vg-root cryptkey=/dev/usbstick:99898:1000 ro
initrd /initramfs-linux.img
Cryptkey hat einen Wert und 2 Argumente: /dev/usbstick ist der Pfad, der über udev (siehe Einleitung, Punkt 3) eingestellt wurde. Das erste Argument der Offset(99898) und das Zweite die Länge des Schlüssels (1000). Damit kann der Schlüssel auf dem USB-Stick an der richtigen Stelle gefunden und zum entschlüsseln genutzt werden.
FYI: Die andere Möglichkeit in Argument 1 ein Dateisystem und als Argument 2 den Pfad zum Keyfile innerhalb dieses Dateisystems anzugeben. In diesem Fall wäre es aber notwendig auch die Unterstützung des Dateisystems ins initramfs einzubauen. Weitere Informationen dazu gibt es im Archlinux-Wiki.
Man erkennt hier schon ein kleines Problem: Die /boot-Partition ist nicht verschlüsselt und Offset und Länge des Schlüssels stehen im initramfs für jeden zugänglich – man braucht nur noch den USB-Stick und kann den Schlüssel vom USB-Stick extrahieren.
Besser wäre sowas:
Also bauen wir es uns:
encrypt-Hook anpassen: Kommen wir zum eigentlich interessanten Teil. Wer den Offset seines Schlüssels nicht frei im initramfs stehen lassen will, sollte weiterlesen. Der encrypt-Hook liegt unter /usr/lib/initcpio/hooks/. Dort wird in den ersten 40 Zeilen des Shellscripts versucht, das verschlüsselte Volume über ein Keyfile zu entschlüsseln. In den Zeilen 21 bis 38 werden die zwei Möglichkeiten ( 1: Schlüssel abgelegt in einer Partition und 2: direkt vom USB-Stick lesen) abgearbeitet – jenachdem ob Argument 1 (Dateisystem bzw. Offset) mit einer Zahl beginnt oder nicht. Wir landen natürlich im 2. Fall ab Zeile 30.
Meine Anpassungen in diesem Skript erlauben es den Offset und die Schlüssellänge während des bootens einzugeben. (Zeile 33-36) Beachtet auch die Änderungen in Zeile 37!
Nachdem die Änderungen im encrypt-Hook getroffen wurden, muss das initramfs noch neu gebaut werden. Das haben wir schon im dritten Schritt gemacht: mkinitcpio -p linux.
Anmerkungen:
- Manche mögen sich fragen: Was haben wir eigentlich gewonnen? Der Bootvorgang geht ja doch wieder nicht automatisch und wir müssen immernoch Eingaben treffen – statt dem Passwort jetzt halt Zahlen. Der Gewinn ist meiner Meinung nach der, dass Zahlen schneller eingetippt sind als ein Passwort mit Sonderzeichen.
- Bitte bewertet es nicht über, dass Offset und Keylänge abgefragt werden – der USB-Stick und dessen Inhalt muss trotzdem unbedingt geheim bleiben. Überlegen wir mal. Angenommen der Inhalt des USB-Sticks ist bekannt: Wir haben einen 4 GB-Stick, eine Schlüssellänge und einen Offset. Daraus ergibt ist eine maximale Schlüsselanzahl von O((4*10^9)^2). (c.a. 16 Trillionen) . Im Vergleich dazu ein Schlüssel der Länge 64 Bit, also 2^64 mögliche Schlüssel gibt 18 Trillionen (Schlüssellänge liegt im Normalfall bei 256 bzw. 512 Bit). Der Schlüsselraum reduziert sich also, wenn der Inhalt des USB-Sticks dritten Bekannt wird, dramatisch.
Fragen? Fragen!

