Ubuntu hard drive encryption with external key

9 June 2008

Updated: 8-Dec-2008

We present the following improved method for installing Ubuntu desktop on an encrypted hard drive and storing the encryption key on a password-protected USB stick or memory card. It works on Ubuntu 7.10 (Gutsy Gibbon) and 8.04 (Hardy Heron). And also on 8.10 (Intrepid Ibex).

Our method is heavily based on a clever solution called GutsyLUKSTwoFormFactor from the community documentation for Ubuntu. We wish to simplify and update the procedure by taking advantage of the Ubuntu Live CD to perform the actual desktop installation, as was done in the article Encrypted root using LUKS and Ubuntu 7.04 installer. For a similar method in pure Debian, you might want to check out Wejn’s solution.

Note that there are many simpler HOWTOs and tutorials for using encryption on Ubuntu, which are based on using the Ubuntu Alternate Install CD. The major limitation of such methods is that they depend solely on a single passphrase. Our method stores a 256-bit key on a portable device, which is, in turn encrypted by a passphrase.

(1) Equipment check

The instructions on this page will clearly destroy your data, so make backups before proceeding. Also, there is no such thing as digital data security so don’t depend on it. By proceeding, you agree to audit this procedure yourself and take responsibilty for any risks of any kind that you might incur. Please help us to spot flaws and make improvements by leaving comments.

All of the following steps occur within the Ubuntu Live CD desktop environment. So, download it, burn it, boot it. We are using Ubuntu 8.04 LTS (Hardy Heron) 8.10 (Intrepid Ibex) PC (Intel x86) desktop CD (but the screenshots are still from 8.04).

When we refer to a USB drive, note that this can be a standalone flash drive or any kind of memory card or other similar device.

(2) Prepare an encrypted USB drive to store the key

After you boot into the Ubuntu Live CD environment, plug in your USB drive. If it’s already formatted, Ubuntu may try to auto-mount it, in which case it will appear as an icon on the desktop. The USB drive needs to be unmounted, so just right-click on it and unmount it.

Open a terminal window and figure out your device names. Your USB drive will be something like /dev/sda, /dev/sdb, etc.

dmesg | grep sd

Terminal window in Ubuntu

In this example (see screenshot), the dmesg output shows a 107 MB device on /dev/sdb and a much larger 8590 MB device on /dev/sda. The /dev/sda device is the hard drive and /dev/sdb is the USB drive. The instructions will always have /dev/sd@ as the USB device name and /dev/sdx as the hard drive device name. You’ll have to substitute as appropriate for your circumstances. So, we would substitue /dev/sdb for /dev/sd@ (the USB drive) and /dev/sda for /dev/sdx (the hard drive).

If your hard drive has an IDE connection rather than SATA, its device name will be /dev/hda or something similar. Figure it out like this.

dmesg | grep hd

Proceed once you know the device names for your USB drive and your hard drive.

Now, be the root user.

sudo -i

Set a couple of variables (which will only exist for the duration of this shell session) to hold the names of your drives. Important: Set these to the actual device names that you discovered above.

export MY_HDD=/dev/sdX MY_USB=/dev/sdY

Now, $MY_HDD and $MY_USB hold your device names. See them like this:

echo My hard drive is $MY_HDD and my USB device is $MY_USB

Fill the USB drive with random bits.

dd if=/dev/urandom of=$MY_USB

Install needed packages into the Live CD environment.

apt-get -y install cryptsetup

…and load the dm-crypt module.

modprobe dm-crypt

Format the USB drive with a 256-bit AES encryption (see cryptsetup man page for more info). You will be prompted for a passphrase, which should be a good one! This passphrase protects your stick, on which your computer’s encryption key will be stored.

cryptsetup luksFormat --hash=sha512 --cipher=aes-cbc-essiv:sha256 --key-size=256 $MY_USB

Now, unlock the drive with the passphrase you just provided.

cryptsetup luksOpen $MY_USB cryptkeys

The encrypted USB device is mapped to /dev/mapper/cryptkeys, which we will format as ext2 and mount.

mkfs.ext2 /dev/mapper/cryptkeys
mount /dev/mapper/cryptkeys /mnt

Make a random keyfile that’s 256 bits big using the device /dev/random. According to the random man page, it’s “suitable for uses that need very high quality randomness such as one-time pad or key generation.” Also, “when the entropy pool is empty, reads from /dev/random will block until additional environmental noise is gathered.” So, move your mouse around and execute:

dd if=/dev/random of=/mnt/mykey bs=1 count=256

Now we have a key at /mnt/mykey.

(3) Encrypt and partition your hard drive

