Ubuntu – How to generate a grub2 menuentry with update-grub for an encrypted zfs root filesystem

bootencryptiongrub2uefizfs

I'm running Ubuntu 18.04 on ZFS 0.8.4 and am playing around with encryption. I'd like to have a menuentry in grub that lets me boot to the encrypted root.

System Setup

A checkmark indicates that there is a corresponding menu entry in grub.

  • aquarium/ds1/u18
    contains my ubuntu 18.04 and has its mountpoint set to /
  • tank/ds1/u18
    is copied from aquarium with the sole difference being that ds1 is encrypted and u18 inherits that property.
  • Windows 10 ✓
    I hope it doesn't have anything to do with my issues though.
  • /dev/nvme0n1p8
    An EXT4-formatted system that is still there from when I switched to the ZFS aquarium

I have an EXT2 partition for /boot and another partition for /boot/efi.

Manual Workarounds

When I select and edit the aquarium entry by replacing root=ZFS=aquarium/ds1/u18 with root=ZFS=tank/ds1/u18 I can boot fine into initramfs where I am prompted for the passphrase. After entering the passphrase, I get my system as expected.

Inside that system (or any other ubuntu system for that matter), I can create a custom entry in /etc/grub/40_custom.

menuentry "Encryptioned Magic 18.04" {
    linux /vmlinuz-5.3.0-51-generic root=ZFS=tank/ds1/u18 ro acpi_backlight=video resume=UUID-OF-SWAP-PARTITION
    initrd /initrd.img-5.3.0-51-generic
}

I'd like to avoid this, because a manual entry means I need to modify it when the UUID of my swap filesystem changes, or something else. I mean, all the reasons why we don't create custom entries for every OS basically also apply here.

Solution Attempt

Ideally, this would use sudo update-grub, but even with the encryption key loaded or even when I am within the encrypted system, it doesn't seem to find it. This log is from running it in aquarium.

$ sudo update-grub
Sourcing file `/etc/default/grub`
Generating grub configuration file ...
Found theme: /boot/grub/themes/poly-dark/theme.txt
Found linux image: /boot/vmlinuz-5.3.0-51-generic
Found initrd image: /boot/initrd.img-5.3.0-51-generic
Found linux image: /boot/vmlinuz-5.3.0-28-generic
Found initrd image: /boot/initrd.img-5.3.0-28-generic
Found Windows Boot Manager on /dev/nvme0n1p2@/EFI/Microsoft/Boot/bootmgfw.efi
Found Ubuntu 18.04.4 LTS (18.04) on /dev/nvme0n1p8
Adding boot menu entry for EFI firmware configuration
done

When I run this same command in the encrypted tank system, I get additionally a message between the second Found initrd image and the Found Windows Boot Manager lines:

device-mapper: reload ioctl on osprober-linux-nvme0n1p10   failed: Device or resource busy
Command failed

The mentioned partition nvme0n1p10 is the single partition where the pool tank with the encrypted dataset tank/ds1/u18 is set up on.

This same error message seems to happen whenever zfs is using a partition when I run update-grub since it opens it for exclusive access, no matter which system I'm logged into. This behaviour is a confirmed bug in os-prober but they call it

not actually failing, just spewing errors during os-prober. We want to clean it up (it is cosmetic).

How can I autogenerate a grub entry for my encrypted zfs dataset?

Best Answer

This is currently still an open issue at zfsonlinux/grub but I have posted a workaround there (and here) thanks to the github user tterpelle:

What we are going to do is grabbing the grub scripts from Ubuntu 19.04 for our Ubuntu 18.04.

  1. Download grub-common_2.04-1ubuntu12_amd64.deb from https://packages.ubuntu.com/eoan/amd64/grub-common/download and extract it with ar x grub-common_2.04-1ubuntu12_amd64.deb.

  2. Get the /etc/grub.d/10_linux_zfs file out of that data.tar.xz. I didn't know the correct way so I just did vim data.tar.xz, opened the file, then did :w 10_linux_zfs.

  3. Move it to /etc/grub.d

  4. Make it executable as it may not be run otherwise.
    sudo chmod +x /etc/grub.d/10_linux_zfs

  5. sudo update-grub

This suffices to generate a correct entry. But we also want not to have any incorrect entries, so we have to replace 10_linux in the same way because they added a few lines that skip generation of zfs entries in there.

   # We have a more specialized ZFS handler, with multiple system in 10_linux_zfs.
   if [ -e "`dirname $(readlink -f $0)`/10_linux_zfs" ]; then
     exit 0
  1. repeat steps 2) to 5) for 10_linux instead of 10_linux_zfs.
Related Question