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:

  1. rd.neednet=1 to make the network stack available
  2. ip=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:

Fedora encrypted disk unlocking via SSH