Minimal initramfs for LUKS and LVM

I think if you do not run your system on a fully encrypted disk, you might as well hand over your data directly. If your data partition is not encrypted, one can simply boot into a live system and take your data. If your system partition is not encrypted, one can simply drop a modified binary on it. Both scenarios do not take much effort beyond physical access, which is a low barrier, at least once you consider who might even be interested in that. Maybe you do not care as a private person. But once your organization has a size where some new cleaning crew goes unnoticed, this is the easiest attack vector.

Having full disk encryption on the other hand requires the attacker to either modify the kernel or massively corrupt your initramfs, which seems to be a lot harder than just planting a sshd which accepts one key more, or at least two attacks, one to corrupt your initramfs and retrieve your passphrases and another one to actually retrieve your data. And disk encryption comes to practically no cost. While it still and obviously does not give absolute security, it is the biggest barrier one can achieve by means of software.

Anyway, having quickly elucidated reasons for full disk encryption, the usual setup in the linux world would be to have your disk(s) encrypted bar the boot partition via LUKS and then running a LVM over it. However, to access this, you need to employ an initramfs. While there are tools to automatically generate these and some distros even roll them out automatically, I dislike the bloat they come with, so I make my own. The link I used to use 404'd a long time ago, so I hope to preserve the knowledge by reposting its content, although it is slightly modified as I had in my personal wiki.

First of all, the setup for the following is having one disk, /dev/sda, with one partition /dev/sda1 for /boot and /dev/sda2 for the encrypted volume, on which there is a volume group vg with a partition named root. If you have more than one disk or use a raid, modify accordingly and perhaps include static mdadm binaries in the initramfs.

First of all, we need static versions of busybox, lvm2 and cryptsetup. In Funtoo/Gentoo you obtain these by setting useflags

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

and then accepting the new changes to the useflags portage wants you to make. Now we may begin by creating the initramfs, so as root we create a folder. All of the following takes place inside that folder.

mkdir bin dev dev/mapper dev/vc etc newroot proc sys

cp /bin/busybox /sbin/cryptsetup /sbin/lvm.static bin/
mv bin/lvm.static bin/lvm
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

This gives us all the binaries we need. Now we need to create the devices we need in order to access the system.

cp -a /dev/console /dev/sda2 /dev/null /dev/random /dev/urandom dev
ln -s ../console dev/vc/0

If we are using some other keyboard than the English standard one, we also want to load it:

busybox dumpkmap > etc/kmap-de
ln -s busybox bin/loadkmap

And maybe some nice motd:

cp /etc/msg etc/

Finally we need to add the init script, which runs all commands. For this we create a file init with the following contents


mount -t proc none /proc
CMDLINE='cat /proc/cmdline'

mount -t sysfs none /sys

#wait a little to avoid trailing kernel output
sleep 3

#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

#/bin/mdadm --assemble /dev/md2 /dev/sda2 /dev/sdb2 || rescue_shell

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

#/bin/cryptsetup luksOpen /dev/md2 vault || rescue_shell
/bin/cryptsetup luksOpen /dev/sda2 vault || rescue_shell

#/bin/lvm vgscan
/bin/lvm vgchange -ay vg || rescue_shell

#root filesystem
mount -r /dev/mapper/vg-root /newroot || rescue_shell

#unmount pseudo FS
umount /sys
umount /proc

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

For everything to work we finally have to make it accessible:

chmod u+x init

Last but not least, we have to actually create the initramfs. I used to use the following command, but it does not work anymore (hints why are welcome!):

find . | cpio --quiet -o -H newc | gzip -9 > /boot/initramfs

Instead, assuming the boot partition is mounted under /boot, we do:

find . -print0 | cpio --null -ov --format=newc > /boot/initramfs

That's it. Now all that is left to do is to have properly configured kernel and telling him to load this initramfs at boot time.