This page describes how to setup a special root disk loaded via a USB stick to unlock a CGD root via a fido2 key and a secret bound to that key via fidocrypt(1) from pkgsrc.
Why boot from USB?
The method here can be easily modified to load the ramdisk image from some partition on the harddisk of the notebook instead (or the EFI partition). However, for a "road warrior" notebook setup the external booting is a nice additional feature.
If you chose so, you can leave a Windows installation (that the notebook probably came with) intact and just shrink its main partition as far as you like, then add the extra NetBSD partitions. By default the notebook w/o the special key then would boot into that Windows setup.
Moving the ramdisk image onto the EFI System Partition (or into another NetBSD partition) is easy and allows for setting up full automatic booting instead of the multi-step ("three factor" unlocking authentication) booting described here.
The boot process
The main parts needed to boot and unlock the root CGD partition look like this:
Before powering up (or rebooting) the notbook insert the USB memory stick, then wait until the unlock system has booted:
encoding -> de.nodead NAME=nbsd_root_cgd's passphrase:
At this point remove the USB memory stick and insert the Fido Key instead. (Side note: if they would not be bound together by a key ring and assuming the Notebook has enough USB ports you could, instead, plug in both during the whole boot process.) Now enter the CGD passphrase. The System makes the Fido key blink and asks you to prove you are there:
encoding -> de.nodead NAME=nbsd_root_cgd's passphrase: [kernel messages about removing sd0 and attaching the fido device] tap key; waiting...
If the passphrase was correct as soon as you touch the blinking Fido key the system unlocks the CGD root and boots to multiuser from it. Now you may remove (and safely store) the two unlock devices.
How does it work?
We use the wip/fidocrypt-git package to unlock the CGD. The setup/configuration is documented in the fidocrypt man page. This page is meant to document how to put it all together and create the root ramdisk image and script updates to it.
There are several parts to the secret used to unlock the CGD in the end:
- the passphrase you have to manually enter
- a crypted file stored inside the ramdisk image
- the fido key enrolled in the crypted file
- user presence (the key tapping)
Here is an example of a GPT when leaving the (preinstalled) Windows on the device (with its main partition resized to make space for the additional NetBSD partitions:
# gpt show -a wd0 start size index contents 0 1 PMBR 1 1 Pri GPT header GUID: ed834012-78ff-45c9-8d3b-5d9167ce0d8a 2 32 Pri GPT table 34 2014 Unused 2048 204800 1 GPT part - EFI System Type: efi TypeID: c12a7328-f81f-11d2-ba4b-00a0c93ec93b GUID: a762ee8f-ed9e-46b1-9422-8aa52f5a1889 Size: 100 M Label: EFI system partition Attributes: [0x8000000000000000] 206848 32768 2 GPT part - Windows reserved Type: windows-reserved TypeID: e3c9e316-0b5c-4db8-817d-f92df00215ae GUID: d70d8d86-2bd8-4acf-9c09-e477c9717a3a Size: 16384 K Label: Microsoft reserved partition Attributes: [0x8000000000000000] 239616 252518400 3 GPT part - Windows basic data Type: windows TypeID: ebd0a0a2-b9e5-4433-87c0-68b6b72699c7 GUID: 719856bf-6581-4b5a-96ca-5749137f0c66 Size: 120 G Label: Basic data partition Attributes: None 252758016 2048 Unused 252760064 16777216 5 GPT part - NetBSD swap Type: swap TypeID: 49f48d32-b10e-11dc-b99b-0019d1879648 GUID: 65b3c248-d71a-456e-89f9-7a5c8ea7a75f Size: 8192 M Label: nbsd swap Attributes: None 269537280 229556224 6 GPT part - NetBSD Cryptographic Disk Type: cgd TypeID: 2db519ec-b10f-11dc-b99b-0019d1879648 GUID: 1c1591ed-1fbc-416f-8318-e6304b98aaee Size: 109 G Label: nbsd_root_cgd Attributes: None 499093504 1024000 4 GPT part - Windows recovery Type: windows-recovery TypeID: de94bba4-06d1-4d40-a16a-bfd50179d6ac GUID: 2d02bc12-8433-4e41-b9f6-f8f967b1a93e Size: 500 M Label: Basic data partition Attributes: required, [0x8000000000000000] 500117504 655 Unused 500118159 32 Sec GPT table 500118191 1 Sec GPT header GUID: ed834012-78ff-45c9-8d3b-5d9167ce0d8a
For initial setup you can create a bootable USB memory stick by just using sysinst(8) and adding the fidocrypt package from a binary pkg repository. Then follow the example in the fidocrypt man page to create the secret file.
The final USB memory stick used is a lot simpler. It has the Efi System Partition that sysinst created, and the ffs root partition, but the latter is mostly empty, it just contains:
total 71074 -rw-r--r-- 1 root wheel 71 Dec 9 12:20 boot.cfg -rw-r--r-- 1 root wheel 13541376 Nov 12 19:59 cgdrd.img -rwxr-xr-x 1 root wheel 29576728 Dec 10 20:56 netbsd -rwxr-xr-x 1 root wheel 29574568 Nov 12 21:32 netbsd.old
The kernel (you can use plain GENERIC) and its .old backup are obvious, cgdrd.img is the ramdisk image used as root filesystem and boot.cfg is the magic to plug it all together.
Populating the ramdisk image
To be able to update the ramdisk contents easily (and because you will need a secure backup as all content on my CGD root disk will be lost if you loose it) we create a staging directory of its content on a secure machine. Below are two simple scripts, one to update the ramdisk contents and one to rebuild the filesystem image (cgdrd.img).
Let's start with the content update script, which needs to run first:
#! /bin/sh SETS=/tmp/downloads/sets PKGS=/tmp/x86_64/All cd root || exit 1 tar xpzf "${SETS}/base.tar.xz" ./dev/\* ./bin/sh ./sbin/init \ ./sbin/fsck_ffs ./sbin/cgdconfig ./sbin/mount_ffs \ ./sbin/sysctl ./sbin/wsconsctl \ ./lib/libc.\* ./lib/libpthread\* ./lib/libm.so.\* \ ./lib/libedit.\* ./lib/libterminfo.\* ./lib/libcrypt.so.\* \ ./lib/libz.so.\* ./lib/libpthread.so.\* ./lib/libcrypto.so.\* \ ./lib/libutil.so.\* ./lib/libprop.so.\* \ ./usr/lib/libcrypto.so.\* ./usr/lib/libc.\* \ ./usr/lib/libusbhid.so.\* ./usr/lib/libz.so.\* \ ./usr/lib/libsqlite3.so.\* ./usr/lib/libm.so.\* \ ./usr/lib/libpthread.so.\* \ ./libexec/ld.elf_so ./usr/libexec/ld.elf_so tar xpzf "${SETS}/etc.tar.xz" ./dev/\* ( cd dev && sh MAKEDEV all ) cd .. rm -rf root/usr/pkg/* mkdir -p root/usr/pkg/pkgdb root/usr/pkg/pkgdb.refcount pkg_add -P root ${PKGS}/fidocrypt-* rm -rf root/usr/pkg/include root/usr/pkg/pkgdb root/usr/pkg/pkgdb.refcount \ root/usr/pkg/lib/cmake root/usr/pkg/man root/usr/pkg/share
After this script has run and updated/populated your root/ staging directory you can pack the contents as a filesystem image, so the bootloader can directly load it:
#! /bin/sh makefs cgdrd.img root
Copy this file to the root directory of the ffs partition of the USB stick (as shown in the directory listing above).
The update-contents.sh
script extracts just enough from the distribution sets to allow booting multi-user after unlocking the CGD root.
The other part copied here is a stripped down version of the binary fidocrypt pkg (and its dependencies). The script above removes documentation and other unneeded parts to make the ramdisk size minimal.
CAVEAT: this script as-is does not work with any unpatched NetBSD version so far!
It requires a pkg_add that has the bug from PR #58809 fixed.
Static ramdisk content
Besides the files that are updated by above script, there are a few static files in the ramdisk root
staging directory.
The main part is a simple replacement script for /etc/rc
:
The wsconsctl line should match your keyboard (makes entering the passphrase a lot easier).
The main work is done by cgdconfig -C
. Then the filesystem on the (now unlocked) CGD device is checked and mounted as /targetroot
. Finaly the sysctl init.root
is set to point at the chroot directory for init.
For the cgdconfig
part to work the root/etc/cgd
directory needs to be populated:
# ls -la root/etc/cgd/ total 40 drwx------ 2 root wheel 512 Nov 2 16:16 . drwxr-xr-x 3 root wheel 512 Nov 5 20:24 .. -rw------- 1 root wheel 71 Nov 2 18:12 cgd.conf -rw------- 1 root wheel 261 Nov 2 16:17 root.cgd -r-------- 1 root wheel 24576 Nov 2 16:54 root_unlock.secret
root_unlock.secret
is the crypted file created by fidocrypt containing your Fido key(s) enrollement and the secret used to unlock the CGD.
The other two tell cgdconfig what to find where:
Note: the NAME used her has to match the label in the GPT
This file is created by cgdconfig when creating the CGD following the fidocrypt man page (with potential minor edits, like verify_method). The -V u2f may not be needed with your Fido key. If your key supports both u2f and fido2, and some idiotic other application or system forced you to set a PIN for the device, forcing u2f format (via -V u2f
) allows you to use the key without the superflous (in this context) PIN.
You need to use the same -V
flag (or none) both when creating the secret file (or enrolling your key), and when retrieving the secret.