It is my opinion that systemd-pkcs11 is not supportable on stock debian/ubuntu.
When the initrd is not using systemd it cannot find the smartcard nor run the unlock process that gets the key from LUKS.. For example, Debian based systems do not use systemd with initramfs. Though rumored to work I also could not make systemd with dracut find the key and prompt for a pin.
I have created a custom process without systemd to decrypt and feed the key to cryptsetup that works with initramfs-tools. It supports the systemd-pkcs11 format and makes minimal changes to the debian boot process. I add some initramfs hooks and scripts in initrd to handle the decrypt. When using pkcs11 that library has to be included which is under 2M.
Below are the details of various attempts to make stock ubuntu work with systemd-pkcs11.
systemd will add a token to a luks2 header that is tied to a smartcard. Some distros (like debian/ubuntu) don't use systemd for boot (thank you Jesus) so we cannot use the pure systemd approach to decrypt on boot. This page is my attempt to bridge the gap. SPOILER : IT DOESN'T WORK
In between steps I often remove the token to clean up before next test using
sudo cryptsetup token remove --token-id 0 $DEV
I'll manually add a token to my root LUKS2 device that is compatible with systemd. Systemd has a command for this but we'll test that later.
DEV=/dev/sda1
URI='pkcs11:model=PKCS%2315%20emulated;manufacturer=ZeitControl;serial=000500001111;token=OpenPGP%20card%20%28User%20PIN%29'
cat > /tmp/token.json <<EOF
{
"type": "systemd-pkcs11",
"keyslots": ["0"],
"pkcs11-uri": "$URI",
"pkcs11-key": ""
}
EOF
# Import (prompts for LUKS passphrase)
sudo cryptsetup token import --token-id 0 $DEV < /tmp/token.json
rm /tmp/token.json
That added a token. I'm trying to test to see if it works before adding it to boot.
sudo cryptsetup open --test-passphrase --token-only $DEV
This fails. A.I. tells me
That "No user has logged in" error persists, which means the systemd-pkcs11 token plugin is failing to establish a proper PKCS#11 session with OpenSC. This is a known limitation: the systemd PKCS#11 plugin doesn't always work reliably with all smartcards/providers.
It suggests that I use systemd cryptenroll....OK. I try again
sudo systemd-cryptenroll --pkcs11-token-uri=auto $DEV
This still fails with errors about user login. I started over and seemed to get better results the next time. but it kept matching the wrong URI and giving me I get messages about matching multiple public keys. Finally worked with one of this format
URI='pkcs11:model=PKCS%2315%20emulated;manufacturer=ZeitControl;serial=000500001111;token=OpenPGP%20card%20%28User%20PIN%29;id=%02;type=public'
I try again.
sudo systemd-cryptenroll $DEV --pkcs11-token-uri=$URI
# Enter LUKS passphrase when prompted
# Enter Librem Key PIN when prompted
sudo cryptsetup open --test-passphrase --token-only $DEV
echo $?
i enable the boot process to use it. The process needs to be copied into the boot process in the initrd. To make that happen I create a hook script and place it at /etc/initramfs-tools/hooks/cryptsetup-token-pkcs11.
vim /etc/initramfs-tools/hooks/cryptsetup-token-pkcs11
This is the contents
#!/bin/sh
PREREQ=""
prereqs()
{
echo "$PREREQ"
}
case $1 in
prereqs)
prereqs
exit 0
;;
esac
. /usr/share/initramfs-tools/hook-functions
# Copy PKCS#11 token plugin
copy_file library /usr/lib/x86_64-linux-gnu/cryptsetup/libcryptsetup-token-systemd-pkcs11.so
# Copy pcscd daemon
copy_exec /usr/sbin/pcscd
# Copy PC/SC Lite library
copy_exec /usr/lib/x86_64-linux-gnu/libpcsclite.so.1
# Copy OpenSC PKCS#11 module
copy_file library /usr/lib/x86_64-linux-gnu/opensc-pkcs11.so
# Copy p11-kit components
copy_exec /usr/lib/x86_64-linux-gnu/libp11-kit.so.0 || true
copy_exec /usr/libexec/p11-kit/p11-kit-remote || true
Make it executable
chmod a+x /etc/initramfs-tools/hooks/cryptsetup-token-pkcs11
There are two options. One tries the token and falls back to the passphrase. this is what I want. there is a switch that can be added to crypttab to not use the passphrase fallback but that's dumb. In my case I don't need to modify crypttab at all. It remains the same as the default LUKS2 setup.
Now I update the initrd and hope for the best?
update-initramfs -u
as long as that works I reboot. In this case Im only updating the current kernel. If it fails and the fallback does not work I'll use the other kernel and initrd to boot.
***
This did not work. at least boot falls back to the passphrase which is more than we can say for stock debian SC support. I tried a variation that also does not work
in crypttab
luks-drive UUID=12343sa-fdasdf-asd23222020asdf0sadf none luks,initramfs,pkcs11-uri=auto
And then A.I. told me to just do it the debian way. I want the luks2 process to be independent of other disks and files. I may have to write my own plugin.
I would like to use my own custom script that does a fallback since stock debian does not but debian ignores the keyscript keyword. This does not work
luks-drive UUID=12343sa-fdasdf-asd23222020asdf0sadf none luks,keyscript=/lib/cryptsetup/scripts/decrypt_opensc
Then I went back to the way I was doing it this morning before I started all of this. Which is to hack the debian stock keyscript and put no options into crypttab
luks-drive UUID=12343sa-fdasdf-asd23222020asdf0sadf none
As much as I'd like to fix the debian boot process with initramfs the debian project rejected keyscript which is a major component of how all of this works. Though I dont know why it is needed since cryptsetup has native support via plugin to handle the decrypt. dracut is rumored to work so I'll try it next. (i also tried clevis and got nowhere)
I did a fresh setup of the cryptenroll after clearing all the previous keys and tokens.
DEV=/dev/sda1
URI='pkcs11:model=PKCS%2315%20emulated;manufacturer=ZeitControl;serial=000500001111;token=OpenPGP%20card%20%28User%20PIN%29;id=%02;type=public'
sudo systemd-cryptenroll $DEV --pkcs11-token-uri=$URI
# Enter LUKS passphrase when prompted
# Enter Librem Key PIN when prompted
sudo cryptsetup open --test-passphrase --token-only $DEV
echo $?
With this working I now turn to the initrd using dracut
apt install dracut dracut-core
need to add dracut options so I create /etc/dracut.conf.d/10-cryptroot.conf and put this in it
hostonly="yes"
use_fstab="yes"
add_dracutmodules+=" crypt systemd systemd-cryptsetup pkcs11 "
install_items+=" /usr/lib/x86_64-linux-gnu/pkcs11/opensc-pkcs11.so "
next i modify crypttab for the options dracut needs. I comment out the original line in case I need to put it back
/dev/sda UUID=abcdefgh none luks,pkcs11-uri=pkcs11:model=PKCS%2315%20emulated;manufacturer=ZeitControl;serial=000500001111;token=OpenPGP%20card%20%28User%20PIN%29;id=%02;object=Encryption%20key;type=private
I run dracut to create the initrd. this only works the first time.
dracut -f --kver $(uname -r)
after that i have to reboot with a different kernel and I dont want to trash it too so I have to be more specific with dracut
sudo dracut -f /boot/initrd.img-6.17.0-14-generic 6.17.0-14-generic
Finaly update grub
update-grub
if boot fails I'm supposed to boot with debug
rd.debug systemd.log_level=debug plymouth.enable=0
Let's see if this works.....nope. after more of the same I try
sudo tee /etc/dracut.conf.d/99-crypt-pkcs11.conf >/dev/null <<'EOF'
add_dracutmodules+=" systemd-cryptsetup crypt "
install_optional_items+=" /usr/lib/x86_64-linux-gnu/cryptsetup/libcryptsetup-token-systemd-pkcs11.so /usr/lib/x86_64-linux-gnu/opensc-pkcs11.so /lib/x86_64-linux-gnu/libopensc.so.12 /lib/x86_64-linux-gnu/libeac.so.3 /lib/x86_64-linux-gnu/libp11-kit.so.0 "
EOF
does this work? no. how about this?
sudo tee /etc/dracut.conf.d/99-crypt-pkcs11.conf >/dev/null <<'EOF'
add_dracutmodules+=" systemd-cryptsetup crypt "
install_optional_items+=" /usr/lib/x86_64-linux-gnu/cryptsetup/libcryptsetup-token-systemd-pkcs11.so /usr/lib/x86_64-linux-gnu/opensc-pkcs11.so /lib/x86_64-linux-gnu/libopensc.so.12 /lib/x86_64-linux-gnu/libeac.so.3 /lib/x86_64-linux-gnu/libp11-kit.so.0 "
drivers+=" ccid "
EOF
next try. that time isystemd cryptsetup does not error but there is no pin prompt. how about this?
sudo tee /etc/dracut.conf.d/99-crypt-pkcs11.conf >/dev/null <<'EOF'
add_dracutmodules+=" systemd-cryptsetup crypt "
install_optional_items+=" /usr/lib/x86_64-linux-gnu/cryptsetup/libcryptsetup-token-systemd-pkcs11.so /usr/lib/x86_64-linux-gnu/opensc-pkcs11.so /lib/x86_64-linux-gnu/libopensc.so.12 /lib/x86_64-linux-gnu/libeac.so.3 /lib/x86_64-linux-gnu/libp11-kit.so.0 /usr/sbin/pcscd "
install_items+=" /lib/systemd/system/pcscd.service /lib/systemd/system/pcscd.socket "
EOF
try again. still no. its really hard to get messages from a failed boot process. I guess I'll try to take a picture with my phone....
Looks LIKE maybe the uri is still messed up. A.I. suggests removing the Encryption Key part of the uri. so it looks like
/dev/sda UUID=abcdefgh none luks,pkcs11-uri=pkcs11:model=PKCS%2315%20emulated;manufacturer=ZeitControl;serial=000500001111;token=OpenPGP%20card%20%28User%20PIN%29;id=%02;type=private
trying reboot....nope. created debug script to run after failed boot.
sh /boot/collect-earlyboot-logs.sh
or
sh /boot/collect-earlyboot-logs.sh /dev/disk/by-uuid/abcde-xxxxx.....
need to modify boot process to drop to shell
what if I put all of the previous attempts together
sudo tee /etc/dracut.conf.d/99-crypt-pkcs11.conf >/dev/null <<'EOF'
add_dracutmodules+=" systemd-cryptsetup crypt "
install_optional_items+=" /usr/lib/x86_64-linux-gnu/cryptsetup/libcryptsetup-token-systemd-pkcs11.so /usr/lib/x86_64-linux-gnu/opensc-pkcs11.so /lib/x86_64-linux-gnu/libopensc.so.12 /lib/x86_64-linux-gnu/libeac.so.3 /lib/x86_64-linux-gnu/libp11-kit.so.0 "
drivers+=" ccid "
install_items+=" /lib/systemd/system/pcscd.service /lib/systemd/system/pcscd.socket "
EOF
that boots without errors but gives no pin prompt. it sites for a while then goes to the repeating dracut-initqueue errors.
sudo tee /etc/dracut.conf.d/99-crypt-pkcs11.conf >/dev/null <<'EOF'
# Core unlock path
add_dracutmodules+=" crypt systemd-cryptsetup "
# Storage/controller drivers needed early
add_drivers+=" vmd nvme nvme_core ahci sd_mod "
# Keep image generic while debugging
hostonly="no"
# Required tools
install_items+=" \
/usr/sbin/cryptsetup \
/usr/sbin/pcscd \
/lib/systemd/system/pcscd.service \
/lib/systemd/system/pcscd.socket \
"
# PKCS#11 + token libs
install_optional_items+=" \
/usr/lib/x86_64-linux-gnu/cryptsetup/libcryptsetup-token-systemd-pkcs11.so \
/usr/lib/x86_64-linux-gnu/opensc-pkcs11.so \
/lib/x86_64-linux-gnu/libopensc.so.12 \
/lib/x86_64-linux-gnu/libeac.so.3 \
/lib/x86_64-linux-gnu/libp11-kit.so.0 \
/lib/x86_64-linux-gnu/libpcsclite.so.1 \
/usr/lib/pcsc/drivers/ifd-ccid.bundle/Contents/Linux/libccid.so \
/usr/lib/pcsc/drivers/ifd-ccid.bundle/Contents/Info.plist \
"
EOF
finally, this prompts for a passphrase but no pin. next i try to switch to systemd only?
sudo tee /etc/dracut.conf.d/99-crypt-pkcs11.conf >/dev/null <<'EOF'
add_dracutmodules+=" systemd systemd-cryptsetup "
omit_dracutmodules+=" crypt "
add_drivers+=" vmd nvme nvme_core ahci sd_mod "
hostonly="no"
install_items+=" /usr/sbin/cryptsetup /usr/sbin/pcscd /usr/lib/systemd/system/pcscd.service /usr/lib/systemd/system/pcscd.socket "
install_optional_items+=" /usr/lib/x86_64-linux-gnu/cryptsetup/libcryptsetup-token-systemd-pkcs11.so /usr/lib/x86_64-linux-gnu/opensc-pkcs11.so /lib/x86_64-linux-gnu/libopensc.so.12 /lib/x86_64-linux-gnu/libeac.so.3 /lib/x86_64-linux-gnu/libp11-kit.so.0 /lib/x86_64-linux-gnu/libpcsclite.so.1 /usr/lib/pcsc/drivers/ifd-ccid.bundle/Contents/Linux/libccid.so /usr/lib/pcsc/drivers/ifd-ccid.bundle/Contents/Info.plist "
EOF
and now debugging with
UUID="$(sudo cryptsetup luksUUID "$DEV")"
URI="$(sudo cryptsetup luksDump "$DEV" | sed -n 's/^[[:space:]]*pkcs11-uri:[[:space:]]*//p' | head -n1)"
sudo tee /etc/dracut.conf.d/99-root-token.conf >/dev/null <<'EOF'
add_dracutmodules+=" systemd systemd-cryptsetup crypt "
EOF
printf 'GRUB_CMDLINE_LINUX="$GRUB_CMDLINE_LINUX rd.luks.uuid=%s rd.luks.options=%s=pkcs11-uri=%s"\n' "$UUID" "$UUID" "$URI" \
| sudo tee /etc/default/grub.d/90-pkcs11-force.cfg >/dev/null
sudo update-grub
sudo dracut -f
add more?
sudo tee /etc/dracut.conf.d/99-pkcs11-deps.conf >/dev/null <<'EOF'
# Extra runtime deps for opensc-pkcs11 in initrd
install_optional_items+=" \
/lib/x86_64-linux-gnu/libopensc.so.12 \
/lib/x86_64-linux-gnu/libeac.so.3 \
/lib/x86_64-linux-gnu/libcrypto.so.3 \
/lib/x86_64-linux-gnu/libz.so.1 \
/lib/x86_64-linux-gnu/libgio-2.0.so.0 \
/lib/x86_64-linux-gnu/libgobject-2.0.so.0 \
/lib/x86_64-linux-gnu/libglib-2.0.so.0 \
/lib/x86_64-linux-gnu/libgmodule-2.0.so.0 \
/lib/x86_64-linux-gnu/libmount.so.1 \
/lib/x86_64-linux-gnu/libselinux.so.1 \
/lib/x86_64-linux-gnu/libffi.so.8 \
/lib/x86_64-linux-gnu/libatomic.so.1 \
/lib/x86_64-linux-gnu/libm.so.6 \
/lib/x86_64-linux-gnu/libpcre2-8.so.0 \
/lib/x86_64-linux-gnu/libblkid.so.1 \
/lib/x86_64-linux-gnu/libusb-1.0.so.0 \
/lib/x86_64-linux-gnu/libudev.so.1 \
"
EOF
maybe this one
# /etc/dracut.conf.d/99-local-crypt-pkcs11.conf
# Keep generic image while debugging token unlock
hostonly="no"
# Needed dracut modules
add_dracutmodules+=" crypt systemd-cryptsetup "
# Kernel storage/USB/HID drivers
add_drivers+=" vmd nvme nvme_core ahci sd_mod xhci_pci xhci_hcd ehci_pci ehci_hcd uhci_hcd usbhid hid_generic "
# Required binaries and units
install_items+=" /usr/sbin/cryptsetup /usr/sbin/pcscd /usr/lib/systemd/system/pcscd.service /usr/lib/systemd/system/pcscd.socket "
# PKCS11/OpenSC/pcsc and runtime deps
install_optional_items+=" \
/usr/lib/x86_64-linux-gnu/cryptsetup/libcryptsetup-token-systemd-pkcs11.so \
/usr/lib/x86_64-linux-gnu/opensc-pkcs11.so \
/lib/x86_64-linux-gnu/libopensc.so.12 \
/lib/x86_64-linux-gnu/libeac.so.3 \
/lib/x86_64-linux-gnu/libp11-kit.so.0 \
/lib/x86_64-linux-gnu/libpcsclite.so.1 \
/usr/lib/pcsc/drivers/ifd-ccid.bundle/Contents/Linux/libccid.so \
/usr/lib/pcsc/drivers/ifd-ccid.bundle/Contents/Info.plist \
/lib/x86_64-linux-gnu/libcrypto.so.3 \
/lib/x86_64-linux-gnu/libz.so.1 \
/lib/x86_64-linux-gnu/libgio-2.0.so.0 \
/lib/x86_64-linux-gnu/libgobject-2.0.so.0 \
/lib/x86_64-linux-gnu/libglib-2.0.so.0 \
/lib/x86_64-linux-gnu/libgmodule-2.0.so.0 \
/lib/x86_64-linux-gnu/libmount.so.1 \
/lib/x86_64-linux-gnu/libselinux.so.1 \
/lib/x86_64-linux-gnu/libffi.so.8 \
/lib/x86_64-linux-gnu/libatomic.so.1 \
/lib/x86_64-linux-gnu/libm.so.6 \
/lib/x86_64-linux-gnu/libpcre2-8.so.0 \
/lib/x86_64-linux-gnu/libblkid.so.1 \
/lib/x86_64-linux-gnu/libusb-1.0.so.0 \
/lib/x86_64-linux-gnu/libudev.so.1 \
/etc/opensc/opensc.conf \
/usr/share/p11-kit/modules \
/usr/share/p11-kit/modules/p11-kit-trust.module \
/usr/lib/x86_64-linux-gnu/pkcs11 \
/usr/lib/x86_64-linux-gnu/pkcs11/p11-kit-client.so \
/usr/lib/x86_64-linux-gnu/pkcs11/p11-kit-trust.so \
"
and force pcscd to load before cryptsetup
sudo mkdir -p /etc/systemd/system/systemd-cryptsetup@.service.d
sudo tee /etc/systemd/system/systemd-cryptsetup@.service.d/10-pcscd.conf >/dev/null <<'EOF'
[Unit]
Wants=pcscd.socket pcscd.service
After=pcscd.socket pcscd.service
EOF
ai suggests moving pcscd earlier in boot process and increasing wait to 2mins
sudo tee /etc/systemd/system/pcscd-initrd.service >/dev/null <<'EOF'
[Unit]
Description=Start pcscd early in initrd
DefaultDependencies=no
Before=cryptsetup.target systemd-cryptsetup@*.service
Wants=pcscd.socket
After=systemd-udevd.service
[Service]
Type=oneshot
ExecStart=/usr/bin/systemctl start pcscd.socket
[Install]
WantedBy=initrd.target
EOF
sudo systemctl enable pcscd-initrd.service
UUID="$(sudo cryptsetup luksUUID "$DEV")"
printf 'GRUB_CMDLINE_LINUX="$GRUB_CMDLINE_LINUX rd.luks.options=%s=pkcs11-uri=auto,token-timeout=120s"\n' "$UUID" \
| sudo tee /etc/default/grub.d/90-pkcs11.cfg >/dev/null
sudo update-grub
sudo dracut -f --kver 6.17.0-14-generic
After 3 days of this I am done. systemd with pkcs11 for encrypted root is UNRELIABLE in stock debian and derivatives.
Im trying to make the systemd-cryptenroll process work with debian/ubunt initramfs-tools. We dont want to bloat initrd with systemd libraries. this is probably why debian doesn't include it by default. that leaves me with the only option to be to write and equivalent decrypt process. Im modifying the boot+decrypt process to handle multiple types of key setups. I'll build a package that supports booting the systemd-pkcs11 setup without needing systemd libraries.
***
after a few hoursdays of this i have working code. https://github.com/jtmoree-github-com/luks-root-smartcard
We can now use debian/ubuntu initramfs-tools with systemd-cryptenroll or gpg-cryptenroll to unlock encrypted luks2 root volumes using only information in the LUKS2 volume and a smartcard. No files on disk are needed. See https://github.com/jtmoree-github-com/luks-root-smartcard