Debian Lenny + LUKS encrypted root + hidden USB keyfile

Posted: May 22nd, 2010 | Author: | Filed under: computing | Tags: , , | 16 Comments »

The setup:

* Recently built up a new 1U Xeon quad core to take over home server duties.

* Installed a fresh copy of Debian Lenny using a USB stick install and the debian-504-amd64-netinst.iso image.

* Setup and partitioned disks manually with the Debian installer:

/dev/sda1 as /boot
/dev/sda2 as encrypted LUKS partition sda2_crypt
sda2_crypt as an LVM physical volume (technically /dev/dm-0)
/dev/mapper/vg0-swap as swap
/dev/mapper/vg0-root as root

* Finished standard install, configured system basics and tested working LUKS setup using normal console password entry during boot.

* Converted system to Proxmox VE running proxmox-ve-2.6.24 kernel. (Shouldn’t be relevant to this how-to).

The Challenge:

All is working well, but since this will be a headless server sitting down in the utility room it’s going to be a PITA to have to physically enter the LUKS password at each reboot. I could setup a remote LUKS passphrase over ssh, but really I want the machine to be able to survive a reboot without my intervention to get it running. So, for my needs a USB key sounds like the ticket.

I found a few nice how-to’s via the Google, but I wanted a few tweaks so I ended up using a blend of the following:
* The passwordless disk encryption in Debian Etch how-to is an excellent guide and provided much of what I needed.
* The Unlocking a luks volume with a USB key how-to doesn’t work for encrypting root, but I liked the idea of hiding the key between the MBR and first partition of the USB stick (yes, I know, security through obscurity is bad.. blah, blah).
* I found a nice udev config in section 2.1 of this post.
* Found the solution of adding kopts parameters to grub from somewhere else I can’t seem to find again.

Getting it done:

This assumes you already have a working LUKS setup using console password entry.

Creating the key

1) Insert your usb stick and use dmesg to identify the device file. We’ll assume /dev/sdx.

2) Fill the entire usb stick with random data (this will erase all data on the usb stick). If you’re extra paranoid and have lots of spare time use /dev/random instead.

# dd if=/dev/urandom of=/dev/sdx bs=1

3) If you still want the usb stick to be usable for storing data you’ll need to recreate the partition table and filesystem. Use fdisk to create a new single partition and mark the partition as type “W95 FAT32” then use mkfs.vfat to format the new partition.

4) Extract 4096 bits of random data off the usb stick that will become the new keyfile from in between the MBR and the first partition.

# dd if=/dev/sdx of=/root/luks-secret.key bs=512 skip=4 count=8

5) Add the key to your LUKS encrypted partition in key slot 1 (your current LUKS password should already be in slot 0). You’ll be prompted for you current password when running this.

# cryptsetup luksAddKey /dev/sda2 /root/luks-secret.key --key-slot 1

Your usb stick is now ready to go.

6) If you don’t want to keep a backup copy of this key on your filesystem then use shred.

# shred --remove --zero /root/luks-secret.key

Creating a udev rule

This will make your specific usb stick available at /dev/usbkey when inserted. Other usb sticks will be ignored even if they contained the same keyfile.

1) Run the following command to get the necessary information about the usb stick.

# udevadm info -a -p $(udevadm info -q path -n /dev/sdx)

2) In the output, look for the section that contains SUBSYSTEMS==”usb”, DRIVERS==”usb”, ATTR{manufacturer}, ATTR{product} and ATTR{serial}. Use this information for creating the rule in the next step.

3) Create a file in “/etc/udev/rules.d/99-unlock-luks.rules” that contains the following (all on one line):

SUBSYSTEMS=="usb", DRIVERS=="usb", ATTRS{manufacturer}=="XYZ Corporation", ATTRS{product}=="Flash Thingy", ATTRS{serial}=="0123456789abc", SYMLINK+="usbkey%n"

4) Reload udev rules with:

# udevadm control --reload-rules

5 ) Test that /dev/usbkey is created when the usb stick is inserted.

Create the keyscript

This will be the shell script responsible for reading the keyfile from the usb stick and passing it via cat to cryptsetup when called upon during boot. If the keyfile is not available or valid then it will revert to asking for your normal LUKS password on the console. The nice part about this keyscript is that by reading the keyfile directly using dd we get to skip worrying about drivers for mounting the usb stick’s filesystem.

