Full Encrypted Btrfs/Native System Root Guide

From Gentoo Wiki
Jump to: navigation, search

Basically this article is an extension to Btrfs/Native System Root Guide which adds Dm-crypt and uses Dracut to create the initramfs rather then dealing with the Early Userspace Mounting approach. As the root partition, which also includes /boot, will end up encrypted, we'll store the keyfile to unlock the btrfs raid partitions within the initramfs. This may be a bit unsafer on runtime as the keyfile ends up in memory but we gain a faster boot process without the need to input the password 4 times (2 x grub and 2 x btrfs raid1). I also have an btrs raid6 with 6 full encrypted disks and this would lead me to enter my password 10 times to have a fully working system. so i'm happy with embedding the keyfile within the initramfs.

As i didn't find a way to get a working system with the initramfs generated by genkernel I've decided to use dracut.

We'll migrate an existing MD software raid1 to an btrfs raid1 without adding extra disks. So better make backups of your data! I assume the raid members of /dev/md1 to be /dev/sda and /dev/sdb.

The whole procedure is straight forward but you have to double check a few things i'll mention later. Please carefully read the whole post and pay extra attention to grub2 and dracut.

There may be better ways to accomplish this setup but after nights of research and testing within a virtual machine this procedure has worked for me.

Required packages

First add the required use flags for the packages. As i'm a lazy person :) i'll use genkernel-next to do the work even if i'll replace the initramfs with the dracut one.

root #echo "sys-apps/systemd cryptsetup" >> /etc/portage/package.use
root #echo "sys-boot/grub device-mapper" >> /etc/portage/package.use
root #echo "sys-fs/cryptsetup static kernel -gcrypt" >> /etc/portage/package.use
root #echo "sys-kernel/genkernel-next cryptsetup" >> /etc/portage/package.use
root #echo "sys-kernel/dracut systemd device-mapper" >> /etc/portage/package.use

Next unmask the packages (Please change the keyword as needed for your system). We'll use the latest available versions.

root #echo "sys-fs/btrfs-progs ~amd64" >> /etc/portage/package.keywords
root #echo "sys-boot/grub:2 ~amd64" >> /etc/portage/package.keywords
root #echo "sys-fs/cryptsetup ~amd64" >> /etc/portage/package.keywords
root #echo "sys-kernel/genkernel-next ~amd64" >> /etc/portage/package.keywords
root #echo "sys-kernel/gentoo-sources ~amd64" >> /etc/portage/package.keywords

Install the required packages

root #emerge --ask systemd grub:2 cryptsetup genkernel-next btrfs-progs dracut gentoo-sources

If this installs newer kernel sources, please change the symlink either using eselect kernel or do it manually, build the kernel and reboot. After that proceed from here.

Preparing for encryption

As we'll use a keyfile to unlock the partitions we'll now create one (paranoid settings).

root #dd if=/dev/random of=/root/secretkey bs=1 count=4096
root #chmod 0400 /root/secretkey

Preparing the first disk

Dealing with the software raid

Remove /dev/sda drive from md array

root #mdadm --manage /dev/md1 --fail /dev/sda
root #mdadm --manage /dev/md1 --remove /dev/sda
root #mdadm --zero-superblock /dev/sda

Partition the drive

We create a bios boot partition and use the remaining space for the root partition.

root #gdisk /dev/sda
Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048            8191   3.0 MiB     EF02  BIOS boot partition
   2            8192       250069679   119.2 GiB   8300  Linux filesystem

Encrypt partition

First we'll format the partitions

root #cryptsetup luksFormat -s 512 -c aes-xts-plain64 /dev/sda2

Next we add the keyfile to the root partition

root #cryptsetup luksAddKey /dev/sda2 /root/secretkey

Finally we open the root partition

root #cryptsetup open -d /root/secretkey /dev/sda2 luks-1

Create filesystem, mountpoints and subvolumes

Now we format the mapped partition.

root #mkfs.btrfs -L BTROOT /dev/mapper/luks-1

Next we create the mountpoints, mount the filesystems and create subvolumes

root #mkdir /mnt/btrfsmirror
root #mount -t btrfs -o defaults,noatime,compress=lzo,autodefrag /dev/mapper/luks-1 /mnt/btrfsmirror
root #mkdir /mnt/newroot
root #btrfs subvol create /mnt/btrfsmirror/activeroot
root #mount -t btrfs -o defaults,noatime,compress=lzo,autodefrag,subvol=activeroot /dev/mapper/luks-1 /mnt/newroot
root #mkdir /mnt/newroot/home
root #mkdir /mnt/newroot/boot
root #btrfs subvol create /mnt/btrfsmirror/home
root #btrfs subvol create /mnt/btrfsmirror/boot
root #mount -t btrfs -o defaults,noatime,compress=lzo,autodefrag,subvol=home /dev/mapper/luks-1 /mnt/newroot/home
root #mount -t btrfs -o defaults,noatime,subvol=boot /dev/mapper/luks-1 /mnt/newroot/boot

Hot copy /

We'll now mount our running system onto /mnt/rawroot and copy everything over.

root #mkdir /mnt/rawroot
root #mount --bind / /mnt/rawroot
root #cd /mnt/rawroot
root #tar cvpf - . | (cd /mnt/newroot; tar xpf -)


Enter the chroot

We now need to chroot into /mnt/newroot and do the usual chroot stuff.

root #cp -L /etc/resolv.conf /mnt/newroot/etc/
root #mount -t proc proc /mnt/newroot/proc
root #mount --rbind /sys /mnt/newroot/sys
root #mount --rbind /dev /mnt/newroot/dev
root #mount --make-rslave /mnt/newroot/sys
root #mount --make-rslave /mnt/newroot/dev
root #chroot /mnt/newroot /bin/bash
root #env-update && source /etc/profile
root #export PS1="(chroot) $PS1"


Check that /etc/mtab contains the following lines and if not, add them:

FILE /etc/mtab
/dev/mapper/luks-1 / btrfs rw,noatime,compress=lzo,autodefrag,subvol=activeroot 0 0
/dev/mapper/luks-1 /home btrfs rw,noatime,compress=lzo,autodefrag,subvol=home 0 0
/dev/mapper/luks-1 /boot btrfs rw,noatime,compress=lzo,autodefrag,subvol=boot 0 0

Next change /etc/fstab to this:

FILE /etc/mtab
LABEL=BTROOT    /mnt/btrfsmirror    btrfs    defaults,noatime,compress=lzo,autodefrag    0 0
LABEL=BTROOT    /                   btrfs    defaults,noatime,compress=lzo,autodefrag,subvol=activeroot    0 0
LABEL=BTROOT    /home               btrfs    defaults,noatime,compress=lzo,autodefrag,subvol=home    0 0
LABEL=BTROOT    /boot               btrfs    defaults,noatime,subvol=boot    0 0

Remove md array configuration

Edit /etc/mdadm.conf and remove your array from it.

Build kernel and initramfs

Now we'll create the kernel with the required configuration.

root #genkernel --luks --btrfs --oldconfig --save-config --menuconfig --install --bootloader=grub2 --udev all
KERNEL Enabling device mapper and crypt target
[*] Enable loadable module support
    Device Drivers --->
        [*] Multiple devices driver support (RAID and LVM) --->
            <*> Device mapper support
            <*> Crypt target support
KERNEL Enabling cryptographic API functions for the cipher you used
[*] Cryptographic API --->
    <*> SHA224 and SHA256 digest algorithm
    <*> XTS support
    <*> AES cipher algorithms
    <*> AES cipher algorithms (x86_64)
KERNEL Enabling initramfs support
General setup  --->
    [*] Initial RAM filesystem and RAM disk (initramfs/initrd) support


Next step is to create a new intitramfs

root #dracut -f -I /root/secretkey

and we just replace the initramfs create by genkernel with the one from dracut.


Customize grub2

We'll change /etc/default/grub to fit our needs.

For that we'll add rd.luks, rd.luks.key and rd.luks.uuid to GRUB_CMDLINE_LUNUX and add GRUB_ENABLE_CRYPTODISK=y as new line somewhere.

The rd.luks.uuid is the uuid of the encrypted (!!) partition in this case /dev/sda2.

Now /etc/default/grub should look like this (i use systemd!):

FILE /etc/default/grub
GRUB_CMDLINE_LINUX_DEFAULT="real_init=/usr/lib/systemd/systemd rd.luks=1 rd.luks.key=/root/secretkey rd.luks.uuid=luks-e57c4e30-7b2e-457a-af9b-3270d085aae2"

Generate grub.cfg

We'll use grub2-mkconfig to generate the grub.cfg

root #grub2-mkconfig -o /boot/grub/grub.cfg

Install grub2 into MBR

root #grub2-install /dev/sda

Finishing first disk

That's it for the first drive. Now we leave the chroot.

Install grub2 into MBR of /dev/sdb if not already done to be able to reboot into the old system in case of problems.

Now reboot.

Booting from encrypted disk

Make sure you boot from /dev/sda !!

You'll be asked for the password to unlock the boot partition and after that it should boot up as normal (without further password request!)

Preparing second disk

Dealing with the software raid - Part 2

We'll now completely stop and destroy the MD array.

root #mdadm --stop /dev/md1
root #mdadm --zero-superblock /dev/sdb

Partition the drive

We create a bios boot partition and use the remaining space for the root partition.

root #gdisk /dev/sdb
Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048            8191   3.0 MiB     EF02  BIOS boot partition
   2            8192       250069679   119.2 GiB   8300  Linux filesystem

Encrypt partition

First we'll format the partitions

root #cryptsetup luksFormat -s 512 -c aes-xts-plain64 /dev/sdb2

