This guide describes the process of setting up full-disk encryption on NetBSD 9.1.
The setup works in the following way:
- A small boot partition is used to boot the kernel with a special kernel module containing an initial ramdisk
- The ramdisk executes an unlock script which mounts the boot partition that also contains CGD configuration
- The unlock script opens all defined encrypted volumes and then mounts cgd0a on /altroot
- The ramdisk then chroots into /altroot and continues init
This guide describes the process for a BIOS-booted GPT-formatted disk with a single OS installation: NetBSD 9.1. However, it should be possible to adapt this guide to other configurations - see the Endnotes for tips.
A moderate familiarity with computers and Unix-like systems is assumed.
It is also assumed that you are starting from a clean disk. If that is not the case, you can use
gpt destroy to make it so.
If you need to reference any of the commands used here, feel free to search online NetBSD manpages.
A Note on GPU Firmware
Unfortunately, as I found out later when installing on real hardware, this setup doesn't work very well with GPUs. The kernel initializes GPUs relatively early in the boot phase (before init) and has to load the GPU firmware. Said firmware is not present on the default ramdisk, and so the boot fails.
There are two solutions: compiling your own ramdisk with the firmware included (which I've been so far unable to do), or disabling the driver (with performance and usability impact). For the latter option, add something like this to your boot.cfg:
userconf disable radeon (substitute "radeon" for the name of your driver).
The bootloader and kernel are still stored on an unencrypted partition. Depending on your threat model this may pose a problem, so take care with mitigating physical access risks.
Likewise, the encryption parameters for the encrypted volume are stored out in the open. If you care about plausible deniability, you may want to think really hard about this fact.
This guide is in part based on other excellent guides found here and on the NetBSD site.
GPT/UEFI Installation by JuvenalUrbino
Root Filesystem Encryption on NetBSD Wiki
Installation on UEFI systems on NetBSD Wiki
The CGD Driver chapter from NetBSD Handbook
Well you have to boot from something, don't you? I used the CD.
After getting to the main installer menu, drop into shell with Utility menu -> Run /bin/sh.
You may want to switch to a more user-friendly shell as well, like /bin/ksh.
Check your disk names with
sysctl hw.disknames. Mine is called "wd0", which is the first IDE/SATA disk. Alternatively, there may be sd* disks (SCSI and USB external), and ld* disks (NVMe, SD card, virtio, etc.).
Create a new GPT table and two partitions.
The boot partition is labeled "cgd.conf" because that's how the unlock script picks it out.
The second partition can be labeled whatever you wish, as long as you remember it, and as long as it's not "cgdroot" (see Endnotes for details).
gpt create -f wd0
gpt add -a 2m -s 128m -t ffs -l cgd.conf wd0
gpt add -a 2m -t cgd -l syscgd wd0
Due to how BIOS booting works, we have to mark the boot partition as bootable. Additionally, the
-A option activates the Protective MBR, which is necessary for older PCs whose firmware doesn't understand GPT.
gpt biosboot -A -i 1 wd0
Along with creating the partitions, you probably got green kernel messages stating some dk* wedges got created as well. On NetBSD, each GPT partition has a corresponding dk wedge that's used to address it - compare with the "wd0e" naming of the MBR/disklabel scheme. You can check what wedges you have with the same sysctl command, or
dkctl wd0 listwedges. Mine are dk0 and dk1 for the boot and encrypted partitions, respectively.
First a small tangent. We have to format and mount the boot partition because that's where the encryption parameters will be stored. So create a new FFSv2 filesystem and mount it to /etc/cgd, because that's where cgdconfig expects it to be.
newfs -O 2 dk0
mount /dev/dk0 /etc/cgd
Now for the actual encrypted volume setup. The volume will contain a disklabel so we're using that as verification method. I like the aes-xts encryption with a 512-bit key, but you can choose whatever you want. The following command merely saves the parameters into a file for later use.
cgdconfig -g -V disklabel -o /etc/cgd/syscgd aes-xts 512
Since the volume does not contain the disklabel yet, we have to override it for the moment. NAME is the label that you chose when you created the GPT partition. Mine is "syscgd".
cgdconfig -V re-enter cgd0 NAME=syscgd /etc/cgd/syscgd
The passphrase you just got asked for (twice) is how you'll be unlocking the volume from this point on, so I really hope you selected a good one.
And now create the actual disklabel.
disklabel -Ii cgd0
-i option starts an interactive mode that's easy to use (and you can get help with "?").
To edit the "a" partition enter "a" and so on.
Valid partitions are a-p, and remember that the first four have a special meaning:
- "a" is the root partition, and in fact gets mounted as / by the unlock script.
- "b" is traditionally the swap partition, so use it as such. Partition type is "swap".
- "c" is the space addressed by the MBR table on x86 and AMD64. Not present here because we don't have an MBR table on the CGD volume. On other architectures this is the whole disk.
- "d" addresses the whole disk on x86/AMD64. You better leave this one alone.
- The rest doesn't have any special meaninig. I usually use e-h as /usr, /var, /tmp, and /home. Partition type is "4.2BSD" just like "a".
After you're happy with your edits, write the label with "W" and exit with "Q".
Now we're going to check if the CGD volume is configured correctly. First unconfigure (that means close) the cgd0 volume:
cgdconfig -u cgd0
Next the cgd0 volume, its dk wedge, and the encryption parameters file are added to the cgd configuration. (Again, substitute "syscgd" for however you labeled your encrypted wedge.)
echo 'cgd0 NAME=syscgd /etc/cgd/syscgd' > /etc/cgd/cgd.conf
And now we open it just how the unlock script would and hope for the best.
If everything went well, you should have the cgd0 volume open and the disklabel you have created is visible with
We'll let the installer take care of the boring parts - that is extracting sets and configuring the various minutiae of the system.
Exit the shell and start the installation like you normally would.
Select the cgd0 volume for installation, and Use existing disklabel partitions.
Fill out the mountpoints for the various partitions, and switch FFS to FFS2 where you can for better performance. You also have to check the newfs and mount options and I think.
When asked about bootblocks, select Use existing bootblocks.
Select full installation, and after the sets get unpacked configure the timezone, and the network, and all the other stuff.
When that's done and you're back in the main installer menu, drop back into shell with Utility menu -> Run /bin/sh.
Kernel and Bootloader
The last part of the configuration.
Mount root back to /mnt and copy the boot.cfg config file and the netbsd kernel to the boot partition (hopefully) still mounted on /etc/cgd.
mount /dev/cgd0a /mnt
cp -p /mnt/boot.cfg /mnt/netbsd /etc/cgd
Next copy over the kernel module that contains the initial ramdisk. (Substitute amd64 for whatever other architecture you're using.)
cp /amd64/installation/miniroot/cgdroot.kmod /etc/cgd
Finally, copy over the second-stage bootloader as well.
cp -p /usr/mdec/boot /etc/cgd
The only thing left now is to install the primary bootloader into the boot partition and adjust the configuration so the module gets loaded first. (Note that the first-stage bootloader name depends on which filesystem the second stage is stored on - in our case it's FFSv2. Also this step is very architecture-specific.)
installboot -v -o timeout=5 /dev/rdk0 /usr/mdec/bootxx_ffsv2
In /etc/cgd/boot.cfg edit the first menu entry so that it has "load cgdroot.kmod" before the boot command. It should look something like this:
menu=Boot normally:rndseed /etc/entropy-file;load cgdroot.kmod;boot
And that's it, you're done!
If you want to do some light cleanup then unmount /mnt and /etc/cgd, and unconfigure (close) the encrypted volume with
cgdconfig -u cgd0.
Then exit the shell and reboot.
If everything went well you should be asked for your passphrase during the boot and then the init continues until you get a login screen.
- On CURRENT the unlock script is a bit more intelligent, and tries to mount root from a "cgdroot" labeled dk wedge. That means you can nest another GPT table inside of the cgd0 volume if you don't like disklabels for some reason.
- Even on 9.1, the unlock script tries to mount the boot partition from wd0a and ld0a in case it doesn't find a dk wedge labeled "cgd.conf". This means you can use an MBR/disklabel partitioning scheme on the physical disk, if you're into that sort of thing.
- LVM is possible, but not for root (on 9.1). Use cgd0a for a small root partition (~512 MB), and the rest should go to cgd0e which you can format as a Physical Volume.
- UEFI looks like it should work, but who knows really. With UEFI you never know for sure. In any case, the bootloader installation will differ, but it seems possible to use the EFI partition as the unencrypted boot partition with kernel, ramdisk, and cgd config. Something, something, future work.
- Dual- and multi-booting different systems should work fine as well, as long as you satisfy the criteria of how the unlock script finds the boot partition with CGD configuration. You should use a GPT-formatted disk, and as long as there is only one dk wedge named "cgd.conf" you should be fine.
- Multiple encrypted NetBSD setups only seem possible if you edit the unlock script and make your own initial ramdisk. The default one will, necessarily, always open and mount the same partition.
- You can see the unlock script here. 1.4 is the version in NetBSD 9.1, and 1.5 is in CURRENT.