1) Create a keyscript containing the following as “/usr/local/sbin/unlock-usb-key.sh”


#!/bin/sh
TRUE=0
FALSE=1

# flag tracking key-file availability
OPENED=$FALSE

# check and modprobe the USB driver if not already loaded
cat /proc/modules | busybox grep usb_storage >/dev/null 2>&1
USBLOAD=0$?
if [ $USBLOAD -gt 0 ]; then
modprobe usb_storage >/dev/null 2>&1
fi

# give the system time to settle and open the USB device
sleep 10

# check for the specifc /dev/usbkey device created by udev using /etc/udev/rules.d/99-unlock-luks.rules
if [ -b /dev/usbkey ]; then
# if device exists then output the keyfile from the usb key (hidden key is 4096 bytes long starting at 2048 bytes)
dd if=/dev/usbkey bs=512 skip=4 count=8 | cat
OPENED=$TRUE
fi

if [ $OPENED -ne $TRUE ]; then
echo "FAILED to get USB key file ..." >&2
/lib/cryptsetup/askpass "Try LUKS password: "
else
echo "Success loading key file. Moving on." >&2
fi

sleep 2

2) Make the script executable:

# chmod a+x /usr/local/sbin/unlock-usb-key.sh

Configure cryptsetup & initramfs-tools

1) Install initramfs-tools package.

2) Add the keyscript parameter to /etc/crypttab

sda2_crypt /dev/sda2 none luks,keyscript=/usr/local/sbin/unlock-usb-key.sh

3) Add the following modules to /etc/initramfs-tools/modules

sha256
aes-x86_64 (or aes-i586 if running 32-bit)
aes_generic
crypto_api
dm-crypt
dm-mod
scsi_dh
usbcore
usbhid
usb_storage

4) Add the following to /etc/initramfs-tools/conf.d/cryptroot

CRYPTROOT=target=sda2_crypt,source=/dev/sda2

5) Ensure “MODULES=most” and “BUSYBOX=y” are set in /etc/initramfs-tools/initramfs.conf

Update grub config & initrd images

Now we need to build a new boot initrd.img that contains the scripts and modules we configured above.
1) First, create a “safe” backup copy of your current initrd image.


# cd /boot

Copy your current initrd version.
# cp initrd.img-2.6.XX-X-amd64 initrd.img-2.6.XX-X-amd64-safe

2) Edit /boot/grub/menu.lst and create a duplicate of your current boot option that utilizes the “safe” initrd.img.


title Debian GNU/Linux, kernel 2.6.XX-X-amd64
root (hd0,0)
kernel /vmlinuz-2.6.XX-X-amd64 root=/dev/mapper/vg0-root ro
initrd /initrd.img-2.6.XX-X-amd64

title Debian GNU/Linux, kernel 2.6.XX-X-amd64-safe
root (hd0,0)
kernel /vmlinuz-2.6.XX-X-amd64 root=/dev/mapper/vg0-root ro
initrd /initrd.img-2.6.XX-X-amd64-safe

3) For some reason I still haven’t figured out (and haven’t spent much time further researching), I needed to add the “cryptopts” to my kernel boot options to make everything work. Otherwise, instead of the init scripts mounting root, I would get errors similar to “LVM driver is detected but LVM is not configured” during boot.
Again, edit /boot/grub/menu.lst and add the “cryptopts” parameters to your current kopts line.

# kopt=root=/dev/mapper/vg0-root ro cryptopts=target=sda2_crypt,source=/dev/sda2,lvm=vg0-root,keyscript=/keyscripts/unlock-usb-key.sh

4) Run “update-grub”. This will update all your boot kernels and the kopts line from the previous step will ensure it’s added to new kernels as well.


title Debian GNU/Linux, kernel 2.6.XX-X-amd64
root (hd0,0)
kernel /vmlinuz-2.6.XX-X-amd64 root=/dev/mapper/vg0-root ro cryptopts=target=sda2_crypt,source=/dev/sda2,lvm=vg0-root,keyscript=/keyscripts/unlock-usb-key.sh
initrd /initrd.img-2.6.XX-X-amd64

5) Now that you have a backup “safe” initrd.img, it’s time to update your current one to include the scripts and modules configured in the previous steps so that they are available at boot. Thanks to the initramfs-tools package, this is as simple as:

# update-initramfs -u -k 2.6.XX-X-amd64

