2017-03-21

Installing Linux Mint 18/Ubuntu 16.04 with Encrypted ZFS Root

I recently discovered how easy it is to set up Linux Mint 18 onto a machine with a ZFS root partition in a LUKS-encrypted volume. I started reading about it late one night, slept on it, did some more reading the next day, and had a working proof of concept before bed that night.

(I presume the following setup is possible under earlier versions of Mint, but you'd need to add the zfs-native PPA and its package, plus its SPL dependencies, and compile some kernel modules. Remember to properly curate your linux kernel headers. In the Live CD. And then again on the installed system. Good luck. Not all kernels are created equal.)

Mint 18 is based on Ubuntu 16.04 and thus contains built-in ZFS kernel support. No more DKMS shenanigans whenever you attempt a kernel upgrade. Hallelujah!

These steps were assembled from a number of other online tutorials. I've listed them at the bottom for reference and further reading. These tutorials were absolutely invaluable to understanding this process and this howto is only a lightly edited compilation of them. It assumes that you're installing Linux Mint 18.1 XFCE from the ISO. Your actual mileage may vary.

linuxmint-18.1-xfce-64bit.iso has the following checksums:

MD5 = 703e14fef35528143063bf2f9f0f5075

SHA1 = 0bc84cf02f3a89db9ad661ac59ed10ec3b379b0b

SHA256 = 7dc167bd5e6e28641c8aaaf406bba46b1be2d472f85d1d86280ed80e19bb8462

SHA512 = 3d4ac84efb00a3c852b7f3a24e4e6c560772b84e2967f339c654d547f78bd0705734bfdc16a3ef0e770d36933b4cf049e349ac1acdcc513ba9d3142d990aeb30

Caveats

This tutorial requires:

  1. A drive that contains no information you value. Its contents will be entirely overwritten.
  2. A 64-bit CPU and a Ubuntu 16.04-based OS.
  3. A lot of RAM. 4GB is really pushing it. More is better.
  4. A working Internet connection. Ubuntu 16.04 includes ZFS kernel support out of the box, but not the userland tools to use it. Sigh... it's a legal thing.

I've structured this howto thematically, so each step can more or less stand on its own. This is why I don't install everything that will be needed at one time. Doing it this way hopefully adds clarity and improves comprehension of the howto, even if it may not be remarkably efficient. Feel free to adjust the sequence to suit your own needs.

Installing Linux Mint 18 with ZFS root on LUKS

Boot the install ISO. Open a Terminal window and become root. Yes, you will be installing additional software into the Linux Live CD environment, so update your apt sources and make sure your network connection works:

sudo su
# stopping the screensaver is not required, but I just don't want it running:
killall xscreensaver
apt update

Determine your root disk. This tutorial will assume it's "/dev/sda" and most commands will use this convention. The smarter thing to do is use the disk's UUID and for settings that persist after the install we will do so. The quick and dirty way is to make a simple partition table and remember it.

dmesg
# Find your root disk. In this case, we'll use /dev/sda:
ROOTDISK=/dev/sda

When we want to refer to /dev/sda now, we can use ${ROOTDISK}. To be clear, when we specify /dev/sda1 or /dev/sda2, we can call it ${ROOTDISK}1 or ${ROOTDISK}2 until later on when we start doing chroot stuff.

Make a simple partition table. This tutorial assumes you'll use an MBR partition. (Note: At some point in the future, you'll need to figure out how to do this with a GPT partition and an EFI-enabled bootloader.)

This partition layout is really basic: we make one partition for what will be our /boot partition and a second partition for the entire rest of the disk. Dual-booting is not covered here. (Adding a dedicated swap partition is also beyond the scope of this tutorial. If you want swap on your machine, consider adding it post-install as a ZFS zvol.)

Be very cautious here. We are making irreversible changes to your disk from this point forward. If the drive has data you care about, do not proceed.

dd if=/dev/zero of=${ROOTDISK} bs=1M count=2
/sbin/parted --script ${ROOTDISK} mklabel msdos
/sbin/parted --script --align optimal ${ROOTDISK} mkpart primary  1MiB 513MB  # /boot
/sbin/parted --script --align optimal ${ROOTDISK} mkpart primary 513MB  100%  # crypt
/sbin/parted --script ${ROOTDISK} set 1 boot on

Check that the partition table looks OK.

/sbin/parted --script ${ROOTDISK} print

