NSLU2-Linux
view · edit · print · history

Table of contents

  1. The Goal of this howto
  2. Requirements
  3. The plan
    1. The (initial) idea
    2. Problems
    3. The new strategy
  4. One way to success
    1. Partition layout
    2. The provisional installation
    3. Choose an appropriate key file
    4. Create the encrypted root partition
    5. Save the flash image
    6. Mount the encrypted partition
    7. Modify the initramfs
    8. Encrypt the remaining partitions
  5. Further enhancements
    1. Make the NSLU2 beep at boot time
  6. Todo
    1. Use UUIDs for the partitions

The Goal of this howto

The goal is to acquire a fully encrypted Debian system on the NSLU2 via LUKS. Usually we would have to type in a passphrase at boot time to decrypt the root partition, but because the NSLU2 doesn't seem to work with a USB keyboard out of the box, we'll have to search for another option, which might be

  • to bring sshd up early enough to type in the passphrase via ssh (if that's possible)
  • to use a key file as a passphrase and let LUKS read it from a USB stick at boot time

As I haven't been able to find any infos how to realize the first option, we go with the "key file on a stick" option.

Some additional notes on the project:

  • Although we want to have a fully encrypted system, we'll not be able to encrypt the kernel and initramfs because the system does have to boot into a minimal system to receive the passphrase/key file. Usually that means that the boot partition will be left unencrypted. But in case of Debian/NSLU2, the boot partition only stores a copy of the kernel and initramfs. The actual kernel and initramfs are in the internal flash of the NSLU2 and are updated automatically by Debian. So in the end, we'll be able to also encrypt the boot partition.
  • The internal flash of the NSLU2 will therefore be unencrypted.
  • The USB stick will only be needed at boot time to decrypt the root partition, so as soon as the system is running, you can remove the USB stick and hide it at a safe place.
  • Only the root partition will have to be decrypted via the key file on the USB stick. Any additional partition will have an additional key file on the root file system, which will be loaded after the root partition has been decrypted.

Notes about this howto:

  • Usage of colors.
    • The commands entered via ssh into the NSLU2 are marked blue.
    • The commands entered on your workstation are marked violet.
    • Content of (config) files are marked light green, and it's filenames above are marked dark green.

Requirements

  • an NSLU2 (of course)
  • a paperclip or something simillar to press the reset button on the NSLU2
  • an external USB harddrive on which to install the system
  • a USB Stick which will hold/provide the key file
  • a day of free time :)

The plan

The (initial) idea

  • Flash the internal flash with the Debian net-installer.
  • Use installer utils to create encrypted partitions, mount them and install the base system.
  • Modify the initramfs to look for the key file of your root partition.

Problems

  • The 32MB of RAM of the NSLU2 are not enough to use the encryption utils.

The new strategy

Usually (on a desktop machine), we would use a live CD to create an encrypted root partition, decrypt it, install a base system on it and configure our bootloader to be able to later open the encrypted root partition. Unfortunately, we cannot use the Debian net-installer that way, because the NSLU2 has not enough RAM and it also doesn't come with cryptsetup (for LUKS support) out of the box. And because there's no trivial way to get a live CD to boot from the NSLU2 with an external CD drive, we'll first install an unencrypted Debian system (the usual way) to let it act as a live CD replacement. So the plan now looks like this:

  • Flash the internal flash with the Debian net-installer.
  • Create a "false" unencrypted root partition and install Debian on it (the live system).
  • Attach the harddrive to your workstation/notebook (primarily to speed up things).
    • Create the "real" encrypted root partition and mount it.
    • Copy everything from the "false" to the "real" root partition.
  • Attach the harddrive to your NSLU2 again.
  • Boot into the unencrypted system.
    • Modify the initramfs to search for the key file on the USB stick and then boot into the "real" encrypted root.
  • If it's successfull, you can now delete the "false" root partition and format the rest of the drive.
  • Encrypt your boot and swap partition, too :)

One way to success

Partition layout

Here's the suggested/used partition layout for the first part of this howto. We'll make a separate boot partition, because then it can be left unmounted when the system is running, thus preventing an attacker from examining your kernel and initramfs.

