SECURITY Encrypting Root Filesystem with DM-Crypt with LUKS
From Gentoo Linux Wiki
[edit] 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
| Installation • Kernel & Hardware • Networks • Portage • Software • System • X Server • Gaming • Non-x86 • Emulators • Misc |
[edit] 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)
[edit] 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)
- A empty partition for Boot on /dev/hda1 filesystem is ext2.
- A empty partition for Root on /dev/hda3 filesystem is reiserfs
- A linux system e.g on /dev/hdb or on a gentoo install cd 2006.0 ,so that you can follow all coming steps.
- You will be prompted for encryption passphrase at boot time.
- You are using udev.
- You are using grub boot loader.
- You're logged in as root.
[edit] 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).
[edit] 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).
[edit] 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
[edit] 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.
[edit] 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.
[edit] 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 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
[edit] 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}
[edit] 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.
[edit] 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
[edit] 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
[edit] 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"
[edit] 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
[edit] 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
[edit] 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
[edit] 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
[edit] 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.
[edit] Password management
luks allows you to easily manage passwords to your encrypted partitions/files/etc...
this section explains how to add and delete passwords.
[edit] 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.
[edit] 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.
[edit] 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 code>
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 code>
In /etc/security/pam_mount.conf code> you need an entry like this:
volume USERNAME crypt - /path/to/enrypted/device /path/to/mountpoint - - - code>
- USERNAME - the volume will be mounted if user USERNAME logs in
- /path/to/encrypted/device - The path to the luks encrypted device, eg. /dev/sda1
- /path/to/mountpoint - The mountpoint where the decrypted volume should be mounted to
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 code> is normally decrypted and mapped to /dev/mapper/_path_to_encrypted_device code> by pam_mount. This file must not exist after the user logged out.
[edit] 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.
