LUKS Encrypted Root with GPG Smartcard
Update: 2019/06/22
After upgrade to Debian 10 with gpg 2.2, the script is broken. One possible workaround is avoiding the use of pinentry-curses, which could be achieved by altering the content of the file /etc/luks_gpg/decrypt.sh to
#!/bin/sh
UUID=`basename $0`
export GNUPGHOME=/etc/luks_gpg/
read -p "pincode: " -s pincode
echo "$pincode" | gpg --batch --pinentry-mode loopback --passphrase-fd 0 \
--no-tty --decrypt "/etc/luks_gpg/$UUID.key.gpg"
and appending allow-loopback-pinentry option to /etc/luks_gpg/gpg-agent.conf.
Background
I used to protect LUKS root on my Linux machines with a static password stored in my yubikey. It is not safe if losing my yubikey, since the static password does not require any pin code. I was always thinking about using yubikey’s GPG smartcard capacity to make it safer, and finally I spent about 1 hour getting it work today. XD
I generally followed this link.
How to
I use Debian stretch, and I have already setup an LUKS partition on a partition whose UUID is <UUID>. Login as root user, and follow these steps.
- Install necessary packages.
apt install -y scdaemon pinentry-curses - Create
/etc/luks_gpg/and set permission properly.mkdir -p /etc/luks_gpg chown root:root /etc/luks_gpg chmod 700 /etc/luks_gpg - Edit
/etc/luks_gpg/gpg-agent.confto force gpg to use curses interface since initramfs has no GUI.pinentry-program /usr/bin/pinentry-curses - Import your public key with
$GNUPGHOMEset to/etc/luks_gpg/, and trust it.gpg --homedir /etc/luks_gpg --import <path_to_your_public_key> gpg --homedir /etc/luks_gpg --edit-key [email protected] gpg> trust - Generate a password file for LUKS and setup it as LUKS’s password.
dd if=/dev/random of=/etc/luks_gpg/<UUID>.key bs=1 count=256 cryptsetup luksAddKey /dev/disk/by-uuid/<UUID> /etc/luks_gpg/<UUID>.key - Use your public key to encrypt the key and safely delete it.
gpg --homedir /etc/luks_gpg --encrypt --recipient [email protected] /etc/luks_gpg/<UUID>.key shred -u /etc/luks_gpg/<UUID>.key - Add
/etc/luks_gpg/decrypt.shas decryption script.#!/bin/sh UUID=`basename $0` export GNUPGHOME=/etc/luks_gpg/ gpg --no-tty --decrypt "/etc/luks_gpg/$UUID.key.gpg" - Don’t forget to setup permission for it.
chmod +x /etc/luks_gpg/decrypt.sh - I applied a simple trick so that I could use different password for different partition easily, for each parition with different
<UUID>, we could create a soft link/etc/luks_gpg/<UUID>to/etc/luks_gpg/decrypt.sh.cd /etc/luks_gpg ln -s decrypt.sh <UUID> - Here is the trick. Update
/etc/crypttabby appending the following to each LUKS partition withUUIDof<UUID>.,keyscript=/etc/luks_gpg/<UUID> - Add
/etc/initramfs-tools/hooks/luks_gpgto include necessary binaries as well as$GNUPGHOMEin initramfs.#!/bin/sh set -e PREREQ="cryptroot" prereqs() { echo "$PREREQ" } case $1 in prereqs) prereqs exit 0 ;; esac . /usr/share/initramfs-tools/hook-functions cp -a /etc/luks_gpg/ "${DESTDIR}/etc/" mkdir -p "${DESTDIR}/etc/terminfo/l/" cp -a /lib/terminfo/l/linux "${DESTDIR}/etc/terminfo/l/linux" copy_exec /usr/bin/gpg copy_exec /usr/bin/gpg-agent copy_exec /usr/bin/pinentry-curses copy_exec /usr/lib/gnupg/scdaemon exit 0 - Setup permissions for it.
chown root:root /etc/initramfs-tools/hooks/luks_gpg chmod 750 /etc/initramfs-tools/hooks/luks_gpg - Before updating the initramfs, you need to trigger a card-edit, or
scdaemonwould not be correctly triggered during boot.export GNUPGHOME=/etc/luks_gpg/ gpg --card-edit - Finally update the initramfs.
update-initramfs -u
How to debug
update-initramfs -uv
lsinitramfs /boot/initrd.img-4.18.0-0.bpo.1-amd64