sda6 will be used as the root partition for the life system, and will be deleted once the encrypted system is up and running. sda5 will later become the root partition of the encrypted system, so make a choice here how much space it might need. FYI, a base system (such as the one for sda6) will require 510-600MB of free space. Because sda6 will be deleted later, we'll leave the rest of our harddrive blank for the time being.

PartitionSizeMount pointFilesystemComments
sda150MB/bootext3unencrypted
sda21GBswapswapunencrypted
sda3<rest><extended>
sda55GB<free>
sda62GB/ext3unencrypted
<rest free>

Here's a suggestion for the resulting partition layout after the root switch:

PartitionSizeMount pointFilesystemComments
sda150MB/bootext3encrypted
sda21GBswapswapencrypted with random key
sda3<rest><extended>
sda55GB/ext3encrypted
<rest what you like :) >encrypted

The provisional installation

First of all we need a standard system. The best way is to just follow an existing howto, like this one:
http://www.cyrius.com/debian/nslu2/install.html
Remember to use the partition layout discussed above (or one that fits your preferences, of course :) ).

Choose an appropriate key file

We now have a working unencrypted base system on sda6. Before we proceed, it's time to think about whether we'll use only a key file for the to-be-encrypted root partition, or additionally a passphrase in case the key file is lost (or has to be deleted). In my case, I've chosen an additional strong passphase for the root partition.

As for the key file, many howtos do something like the following to generate a file of a few KB with random data:

# head -c 2880 /dev/urandom | uuencode -m - | head -n 65 | tail -n 64 > root.key

However, we can also be creative and use an existing file, like a small pictures or a document or whatever, so that even if the USB stick is compromised, the key file is hidden in maybe a bunch of other files and is not as suspicious as a file named root.key or a file filled with completely random data. Of course the security gain is not very high, because every file on the stick can be tried out as the key file by an attacker, and, last but not least, the file name of the key file will be written to the internal flash of the NSLU2 in clear text in the initramfs. But that's entirely your choice what to choose.

To simplify the howto, I'll call the key file root.key, but you can (and maybe should) rename it to something else. Copy it to your USB stick and make a backup of it, preferably on another encrypted machine or medium.

Create the encrypted root partition

Next, we shutdown the NSLU2 and attach the harddrive to our workstation.

There are many encryption algorithms out there, most of which work with different key sizes (currently most between 128 and 256 bytes), and they use a hash function to store the hash of the passphrase on the partition. Considering the low-end performance of the NSLU2, you might want to use a small key size of 128 bytes, which is by-the-way also the default value for cryptsetup. In this tutorial, we'll use the AES algorithm in cbc-essiv mode with a key size of 256 and the sha256 hash algorithm.

Note that the external harddrive device name might change on your system, because of the other attached disk(s). In my case, it's now located under /dev/sdb

# cryptsetup luksFormat /dev/sdb5 -y --cipher aes-cbc-essiv:sha256 --key-size 256

WARNING!
========
This will overwrite data on /dev/sdb5 irrevocably.

Are you sure? (Type uppercase yes): YES
Enter LUKS passphrase:
Verify passphrase:
Command successful.
# cryptsetup luksAddKey /dev/sdb5 root.key
Enter any LUKS passphrase:
key slot 0 unlocked.
Command successful.

We have now created our encrypted partition with a passphrase and a key file. BTW, you can look at the partition's info with this command:

# cryptsetup luksDump /dev/sdb5

Now we open it, create an ext3 filesystem on it, and mount it.

# cryptsetup luksOpen /dev/sdb5 root2 --key-file root.key
key slot 1 unlocked.
# mkfs.ext3 /dev/mapper/root2
# mount /dev/mapper/root2 /mnt/sdb5

Now we also mount the unencrypted base system, and copy the content to the other partition. But we take a detour and first copy everything to our workstation, and then on the encrypted partition. That way, the harddrive doesn't have to constantly switch between the two partitions, thus sparing the harddrive. Additionally we'll have a complete backup of the base system on our workstation. In my case, I prefer to use tar for this task:

# mount /dev/sdb6 /mnt/sdb6
# cd /mnt/sdb6
# tar -cvjf /path/to/slug-root-backup.tar.bz2 ./
# tar -xvjf /path/to/slug-root-backup.tar.bz2 -C /mnt/sdb5
# umount /mnt/sdb5
# umount /mnt/sdb6
# crpytsetup luksClose root2

