Laptop – SSD mit dm-crypt/Luks -Verschlüsselung und Opensuse Leap 15 – XI – Keyfiles, LUKS2-Volumes

Ich setze meine Artikel-Serie zur Voll-Verschlüsselung eines Linux-Laptops unter Opensuse Leap 15 fort.

Laptop – SSD mit dm-crypt/Luks -Verschlüsselung und Opensuse Leap 15 – I – Vorüberlegungen
Laptop – SSD mit dm-crypt/Luks -Verschlüsselung und Opensuse Leap 15 – II – Vorüberlegungen zur Virtualisierung
Laptop – SSD mit dm-crypt/Luks -Verschlüsselung und Opensuse Leap 15 – III – Zugriffs-Layer
Laptop – SSD mit dm-crypt/Luks -Verschlüsselung und Opensuse Leap 15 – IV – Disk-Layout
Laptop – SSD mit dm-crypt/Luks -Verschlüsselung und Opensuse Leap 15 – V – kryptierte Partitionen und Alignment
Laptop – SSD mit dm-crypt/Luks -Verschlüsselung und Opensuse Leap 15 – VI – Key-Slots, PBKDF2- und MK-Iterationen
Laptop – SSD mit dm-crypt/Luks -Verschlüsselung und Opensuse Leap 15 – VII – Grundinstallation für LUKS on LVM
Laptop – SSD mit dm-crypt/Luks -Verschlüsselung und Opensuse Leap 15 – VIII – Systemd-Fehler nach Neustart
Laptop – SSD mit dm-crypt/Luks -Verschlüsselung und Opensuse Leap 15 – IX – Verschlüsselter SWAP
Laptop – SSD mit dm-crypt/Luks -Verschlüsselung und Opensuse Leap 15 – X – Hibernation

Wir haben bislang "LUKS on LVM" genutzt und zwei LVM-Volumes mit LUKS1-Verschlüsselung angelegt - eines für das Root-Filesystem ("/"-FS; inkl. /boot-Verzeichnis) und eines für den SWAP. Hibernation funktionierte damit zufriedenstellend; allerdings mussten wir zur Vermeidung von Fehlern Plymouth abschalten. Beim Systemstart verlangt Grub2 von uns die Passphrase für das "/"-FS. Sobald der Kernel und systemd übernehmen, müssen wir allerdings Passphrases für alle (!) bisher angelegten LUKS-Volumes einzeln eingeben.

Schon in unserer einfachen Konfiguration führt das zu 3 Passworteingaben während des Systemstarts. Dabei haben wir bei weitem noch nicht alle Volumes angelegt, die wir im 4-ten Artikel der Serie geplant hatten. Eine zusätzliche Eingabe von Passphrases für alle noch benötigten Volumes wäre wirklich unbequem. Wir befassen uns in diesem Beitrag daher mit der Nutzung sog. LUKS-Keyfiles und zugehöriger Key-Slots.

Ein anderes Thema ist die Anlage weiterer Volumes - diesmal mit LUKS2. LUKS2 sorgt nicht nur für Konsistenz auf der Ebene verschlüsselter Blöcke, es nutzt auch ein anderes KDF-Verfahren - nämlich argon2i - für die iterative Erzeugung hashbasierter Keys zur Verschlüsselung des LUKS-Masterkeys [MK].

Keyfiles

Keyfiles beinhalten eine Passphrase, mit deren Hilfe die "Key Derivation"-Verfahren PBKDF2 (LUKS1) bzw. argon2i (LUKS2) unter Einsatzes von Hashes iterativ einen Schlüssel erzeugen, mit dem wiederum der MK für ein LUKS-Volume ver-/entschlüsselt wird. Der verschlüsselte MK wird in einem Key-Slot des LUKS-Volumes untergebracht. Wir müssen uns die Passphrases nicht merken; es empfiehlt sich daher, eine möglichst komplexe Passphrases hoher Entropie zu verwenden. Zur Erzeugung können wir unter Linux "urandom" verwenden.

