Network-bound disk encryption on Arch Linux
Posted on zo 05 juni 2022 in Arch Linux
While in a discussion with my coworkers, a coworker brought up that they wanted to have automatic LUKS disk decryption on their desktop while it was at home. Normally they would use a passphrase to decrypt the LUKS volume but would prefer automatic decryption. There are multiple ways to achieve this with TPM2 or Tang.
A Trusted Platform Module (TPM) is a secure processor which contains the secrets required to decrypt the LUKS volume when certain conditions are met (secure boot not disabled, CMOS not reset and others..).
Tang is a network service which runs in your network and on boot is used to decrypt your LUKS volume automatically.
I currently encrypt my desktop for the scenario that my desktop is stolen, in that case it's powered off and encrypted. As it's most likely that someone would steal the whole desktop using a TPM didn't make sense and Tang seems to be a good solution. It is highly unlikely that someone will replicate my home setup when stealing my desktop it is even more unlikely that they will try to decrypt the data. So I decided to go with Tang.
First off you will need a server and install tang
there, by default it runs on port 80. If this does not suite your setup you can override it using systemctl edit tangd.socket
:
[Socket]
ListenStream=
ListenStream=6666
Then enable it with systemctl enable --now tangd.socket
, now you'll want to check if you can fetch tang's key:
tang-show-keys 6666
On your desktop you'll need to install clevis
and libpwquality
. To verify the tang service works run the following commands:
echo "Hello World..." | clevis encrypt tang '{ "url": "http://tang.local"}' > secret.jwe
This command will ask you to verify tang's key and this should correspond to the output of tang-show-keys
.
To verify that the tang server can be used to decrypt the secret run:
clevis decrypt < secret.jwe
To let tang decrypt your desktop automatically, on boot in initramfs clevis
needs to contact the tang server and call clevis to decrypt your LUKS volume. Ideally if no network is available your normal LUKS passphrase prompt is shown.
As of now mkinitcpio has no official support for clevis yet, thanks to the excellent work of diabonas there is now a pull request open support it. To have network when in initramfs the net
hook can be used from mkinitcpio-nfs-utils
, to set this all up:
Adding tang to the LUKS Slot
clevis luks bind -d /dev/vda2 tang '{"url": "http://tang.local"}'
Now to configure the initramfs install mkinitcpio-nfs-utils
and then copy the snippet below to /etc/initcpio/hooks/clevis
:
#!/usr/bin/ash
run_hook() {
IFS=: read cryptdev cryptname cryptoptions <<EOF
$cryptdevice
EOF
if resolved=$(resolve_device "${cryptdev}" ${rootdelay}); then
clevis luks unlock -d "$resolved" -n "$cryptname"
fi
}
And create /etc/initcpio/install/clevis
#!/bin/bash
build() {
local mod
add_module "dm-crypt"
add_module "dm-integrity"
if [[ $CRYPTO_MODULES ]]; then
for mod in $CRYPTO_MODULES; do
add_module "$mod"
done
else
add_all_modules "/crypto/"
fi
add_binary "cryptsetup"
map add_udev_rule \
'10-dm.rules' \
'13-dm-disk.rules' \
'95-dm-notify.rules' \
'/usr/lib/initcpio/udev/11-dm-initramfs.rules'
# cryptsetup calls pthread_create(), which dlopen()s libgcc_s.so.1
add_binary "/usr/lib/libgcc_s.so.1"
add_binary "clevis"
add_binary "clevis-decrypt"
add_binary "clevis-decrypt-sss"
add_binary "clevis-decrypt-tang"
add_binary "clevis-decrypt-tpm2"
add_binary "clevis-luks-common-functions"
add_binary "clevis-luks-unlock"
add_binary "bash"
add_binary "curl"
add_binary "grep"
add_binary "jose"
[ -f "/usr/bin/luksmeta" ] && add_binary "luksmeta"
if [ -f "/usr/bin/tpm2" ]; then
add_checked_modules '/tpm/'
add_binary "tpm2_createprimary"
add_binary "tpm2_unseal"
add_binary "tpm2_load"
add_binary "tpm2_flushcontext"
add_binary "/usr/lib/libtss2-tcti-device.so.0"
fi
add_runscript
}
For networking to work the net
hook needs to know how your network interface should be configured. The configuration happens through kernel parameters, note that it uses the kenrel's interface naming so it's eth0
not enps0
or something similiar for more information see the wiki.
I use dhcp and have one interface so my kernel parameter is:
ip=:::::eth0:dhcp
Note: there is no timeout by default configured in the net
hook, so if your machine can't connect to the network it won't fall back to the encrypt
hook. So I recommend editing /usr/lib/initcpio/hooks/net
and changing the ipconfig
invocation to:
ipconfig -t 30 "ip=${ip}"
This means ipconfig
will stop trying to try to connect after 30 seconds.
Now all that's left is to configure your mkinitcpio HOOKS
, I prepend net clevis
before encrypt
so it shows up as following:
HOOKS=(base udev autodetect keyboard modconf block net clevis encrypt filesystems btrfs fsck)
Now re-generate your mkinitcpio hook:
mkinitcpio -p linux
Now rebooting should automatically decrypt your LUKS volume. Once again a huge thanks to diabonas for writing the clevis hook and helping out with setting it up.