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.conf
to force gpg to use curses interface since initramfs has no GUI.pinentry-program /usr/bin/pinentry-curses
- Import your public key with
$GNUPGHOME
set 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.sh
as 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/crypttab
by appending the following to each LUKS partition withUUID
of<UUID>
.,keyscript=/etc/luks_gpg/<UUID>
- Add
/etc/initramfs-tools/hooks/luks_gpg
to include necessary binaries as well as$GNUPGHOME
in 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
scdaemon
would 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