Introduction
============
This page contains instructions describing how to set-up Debian Jessie in order to protect the LUKS decryption key using GnuPG in combination with OpenPGP smart-card.
**Be warned that this is a fairly involving and complicated process that can not only make your data inaccessible while setting it up, but also in case something goes wrong during system upgrades! You should feel very comfortable with both GnuPG and smart-cards, and have backups just in case. It is recommended to keep backup of disk decryption keys in one form or another, and to have handy procedures on how to recover system in case something fails after a package upgrade. If upgrading distro, I would in fact recommend switchin the system back to password encryption before actually upgrading to latest release.**
The goal is to improve security of the encryptped disk by making it harder to brute-force the installation, and to ensure that you need a physical token before decryption takes place (some caveats to keep in mind are listed below).
The following is covered:
- Basic installation of Debian Jessie with disk encryption.
- Setting-up GnuPG keyring that will be used for decryption purposes.
- Configuring and building initramdisk in order to make it possible to use GnuPG during boot process for decryption.
- Configuring and building initramdisk in order to make it possible to use OpenPGP smart-card during boot process for decryption (in conjunction with GnuPG).
- Setting-up a separate boot device (USB flash).
Installing the OS
=================
When it comes down to Debian Jessie installation, you should be able to use standard procedure for installing it on top of encrypted partitions. This guide will assume the simplest possible case with three partitions (boot, root, and swap).
Actual installation will probably differ, but you might find this a good starting step when testing things out.
Install Debian Jessie using the following steps:
1. Boot Debian installation CD.
2. Select option `Install`.
3. Set language to `English`.
4. Set country to `Sweden`.
5. Set keyboard layout to `en_US.UTF-8`.
6. Set keymap to `American English`.
7. Set hostname to `encrypt`.
8. Set domain to `local`.
9. Set password for the **root** account.
10. Set full name for the user to `My User`.
11. Set username for the user to `user`.
12. Set password for the **user** account.
13. Partition the disk:
1. Set method to `Manual`.
2. Wipe-out any available partitions and create a new partition table.
3. Create partitions as follows:
- `256MB`, `Primary`, `Beginning`, `Ext4`, `/boot/`
- `8GB`, `Primary`, `Beginning`, `physical volume for encryption`
- `remaining space`, `Primary`, `physical volume for encryption`
4. Select option `Configure encrypted volumes`.
5. Write changes to disk.
6. Select `Create encrypted volumes`, select `/dev/sda2`.
7. Select `Create encrypted volumes`, select `/dev/sda3`.
8. Select `Finish`.
9. Confirm operation.
10. Wait for erase to finish.
11. Provide encryption password for first partition. This password will be replaced at later stage.
12. Provide encryption password for second partition. This password will be replaced at later stage.
13. Set-up the two new encrypted partitions:
- `sda2_encrypt` should be `Ext4` with mount point `/`.
- `sda3_encrypt` should be `swap`.
14. Finally finish partitioning and write changes to disk.
14. Select Debian archive mirror.
15. Opt out of popularity contest.
16. On software selection screen pick:
- `SSH server`
- `standard system utilities`
17. When prompted, install GRUB boot loader to `/dev/sda`.
18. Finally Reboot into the new system, and provide decryption passwords.
Setting-up GnuPG keyring
========================
We will start off with setting-up a GnuPG keyring.
Perform the following steps:
1. Log-in as `root` into the machine.
2. Create directory where the keyring will be stored:
mkdir /etc/luks_gnupg/
chmod 700 /etc/luks_gnupg/
3. Initialise the keyring and create GnuPG private key that will be used later on for encrypting the LUKS encryption key:
gpg2 --homedir /etc/luks_gnupg/ --gen-key
4. Pick `RSA and RSA` as key type.
5. Set length to `2048` bits.
6. Set expiration to `0`.
7. Confirm the key does not expire.
8. Set real name to `My User`.
9. Set email address to `myuser@localhost`.
10. Set empty comment.
11. Confirm identity.
12. Provide password for GnuPG key.
13. Install PIN entry application:
apt-get install pinentry-curses
14. Configure GnuPG agent to use *pinentry-curses* by creating file `/etc/luks_gnupg/gpg-agent.conf`:
pinentry-program /usr/bin/pinentry-curses
Setting-up new decryption key for LUKS
======================================
During installation a password was provided for decrypting the partitions. What we want to do is to replace this password with a more complex symmetric key, that will in turn be encrypted using GnuPG private key.
Set-up new decryption key for encrypted partitions:
1. Generate 256-bit random key:
dd if=/dev/random bs=1 count=256 > /etc/luks_gnupg/disk.key
2. Add the generated decryption key to first encrypted partition:
cryptsetup luksAddKey /dev/sda2 /etc/luks_gnupg/disk.key
3. Provide existing password.
4. Add the generated decryption key to second encrypted partition:
cryptsetup luksAddKey /dev/sda3 /etc/luks_gnupg/disk.key
5. Provide existing password.
6. Encrypt the decryption key using GnuPG private key:
gpg2 --homedir /etc/luks_gnupg/ --recipient myuser@localhost --encrypt-files /etc/luks_gnupg/disk.key
7. Shred and remove the the plaintext disk encryption key:
shred -u /etc/luks_gnupg/disk.key
Setting-up decryption script
============================
In order to use GnuPG, we will create a decryption script that will invoke GnuPG with correct parameters.
Perform the following steps:
1. Create script for obtaining decryption key `/usr/local/sbin/luks_gnupg_decrypt.sh`:
#!/bin/sh
# This is the safest way to ensure the GnuPG home directory is correctly set.
export GNUPGHOME=/etc/luks_gnupg/
gpg2 --no-tty --decrypt /etc/luks_gnupg/disk.key.gpg
2. Fix script permissions:
chown root:root /usr/local/sbin/luks_gnupg_decrypt.sh
chmod 750 /usr/local/sbin/luks_gnupg_decrypt.sh
3. Test decryption of first partition:
/usr/local/sbin/luks_gnupg_decrypt.sh | cryptsetup open --test-passphrase /dev/sda2 test
4. Test decryption of second partition:
/usr/local/sbin/luks_gnupg_decrypt.sh | cryptsetup open --test-passphrase /dev/sda3 test
Preparing initramdisk
=====================
With all pieces in place, it is time to prepare a new initramdisk that will contain GnuPG binaries, GnuPG keyring, and decryption scripts.
Perform the following steps:
1. Back-up the existing initramdisk:
cp -a /boot/initrd.img*-amd64 /boot/initrd.bak
2. Update `/etc/crypttab` by appending the following to both lines:
,keyscript=/usr/local/sbin/luks_gnupg_decrypt.sh
3. Create initramdisk hook that will make sure the necessary binaries, GnuPG keyring, and decryption script are included in resulting image. Create file `/etc/initramfs-tools/hooks/luks_gnupg`:
#!/bin/sh
set -e
PREREQ="cryptroot"
prereqs()
{
echo "$PREREQ"
}
case $1 in
prereqs)
prereqs
exit 0
;;
esac
. /usr/share/initramfs-tools/hook-functions
# Deploy the keyring.
cp -a /etc/luks_gnupg/ "${DESTDIR}/etc/"
# Deploy terminfo (required for pinentry-curses).
mkdir -p "${DESTDIR}/etc/terminfo/l/"
cp -a /lib/terminfo/l/linux "${DESTDIR}/etc/terminfo/l/linux"
# Deploy GnuPG binaries and pinentry-curses.
copy_exec /usr/bin/gpg2
copy_exec /usr/bin/gpg-agent
copy_exec /usr/bin/pinentry-curses
exit 0
4. Fix script permissions:
chown root:root /etc/initramfs-tools/hooks/luks_gnupg
chmod 750 /etc/initramfs-tools/hooks/luks_gnupg
5. Update the initramdisk:
update-initramfs -u -k all
Booting into the system
=======================
If you have not made any errors, at this point you should be able to boot into the new system, this time around using the decryption key protected by your GnuPG keyring.
Perform the following steps:
1. Reboot the machine.
2. Boot Debian from disk.
3. You should be prompted to provide password for the GnuPG encryption key twice (for both partitions). Don't mistake this password for the initial disk encryption password, they are completely separate.
Setting-up OpenPGP smart-card
=============================
Once you have LUKS disk decryption working using GnuPG soft keys, you may want to move on onto setting-up an OpenPGP smart-card for this purpose. The main advantage of switching to such a schema is that in case someone gains access to your initrd image, they will be unable to perform brute force against the software key stored within. I.e. the security will be improved by storing the private key on a dedicated physical device which makes it hard to extract the credentials.
Perform the following steps:
1. Log-in as `root` into the machine`.
2. Install additional package that allows GnuPG to use smart-cards:
apt-get install scdaemon
3. Set-up configuration file for scdaemon that will redirect all log messages to `/dev/null` in order to reduce clutter. Create file `/etc/luks_gnupg/scdaemon.conf`:
log-file /dev/null
4. Insert smart-card into smart-card reader connected to the machine.
5. Transfer the encryption private key to smart-card:
1. Start command for manipulating the keys:
gpg2 --homedir /etc/luks_gnupg/ --edit-key myuser@localhost
2. Toggle to manipulation of secret key material:
toggle
3. Select the encryption sub-key:
key 1
4. Transfer the encryption sub-key to smart-card:
keytocard
5. When prompted where to store the key, type `2` (for `(2) Encryption key` entry).
6. You will be prompted for the key password first. Provide it.
7. Afterwards you will be prompted for the card admin password. Provide it.
8. Wait for operation to finish.
9. Save keyring changes and exit:
save
6. Verify that decryption now uses the OpenPGP smart-card:
/usr/local/sbin/luks_gnupg_decrypt.sh | cryptsetup open --test-passphrase /dev/sda2 test
Updating initramdisk to handle OpenPGP smart-card
=================================================
Once the encryption key has been moved to the OpenPGP smart-card, the initramdisk needs to be updated as well in order to include the necessary binaries.
Perform the following steps:
1. Update file `/etc/initramfs-tools/hooks/luks_gnupg` to include the line `copy_exec /usr/lib/gnupg2/scdaemon`. The full file should look as follows:
#!/bin/sh
set -e
PREREQ="cryptroot"
prereqs()
{
echo "$PREREQ"
}
case $1 in
prereqs)
prereqs
exit 0
;;
esac
. /usr/share/initramfs-tools/hook-functions
# Deploy the keyring.
cp -a /etc/luks_gnupg/ "${DESTDIR}/etc/"
# Deploy terminfo (required for pinentry-curses).
mkdir -p "${DESTDIR}/etc/terminfo/l/"
cp -a /lib/terminfo/l/linux "${DESTDIR}/etc/terminfo/l/linux"
# Deploy GnuPG binaries and pinentry-curses.
copy_exec /usr/bin/gpg2
copy_exec /usr/bin/gpg-agent
copy_exec /usr/bin/pinentry-curses
copy_exec /usr/lib/gnupg2/scdaemon
exit 0
2. Update the initramfs:
update-initramfs -u -k all
3. Reboot the system with smart-card still present in the smart-card reader. You should be prompted to provide the smart-card PIN twice (once per encrypted disk partition).
Removing old encryption password
================================
With everything working, finally it is time to remove the old encryption password.
Perform the following steps:
1. Log-in as `root` into the machine.
2. Verify that decryption using the original password still works:
cryptsetup open --test-passphrase /dev/sda2 test
cryptsetup open --test-passphrase /dev/sda3 test
3. Remove the old decryption password from root partition device:
cryptsetup luksRemoveKey /dev/sda2
4. Enter the old password when prompted.
5. Remove the old decryption password from swap partition device:
cryptsetup luksRemoveKey /dev/sda3
6. Enter the old password when prompted.
7. Verify that decryption using the original password does not work anymore:
cryptsetup open --test-passphrase /dev/sda2 test
cryptsetup open --test-passphrase /dev/sda3 test
Setting-up separate boot device
===============================
At this point you should be pretty well off when it comes down to securing the disk decryption keys. If you are using an OpenPGP card, you are already combining something that you have (smart-card) with something that you know (smart-card PIN) in order to access the data during boot.
However, one weak link in this process is the boot device (partition in this case). Since bootloader cannot read encrypted partitions on its own, it must rely on boot partition (the Linux kernel and initramdisk) to provide this capability. This means that the boot partition itself has to be kept unencrypted.
A possible attack may involve an adversary that is able to gain access to your hard-drive for a limited time - long enough to modify the kernel and/or initramdisk to introduce a malicious payload into the system (or even replace the bootloader). This payload could easily extract disk decryption key by wrapping around the GnuPG calls to output the file in raw form and submit it over network or some other way. After that it would be sufficient for the attacker to steal the disk and have unhindered and immediate access to all data.
Even with password set in BIOS (to prevent boots from arbitrary devices), adversary could quickly remove disk from your machine, update partition on another computer, and put the disk back in, without you even noticing. This can be particularly effective since if planned correctly you might not notice the change (for example if the machine was off at the point you left it).
As a side-note, there is also a proverbial 5-dollar wrench (see [the famed XKCD comic](https://www.xkcd.com/538/)).
There is a couple of ways that could help you thwart this kind of attack. One could involve using secure boot - although, do not take my word for it, since I have not had time to deal with it in detail (in particular, not sure if initramdisk/kernel stored on boot partition can also be verified for integrity during the boot). This kind of protection would also require that the distro you are using works well with secure-boot (has properly signed bootloader/kernel images etc).
An alternative would be to boot off of a USB device (flash drive) over which you have a full control, and which you never (ever) leave anywhere without attention (yes, I know, good luck with that...).
In this section we will cover how to set-up a separate USB boot device, replacing the boot device set-up during the OS installation. You will need a USB flash drive, so make sure you have one handy.
Perform the following steps in order to set-up a separate USB boot device:
1. Insert the USB flash drive into machine.
2. Log-in as `root` into the machine.
3. Check if the USB flash drive is visible, and verify device path (should be `/dev/sdb`):
lsblk
4. Start a disk partitioner:
fdisk /dev/sdb
5. Create a new empty DOS partition table:
Command (m for help): o
6. Create a new partition:
Command (m for help): n
7. Press `ENTER` to create it as a primary partition.
8. Press `ENTER` to create it as a first partition on disk.
9. Press `ENTER` twice to allocate all space for this partition.
10. Write the changes and exit from disk partitioner:
Command (m for help): w
11. Create an `ext4` file-system on partition:
mkfs.ext4 -L boot /dev/sdb1
12. Create directory for mounting the new boot partition:
mkdir /mnt/new_boot
13. Mount the new boot partition:
mount /dev/sdb1 /mnt/new_boot
14. Copy content from the existing boot partition to the new partition:
cp -a /boot/* /mnt/new_boot/
15. Determine the UUID of the new boot partition:
blkid /dev/sdb1
16. Update the `/boot` entry in `/etc/fstab`, replacing the old boot partition UUID with the new one.
17. Unmount both boot partitions:
umount /mnt/new_boot/
umount /boot/
18. Remount the boot partition:
mount /boot/
19. Check if correct partition got mounted (should be `/dev/sdb1`):
mount
20. Reconfigure bootloader to use the USB flash drive:
dpkg-reconfigure grub-pc
21. When prompted for `Linux command line` and `Linux default command line`, just press `ENTER` to accept existing values.
22. When prompted for devices where bootloader should be installed, select `/dev/sdb`, and make sure `/dev/sda` is deselected.
23. Reboot the system, and boot from the flash drive. If all is good, the boot process should finish successfully.
24. Log-in as `root` into the machine.
25. Wipe the old boot partition:
dd if=/dev/urandom of=/dev/sda1 bs=16M
26. Wipe the initramdisk backup:
shred -u /boot/initrd.bak
27. Reboot once again, boot from the disk (not USB flash drive). Verify the boot process fails (unknown filesystem type should be reported by bootloader).
28. Reboot again, boot from USB flash drive to verify the boot process works.
Using existing keyring
======================
Previous sections all assume you do not have any GnuPG keyring of your own - mainly in order to make testing easier. In real situation you probably already have the GnuPG keyring already set-up (probably together with the OpenPGP card).
Let's have a look what you need to do to create a minimal keyring for LUKS. Instructions will assume that the keyring is available in standard location in home directory, and that you are running them as regular user.
**If you happen to be using the keyring from previous sections for testing, just make sure you don't shoot yourself in the foot by wiping out the disk decryption key. Been there, done that ;)**
Perform the following steps:
1. Insert the OpenPGP smart-card into the reader.
2. Identify secret key stored on the smart-card (the short identifier):
gpg2 --list-secret-keys
3. Export the encryption secret sub-key. Provided you have moved the encryption sub-key to smart-card, this will result in stub file only, not the actual key. Make sure to supply your own short key ID, and to preserve the exclamation sign at end (this will instruct GnuPG to export stub for only this specific sub-key):
gpg2 --export-secret-subkeys --armour 'KEYID!' > ~/enckey.asc
4. Create directory for storing the minimal keyring:
mkdir ~/luks_gnupg/
chmod 700 ~/luks_gnupg
5. Import the encryption sub-key stub into the new keyring:
gpg2 --homedir ~/luks_gnupg/ --import ~/enckey.asc
6. Verify that the key is available in new keyring:
gpg2 --homedir ~/luks_gnupg/ --list-secret-keys
7. Set-up GnuPG agent to use `pinentry-curses`:
echo "pinentry-program /usr/bin/pinentry-curses" > ~/luks_gnupg/gpg-agent.conf
8. Redirect all logging for `scdaemon` to `/dev/null`:
echo "log-file /dev/null" > ~/luks_gnupg/scdaemon.conf
9. Switch to `root` user.
10. Move the newly-created keyring to destination directory, and set-up its permissions:
mv /home/user/luks_gnupg/ /etc/
chown -R root:root /etc/luks_gnupg/
11. The rest of instructions should still apply, simply skip things like generating new GnuPG key or transferring the keys to smart-card.
Troubleshooting
===============
How do I boot using backup initramdisk?
---------------------------------------
If you happen to run into issues while booting the system, and you still have both password set as decryption key on the disk, and the backed-up initramdisk, you should be able to easily boot by modifying the boot entry. Perform the following steps:
1. When you reach the bootloader, move the cursor to `Debian GNU/Linux` entry
2. Press `e`.
3. Navigate to the *initrd* line, and set the filename to `initrd.bak`.
4. Press `Ctrl-x` to boot using the backup initramdisk.
How do I debug initramdisk if something is wrong?
-------------------------------------------------
If you ever need to figure out what is happening within the initramdisk (more specifically why mounting is failing), you can easily drop into middle of the boot process by passing in additional boot parameters.
Perform the following steps:
1. When you reach the bootloader, move the cursor to `Debian GNU/Linux` entry
2. Press `e`.
3. Navigate to the *linux* line, and append `break=premount` to it.
4. Press `Ctrl-x` to boot the system.
5. You should be dropped to shell, just prior to initramdisk mounting the disk. From within this environment you should be able to figure out why the decryption commands etc do not work. Some example commands you might find useful:
# Try to decrypt the key manually.
GNUPGHOME=/etc/luks_gnupg gpg2 --decrypt /etc/luks_gnupg/disk.key.gpg
# Try to run decryption script.
/lib/cryptsetup/scripts/luks_gnupg_decrypt.sh
6. Depending on errors you get, you could try to figure out what is happening. Perhaps a binary is missing, or some dependency? Maybe you have a typo somewhere in the configuration? A good way to figure out missing files etc is to set-up initramdisk with `strace` in addition to binaries listed above. This way you can figure out what files are being accessed during the boot.