Setting up ZFS on LUKS
Native Encryption
ZFS now has native support for encryption. This has many advantages over ZFS on LUKS including multi-disk, encrypted zfs send, portable across *BSD/Linux and others.
For a Root on ZFS guide with native encryption, see here.
Introduction
This documentation describes how to set up Alpine Linux using ZFS with a pool that is located in an encrypted partition. To encrypt the partition the Device Mapper crypt (dm-crypt) module and Linux Unified Key Setup (LUKS) is used.
Note that you must install the /boot/
directory on an unecrypted partition to boot correctly.
We'll be using the syslinux bootloader and traditional BIOS booting.
Requirements
- An instance Alpine on a medium other than the one you'll boot from, see official installation guide.
Hard Disk Device Name
The following documentation uses the /dev/sda
device as the installation destination. If your environment uses a different device name for your hard disk, use the corresponding device name in the examples.
Setting up Alpine Linux Using ZFS on Top of a LUKS Partition
To install Alpine Linux in a ZFS pool on top of a LUKS encrypted partition, you cannot use the official installation procedure. The installation requires several manual steps you must run in the Alpine Linux Live CD environment.
Preparing the Installation Environment
Before you begin to install Alpine Linux on the medium you intend to boot from, prepare the installation you already have:
- Update the
apk
cache:
# apk update
- Install the following packages required to set up ZFS and LUKS:
# apk add haveged cryptsetup e2fsprogs syslinux zfs zfs-$(uname -r | rev | cut -d'-' -f1 | rev) # modprobe zfs
- Optionally, start the
haveged
service for unpredictable random numbers used for encryption:
# rc-service haveged start
Creating the Partition Layout
Linux requires an unencrypted /boot/
partition to boot. You can assign the remaining space for the encrypted ZFS pool.
- Start the
fdisk
utility to set up partitions:
# fdisk /dev/sda
- Create the
/boot/
partition:
- Enter
n
→p
→1
→1
→100m
to create a new 100 MB primary partition.
- Enter
- Create the
- Set the
/boot/
partition active:
- Enter
a
→1
.
- Enter
- Set the
- Create the LUKS partition:
- Enter
n
→p
→2
to start creating the next partition. PressEnter
to select the default start cylinder. Enter the partition size. For example,512m
for 512 MB or5g
for 5 GB. Alternatively, pressEnter
to set the maximum available size.
- Enter
- To verify the settings, press
p
. The output should look similar to this:
- To verify the settings, press
Device Boot Start End Sectors Size Id Type /dev/sda1 * 2048 206847 204800 100M 83 Linux /dev/sda2 206848 41943039 41736192 19.9G 83 Linux
- Press
w
to save the changes.
- Optionally, fill the LUKS partition with random values:
# haveged -n 0 | dd of=/dev/sda2
Encrypting the ZFS Partition
- To encrypt the partition which will later contain the LVM PV:
# cryptsetup luksFormat /dev/sda2
- Note: Alpine Linux uses the
en-us
keyboard mapping when prompting for the password to encrypt the partition at boot time. If you changed the keyboard map in the temporary environment, the password you enter during encrypting the partition in this step, may not match the password you will enter during the system boots. - If you prefer setting an individual hashing algorithm and hashing schema:
- To run a benchmark:
# cryptsetup benchmark
- To encrypt the partition using individual settings, enter, for example:
# cryptsetup -v -c aes-xts-plain64 -s 256 --hash sha256 --iter-time 2000 --use-urandom luksFormat /dev/sda2
Consult cryptsetup --help
for options.
Creating the filesystems
- Open the LUKS partition:
# cryptsetup open --type luks /dev/sda2 crypt
Creating the ZFS pool
# zpool create -o ashift=12 -O normalization=formD -O atime=off -m none -R /mnt -O compression=lz4 tank /dev/mapper/crypt
Meaning of the zpool create
options:
Option | Meaning |
---|---|
zpool create | Creating the zpool |
-o ashift=12 | 4K blocks |
-O normalization=formD | Set the default Unicode (UTF-8) normalization to 'formD' |
-O atime=off | Disabling updates to file access time. This reduces writes to disk, but might cause issues with mailers, like mutt .
|
-m none | No mountpoint, as we'll handle this later. |
-R /mnt | Set the altroot to /mnt . It's like a temporary mountpoint for the pool.
|
-O compression=lz4 | Use lz4 compression for the pool. Is generally recommended. |
tank | The pool name. tank will be used in throughout this guide.
|
/dev/mapper/crypt | The path to the block device ZFS will use. |
After completing this, confirm that the pool has been created:
# zpool status
Should return something like:
pool: tank state: ONLINE scan: none requested config: NAME STATE READ WRITE CKSUM tank ONLINE 0 0 0 crypt ONLINE 0 0 0 errors: No known data errors
Creating the required datasets
# zfs create -o mountpoint=none -o canmount=off tank/ROOT # zfs create -o mountpoint=/ tank/ROOT/alpine
Creating optional datasets (feel free to add your own)
# zfs create -o mountpoint=/home tank/HOME # zfs create -o mountpoint=/var/log tank/LOG
Creating the /boot
filesystem
# mkfs.ext4 /dev/sda1
Mounting the /boot
filesystem
- Create
/mnt/boot/
directory and mount the/dev/sda1
partition in this directory:
# mkdir /mnt/boot/ # mount -t ext4 /dev/sda1 /mnt/boot/
Installing Alpine Linux
In this step you will install Alpine Linux in the /mnt/
directory, which contains the mounted file system structure.
- Install Alpine Linux:
# setup-disk -m sys /mnt/
- The installer downloads the latest packages to install the base installation. Additionally, the installer automatically creates the entries for the mount points in the
fstab
file (but we'll have to edit it manually later), which are currently mounted in the/mnt/
directory.
- Note: The automatic writing of the master boot record (MBR) fails in this step. Later, you'll write the MBR to the disk manually.
- To enable the operating system to decrypt the LUKS partition at boot time, create the
/mnt/etc/crypttab
file. Enter the following line into the file to decrypt the/dev/sda2
partition using theluks
module and map it to thelvmcrypt
name:
crypt /dev/sda2 none luks
- Delete the zfs entries in
/mnt/etc/fstab
as ZFS mounts them automagically. Your fstab should look similar to this:
UUID=6b4f2c9c-0a0f-4a8c-a73b-d2b47920ad6f /boot ext4 rw,relatime,stripe=4,data=ordered 0 2
- Edit the
/mnt/etc/mkinitfs/mkinitfs.conf
file and append thecryptsetup
andzfs
module to thefeatures
parameter:
features="ata base ide scsi usb virtio ext4 lvm cryptsetup zfs"
- Rebuild the initial RAM disk:
# mkinitfs -c /mnt/etc/mkinitfs/mkinitfs.conf -b /mnt/ $(ls /mnt/lib/modules/)
- The command uses the settings from the
mkinitfs.conf
file set in the-c
parameter to generate the RAM disk. The command is executed in the/mnt/
directory and the RAM disk is generated using the modules for the installed kernel. Without setting the kernel version using the$(ls /mnt/lib/modules/
) option,mkinitfs
tries to generate the RAM disk using the kernel version installed in the temporary environment, which can differ from the latest one installed by thesetup-disk
utility.
- Edit the
/mnt/etc/update-extlinux.conf
file, set the root ZFS dataset and append the following kernel options to thedefault_kernel_opts
parameter:
root=tank/ROOT/alpine default_kernel_opts="... cryptroot=/dev/sda2 cryptdm=crypt rootfstype=zfs"
- The
cryptroot
parameter sets the name of the device that contains the root file system. Thecryptdm
parameter sets the name of the mapping previously set in thecrypttab
file. Therootfstype
option sets the root filesystem type to zfs.
- Because the
update-extlinux
utility operates only on the/boot/
directory, temporarily change the root to the/mnt/
directory and update the boot loader configuration:
# chroot /mnt/ # update-extlinux # exit
- Ignore the errors the
update-extlinux
utility displays.
- Write the MBR to the
/dev/sda
device:
# dd bs=440 count=1 conv=notrunc if=/mnt/usr/share/syslinux/mbr.bin of=/dev/sda
Unmounting the filesystems
- Unmount
/mnt/boot/
:
# umount /mnt/boot/
- Unmount all zfs filesystems:
# zfs unmount -a
- Export all zfs pools:
# zpool export -a
- Close the
lvmcrypt
device:
# cryptsetup luksClose crypt
- Reboot the system:
# reboot
Troubleshooting
General Procedure
In case your system fails to boot, you can verify the settings and fix incorrect configurations:
- Load the ZFS kernel module:
# modprobe zfs
# zpool import -R /mnt tank # mount -t ext4 /dev/sda1 /mnt/boot
- Verify that you run the steps described in the Installing Alpine Linux section correctly. Update the configuration if necessary.