Unencrypted USB Stick for Booting GELI Encrypted root disk with UFS

I've seen some posts lately asking about GELI decryption keys on a USB stick for decrypting a root partition, so I wanted to play around with it for fun.
I documented my steps, so I thought I would share them here in case anyone is curious, but you can probably find other howtos like this on the internet.
I dug out a really old laptop for this experiment, so I'm using UFS with GPT on a BIOS system.

My disks:
da0 is the Freebsd 14.2 installer USB stick
da1 is going to be my permanent boot stick
ada0 is my internal SSD

I booted the installer and went through the usual: keymap, hostname, distribution components.
At "Partitioning," I chose the "Shell" option.
You should probably do something to wipe your internal disk first, but I'm not going over that here.

I at least destroyed my current table on my permanent boot stick:
Code:
gpart destroy -F da1
Create a new GPT on it:
Code:
gpart create -s gpt da1
Create a freebsd-boot partition for the stage 1 and 2 boot code:
Code:
gpart add -t freebsd-boot -s 512k da1
Install the protective MBR code and the stage 1 and 2 code:
Code:
gpart bootcode -b /boot/pmbr -p /boot/gptboot -i 1 da1

Create /boot:
Code:
gpart add -t freebsd-ufs -l boot da1
newfs -U -L bootfs /dev/gpt/boot

Setup the table and partitions on the internal SSD:
Code:
gpart destroy -F ada0
gpart create -s gpt ada0
gpart add -t freebsd-ufs -l root -a 4k -s 460473440 ada0
gpart add -t freebsd-swap -l swap -a 4k ada0
In the above, I calculated the size (-s) to use all of my available capacity for the root partition, except for the last 4g, which then went to the swap partition.
If you try this yourself, you'll need to adjust that size.

Mount the boot stick:
Code:
mount /dev/gpt/boot /mnt
Encrypt root:
Code:
mkdir -p /mnt/boot/keys
cd /mnt/boot/keys
dd if=/dev/random of=root.key bs=128k count=1
geli init -b -K root.key -s 4096 gpt/root
I wanted a passphrase also. If you don't, add '-P' above.
Attach:
Code:
geli load
geli attach -k root.key gpt/root
# Unmount the bootstick:
Code:
cd /
umount /mnt

Create the root FS:
Code:
newfs -t -U -L rootfs gpt/root.eli
(I use -t for SSD TRIM.)
Mount it:
Code:
mount /dev/gpt/root.eli /mnt
Now this gets a little weird.
Stage 2 expects the stage 3 loader to be in the /boot directory on the UFS on your USB stick, so you need to have a /boot on your USB stick, but when / is mounted from your internal SSD, your USB stick gets mounted at /boot on that root filesystem, which means that your stage 3 boot files would be under /boot/boot/, which is not good.
I just get around this with some symlinking:
Code:
mkdir /mnt/bootstick
mount /dev/gpt/boot /mnt/bootstick
cd /mnt
ln -s bootstick/boot boot

Create the fstab file that the installer will later put on the real root (internal disk) FS:
Code:
vi /tmp/bsdinstall_etc/fstab
Mine has:
Code:
/dev/gpt/root.eli /           ufs   rw  1 1
/dev/gpt/swap.eli none        swap  sw  0 0
/dev/gpt/boot     /bootstick  ufs   rw  2 2
I have no need to setup a permanent key for swap.
GELI has a onetime feature that I can use for swap on every boot.
This line in my fstab will be enough to trigger that.

Create the loader.conf file that the installer will later add to and put on the real root (internal disk) FS:
Code:
vi /tmp/bsdinstall_boot/loader.conf
Here's mine:
Code:
geom_eli_load="yes"
geom_eli_passphrase_prompt="yes"
kern.geom.label.disk_ident.enable="0"
kern.geom.label.gptid.enable="0"
geli_ada0p1_keyfile0_load="yes"
geli_ada0p1_keyfile0_type="ada0p1:geli_keyfile0"
geli_ada0p1_keyfile0_name="/boot/keys/root.key"
# These four only useful
# if the device became renamed
geli_root_keyfile0_load="yes"
geli_root_keyfile0_type="gpt/root:geli_keyfile0"
geli_root_keyfile0_name="/boot/keys/root.key"
# In case device is renamed
#vfs.root.mountfrom="ufs:gpt/root.eli"
vfs.root.mountfrom="ufs:ada0p1.eli"

