Search:  
Gentoo Wiki

SECURITY_Encrypting_Root_Filesystem_with_DM-Crypt_with_LUKS

Deprecated (EXCEPT the initramfs part which works pretty well!)

This seems to be deprecated ( http://forums.gentoo.org/viewtopic.php?p=3441439#3441439 ). Please look here: SECURITY System Encryption DM-Crypt with LUKS


Contents



This article is part of the HOWTO series.
Installation Kernel & Hardware Networks Portage Software System X Server Gaming Non-x86 Emulators Misc
This article is part of the Security series.

Introduction (Theoretical stuff... not that important)

There are a lot of threads out there on how to encrypt your root file system. This howto attempts to use a new concept implemented by Clemens Fruhwirth [1] named luks.

luks doesn't store information in configuration files that can be lost or compromised, but on the encrypted partition itself. It creates a header on the partition that contains all information required by the decryption program, such as the algorithm, mode, etc...

Another benefit of luks is that it doesn't directly use the password that you give it at the beginning to encrypt the partition. Instead, it encrypts the master key (the one used to encrypt the data) with your password. If you want to change your password ('oldpassword' for ex.), it decrypts the master key with 'oldpassword', and reencrypts it with 'newpassword'. The master key isn't changed in this procedure, it is merely reencrypted, saving you the trouble of reencrypting your entire harddrive. This is especially handy in the case of a password compromise.

It's also possible to make multiple copies of the master key, all encrypted with a different password, allowing a type of password management system. this allows you to revoke passwords if they get compromised, or give different users different passwords to access the encrypted data. luks reserves space at the beginning of the partition for 8 different password (called key slots). Be aware that anyone with ANY valid key to the drive can delete/add keys.

+--------+--------+-----+--------+----------------+
| Header | Slot 0 | ... | Slot 7 | Encrypted Data |
+--------+--------+-----+--------+----------------+

Anyways, to the practical stuff... (some of this shamelessly copied from here: SECURITY Encrypting Root Filesystem with DM-Crypt)

Assumptions

# Kernel 2.6.11: disk driver builtin, ext2/reiserfs filesystem drivers builtin,
# device mapper/encryption modules dm-crypt/dm-mod builtin, aes builtin,
# ramdisk and initial ramdisk (initrd) builtin.
File systems  --->
  <*> Second extended fs support
  <*> Reiserfs support
Device Drivers  --->
  Block devices  --->
    <*> Loopback device support
    < >   Cryptoloop Support
    (4096) Default RAM disk size (kbytes)
    [*]   Initial RAM disk (initrd) support
  Multi-device support (RAID and LVM)  --->
    <*>   Device mapper support
    <*>     Crypt target support
Cryptographic options  --->
    <*>   SHA256 digest algorithm
    <*>   AES cipher algorithms (i586)
  1. A empty partition for Boot on /dev/hda1 filesystem is ext2.
  2. A empty partition for Root on /dev/hda3 filesystem is reiserfs
  3. A linux system e.g on /dev/hdb or on a gentoo install cd 2006.0 ,so that you can follow all coming steps.
  4. You will be prompted for encryption passphrase at boot time.
  5. You are using udev.
  6. You are using grub boot loader.
  7. You're logged in as root.

Requirements

You'll need to emerge device-mapper:

emerge device-mapper

You'll need cryptsetup 1.0.5 or newer (which contains luks support):

echo "sys-fs/cryptsetup ~x86" >> /etc/portage/package.keywords
emerge '>=sys-fs/cryptsetup-1.0.5'

If you have udev you will also need the multipath-tools

echo "sys-fs/multipath-tools ~x86" >> /etc/portage/package.keywords
emerge multipath-tools

Note: Not all distribution (other than gentoo) have multipath-tools package, in such case check the section about modifying grub, fstab and local udev rules below before you decide to compile it on your own (or you have a good reason not to emerge multipath-tools package).

Preparing Initrd Image

Now we need to create our initrd, I'll call it myinitrd. It's a simple task once you played around a bit with it. I highly recommed playing with initrd's before you go actually and encrypt your root (last step in this mini-howto).
First create the image, I'm using a 4MB initrd but feel free to expand that if you need more, just remember to set the option in your kernel configuration for the maximum ramdisk size properly. (see 'Assumptions' section)

cd /mnt
touch myinitrd
dd if=/dev/zero of=myinitrd bs=1024k count=4
losetup /dev/loop0 myinitrd
mke2fs /dev/loop0
mkdir /mnt/initrd
mount /dev/loop0 /mnt/initrd

Now populate the image with required directories and files:

cd /mnt/initrd
mkdir etc dev lib bin sbin proc new
touch etc/fstab
#note: leave etc/fstab empty
touch linuxrc
chmod +x linuxrc
touch devmap_mknod.sh
chmod +x devmap_mknod.sh

Linuxrc is where the action will be. It's a script file to be loaded by linux on initial boot, more below. Now you need to copy necessary files into bin, sbin and lib. For bin, copy the following from your current system:

cd /bin
cp sh cat mount umount mkdir chroot cryptsetup sleep mknod sed rm /sbin/pivot_root /sbin/blockdev /mnt/initrd/bin
#if you use jfs:
#cp /sbin/fsck.jfs /mnt/initrd/bin

For lib, you'll need to find out which lib files are needed by each of the binaries above. The way to do it is to run 'ldd' for each file above and copy the required libs over. Example:

ldd /bin/mount
   linux-gate.so.1 =>  (0xffffe000)
   libc.so.6 => /lib/libc.so.6 (0x4002e000)
   /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
cp /lib/libc.so.6 /mnt/initrd/lib/
cp /lib/ld-linux.so.2 /mnt/initrd/lib/

And so on for the rest of the binaries. Or use this perl script to do it for you

#!/usr/bin/perl
chdir "/mnt/initrd/bin";
$lol = `ldd \`ls\``; #ldd of every bin
@lines = split /\n/, $lol; #put each line in array
foreach(@lines){
  if($_ =~ /\/lib\/(\S+)/){ #find "/lib/*"
    $hash{ $1 } = 0; #use hash to prevent duplicates
  }
}
@files = keys %hash; #pull the files out of the hash
foreach (@files){ #step through each
  `cp /lib/$_ /mnt/initrd/lib` #copy to initrd lib dir
}

Note: copy to /mnt/initrd/lib even if the library is on /usr/lib on your system
Now, we need to create necessary devices in the dev directory:

mknod /mnt/initrd/dev/console c 5 1
mknod /mnt/initrd/dev/null c 1 3
mknod /mnt/initrd/dev/hda3 b 3 3
mknod /mnt/initrd/dev/tty c 5 0

You can also try: (please delete if this isn't right)

cp -a /dev/{console,null,hda3,tty} /mnt/initrd/dev/

If you are using hda2 replace the numbers above with "b 3 2". If you are unsure of the major/minor numbers you should use mount /proc and cat /proc/devices. This should be done off the ram drive (more about that later).

Initrd Scripts

Now we need to create the linuxrc and devmap_mknod.sh scripts that will be executed in the initrd. The linuxrc script should setup dm-crypt and mount root on it, then start the real init of the system.
Here it is:

#!/bin/sh
export PATH=/bin:/sbin

# Get cmdline from proc
mount -t proc proc /proc
CMDLINE=`cat /proc/cmdline`
# Create /dev/mapper/control nod for udev systems
sh devmap_mknod.sh

# Mount real root and change to it
sleep 1
cryptsetup -y luksOpen /dev/hda3 root
while test $? -ne 0; do
  cryptsetup -y luksOpen /dev/hda3 root;
done
umount /proc
# If you use JFS, check the filesystem before mounting to make sure it's clean.
# If it's not clean, mounting will fail.
# fsck.jfs /dev/mapper/root
mount /dev/mapper/root /new
cd /new
mkdir initrd
pivot_root . initrd

# Start init and flush ram device
exec chroot . /bin/sh <<- EOF >/dev/console 2>&1
umount initrd
rm -rf initrd
blockdev --flushbufs /dev/ram0
exec /sbin/init ${CMDLINE}
EOF

Now look at the devmap_mknod script. It has to create the /dev/mapper/control node with good values provided by /proc/devices.

#!/bin/sh

# Startup script to create the device-mapper control device
# on non-devfs systems.
# Non-zero exit status indicates failure.
DM_DIR="mapper"
DM_NAME="device-mapper"

set -e

DIR="/dev/$DM_DIR"
CONTROL="$DIR/control"

# Check for devfs, procfs
if test -e /dev/.devfsd ; then
        echo "devfs detected: devmap_mknod.sh script not required."
        exit
fi
if test ! -e /proc/devices ; then
        echo "procfs not found; please create $CONTROL manually."
        exit 1
fi
# Get major, minor, and mknod
# sed does not work as expected!
# See the discussion page if you wonder why the following two lines are commented out.
#MAJOR=$(sed -n 's/^ *\([0-9]\+\) \+misc$/\1/p' /proc/devices)
#MINOR=$(sed -n "s/^ *\([0-9]\+\) \+$DM_NAME\$/\1/p" /proc/misc)
MAJOR=`awk '/misc$/ {print $1}' /proc/devices`
MINOR=`awk '/'"$DM_NAME"'$/ {print $1}' /proc/misc`
if test -z "$MAJOR" -o -z "$MINOR" ; then
        echo "$DM_NAME kernel module not loaded: can't create $CONTROL."
        exit 1
fi

mkdir -p --mode=755 $DIR
test -e $CONTROL && rm -f $CONTROL

echo "Creating $CONTROL character device with major:$MAJOR minor:$MINOR."
mknod --mode=600 $CONTROL c $MAJOR $MINOR

That's basically it for the initrd.
It's advisable to test all bin files in it by chrooting and running them one by one. You should get no error messages about missing libraries.

chroot /mnt/initrd /bin/sh
/bin/chroot --help
/bin/mkdir --help
....

Unmount the initrd and copy it over to /boot. If you use bootsplash you can append the bootsplash initrd to it. Note that you can still mount/unmount the image and play with it event after cat'ing the bootsplash image to it. mount knows its start and end.

With bootsplash:

umount /mnt/initrd
mount /boot
cat /boot/bootsplash-initrd >> myinitrd
cp myinitrd /boot/
umount /boot

Without bootsplash:

umount /mnt/initrd
mount /boot
cp myinitrd /boot/
umount /boot

Using initramfs and busybox

Initramfs is a newer replacement for initrd which will become the new standard for booting Linux systems. At a high level it's just a different way to populate a ram disk for booting. Unfortunately, pivot_root does not work from initramfs and it looks like it never will, see http://bugzilla.kernel.org/show_bug.cgi?id=4857 , http://www.ussg.iu.edu/hypermail/linux/kernel/0510.1/0014.html , and https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=137005 . Instead of pivot_root, we will use busybox's switch_root.

Busybox is a Swiss army knife shell that contains almost everything you will need to boot. It can be installed as a statically linked binary, so you won't have to deal with copying libraries for the binaries you use.

Busybox

Emerge a static busybox. If you don't want to overwrite the busybox that's already on your sytstem, use emerge's ROOT= option.

ROOT="/tmp/bb" USE="static -readline" emerge busybox

The busybox ebuild makes an inconvenient choice for the CONFIG_BUSYBOX_EXEC_PATH option. Busybox uses this setting to locate itself when it needs to execute commands. The default for this option is /proc/self/exe, which has the effect that /proc needs to be mounted for most scripts to work. When the kernel starts /linuxrc, /proc is not mounted, so the above scripts for initrd will fail. The fix is to explictly reference /bin/busybox when /proc is not mounted. If you would rather not prefix some commands with /bin/busybox, you can build your own busybox with CONFIG_BUSYBOX_EXEC_PATH="/bin/busybox". You can also save about a half meg of memory by disabling the two hundred or so unneeded commands that the ebuild includes.

Preparing the initramfs image

Initramfs uses a compressed cpio archive, rather than the raw filesystem image that initrd uses. First, create a root for your initramfs and make the top level directories:

mkdir /tmp/myroot
cd /tmp/myroot
mkdir bin dev etc proc new

Now copy busybox into your initramfs and hard link sh to it. Busybox doesn't include cryptsetup, so you also need to copy that. Fortunately, cryptsetup is statically linked.

cd /tmp/myroot/bin
cp /tmp/bb/bin/busybox .
for i in echo mkdir mknod mount sed sh switch_root test touch umount
do
    ln busybox $i
done
cp /sbin/cryptsetup .

Create the necessary devices in your dev directory. Replace hda3 with whatever device you are using for your encrypted parition. Use ls -l to get the right major and minor device numbers.

cd /tmp/myroot/dev
mknod -m600 console c 5 1
mknod -m660 hda3 b 3 3
mknod -m660 null c 1 3
mknod -m660 ram0 b 1 0
mknod -m666 tty c 5 0

Make the initramfs archive file and copy it to /boot (do this AFTER preparing linuxrc and devmap_mknod.sh):

cd /tmp/myroot
find . -print | cpio -o -H newc | gzip -9 > ../myiramfs
cp ../myiramfs /boot

linuxrc for initramfs

The linuxrc script opens and mounts the encrypted root partition and then calls /sbin/init to boot the OS. Change CRDEV to point to your encrypted partition. Create this file in the root of your initramfs. When you are done, make it executable with chmod +x linuxrc. If you use the kernel's rdinit= option, you do not need an /init. Switch_root checks for /init to make sure it is being run correctly, so the script has to make sure it's there. Instead of using touch, you could just put an empty /init in the root of your initramfs.

#!/bin/sh
export PATH=/bin:/sbin

CRDEV=/dev/hda3

/bin/busybox mount -t proc proc /proc
CMDLINE=`cat /proc/cmdline`

sh devmap_mknod.sh

until /bin/cryptsetup -y luksOpen ${CRDEV} root; do :; done

mount -r /dev/mapper/root /new

# busybox switch_root insists on finding a regular file called /init
touch /init
umount /proc
exec /bin/busybox switch_root /new /sbin/init ${CMDLINE}

devmap_mknod.sh for initramfs

devmap_mknod.sh is the same as for initrd, except that the busybox mkdir and mknod commands don't support long options. Create this file in the root of your initramfs.

#!/bin/sh

# Startup script to create the device-mapper control device
# on non-devfs systems.
# Non-zero exit status indicates failure.
DM_DIR="mapper"
DM_NAME="device-mapper"

set -e

DIR="/dev/$DM_DIR"
CONTROL="$DIR/control"

# Check for devfs, procfs
if test -e /dev/.devfsd ; then
        echo "devfs detected: devmap_mknod.sh script not required."
        exit
fi
if test ! -e /proc/devices ; then
        echo "procfs not found; please create $CONTROL manually."
        exit 1
fi
# Get major, minor, and mknod
# The lines with sed caused unexpected errors in busybox (1.8.2), see discussion! -Craig-
#MAJOR=$(sed -n 's/^ *\([0-9]\+\) \+misc$/\1/p' /proc/devices)
#MINOR=$(sed -n "s/^ *\([0-9]\+\) \+$DM_NAME\$/\1/p" /proc/misc)
MAJOR=`awk '/misc$/ {print $1}' /proc/devices`
MINOR=`awk '/'"$DM_NAME"'$/ {print $1}' /proc/misc`
if test -z "$MAJOR" -o -z "$MINOR" ; then
        echo "$DM_NAME kernel module not loaded: can't create $CONTROL."
        exit 1
fi

mkdir -p -m755 $DIR
test -e $CONTROL && rm -f $CONTROL

echo "Creating $CONTROL character device with major:$MAJOR minor:$MINOR."
mknod -m600 $CONTROL c $MAJOR $MINOR

NOTE: please check the "discussion and bugs" for this page regarding busybox and devmap_mknod.sh. The above script didn't work for me with the busybox tools as the output from sed was not assigned to the variables MAJOR and MINOR. - Luud
Fixed by -Craig- with awk-skills. It was a bug with sed in busybox 1.8.2 (and 1.9.0?!) which was reported to and fixed by upstream.

grub.conf entry for initramfs

Add this to your grub.conf. Replace /bzImage-2.6.16 with the name of your kernel image. Note that we use rdinit=, not init=.

default 0
timeout 30

title bzImage-2.6.16
    root (hd0,0)
    kernel /bzImage-2.6.16 root=/dev/ram0 rdinit=/linuxrc ramdisk=8192 real_root=/dev/mapper/root
    initrd /myiramfs

Encrypting the Filesystem

This is a bit tricky. In order to encrypt the filesystem using luks, you need to be able to use a cryptsetup-luks binary, not a normal cryptsetup binary. You can do this with a gentoo install cd 2006.0 or later. Earlier versions only contain the normal cryptsetup binary.
i'm going to do this using a second hard drive (/dev/hdb). i'm going to set up the encrypted partition and copy everything from the 'old' hard drive (/dev/hdb) to the 'new' one (/dev/hda).

you could do this with a livecd, by starting it with the docache option, then switching in a cd that has a cryptsetup-luks binary on it, but that's a bit more complicated than this method.
note: if the livecd supports usb sticks, you could put the luks cyptsetup onto one of those and use that... just another idea...


ok, so now i have two hard drive in my pc the old (/dev/hdb) and the new (/dev/hda) that i will be encrypting.

/dev/hdb1 is the boot partition
/dev/hdb2 is the swap partition
/dev/hdb3 is the root partition (the one we want to encrypt)

# first, make sure everything that was on the drive can never be read again
# (note: this is optional, and can take a LONG time)
shred -v -n 2 /dev/hda3

# now we format the partition with luks.
# this creates the header and master key
cryptsetup -y -s 256 luksFormat /dev/hda3
--> type YES
--> enter password twice when prompted

# now we need to let cryptsetup create the /dev/mapper entry
cryptsetup luksOpen /dev/hda3 root
--> enter password when prompted
# from now on, the partition can be accessed through /dev/mapper/root

# now format the partition. i'm going to use reiserfs.
mkreiserfs /dev/mapper/root

# now mount the partition
mount /dev/mapper/root /mnt/hda3

# i'm going to copy everything from my current root partition to this new encrypted partition
# first i'm going to mount the drive i'm going to copy from at another location (to make copying easier)
mkdir /mnt/hdb3
mount /dev/hdb3 /mnt/hdb3
# now copy everything
cp -r -p -v /mnt/hdb3/* /mnt/hda3

note: this howto is not quite finished, but is actively being worked on

Modifying fstab & grub.conf (& local udev rules if necessary)

We need to modify /etc/fstab to point to our new root. Here's my new fstab:

/dev/mapper/root  /         reiserfs  noatime   0 1
/dev/hda1         /boot     ext2      noauto    0 0
/dev/hda4         none      swap      sw        0 0
none              /proc     proc      defaults  0 0
none              /dev/shm  tmpfs     defaults  0 0

And here's my new grub.conf:

default 0
timeout 5
splashimage=(hd0,0)/grub/splash.xpm.gz

title=Gentoo Linux (2.6.11)
  root (hd0,0)
  kernel (hd0,0)/bzImage-2.6.11 root=/dev/ram0 rw init=/linuxrc
  initrd (hd0,0)/myinitrd

The following section is most probably needed only in case you didn't emerge multipath-tools (they are not included yet in many (other than gentoo) distributions). If this is your case, use this line in your custom/local udev rules (e.g. in /etc/udev/00-local.rules):

SUBSYSTEM=="block", KERNEL=="dm-0", NAME="mapper/root"

Administering LUKS

For this section, we will be using a loopback file to demonstrate basic luks administration.

first you will need to create the loopback file and set it up.

# create an approx. 50 MB file
dd if=/dev/zero of=secretfile.loop bs=52428800 count=1
# create the loopback device
losetup /dev/loop1 secretfile.loop

Formatting

Now that we have a device, we need to format it using luks
note: 'format' here means add the luks header and create a master key. this has nothing to do with the filesystem

cryptsetup -y -s 256 luksFormat /dev/loop1
--> enter password when prompted

Opening a device

Now that the device is formatted, we need to 'open' it. This means that it is set up to where we can use it in a meaningful way.

cryptsetup luksOpen /dev/loop1 encrypted
--> enter your password

this created the device /dev/mapper/encrypted. You can choose the name as you wish

Making a Filesystem and Mounting

Now we can 'format' it again. This time format means to create a filesystem on it. I'm going to use reiserfs.

mkreiserfs /dev/mapper/encrypted
mkdir /mnt/foo
mount /dev/mapper/encrypted /mnt/foo

Unmounting and Closing a device

To unmount and close a file, do this:

umount /mnt/foo
cryptsetup luksClose encrypted
# remove the loopback device:
losetup -d /dev/loop1

the device /dev/mapper/encrypted no longer exists. neither does /dev/loop1.

Password management

luks allows you to easily manage passwords to your encrypted partitions/files/etc...
this section explains how to add and delete passwords.

Adding a Password

if you want to add another password, setup the loopback device again and use this command:

losetup /dev/loop1 secretfile.loop
cryptsetup -y luksAddKey /dev/loop1
--> enter the first password
--> enter new password
--> reenter new password

what happened now was this:
you entered the first password to decrypt the master key. you then gave cryptsetup a new password to encrypt the master key with. it then stored this new encrypted version of the master key in slot1.
now you have two valid passwords for this file

to test this, you would use cryptsetup luksOpen /dev/loop1 foo and try both passwords (doing a cryptsetup luksClose foo in between of course)

note: the 'first password' can be any valid password. For example, if you have 5 valid passwords, and want to add a sixth one, you can enter any of those 5 to create the 6th.

Deleting a Password

This command deletes the password stored in slot1 (the second slot)

cryptsetup luksDelKey /dev/loop1 1
# the 1 at the very end specifies which slot to delete the password from.

Automatic Mounting of Encrypted User Directories

Till 14:36, 5 April 2006 (GMT): Im not using Gentoo, so I may miss some Gentoo specific details. pam_mount 0.13.0 (and maybe older releases, too) can handle luks encrypted partitions.

You can test manually whether this works with:

mount -t crypt /path/to/encrypted/device /path/to/mountpoint

This should ask you for the luks passphrase, decrypts /path/to/encrypted/device and mounts it to /path/to/mountpoint If mount tells you something about crypt beeing an unknown filesystem, make sure that mount.crypt is in /sbin, it may be in /usr/bin, with a symlink:

ln -s /usr/bin/mount.crypt /sbin/mount.crypt

In /etc/security/pam_mount.conf you need an entry like this:

volume USERNAME crypt - /path/to/enrypted/device /path/to/mountpoint - - -

Additional options about ciphers etc. are stored in the luks header of the partition after invoking the luksFormat command and they do not need to be specified here (they will even be ignored when specified).

WARNING Make sure that you luks encrypted partition ist closed after logout, umounting it is not enough. A encrypted partition /path/to/encrypted/device is normally decrypted and mapped to /dev/mapper/_path_to_encrypted_device by pam_mount. This file must not exist after the user logged out.

Credits

Thanks to everyone who helped create the article on how to encrypt the root filesystem with dm-crypt (found here: SECURITY Encrypting Root Filesystem with DM-Crypt)

Thanks also to Clemens Fruhwirth, who implemented luks and made this possible.


Last modified: Wed, 20 Aug 2008 20:22:00 +0000 Hits: 34,308