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:
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:
Boot Debian installation CD.
Select option Install
.
Set language to English
.
Set country to Sweden
.
Set keyboard layout to en_US.UTF-8
.
Set keymap to American English
.
Set hostname to encrypt
.
Set domain to local
.
Set password for the root account.
Set full name for the user to My User
.
Set username for the user to user
.
Set password for the user account.
Partition the disk:
Set method to Manual
.
Wipe-out any available partitions and create a new partition table.
Create partitions as follows:
256MB
, Primary
, Beginning
, Ext4
, /boot/
8GB
, Primary
, Beginning
, physical volume for encryption
remaining space
, Primary
, physical volume for encryption
Select option Configure encrypted volumes
.
Write changes to disk.
Select Create encrypted volumes
, select /dev/sda2
.
Select Create encrypted volumes
, select /dev/sda3
.
Select Finish
.
Confirm operation.
Wait for erase to finish.
Provide encryption password for first partition. This password will be replaced at later stage.
Provide encryption password for second partition. This password will be replaced at later stage.
Set-up the two new encrypted partitions:
sda2_encrypt
should be Ext4
with mount point /
.sda3_encrypt
should be swap
.Finally finish partitioning and write changes to disk.
Select Debian archive mirror.
Opt out of popularity contest.
On software selection screen pick:
SSH server
standard system utilities
When prompted, install GRUB boot loader to /dev/sda
.
Finally Reboot into the new system, and provide decryption passwords.
We will start off with setting-up a GnuPG keyring.
Perform the following steps:
Log-in as root
into the machine.
Create directory where the keyring will be stored:
mkdir /etc/luks_gnupg/
chmod 700 /etc/luks_gnupg/
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
Pick RSA and RSA
as key type.
Set length to 2048
bits.
Set expiration to 0
.
Confirm the key does not expire.
Set real name to My User
.
Set email address to myuser@localhost
.
Set empty comment.
Confirm identity.
Provide password for GnuPG key.
Install PIN entry application:
apt-get install pinentry-curses
Configure GnuPG agent to use pinentry-curses by creating file /etc/luks_gnupg/gpg-agent.conf
:
pinentry-program /usr/bin/pinentry-curses
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:
Generate 256-bit random key:
dd if=/dev/random bs=1 count=256 > /etc/luks_gnupg/disk.key
Add the generated decryption key to first encrypted partition:
cryptsetup luksAddKey /dev/sda2 /etc/luks_gnupg/disk.key
Provide existing password.
Add the generated decryption key to second encrypted partition:
cryptsetup luksAddKey /dev/sda3 /etc/luks_gnupg/disk.key
Provide existing password.
Encrypt the decryption key using GnuPG private key:
gpg2 --homedir /etc/luks_gnupg/ --recipient myuser@localhost --encrypt-files /etc/luks_gnupg/disk.key
Shred and remove the the plaintext disk encryption key:
shred -u /etc/luks_gnupg/disk.key
In order to use GnuPG, we will create a decryption script that will invoke GnuPG with correct parameters.
Perform the following steps:
Create script for obtaining decryption key /usr/local/sbin/luks_gnupg_decrypt.sh
:
1 2 3 4 5 6 |
|
Fix script permissions:
chown root:root /usr/local/sbin/luks_gnupg_decrypt.sh
chmod 750 /usr/local/sbin/luks_gnupg_decrypt.sh
Test decryption of first partition:
/usr/local/sbin/luks_gnupg_decrypt.sh | cryptsetup open --test-passphrase /dev/sda2 test
Test decryption of second partition:
/usr/local/sbin/luks_gnupg_decrypt.sh | cryptsetup open --test-passphrase /dev/sda3 test
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:
Back-up the existing initramdisk:
cp -a /boot/initrd.img*-amd64 /boot/initrd.bak
Update /etc/crypttab
by appending the following to both lines:
,keyscript=/usr/local/sbin/luks_gnupg_decrypt.sh
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
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
|
Fix script permissions:
chown root:root /etc/initramfs-tools/hooks/luks_gnupg
chmod 750 /etc/initramfs-tools/hooks/luks_gnupg
Update the initramdisk:
update-initramfs -u -k all
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:
Reboot the machine.
Boot Debian from disk.
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.
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:
Log-in as root
into the machine`.
Install additional package that allows GnuPG to use smart-cards:
apt-get install scdaemon
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
Insert smart-card into smart-card reader connected to the machine.
Transfer the encryption private key to smart-card:
Start command for manipulating the keys:
gpg2 --homedir /etc/luks_gnupg/ --edit-key myuser@localhost
Toggle to manipulation of secret key material:
toggle
Select the encryption sub-key:
key 1
Transfer the encryption sub-key to smart-card:
keytocard
When prompted where to store the key, type 2
(for (2) Encryption key
entry).
You will be prompted for the key password first. Provide it.
Afterwards you will be prompted for the card admin password. Provide it.
Wait for operation to finish.
Save keyring changes and exit:
save
Verify that decryption now uses the OpenPGP smart-card:
/usr/local/sbin/luks_gnupg_decrypt.sh | cryptsetup open --test-passphrase /dev/sda2 test
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:
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
|
Update the initramfs:
update-initramfs -u -k all
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).
With everything working, finally it is time to remove the old encryption password.
Perform the following steps:
Log-in as root
into the machine.
Verify that decryption using the original password still works:
cryptsetup open --test-passphrase /dev/sda2 test
cryptsetup open --test-passphrase /dev/sda3 test
Remove the old decryption password from root partition device:
cryptsetup luksRemoveKey /dev/sda2
Enter the old password when prompted.
Remove the old decryption password from swap partition device:
cryptsetup luksRemoveKey /dev/sda3
Enter the old password when prompted.
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
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).
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:
Insert the USB flash drive into machine.
Log-in as root
into the machine.
Check if the USB flash drive is visible, and verify device path (should be /dev/sdb
):
lsblk
Start a disk partitioner:
fdisk /dev/sdb
Create a new empty DOS partition table:
Command (m for help): o
Create a new partition:
Command (m for help): n
Press ENTER
to create it as a primary partition.
Press ENTER
to create it as a first partition on disk.
Press ENTER
twice to allocate all space for this partition.
Write the changes and exit from disk partitioner:
Command (m for help): w
Create an ext4
file-system on partition:
mkfs.ext4 -L boot /dev/sdb1
Create directory for mounting the new boot partition:
mkdir /mnt/new_boot
Mount the new boot partition:
mount /dev/sdb1 /mnt/new_boot
Copy content from the existing boot partition to the new partition:
cp -a /boot/* /mnt/new_boot/
Determine the UUID of the new boot partition:
blkid /dev/sdb1
Update the /boot
entry in /etc/fstab
, replacing the old boot partition UUID with the new one.
Unmount both boot partitions:
umount /mnt/new_boot/
umount /boot/
Remount the boot partition:
mount /boot/
Check if correct partition got mounted (should be /dev/sdb1
):
mount
Reconfigure bootloader to use the USB flash drive:
dpkg-reconfigure grub-pc
When prompted for Linux command line
and Linux default command line
, just press ENTER
to accept existing values.
When prompted for devices where bootloader should be installed, select /dev/sdb
, and make sure /dev/sda
is deselected.
Reboot the system, and boot from the flash drive. If all is good, the boot process should finish successfully.
Log-in as root
into the machine.
Wipe the old boot partition:
dd if=/dev/urandom of=/dev/sda1 bs=16M
Wipe the initramdisk backup:
shred -u /boot/initrd.bak
Reboot once again, boot from the disk (not USB flash drive). Verify the boot process fails (unknown filesystem type should be reported by bootloader).
Reboot again, boot from USB flash drive to verify the boot process works.
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:
Insert the OpenPGP smart-card into the reader.
Identify secret key stored on the smart-card (the short identifier):
gpg2 --list-secret-keys
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
Create directory for storing the minimal keyring:
mkdir ~/luks_gnupg/
chmod 700 ~/luks_gnupg
Import the encryption sub-key stub into the new keyring:
gpg2 --homedir ~/luks_gnupg/ --import ~/enckey.asc
Verify that the key is available in new keyring:
gpg2 --homedir ~/luks_gnupg/ --list-secret-keys
Set-up GnuPG agent to use pinentry-curses
:
echo "pinentry-program /usr/bin/pinentry-curses" > ~/luks_gnupg/gpg-agent.conf
Redirect all logging for scdaemon
to /dev/null
:
echo "log-file /dev/null" > ~/luks_gnupg/scdaemon.conf
Switch to root
user.
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/
The rest of instructions should still apply, simply skip things like generating new GnuPG key or transferring the keys to smart-card.
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:
When you reach the bootloader, move the cursor to Debian GNU/Linux
entry
Press e
.
Navigate to the initrd line, and set the filename to initrd.bak
.
Press Ctrl-x
to boot using the backup initramdisk.
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:
When you reach the bootloader, move the cursor to Debian GNU/Linux
entry
Press e
.
Navigate to the linux line, and append break=premount
to it.
Press Ctrl-x
to boot the system.
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
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.