Next we add the keyfile to the root partition

root #cryptsetup luksAddKey /dev/sdb2 /root/secretkey

Finally we open the root partition

root #cryptsetup open -d /root/secretkey /dev/sdb2 luks-2

BTRFS Raid 1

Now we add the second disk to the btrfs filesystem and convert it to raid1.

root #btrfs device add /dev/mapper/luks-2 /mnt/btrfsmirror
root #btrfs balance start -dconvert=raid1 -mconvert=raid1 /mnt/btrfsmirror


Recreate the intitramfs with dracut and replace the old one.

root #dracut -f -I /root/secretkey


Customize grub2

Append rd.luks.uuid for /dev/sdb2 to GRUB_CMDLINE_LINUX_DEFAULT in /etc/default/grub

FILE /etc/default/grub
GRUB_CMDLINE_LINUX_DEFAULT="real_init=/usr/lib/systemd/systemd rd.luks=1 rd.luks.key=/root/secretkey rd.luks.uuid=luks-e57c4e30-7b2e-457a-af9b-3270d085aae2 rd.luks.uuid=luks-26ab8aed-7c84-4993-bfe4-579c83c96b05"

Generate grub.cfg

Recreate the grub.cfg

root #grub2-mkconfig -o /boot/grub/grub.cfg
The current grub2-mkconfig (time of writing) creates an invalid grub.cfg. There is a bug which cannot handle multiple boot devices (see https://www.mail-archive.com/bug-grub@gnu.org/msg15384.html and https://bugzilla.redhat.com/show_bug.cgi?id=1177470).

grub2-mkconfig will generate the following broken lines in different places:

FILE /boot/grub/grub.cfg
cryptomount -u 26ab8aed7c844993bfe4579c83c96b05
set root='cryptouuid/26ab8aed7c844993bfe4579c83c96b05

You've to search and fix them.

I'm not sure how they should look like, but this is how they look on my working system:

FILE /boot/grub/grub.cfg
cryptomount -u 26ab8aed7c844993bfe4579c83c96b05
cryptomount -u a044adcaa23b400bad6ca256a9509c75
set root='cryptouuid/26ab8aed7c844993bfe4579c83c96b05'
set root='cryptouuid/a044adcaa23b400bad6ca256a9509c75'

Also check the UUIDs are correct!

Install grub2 into MBR

Reinstall on /dev/sda

root #grub2-install /dev/sda

Install on /dev/sdb

root #grub2-install /dev/sdb

Finishing up


Remove obsolete mountpoints

root #rm -rf /mnt/newroot /mnt/rawroot

Backup LUKS headers

You definitely should make a backup of your LUKS headers as you'll not be able to rescue the data if the header gets damaged for some reason.

root #cryptsetup luksHeaderBackup /dev/sda2 --header-backup-file /mnt/usb/luks-header/<UUID of /dev/sda2>.img
root #cryptsetup luksHeaderBackup /dev/sdb2 --header-backup-file /mnt/usb/luks-header/<UUID of /dev/sdb2>.img

Or use this dirty script to find and save all LUKS headers.


if [ -z "$1" ]; then
	echo "Usage:"
	echo $(basename $0) " <destination path>"
	exit 1

for DISK in `ls /dev/sd*`; do
	TYPE=$(blkid -o value -s TYPE $DISK)

	if [ -n "$TYPE" ] && [ "crypto_LUKS" == "$TYPE" ]; then
		UUID=$(blkid -o value -s UUID $DISK)
		cryptsetup luksHeaderBackup "${DISK}" --header-backup-file "${DEST}/${UUID}.img"



At this point you should have a full encrypted and working system.

You should now reboot. Grub will ask you for the password twice as it needs to unlock both disks.

After that you should not be prompted for password input anymore.

That's it. Have fun with your encrypted system!


Systemd asks for password on wall

If systemd asks for the password on wall while your system is already booted up, you should check /etc/crypttab and make sure your root partitions are not listed there. This is because they are already unlocked by dracut and systemd simply doesn't check that and thus tries to unlock them again which fails as the mapping device already exists.

Mapping names are build from uuid

After a reboot your root partitions mapping devices will look like /dev/mapper/<UUID>. If you prefer clean names (i do) like /dev/mapper/luks-1.1 and /dev/mappper/luks1.2 you can achieve this by adding the partitions to /etc/crypttab and include it into the initramfs. But make sure to comment the lines in /etc/crypttab after you have created the initramfs!

Example of /etc/crypttab

FILE /etc/crypttab
luks-1.1 UUID=e57c4e30-7b2e-457a-af9b-3270d085aae2 /root/secretkey luks
luks-1.2 UUID=26ab8aed-7c84-4993-bfe4-579c83c96b05 /root/secretkey luks

Now include it into the initramfs.

root #dracut -f -I /root/secretkey -I /etc/crypttab