Install NetBSD 10 with (almost) full disk encryption
The first time I installed NetBSD I used sysinst, a menu-based program launched at boot that runs in the console. It has a clear and concise layout and I was quickly up-and-running on my new BSD system.
For my second time installing NetBSD I wanted to include full disk encryption to protect personal data in case the device is lost or stolen. Its not really enough to simply encrypt home directories. Passphrases and sensitive data can linger and be extracted from locations such as system logs and swap memory. There is a trade-off to be made between how much to encrypt, the convenience of operating the system, and the ability for the system to boot.
The arrangement outlined below uses an unencrypted EFI system partition (UEFI) plus a minimal root filesystem that boots up to an encryption passphrase prompt, which upon successful entry proceeds to unlock an encrypted partition containing var
, swap
, usr
, and home
.
Sysinst does not provide the option for encrypting the system in this manner, so early in the install process I switch to /bin/sh
and proceed to manually install NetBSD.
This is how I do it...
1. Let's go!
1.1 Setup
- Target device is
x86_64
architecture using UEFI boot - NetBSD is the sole OS on a single 1TB NVMe disk
- GPT partition table with 3 partitions (wedges):
- dk0 = Size:
128MB
; Format:msdos
; Use as:EFI system partition
- dk1 = Size:
6GB
; Format:ffs
; Use as:root partition
- dk2 = Size:
->END
; Format:cgd
; Use as:encrypted partition
- dk0 = Size:
- Within the encrypted partition 4 disklabels are created:
cgd0a
for/var
; Size:6GB
cgd0b
forswap
; Size:4GB
cgd0e
for/usr
; Size:40GB
cgd0f
for/home
; Size:->END
1.2 Download
The latest install images are available here: pub/NetBSD
Download the NetBSD-10.0-amd64-install.img.gz
install image and SHA512
hash file.
Verify image integrity:
$ sha512sum -c --ignore-missing SHA512
NetBSD-10.0-amd64-install.img.gz: OK
1.3 Prepare install media
Note: Installer was prepared on a Linux system.
Unpack install image:
$ gunzip NetBSD-10.0-amd64-install.img.gz
Write installer to an unmounted USB storage device using the dd
command as root.
BE VERY CAREFUL TO NOTE THE PROPER DEVICE. ALL DATA ON THE DEVICE WILL BE OVERWRITTEN.
Example: On a Linux system, if a USB stick appears as sdx1
, then write the installer to sdx
(omit the partition number):
# dd if=/path/to/NetBSD-10.0-amd64-install.img of=/dev/sdx bs=1M
$ sync
2. Configure live environment
2.1 Boot
After the installer has successfully booted into the NetBSD kernel, the menu-based sysinst program is launched.
Select the desired language and keyboard type.
2.2 Network
Select Utility menu
then Configure network
.
Example: On my system I have two interfaces detected: wn0
(wired) and iwn0
(wireless).
I choose to configure the wired ethernet interface with autoconfiguration
.
2.3 (Optional) Remote login to installer
I make this manual install process easier (cut-n-paste commands; lookup info online) by remotely logging into the installer via SSH from another computer.
Setup SSH on the installer by navigating back to the Utility menu
and selecting Run /bin/sh
.
Create password for root:
# passwd root
Modify /etc/ssh/sshd_config
to allow root to login with password:
PermitRootLogin yes
Start sshd
:
# /etc/rc.d/sshd onestart
Retrieve IP address for active interface (example: iwm0
):
# ifconfig iwm0
Login to the target device via SSH:
$ ssh root@<target_device_ip_address>
3. Prepare disk
3.1 Identify disks and partitions
Discover what disk devices and partitions have been recognized by the kernel using sysctl(8):
# sysctl hw.disknames
hw.disknames = ld0 dk0 dk1 dk2 dk3 sd0 dk4 dk5 sd1
NVMe devices show up as ld
and hard disks are identified by wd
. USB devices usually show up as sd
.
The dk
devices are wedges (partitions) on the storage devices, and this early after boot are usually displayed in order, that is: dk0
through dk3
are wedges on the NVMe target device ld0
, and dk4
and dk5
on the USB installer sd0
.
Verify by asking for a list of wedges on sd0
using dkctl(8):
# dkctl sd0 listwedges
/dev/rsd0: 2 wedges:
dk4: EFI system, 262144 blocks at 2048, type: msdos
dk5: 30c4cc4e-5369-449c-8994-a4b1ea665b4b, 4853760 blocks at 264192, type: ffs
Check which device the installer booted from using dmesg(8):
# dmesg | fgrep "root on"
[ 4.158650] root on dk5
More details about individual disks can be retrieved by extracting parts of the kernel output from the dmesg
output (example: for ld0
):
# dmesg | fgrep ld0
[ 1.045032] ld0 at nvme0 nsid 1
[ 1.045032] ld0: 931 GB, 121601 cyl, 255 head, 63 sec, 512 bytes/sect x 1953525168 sectors
[ 2.178653] ld0: GPT GUID: 2271c564-dd66-4398-b6f3-12bec0c9df07
[ 2.178653] dk0 at ld0: "esp", 614400 blocks at 2048, type: msdos
[ 2.248652] dk1 at ld0: "boot", 2097152 blocks at 616448, type: ext2fs
[ 2.308652] dk2 at ld0: "root", 134217728 blocks at 2713600, type: <unknown>
[ 2.378653] dk3 at ld0: "home", 1816573327 blocks at 136931328, type: <unknown>
There is also the nvmectl(8) command (use atactl(8) for SATA drives):
# nvmectl identify nvme0 | egrep 'Model|Device type|Capacity'
Model Number: Samsung SSD 980 1TB
3.2 Set disk variable
For the purposes of this HOWTO, the NVMe storage device detected as ld0
is where NetBSD will be installed.
Set the $disk
variable:
# disk="ld0"
3.3 Create GPT partition table
Target device uses UEFI to boot and will use GPT partitions:
# gpt destroy $disk
# gpt create -f $disk
3.4 Create partitions
# gpt add -a 2m -s 128m -t efi -l "efi-system" $disk
# gpt add -a 2m -s 6g -t ffs -l "netbsd-root" $disk
# gpt add -a 2m -t cgd -l "syscgd" $disk
Verify the result:
# gpt show $disk
start size index contents
0 1 PMBR
1 1 Pri GPT header
2 32 Pri GPT table
34 4062 Unused
4096 262144 1 GPT part - EFI System
266240 12582912 2 GPT part - NetBSD FFSv1/FFSv2
12849152 1940672512 3 GPT part - NetBSD Cryptographic Disk
1953521664 3471 Unused
1953525135 32 Sec GPT table
1953525167 1 Sec GPT header
List wedges:
# dkctl $disk listwedges
/dev/rld0: 3 wedges:
dk0: efi-system, 262144 blocks at 4096, type: msdos
dk1: netbsd-root, 12582912 blocks at 266240, type: ffs
dk2: syscgd, 1940672512 blocks at 12849152, type: cgd
3.5 Format partitions
Format the EFI partition:
# newfs_msdos /dev/rdk0
Format the root partition:
# newfs -O 2 dk1
3.6 Add EFI boot entries
# mount /dev/dk0 /mnt
# mkdir -p /mnt/EFI/boot
# cp -v /usr/mdec/*.efi /mnt/EFI/boot
/usr/mdec/bootia32.efi -> /mnt/EFI/boot/bootia32.efi
/usr/mdec/bootx64.efi -> /mnt/EFI/boot/bootx64.efi
# umount /mnt
3.7 Mount future root
# mount /dev/dk1 /targetroot
4. Encryption
4.1 Create CGD config
NetBSD uses the cryptographic device driver (CGD) to create and manage encrypted storage.
Using cgdconfig(8), a parameters file is generated that stores the encryption type, key length, and a random password salt for the new cgd
.
There are a few different encryption ciphers supported. I choose aes-xts
with a 512-bit key:
# mkdir -p /targetroot/etc/cgd
# chmod 700 /targetroot/etc/cgd
# cgdconfig -g -V disklabel -o /targetroot/etc/cgd/syscgd aes-xts 512
4.2 Create CGD drive
Create the encrypted storage and assign it a passphrase. This passphrase will be used to open the CGD drive at boot.
Example: NAME
is the label used when the GPT partition for CGD was created earlier (syscgd
):
# cgdconfig -V re-enter cgd0 NAME=syscgd /targetroot/etc/cgd/syscgd
4.3 Create disklabels
Note: Disklabels c
and d
have special meaning in NetBSD (addressing the whole disk) and should not be used.
In my example setup, I use:
cgd0a
for/var
; Size:6GB
cgd0b
forswap
; Size:4GB
cgd0e
for/usr
; Size:40GB
cgd0f
for/home
; Size:->END
Using disklabel(8) in interactive mode:
# disklabel -Ii cgd0
Enter '?' for help
...
Setup /var
:
partition>a
Filesystem type [4.2BSD]:
Start offset ('x' to start after partition 'x') [0c, 0s, 0M]:
Partition size ('$' for all remaining) [947594c, 1940672512s, 947594M]: 6G
a: 12582912 0 4.2BSD 0 0 0 # (Cyl. 0 - 6143)
Setup swap
:
partition>b
Filesystem type [unused]: swap
Start offset ('x' to start after partition 'x') [0c, 0s, 0M]: a
Partition size ('$' for all remaining) [0c, 0s, 0M]: 4G
b: 8388608 12582912 swap # (Cyl. 6144 - 10239)
Setup /usr
:
partition>e
Filesystem type [unused]: 4.2BSD
Start offset ('x' to start after partition 'x') [0c, 0s, 0M]: b
Partition size ('$' for all remaining) [0c, 0s, 0M]: 40G
e: 83886080 20971520 4.2BSD 0 0 0 # (Cyl. 10240 - 51199)
Setup /home
:
partition>f
Filesystem type [unused]: 4.2BSD
Start offset ('x' to start after partition 'x') [0c, 0s, 0M]: e
Partition size ('$' for all remaining) [0c, 0s, 0M]: $
f: 1835814912 104857600 4.2BSD 0 0 0 # (Cyl. 51200 - 947593)
If satisfied with the edits, write the label and exit:
partition>W
Label disk [n]?y
Label written
partition>Q
4.4 Verify CGD drive
Copy the config to the target device:
# echo 'cgd0 NAME=syscgd /etc/cgd/syscgd' > /targetroot/etc/cgd/cgd.conf
Close the CGD drive:
# cgdconfig -u cgd0
Unlock it again with the passphrase set earlier:
# cgdconfig cgd0 NAME=syscgd /targetroot/etc/cgd/syscgd
If everything worked, the cgd0
drive is now open and disklabel is visible with:
# disklabel cgd0
# /dev/rcgd0:
type: cgd
disk: cgd0
label: fictitious
flags:
bytes/sector: 512
sectors/track: 2048
tracks/cylinder: 1
sectors/cylinder: 2048
cylinders: 947594
total sectors: 1940672512
rpm: 3600
interleave: 1
trackskew: 0
cylinderskew: 0
headswitch: 0 # microseconds
track-to-track seek: 0 # microseconds
drivedata: 0
6 partitions:
# size offset fstype [fsize bsize cpg/sgs]
a: 12582912 0 4.2BSD 0 0 0 # (Cyl. 0 - 6143)
b: 8388608 12582912 swap # (Cyl. 6144 - 10239)
d: 1940672512 0 unused 0 0 # (Cyl. 0 - 947593)
e: 83886080 20971520 4.2BSD 0 0 0 # (Cyl. 10240 - 51199)
f: 1835814912 104857600 4.2BSD 0 0 0 # (Cyl. 51200 - 947593)
4.5 Format and mount
# newfs -O 2 cgd0a
# newfs -O 2 cgd0e
# newfs -O 2 cgd0f
# mkdir /targetroot/var /targetroot/usr /targetroot/home
# mount /dev/cgd0a /targetroot/var
# mount /dev/cgd0e /targetroot/usr
# mount /dev/cgd0f /targetroot/home
5. Install NetBSD
The new system is composed of sets (collections of packages) installed to the target device. These sets are located in /amd64/binary/sets
.
At a minimum, you must select a kernel and the base
and etc
sets. Below are the sets I choose to install.
Note: The tar flag p
is very important. It ensures that all files preserve their owners and mode.
# cd /amd64/binary/sets
# for set in base comp etc games gpufw kern-GENERIC man misc modules tests text xbase xcomp xetc xfont xserver; do
> tar -xvzpf $set.tar.xz -C /targetroot
> done
6. Configure NetBSD
6.1 Chroot
Enter chroot
on the target device:
# chroot /targetroot
6.2 Create directories
Create the kern
and proc
directories:
# mkdir kern proc
6.3 Create devices
# cd dev
# sh MAKEDEV all
Note: On a previous install, after rebooting the boot process halted with the error message:
/etc/defaults/rc.conf: cannot create /dev/null: read-only file system
/etc/rc: cannot create /dev/null: read-only file system
MAKEDEV
had created null
but it was incorrectly configured:
-rw-r--r-- 1 root wheel 0 Sep 4 16:57 null
Fix: To avoid this error, remove the existing null
:
# rm /dev/null
Re-create null
with mknod(8):
# mknod -m 0666 -u root -g wheel /dev/null c 2 2
Show:
# ls -l null
crw-rw-rw- 1 root wheel 2, 2 Sep 27 16:59 null
6.4 Create fstab
# cat > /etc/fstab << EOF
NAME=netbsd-root / ffs rw 1 1
tmpfs /tmp tmpfs rw,-m=1777,-s=ram%25
kernfs /kern kernfs rw
ptyfs /dev/pts ptyfs rw
procfs /proc procfs rw
tmpfs /var/shm tmpfs rw,-m1777,-sram%25
# Encrypted file-systems
/dev/cgd0a /var ffs rw 1 2
/dev/cgd0b none swap sw
/dev/cgd0e /usr ffs rw 1 2
/dev/cgd0f /home ffs rw 1 2
EOF
6.5 Edit rc.conf
# vi /etc/rc.conf
Note: In this example, my hostname was earlier set to demobsd.localdomain
during network setup, and my wired interface is wm0
. Adjust accordingly.
Settings:
# Set this to "YES", otherwise system will boot in single-user mode.
rc_configured=YES
# Add local overrides below. Wait for CGD to be unlocked before mounting.
critical_filesystems_local="OPTIONAL:/var OPTIONAL:/usr"
hostname=demobsd.localdomain
dhcpcd=YES
dhcpcd_flags="-qM wm0"
sshd=YES
ntpd=YES
ntpdate=YES
wscons=YES
cgd=YES
6.6 Set root password
# passwd root
6.7 Set keyboard
Set encoding <type_of_keyboard>
in /etc/wscons.conf
.
A full list of keyboard mappings and variants can be found in wskbd(4).
6.8 Set timezone
# ln -sf /usr/share/zoneinfo/<region/<city_or_sub-region> /etc/localtime
6.9 System locale
Example: Using en_CA.UTF-8
:
# echo "export LANG=\"en_CA.UTF-8\"" >> /etc/profile
# echo "export LC_CTYPE=\"en_CA.UTF-8\"" >> /etc/profile
# echo "export LC_ALL=\"\"" >> /etc/profile
6.10 Network interface
Example: For ethernet interface wm0
that uses DHCP:
# cat > /etc/ifconfig.wm0 << EOF
up
media autoselect
EOF
7. Finish up
Exit chroot:
# exit
Unmount the new system:
# cd /
# umount /targetroot/home
# umount /targetroot/usr
# umount /targetroot/var
# umount /targetroot/
Close CGD device:
# cgdconfig -u cgd0
Reboot:
# shutdown -r now
Note: Encryption passphrase uses a us qwerty
keyboard for entry regardless of setting in wscons.conf
.
During boot the system halts and prompts for the passphrase to unlock the CGD drive.
Welcome to NetBSD!
8. Resources
- Big thanks owed to vsis whose HOWTO was crucial in getting my system configured with encryption: NetBSD - UEFI installation with Full Disk Encryption
- An alternative approach for disk encryption using a ramdisk on BIOS boot systems: NetBSD Full-Disk Encryption with CGD (BIOS/GPT)
- NetBSD Wiki: Installing NetBSD on a x86 system with UEFI
- NetBSD Guide: Chapter 14. The cryptographic device driver (CGD)
- Suggested tools for inspecting disks: Disk management from Installation ISO
- Skipping the NetBSD sysinst guided install for a more "hands-on" approach: Manual NetBSD installation on GPT/UEFI
You can like, share, or comment on this post on the Fediverse 💬
» Next: Install Chimera Linux with encrypted Root-on-ZFS
« Previous: Secure remote access to NetBSD devices using SSH keys