Random Key, zugehöriger Slot und Keyfile für den SWAP

Ich demonstriere die Nutzung eines Keyfiles am Beispiel des SWAPs. Zuerst entscheiden wir uns für einen Ordner, in dem wir unsere Keyfiles unterbringen wollen; das Verzeichnis darf natürlich nur dem User root zugänglich sein. In Frage kommt dafür etwa ein Verzeichnis unterhalb von "/root". Das nachfolgende Beispiel zeigt die grundsätzliche Vorgehensweise; die Verzeichnis- und Datei-Namen müsst ihr natürlich selbst passend wählen:

mylap:~ # mkdir /root/gkv
mylap:~ # chown root.root /root/gkv
mylap:~ # chmod 700 /root/gkv

(Der aufmerksame Leser hat sicher gemerkt, dass unser Laptop inzwischen den Hostnamen "mylap" bekommen hat.

Nun füllen wir mittels des Kommandos "dd" eine Datei ".swk" mit Zufallszahlen; die generierte Sequenz von 1024 Bytes stellt später den LUKS-Key dar.

mylap:~ # dd if=/dev/urandom of=/root/gkv/.swk bs=1024 count=1
1+0 records in
1+0 records out
1024 bytes (1.0 kB, 1.0 KiB) copied, 5.8176e-05 s, 25.8 MB/s

Nun müssen wir unser LUKS1-Volume für den SWAP ("/dev/vgs/lvs1"; siehe den letzten Beitrag) mit einem Key-Slot versehen, der eine Version des MK aufnimmt, die mit der Passphrase aus der Datei ".swk" verschlüsselt wurde. Das geschieht mit dem Kommando "cryptsetup luksAddKey" und passenden Parametern:

mylap:~ # cryptsetup --hash sha512 --pbkdf-force-iterations 650000 luksAddKey   /dev/vgs/lvs1   /root/gkv/.swk
Enter any existing passphrase: 
Command successful.

Wir geben dabei den Pfad zum Keyfile nach dem Device an. Als Passphrase zur Autorisierung des Befehls benutzen wir die, de wir bereits früher für das Volume definiert hatten. Wir testen die Wirksamkeit des Keyfiles für unseren SWAP durch folgende Kommando-Sequenz:

mylap:~ # swapoff -a
mylap:~ # cryptsetup close cr-swap
mylap:~ # cryptsetup open --key-file /root/gkv/.swk   /dev/vgs/lvs1    cr-swap 
mylap:~ # swapon -a
mylap:~ # swapon -s
Filename                                Type            Size    Used       Priority
/dev/dm-8                               partition       17819644  0-1
mylap:~ # 
mylap:~ # la /dev/mapper
total 0
drwxr-xr-x  2 root root     340 Jan 16 12:02 .
drwxr-xr-x 22 root root    4600 Jan 16 12:02 ..
crw-------  1 root root 10, 236 Jan 16 12:01 control
lrwxrwxrwx  1 root root       7 Jan 16 12:01 cr-root -> ../dm-1
lrwxrwxrwx  1 root root       7 Jan 16 12:01 cr-swap -> ../dm-8

Nun müssen wir das Ganze noch dauerhaft machen und ändern deshalb die "/etc/crypttab" bzgl. folgenden Eintrags so ab, dass ein Verweis auf das Keyfile hinzugefügt wird. Nachfolgend wird das Ergebnis dieser Änderungsaktion angezeigt:

mylap:~ # cat /etc/crypttab
cr-swap   /dev/vgs/lvs1   /root/gkv/.swk
cr-root   /dev/vga/lva1   none

Im Anschluss können wir neu booten und unsere neue Konfiguration testen. Es sollte nun nicht mehr notwendig sein, das Passwort für das SWAP-Volume im Zuge des Systemstarts manuell anzugeben. So weit, so gut.

Nachtrag 19.01.2019: Eine wichtige Frage ist allerdings, ob und wie ein verschlüsselter SWAP über ein Keyfile auch Hibernation unterstützt. Unsere erste Vermutung ist, dass wir unser Keyfile vermutlich auch im initramfs benötigen. "dracut" benötigt zur Integration von Dateien in das initramfs hierfür eine Anweisung. Wir ergänzen daher eine Datei "/etc/dracut.conf.d/99-key-conf" um folgenden Eintrag (falls die Datei nicht vorhanden ist, muss sie angelegt werden):

 
mylap:~ # cat /etc/dracut.conf.d/99-key-conf 
install_items+="   /root/gkv/.swk   "
mylap:~ # mkinitrd

Die Blanks nach bzw. vor den Hochkommata sind notwendig! Danach müssen wir unter Opensuse Leap "mkinitrd" absetzen, um das "initramfs" neu erstellen zu lassen. Checken können wir die Existenz der Datei im initramfs mit "lsinitrd | grep swk".

Nachtrag 19.01.2019: Ein Test zeigt leider, dass Hibernation so nicht funktioniert. Der Boot-Vorgang mündet in seiner zweiten Phase regelmäßig in einen normalen Bootvorgang. Offenbar kann das Keyfile für den SWAP im Resume-Prozess nicht aufgedröselt werden. Wir müssen daher tiefer in die Trickkiste greifen, um den verschlüsselten SWAP während des Resume-Vorgangs mit einer Art "Automatik" für eine Passphrase öffnen zu können Ich komme darauf im nächsten Artikel zurück ....

Leute, die Hibernation in der Zwischenzeit unbedingt brauchen, müssen den Eintrag in der "/etc/crypttab" wieder zurücksetzen und damit wieder die Passphrase im Boot-Prozess eingeben:

cr-swap   /dev/vgs/lvs1   none

Die normale Passphrase sollte sich ja immer noch im Keyslot 0 des Swap-Volumes befinden!

Trotz dieser zwischenzeitlichen Enttäuschung haben wir aber gelernt, dass und wie ein Keyfile im Prinzip funktioniert 🙂 . Wir nutzen dieses Wissen nun für weitere Volumes.

Verschlüsseltes LUKS2-Volume für das /home-Verzeichnis

Ich möchte nun am Beispiel eines separaten LUKS-Volumes für das "/home"-Verzeichnis zeigen, wie man LUKS2 einsetzt. Ein wichtiger Punkt, bei dem man sich im Vergleich mit PBKDF2 schnell auf Abwege begeben kann, ist die Zahl der Iterationen:

Während wir im Falle von PBKDF2 Hunderttausende bis Millionen Iterationen vorgeben, liegen die entsprechenden Zahlen für argon2i (ohne Einschränkung der Sicherheit) im einstelligen Bereich!

Wir evaluieren das erneut mittels "cryptsetup benchmark" (s. die früheren Artikel):

mylap:~ # cryptsetup benchmark
# Tests are approximate using memory only (no storage IO).
...
PBKDF2-sha512    1182160 iterations per second for 256-bit key
...
argon2i       6 iterations, 1048576 memory, 4 parallel threads (CPUs) for 256-bit key (requested 2000 ms time)
...

Hinweis: In diesem Fall ist die früher festgestellte schlechte Performance von Grub2 für die Durchführung von PBKDF2-Iterationen
(s. Full encryption with LUKS (SHA512, aes-xts-plain64) – grub2 really slow … )
ebenso irrelevant wie die immer noch gegebene Unverträglichkeit von Grub2 mit LUKS2. Die im obigen Test für den Kernel gemessene Zeit (2000ms) ist richtig, da wir unsere weiteren LUKS2-Volumes beim System-Startup erst einbinden, nachdem der Kernel geladen ist!

Wir legen nun zuerst ein passendes LVM-Volume an; z.B. können wir mittels YaST der Volume Group [VG] "vga" unserer bisherigen Konfiguration ein weiteres Logical Volume [LV] der Größe 30 GiB hinzufügen. Ich gehe auf diese Standardschritte nicht näher ein. Dann verschlüsseln wir das Verzeichnis - diesmal mit LUKS2:

mylap:~ # cryptsetup --cipher aes-xts-plain64 --hash sha512 --key-size 512 --pbkdf argon2i  --pbkdf-force-iterations 6 --pbkdf-memory 1048576 --pbkdf-parallel 4 --type luks2  -v luksFormat   /dev/vga/lva2

WARNING!
========
This will overwrite data on /dev/vga/lva2 irrevocably.

Are you sure? (Type uppercase yes): YES
Enter passphrase for /dev/vga/lva2: 
Verify passphrase:

Man beachte die Parameter; im Besonderen "--type luks2"! Für weitere Details werfe man einen Blick in die man-Seiten.

Nun erzeugen wir mit "dd" ein Keyfile "/root/gkv/.hok" nach dem für den SWAP beschriebenen Muster und erzeugen einen zugehörigen Keyslot für unser LUKS2-Device - allerdings mit passenden Parametern für argon2i (s.o.):

mylap:~ # dd if=/dev/urandom of=/root/gkv/.hok bs=1024 count=1
1+0 records in
1+0 records out
1024 bytes (1.0 kB, 1.0 KiB) copied, 5.8176e-05 s, 23.9 MB/s
mylap:~ # 
mylap:~ # cryptsetup  --hash sha512  --pbkdf argon2i  --pbkdf-force-iterations 6 --pbkdf-memory 1048576 --pbkdf-parallel 4  -v luksAddKey   /dev/vga/lva2   /root/gkv/.hok
Enter any existing passphrase: 
Command successful.

Man beachte den Verweis auf das Keyfile.

In der Datei "/etc/crypttab" müssen wir noch verankern, unter welcher Mapper-Bezeichnung (hier: cr-home) unser neues Krypto-Device angesprochen werden soll:

mylap:~ # cat /etc/crypttab
cr-swap   /dev/vgs/lvs1   /root/gkv/.swk
cr-root   /dev/vga/lva1   none
cr-home   /dev/vga/lva2   /root/gkv/.hok

Wir öffnen nun unser Device (mittels "cryptsetup open" seiner Passphrase), legen ein ext4-Filesystem an und lassen uns danach die UUIDs anzeigen:

mylap:~ # cryptsetup open /dev/vga/lva2 cr-home
Enter passphrase for /dev/vga/lva2:
mylap:~ # 
mylap:~ # mkfs.ext4 /dev/mapper/cr-home
mke2fs 1.43.8 (1-Jan-2018)
Creating filesystem with 7863296 4k blocks and 1966080 inodes
Filesystem UUID: d6111122-abc1-6519-9bb2-8e61123dcfcc
Superblock backups stored on blocks: 
        32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208, 
        4096000

Allocating group tables: done                            
Writing inode tables: done                            
Creating journal (32768 blocks): done
Writing superblocks and filesystem accounting information: done   
mylap:~ # 
mylap:~ # lsblk -o name,UUID
NAME           UUID
sda            
├─sda1         
├─sda2         UUID-SEQUENZ
├─sda3         UUID-SEQUENZ
│ └─vgs-lvs1   d6bcf6fb-641c-4238-4f55-bc677d22d245
│   └─cr-swap  663e1134-ab5d-3aac-bb6f-d45b7b1f34dc
├─sda4         UUID-SEQUENZ
  ├─vga-lva1   db43615a-23e2-5125-49c1-ccbc2041e76c
  │ └─cr-root  a3202dac-a44e-4344-b551-dd123ce893b2
  └─vga-lva2   c9000bf4-5ff6-43b1-dccf-774532ccc1fb
    └─cr-home  d6111122-abc1-6519-9bb2-8e61123dcfcc

Um unser System bei einem nächsten Reboot nicht außer Gefecht zu setzen, ist es erforderlich, alle Daten des während der Installation angelegten "/home"-Verzeichnisses (für den bislang definierten User) in unser neues Filesystem zu übernehmen. Dafür eignet sich am besten das Kommando "cp -dpRv":

mylap:~ # mount /dev/mapper/cr-home /mnt
mylap:~ # cp -dpRv /home/* /mnt/
....
mylap:~ # umount /mnt

Danach können wir den Inhalt des "/home"-Verzeichnisses löschen, wenn wir wollen (und nicht als normaler User angemeldet sind), und "/dev/mapper/cr-home" versuchsweise auf "/home" mounten.

mylap:~ # mount /dev/mapper/cr-home /home
mylap:~ # mount
...
/dev/mapper/cr-root on / type ext4 (rw,relatime,data=ordered)
hugetlbfs on /dev/hugepages type hugetlbfs (rw,relatime)
debugfs on /sys/kernel/debug type debugfs (rw,relatime)
systemd-1 on /proc/sys/fs/binfmt_misc type autofs (rw,relatime,fd=31,pgrp=1,timeout=0,minproto=5,maxproto=5,direct,pipe_ino=15729)
mqueue on /dev/mqueue type mqueue (rw,relatime)
/dev/mapper/cr-home on /home type ext4 (rw,relatime,data=ordered)
...

Für den nächsten Reboot-Vorgang müssen wir nun aber der "/etc/fstab" noch einen Eintrag für das auf "/home" zu mountende Device hinzufügen. Das Ergebnis sollte dann etwa wie folgt aussehen:

mylap:~ # cat /etc/fstab
UUID=a3202dac-a44e-4344-b551-dd123ce893b2  /                      ext4  acl,user_xattr  0  1
UUID=663e1134-ab5d-3aac-bb6f-d45b7b1f34dc  swap                   swap  defaults        0  0
UUID=d6111122-abc1-6519-9bb2-8e61123dcfcc  /home                  ext4  acl,user_xattr  0  2

Man beachte, dass hier die UUIDs für die Filesysteme zu verwenden sind! Nun können wir unsere Konfiguration über das Auslösen eines Reboot-Vorgangs testen. Das sollte alles anstandslos funktionieren. Die Passphrases für den SWAP und das "/home"-Volume müssen nicht eingegeben werden.

Anmerkungen zur Sicherheit

Wir binden an dieser Stelle die Sicherheit der mit Keyfiles versehenen Volumes an 2 Punkte:

  • Die Files sind in unserer Konfiguration im "/"-FS untergebracht, welches wiederum in einem verschlüsselten LUKS-Volume liegt. Ein Zugriff ist bei einem Diebstahls des Laptops also nicht möglich, solange der Angreifer nicht die Haupt-Passphrase zum "/"-FS knackt. (Voraussetzung ist dabei natürlich, dass der Laptop sich in einem Hibernation- oder einem "Power OFF"-Zustand befindet und nicht in einem StR-Zustand ..)
  • Im laufenden Betrieb hängt die Sicherheit der LUKS-Volumes an der des root-Accounts.

Zum zweiten Punkt ist anzumerken: Sollte ein Hacker im laufenden Betrieb Zugriff auf den root-Account erreicht haben, so haben wir weit größere Probleme als den Zugriff auf die Keyfiles. Der Angreifer kann sich den zum LUKS-Volume "/dev/vga/lva1" gehörigen Master-Key aus dem RAM verschaffen. Dito für weitere geöffnete LUKS-Volumes, die virtuelle Maschinen beinhalten. Bei letzteren hilft dem Angreifer auch ein Keylogger - denn irgendwann müssen wir die Volumes ja manuell öffnen.

Das größte Risiko sehe ich hier übrigens beim zeitweisen Verlassen des Laptops (z.B. bei einem Kunden) und aktiviertem Screen-Saver (mit Passworteingabe). Einerseits kann das Root-Password zu einfach sein. Andererseits eröffnet ein physikalischer Zugriff des Angreifers womöglich Optionen zum Auslesen des RAMs, die wir nicht im Griff haben. Also: Beim Verlassen des Laptops Hibernation auslösen.

Der Leser hat sicher bemerkt, dass ich dennoch für das Volume "/dev/vga/lva1", das das "/"-FS beinhaltet, kein Keyfile angelegt habe. Die erforderlich zweimalige Passwort-Eingabe beim Systemstart nehme ich in Kauf. Genauso halte ich es später mit Volumes, die wertvolle Kundendaten innerhalb separater virtueller Maschinen aufnehmen sollen. Wir machen Angreifern das Leben hier etwas schwerer - zumindest für das Volume mit dem "/"-FS. Wirklich mehr Sicherheit erreichen wir hier gegenüber kompetenten Angreifern aus den genanten Gründen nicht.

Anmerkungen zur FS-Konsistenz

Ein Hinweis noch: Alle angelegten Filesysteme zusätzlicher Volumes sollten über "tune2fs -c n /dev/mapper/cr-...." mit einem Intervall an mount-Prozessen versorgt werden, nach denen automatisch die Konsistenz des Filesystems geprüft wird. LUKS2 garantiert Konsistenz auf dieser Ebene natürlich nicht.

Warum erwähne ich das? In letzter Zeit ist mir zeitweilig aufgefallen, dass auch nach dem Schließen eines Plasma-Schirms - je nach noch laufenden Applikationen beim Abmelden - noch Prozesse (meist kio_http_cache_cleaner) mit Zugriff auf das "/home"-Verzeichnis offen geblieben sind. Die verhindern ggf. ein direktes Unmounten des zugehörigen Volumes.

Das sollte eigentlich grundsätzlich nicht passieren, ich bin noch auf der Suche nach der Ursache und der zugehörigen Applikation. Dennoch ist das eine grundsätzliche Warnung, sich systematisch um die Konsistenz der Filesysteme in verschlüsselten LUKS-Volumes zu kümmern.

Fazit

Es ist relativ einfach, weitere LUKS1- oder LUKS2-Volumes mit Linux-Filesystemen (FS on LUKS on LVM) anzulegen und deren Berücksichtigung (Mounten) im Systemstart zu veranlassen. Die Dateien "/etc/crypttab" und "/etc/fstab" müssen um entsprechende Einträge erweitert werden. Die notwendigen Passphrases für das Öffnen kann man dabei aus Keyfiles auslesen lassen. Das erspart einem - bis auf die Eingabe der Passphrase für das Volume mit dem "/"-FS die manuelle Eingabe von mehreren Passphrases nach dem Laden des Kernels im Systemstart.

Natürlich gibt es auch die Möglichkeit, die Keys oder Keyfiles auf einem externen USB-Stick unterzubringen. Hinweise dazu, wie man einen solchen Stick in den Systemstart einbindet, findet ihr in weiter unten angegebenen Links. Der Einsatz von Keyfiles empfiehlt sich ggf. vor allem für später manuell zu öffnende Volumes mit virtuellen Maschinen. Den Stick kann man dann selbst natürlich auch wieder verschlüsseln (mit LUKS oder Veracrypt).

Leider funktioniert Hibernation nicht mit einem Keyfile für das SWAP-Volume, wenn wir dieses Keyfile im "/"-FS eines ebenfalls kryptierten Volumes untergebracht haben.

Im nächsten Beitrag befassen wir uns daher erneut mit Hibernation - ohne Passwort-Eingabe für den SWAP sowie von einer grafischen Desktop-Oberfläche aus.

Links

argon2
Password-Hashing-Competition
https://de.wikipedia.org/wiki/Argon2
https://crypto.stackexchange.com/questions/48935/why-use-argon2i-or-argon2d-if-argon2id-exists

Keyfiles on USB Dvices
https://willhaley.com/blog/unlock-luks-volumes-with-usb-key/
https://wiki.ubuntuusers.de/System_verschl%C3%BCsseln/Entschl%C3%BCsseln_mit_einem_USB-Schl%C3%BCssel/
https://www.gaztronics.net/howtos/luks.php
https://www.technikamateur.de/server/luks-partition-mit-usb-stick-entschluesseln