If you've chosen to use an extra boot partition, back it up, too:

# mount /dev/sdb1 /mnt/sdb1
# cd /mnt/sdb1
# tar -cvjf /path/to/slug-boot-backup.tar.bz2 ./
# umount /mnt/sdb1

Now you can attach the external harddrive to your NSLU2 again.

Save the flash image

Take this step seriously!

If you forget it, you're almost guaranteed to spend another day of free time, because in case the switch to the encrypted root will fail, your NSLU2 won't give you a chance to reboot into the unencrypted system unless you reflash it with this flash image. Otherwise you'll have to install Debian via the net-installer again!

So now boot into your NSLU2 again, and make a copy of your current flash image:

# cat /dev/mtdblock* > image.bin

And then get it from your workstation:

# scp root@slug1:~/image.bin .
root@slug1's password: 
image.bin                                     100% 8192KB   1.0MB/s   00:08

I've needed to reflash the NSLU2 a couple of dozen times to complete this howto!

Mount the encrypted partition

Now we'll mount the encrypted partition and chroot into it. That means we change the root directory in which we (and every of our executed commands) operate. Therefore we need to mount a couple of directories, e.g. dev, proc and sys. We also mount the boot partition and, if you've already installed any new software (like cryptsetup), you can also mount the package cache:

# apt-get install cryptsetup
# modprobe sha256
# modprobe dm-crypt
# modprobe aes
# cryptsetup luksOpen /dev/sda5 root
# mkdir /mnt/misc
# mount /dev/mapper/root /mnt/misc/
# mount -o bind /dev/ /mnt/misc/dev/
# mount -t proc none /mnt/misc/proc/
# mount -t sysfs none /mnt/misc/sys/
# mount -o bind /var/cache/apt/archives/ /mnt/misc/var/cache/apt/archives/
# mount -o bind /boot/ /mnt/misc/boot/
# chroot /mnt/misc/ /bin/bash

Congratulations! You are now in your encrypted root, at least to some extend. :) But to be able to boot into this root partition, we'll now have to modify the initramfs.

Modify the initramfs

Finally, we've arrived at the central part of this howto! This section is heavily based on the following article:
http://www.debian-administration.org/articles/428

If you don't understand everything in this part, take a look at the original article above, because it's more verbose than this howto.

There are several tools to generate an initramfs.
More on that here:
http://kernel-handbook.alioth.debian.org/ch-initramfs.html

We'll use yaird to accomplish our goal, so we'll install it now together with cryptsetup. Yes, you've read right, we'll need to install cryptsetup again because we're now in the encrypted root:

# apt-get install cryptsetup yaird

Now we'll edit /etc/yaird/Default.cfg and add a couple of modules, which will be required at boot time. Just append them after the other modules:

File: /etc/yaird/Default.cfg (extract only)

# Required for decryption
MODULE    dm_crypt
MODULE    sha256
MODULE    aes
# Required for the USB harddrive
MODULE    ohci_hcd
MODULE    sd_mod
MODULE    usb_storage
# Required for your USB stick
MODULE    vfat
MODULE    nls_cp437
MODULE    nls_iso8859_1

Note that your modules might vary, especially for the USB stick. You can use lsmod to see what modules are currently loaded to use your hardware. You can also check what dmesg says after you plug in your USB stick.

First we'll add a few more files which will be included in the initramfs at the beginning of the file, in the prologue-section of /etc/yaird/Templates.cfg:

File: /etc/yaird/Templates.cfg (extract only)

FILE "/sbin/cryptsetup"
FILE "/sbin/halt"
DIRECTORY "/tmp"

Now we'll modify the init-script section. Here's a part of the section, along with a few added lines. The new lines are marked with a ** at the beginning, which is not part of that line!

keyfile is the filename of the key file, or in other words, the path to the key file on the USB stick. So keyfile=root.key means that the key file named root.key will be search for in the root folder of your USB stick.

The waitusb=10 option means that at boot time, cryptsetup will wait for 10 seconds before it tries to use the key file on the USB key. This might be necessary to give the system enough time to detect the USB stick and load the appropriate drivers.