This requires some explanation.
The top line is mandatory.
The geli_eli_passphrase line is not mandatory, but I like it if I'm using a passphrase for my encrypted root partition.
Basically, before the stage 3 loader menu, I'll have a little prompt to enter my passphrase.
It only gives you one chance, but if you get it wrong, boot will continue, and it will just count as your first wrong attempt when GELI does it's "normal" passphrase prompting.
I find that "normal" prompting hard to see sometimes in all of the other boot messages, so I really like the geli_eli_passphrase option.
Use the two kern.geom.label lines also.
The geli_ada0p1 lines tell geli where the key is.
The geli_root lines are for a "just in case" scenario.
With the way GELI scans, it will find ada0p1 before it finds that same partition by its GPT label, root.
On my laptop, the position of my disk on the bus/channel/whatever is not going to change, but on a more complicated system it may.
So if I ever boot and the partition became, say, ada1p1, my loader.conf wouldn't have a keyfile specified for that.
No problem. I'll just hit ENTER on all three passphrase attempts, then when GELI gets to the GPT labels, it will prompt me for the passphrase for gpt/root, which IS in my loader.conf, so it will be able to decrypt that.
Root will fail to mount, because my vfs.root line is for "ufs:ada0p1.eli," but that's not a big deal either.
When I get the mountfrom> prompt, I'll just enter "ufs:gpt/root.eli" manually.
I left that alternate mountfrom line in loader.conf but commented-out as reminder.

Anyway, once your to-be loader.conf is created, you can hit ^d to get back to the installer.
It should immediately start extracting the usual archives.
Finish the usual steps after that: password, network, time/date, startup services, hardening, firmware, other users.
On the Final Configuration screen, choose "Exit."
On the Manual Configuration screen, choose "No."
On the "Complete" screen, choose "live system."
Login as "root" with no password.
Your root.eli should still be attached.
Copy the backup to your permanent root:
Code:
mount /dev/gpt/root.eli /mnt
cp /var/backups/gpt_root.eli /mnt/var/backups/
umount /mnt

And cleanup:
Code:
geli detach gpt/root
shutdown -r now
Remove installer stick.

BIOS should find the pmbr code on the boot stick, which should lead to the stage 1 and 2 loader code, which should then lead to the stage 3 loader (with the optional GELI passphrase prompt first), then loader should be able to decrypt root and finish booting.
Just make sure you wait for boot to finish, then login and unmount /bootstick, before you physically remove it.
You could probably add a little service to unmount /bootstick for you.
Make sure also to mount /bootstick whenever you need to do something that updates /boot.
Do a 'geli status' and a 'geli list gpt/swap.eli' to make sure that the GELI "onetime" attachment occurred for swap.

I haven't had a chance yet to play with this old laptop much after the installation, so I can't guarantee there aren't any other caveats.
I also did not have a chance to try a similar setup on a UEFI system.
But I'm hoping someone may get something useful out of this.
 
I was desperately looking for a solution to boot an headless system with Geli.

Thanks for sharing this, but eventually I opted for Linux, the alternative would had been to connect the keyboard and typing the passphrase, unplug the keyboard.

Having a second OS, whether is partition or a USB, it is a workaround that really doesn't work for my use case.
 
I decided to get back to this topic.
I am really unsatisfied with the current GNU/Debian/Linux status...

My idea is to unlock from an mini SD card, my miniPC has the reader, not sure if FreeBSD can see it, but if it can I will definitely go for it.

My only concern is if the loader can see the the mini sd card allows the computer to unlock itself if the sd is inserted... 🤔
 
Back
Top