6) If you want to verify that everything has copied correctly, you can unpack your current initrd.img to the tmp directory and look through the extracted files. The keyscript should have been copied into the “keyscripts” folder.

# cd /tmp
# zcat /boot/initrd.img-2.6.XX-X-amd64 | cpio -iv
# ls -al keyscripts/

Testing

1) Reboot with the usb stick installed and select the boot option that uses the new initrd.img you created. The system should boot all the way to the login prompt.
2) Reboot without the usb stick and it should stop at the prompt for your LUKS password.
3) If you run into issues you can reboot with your “safe” kernel.

Advantages

In my opinion there are a couple advantages to this setup:
1) The udev script approach makes it a tiny bit more difficult for someone to use an alternate usb stick even if they had the keyfile.
2) The dd method of “hiding” the keyfile in random deadspace means that even if someone got their hands on your usb stick they wouldn’t know they had a keyfile.

Why?

So.. what’s the point to encrypting if you’re just going to leave the key sitting in the machine? For me, the drive encryption isn’t about protecting against the NSA or uber-l33t hackers. I just want to ensure if the hardware gets ripped off during a break-in that the data is secure. My home setup will allow me to physically secure the usb key nearby and run a usb extension cable from the key to the server. I’ll take my chances that the average criminal would just unplug any cables when taking the machine and regardless, it would be extremely difficult to physically get to the secured usb stick even if they knew to look for it. Of course if you value security over the convenience of unattended reboots then just pull usb stick when not in use.

Enjoy.

Update 10/14/2010 -- corrected several typos.
Update 11/24/2010 -- added part 2: http://www.oxygenimpaired.com/debian-lenny-luks-encrypted-root-hidden-usb-keyfile-part-2
Update 01/02/2011 -- Updated version for Ubuntu/Grub2: http://www.oxygenimpaired.com/ubuntu-with-grub2-luks-encrypted-lvm-root-hidden-usb-keyfile


