LUKS Header auf USB-Stick verschieben (für Debian Jessie)
Inhaltsverzeichnis
- 1 Ausgangssituation und Ziel
- 2 FAQ
- 3 Die Anleitung Schritt für Schritt
- 3.1 Dateien kopieren
- 3.2 Verzeichnisse vorbereiten
- 3.3 Vorbereitung des USB Sticks
- 3.4 Sicherung des Headers
- 3.5 Editieren von remoteheader.conf
- 3.6 Verschieben der Dateien
- 3.7 Update der initramfs
- 3.8 Entfernung des Headers von der Festplatte und Reboot
- 3.9 Entfernung der Sicherungskopie des Headers
- 4 Troubleshooting
Ausgangssituation und Ziel
Wir gehen davon aus, dass Sie eine LUKS verschlüsselte Festplatte haben, deren Header Sie entfernen und stattdessen von einem USB Stick aus benutzen wollen. Für Geräte, die erst nach dem Bootprozess entschlüsselt werden, ist dies im Allgemeinen kein Problem. Soll dagegen von dem Gerät gebootet werden, ist der Vorgang etwas komplizierter und wird hier beschrieben. Die beschriebene Vorgehensweise ist für Debian Jessie geeignet.
FAQ
Wozu sollte ich den Header von der Festplatte entfernen?
LUKS-verschlüsselte Partitionen sind schwer zu knacken, sofern man keine Konfigurationsfehler begangen hat. Jedoch benötigt jede LUKS-Partition einen Header, in dem steht, wie genau verschlüsselt wird und wo die verschlüsselten Daten beginnen. Dieser Header ist natürlich unverschlüsselt. Selbst mit den Informationen des Headers ist eine Entschlüsselung jedoch sehr aufwendig. Aber: Seine Existenz verrät, dass verschlüsselte Daten vorliegen; und mit einem erpressten Passwort oder Schlüsselfile werden diese Daten zugänglich. Ist der Header (sowie Boot-Partition und Bootloader) aber nicht auf dem Gerät zu finden, das verschlüsselt worden ist, so sind seine Inalte nicht mehr von zufälligen Daten zu unterscheiden und eine Verschlüsselung kann abgestritten werden. Zudem benötigt ein Angreifer nun das Passwort/Schlüsselfile und den Header, man hat also nebenbei eine wirksame Zwei-Faktor-Authentifizierung eingerichtet.
Ist das nicht unsicherer, wenn ich den Header unverschlüsselt mit mir herumtrage?
Der Header ist auch auf der Festplatte schon unverschlüsselt und kann direkt ausgelesen werden, sobald man physischen Zugriff auf das Gerät hat. Auf dem Header ist aber der eigentliche Schlüssel nur verschlüsselt abgelegt; verschlüsselt mit dem Passwort oder Schlüsselfile, das man angelegt hat. Um bei einem schwachen Passwort die Daten auf dem Gerät zu entziffern, reichte zuvor ein Zugang zum Rechner. Nun muss ein Angreifer den USB Stick und den Rechner in die Hände bekommen. Zwei Arten von Unsicherheit treten aber in der Tat neu auf:
- Erhält jemand den USB Stick, so kann er in Ruhe eine Brute-Force-Attacke gegen den Master Key auf dem Header fahren. Hat er den geknackt und bekommt nun physischen Zugriff auf das verschlüsselte Gerät, so sind dessen Daten sofort entziffert. Der Master Key lässt sich nur durch eine komplette Neuverschlüsselung ändern, so dass ein Verlust des USB Sticks großen Sicherungsaufwand bedeutet.
- Files auf einem USB-Stick sind volatiler und die Gefahr einer unabsichtlichen Zerstörung des Headers größer als zuvor. Um dem zu begegnen, schreiben wir den Header nicht als Datei oder in die initramfs auf den Stick, sondern in einen unpartitionierten Bereich. Dort kann nur absichtlich geschrieben oder gelesen werden. Wie zuvor aber auch, ist es empfehlenswert, ein Backup des Headers zu haben.
Was genau ist unsere Strategie?
Unsere Vorgensweise ist inspiriert von https://wiki.ubuntuusers.de/System_verschl%C3%Bcsseln/Entschl%C3%Bcsseln_mit_einem_USB-Schl%C3%Bcssel. Dort wird beschrieben, wie man einen USB-Stick als LUKS-Schlüssel einrichtet.
Wir schreiben ein Shellskript, das für die Entschlüsselung unter Benutzung des entfernten Headers sorgt. Die Disk-Entschlüsselung startet mit dem Aufruf von cryptroot. Dies geschieht als letztes Script im Ordner /usr/share/initramfs-tools/scripts/local-top. Um dem zuvor zu kommen, legen wir unser Shellskript in diesen Ordner. Über einen Hook sorgen wir dafür, dass dem Shellskript die normalen cryptsetup und vgchange zur Verfügung stehen.
Das Shellskript lädt eine Konfigurationsdatei, die dem Skript sagt, wo der Header auf dem Stick zu finden ist - in einem unpartitionierten Bereich des Sticks - und lädt ihn in das temporäre Filesystem der initramfs. Dort wird er zum Entschlüsseln benutzt. Im Kontrast dazu, den Header direkt in das initrd.img-File zu schreiben, hat das die Vorteile, dass der Header nicht auf dem Filesystem vorgehalten werden muss (weder in der Bootpartition noch in den verschlüsselten Partitionen), um Kernel-Updates reibungslos zu erlauben. Der Header wird nur kurz während des Bootvorgangs in den RAM des Rechners geladen und ist ansonsten nirgends auf dem Filesystem zu finden.
Warum kann ich nicht einfach die header option in der crypttab benutzen?
Debian Jessie benutzt beim Booten nicht die normale cryptsetup-Binary, sondern "systemd-cryptsetup". Momentan ist die Übergabe der "header"-Option dafür nicht implementiert. Wir laden deshalb die später benutzte cryptsetup-Binary nach und benutzen sie mit der Header-Option während der intitramfs-Phase.
Diese Information bezieht sich auf systemd in der Version 215-17+deb8u3. Falls Sie bereits eine spätere Version haben, könnte es durchaus sein, dass Sie die Header-Option benutzen können. Das sollten Sie dann auch tun. In dem Fall müssen Sie aber immer noch die USB-Module rechtzeitig laden, um den Header vom USB Stick ins initramfs zu bekommen. Die Vorgehensweise wäre fast identisch, nur folgende drei Änderungen wären nach dem Erstellen der Dateien remoteheader.conf, remoteheader.hook und remoteheader_script.sh nötig:
- in /etc/crypttab
header=/etc/remoteheader/header
ergänzen,
- in remoteheader_script.sh die Zeilen
/bin/dd if=$REMOTEHEADER_FILE of=/etc/remoteheader/header bs=$REMOTEHEADER_BLOCKSIZE skip=$REMOTEHEADER_SKIPBLOCKS count=$REMOTEHEADER_READBLOCKS 2>/dev/null && /sbin/cryptsetup luksOpen $REMOTEHEADER_DEVICENAME_ENCRYPTED --header /etc/remoteheader/header $REMOTEHEADER_DEVICENAME_DECRYPTED && /sbin/vgchange -ay
durch
/bin/dd if=$REMOTEHEADER_FILE of=/etc/remoteheader/header bs=$REMOTEHEADER_BLOCKSIZE skip=$REMOTEHEADER_SKIPBLOCKS count=$REMOTEHEADER_READBLOCKS 2>/dev/null
ersetzen und
- in remoteheader.hook die Zeilen
. /usr/share/initramfs-tools/hook-functions # provides copy_exec
und
copy_exec /sbin/cryptsetup /sbin/ # provides cryptsetup copy_exec /sbin/vgchange /sbin/ # provides lvm2 vgchange
entfernen.
Die Anleitung Schritt für Schritt
Wir werden das Bootimage ändern, daher besteht immer die Gefahr, dass das geänderte System zunächst nicht bootet. Halten Sie für diesen Notfall ein Live-Linux-System bereit, von dem Ihr Rechner booten kann.
Alle Schritte sollten Sie als Root ausführen. Gegebenenfalls müssen Sie daher sudo vor die entsprechenden Befehle schreiben.
Wir gehen im Folgenden davon aus, dass die verschlüsselte Partition /dev/sda5 ist und der USB Stick unter /dev/sdb zu finden ist. Bitte passen Sie die unten folgenden Schritte Ihrer Situation entsprechend an.
Um herauszufinden, wie die Gerätedateien tatsächlich heißen, kann man den Befehl lsblk benutzen. LUKS-entschlüsselte Geräte sind die Eltern der Geräte vom Typ crypt.
Dateien kopieren
Sie benötigen die folgenden drei Dateien:
Kopieren Sie bitte die Linkinhalte in Files mit dem angegebenen Namen.
Verzeichnisse vorbereiten
Wir legen das Verzeichnis an, das unsere Bootkonfiguration ergänzen soll.
mkdir /etc/remoteheader
Vorbereitung des USB Sticks
Wir exportieren zunächst den Header
cryptsetup luksHeaderBackup /dev/sda5 --header-backup-file /etc/remoteheader/header
und sehen uns an, wieviel Platz wir dafür benötigen:
ls -l /etc/remoteheader
Meistens sind dies zwischen zwei und drei Megabyte. Nun müssen wir dafür sorgen, dass auf dem USB-Stick mindestens so viel unpartitionierter Platz zur Verfügung steht. Dazu kann man jedes Partitionierungsprogramm verwenden, am sichersten aber erscheint uns gparted:
gparted /dev/sdb
Falls gparted nicht zur Verfügung steht, kann es mittels
apt-get install gparted
nachinstalliert werden. Mittels fdisk kontrollieren wir nun das Ergebnis:
fdisk -l /dev/sdb
Das kann dann zum Beispiel so aussehen:
Disk /dev/sdb: 7.4 GiB, 7902068736 bytes, 15433728 sectors Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disklabel type: dos Disk identifier: 0x000178bc Device Boot Start End Sectors Size Id Type /dev/sdb1 * 62 15294407 15294346 7.3G 83 Linux
In diesem Fall haben wir vor der Partition /dev/sdb1 62 und danach 15433728-15294407-1=139320 Blöcke von jeweils 512 Bytes zur Verfügung. Vor die Partition passt der Header also nicht, dahinter aber haben wir große Auswahl. Wir könnten zum Beispiel bei Block 15300000 mit dem Header beginnen. Um das zu tun, benutzen wir dd:
dd if=/etc/remoteheader/header of=/dev/sdb bs=512 seek=15300000
Passen Sie bei der Benutzung von dd besonders auf: Fehler können im Allgemeinen nicht rückgängig gemacht werden und eine falsche Gerätedatei kann verheerende Folgen haben.
Sicherung des Headers
Für den Fall, dass etwas schief läuft, sollten wir eine weitere Kopie des Headers in der Boot-Partition haben.
cp /etc/remoteheader/header /boot/header
Editieren von remoteheader.conf
Um die Datei remoteheader.conf anzupassen, benötigen wir folgende Informationen:
- Die ID des USB Sticks. Diese dient dazu, dass der Header nur von diesem speziellen USB-Stick gelesen wird. Eine Byte-für-Byte-Kopie des USB-Sticks würde nicht funktionieren. Die ID des Sticks findet man so heraus:
ls -l /dev/disk/by-id
Es ist die ID, die zum Gerät /dev/sdb verlinkt. Sie ist meist von der Form "usb-....-0:0". Wir schreiben sie in Anführungszeichen hinter
REMOTEHEADER_DISKID=
Wollen Sie den Header auf mehreren USB-Sticks benutzen können (ein Backup des Headers sollte stets vorhanden sein), so schreiben Sie hier alle IDs hintereinander, getrennt durch jeweils ein Leerzeichen.
- Den Gerätenamen, vor und nach der Entschlüsselung. Hier haben wir angenommen, dass der erste /dev/sda5 heißt und Debian nennt dann das entschlüsselte Gerät sda5_crypt, sofern man keine andere Angaben macht. Dies sind die Default-Einstellungen
REMOTEHEADER_DEVICENAME_ENCRYPTED="/dev/sda5" REMOTEHEADER_DEVICENAME_DECRYPTED="sda5_crypt"
und müssen bei Bedarf angepasst werden. Wir hätten diese Information auch aus der crypttab lesen können, aber da das Skript kein vollständiges crypttab-Parsing durchführt, wäre dies Augenwischerei.
- Die Blockgröße des Geräts, meist 512. Sie ist in der Ausgabe des obigen fdisk-Befehls zu finden und wir tragen sie als
REMOTEHEADER_BLOCKSIZE=
ein.
- Der Startblock des Headers auf dem Stick. Hier haben wir 15300000 gewählt, also wäre
REMOTEHEADER_SKIPBLOCKS="15300000"
einzutragen.
- Die Länges Headers in Blocks. Ist <HEADERSIZE> die Größe des Heades in Bytes und <BLOCKSIZE> die Blockgröße, so bestimmen wir sie mittels
echo <HEADERSIZE>/<BLOCKSIZE> | bc -l
Dies sollte ganzzahlig sein. Wir tragen den Wert als
REMOTEHEADER_READBLOCKS=
in die Konfigurationsdatei ein.
Verschieben der Dateien
Alle Dateien müssen Root gehören:
chown root:root remoteheader.conf remoteheader.hook remoteheader_script.sh
Die Konfigurationsdatei muss nun in dem neuen /etc-Ordner zu finden und mit minimalen Rechten ausgestattet sein.
cp remoteheader.conf /etc/remoteheader/ chmod 400 /etc/remoteheader/remoteheader.conf
Das Hook-Skript und das Boot-Skript müssen ausführbar sein und in die richtigen Verzeichnisse:
chmod 755 remoteheader.hook remoteheader_script.sh cp remoteheader.hook /etc/initramfs-tools/hooks/ cp remoteheader_script.sh /usr/share/initramfs-tools/scripts/
Update der initramfs
Um den laufenden Kernel herauszufinden, benutzen Sie
uname -r
und um den entsprechenden Kernel zu aktualisieren, können Sie einfach
update-initramfs -k $(uname -r) -u
benutzen, oder hinter -k die gewünschte Kernelversion schreiben. Um alle Kernels zu aktualisieren, schreiben Sie "all" anstatt der Kernelversion. Die einzigen Fehlermeldungen, die hier auftreten dürfen, sind von der Form "Device /dev/sda5 already exists. FAILED to decrypt using ...". Treten weitere Fehler auf, sollten Sie hier aufhören und auf Fehlersuche gehen. Erst, wenn dieser Schritt erfolgreich war, fahren Sie mit dem nächsten fort.
Entfernung des Headers von der Festplatte und Reboot
Wir überschreiben den Header mit (quasi-)zufälligen Daten:
head -c <HEADERSIZE> /dev/urandom > /dev/sda5; sync
Die heruntergeladenen Dateien sollten nun entfernt werden.
rm -f remoteheader.conf remoteheader.hook remoteheader_script.sh
Das System kann nun neu gebootet werden.
Entfernung der Sicherungskopie des Headers
Bootet der Rechner wie gewünscht, so sollte die Sicherungskopie entfernt werden.
shred -uz /boot/header
Troubleshooting
Wiederherstellung des Headers (mit Sicherheitskopie)
Sollte der Rechner beim ersten Test nicht mehr booten, starten Sie ein Live-Linux-System. In einer Shell können Sie dann den folgenden Befehl ausführen.
cryptsetup luksHeaderRestore /dev/sda5 --header-backup-file /boot/header
Da der Header vollständig entfernt wurde, muss der Vorgang mit YES bestätigt werden. Jeder nicht-aktualisierte Kernel sollte nun wieder booten. Die aktualisierten Kernel booten, wenn der USB-Stick mit dem Header nicht eingesteckt ist.
Wiederherstellung des Headers (ohne Sicherheitskopie)
Weigert sich der Rechner plötzlich zu booten und haben Sie die Sicherheitskopie wie empfohlen entfernt, haben wir die Möglichekit, den Header so wieder zu finden:
Wir entpacken die modifizierte initramfs, hier mit <INITRAMFS> bezeichnet, lesen die Konfigurationsdatei aus und erzeugen die Sicherheitskopie:
mkdir temp; cd temp; gunzip -c /boot/<INITRAMFS> | cpio -i . etc/remoteheader/remoteheader.conf dd if=/dev/sdb of=/boot/header bs=$REMOTEHEADER_BLOCKSIZE skip=$REMOTEHEADER_SKIPBLOCKS count=$REMOTEHEADER_READBLOCKS
Nun gehen wir wie oben vor.
Wiederherstellung des Headers (ohne Sicherheitskopie und Konfigurationsdatei)
Fehlt Ihnen auf unerklärliche Weise auch die Konfigurationsdatei, so durchsuchen wir den Stick so lange, bis wir auf den Header treffen:
hexdump -C /dev/sdb | grep LUKS | awk '{ print $1 }'
gibt uns die Startadressen gefundener LUKS-Header in hexadezimaler Form. Das kann ziemlich lange dauern. Bekommen Sie mehrere Treffer, so haben Sie vielleicht noch alte Header oder entfernte Kopien auf dem Stick. Schwieriger wird es nun, das Ende des Headers zu finden. Dazu nehmen Sie eine vielversprechende der erhaltenen Adressen, sagen wir <STARTADDRESS_HEX> und führen die Suche
hexdump -C /dev/sdb -s 0x<STARTADDRESS_HEX> | grep "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" | awk '{ print $1 }'
durch. Liegt eine der gefundenen Addresse etwa 2 bis 3 MB von der Startadresse entfernt, sagen wir <ENDADDRESS_HEX>, so sind wir dem Ende des Headers nahe. Wir berechnen nun die dezimale Start- und Endaddresse als
STARTADDRESS=$((0x<STARTADDRESS_HEX>)) ENDADDRESS=$((0x<ENDADDRESS_HEX>))
Die Länge des Headers in Blöcken (von hier 512 Bytes) und die anderen Parameter setzen wir dann als
REMOTEHEADER_BLOCKSIZE=512 REMOTEHEADER_SKIPBLOCKS=$((STARTADDRESS/512)) REMOTEHEADER_READBLOCKS=$(((ENDADDRESS-STARTADDRESS)/512 + 4))
und schreiben den Header
dd if=/dev/sdb of=/boot/header bs=$REMOTEHEADER_BLOCKSIZE skip=$REMOTEHEADER_SKIPBLOCKS count=$REMOTEHEADER_READBLOCKS
Nun haben wir hoffentlich wieder die Sicherheitskopie des Headers.