Project Fulla: Crypto utility USB key - Part 1: Preparation
For the last couple of years, I’ve carried a couple of USB drives with me: one
with a LUKS encrypted volume holding GPG keys, an Arch Linux LiveUSB system and
some other useful things, and one holding the /boot
partition and LUKS header
for decrypting and booting up my laptop. I’ve been meaning to merge the two into
one, and finally got around to doing it. This post is the first in a series of
three where I will lay out what I wanted to do, and how I did it.
I call the project - and the result thereof - Fulla, after the Norse goddess who carries the goddess Frigg’s ashen chest and in whom Frigg confides her secrets.
The result
At the end of this series of posts, I will have shown you how to move your
/boot
partition and, optionally, LUKS headers to en encrypted USB storage
device, making it a USB key in the true sense of the word. You can share this
/boot
between multiple systems and use the same bootloader as a gateway to all
of them, and also keep a bootable Linux system on the same drive.
Let’s start off with how I’ve done this and what this allows me to do.
Design
My setup looks like this. The USB key consists of three major parts:
- an Arch Linux system installed on LVM on top of a LUKS encrypted
partition, whose
/boot
partition is used as the shared/boot
for other systems as well, - a separate LUKS encrypted partition for sensitive data - which will allow usage of the embedded system without exposing the sensitive contents - and
- GRUB with multi-boot configuration, including the embedded system.
With this setup, I’ve moved the /boot
partition and the full disk encryption
LUKS header for my laptop to the USB key. In this way the USB key is required
for unlocking the laptop, making it a kind of 2-factor authentication. I also
always have a bootable Linux system with me, which I can use for recovery when I
break one of my systems or as a makeshift airgapped machine where I can use my
GPG master key. And, since the whole thing is encrypted, I can be reasonably
assured that noone can tamper with the USB key if I leave it out of sight for a
while.
How to
The procedure is pieced together from various articles on the Arch Wiki, with this article by Pavel Kogan as a major guideline.
Throughout the steps below, /dev/sde
will refer to the USB key. If following
these steps for your own setup, adjust the paths accordingly. I’m also writing
this as an Arch Linux user, but the procedure should be applicable to other
distributions as well.
Step 0: Secure wipe
First off, let’s do a secure wipe of the drive - that is, fill it with random data. An easy way to do this is to use plain dm-crypt encryption as a pseudo-random generator:
# cryptsetup open --type plain /dev/sde container --key-file /dev/random
# dd if=/dev/zero of=/dev/mapper/container status=progress
This will take a while. For my 8 GB USB 2.0 drive it took somewhere around 30 minutes.
Step 1: Partitioning and encryption setup
With our shiny new wiped drive, it’s time to get started. I want to use GPT for no particular reason other than newer is cooler, and I also want to be able to boot the system on my BIOS-only EEEPC netbook, so that means a BIOS boot partition is required. That means I’ll have four partitions on my 8 GB (7.2 GiB) drive:
/dev/sde1
: BIOS boot partition: 1 MiB/dev/sde2
:/boot
and Linux system partition (LVM): 4 GiB/dev/sde3
: Sensitive data partition: 1 GiB/dev/sde4
: Unsensitive general purpose storage: ~2.2 GiB (leftovers)
Use your favourite partitioning tool to set these up; I used gdisk
. Note that
if you need the BIOS boot partition you’ll need to set the correct partition
type, which is EF02
in gdisk
.
Next, we’ll set up LUKS encryption headers. I’ll simply go with the cryptsetup
default settings - they should be good enough if you have a recent version of
cryptsetup
, which you probably do if you run Arch. Here you’ll need to come up
with two strong passphrases for the two encrypted volumes.
# cryptsetup luksFormat /dev/sde2
# cryptsetup luksFormat /dev/sde3
# cryptsetup luksOpen /dev/sde2 fulla
# cryptsetup luksOpen /dev/sde3 eskir
Next, we need some filesystems:
# pvcreate /dev/mapper/fulla
# vgcreate fulla /dev/mapper/fulla
# lvcreate -L 500M -n boot fulla
# lvcreate -L 2G -n root fulla
# lvcreate -L 1G -n home fulla
# mkfs.ext3 /dev/mapper/fulla-boot
# mkfs.ext4 /dev/mapper/fulla-root
# mkfs.ext4 /dev/mapper/fulla-home
# mkfs.ext4 -L "Removable storage" /dev/mapper/eskir
So, this is what the setup looks like at the end of step 1:
# lsblk -f
NAME FSTYPE LABEL UUID MOUNTPOINT
sde
├─sde1
├─sde2 crypto_LUKS 221710ed-d6d3-4b83-bcc5-ef5307eadf21
│ └─fulla LVM2_member j40SO3-6xJ1-IOXH-58C7-lpNL-Zrqg-WKViEz
│ ├─fulla-boot ext3 f5909d93-e2f6-46ee-bd11-592bbea5f5fd
│ ├─fulla-root ext4 7994b2a9-6d69-4bb7-baf4-3bba6b86b7eb
│ └─fulla-home ext4 bd4e1b21-cdda-4995-be76-a1c93386bd67
├─sde3 crypto_LUKS e2990758-6a2b-40e8-957c-a6a4ee46d291
│ └─eskir ext4 7764923a-9fa9-46ec-a721-cdc2c8dd3ff2
└─sde4 ext4 Removable storag b4187b5f-549c-49c1-818f-9a110b44f41e
In the next section I’ll go through installing Arch Linux on the fulla
partition. Refer to your distribution’s documentation if you want another
distro, or feel free to skip the section if you don’t need a bootable Linux
system on the USB key.
Step 2: Install Arch Linux
This part is pretty straightforward: I simply follow the usual Arch Linux installation guide, keeping in mind the tweaks mentioned in Installing Arch Linux on a USB key. At this time I won’t bother disabling the ext4 journal, though, since I don’t plan to put the system to heavy or even daily use.
Some notable deviations from the usual installation instructions are:
- I leave network configuration out, since the main intended use of the system is as a makeshift airgapped environment for working with the sensitive data. I would probably be better off with Tails, but right now I’m more interested in just getting this to work at all and don’t want to spend the time getting acquainted with a new distro.
- I hold off building the initramfs (
mkinitcpio
) until I’ve configured it to work as I need, as explained in the next subsection.
For the remainder of this section, we’ll be working in the chroot environment,
with /
as the filesystem root of the embedded system.
Configure GRUB and mkinitcpio
There are a few things that need to happen with mkinitcpio.conf
for the
embedded system:
- As documented on the Arch wiki, we need the
block
hook right after theudev
hook. - We need the
lvm2
andencrypt
hooks for our LVM on LUKS setup.
As for GRUB, we need to tweak the configuration to allow use of encrypted
/boot
and /root
filesystems. This is excellently documented in Pavel
Kogan’s article. What we need is the following settings in
/etc/default/grub
:
GRUB_CMDLINE_LINUX="cryptdevice=UUID=221710ed-d6d3-4b83-bcc5-ef5307eadf21:lvm"
GRUB_ENABLE_CRYPTODISK=y
As you can see, the UUID here is the one for the /dev/sde2
device in the
lsblk
output in the previous section. Also, to save us the hassle of having to
input the passphrase twice - once for GRUB and once for the Linux kernel - when
booting the embedded system, we’ll set up a keyfile in the encrypted /boot
partition which the kernel will use to unlock the encrypted volume. Again, these
steps are taken from Kogan’s article.
# dd bs=512 count=4 if=/dev/urandom of=/boot/keyfile-fulla.bin
# cryptsetup luksAddKey /dev/sde2 /boot/keyfile-fulla.bin
Configure mkinitcpio
to include the keyfile in the initramfs by adding it to
FILES
in mkinitcpio.conf
:
FILES="/boot/keyfile-fulla.bin"
Lastly, set the cryptkey
kernel parameter in /etc/default/grub
:
GRUB_CMDLINE_LINUX="cryptdevice=UUID=221710ed-d6d3-4b83-bcc5-ef5307eadf21:lvm cryptkey=rootfs:/boot/keyfile-fulla.bin"
The rootfs
in the cryptkey
value is a magic string that makes the encrypt
hook resolve the part after the :
as within the initramfs, as opposed to
resolving the part before :
as a block device to use as the root.
With that, we’re all set. Time to build the initramfs and install GRUB:
# mkinitcpio -p linux
# grub-mkconfig -o /boot/grub/grub.cfg
# grub-install --target=i386-pc /dev/sde
Next steps
With that we now have a couple of encrypted volumes and, optionally, an
embdedded Linux system set up on the USB key, ready to be filled with /boot
files and LUKS headers from other systems. The next few steps will get a bit
more involved, as we’ll need to modify the encrypt
hook and hand-edit the GRUB
configuration. This will all be covered in the next post.