It’s not a bad idea to fill the drive with random bits, but this takes a long time. It’s up to you if you want to skip it. Remember to substitute /dev/sdx for the hard drive device we figured out in (1). And, as previously mentioned, this will destroy your data (!!) so be sure about what you’re doing.

dd if=/dev/urandom of=$MY_HDD

Now, we will create the following partitions with fdisk, but the same effect can be achieved with a GUI tool like gparted.

  • 100 MB for /boot
  • 1 GB for swap
  • and the rest on /

We will use these one-letter commands in fdisk:

  • o create a new empty DOS partition table
  • n add a new partition
  • a toggle a bootable flag
  • p print the partition table
  • w write table to disk and exit

Run fdisk and issue these commands (see below for a lazy method).

fdisk $MY_HDD
  1. Enter o to clear the partition table.
  2. Enter n, then p for primary, then 1, then hit enter for the default, then +100M for a 100 megabyte chunk.
  3. Enter n, then p for primary, then 2, then hit enter for the default, then +1G for a 1 gigabyte chunk.
  4. Enter n, then p for primary, then 3, then hit enter twice for the defaults, to use the remainder of the disk.
  5. Enter a, then 1, to set bootable flag on the first partition.
  6. Enter p to show you the partitions.
  7. Enter w to write the partition data and quit.

The express/lazy method of doing everything at once is:

echo -e "o\nn\np\n1\n\n+100M\nn\np\n2\n\n+1G\nn\np\n3\n\n\na\n1\np\nw\n" | fdisk $MY_HDD

You should see some output like this.

Output from fdisk

So, assuming you have three partitions (boot, swap and root), roughly as outlined above, the following new variables are easily set:

export MY_BOOT=${MY_HDD}1 MY_SWAP=${MY_HDD}2 MY_ROOT=${MY_HDD}3

Once again, you can echo the variables to confirm what they are.

echo boot is $MY_BOOT, swap is $MY_SWAP, root is $MY_ROOT

Encrypt the root partition (/dev/sdx3) with the key generated in step 2.

cryptsetup --hash=sha512 --cipher=aes-cbc-essiv:sha256 --key-size=256 luksFormat $MY_ROOT /mnt/mykey

Open the drive as root, which will create the device /dev/mapper/root.

cryptsetup --key-file /mnt/mykey luksOpen $MY_ROOT root

Format the encrypted drive as ext3 (or some other file system type of your choosing).

mkfs.ext3 /dev/mapper/root

Now we’re ready to install Ubuntu on root. (But don’t close this terminal window, because we’ll use it again in a few minutes.)

(4) Install Ubuntu

Double-click the install icon on the desktop and do the following.

  • choose language and time zone, etc.
  • then choose manual partitioning
    • click on the second /dev/mapper/root line, click new partition table, then click on the free space, click new paritition, select ext3, mount point /, and check the format box if necessary
    • click /dev/sdx1on your boot partition, click edit parition, select ext3, mount point /boot, check the format box
    • click /dev/sdx2on your swap partition, click edit parition, select swap area
    • Partition Table
    • leave the rest
  • do NOT restart (!!) at the end of the installation (but do exit the installation by clicking “Continue using the live CD” because only then will it unmount your root drive)

(5) Configure the crypttab

To unambiguously reference your various partitions, it’s best to use UUIDs. The command blkid spits out the UUID for any block device. This little piece of script will collect the UUIDs of the relevant partitions.

for var in SWAP ROOT USB; do i=MY_$var; x=`eval echo \\$$i`; export MY_${var}_UUID=`blkid -o value -s UUID $x`; done

In effect, this auto-generates $MY_SWAP_UUID, $MY_ROOT_UUID and $MY_USB_UUID variables. To see all such variables check out:

env | grep MY_

Find out the UUIDs of the USB stick and all the sdx partitions. Just leave the output on the screen, we’ll use it in a second.

Unmount and close USB drive (you can unplug it too).

umount /mnt
cryptsetup luksClose cryptkeys

Mount the new system (n.b. we’re reusing the same mount point that was last used by the USB drive):

mount /dev/mapper/root /mnt

Edit the crypttab, which is easy with gedit:

Make it look something like this, substituting in UUIDs that correspond to the swap (/dev/sdx2), root (/dev/sdx3) and USB drive (/dev/sd@) device names. The screenshot might help.

Auto-generate the crypttab:

cat > /mnt/etc/crypttab << EOF
# <target name> <source device>         <key file>      <options>
swap /dev/disk/by-uuid/$MY_SWAP_UUID /dev/urandom swap
root /dev/disk/by-uuid/$MY_ROOT_UUID /dev/disk/by-uuid/$MY_USB_UUID/mykey luks,keyscript=/usr/local/sbin/keyscript
EOF

Ubuntu doesn’t come with a script that will look for a key on an external USB drive, so you have to provide one. You can use the one that we use or roll your own. A key script is a personal thing, so we recommend customizing it a little or at least familiarizing yourself with it. The following steps use our script.

cd /mnt/usr/local/sbin
wget http://mazeoflies.com/files/keyscript
chmod 755 keyscript

Basically, the crypttab is an administrator-maintained file that describes your encrypted filesystems. Here it’s saying, give the arbitrary name swap to a certain disk (by UUID) and use /dev/random/dev/urandom as the key, which will result in a pseudo random key for each boot. The option swap at the end of the line tells the system to use mkswap to create the filesystem. The next line says, give the name root to a certain disk (by UUID), with the options luks (which uses LUKS encryption extensions) and keyscript. The script that we just installed to /usr/local/sbin will receive as a parmeter:

/dev/disk/by-uuid/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/mykey

The keyscript will then use this parameter to get the key mykey from the USB drive (examine the keyscript to see how this happens).

(6) Configure the boot menu and the fstab

Mount the boot drive:

mount $MY_BOOT /mnt/boot

Edit the boot menu.

gedit /mnt/boot/grub/menu.lst &

Take out splash from the defoptions line. The splash screen needs to be removed because the keyscript we’re using doesn’t support it.

Boot Menu

Now, fstab needs to be modified to use the encrypted partitions.

gedit /mnt/etc/fstab &

For the root entry, take out UUID=xxxx… and replace with /dev/mapper/root and similarly, for the swap entry take out UUID=xxxx… and replace with /dev/mapper/swap. The result might look something like this:

Fstab

(7) Get into the system

Mount another few things in preparation to “change root” into the new system.

for dir in proc dev sys; do mount --bind /$dir /mnt/$dir; done

Change root and say hello to your new system.

chroot /mnt

Update Ubuntu and install cryptsetup (all this takes a bit of time, but hey, it’s your new system, so it’s worth it).

apt-get -y update && apt-get -y upgrade
apt-get -y install cryptsetup

Yes, that was the second time you had to install cryptsetup, but the first time was only for the Live CD environment. This time it’s for your system.

(8) Update and verify initramfs

update-initramfs -u ALL
update-grub

This process will examine the fstab and crypttab modifications we’ve made and build an appropriate image for the initial root filesystem. The initramfs, which is a gzipped cpio archive living in /boot/, can be examined by unzipping it.

mkdir /tmp/initramfs
cd /tmp/initramfs
gunzip -c -9 /boot/initrd.img-`uname -r` | cpio -i -d -H newc --no-absolute-filenames

The first thing to check is for the presence of a file called cryptroot.

cat conf/conf.d/cryptroot

It should be similar to the crypttab, e.g.

target=root,source=/dev/mapper/root,key=/dev/disk/by-uuid/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/keyfile.key,keyscript=/keyscripts/keyscript

Note that the path of the keyscript is now /keyscripts/keyscript, which you should also be able to find in the unpacked initramfs.

cat keyscripts/keyscript

This outputs the keyscript. Note that the update-initramfs command worked some magic to find the keyscript that we put in /usr/local/sbin and stuff it into the initramfs image.

(9) Reboot

exit
for dir in proc dev sys boot; do umount /mnt/$dir; done
cd
umount /mnt
cryptsetup remove root

Cross your fingers. Reboot and plug in your USB stick. If everything worked, your new system prompt you for a password to unlock the stick and then boot you into your new system.

Boot prompt

(10) Post-installation

It worked? Nice. Open a terminal window.

cat /proc/swaps

The output should mention /dev/mapper/swap as the swap partition. Then try out these commands. They show the status of the two encrypted partitions.

sudo cryptsetup status swap
sudo cryptsetup status root

Summary

Now the hard drive is partitioned into three parts. The swap partition is encrypted with a random key. The root partition is encrypted with a 256-bit key that is stored on a USB stick, which itself is encrypted with a password. Note that the third, boot, partition is not encrypted, and is therefore susceptible to being modified by anyone having access to the machine. Also, if someone gains access to your machine while it’s on or in sleep mode, the key can be found in the RAM and the whole encryption scheme falls apart. There is a more detailed, but not exhaustive, discussion of vulnerabilities in the GutsyLUKSTwoFormFactor article and elsewhere on the internet. As stated in the introduction, spend time and energy auditing this procedure in proportion to your security needs.

antipode

,

---

Comment

  1. This is a great article. The reason I haven't encrypted my filesystems up to now is that I see a high risk of losing my data by either forgetting the password, losing the encryption key (if it's stored somewhere) or somehow otherwise corrupting a key part of a file index or something. So many times I've accidentally screwed up an important part of my OS (especially on Windows computers) and had to rip out the hard drive and recover the data on a second computer. In this case, the whole system is only as good as not losing the USB key or not having it corrupted accidentally, like being stepped on or chewed up by a cat. In the end, the door to my apartment is (hopefully) enough security for my PC.

    — Matt · 11 June 2008 · #

  2. Hi antipode, thanks for the pointer - as you can see I found my way here :) I'll need time to look at this in more detail, but it looks like a great way of doing things... and it's a big way to kick off a new blog, too!

    yungchin · 25 June 2008 · #

  3. Really great article, thank you very much ! I successfully setup the encryption on my eeePC with eeebuntu netbook.

    — Glucose · 30 June 2008 · #

  4. If you tend to do too many things at the same time (like me) and then mess up the disk UUIDs (grub tells you an initramfs error that it cannot find some device), you don't need to start over. Instead, so this: - reboot from the Live CD - open your USB key device:
    cryptsetup luksOpen /dev/sd@ cryptkeys
    - mount it to get to the key file:
    mount /dev/mapper/cryptkeys /mnt
    - open your root file system:
    cryptsetup --key-file /mnt/mykey luksOpen /dev/sdx3 root
    - continue as in step 5 above, trying to pay more attention ;-) Another warning: on the first successful boot, the system seemed to hang. Turned out it was doing a file system check and booting resumed after 5 minutes. The second boot went noticeably faster! So be patient.

    — Robert · 16 September 2008 · #

  5. Thank you for your superb article! Works like charm for me.

    — Michael · 28 September 2008 · #

  6. Hi. Thanks for the great article. Worked for me several times on 8.04. Anyway, I've tried the procedure on 8.10, but it doesnt work. Got unknown fs type error, I guess there is a problem with /dev/mapper/rootp1, which is the only difference in procedure. Has anyone tried this on ubuntu 8.10? Thanks.

    — mike · 2 December 2008 · #

  7. @mike: Thanks for pointing that out. We've updated the article.

    — antipode · 9 December 2008 · #

  8. Nice update, antipode!

    Inspired by http://www.debianhelp.org/node/1116 I tried to add some stealth to the encryption: In the BIOS I set the boot order to 1st USB and 2nd internal hard drive. Then I installed /boot on an old 256MB USB stick and in the advanced settings of the Ubuntu installer right after partitioning, I told it to install GRUB in the boot sector of the USB stick. Now if the stick is not present, the system boots into the decoy Windows as if there was no Linux on that machine. If the stick is there it boots from the stick and GRUB lets me pick what I want. Haven’t yet polished up the details on automatically unmounting /boot after I am done booting yet.

    For the above to work you need to fill only individual partitions with random bytes and consequently only encrypt individual partitions as opposed to the whole hard disk. You also need different partitions on the USB stick (1 for the key, 1 for /boot, and maybe a Windows decoy partition so that the stick looks innocent enough to some random thief or TSA guy).

    Then I like to have /home on a separate partition. If I set it up like the root partition, I need to enter my passphrase twice, even if the same key is used. Since I find that annoying, I copied the key to /etc and after the root partition is mounted, the other partitions in /etc/crypttab don’t use the keyscript anymore but directly refer to the key.

    Last but not least I changed the keyscript to fake a kernel panic, i.e., it does not ask for a password but just displays some error messages and “hangs”. You can still enter the password and booting resumes as normal, but maybe that can discourage an attacker as well if he thinks the hard drive is toast.

    Just because I am paranoid doesn’t mean they are NOT after me! ;-)

    — Robert · 16 January 2009 · #

  9. hey! this is a well understandable, wonderful tutorial! considering that i knew nothing of terminal nor linux, it helped me to get a grasp of how things work there… however i encountered a few problems… I spent the last three days trying to encrypt ubuntu with various other tutorials the alternate cd etc… none of them worked, so I am referring back to this one, as it also is the one which suits my needs the best.
    I want a dual boot osx (ppc) ubuntu (encrypted) computer… the normal dual boot without encryption works… so for the encrypted installtion I just took the linux and the newworld boot partitions left over from the normal installation… everything works fine up until (6) as somehow there is no grub menu list as it uses yaboot for startup (grub insn’t even installed)…

    I would be very grateful if someone could tell me how to configure all this with yaboot… I have to say here again: I am absolutley new to this…
    roberts preceeding post would be the optimal solution… but I am entirely happy, if I get it to work the normal way too…
    by the way, I am using 8.10 …

    best wishes,
    opk

    — opk · 23 April 2009 · #

  10. @opk: I’ve never tried this with a PPC and I’ve never used yaboot, so I can’t really be of much help. The best info I can find that seems to address your needs appears in this 2004 article in Linux Journal but you’ve probably looked at that already!

    — antipode · 26 April 2009 · #

  (Please preview your comment before submitting it.)
Individual article
---