Ubuntu Lucid Lynx 10.04 Full Disk Encryption with USB Key Authentication
From Linux Full Disk Encryption
Introduction
Most modern operating systems include some form of full disk encryption, designed to protect your private information. Linux supports encrypted filesystems in a way that is more powerful and extensible than most operating systems.
In this howto, I’ll show you how to use an encryption key on a USB stick and a password to encrypt an Ubuntu Lucid Lynx installation. Why a key on a USB stick you ask? To provide two factors of authentication. Your key and your password. If you keep your USB stick on your person (for example on a keyring), if your laptop is compromised it will be cryptographically impossible to unlock it with a password only.
Another more “creative” feature of this system is that it allows a person to destroy information in an instant. Destroy the USB key and all is lost. For this reason, I highly recommend backing up your USB key!
Technology
The Linux Unified Key Setup (LUKS) system will be used for encrypting the partition and the Logical Volume Manager (LVM2) will be used to “divide up” the encrypted partition. We use LVM2 as it allows us to unlock multiple logical volumes with one encryption key. Lastly, we use GPG to password protect the key on your USB stick. If you lose it, your key won’t be compromised as long as your password is strong.
History
This howto has been implemented in various shapes and forms since the release of Ubuntu Hardy. It’s gone through numerous changes over time. I'm very thankful to all the readers of my blog who have had the guts to try this tutorial and help improve the quality of the process.
This howto has been designed to be much faster than the previous versions, so if you’re interested in learning more about the theory, TODO: put at bottom (see other posts).
Features
- Real Full Disk Encryption. We encrypt the entire drive, not just everything except for /boot
- The Lucid version of this tutorial introduces a few changes:
- Support for the splash screen (Plymouth). It no longer drops to a shell to ask for the password
- A wiki style tutorial so the community can contribute
- A new domain name lfde.org (Linux Full Disk Encryption) - coming in the next few days
- An SVN repository to track changes (see http://lfde.org/lfde/browser)
- I've fixed a few security vulnerabilities that Jost pointed out
Conventions
In this tutorial, I’ll ask you to set a number of environment variables. It’s a good idea to set these variables as it allows you to cut and paste into a terminal window. Once you’ve followed this howto a number of times, you’ll be able to set up encrypted systems in a few minutes more than usual by cutting and pasting.
Note: I highly recommend opening this tutorial in a Firefox window on the computer you’re installing. A lot of the steps in this tutorial are very sensitive to typos and errors, so using cut-and-paste is recommended.
Environment variables are in the form:
${VARIABLE}
and are set with:
export VARIABLE=[blah]
Anything in [square brackets] needs to be replaced with your own information.
Steps
Boot the Ubuntu Lucid LiveCD
Simply place the Ubuntu LiveCD into the drive, and choose the option to try the desktop first. We use this option to give us some more flexibility with the user interface.
Start a terminal and become root
Start by opening a terminal:
Applications -> Accessories -> Terminal
Almost all of our commands will need root access so use sudo to become root:
sudo bash
Find Your USB Key
You need to create one partition on your USB key and format it as ext3 or ext4. Creating a partition is not covered in this tutorial, because hey, if you’re setting up a LUKS encrypted drive, you probably don’t need to be told how.
All you need to do is fdisk your USBKEY with at least one partition (marked as type “Linux”). Don’t bother formatting it yet as we’ll do that later. Just remember that this USB disk will be completely wiped.
When you insert your USB key, make sure any partitions on it are completely unmounted
Set the following environment variable that contains the device node for your USB key.
export USBKEY=[your device node]
For example, if your USB key is at /dev/sdb1, execute the following:
export USBKEY=/dev/sdb1
Set up some more environment variables
Because we're using GPG to protect our strong encryption key, we need to create a new GPG keyring. The convention is to use an email address to keep track of your keys. In our case, we only want to use this GPG keyring for our full disk encryption. We don't want to use it for protecting actual email, because we shouldn't leave the USB key in the drive any longer than is necessary to boot the system.
So you don't get your real GPG keyring confused with your full disk encryption keyring, come up with a kind of "internal" email address. In my case, I use "inet" as my domain suffix internally, so I simply make it mark@inet: Set the EMAIL environment variable to this internal email address:you want to
export EMAIL=[your internal email address]
So I would run the following command:
export EMAIL=mark@inet
Set an environment variable with the hostname you will use for your new system. This will be used to name our key files and allow us to use the USB key in multiple computers (note that this isn't currently supported using this howto, but will be soon).
export HOSTNAME=[your host name]
In my case, I called one of my computers “nightcrawler”, so I would run:
export HOSTNAME=nightcrawler
The CRYPTDISK variable will contain the partition that contains the encrypted filesystem. You can have as many partitions in here as you want once we set up LVM. Note that we use the entire device as we place our /boot partition on our USBKEY.
export CRYPTDISK=[encrypted device node]
For example, I would run:
export CRYPTDISK=/dev/sda
Wipe your hard disk
I recommend completely wiping your drive with random data at this stage. To wipe it, run the following command. It will take a reasonable amount of time (could take hours depending on how large your disk is).
shred -v -n 1 ${CRYPTDISK}
If you have an SSD, you might not want to run the above command as SSDs lose performance when completely full. Also a 35-pass-shred introduces excessive wear to the drive and the effect of overwriting the same cell multiple times will most likely be counteracted by the drive's wear leveling firmware anyways. If you're paranoid you could fill the drive with pseudo-random data instead, but there is a fundamental problem with disguising empty space on SSDs so read the following paragraph first, before doing
dd if=/dev/urandom of=${CRYPTDISK} bs=512M
about wiping SSDs: Completely filling SSDs with (pseudo)data is problematic. The pattern doesn't matter. What's important for SSDs is that the drive's firmware is aware of which blocks are in use and which are not. As soon as a block is written to once it is flagged as 'in use', and the ATA-TRIM-Command is necessary to reset that flag when files are deleted. Support for this command requires 2.6.33 kernel and hdparm 9.28 at least, both are not in Lucid Lynx by default, so by zeroing/filling the drive you will mark every single cell on the SSD as 'in use'. Flash Memory can only be erased in large blocks, so every time a used storage cell is modified the entire block has to be read, cached, erased, modified in cache and rewritten, which is slow and causes additional wear. If the SSD knows it has unused blocks it won't flash the old one right away, it will just use a fresh one to store the modification, quickly remap the file system pointer to that block and do a garbage collection later. This is of course much faster. The performance drop can be massive! Without zeroing it took me about 7 minutes to install ubuntu, after the zeroing it took 35 hours to copy the same files to the same high-end SSD! This effect is cumulative, you will eventually run into it even if you don't completely wipe the SSD, it will just take longer. This is not a bug, it's a hardware limitation of flash memory. Unfortunately it collides with one of the goals of encryption (to make data indistinguishable from noise). If you're daring and don't mind trading in a little security to keep your performance you can get TRIM-Support for encrypted filesystems in Lucid, but it requires patching the kernel and manually installing a newer hdparm, so it's to be considered experimental at best. More info is available on hdparm's forums. If you do decide to zero your drive have dd write in large chunks. It won't help with the performance issue, but at least it gives the firmware a chance to pool and rearrange the write requests to flash and write whole blocks of data at a time, rather than flashing every block several times because data is arriving bit by bit.
Choose an encryption algorithm
There are a large number of encryption algorithms you can choose from. You can see benchmarks for some of the common ciphers at http://blog.wpkg.org/2009/04/23/cipher-benchmark-for-dm-crypt-luks/
For our purposes, we list a few popular ones.
- aes-cbc-essiv:sha256
- blowfish-cbc-essiv:sha256
If your hardware can handle it (this includes most modern PCs/laptops), use aes-xts-plain:sha512. If you have lower end hardware, use aes-cbc-essiv:sha256. Both are completely unlikely to be cracked by anyone except a particular three-letter-agency....maybe.
Choose your cipher by setting the following environment variable:
export CIPHER=[your cipher choice]
For example, I would choose:
export CIPHER=blowfish-cbc-essiv:sha256
First time USB Key preparation
VERY IMPORTANT: This section only needs to be run once. You will be able to use your USB key on multiple systems in the near future, so don’t run this again when you install your second encrypted system, otherwise you’ll delete your encryption keys!!!
Format your USB key with ext4 and label it as “usbkey”:
mkfs.ext4 ${USBKEY} -L usbkey
Mount your USB key by pulling it out of the computer, waiting a few seconds, then inserting it again. It should be mounted at the following location:
/media/usbkey
Create some directories to hold your configuration and keys:
mkdir -p /media/usbkey/.crypto
Next you need to create a GPG keyring. GPG will be used to encrypt your encryption key that is stored on your USB key. This means that if you lose your USB key, your key is not necessarily compromised. Run the following command to create the GPG key:
gpg --homedir /media/usbkey/.gnupg --gen-key
Choose the following options:
* DSA and Elgamal * 2048 bits * 5y
- Check the values and enter “y” if it’s correct. Enter your real name.
- When prompted, use the email address you set in the ${EMAIL} environment variable. If you don’t do this, GPG will not find your encryption key. In my case, I typed in “mark@inet”.
- Don’t bother entering a comment
- Select “O” for OK when you’re done.
- Choose a good, secure password. This will be the password you are prompted for when you enter your USB key.
It’ll take a while to generate the GPG key.
Create the encrypted disk
We need to install some extra packages, as the Live CD does not have all the encryption tools we need:
apt-get update apt-get install lvm2
Note: there is a known bug about updating initramfs-tools (a requirement for lvm2) on live systems running from USB-Sticks. See [1] for details. If you run into it use the CD or a "non-persistent" USB-Livesystem (discards changes on shutdown) as a workaround.
Next, load the modules needed to manipulate encrypted filesystems:
modprobe dm-crypt modprobe sha256 modprobe sha512 modprobe aes modprobe blowfish
Create the encryption key. This is the raw, unencrypted key that we will protect with GPG.
dd if=/dev/urandom | tr -d '\n' | dd bs=32 count=1 > /dev/shm/user.key
Now we need to encrypt the encryption key with GPG:
gpg --homedir /media/usbkey/.gnupg -sea -r ${EMAIL} /dev/shm/user.key
This creates the encrypted file /dev/shm/user.key.asc.
Now move the encrypted key to your USB stick:
cp -p /dev/shm/user.key.asc /media/usbkey/.crypto/${HOSTNAME}.key.enc
Now set up LUKS on the partition to be encrypted by running the following command:
cryptsetup -c ${CIPHER} luksFormat ${CRYPTDISK} /dev/shm/user.key
Open the encrypted device:
cryptsetup -c ${CIPHER} luksOpen -d /dev/shm/user.key ${CRYPTDISK} cryptdisk
This command will create a device node for the encrypted disk in /dev/mapper/cryptdisk. This is the encrypted version of ${CRYPTDISK}. For example, in my case, my /dev/sda2 drive will map to /dev/mapper/cryptdisk. Neat!
Set up LVM partitions
First of all, create a physical volume out of the new encrypted disk. This allows LVM to use the device.
pvcreate /dev/mapper/cryptdisk
Create a volume group with out of the physical volume:
vgcreate cryptdisk /dev/mapper/cryptdisk
Now we can create two logical volumes. One for the root filesystem and one for swap. The size of your swap partition will depend on how much memory you have. As a rule, use double the amount of memory you have. Set an environment variable with the size of your swap partition (in megabytes):
export SWAPSIZE=[your swap size]
For example, in my case, I need 4096M swap, so I would run
export SWAPSIZE=4096
Now create the swap logical volume:
lvcreate --name swap --size ${SWAPSIZE}M cryptdisk
Now we need to use the remaining space for the root partition. To determine the remaining space, type the following command:
vgdisplay
The output should look something like:
--- Volume group --- VG Name cryptdisk System ID Format lvm2 Metadata Areas 1 Metadata Sequence No 2 VG Access read/write VG Status resizable MAX LV 0 Cur LV 1 Open LV 0 Max PV 0 Cur PV 1 Act PV 1 VG Size 186.07 GBPE Size 4.00 MB Total PE 47633 Alloc PE / Size 512 / 2.00 GB Free PE / Size 47121 / 184.07 GB VG UUID xa22XM-UFvT-Qh0T-lmqf-fihf-yNWx-NLSMlR
Look at the line that says Free PE/Size (in bold). Note the first number (in my instance 47121), which is the number of “extents” remaining on the disk (i.e. free space). Now create the root partition with the following commands, replacing the EXTENTS value with the number of “extents” you recorded above:
export FREE_EXTENTS=[your free extents]
In my case, I would execute:
export FREE_EXTENTS=47121
Now create a logical volume for your root partition with the remaining disk space:
lvcreate --name root -l ${FREE_EXTENTS} cryptdisk
Your LUKS and LVM setup should now be complete. Verify everything is working by looking in /dev/mapper for cryptdisk, cryptdisk-root and cryptdisk-swap:
ls /dev/mapper/cryptdisk*
If the device nodes are not there, double check the steps above as the operating system will not work without them.
Format the partitions
In previous versions of this howto, we didn’t need to do these steps, as we used to get the installer to do the filesystem creation. Around about Ubuntu Intrepid, the installer was changed so it would insist on creating partition tables on devices. I.e. the installer no longer lets us use “whole devices”.
We get around this by creating the devices first, so the installer thinks they’re valid disks.
Format the root and swap partitions with the following commands:
mkfs.ext4 /dev/mapper/cryptdisk-root mkswap /dev/mapper/cryptdisk-swap
Unmount USB Key
In order for the installer to work, unmount the USB key before continuing.
umount ${USBKEY}
Install Ubuntu
WARNING: Make sure not to fall back into old habits and use ${CRYPTDISK} as your root partition, as the installer will not stop you. Make sure you use cryptdisk-root for your root partition and cryptdisk-swap for your swap partition. The only "disk" partition (i.e. /dev/sd*) you should use in the installer is your boot partition (i.e. ${USBKEY}).
IMPORTANT: Make sure you keep the terminal window that you’ve been using open during the installation. We’ll need it later with all of it’s environment variables intact.
Double click the installer on the desktop. Be sure to keep the same terminal window open as we will use it later (including the environment variables we’ve set up).
Navigate through the menus and choose the options you usually would, until you reach the partitioning section.
When the partitioner appears, choose “Specify partitions manually (advanced) and follow the directions below.
Configure your disk as follows:
Use the device /dev/mapper/cryptdisk-root as your root partition (i.e. your / partition). You can make this ext3 or ext4. Both work.
Use the device /dev/mapper/cryptdisk-swap as your swap partition. This should be formatted as swap.
Use the device you stored in ${USBKEY} (i.e. your USB drive) as your boot partition. DO NOT FORMAT IT.
When you are done with the partitioner, click next. When you’re asked to review the partitions, click “Advanced”. You need to install GRUB 2 on your USB drive instead of the hard drive. This helps us maintain the physical integrity of the drive when you’re not around. Drop down the list and change it to the usb device node, not the partition. So in my case, I would choose /dev/sdb (not /dev/sdb1).
Now install the system like you normally would, paying particular attention to the warning below.
IMPORTANT: Don’t close the dialog box that asks you whether you want to reboot your system. You will lose your /target mount if you do. We need to keep this dialog box open and switch back to the terminal window we were using before.
Post Installation
After the installation completes, the newly built root filesystem will be mounted on /target. We need to modify some of the new system files to enable an encrypted root partition.
Switch back to the terminal window you were using before.
We need to take a few steps to ensure that the system mounted under /target resembles a real running Linux installation. To do this we have to mount the proc and dev filesystems:
mount -o bind /proc /target/proc mount -o bind /dev /target/dev mount -o bind /sys /target/sys
Installing Additional Packages
The installer does not install all of the packages we need to support an encrypted filesystem. The instructions differ based on which version of the Ubuntu installer you're using (CD or DVD).
If you're running the normal Ubuntu Live CD
Run the following commands to download and install the packages to your new system:
apt-get -d --reinstall install watershed dpkg --root /target -i /var/cache/apt/archives/watershed*.deb apt-get -d --reinstall install cryptsetup apt-get -d --reinstall install libreadline5 dpkg --root /target -i /var/cache/apt/archives/cryptsetup*.deb dpkg --root /target -i /var/cache/apt/archives/libreadline5*.deb dpkg --root /target -i /var/cache/apt/archives/libdevmapper-event*.deb dpkg --root /target -i /var/cache/apt/archives/lvm2*.deb
If you're running the Ubuntu Live DVD
Run the following commands to install the packages to your new system:
dpkg --root /target -i /cdrom/pool/main/w/watershed/watershed*.deb dpkg --root /target -i /cdrom/pool/main/c/cryptsetup/cryptsetup*.deb dpkg --root /target -i /cdrom/pool/main/r/readline5/libreadline5*.deb dpkg --root /target -i /cdrom/pool/main/l/lvm2/libdevmapper-event*.deb dpkg --root /target -i /cdrom/pool/main/l/lvm2/lvm2*.deb
Create the crypttab
The /target/etc/crypttab file is similar to the /etc/fstab file, only containing information on encrypted filesystems to be opened. Create your /target/etc/crypttab file by issuing the following commands:
echo cryptdisk ${CRYPTDISK} none luks,cipher=${CIPHER},keyscript=/sbin/cryptgetpw > /target/etc/crypttab
Keyscript
The keyscript allows us to extend the functionality of the encrypted filesystem. This is where all the magic happens to decrypt the USB stick and ask for a password.
Download and install the keyscript with the following commands:
wget http://lfde.org/trac/browser/trunk/scripts/cryptgetpw?format=txt -O /target/sbin/cryptgetpw
chmod a+x /target/sbin/cryptgetpw
Configuration
Create a configuration file for our encryption setup. The config file tells the keyscript the hostname and the UUID of the USB stick.
Run the following commands:
echo HOSTNAME=${HOSTNAME} > /target/etc/initramfs-tools/conf.d/crypto
Extra Modules
A number of modules are loaded at boot time. We need to add a few more modules to support things like encryption and mounting a USB key. Run the following command to add the modules to the boot process:
echo -e sd_mod\\nehci_hcd\\nuhci_hcd\\nohci_hcd\\nusb_storage\\nnls_cp437\\nnls_iso8859_1\\ndm-cryptaes\\nsha256\\nsha512\\nblowfish\\nloopcbc\\nblkcipher\\n >> /target/etc/initramfs-tools/modules
Initramfs Hook Script
The “initramfs” is a file that contains all the things needed to boot a Linux system. We need to add a script to the system that copies the encrypted filesystem configuration to the initramfs.
Run the following command to download the “hook” file:
wget http://lfde.org/trac/browser/trunk/scripts/gpg_cryptroot?format=txt -O /target/etc/initramfs-tools/hooks/gpg_cryptroot chmod a+x /target/etc/initramfs-tools/hooks/gpg_cryptroot
Update GRUB2 and initramfs
Enter the new system using the chroot command and update GRUB2 and initramfs:
chroot /target update-grub2 update-initramfs -u ALL
Reboot
Now reboot. If all goes well, you should be prompted to insert your USB key and password!
If you have any questions about the process including success and failure reports, please let me know!
Reconfigure your bios
You need to configure your bios to boot from a USB disk over a hard disk. When the USB key is not present you should see something like "Missing operating system". When it is plugged in though, it should boot up Ubuntu and ask you for your passphrase.
Special Requirements
Bypassing the need for a USB key
Some people may want to remove the need for a USB key. Some scenarios include:
- The system has no keyboard
- The system is remote
- You want to pass the system on to a friend who doesn't like the USB boot process
See the page Bypass Key on how to remove the need for the key, This will store the unencrypted key in the initramfs making it readable to anyone who can read your /boot partition, but it is probably better than nothing.
Troubleshooting
If things go wrong, try some of the tips below.
Platform Specific Issues
If you have any platform specific issues, please report them. Below are a list of known issues:
Feedback and discussion
For now, please leave all feedback in the following blog post until I have time to set up a forum:
