System encryption on Debian Etch

Posted by goeb on Mon 14 Aug 2006 at 06:21

In this article I will describe how to setup a nearly complete encrypted system using Debian Etch and cryptsetup with LUKS. The goal is: encrypt all partitions except /boot. The user should enter a password at boot time or provide a keyfile on an USB device to decrypt the root partition. Keyfiles for additional partitions are located on the root, so the user does not need to enter a password for every partition.

Before we start...

Note that there is already some support for encrypted partitions in Etch, and if you choose to do a new installation you may download a daily build of the Etch CD image and encrypt your partitions during the installation process. Look at PartmanCrypto for some more details. However, partman-crypto is still pre-beta and I had some problems with it, so I decided to do it manually.

Update: On August 11th the Etch beta 3 installer was announced, it contains partman-crypto, so you do not need to use a daily built image. I did not test the beta 3 installer, maybe it works a bit better than some weeks ago.

I will describe the necessary steps of the encryption based on a new minimal installation of Debian Etch, though you may also encrypt a working system.

Important: Before you begin, do yourself a favour and create a backup of your data. Though I tested this guide several times I can not guarantee that it works for you and that nothing can happen to your data or your system. Please read this guide to the end, and if you have any questions ask before you start.

Some more things before we start...

What you will need during the process:

During the installation I chose the following partitioning scheme:

/boot    /dev/hda1    (will not be encrypted)
/        /dev/hda5    (will be encrypted)
/home    /dev/hda6    (will be encrypted)
swap     /dev/hda7    (will be encrypted)
All partitions will get an ext3 file system. Of course, you may change this to whatever you want, just keep in mind that you need a separate boot partition and that you need to change some commands during this guide according to your settings. (Note: There is no extra partition for the backups available in my setup. Instead, I let the installer create an ext3 filesystem on /dev/hda7 and use my future swap partition for this task.)

Note: Since I have no experience using LVM or RAID stuff, this guide will only explain how to achive our goal with a normal partition setup. Also, suspend to disk will not work with this guide (though it should not be to difficult for you to implement if you want to).

Just one more note: I installed a 2.6.16-2-686 kernel using a daily built installation image from this page, it may work with some older 2.6 kernels, too. But it will definitely not work with a 2.4 kernel!

Now let's begin with the real work!

First, you need to install cryptsetup:

root@test:~# apt-get install cryptsetup

Make sure you can load the modules that are required for the encryption:

root@test:~# modprobe dm_crypt
root@test:~# modprobe sha256
root@test:~# modprobe aes_i586

You should check if everything works as expected. First, there should be a /dev/mapper/control device:

root@test:~# ls -L /dev/mapper/control
/dev/mapper/control
Now check if aes and sha256 are available:
root@test:~# cat /proc/crypto
[...]
name          : sha256
driver        : sha256-generic
module        : sha256
[...]
name          : aes
driver        : aes-i586
module        : aes_i586
[...]
If everything is ok, you are ready to go on.

Preparing the initramfs...

In order to boot your system from an encrypted root partition, you need to create an initial ram disk (initramfs) that contains all information and all necessary programs and modules to decrypt your root at boot time. There are two tools to create the initramfs: initramfs-tools (which is the default tool in Debian) and yaird. I prefer to use yaird, so you need to install it for this guide:

root@test:~# apt-get install yaird

Now you need to configure yaird, you will find the configuration files in /etc/yaird/. These files are well documented, take some time to read the comments, maybe you will need some more configuration as explained here. First, edit /etc/yaird/Default.cfg. The default settings should be fine, but you need to include the modules mentioned above in your initramfs, so add the following lines (there are already two MODULE lines, put yours below them):

MODULE    dm_crypt
MODULE    sha256
MODULE    aes-i586
Additionally, if you want to be able to decrypt your system with a keyfile on an USB stick, you need to include the modules that are required to access it. Plug in your USB stick and watch the messages you get (in my case there is a line saying "new full speed USB device using uhci_hcd") and check what additional modules are loaded using lsmod, you may, for example, need to include the following:
MODULE    uhci_hcd
MODULE    sd_mod
MODULE    usb_storage
Don't forget to add the vfat module if your stick's file system is FAT, and you will also need the correct codepages to mount it (mount your stick and use lsmod to find out what you need):
MODULE    vfat
MODULE    nls_cp437
MODULE    nls_iso8859_1

Important: These are only examples that work for me, make sure you include the modules you need on your system if you want to use the key file on USB option!

Now /etc/yaird/Templates.cfg, there's a bit more to do. Let's start with the prologue template. In the first part of this template several files that should be included in the image and directories that should be created are listed. Add the following lines to that part of the configuration:

FILE "/sbin/cryptsetup"
FILE "/sbin/halt"
DIRECTORY "/tmp"
If you need to load a keymap for your keyboard to work properly, add:
FILE "/bin/gunzip"
FILE "/bin/loadkeys"
Now find out what keymap is required on your system. Look in the directory /usr/share/keymaps/[arch]/[layout] and select the keymap you need, include it in the configuration with
FILE "/usr/share/keymaps/[arch]/[layout]/[file]
Now you need to open the keymap to find out which other files are required by it (in Midnight Commander you can simply press F3 to view it). There should be one or more include "[another_keymap]" lines. The files specified can be found in the /usr/share/keymaps/[arch]/include directory with the suffix .inc.gz. You need to include these in your initramfs, too. And there may be even more include directives in the included files, so you need to get a bit recursive and include all these files. I'm using a german keyboard layout, here's what I need to include:
FILE "/usr/share/keymaps/i386/qwertz/de-latin1.kmap.gz"
FILE "/usr/share/keymaps/i386/include/qwertz-layout.inc.gz"
FILE "/usr/share/keymaps/i386/include/linux-with-alt-and-altgr.inc.gz"
FILE "/usr/share/keymaps/i386/include/linux-keys-bare.inc.gz"