Create an encrypted LUKS volume on the second partition. If you want a good password, consider digging up your old copy of Yahtzee and rolling a Diceware password. Once the LUKS volume is created, open it.

cryptsetup luksFormat -h sha512 ${ROOTDISK}2
cryptsetup luksOpen  ${ROOTDISK}2 cryptroot

Install the ZFS utilities for Linux and begin creating your zpool and ZFS datasets in the mounted LUKS volume.

A ZFS dataset is an odd construct, something between an LVM logical volume and a file system. ZFS is a physical device management tool, a volume manager, and a file system all at once. Since it is simultaneously both the file system layout on a device and it oversees that same device for the kernel, it doesn't precisely fit into one definition or another. Think of a zpool as the abstracted logical grouping of your devices (like one or more physical disks), and a dataset as the configuration for the files on those devices (like mount points and size quotas). Between your files and the platters on the hard disk itself, there is no Dana. Only ZFS.

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

UPDATE: You will need to install a number of ZFS packages and dependencies to the installer as well as re-installing them to the target OS after you run chroot later in this howto. You can, however, save the .deb files and re-use them for both targets. If you intend to do this to multiple machines, this could save you time and bandwidth. If you don't want to download the .deb files twice, do this instead of running apt install zfsutils-linux:

Optional .deb file download steps

