Unlocking LUKS2 with X509 Nitrokey during boot

Published on

A continuation of previous post with the instruction to unlock encrypted root partition during boot.

Intro #

We will be setting up and installing a few components that will be run at a specific point during Linux boot, allowing systemd-cryptsetup to prompt for the hardware token passphrase to unlock the LUKS2-encrypted partition. This procedure assumes that the partition is already configured to be unlocked using X509 certificate.

If you would like to use Nitrokey Storage to unlock LUKS2-encrypted device with the X509 certificate using systemd-cryptsetup, I recommend reading the previous post for configuration instructions.

I tried to write the instructions in this post in a generic way so they could be applicable regardless of the token device, Linux distribution or initramfs generation method.

In my configuration I use:

If you use a similar setup – I have packaged my dracut module as a package in my overlay. Just install sys-kernel/dracut-pcscd-cryptsetup and rebuild initramfs.

Disclaimer/warning #

Please implement the following procedure with extreme caution. If something goes wrong – the partition will not be unlocked during the boot process. Make sure you have access to a rescue shell in your initramfs or have a backup way to boot your machine (like a live USB with chroot).

Also, using the language from the GPL license: this post is written in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

Overview #

It is possible to fully rely on systemd to manage the init process. If /etc/crypttab is included in the initramfs, systemd will try to start cryptsetup.target. This target contains a set of automatically generated services that unlock devices specified in crypttab. In version 248, systemd added support for pkcs#11 certificates into systemd-cryptsetup, the binary that those services run to actually unlock the device and that can query the user for a passphrase.

When the system is fully booted, systemd-cryptsetup uses pcscd and a set of other tools to communicate with the smartcard devices.

So, in order to use systemd-cryptsetup to unlock our drive during boot, we will need to install everything it needs to talk to a card into initramfs.

Setup #

Installing dependencies #

During my experiments I have found that the following packages are required, at least for Nitrokey Storage:

If you are using a different token, you may need some other drivers, or adjust opensc configuration.

On Gentoo, these packages need to be installed with specific use flags, see the ebuild for details.

Configuring initramfs creation #

As mentioned in the overview, we will need to include systemd-cryptsetup and /etc/crypttab in the initramfs. On dracut it can be done through the following configuration:

~# cat /etc/dracut.conf.d/cryptsetup.conf
add_dracut_modules+=" crypt "
install_items+=" /etc/crypttab "

(note the spaces before and after quotation marks)

See man dracut.conf for more info on those configuration files.

This instructs dracut to include /etc/crypttab as a file in the initramfs and triggers module-install.sh from dracut crypt module. The crypt module (/usr/lib/dracut/modules.d/90crypt) examines the system and includes systemd-cryptsetup and related generators in the initramfs.

If using the dracut module created for this article, the add_dracut_modules line would look like so:

add_dracut_modules+=" crypt pcscd-cryptsetup "

Including dependencies in initramfs #

In order to function properly, certain files provided by packages from “Installing dependencies” section need to be present in initramfs. This list is maintained in the dracut module, the files are installed by calls to inst_multiple, inst_rules, inst and inst_libdir_file.

Allowing pcscd to start when requested by cryptsetup #

By default pcscd is socket-activated. Pcscd socket is wanted by sockets.target, which causes it to start after cryptsetup.target. When installing the pcscd.service and pcscd.socket into initramfs, we need to configure these units to allow starting without waiting sockets.target, otherwise they will be put into systemd job queue and not start when systemd-cryptsetup requests them.

To do that, we can leverage systemd’s drop-in files support.

For both socket and service, we will need to override the DefaultDependencies setting and declare that the socket and service are wanted by cryptsetup-pre.target. cryptsetup-pre.target is a special target that can be used to launch services before cryptsetup will need them.

The configuration drop-ins look like this:


Final step #

The only remaining thing is to rebuild initramfs. After that is done, reboot, insert the token and wait for the prompt from cryptsetup. The message “Started PC/SC Smart Card Daemon” should appear right prior to the passphrase prompt.

Troubleshooting #

This is a collection of notes and hints that I wrote down when configuring this setup. The most common error is that systemd-cryptsetup does not prompt for the passphrase. If using dracut it will result in emergency shell being offered after initqueue times out.