After that part of the template the first part of the init script is defined. This script is responsible for decrypting and mounting our root. First, look for the part that parses the kernel options. With our modifications it should look like this (the lines you need to insert are marked with "**", do not add that "**" to the configuration file!):

	!ro=-r
	!ip=
	!nfsroot=
	!noresume=
	!resume=
	!resume2=
	!init=/sbin/init
    **	!keyfile=root.key
    **	!waitusb=0
	!for i in $(cat /proc/cmdline)
	!do
	!	case "$i" in
	!	init=*)
	!		init=${i#init=}
	!		;;
	!	ro)
	!		ro=-r
	!		;;
	!	rw)
	!		ro=
	!		;;
	!	ip=*|nfsaddrs=*)
	!		ip="$ip $i"
	!		;;
	!	nfsroot=*)
	!		nfsroot="$i"
	!		;;
	!	noresume)
	!		noresume=1
	!		;;
	!	resume=*)
	!		resume=${i#resume=}
	!		;;
	!	resume2=*)
	!		resume2=${i#resume2=}
	!		;;
	!	ydebug)
	!		INIT_DEBUG=yes
    **	!		;;
    **	!	key=*)
    **	!		keyfile=${i#key=}
    **	!		;;
    **	!	waitusb=*)
    **	!		waitusb=${i#waitusb=}
    **	!		;;
	!	esac
	!done
The few added lines are responsible for reading the parameters key and waitusb from the kernel command line, both will be explained later on.

Now scroll down. Notice that there are two cryptsetup templates. yaird will automatically detect if your root partition is encrypted and include the appropriate template in your init script. However, currently we do not have an encrypted root and yaird will only include the mount template. If you want to create a new initramfs and your root is encrypted, yaird will include the cryptsetup_luks template, too. Since we do not need this with the modifications below, you need to change cryptsetup_luks template to:

TEMPLATE cryptsetup_luks
BEGIN
END TEMPLATE

Now scroll further down and replace the mount template with the following to decrypt and mount your root:

TEMPLATE mount
BEGIN
  SCRIPT "/init"
  BEGIN
    !ROOTDEV=/dev/hda5
    !/bin/loadkeys de-latin1.kmap.gz
    !if ( /sbin/cryptsetup isLuks $ROOTDEV 2>/dev/null ); then
    !  NOTOPEN=1
    !  echo "waiting $waitusb seconds for usb..."
    !  /bin/sleep $waitusb
    !  for DEV in a b c d; do
    !    if [ "$NOTOPEN" != "0" ]; then
    !      if [ -f /sys/block/sd${DEV}/sd${DEV}1/dev ]; then
    !        mkbdev /dev/sd${DEV} sd${DEV}
    !        mkbdev /dev/sd${DEV}1 sd${DEV}/sd${DEV}1
    !        mount -n /dev/sd${DEV}1 /tmp
    !        if [ -f /tmp/$keyfile ]; then
    !          /sbin/cryptsetup --key-file /tmp/$keyfile luksOpen $ROOTDEV root
    !          NOTOPEN=$?
    !        fi
    !        umount /tmp
    !      fi
    !    fi
    !  done
    !  if [ "$NOTOPEN" != "0" ]; then
    !    /sbin/cryptsetup luksOpen $ROOTDEV root
    !    NOTOPEN=$?
    !  fi
    !  if [ "$NOTOPEN" != "0" ]; then
    !    echo "FATAL: could not open root, shutting down..."
    !    /bin/sleep 5
    !    /sbin/halt -n -d -f -p
    !  else
    !    /bin/mount -n $ro -t ext3 -o 'errors=remount-ro' /dev/mapper/root /mnt
    !  fi
    !else
    !  /bin/mount -n $ro -t ext3 -o 'errors=remount-ro' $ROOTDEV /mnt
    !fi
  END SCRIPT
END TEMPLATE
Note: leave out the loadkeys line if you do not need to load another keymap, if you need to, insert your keymap's name. Also, replace /dev/hda5 with your root partition.

The code we just inserted does the following: check if root is encrypted, if not, just mount it and go on to the next part. If it is encrypted, wait as many seconds as given by the waitusb kernel parameter and then check if an USB device is present as sda, sdb, sdc or sdd (we do this since there may be more than one USB device attached and the correct device may not be known at boot time).

Important: This assumes that your USB devices can be accessed as /dev/sda, /dev/sdb etc. If this is not the case, you need to change the code.

If an USB device is available, mount the first partition to /tmp and check if the keyfile (root.key or the name given by the key kernel parameter) exists on that drive. If yes, try to decrypt root with that key. If not or if it was not the correct keyfile go on with the next device until the last (sdd) was checked.

If the partition was successfully decrypted, there is nothing more to do than mounting it, if not, give the user the chance to provide a password. cryptsetup will ask up to three times for the password, if the user does not enter a valid one, shut down.

Note: If you want to test your script you may add a /bin/dash line wherever you want, you will then get a shell at boot time, though some common commands like ls are not available (if you need some more commands you can include them with the FILE option, see above).

That's it, now create the initramfs in /boot:

root@test:~# yaird -o /boot/yaird.initramfs

Note: If you want to check the contents of the initramfs before you reboot, you can do so by executing
root@test:~# yaird -f directory -o ~/yaird
This will create a directory yaird in your home that contains all files that will be included in the initramfs. If you want to change something in this directory and want to build an initramfs with the modified contents of that folder, do the following:
root@test:~# cd ~/yaird
root@test:~# find . | cpio --quiet -o -H newc | gzip -9 > /boot/yaird.initramfs

Now you need to create a new entry in Grub's configuration to boot the system using your initramfs. Add an entry like this to your /boot/grub/menu.lst:

title        Debian GNU/Linux, kernel 2.6.16-2-686 ENCRYPTED
root         (hd0,0)
kernel       /vmlinuz-2.6.16-2-686 root=/dev/hda5 ro
initrd       /yaird.initramfs
savedefault
boot
Of course, change the settings according to your system.

Now it's time to do a little test, just reboot your system. As mentioned above, the init script will notice that your root is not encrypted and simply mount it. It's just a little test to see that there are no errors in the init script, though the important part will actually be skipped. (Change the test for the encryption to something like "if [ 1 ]; then" if you want to test it before you continue, this may be useful to see if your USB stick can be mounted.)

Now change the / entry in your /etc/fstab to:

/dev/mapper/root    /    ext3    defaults,errors=remount-ro    0    1
and create an entry in your /etc/crypttab:
root    /dev/hda5    none    luks

The encryption of /...

Now you are ready to encrypt your partition. Boot your Kubuntu Live CD and open a terminal. First, copy all data on your root partition (in my case hda5) to your backup partition (in my case hda7):

ubuntu@ubuntu:~# sudo mkdir /mnt/hda5 /mnt/hda7
ubuntu@ubuntu:~# sudo mount /dev/hda5 /mnt/hda5
ubuntu@ubuntu:~# sudo mount /dev/hda7 /mnt/hda7
ubuntu@ubuntu:~# cd /mnt/hda5
ubuntu@ubuntu:/mnt/hda5# sudo cp -ax * /mnt/hda7
ubuntu@ubuntu:/mnt/hda5# cd
ubuntu@ubuntu:~# sudo umount /dev/hda5

Since cryptsetup is not included, you need to install it (with the new Kubuntu Desktop CD you first need to enable the universe repository in /etc/apt/sources.list and update your package list):

ubuntu@ubuntu:~# sudo apt-get install cryptsetup

Load the modules:

ubuntu@ubuntu:~# sudo modprobe dm_crypt
ubuntu@ubuntu:~# sudo modprobe sha256
ubuntu@ubuntu:~# sudo modprobe aes_i586

If you want to be on the safe side, you can overwrite your root with random data before creating the encrypted partition. Note that this may take a long time, depending on the size of your partition:

ubuntu@ubuntu:~# sudo dd if=/dev/urandom of=/dev/hda5

Now all you need to do is format your partition using LUKS, create a new filesystem and copy back your data:

ubuntu@ubuntu:~# sudo cryptsetup -c aes-cbc-essiv:sha256 -y luksFormat /dev/hda5
ubuntu@ubuntu:~# sudo cryptsetup luksOpen /dev/hda5 root
ubuntu@ubuntu:~# sudo mke2fs -j /dev/mapper/root
ubuntu@ubuntu:~# sudo mount /dev/mapper/root /mnt/hda5
ubuntu@ubuntu:~# cd /mnt/hda7
ubuntu@ubuntu:/mnt/hda7# sudo cp -ax * /mnt/hda5

Now you should reboot and hope that everything works as expected. cryptsetup should ask you for the password you entered in the previous step to decrypt your root partition at boot time and then continue the boot process like before the encryption.

Get rid of that password query...

If you want to use the USB key file method, you may now create the key file and add it to the key store. I use the following commands to create a nice key file:

root@test:~# apt-get install sharutils
root@test:~# head -c 2880 /dev/urandom | uuencode -m - | head -n 65 | tail -n 64 > ~/root.key
root@test:~# cryptsetup luksAddKey /dev/hda5 ~/root.key
Now copy root.key to your USB stick. I do not need to say that you should never give away this key file, since any person with a copy of this file can decrypt your data. However, if you should loose it, make sure remove the key file from your key store, see man cryptsetup for this and some more options.

Now reboot with your USB stick plugged in. In Grub's boot menu press E and add the following to the kernel line:

waitusb=3
Press Enter and B. Your system should now boot and wait 3 seconds before the script checks for the presence of USB devices. If your system needs more time for the USB device to be available, use a higher value for waitusb. You may even test it with a value of 2 or even 1, maybe it will work for you, on my test systems 3 is the minimum. If waitusb is set to a high enough value your system should boot with the USB stick plugged in without asking you for the password. You should now add the waitusb parameter to your /boot/grub/menu.lst file. If you do not have the key file with you you will still be able to boot by providing a correct password.

Note: If you want to rename your key file you may provide the parameter key=[filename] at boot time or change the init script accordingly.

Some more partitions to encrypt...

Now all we need to do to finish the process is to encrypt the remaining partitions, in my case /home and swap. You may be able to do this from within your running system, but in general it is recommended that you boot your live disk again. Before you do this, do not forget to modify your /etc/fstab and /etc/crypttab files accordingly, e.g fstab:

/dev/mapper/home    /home    ext3    defaults    0    2
/dev/mapper/swap    none     swap    sw          0    0
and crypttab:
home    /dev/hda6    none    luks
swap    /dev/hda7    none    luks
I did not include the swap option (luks,swap) since I could not get it to work with that, and it is not required to use the swap partition.

The encryption process is the same as for your root partition, just repeat the steps you did before for all of your remaining partitions (of course, except /boot).

After all your partitions are encrypted boot your system. You will need to enter the password for each encrypted partition. To avoid this, create key files for every partition as described above, create a /etc/cryptkeys directory, put the files in there and chmod them to 600 so only root can read them. Add the key files to your partition's key slots and change the key file option in your /etc/crypttab:

home    /dev/hda6    /etc/cryptkeys/home.key    luks
swap    /dev/hda7    /etc/cryptkeys/swap.key    luks

That's it...

Now reboot again and enjoy your encrypted system.


This article can be found online at the Debian Administration website at the following bookmarkable URL (along with associated comments):

This article is copyright 2006 goeb - please ask for permission to republish or translate.