Floating Octothorpe

Building a NAS: part 3 - ZFS

In the previous post in this series I covered installing Ubuntu server and encrypting the OS disk. In this post I'm going to go over setting up an encrypted ZFS storage pool and file system for the NAS.

Installing ZFS utils

Unlike many Linux distributions, ZFS is available directly from the Ubuntu repositories, and can easily be installed with apt:

sudo apt-get update
sudo apt-get install zfsutils-linux

If everything goes well the zfs and zpool commands should be available to setup and manage a new storage pool:

$ zfs version
zfs-0.8.3-1ubuntu12
zfs-kmod-0.8.3-1ubuntu12

Creating a new pool

All data in ZFS is allocated from a storage pool. The pool is made up of one or more vdevs, in this instance the vdev is backed by a single disk. Datasets such as ZFS file systems or volumes are then created from the storage pool:

ZFS layout diagram

Note: there are additional vdev types such as raidz, mirrors, and cache; the OpenZFS docs have more info if you're curious.

A storage pool can be created with the following steps:

  1. Identify the disk you want to back the pool with:

    lsblk
    
  2. Create a new storage pool using the block device:

    zpool create -o feature@encryption=enabled mypool /dev/sdc
    

Note: features such as encryption can be enabled using the -o option.

Creating an encrypted ZFS file system

Once a storage pool has been set up it should be online, this can be verified by running zpool list:

$ zpool list
NAME     SIZE  ALLOC   FREE  CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP    HEALTH  ALTROOT
mypool  3.62T   996K  3.62T        -         -     0%     0%  1.00x    ONLINE  -

An encrypted ZFS file system can then be created with the following steps:

  1. Create a directory to store the encryption key:

    mkdir /root/zfs
    chmod 700 /root/zfs
    
  2. Generate a new key for the dataset:

    openssl rand -out /root/zfs/mydataset 32
    chmod 400 /root/zfs/mydataset
    
  3. Create a new dataset using the key:

    zfs create -o encryption=on -o keyformat=raw \
      -o keylocation=file:///root/zfs/mydataset mypool/mydataset
    

If everything goes well the new file system should now be mounted:

$ mountpoint /mypool/mydataset/
/mypool/mydataset/ is a mountpoint

$ zfs list
NAME              USED  AVAIL     REFER  MOUNTPOINT
mypool            660K  3.51T       96K  /mypool
mypool/mydataset  192K  3.51T      192K  /mypool/mydataset

Mounting automatically during boot

Unfortunately after rebooting my NAS I found the file system wasn't mounted! After a bit of investigation it turned out the encryption keys were not loaded when the zfs-mount.service unit file is run. To get around this I created a new unit file to call zfs load-key -a

$ cat /etc/systemd/system/zfs-load-keys.service
[Unit]
Description=Load ZFS encryption keys
DefaultDependencies=no
Before=zfs-mount.service
After=zfs-import.service zfs-load-module.service zfs-import.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/bash -c '/usr/sbin/zfs load-key -a'

[Install]
WantedBy=zfs-mount.service

Finally I enabled the service and restarted to make sure everything mounted correctly:

$ systemctl enable zfs-load-keys.service
Created symlink /etc/systemd/system/zfs-mount.service.wants/zfs-load-keys.service → /etc/systemd/system/zfs-load-keys.service.
$ systemctl reboot

Note: I suspect I could have used ZFS mount generator as an alternative to creating a new ZFS service