File: /etc/yaird/Templates.cfg (extract only)

	!ro=-r
	!ip=
	!nfsroot=
	!noresume=
	!resume=
	!resume2=
	!init=/sbin/init
    **	!keyfile=root.key
    **	!waitusb=10
	!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

Because we'll use our own hack to do the decryption, we'll comment out the content of the cryptsetup_luks-section:

File: /etc/yaird/Templates.cfg (extract only)

	TEMPLATE cryptsetup_luks
	BEGIN
	#	FILE "/sbin/cryptsetup"
	#	SCRIPT "/init"
	#	BEGIN
	#		!DOCRYPT=1
	#		!while [ "$DOCRYPT" != "0" ]
	#		!do
	#		!	/sbin/cryptsetup \
	#		!		<TMPL_IF NAME=verify>-y </TMPL_IF> \
	#		!		luksOpen '<TMPL_VAR NAME=src>' \
	#		!		'<TMPL_VAR NAME=target>'
	#		!	DOCRYPT=$?
	#		!done
	#	END SCRIPT
	END TEMPLATE

Next, we'll replace the mount-section. The result looks like the following:

File: /etc/yaird/Templates.cfg (extract only)

	TEMPLATE mount
	BEGIN
	#	SCRIPT "/init"
	#	BEGIN
	#		!/bin/mount -n \
	#		!	<TMPL_IF NAME=isRoot>$ro</TMPL_IF> \
	#		!	-t <TMPL_VAR NAME=fsType> \
	#		!	<TMPL_VAR NAME=options> \
	#		!	'<TMPL_VAR NAME=device>' \
	#		!	'<TMPL_VAR NAME=target>'
	#	END SCRIPT
	SCRIPT "/init"
	BEGIN
		!ROOTDEV=/dev/sda5
		!if ( /sbin/cryptsetup isLuks $ROOTDEV 2>/dev/null ); then
		!  NOTOPEN=1
		!  /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/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

Now we can finally leave /etc/yaird/Templates.cfg. The next step is to tell the system that we actually want to use yaird, so add the following into /etc/kernel-img.conf:

File: /etc/kernel-img.conf (extract only)

ramdisk = /usr/sbin/mkinitrd.yaird

Now comment out any dublicated entries in /etc/fstab that will be mounted on /media/usb? and then replace sda6 with sda5 for the time being. The last one will ensure that the node /dev/sda5 will actually be created in the initramfs by yaird.

Finally the time has come to generate the new initramfs and flash it into your NSLU2. This can be done with one simple command. :)

This is the second most exciting part of the howto.

# dpkg-reconfigure linux-image-$(uname -r)

If you haven't made any mistakes so far, you should now have the right flash image installed, but don't just reboot yet! You have to make 2 last changes:

First, edit /etc/fstab again and replace /dev/sda6 with /dev/mapper/root.

Second, edit /etc/crypttab and add the following:

File: /etc/crypttab

root    /dev/sda5       none            luks

If you had done those two steps before-head, yaird might have complained upon creating the initramfs.

If you haven't done it yet, plug in your USB stick into the NSLU2. And now we've reached the most exciting part of the howto.

# reboot

Reboot and hope that the system will boot into your new and shiny root. The system should change it's LED-blinking mode after 2min 15sec, and should be accessible via ssh after 2min 24sec.

If nothing has happened after 3 minutes, it's most likely that something has failed, but there's also the possibility that the failure was just temporal, which sometimes happens. If nothing should happen for 3 minutes, the system has frozen, and you'll have to cut the power off and on again to restart it. If, after the 3. try it still freezes, the configuration and/or flash image might contain errors, and that is when your flash image, which you of course backed up before, will come in handy. Because then you'll have to reflash the NSLU2 with the upslug2 tool and that image to be able to access your unencrypted system, so you can make changes to the yaird configs and run dpkg-reconfigure again.

If you are confident that the flash image is OK, and that only the other configs like /etc/fstab and /etc/crypttab may contain the bug, you would be better of by just plugging in the external harddrive to your workstation and change the corresponding config files.

If the system has successfully booted into the encrypted root, don't forget to make a backup of your new flash image! You never know when you'll need it next!

Encrypt the remaining partitions