16 Comments on “Debian Lenny + LUKS encrypted root + hidden USB keyfile”

  1. 1 murmur said at 9:07 am on July 18th, 2010:

    great howto – really saved me hours. Thank you.
    2 details:

    script: dd if=/dev/usbkey bs=512 skip=4 count=8

    generating key: dd if=/dev/usbkey bs=512 seek=4 count=8

    will result in snafu

    and on debian it is /etc/udev/rules.d/ – could be a flavour-issue.

    again kudos
    P

  2. 2 admin said at 11:05 am on July 26th, 2010:

    @murmur – thanks for the feedback. glad it helped.

    To clarify, the “dd if=/dev/usbkey bs=512 skip=4 count=8” line within the script is not for creating a key. It’s for reading it and piping to “cat”. This would only work if you previously created the key and the /dev/usbkey device as described in the earlier steps.

    Thanks for pointing out the udev path. That was a typo on my part and has been corrected.

  3. 3 murmur said at 6:49 am on August 31st, 2010:

    sorry for being unprecise.
    you have to be consistent using skip OR seek
    or the keys will differ I think.

    dd if=/dev/usbkey bs=512 skip=4 count=8
    ne
    dd if=/dev/usbkey bs=512 seek=4 count=8

    I for my case was happy I made a backup of the image.

  4. 4 admin said at 4:04 pm on August 31st, 2010:

    @murmur – thanks for the clarification. You are correct, it was meant to be “skip” in both places. I’ve corrected the post and apologize if it caused you any issues.

  5. 5 murmur said at 7:14 am on May 16th, 2011:

    Hi,

    I tried to install this on squeeze and it seems as if the loading of the USB-driver in the boot sequence seems to be a bit more tricky than with lenny. Did you try to upgrade one of your boxes to the recent one? I will be posting here if I find something worth mentioning.

  6. 6 admin said at 11:55 am on May 16th, 2011:

    @murmur – Nope. The box running this setup is still on Lenny. Please keep me posted on what issues you run into. You may also want to switch over to UUIDs to ensure disk ordering isn’t the issue:
    http://www.oxygenimpaired.com/debian-lenny-luks-encrypted-root-hidden-usb-keyfile-part-2

  7. 7 salsadancer said at 12:02 pm on June 24th, 2011:

    Hi,

    thanks for the detailed description. I tried this on squeeze and of course it complained that it could not find the key. I was not really expecting it to, as the udev rule for the stick was nowhere to be found in initrd.
    There is probably some hook in initramfs-tools for that, which I could not find.
    So I went the easy way and unpacked the initrd, added the 99-unlock-luks.rules in lib/udev/rules.d.
    After packing the fileset and putting it back into /boot, everything works.

    One of the next upgrades will probably generate a new initrd and break this hack.

    Anybody has a more permanent solution?

  8. 8 admin said at 12:46 pm on June 24th, 2011:

    @salsadancer – that’s good to know and is probably the cause of the issue that murmur was having as well. I won’t have time to look at this for a while but I would guess the answer might be found in the changelogs for the initramfs-tools package. My Lenny box is running initramfs-tools 0.92o, so the behavior change should be documented somewhere between that and the current version included in Squeeze. I’ll be interested to get this figured so I can upgrade this box down the road.

  9. 9 salsadancer said at 3:14 am on June 29th, 2011:

    Hi,

    I did some more digging in the initramfs-tools documentation. The way to do it is to create a script in
    /etc/initramfs_tools/hooks.

    I called my script “udev_hook”.
    When update-initramfs is run, this script is executed.

    I took it from /usr/share/doc/initramfs-tools/examples/example_hook and added these lines:

    . /usr/share/initramfs-tools/hook-functions

    if [ -e /etc/udev/rules.d/99-unlock-luks.rules ]; then

    cp -p /etc/udev/rules.d/99-unlock-luks.rules $DESTDIR/lib/udev/rules.d/

    fi

    This will automatically copy the udev rule to the correct place, every time update-initramfs is run.

    Also, in my case the /etc/initramfs-tools/conf.d/cryptoroot just needs to exist (empty file). When update-initramfs is run these lines are automatically added there:

    target=sda2_crypt,source=UUID=8c77f24d-19c2-4e06-b18b-d5414e78b25a,key=none,rootdev,lvm=gate_vg-gate_root_lv,keyscript=/lib/cryptsetup/scripts/unlock-usb-key.sh

    target=sda2_crypt,source=UUID=8c77f24d-19c2-4e06-b18b-d5414e78b25a,key=none,lvm=gate_vg-gate_swap_lv,keyscript=/lib/cryptsetup/scripts/unlock-usb-key.sh

    The setup works now without problems.

  10. 10 admin said at 1:04 pm on June 29th, 2011:

    Nice work, salsadancer. Very glad to hear you got this working. I’ll try to get the how-to updated with your contribution soon. Thank you.

  11. 11 admin said at 7:58 pm on January 2nd, 2012:

    @murmur – fyi.. updated guide here: http://www.oxygenimpaired.com/ubuntu-with-grub2-luks-encrypted-lvm-root-hidden-usb-keyfile

    @salsadancer – I reeeeeeally wish I had reread your comment yesterday. I just finished working through the same thing and came out with almost exactly the same solution as you posted. Spent hours recreating the wheel evidentially.

  12. 12 Paul Tobias said at 9:56 pm on September 5th, 2013:

    You are right about the robbers. A few years ago somebody broke into my apartment and stole my desktop computer. They unplugged all the cables, and didn’t take anything else, just the case itself. (Although there was other expensive hardware lying around, but they seem to have been in a hurry.) I’m not worried about the contents as it was encrypted with a strong password, but a USB key approach like yours would have worked too.

  13. 13 salsadancer said at 10:14 am on January 28th, 2016:

    A few years later… the same solution flawlessly works for jessie.

    [WORDPRESS HASHCASH] The poster sent us ‘0 which is not a hashcash value.

  14. 14 Passwordless encryption of the Linux root partition on Debian 8 with an USB key | Arizanta Portal said at 7:28 am on March 31st, 2016:

    […]  The script is created as “/usr/local/sbin/openluksdevices.sh” and taken from the http://www.oxygenimpaired.com/ […]

  15. 15 Passwordless encryption of the Linux root partition on Debian 8 with an USB key - Share Coupon Domain, Hosting, VPS as coupon Godaddy, Domain.com, Namesilo, Namcheap, bigrock, said at 9:36 pm on March 31st, 2016:

    […]  The script is created as “/usr/local/sbin/openluksdevices.sh” and taken from the http://www.oxygenimpaired.com/ […]

  16. 16 Passwordless encryption of the Linux root partition on Debian 8 with an USB key - smarty wifi said at 8:52 am on April 6th, 2016:

    […] be created as “/usr/native/sbin/openluksdevices.sh” together with could be taken by http://www.oxygenimpaired.com/ […]


Leave a Reply