linguistics, computers, and puns

Portable, Encrypted USB Drives with ZFS

Tim Hammerquist December 21, 2023 #bsd #linux #macos #zfs

FAT16 and FAT32 and their Flash-friendly cousin exFAT are all well and good for boot partitions or hauling PDFs down to the printers, but what if you could use a modern, full-featured filesystem like ZFS to portably share files between operating systems?

Thanks to the work of OpenZFS, this is well within reach — with just a bit of legwork.

What is ZFS?

OpenZFS is an effort to port and maintain Sun Microsystems' CDDL-encumbered ZFS filesystem.

Having acquired Sun Microsystems, Oracle ships ZFS support with its Solaris and Oracle Enterprise Linux operating systems.

FreeBSD has made ZFS a first-class citizen in its operating system, while several Linux distributions — like Ubuntu Desktop — have begun shipping ZFS support for their kernels.

OpenZFS also makes packages and source available for many more distributions of Linux, some versions of macOS, and Windows.

What You'll Need

Identifying the Drive

The easiest way to identity the drive on Linux or FreeBSD will probably be to examine the dmesg(8) output immediately after attaching the USB drive. It should describe the type of device, possibly the manufacturer/model, as well as the specific /dev/XXX the kernel assigns to it.

Partitioning

While ZFS will happily use a raw, unpartitioned storage device, we get a couple bonuses if we can spare a few sectors for a GPT ("GUID Partition Table").

Namely you get a label giving the partition a unique and possibly even human-friendly name. The partition type listed in the GPT also helps the host operating system find and identify any zpools available to import.

In FreeBSD this partitioning looks like:

#             (1)
gpart create -s gpt /dev/XXX
#           (2)       (3)           (4)
gpart add -a 1m -l portapool -t apple-zfs /dev/XXX

Here's the same process using sgdisk(8) in Linux:

#      (1)   (2)              (3)          (4)
sgdisk -og -n1:2048:0 -c 1:portapool -t 1:bf01 /dev/XXX

Regardless of which OS/tool you use:

  1. Creates a GPT on the drive
  2. Adds a single, full-disk partition aligned on 1MB boundaries
  3. Labels the single partition for ZFS with the intended zpool name.
  4. Assigns the apple-zfs partition type

An apple-zfs partition type should work in Linux, FreeBSD, and Solaris, and is appropriate for use as a portable (non-root) zpool.

Compatibility

Left to its own devices, zpool create will enable every default feature it knows about on the new pool. But if we plan to share this pool across multiple systems, we'll want to restrict these features to those that we can be confident any of our target systems will support.

For this, ZFS offers compatibility feature sets. In this case we'll use the 2021 feature set — a set of all the features expected to be available across all ZFS implementations as of the year 2021. To see which specific features are available in this feature set, you can view the contents of /usr/share/zfs/compatibility/2021.

zpool create \
    -o compatibility=2021 \
    -O compression=lz4 \
    portapool /dev/XXX

Et voilà. The top-level dataset of the new pool should be automatically imported and mounted at /portapool. Type zfs mount to confirm that portapool has been imported and mounted.

Creating the pool also creates the default dataset, so you can start using it immediately. Just be sure to export the zpool before disconnecting the drive!


Bonus: Encryption

Several modern operating system offer storage encryption, like FileVault on macOS, BitLocker on Windows 11, and LUKS on Linux. However, most of these are only compatible with the originating operating system.

For example, Windows won't decrypt a LUKS-encrypted volume, and macOS won't be able to access your BitLocker-encrypted data. LUKS isn't proprietary, but neither Windows nor macOS provide any support for it.

ZFS offers native encryption support that isn't bound to any specific operating system, and this encryption is available on any block device that holds a zpool, so long as the ZFS support on the system is recent enough.

The steps below will create a non-encrypted zpool with a separate, encrypted ZFS dataset.

Creating an encrypted zpool

Before continuing, note that encryption is not technically a feature of the 2021 feature set.

However, it is widely available on all recent versions of OpenZFS tooling/drivers, and the steps below have been tested and confirmed on FreeBSD 13, Fedora 38, Ubuntu 23.04, and the drives created have been confirmed to be accessible on macOS Ventura (10.13) as well.

Because encryption is not in the 2021 feature set, you'll get a warning at pool creation about enabling it, but it won't prevent you from doing it.

  zpool create \
    -o compatibility=2021 \
    -o feature@encryption=enabled \
    -O compression=lz4 \
    -O encryption=on \
    -O keyformat=passphrase \
    -O keylocation=prompt \
    "$POOL" "$VDEV"

Here you'll notice several additional arguments.

Note that the use of encrypted datasets make mounting of ZFS zpools/datasets basically a manual process.

Specifically, pass the -l flag when importing the drive so ZFS will request any keys for encrypted datasets.

Otherwise, there's a chance that encrypted datasets may go unmounted, and you may end up accidentally writing data to an unencrypted dataset!

Conclusion

You can take your USB drive and use it with any macOS, Linux, FreeBSD, or Windows machine that has a recent version of OpenZFS installed.

If you followed the instructions for encryption, the drive will also be unreadable by anyone without access to the encryption passphrase.

ZFS datasets provide an immense level of power and flexibility; far more powerful, safe, and efficient than ExFAT, the only commonly available filesystem for this selection of operating systems.