apt install -y --download-only zfsutils-linux
mkdir ./my-zfs-debs
cp -p /var/cache/apt/archives/*.deb ./my-zfs-debs
# scp -r ./my-zfs-debs my@backup:~/backup-location # export .deb files to a remote location
dpkg -i ./my-zfs-debs/*.deb # manually install from local disk

Because the / partition is already mounted, you will get an error running the last "zfs create" command. It's OK to proceed. You only need one mountpoint for ZFS to work, but you may optionally set up more fine-grained mount points now. A separate home dataset is probably a good idea:

Optional mount point:

zfs create -o mountpoint=/home zmint/home
zfs list

When the ZFS datasets are created how you want them, export the zpool and reimport it under the /mnt directory:

zpool export -a
zpool import -R /mnt zmint

Format the /boot partition. Linux needs to boot something when the machine starts, and it needs to be both (1) unencrypted and (2) not ZFS. The only open source operating systems I know that don't need an unencrypted bootstrap component to support full disk encryption are FreeBSD 10+ and OpenBSD. Traditionally, if your Linux boot data isn't on ext2 or ext3, you're asking for trouble. Some people will insist that it's OK to format /boot as an ext4 file system. We have a word for people like that: "youths". Or some days, "whippersnappers". I prefer ext3 because (1) it's got journaling and (2) that journaling can be turned off and you can just go back to treating it like ext2 in a pinch.

UPDATE: I've learned how to encrypt /boot. Details are provided in a companion tutorial that you can reference in addition to this one.

mkfs.ext3 ${ROOTDISK}1
mkdir /mnt/boot
mount -o noatime ${ROOTDISK}1 /mnt/boot

Install Linux Mint 18. There are a number of ways to do this, but the one I really enjoyed learning about is to skip the Ubiquity GUI and just extract the base system layout onto /mnt with 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`

I hear debootstrap may also work. I like to time the unsquashfs command because I've never installed Linux in under 2 minutes with a GUI. Technology. Ain't it grand?

Make some additional mounts and then chroot into your fledgling Mint system to do configuration:

cd /
for i in /dev /dev/pts /proc /sys; do mount -B $i /mnt$i; done
chroot /mnt /bin/bash --login

Set up /etc/fstab. You will need to add an entry for /boot and for the decrypted ZFS root dataset.

Add lines for /boot and /dev/mapper/cryptroot. Instead of using /dev/sda, find the UUID of your device and use it here. UUIDs are unique and will save you headaches in the future if you ever have to swap drives, SATA cables, or motherboards. Since you mounted the Live CD's /dev partition, take a look for what UUID maps to /dev/sda1:

ls -l /dev/disk/by-uuid

Note the symlink for ../../sda1, a hexadecimal string like "702fec05-5da3-449e-9943-a7b546883fa2". Use it to refer to your /boot partition in /etc/fstab:

vi /etc/fstab
# Add these lines, adjusting your UUID value appropriately:
# UUID=702fec05-5da3-449e-9943-a7b546883fa2 /boot ext3 defaults,noatime 0 2
# /dev/mapper/cryptroot                     /     zfs  defaults         0 0

Make sure your Mint system will correctly identify your LUKS volume when it boots. Find the UUID for ../../sda2 and add it to /etc/crypttab. For this example, let's say the UUID for /dev/sda2 is b43e9b90-3b4c-4ac0-9969-073b66dd7d73:

ls -l /dev/disk/by-uuid
echo cryptroot UUID=b43e9b90-3b4c-4ac0-9969-073b66dd7d73 none luks,discard >> /etc/crypttab

This is basic setup stuff:

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 and a locale.

cd /usr/lib/ubiquity/user-setup
./user-setup-ask
./user-setup-apply

locale-gen en_US.UTF-8
cd /usr/lib/ubiquity/localechooser
./localechooser
./post-base-installer

We already installed the ZFS utilities, but that was on the Live CD and they will not persist after a reboot. Install them a second time onto the new system. Canonical lawyers, Oracle lawyers, OpenZFS leadership team... please fix this.

apt update
apt install -y zfsutils-linux

Optionally, if you chose to download the .deb files for the zfsutils-linux package, you can install them now, either by mounting the ./my-zfs-debs dir to somewhere under /mnt, or by copying them from your backup location and doing a dpkg -i ./my-zfs-debs/*.deb.

Prepare a ZFS-enabled initramfs:

apt install -y zfs-initramfs
cp -v -p /lib/udev/rules.d/60-zpool.rules /etc/udev/rules.d/
update-initramfs -c -k all

update-initramfs looks like it gets run when zfs-initramfs gets initially installed, I'm not totally sure. I run it manually and it seems to not hurt anything.

Edit your grub config. Specifically, find /etc/default/grub and change the GRUB_CMDLINE_LINUX_DEFAULT line:

vi /etc/default/grub
# change: 
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"
# to: 
GRUB_CMDLINE_LINUX_DEFAULT="boot=zfs quiet"

I have had limited success with the Linux Mint splash screen on a ZFS-root system. Command-line prompts for the decryption password work OK. Leave in the "splash" argument if you're feeling lucky.

Before you update grub, make sure that /dev/mapper/cryptroot is symlinked to /dev/cryptroot. Otherwise, it will throw an error.

ln -s /dev/mapper/cryptroot /dev/cryptroot

You have to do this once by hand, but you can set it up to be automatic every time the machine restarts. Do this now so you don't forget after a reboot and potentially mess up your next apt upgrade:

echo 'ENV{DM_NAME}=="cryptroot", SYMLINK+="cryptroot"' > /etc/udev/rules.d/99-cryptroot.rules

Update and install the new grub config, presumably to /dev/sda:

update-grub
grub-install /dev/sda

Exit the chroot, unmount all your /mnt mounts, export your zpool, 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

Eject the install media and test your new Mint machine. From here, you're on your own. Patch your system, make your first ZFS snapshots, and start customizing it for your needs. Congrats!

By the way, if you want ZFS without this hassle, FreeBSD supports root ZFS in the default installer and has for a while now. There's even a book I recommend.

Notes

Different tutorials on this subject insist that the names of the LUKS volume or the ZFS zpool (which I called "cryptroot" and "zmint", respectively) are hard-coded and must be the exact strings "cryptroot" and "rpool". I tested this with different values for each and it still worked. You can call your LUKS volume "bob" for all it seems to matter. Just don't call your zpool "tank". It will still work, but it's cliché and you'll just make Allan Jude sad.

External References and Resources

https://github.com/zfsonlinux/pkg-zfs/wiki/HOWTO-install-Ubuntu-16.04-to-a-Whole-Disk-Native-ZFS-Root-Filesystem-using-Ubiquity-GUI-installer

https://morph027.gitlab.io/post/ubuntu-zfs-luks/

https://gotechnies.com/howto-install-ubuntu-16-04-native-zfs-root-filesystem/

https://janweitz.de/article/creating-a-zfs-zroot-raid-10-on-ubuntu-16.04/

http://www.larsko.org/ZfsUbuntu

http://www.makethenmakeinstall.com/2014/10/zfs-on-linux-with-luks-encrypted-disks/

https://help.ubuntu.com/community/encryptedZfs

https://wiki.archlinux.org/index.php/GNU_Parted

https://github.com/zfsonlinux/pkg-zfs/wiki/HOWTO-use-a-zvol-as-a-swap-device

No comments: