Initramfs mit Cryptsetup und LVM


Bisher habe ich immer better-initramfs von Piotr Karbowski genutzt. Es liess sich leicht in meine Funtoinstallation einbinden und unterstützte alles Sachen die ich benötigte (cryptsetup, lvm2 und dropbear). Seit dem Versionssprung von cryptsetup auf die 2.x Version habe ich aber nur noch Probleme. better-initramfs nutzt nicht die binaries meiner Installation sondern lädt sich selber die Sources und kompiliert diese. Im Normalfall funktioniert das auch. Bei cryptsetup macht das allerdings Probleme da noch eine alter Version von 1.x genutzt wird.

Schon länger war mir das ein Dorn im Auge. Wenn ich schon meine Binaries selber kompiliere dann möchte ich diese auch gerne im initramfs nutzen.

Auf der Suche nach einer Alternative hat mir im funtoo irc Channel dann deaddy einfach mal einen Link zu seinem Blog geschickt.

Ein initramfs lässt sich mit einigen bash Kenntnissen sehr einfach selbst bauen. Da der Artikel schon etwas älter ist gibt es ein paar Änderungen die ich hier gerne dokumentieren möchte.

Zuerst müssen die Pakete busybox, lvm2 und cryptsetup mit folgenden use flags kompiliert werden:

sys-fs/lvm2 static
sys-apps/busybox static
sys-fs/cryptsetup static

Als root sucht man sich dann die Dateien zusammen und legt sie in einem Ordner (z.B. /root/initramfs) ab. Um das ganze copy & paste freundlich zu halten hier erst mal die Befehle, Erklärungen folgen später:

# create folders
mkdir bin dev dev/mapper dev/vc etc newroot proc sys run run/cryptsetup
# copy binaries
cp /bin/busybox /sbin/cryptsetup bin/
cp /sbin/lvm.static bin/
ln -s busybox bin/cat
ln -s busybox bin/mount
ln -s busybox bin/sh
ln -s busybox bin/switch_root
ln -s busybox bin/umount
ln -s busybox bin/sleep
ln -s lvm bin/vgscan
ln -s lvm bin/vgchange
# copy device files
cp -a /dev/console /dev/sda2 /dev/null /dev/random /dev/urandom dev
ln -s ../console dev/vc/0
# create german keymap
busybox dumpkmap > etc/kmap-de
ln -s busybox bin/loadkmap
# create init file
touch init
chmod u+x init

Bisher gibt es nur eine Änderung gegenüber dem Blogeintrag von deaddy. cryptsetup benötigt jetzt das Verzeichnis /run/cryptsetup. Dies wird in der ersten Zeile direkt miterzeugt. Die eigentlich "Logik" steckt im init Script:

#!/bin/sh
VOLUMEGROUP="vg"
CRYPTDEVICE="/dev/sda2"

mount -t proc none /proc
mount -t proc none /proc
mount -t sysfs none /sys

CMDLINE=$(cat /proc/cmdline)

mount -t sysfs none /sys

# no kernel output
echo 0 > /proc/sys/kernel/printk

#rescue function in case something is going wrong
rescue_shell() {
    echo "Something went wrong. Dropping you to a shell."
    busybox --install -s
    exec /bin/sh
}

#If you don't have a qwerty keyboard, uncomment the next line
loadkmap < /etc/kmap-de

#If you have a msg, show it:
#cat /etc/msg

#cryptsetup
/bin/cryptsetup --tries 3 luksOpen ${CRYPTDEVICE} vault || rescue_shell

#lvm
/bin/lvm vgscan --mknodes || rescue_shell # creates /dev/mapper/control
/bin/lvm vgchange -ay ${VOLUMEGROUP} || rescue_shell
/bin/lvm vgscan --mknodes || rescue_shell # creates /dev/mapper/${VOLUMEGROUP}-root and /dev/${VOLUMEGROUP}/root

#root filesystem
mount -r /dev/mapper/${VOLUMEGROUP}-root /newroot || rescue_shell

# mount move pseudo FS
mount --move /proc /newroot/proc || rescue_shell
mount --move /sys /newroot/sys || rescue_shell
mount --move /dev /newroot/dev || rescue_shell

#root switch
exec /bin/busybox switch_root /newroot /sbin/init ${CMDLINE}

Hier haben sich mehrere Änderungen ergeben. Die erste wichtige Änderung ist der mehrmalige Aufruf von vgscan mit --mknodes. Beim ersten Aufruf wird das control File in /dev/mapper/ erzeugt. Nach dem Aktivieren der Volumegroup wird dann noch einmal vgscan mit --mknodes aufgerufen um die dev Files für die Logical Volumes zu erzeugen.

Zum Schluss hat Deaddy seine pseudo FS schön unmountet. Das hat bei mir leider nicht geklappt. Der switch_root der danach kommt hing dann dann einfach. Stattdessen mache ich hier einen mount --move.

Um das initramfs dann zu bauen, muss man in diesem Ordner dann folgende Befehl ausführen:

find . -print0 | cpio --null --create --verbose --format=newc | gzip --best > /boot/initramfs.cpio.gz

Bis hierher hat man ein minimales initramfs gebaut, das cryptsetup und lvm2 nutzt.

lvm2 baut auf udev auf. Im initramfs gibt es aber kein udev, was zu verschiedenen Warnung führt.

Hier gibt es jetzt verschiedene Möglichkeiten: LVM mit dem use flag -udev kompilieren. Dann ist auch der vgscan --mknodes nicht mehr nötig. Die Festplatte über die uuids mounten * Oder udev in der lvm config des initramfs deaktivieren:

etc/lvm/lvm.conf:

global {
    use_lvmetad = 0
}
devices {
    md_component_detection = 0
}
activation {
    udev_rules = 0
    udev_sync = 0
}

Gleichzeitig wird hier auch direkt die Benutzung des lvmetad deaktiviert.

Zum debuggen sind auch noch folgende zwei Befehle hilfreich:

gunzip initramfs.cpio.gz
cpio --extract --make-directories --format=newc --no-absolute-filenames < initramfs.cpio

Hiermit kann das erzeugte initramfs noch mal entpackt werden.

UPDATE: Fixed a copy&paste Typo.