NetBSD supports full disk encryption with cgd(4).

Disk encryption prevents disclosure of secrets stored on encrypted partitions while the machine is powered off in the event of theft, or if you recycle the disk later. With partial disk encryption, e.g. encrypting just the /home partition but not the / partition in a laptop's internal disk, there may be some secrets stored in unencrypted parts of the disk, such as wifi keys in /etc/wpa_supplicant.conf, IPsec keys in /etc/ipsec.conf or /etc/racoon/psk.txt, or hashed passphrases in /etc/master.passwd. Full disk encryption covers the whole disk, including the / partition.

The mechanism described here still requires one unencrypted partition to boot from. This partition can be on a removable medium such as a USB flash drive, which can be removed after boot.

Note: Disk encryption does not defend against tampering with a disk. See also the NetBSD Guide chapter on disk encryption.

The boot process

  1. The bootloader loads the kernel from the boot partition.
  2. The kernel loads a file system image ramdisk-cgdroot.fs from the boot partition as the initial root file system and starts init(8) from inside it.
  3. init(8) runs the /etc/rc script inside ramdisk-cgdroot.fs to configure cgd(4) volumes, which will prompt for your passphrase.
  4. Once the cgd(4) volumes are configured, the /etc/rc script inside ramdisk-cgdroot.fs mounts the final root file system at /altroot, and instructs init(8) to chroot into it.
  5. init(8) chroots into /altroot, where the final root file system is and the rest of the boot proceeds from there.

The boot partition on disk must contain:

Additionally, there must be a config partition, which is either named cgd.conf in a GPT, or is the disklabel partition wd0a or ld0a, containing;

The cgd.conf file must configure a cgd(4) volume with a partition having the name cgdroot, or a disklabel partition cgd0a.

The boot partition and the config partition may be one and the same, either wd0a or ld0a—this way, you can just use the first partition of a disklabel-formatted internal ATA disk (wd0a) or NVME module (ld0a). Alternatively, you can use a GPT-formatted disk with separate boot and cgd.conf partitions. The boot and cgd.conf partitions may also be on the USB flash drive while the encrypted root partition is on the internal medium; this way, the USB flash drive together with your passphrase are both needed to boot, but as soon as the kernel has started (green text appears), you can remove the USB flash drive.

In practice the memory disk remains the real root, and the regular system is really run from a chroot in /altroot.

Obtaining the ramdisk

The ramdisk-cgdroot.fs image is part of the regular NetBSD releases since NetBSD 10.0 (or 9.4). It can be found in the <arch>/installation/ramdisk directory of the release. For instance, for the amd64 architecture of the 10.1 release, download it at https://cdn.NetBSD.org/pub/NetBSD/NetBSD-10.1/amd64/installation/ramdisk/ramdisk-cgdroot.fs.

Configuring the ramdisk

The ramdisk needs to be available in the boot partition, alongside the desired kernel. The bootloader configuration in /boot.cfg should be modified to load the ramdisk, as in this example:

/boot.cfg
menu=Boot normally:rndseed /etc/entropy-file;fs /ramdisk-cgdroot.fs;boot /netbsd.gz

Building the ramdisk

The ramdisk can be compiled from the source tree for the NetBSD base system, once the distribution has been built. Change to the distrib/<arch>/ramdisks/ramdisk-cgdroot directory and use nbmake-<arch> to build:

src/distrib/amd64/ramdisks/ramdisk-cgdroot$ /path/to/tooldir/bin/nbmake-amd64
[...]
     create  ramdisk-cgdroot/ramdisk-cgdroot.fs
Calculated size of `ramdisk-cgdroot.fs.tmp': 5120000 bytes, 85 inodes
Extent size set to 4096
ramdisk-cgdroot.fs.tmp: 4.9MB (10000 sectors) block size 4096, fragment size 512
        using 1 cylinder groups of 4.88MB, 1250 blks, 96 inodes.
super-block backups (for fsck -b #) at:
 32,
Populating `ramdisk-cgdroot.fs.tmp'
Image `ramdisk-cgdroot.fs.tmp' complete

Caveats

The biggest (known) issue with this setup occurs when firmware needs to be loaded early in the boot process (such as graphics drivers for the console). At the moment they need to be provided as part of the memory disk. Some network interfaces, of which some wireless devices in particular, also require loading firmware to work properly.

Firmware that can be loaded later (e.g. microcode in sysutils/intel-microcode-netbsd package) can be found only if the corresponding paths in the hw.firmware.path sysctl variable are adjusted to start with /altroot, or by adding a symlink in the ramdisk /libdata/altroot/libdata.

It is also possible to boot a Xen DOM0 system with full disk encryption. However, Xen-enabled NetBSD kernels currently do not support loading ramdisks at boot-time. The memory disk has to be placed directly inside the kernel instead (with mdconfig(8) or a new kernel configuration).

It should really be possible to install NetBSD this way with sysinst(8). Unfortunately this is not supported yet.

Customized Boot and unlocking via pkgsrc tools

It is relatively straight forward to customize the ramdisk contents and set up a different process to unlock the cgd root via for example tools form pkgsrc and a FIDO key.

References