Gentoo full disk encryption with dm-crypt/LUKS
This post covers the manual installation procedure of Gentoo Linux with encrypted root and swap partitions using LUKS and dm_crypt on a legacy BIOS system.
Initial setup
The procedure is more or less the same as the one outlined in the Gentoo Linux x86 Handbook. However, when it comes to paritioning the drive, compiling the Kernel and setting the initial ramdisk, several different steps must be carried out.
I went through the whole process inside a Virtual Machine, using VMWare Player
as hypervisor. The Gentoo live image I used is the weekly build
install-x86-minimal-20130820
(sha512: d3135b53). Working “remotely” through ssh is much
more convenient. RSA/DSA ssh keys must be generated with ssh-keygen, a root password
set and sshd daemon started.
Drives configuration
The Gentoo Linux x86 Handbook can be followed up to step 4, which covers hard disks configuration. I will be using /dev/sda both for boot and root partitions. The first step is to create a plain primary boot partition with fdisk and to format it with a Unix-like filesystem, ext4 in this case, with mkfs.ext4. As far as the size is concerned, 256M are enough. The second partition, which will be used as a LVM physical volume with on top two logical volumes for root and swap, can take up all the space left on the device. This second partition must be formatted as a LUKS partition.
livecd ~ # cryptsetup --verify-passphrase luksFormat /dev/sda2
[...]
WARNING!
========
This will overwrite data on /dev/sda2 irrevocably.
Are you sure? (Type uppercase yes): YES
Enter LUKS passphrase:
Verify passphrase:
By opening the LUKS volume, a mapping with a plaintext device via the device mapper layer is created. This can be done with the following command.
livecd ~ # cryptsetup luksOpen /dev/sda2 vault
Enter passphrase for /dev/sda2:
The device mapper creates a /dev/mapper/vault. This becomes the LVM physical volume, which is then added to the volume group.
livecd ~ # pvcreate /dev/mapper/vault
Physical volume "/dev/mapper/vault" successfully created
livecd ~ # vgcreate vg /dev/mapper/vault
Volume group "vg" successfully created
Now the logical volumes can be created. I used a 4GB LV for swap and a LV for root which takes take up the remaining capacity of the volume group.
livecd ~ # lvcreate --size 4G --name swap vg
Logical volume "swap" created
livecd ~ # lvcreate --extents 100%FREE --name root vg
Logical volume "root" created
The two LVs should appear under /dev/mapper: /dev/mapper/vg-root and /dev/mapper/vg-swap. A root and swap filesystems must be created on top of the LVs.
livecd ~ # mkswap /dev/mapper/vg-swap
Setting up swapspace version 1, size = 4194300 KiB
no label, UUID=8fd4d40a-617b-409d-a5e8-ec6bfe926cc5
livecd ~ # mkfs.ext4 /dev/mapper/vg-root
[...]
Now the Gentoo Handbook can be resumed from point 4.f
livecd ~ # mkdir /mnt/gentoo/
livecd ~ # mkdir /mnt/gentoo/boot
livecd ~ # mount /dev/sda1 /mnt/gentoo/boot
livecd ~ # swapon /dev/mapper/vg-swap
livecd ~ # mount /dev/mapper/vg-root /mnt/gentoo/
Kernel compilation
After the precompiled filesystem has been downloaded and the chrooted environment
has been set, the kernel must be compiled. The kernel source code can be
retrieved through Portage, Gentoo package manager, by “emerging” gentoo-sources
.
The version installed with this live image is linux-3.10.7-gentoo-r1
, but
the configuration procedure is highly hardware dependend. Make sure to activate
all the necessary modules to support the underlying hardware. For instance,
a while ago while I was working on a physical machine, I remember having problems
with the SATA controller which was supported by sata_nv module, compiled through
the CONFIG_SATA_NV
configuration option. Now, considering that I am working
on a virtual machine, the i386_defconfig
lacked these options:
-
CONFIG_FUSION_SPI
for LSI SCSI controller (which is the one emulated by VMPlayer) -
CONFIG_CRYPTO_SHA256
to support SHA256 algorithm in kernel space -
CONFIG_DM_CRYPT
to support dm_cyrpt framework -
CONFIG_PCNET32
for network support (this is not strictly necessary to set up the environment)
Once the kernel is properly configured, it can be compiled together with the modules.
make -j4 i386_defconfig
make modules
make modules_install
After having compiled the kernel and copied the bzImage into /mnt/gentoo/boot, Gentoo Handbook can be resumed from Chapter 8. In section 8.a, the fstab file is set up. Since I am using logical volumes, the procedure is slightly different from the one outlined in the guide. My fstab looks like the following:
/dev/sda1 /boot ext4 noauto,noatime 1 2
/dev/mapper/vg-root / ext4 noatime 0 1
/dev/mapper/vg-swap none swap sw 0 0
/dev/cdrom /mnt/cdrom auto noauto,ro 0 0
/dev/fd0 /mnt/floppy auto noauto 0 0
/proc /proc proc default
Bootloader installation
Chapter 10 of the Gentoo Handbook covers the installation of the bootloader. I will use grub legacy (i.e. v0.97), since I am quite familiar with it and it will help speed up the process.
export DONT_MOUNT_BOOT=1
emerge --config =grub-0.97-r12
* Enter the directory where you want to setup grub:
/boot
* Linking from new grub.conf name to menu.lst
* Copying files from /lib/grub and /usr/share/grub to /boot/grub
Probing devices to guess BIOS drives. This may take a long time.
* Grub has been installed to /boot successfully.
DONT_MOUNT_BOOT
variable prevents grub from trying to mount the boot partition,
already mounted, and consequently failing. When prompted for the
installation directory, just type /boot. grub stage1 and stage1.5 must then be installed
respectively on the MBR and in the DOS compatibility region of /dev/sda.
grub> root (hd0,0)
Filesystem type is ext2fs, partition type 0x83
grub> setup (hd0)
Checking if "/boot/grub/stage1" exists... yes
Checking if "/boot/grub/stage2" exists... yes
Checking if "/boot/grub/e2fs_stage1_5" exists... yes
Running "embed /boot/grub/e2fs_stage1_5 (hd0)"... 22 sectors are embedded.
succeeded
Running "install /boot/grub/stage1 (hd0) (hd0)1+22 p (hd0,0)/boot/grub/stage2
/boot/grub/menu.lst" succeeded
Done.
An alternative way to install grub is to simply use grub-install
on /dev/sda.
update-grub
can normally be used to update menu.lst (or grub.cfg) based on the kernels
available under /boot, but in this case the configuration file is so simple that
it can be populated manually. As a aside note, update-grub relies on the output
of df command, which must report correctly an entry for the boot partition. If /etc/mtab is empty,
an error is raised (df: cannot read table of mounted file systems). A quick workaround
is to manually add to /etc/mtab the following line
/dev/sda1 /boot ext4 rw,relatime,data=ordered 0 0"
Creation of the initrd
The initial ramdisk responsible for mounting the encrypted device must contain cryptsetup tools and all the dependencies listed by ldd. For example:
livecd boot # ldd /sbin/cryptsetup
linux-gate.so.1 (0xb7777000)
libcryptsetup.so.4 => /usr/lib/libcryptsetup.so.4 (0xb7750000)
libpopt.so.0 => /usr/lib/libpopt.so.0 (0xb7742000)
libc.so.6 => /lib/libc.so.6 (0xb75c2000)
libuuid.so.1 => /lib/libuuid.so.1 (0xb75bc000)
libdevmapper.so.1.02 => /lib/libdevmapper.so.1.02 (0xb7579000)
libgcrypt.so.11 => /usr/lib/libgcrypt.so.11 (0xb74e8000)
/lib/ld-linux.so.2 (0xb7778000)
libudev.so.1 => /lib/libudev.so.1 (0xb74d4000)
libgpg-error.so.0 => /usr/lib/libgpg-error.so.0 (0xb74cf000)
librt.so.1 => /lib/librt.so.1 (0xb74c5000)
libpthread.so.0 => /lib/libpthread.so.0 (0xb74a9000)
After leaving the chrooted environment, the following script can be used to
setup the initrd, which will be packaged under $(pwd)/initramfs
.
Code
#!/bin/bash
set -euo pipefail
INITRD_ROOT="$(mktemp -d)"
exit() {
echo "Cleaning up ${INITRD_ROOT}"
rm -rf "${INITRD_ROOT}"
}
trap exit EXIT
echo "Building INITRD in ${INITRD_ROOT}"
pushd "$(pwd)"
cd "${INITRD_ROOT}"
mkdir bin lib dev dev/mapper dev/vc etc newroot proc sys
cp "$(which busybox)" "$(which cryptsetup)" "$(which mdadm)" bin
tools=(
bin/cat
bin/mount
bin/sh
bin/switch_root
bin/umount
bin/sleep
)
for t in "${tools[@]}";
do
ln -s /bin/busybox "${t}"
done
cp -a /sbin/vgchange bin
cp -a /sbin/vgscan bin
cp -a /sbin/lvm bin
mknod -m 444 dev/random c 1 8
mknod -m 600 dev/console c 5 1
mknod -m 444 dev/urandom c 1 9
deps=(
vgscan
vgchange
cryptsetup
)
for d in "${deps[@]}";
do
echo "Copying deps for ${d}"
LIBS=$(ldd "$(which "${d}")" 2>&1 | awk -F"=>" '{print $2}' | grep -o "/[^ ]*")
for l in ${LIBS};
do
echo " Copying ${l}"
cp -a "${l}" lib
done
done
cat > init << EOF_init
#!/bin/sh
echo "Unlocking LUKS encrypted volume..."
mount -t proc proc /proc
mount -t sysfs sysfs /sys
/bin/cryptsetup luksOpen /dev/sda2 vault
/bin/vgchange -ay vg
mount -r /dev/mapper/vg-root /newroot
umount /sys && umount /proc
exec switch_root /newroot /sbin/init \$(cat /proc/cmdline)
EOF_init
chmod a+x init
popd
echo "Packing initrd in $(pwd)/initramfs"
find "${INITRD_ROOT}" | cpio --quiet -o -H newc | gzip -9 > initramfs
echo "Done"
Final steps
Once created the initrd, grub.conf should be configured to load the kernel image and the initrd.
title Gentoo
root (hd0,0)
kernel /bzImage vga=791
initrd /initramfs
After umounting /mnt/gentoo/boot, /mnt/gentoo/proc, /mnt/gentoo and rebooting the machine, the initrd should prompt for the password of the encrypted volume and then mount the root filesystem.