Network Bound Disk Encryption with clevis and tang on OpenWRT
Table of Contents
Introduction⌗
Since I always fully encrypt my drives for security reasons, I needed a solution to decrypt them in my home network in a secure way after a reboot. The background story was especially the automatic installation of system updates that require a reboot (e.g. for a new kernel version).
This guide was created on a Fedora 37 system.
Network Bound Disk Encryption⌗
Clevis and Tang encryption represent standard client and server components that deliver network bound disk encryption. Both the client- and server-side components utilize the José library to execute encryption and decryption operations.
Clevis
Clevis is a pluggable framework designed for automated decryption. In the context of NBDE, Clevis facilitates automated unlocking of LUKS volumes. The client side of the feature is provided by the clevis package.
Tang
Tang serves as a mechanism for binding data to a network presence, enabling secure access to a system containing data when it is bound to a specific network. Stateless and devoid of TLS or authentication requirements, Tang offers convenience and ease of use.
Installation⌗
First, we will set up tang
on the OpenWRT router, then install clevis
and the associated dracut
module on the client, and finally bind a LUKS volume to the tang
instance.
OpenWRT Setup⌗
Since our OpenWRT router is always accessible in our home network, it is ideal as an authentication server.
To install tang
on the router, we log in as root
on the machine and update the package lists.
Then we install the identical named package. On OpenWRT, systemd
can’t be used to control the socket because it is simply not available.
Instead, the package uses xinetd
to manage tang
in the configuration we want.
opkg update
opkg install tang
The following xinetd
configuration is created automatically.
To change the port or other parameters, this file needs to be changed accordingly.
service tangd
{
port = 8888
socket_type = stream
wait = no
user = root
server = /usr/libexec/tangdw
server_args = /usr/share/tang/db
log_on_success += USERID
log_on_failure += USERID
disable = no
}
As we can see tang
is executed by default on port 8888
. This will probably be unused on most OpenWRT installations.
Now it should be enough to reload the xinetd
service under /etc/init.d/xinetd
. Otherwise I recommend to restart the machine.
/etc/init.d/xinetd restart
Last but not least, we verify that the port was opened by tang
.
netstat -tulpn | grep xinetd
tcp 0 0 0.0.0.0:8888 0.0.0.0:* LISTEN 6897/xinetd
By the way, keys are automatically generated when a new client is registered and are stored under /usr/share/tang/db/
.
Client Setup⌗
On the client side, we first install the clevis
package and the associated dracut
module named clevis-dracut
.
Since we want to work with a LUKS
encrypted system partition, you should make sure that the clevis-luks
dependency is also installed when clevis
is installed.
dnf install clevis clevis-dracut
In the next step we need to enable networking in early boot phase and add two parameters to the kernel options in /etc/default/grub
:
rd.neednet=1
to make the network stack availableip=dhcp
so that the first network interface can get an IP from the DHCP (WARNING: footgun)
If the system has multiple active network ports, the IP configuration is handled on a first come, first served basis. In the worst case, it is then not possible to reach the machine via the actually desired network and to unlock it accordingly. This can be prevented by specifying the desired network interface and possibly even the entire IP configuration in the parameter.
For example a static IP configuration:
ip=192.168.180.120::192.168.180.1:255.255.255.0::enp1s0:off
Or a dynamic one with specified interface:
ip=enp1s0:dhcp
More informations about ip networking in early user space:
The modification in /etc/default/grub
should look something like this:
GRUB_CMDLINE_LINUX="resume=/dev/mapper/server-swap rd.luks.uuid=luks-**** rd.lvm.lv=server/root rd.md.uuid=**** rd.lvm.lv=server/swap rhgb quiet rd.neednet=1 ip=dhcp"
In the next step, we need to activate the new boot configuration. To do this, we execute the following commands:
grub2-mkconfig -o /etc/grub2.cfg
grub2-mkconfig -o /etc/grub2-efi.cfg
Finally we can now update the initramfs configuration with Dracut:
dracut -fv --regenerate-all
The following modules should now appear in the output of the command:
dracut: *** Including module: network ***
dracut: *** Including module: clevis ***
By the way, the clevis-dracut
module can also be used together with the dracut-sshd
module.
Bind LUKS Volume to Tang⌗
First, let’s find the LUKS encrypted volume on our system. In my case, it is a fully encrypted system partition.
blkid -t TYPE=crypto_LUKS -o device
/dev/nvme0n1p3
Now we tell clevis
to bind the volume to the tang
server we specified by IP:PORT
.
Then the LUKS
password for decryption must be entered.
clevis luks bind -d /dev/nvme0n1p3 tang '{"url":"openwrt.example.com:8888"}'
The advertisement contains the following signing keys:
-NYm6-gTZ9dquHe6zy9ynGU8SAI
Do you wish to trust these keys? [ynYN] Y
Enter existing LUKS password:
The displayed key can be verified on the OpenWRT router side.
For this purpose tang-show-keys
must be called with the corresponding tang
port 8888
.
tang-show-keys 8888
-NYm6-gTZ9dquHe6zy9ynGU8SAI
Now another keyslot and a new token should be created by clevis
.
cryptsetup luksDump /dev/nvme0n1p3
...
Tokens:
0: clevis
Keyslot: 1
...
The last thing that can be done is to restart the machine and test our setup. It should decrypt the volume automatically without password entry now.
Further Information⌗
If you prefer a remote solution using a password, you may like to read on here: