Using autofs for GPG keys on a USB stick

The problem

GPG potentially provides a great deal of security, both for encryption and signing. However, like all crypto programs the security provided is only as secure as the keys used. Therefore, keeping private keys safe is important.

Like a lot of Free Software developers, I do much of my development and email work on my laptop. Quite a bit of this work need GPG (e.g. for signing packages before uploading them into the Debian archive), so I need to have access to my key(s) on my laptop. Laptops present a significant security risk here: rather than living in a reasonably secure location at home or in the office, they travel around and get used in all sorts of locations. This means they're much more likely to be lost or stolen, along with whatever data is on them. Losing your GPG private key is bad!

To mitigate against this, many people choose not to store their private keys directly on their laptops; the most common way to do this is using a USB stick: small, cheap removable storage that should be easy to keep secure (it will fit in a pocket!). So long as an attacker does not get the USB stick, the key is secure.

However, even that is not ideal. USB sticks are still quite easy to lose. An even better solution would be to make sure that an attacker needs both the laptop and the USB stick to be able to gain access to the GPG private key. There are quite a few ways to do this (see other options below). Here is my solution.

My solution

I use a combination of the following software to store my GPG key on a USB stick so that I can safely use GPG on my laptop:

I'm using the versions of both as they're found in Debian Etch (4.0r2) - I run "stable" as the base operating system on my laptop. I would expect all of the following to work with newer versions, but I can't strictly guarantee that.

I use autofs already on my laptop for automatically mounting various network and other filesystems like removable media. There are other similar options available if you use Gnome or KDE, but I don't by preference. As I've already chosen to use autofs, it's an easy matter to add extra uses for it.

USB stick setup

When I started using a USB stick for my private key, I just had a 32MB stick (the cheapest I could find). At that point, there was not much space free on the stick. As USB sticks get larger and larger in capacity, that's no longer the case. It's a shame to waste the space on one by just using it for private keys, so now I partition my current 1GB stick and split up the space. I have one partition simply formatted with FAT32, and a (much smaller) second encrypted partition where I store my private key data. I've found this quite handy - I have space that I can use for carrying around data I need to share, without having to expose my key data.

# fdisk -l /dev/sdb

Disk /dev/sdb: 1048 MB, 1048576000 bytes
1 heads, 63 sectors/track, 32507 cylinders
Units = cylinders of 63 * 512 = 32256 bytes

   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1               2       27904      878944+   c  W95 FAT32 (LBA)
/dev/sdb2           27905       32507      144994+  83  Linux

There are a couple of non-obvious things here. By setting up a Windows-friendly partition ID on the first partition, this stops Windows from helpfully suggesting "this USB stick doesn't appear to contain anything. Should I format it?" Then using a non-Windows partition ID on the other partition stops Windows from messing with it. Linux won't care what the partition ID is later.

The first partition on the USB stick is uninteresting at this point; it's just a bog-standard, boring FAT32 for data sharing with anything that can read/write FAT32.

Setting up an encrypted filesystem

The next step is to create an encrypted filesystem within that second partition. I'm quite specific about how I do this; maybe I'm being overly (or even underly- !) paranoid, but my goal (as stated above) is to make the private data on the USB stick useless without the laptop. To do that, I create an AES-encrypted filesystem in the second partition on the stick. There are (again!) some non-obvious steps to this:

After you install them, loop-aes comes with a modified loopback block device driver and loop-aes-utils also adds more featureful versions of the losetup, mount and umount programs that interact with it. These are all you need to make an encrypted loopback device and then use it. For example:

# losetup -e aes256 -o 65326520 -p 0 /dev/loop/0 /dev/sdb2 < /etc/loop.key
# mkfs.vfat /dev/loop/0
# mount /dev/loop/0 /mnt/pen
# mkdir /mnt/pen/gpg
# cp $HOME/.gnupg/secring.gpg /mnt/pen/gpg
# umount /mnt/pen
# losetup -d /dev/loop/0

You now have an encrypted block device that contains a filesystem that holds your GPG private key. Without knowing both the randomly-chosen key data and the offset that we supplied above, that filesystem is useless to anybody. That's the point; storing those 2 secret bits of information on the laptop is the important next step.

At this point, you can try the following to verify that the filesystem mounts fine for you:

# cat /etc/loop.key | mount -t vfat -p0 -o loop,encryption=aes256,offset=65326520 /dev/sdb2 /mnt/pen

It's worth doing this by hand for a sanity check; you'll be tearing your hair out later otherwise when autofs doesn't work for you!

If you're wondering why I use vfat/FAT32 as the filesystem in the encrypted area, it's quite simple. I wanted a small, low-overhead filesystem that supports long file names. It's not such an issue now, but when I started using a small USB key ext2 and most of the other Linux filesystems had a lot of overhead on small media.

Autofs setup

Autofs is quite easy to configure, for at least simple setups. /etc/auto.master defines the list of mount points that it knows about; the autofs init script will start one copy of autofs for each of them. Mine looks like:

# $Id: auto.master,v 1.2 1997/10/06 21:52:03 hpa Exp $
# Sample auto.master file
# Format of this file:
# mountpoint map options
# For details of the format look at autofs(5).
/auto   /etc/auto.misc
/auto.removable   /etc/auto.removable
/auto.pen   /etc/auto.pen

The format here is simple: when a user asks for a file under /auto, autofs will look up the right thing to do in the "map" file /etc/auto.misc etc. If the map file is non-executable, then it should contain a simple list of mappings (lookup key, mount options and device). If the map file is executable, then the map file will be executed, with the lookup key passed in as an argument. The map script is then responsible for printing the options and device on stdout; the calling autofs process will then use that information and call mount appropriately. If that sounds complicated, it's not too bad. But it's very flexible. See the autofs documentation for much more detail if you're interested.

The map that matters here is /etc/auto.pen. I've written a simple shell script that will attempt to detect if the USB stick is connected, by looking for a /dev/sdX device with the right size. More intelligent heuristics are clearly possible, but this works well enough for me. If the USB stick is not detected, it returns a device of "missing". If the USB stick is found, my script will return a device of /dev/sdX1 or /dev/sdX2 depending on the key asked for. In each case, the script will also print a selection of options. Most of those will be obvious, but -fstype=pen is special.

#!/bin/sh
#
# Simple automounter script /etc/auto.pen
# 
# Find the USB key
#
# Remember: this script needs to be executable!
#
# GPL v2 (c) Steve McIntyre 2008
# 

OPTS="-fstype=pen,sync,nodev,nosuid,ro"

DEVBASE=`fdisk -l /dev/sd? 2>/dev/null | grep ^Disk.*1048576000\ bytes | sed 's?^.*/d?/d?g;s?:.*$??g'`

if [ "$DEVBASE"x = ""x ] ; then
    DEV=missing
else
    case $1 in
        pen)
            DEV=${DEVBASE}2;;
        *)
            DEV=${DEVBASE}1;;
    esac
fi

echo "$OPTS :$DEV"

Normally when working with autofs, you'd specify a normal filesystem type using -fstype (such as ext3, vfat). It passes that option on to the mount program underneath using -t fstype. However, there is a clever feature here. Mount is designed to be extensible - if you tell it to work with a filesystem type it does not recognise, it will look for a helper program to run instead, called /sbin/mount.fstype. This is the final step in the autofs configuration - I have a special mount script (in /etc/mount.pen for sanity, but sym-linked into /sbin/mount.pen) that knows just how to call mount to make the encrypted filesystem work.

#!/bin/sh
#
# /etc/mount.pen
#
# GPL v2 (c) Steve McIntyre 2008
#
# Simple mount script, ready for calling via the automounter
#
# Find the USB key and attempt to mount it
#
# To make this work, use -fstype=pen in the autofs map, and sym-link
# this file into /sbin/mount.pen

DEV=$1
shift
MOUNTPOINT=$1
shift

case "$DEV" in
    *2)
        logger -t mount.pen -p kern.error "Auto-mounting $DEV secure storage"
        OPTS="$@,loop,encryption=aes256,offset=65326520"
        cat /etc/loop.key | mount -tvfat -p0 $OPTS $DEV $MOUNTPOINT
        ;;
    *)
        logger -t mount.pen -p kern.error "Auto-mounting $DEV normal storage"
        mount -tauto $DEV $MOUNTPOINT
        ;;
esac

GnuPG setup

GnuPG does not know anything about the details of the USB stick at all. I have simply moved my private keyring ($HOME/.gnupg/secring.pgp) to the filesystem on the USB stick and replaced it with a symbolic link:

tack:~/.gnupg$ ls -al
total 13516
drwx--S---  2 steve users     4096 2008-02-03 02:21 ./
drwxr-xr-x 36 steve users     4096 2008-02-03 02:06 ../
-rw-rw-r--  1 steve users     4397 2007-06-26 22:21 options
-rw-------  1 steve users 13631090 2008-02-03 02:07 pubring.gpg
-rw-------  1 steve users      600 2008-01-27 23:27 random_seed
-rw-r--r--  1 steve users      263 2003-08-22 08:33 revoke
lrwxrwxrwx  1 steve users       20 2008-01-06 22:12 secring.gpg -> /auto.pen/pen/gpg/secring.gpg
-rw-rw-r--  1 steve users   163280 2008-02-03 02:07 trustdb.gpg

The sym-link will work only when the USB stick is mounted - easy. That's all the configuration that I need for GPG.

And here's one I prepared earlier!

After doing all the above steps, I have my working encrypted filesystem on my USB stick. Voila!

# stick unplugged
tack:~$ ls -al /auto.pen/pen/gpg/
ls: /auto.pen/pen/gpg/: No such file or directory
tack:~$ ls -al .gnupg/secring.gpg 
lrwxrwxrwx 1 steve users 20 2008-01-06 22:12 .gnupg/secring.gpg -> /auto.pen/pen/gpg/secring.gpg
tack:~$ ls -alL .gnupg/secring.gpg 
ls: .gnupg/secring.gpg: No such file or directory

# stick plugged in, wait a few seconds for the USB storage detection
# to work
tack:~$ ls -al /auto.pen/pen/gpg/
total 24
drwx------ 2 steve root  2048 2008-01-08 14:36 ./
drwx------ 4 steve root 16384 1970-01-01 01:00 ../
-rwx------ 1 steve root  2696 2008-01-08 14:36 secring.gpg
tack:~$ ls -al .gnupg/secring.gpg 
lrwxrwxrwx 1 steve users 20 2008-01-06 22:12 .gnupg/secring.gpg -> /auto.pen/pen/gpg/secring.gpg
tack:~$ ls -alL .gnupg/secring.gpg 
-rwx------ 1 steve root 2696 2008-01-08 14:36 .gnupg/secring.gpg

Once I've finished with the GPG key, autofs will time out and unmount the USB stick. I explicitly mount that filesystem read-only, so even if I unplug the USB stick early it can't cause any problems on the filesystem. I also tweak the timeout in the autofs init.d script to make things work slightly faster - the default is 30 seconds, but I prefer 5.

Other options

Other people have come up with similar ideas to make their private data secure on their laptop/USB stick combination. As I have more time I'll link to more of them here.

My good friend Daniel Silverstone has written libgfshare, a secret-sharing library, which is how he solves at least some of this problem.

Matthew Johnson has another way of working: encrypting the entire hard disk and then using a USB stick to hold the needed passphrase.

Revision history

v1 (2008-02-05) Initial release
v2 (2008-02-05) Added mention of libgfshare and cryptkey, clarified why I use vfat in the encrypted filesystem
v3 (2008-02-28) Fixed typo, added mention of AES packages that need to be installed