Installing Linux Mint 18/Ubuntu 16.04: Encrypted ZFS Root and /boot Partitions
A while ago I cobbled together a spate of howtos into a singular method for installing a root-on-ZFS Ubuntu/Mint system with LUKS. Since then, I've kept reading on the subject and I found a solution for a long-standing problem with Linux/GRUB OSes. I lamented that there wasn't a good way to boot your machine using full-disk encryption, so even if all of your /home and system data is secure when the system is powered off, you still had this pesky /boot partition hanging around out in the open. There's a big Achilles' heel in your nice, secure disk encryption setup if your bootloader and kernel are just sitting ducks.
Turns out, there's a way you can really get full disk encryption with LUKS and GRUB. With a few changes to the previous instructions, we can get a system that runs ZFS on root in a LUKS-encrypted container and get an LUKS-encrypted /boot partition as well.
For stability purposes, the /boot partition will remain ext2/3-formatted. There was a recent mailing list thread crowing about ext2's "worse is better" design philosophy that I found quite entertaining.
The following is a terse set of instructions that skip a lot of explanation. Refer to the previous instructions for further details where desired. All the usual caveats still apply: do not perform these steps on a disk that contains data you care about. Be comfortable with the concepts of using disk partitioning tools and ZFS. Your actual mileage may vary. Not applicable where void by law. Safety not guaranteed. Use at your own risk. This means you.
Boot the Linux Mint 18+ (or Ubuntu 16.04+) ISO. Open a terminal and become root.
sudo su killall xscreensaver
Identify your storage device. This is usually /dev/sda, and we use that as an example in this howto. Wipe the partition table off of this device with dd or, if you prefer, wipefs.
ROOTDISK=/dev/sda wipefs --force --all ${ROOTDISK} # or: # dd if=/dev/zero of=${ROOTDISK} bs=1M count=2
Set up a new partition table on the disk. Create one partition for your encrypted /boot container and one for your encrypted ZFS pool.
/sbin/parted --script ${ROOTDISK} mklabel msdos /sbin/parted --script --align optimal ${ROOTDISK} mkpart primary 1MiB 513MB # encrypted /boot /sbin/parted --script --align optimal ${ROOTDISK} mkpart primary 513MB 100% # cryptroot /sbin/parted --script ${ROOTDISK} set 1 boot on
Our steps now begin to differ from the original howto. Instead of one LUKS container, we create and mount two. I first started experimenting with containers named "cryptroot" and "cryptboot" and you can imagine how quickly that got confusing. Among other things, /boot holds the kernel so I call the LUKS container that will hold /boot "cryptkern" here to reduce confusion.
cryptsetup luksFormat -h sha512 ${ROOTDISK}1 cryptsetup luksFormat -h sha512 ${ROOTDISK}2 cryptsetup luksOpen ${ROOTDISK}1 cryptkern cryptsetup luksOpen ${ROOTDISK}2 cryptroot
Install the ZFS utilities on the live CD session. You can also fetch these .DEB files and store them locally, but that is not covered here.
apt update apt install -y zfsutils-linux zpool create -O mountpoint=none -O compression=lz4 -O atime=off -o ashift=12 zmint /dev/mapper/cryptroot zfs create zmint/root zfs create -o mountpoint=/ zmint/root/mint18 zpool set bootfs=zmint/root/mint18 zmint
Additional ZFS datasets can be created at this point, but this howto skips them for simplicity.
Export the zpool and reimport it under /mnt.
zpool export -a zpool import -R /mnt zmint
Format and mount the /boot partition. Note that the mkfs.ext3 command is formatting the unlocked LUKS container device and not ${ROOTDISK}1, which is the LUKS container itself.
mkfs.ext3 /dev/mapper/cryptkern mkdir /mnt/boot mount -o noatime /dev/mapper/cryptkern /mnt/boot
Install the OS to /mnt. I use unsquashfs.
apt install -y squashfs-tools time unsquashfs -f -d /mnt/ /media/cdrom/casper/filesystem.squashfs cp -v -p /run/resolvconf/resolv.conf /mnt/run/resolvconf/resolv.conf cp -v -p /media/cdrom/casper/vmlinuz /mnt/boot/vmlinuz-`uname -r`
A totally random aside:
There's a lot of banter online about the merits and deficiencies of using /dev/random versus /dev/urandom and which OSes have greater or fewer differences between the two. Even professionals like to bicker about it. Ultimately, I suggest we all start using /dev/arandom. He who fears PRNG-deciphering nation states and loves wearing a tinfoil hat can make his own decisions. Where you value speed, I find haveged to be great at dramatically improving PRNG performance if you trust your machine's rand()-making device. It's less useful here than when creating enormously-sized PGP keys, but it's handy to have around.
# Optional apt install -y haveged haveged
Once you trust your randomness, create a LUKS decryption key file for each container. This prevents us from needing to type decryption passwords for each container every time we boot.
time dd bs=512 count=4 if=/dev/random iflag=fullblock of=/mnt/boot/rootkey.bin time dd bs=512 count=4 if=/dev/random iflag=fullblock of=/mnt/root/kernelkey.bin cryptsetup luksAddKey ${ROOTDISK}1 /mnt/root/kernelkey.bin cryptsetup luksAddKey ${ROOTDISK}2 /mnt/boot/rootkey.bin chmod 0 /mnt/root/kernelkey.bin chmod 0 /mnt/boot/rootkey.bin
Mount important system directories into /mnt and chroot to the new system:
cd / for i in /dev /dev/pts /proc /sys; do mount -B $i /mnt$i; done chroot /mnt /bin/bash --login
Write your fstab:
/dev/mapper/cryptkern /boot ext3 defaults,noatime 0 2 /dev/mapper/cryptroot / zfs defaults 0 0
Configure the system. This section is abbreviated for simplicity. See the original instructions for details.
passwd passwd -u root ln -s /proc/self/mounts /etc/mtab echo myhostname > /etc/hostname echo 127.0.0.1 myhostname >> /etc/hosts dpkg-reconfigure tzdata # add a user account, etc...
Get the list of UUIDs for your system's partitions. You will use these UUIDs for decrypting the LUKS container partitions and in the bootloader.
ls -l /dev/disk/by-uuid
Write your crypttab. If your cryptkern partition is /dev/sda1, use the sda1 UUID for that line in crypttab and so on. crypttab is evaluated from top to bottom, so ordering matters here: I put the cryptroot line at the top and the cryptkern line beneath it, since the key for cryptkern is kept in the cryptroot container.
vi /etc/crypttab # Add these lines: cryptroot UUID=UUIDHERE /rootkey.bin luks,keyscript=/bin/cat cryptkern UUID=UUIDHERE /root/kernelkey.bin luks,discard
Write a hook script for initramfs to get a copy of the key to decrypt cryptroot.
vi /etc/initramfs-tools/hooks/crypto_keyfile # Add these lines: #!/bin/sh cp -p /boot/rootkey.bin "${DESTDIR}"
Make the hook script executable.
chmod +x /etc/initramfs-tools/hooks/crypto_keyfile
Install ZFS and a ZFS-aware initramfs on the installed system.
apt update # again apt install -y zfsutils-linux apt install -y zfs-initramfs cp -v -p /lib/udev/rules.d/60-zpool.rules /etc/udev/rules.d/
Edit /etc/default/grub. This is where you'll point to the encrypted root partition.
vi /etc/default/grub # Make the following changes GRUB_CMDLINE_LINUX_DEFAULT="" GRUB_CMDLINE_LINUX="cryptdevice=UUID=UUIDHERE:cryptroot" GRUB_ENABLE_CRYPTODISK=y
Note that you're NOT putting the cryptkern UUID and LUKS container here. Your boot sequence will be:
- Power on
- Get password for cryptkern
- Load initramfs
- initramfs loads key for cryptroot from /boot
- Mount /
- System loads key to mount cryptkern from /root
You could use passwords for all of your LUKS containers if you really wanted to do so, but keys are quicker and relatively safe if you can wrap you head around this ping ponging of device decryption.
Symlink your cryptroot device under /dev so updating the bootloader won't throw an error.
ln -sf /dev/mapper/cryptroot /dev echo 'ENV{DM_NAME}=="cryptroot", SYMLINK+="cryptroot"' > /etc/udev/rules.d/99-cryptroot.rules
Update your initramfs and bootloader.
update-initramfs -c -k all update-grub grub-install /dev/sda
Quit the chroot, ummount everything, and reboot.
exit umount /mnt/dev/pts umount /mnt/dev umount /mnt/proc umount /mnt/sys umount /mnt/boot zfs umount -a zpool export -a reboot
If all goes according to plan, after POST you'll get a screen like this:
Attempting to decrypt master key... Enter passphrase for hd0.msdos1 (a9e29f6295bc49919d5ed7820f941974):
That's hd0 (your disk), msdos1 (your MBR partition table), and as an example, a9e29f6295bc49919d5ed7820f941974, which corresponds to the UUID of your cryptkern LUKS container as per /dev/disk/by-uuid. If you type your password correctly, you'll see this:
Slot 0 opened
And your boot sequence will proceed as intended. When necessary to decrypt each LUKS container, the boot sequence will use to the keys you specified in /etc/crypttab and not prompt for manual input.
External References and Resources
http://www.pavelkogan.com/2014/05/23/luks-full-disk-encryption
No comments:
Post a Comment