Now comes the relaxing part of the howto, because most of what comes now is already known to us. :)

What remains to us now are the boot and the swap partitions which are still unencrypted. As already stated above, we'll use a key file for the boot partition, which will be located on the root partition, and we'll use a randomly generated key for the swap partition for every new session.

First of all we create a directory to store the key(s) and generate one for the boot partition.

# install -d -m700 /etc/cryptkeys    # this already sets the permissions correctly
# cd /etc/cryptkeys
# head -c 2880 /dev/urandom | uuencode -m - | head -n 65 | tail -n 64 > ./boot
# chmod 600 boot.key

Now we have a new key file for the boot partition. What follows now is pretty similar to what we've done before before.

So backup the content of the boot partition, unmount it, create a new encrypted volume on it with boot.key as the key file, open it with cryptsetup with the tag boot (which will create the device /dev/mapper/boot), create a new filesystem on it, mount it and copy the files of the old boot partition to it.

Now we only need to make some changes in /etc/fstab and /etc/crypttab:

File: /etc/crypttab

root    /dev/sda5       none                     luks
boot    /dev/sda1       /etc/cryptkeys/boot.key  luks
swap    /dev/sda2       /dev/urandom             swap

File: /etc/fstab (extract only)

/dev/mapper/boot /boot          ext3    defaults,noauto 0       2
/dev/mapper/swap none           swap    sw              0       0

Note that the noauto option will result that every time you install or modify the kernel or initramfs, you'll have to mount it manually. Remember, that was part of the original strategy.

And thats it. After a reboot, you can check whether the changes are correct:

# swapon -s      # should point to /dev/mapper/swap now
# ls -l /boot    # should now be empty
# mount /boot
# ls -l /boot    # should now be filled

Thats it. Congratulations, you now have an NSLU2 with a fully encrypted root!

You can now safely delete /dev/sda6 and format the remaining space of your harddrive.


Further enhancements

Make the NSLU2 beep at boot time

If I haven't made any mistakes in this howto, and you haven't made any yourself, you'll just have a running encrypted slug (at least most of the time). But if there's occurred a mistake, this setup is really difficult to debug and find out what's gone wrong. Therefore, it would be cool it the slug could beep for between any commands of the init-script, so that we can at least find out when the failure occurred.

Note that if you have enabled the serial port and are therefore able to watch the boot process, you obviously don't need this feature. But if you do not want to modify your hardware, a beeping NSLU2 is at least one possible software solution to debug your boot process.

Here's one of my unsuccessful tries:

We can beep via a neat tool called beep, and even polyphonic:

# apt-get install beep
# beep -f 10
# beep -f 15
# beep -f 20

This program would also have to be included into /etc/yaird/Templates.cfg

File: /etc/yaird/Templates.cfg (extract only)

FILE "/dev/tty0"  # beep complains, if this isn't included...
FILE "/usr/bin/beep"

Now we can make a quick test:

# yaird -f directory -o ~/yaird
# chroot ~/yaird /bin/dash
# beep -f 10

Great, we can now hear a beep! But that doesn't seem to be enough, because if I add a few beeps into /etc/yaird/Templates.cfg and run dpkg-reconfigure again, I don't hear anything at boot time at all.

This problem is solved now. What was missing was loading the correct module:

File: /etc/yaird/Default.cfg (extract only)

MODULE          ixp4xx_beeper

After that, you can add a few beeps into /etc/yaird/Templates.cfg where you would expect possible problems in the process of unlocking your root partition. You can use different frequencies for each beep to distinguish them, or make use of /bin/sleep if it beeps to fast. That's all up to you. You can then conclude from the sequence of beeps which actions have taken place.


Todo

Or to be more precise, to find out how to do...

Use UUIDs for the partitions

I don't know why the slug sometimes fails to boot into the encrypted system, but perhaps because the harddrive (sda) and the USB stick(sdb) might switch sometimes, because the order should be random. So I've tried to use UUID entries in /etc/fstab and /etc/crypttab, but that doesn't solve the problem. I even have the impression that it makes it only worse, because then the boot process freezes more frequently.

So that project is frozen for the time being...

view · edit · print · history · Last edited by N36.
Originally by N36.
Page last modified on December 14, 2008, at 12:55 PM