Suspend to Cryptodisk
Contents
Einleitung
Früher war alles besser, da hat sich noch das BIOS in einem Laptop um das Suspenden auf die Festplatte gekümmert. Heute haben alle Laptops ACPI, und das Betriebssystem muss sich selber kümmern. Das hat den Vorteil, dass man sich wirklich um alle Kleinigkeiten kümmern kann, aber gleichzeitig auch muss.
Das große Problem für den sicherheitsbewussten Anwender ist, dass der Inhalt des Arbeitsspeichers unverschlüsselt auf der Festplatte landet, und damit sämtliche Festplattenverschlüsselung ausgehebelt wird, da natürlich der Key für diese Verschlüsselung irgendwo im RAM liegt.
Dieses How-To will eine Anleitung sein, wie man unter Debian GNU/Linux (aktuell Etch/testing) auf eine verschlüsselte Festplatte suspendet, so dass dort keinerlei Daten im Klartext rumfliegen.
Voraussetzungen
- Linux
- Kernel mit Device-Mapper, crypt-Target und aes Support
- installierte "initramfs-tools" und "gnupg" Packages
- vorhandener GPG-Key
- USB-Stick fuer den Schluessel
Schlüssel anlegen
USB-Stick ist unter /mnt/usb gemountet, der gpg Privatekey liegt unter /mnt/usb/.gnupg und Schluessel werden unter /mnt/usb/dm abgelegt. Die Key-ID des GPG-Keys ist 0x12345678
Dann erzeugt man einen Schluessel für 256Bit AES:
$ export GNUPGHOME=/mnt/usb/.gnupg $ dd if=/dev/random bs=1 count=32 | gpg -r 0x1234568 -e > /mnt/usb/dm/secretkey
initramfs bauen
Das Swap-Device, wohin nachher suspendet werden soll und was während des Betriebs als normaler (natürlich encrypteter) Swap dient, ist /dev/sda3. Beim Booten muss nun im initramfs der Key vom USB-Stick gelesen, decryptet und zum aufsetzen des Swaps verwendet werden.
Dazu werden die entsprechenden Scripte und Hooks unter /etc/initramfs-tools installiert:
/etc/initramfs-tools/hooks/suspend2:
#!/bin/sh # # initramfs hooks file for encrypted suspend2 PREREQ="" prereqs() { echo "$PREREQ" } case $1 in prereqs) prereqs exit 0 ;; esac . /usr/share/initramfs-tools/hook-functions manual_add_modules aes copy_exec /usr/local/sbin/suspend2ui_fbsplash /sbin copy_exec /usr/local/sbin/suspend2ui_text /sbin copy_exec /usr/bin/gpg /bin copy_exec /sbin/cryptsetup /sbin copy_exec /bin/loadkeys /bin # dump current keyboard layout dumpkeys > ${DESTDIR}/conf/keymap
/etc/initramfs-tools/scripts/local-top/suspend2
#!/bin/sh # # initramfs script file for encrypted suspend2 # color functions RED="�[31m" GREEN="�[32m" YELLOW="�[33m" BLUE="�[34m" NORMAL="�[0m" warn() { echo "${YELLOW}$*${NORMAL}" } message() { echo "${GREEN}$*${NORMAL}" } error() { echo "${RED}error: $*${NORMAL}" echo "${RED}rebooting${NORMAL}" exit 0 } # dependencies PREREQ="" prereqs() { echo "$PREREQ" } case $1 in prereqs) prereqs exit 0 ;; esac # Begin real processing below this line . /scripts/functions if [ -e /conf/keymap ]; then loadkeys /conf/keymap || warn "unable to load custom keymap" fi if [ ! "$swap" ]; then error "no swap partition, \$swap is empty!" fi if [ ! -x /sbin/cryptsetup ]; then error "no cryptsetup in /sbin??" fi if [ "$usb" -a "$no_swap" != "1" ]; then message "waiting for usb device \"$usb\"" TIMEOUT=30 while ! test -b /dev/$usb; do echo -n "." sleep 1 TIMEOUT=$(($TIMEOUT -1)) if [ "$TIMEOUT" = "0" ]; then error "timeout" break fi done echo "mounting usb" mkdir /usb mount -t vfat -o shortname=lower /dev/$usb /usb || error "unable to mount /dev/$usb to /usb" if [ ! "$keyfile" -o ! -e "/usb/$keyfile" ]; then error "\$keyfile (\"$keyfile\") empty or keyfile not found" fi echo "trying to setup swap using /usb" # tty fix rm /dev/tty ln -s /dev/console /dev/tty export GNUPGHOME=/usb/.gnupg gpg --decrypt /usb/$keyfile | cryptsetup -d /proc/self/fd/0 -c aes-cbc-essiv:sha256 -s 256 create swap $swap || error "cryptsetup failed" echo "umounting usb, remove now" umount /usb else echo "${RED}please enter passphrase for device "$swap" ${NORMAL}" # note: cryptsetup luks.... with cbc-essiv should be preferred! while ! cryptsetup -y -c aes create swap $swap; do warn "passphrases do not match, try again" done fi if [ "$do_resume" != "1" ]; then message "not resuming, \$do_resume is \"$do_resume\"" else message "waiting for swap mapper device" TIMEOUT=30 while ! test -b /dev/mapper/swap; do echo -n "." sleep 1 TIMEOUT=$(($TIMEOUT -1)) if [ "$TIMEOUT" = "0" ]; then error "timeout" break fi done SS2_CTRL_DIR=/proc/suspend2 # suspend2 >= 2.2.8, kernel >=2.6.18 use /sys/power/suspend2 if [ -d /sys/power/suspend2 ] ; then SS2_CTRL_DIR=/sys/power/suspend2 fi # better to set this in /etc/hibernate/suspend2.conf, at least for suspend2 >=2.2.8: echo /usr/local/sbin/suspend2ui_text > $SS2_CTRL_DIR/userui_program echo swap:/dev/mapper/swap > $SS2_CTRL_DIR/resume2 echo > $SS2_CTRL_DIR/do_resume fi
Nicht vergessen beiden Dateien ausführbar zu machen- initramfs macht auf solche Fehler nicht aufmerksam! Danach wird für die aktuelle Kernelversion (hier 2.6.17) eine initramfs gebaut:
$ update-initramfs -v -c -k 2.6.17 Generating /boot/initrd.img-2.6.17 Adding module /lib/modules/2.6.17/updates/loop.ko Adding module /lib/modules/2.6.17/kernel/crypto/aes.ko Adding module /lib/modules/2.6.17/kernel/drivers/usb/core/usbcore.ko Adding module /lib/modules/2.6.17/kernel/drivers/usb/host/ehci-hcd.ko Adding module /lib/modules/2.6.17/kernel/drivers/usb/host/uhci-hcd.ko Adding module /lib/modules/2.6.17/kernel/drivers/usb/input/usbhid.ko Adding module /lib/modules/2.6.17/kernel/drivers/usb/storage/usb-storage.ko Adding module /lib/modules/2.6.17/kernel/drivers/net/e1000/e1000.ko Adding module /lib/modules/2.6.17/kernel/drivers/net/slhc.ko Adding module /lib/modules/2.6.17/kernel/drivers/scsi/dpt_i2o.ko Adding module /lib/modules/2.6.17/kernel/drivers/scsi/scsi_transport_fc.ko Adding module /lib/modules/2.6.17/kernel/drivers/scsi/scsi_transport_spi.ko Adding module /lib/modules/2.6.17/kernel/drivers/acpi/fan.ko Adding module /lib/modules/2.6.17/kernel/drivers/acpi/processor.ko Adding module /lib/modules/2.6.17/kernel/drivers/acpi/thermal.ko
Anschließend muss der Kernel beim Booten noch ein paar Argumente (vor allem das USB-Stick-Device, hier sbd1 (OHNE "/dev/"!)) und natürlich das initramfs mitbekommen, im GRUB könnte das z.B. so aussehen:
title 2.6.17-crypt RESUME root (hd0,0) kernel /vmlinuz-2.6.17-crypt root=/dev/sda2 ro swap=/dev/sda3 do_resume=1 usb=sdb1 acpi_sleep=s3_bios,s3_mode keyfile=/dm/secretkey initrd /initrd.img-2.6.17-crypt boot title 2.6.17-crypt NORMAL root (hd0,0) kernel /vmlinuz-2.6.17-crypt root=/dev/sda2 ro swap=/dev/sda3 do_resume=0 usb=sdb1 acpi_sleep=s3_bios,s3_mode keyfile=/dm/secretkey initrd /initrd.img-2.6.17-crypt boot title 2.6.17-crypt noinitrd root (hd0,0) kernel /vmlinuz-2.6.17-crypt root=/dev/sda2 ro acpi_sleep=s3_bios,s3_mode boot
Suspenden
Suspendet wird dann auf /dev/mapper/swap, was vorher per mkswap initialisiert werden muss und dann auch als swap eingebunden werden kann.
fertig.
Variante: Filewriter und nur Verschlüsselung von einer Datenpartition
Wer zum Beispiel aus Performancegründen, oder um anderen User die Möglichkeit zu geben auch an dem System als Gast zu arbeiten nicht sein komplettes Root-Dateisystem sondern nur eine Datenpartition verschlüsselt kann entweder 2 swappartitionen (eine verschlüsselte und eine unverschlüsselte) nutzen, oder einfach einen Filewriter. Der Filewriter erzeugt das Image dabei an der selben Stelle im Dateisystem, da hier jedoch einmal ein dm-device gemountet ist und einmal nicht gibt es 2 unterschiedliche resume-parameter. Diese kann man ganz einfach im Grub als alternativoptionen angeben: /boot/grub/menu.list:
# altoptions=(recovery mode) single # altoptions=(plain-awake) resume=file:/dev/hda1:0x1febc00 # altoptions=(crypted-awake) resume=file:/dev/dm-0:0x47398
Und schon kann man beim starten wählen ob man mit oder ohne verschlüsselter Partition booten bzw. resumen will. Das Problem dabei ist, dass das Image der jeweils andren Option dann nicht deaktiviert wird, und beim nächsten boot mit dieser wieder resumed wird, was dann (dank verändertem dateisystem) probleme auf der Systemplatte geben kann.
Das geninitrfs-Script erstellt 2 initrds: eine mit entschlüsseln und eine ohne (die suspend2 scripte müssen halt vorher angepasst werden). S80savebootmode schreibt auf eine mini-extrapartition den aktuellen resume-parameter und unmounted es dann bei Systemstart. Beide initrd-Scripte prüfen nun ob der suspend2 parameter vom letzten boot der gleiche ist wie beim aktuellen boot (das plain tut nur das). Falls ja kann resumed werden, falls nicht wurde das Dateisystem verändert und resumen ist keine so gute idee.
/etc/rcS.d/S80savebootmode:
#!/bin/sh cp /sys/power/suspend2/resume2 /var/lastboot/lastbootmode umount /var/lastboot
natürlich /var/lastboot noch erstellen, die mini-partition erstellen, und in die /etc/fstab eintragen.
/usr/local/bin/geninitrfs:
#!/bin/bash case "$1" in crypted) if [ -z $2 ]; then echo "please specify kernel-version in param2"; exit 1; else cp /var/lib/geninitrfs/suspend2-crypted /etc/initramfs-tools/scripts/local-top/suspend2 chmod 755 /etc/initramfs-tools/scripts/local-top/suspend2 update-initramfs -v -c -k ${2} mv /boot/initrd.img-${2} /boot/initrd.img-${2}-crypted fi; ;; plain) if [ -z $2 ]; then echo "please specify kernel version in param2"; exit 1; else cp /var/lib/geninitrfs/suspend2-plain /etc/initramfs-tools/scripts/local-top/suspend2 chmod 755 /etc/initramfs-tools/scripts/local-top/suspend2 update-initramfs -v -c -k ${2} mv /boot/initrd.img-${2} /boot/initrd.img-${2}-plain fi; ;; *) echo "usage: geninitrfs plain|crypted kernelversion" ;; esac;
damit das script funktioniert muss in /var/lib/geninitrfs/ noch suspend2-plain und suspend2-crypted.
Die suspend2-crypted ist die im artikel erwähnte (angepasste) scripts/local.top/suspend2, nur anstatt
echo > $SS2_CTRL_DIR/do_resume
kommt
#check for other boots after creating this image mkdir /sysdisk mount /dev/hda4 /sysdisk if [ `cat ${SS2_CTRL_DIR}/resume2` = `cat /sysdisk/lastbootmode` ]; then umount /sysdisk rmdir /sysdisk echo > $SS2_CTRL_DIR/do_resume else echo "last boot used another suspend2-image: don't resume" sleep 3 fi; umount /sysdisk rmdir /sysdisk
die suspend2-plain besteht nur aus diesem code. /dev/hda4 ist die minipartition. Das sleep ist nur drin damit ich die meldung auch sehe, und sie beim booten nicht einfach vorbeirrauscht.
/var/lib/geninitrfs/suspend2-plain:
#!/bin/sh #check for other boots after creating this image SS2_CTRL_DIR=/sys/power/suspend2 mkdir /sysdisk mount /dev/hda4 /sysdisk if [ `cat ${SS2_CTRL_DIR}/resume2` = `cat /sysdisk/lastbootmode` ]; then umount /sysdisk rmdir /sysdisk echo > $SS2_CTRL_DIR/do_resume else echo "last boot used another suspend2-image: don't resume" sleep 3 fi; umount /sysdisk rmdir /sysdisk
Alles noch ausführbar machen und dann
$ geninitrfs plain 2.6.xxx $ [...] $ geninitrfs crypted 2.6.xxx $ [...]
Dann hat man in /boot zwei initrd für den gewählten kernel. eine mit -crypted und eine mit -plain. Die kann man jetzt den boot-einträgen zuordnen. Das macht man am einfachsten von Hand (bei grub: /boot/grub/menu.list).