Fixing initrd luks LVM booting

From Phormix Wiki
Jump to: navigation, search

Preamble

This will show you how to include a LUKS encrypted LVM with the root filesystem as well as other mountpoints. Decryption can occur via password after GRUB boot. It will also detail how to auto-decrypt via a key-file in the initrd. This is not overly effective at securing your files, but does make it easier to remove/wipe a disk as you can distrust/remove a single key-file.

Currently these instructions are for Debian/Ubuntu based systems. Some items related to building the initrd may vary with RedHat-based systems

Layout

When creating your partitions, the LUKS partition should occupy the physical partition, and the LVM PV should sit within the decrypted LUKS partition, e.g.

  • sda
    • sda1 (/boot)
    • sda2 (LUKS volume)
      • LVM2 PV (on luks volume, cryptvol)
        • LVM2 VG cryptvg0
          • cryptvg0-rootfs (/root)
          • cryptvg0-usr (/usr)
          • cryptvg0-var (/var)
          • cryptvg0-swap (swap)
          • cryptvg0-tmp (/tmp)
          • etc etc

Note that all volumes except /boot are mounted via under the encrypted volume, including a swap volume. This allows for more easy resizing of individual partitions etc

 

Disk/volume setup

Setup and open the LUKS partition

If you are doing this as a fresh install, you will need to do this prior to running the actual installer, from a command prompt. This assumes that you've got a disk sda with two partitions, sda1 being used for a boot partition and sda2 being the location of the LUKS volume.

Create the encrypted LUKS partition (you will be prompted for a password to use)

cryptsetup luksFormat /dev/sda2

Open the encrypted partition as "cryptvol"

cryptsetup luksOpen /dev/sda2 cryptvol

Create LVM volumes

Create the LVM Physical Volume (PV) in the encrypted volume

pvcreate /dev/mapper/cryptvol

Create a volume group (VG) on the PV

vgcreate cryptvg0 /dev/mapper/cryptvol

Create your LV's (may vary by which LV's and space allocation as you'd like)

lvcreate -n rootfs -L 15G cryptvg0
lvcreate -n swap -L 16G cryptvg0
lvcreate -n usr -L 15G cryptvg0
lvcreate -n var -L 15G cryptvg0
lvcreate -n tmp -L 2G cryptvg0

Install your OS

Start the installer. Make sure you mount the LV's as appropriate and the /boot partition.

DO NOT reboot. You can do the following steps prior to rebooting

 

Mount the volumes/partition and devices in target (if necessary)

If the OS installation has already completed, it has likely unmounted your drives from /target.

If this has occured, you will need to remount (if not, you can skip this section)

First, you will need to remount the devices first

mount /dev/mapper/cryptvg0-rootfs /target
mount /dev/sda1 /target/boot
mount /dev/mapper/cryptvg0-tmp /target/tmp
mount /dev/mapper/cryptvg0-var /target/var
mount /dev/mapper/cryptvg0-usr /target/usr
etc

Also, you may need to bind-mount some devices from the installer's root filesystem into the chroot

mount --bind /dev /target/dev
mount --bind /dev/pts /target/dev/pts
mount --bind /proc /target/proc
mount --bind /sys /target/sys


Add the LUKS config file

Normally the disks are mounted under the mountpoint of /target during install. We'll assume you are either working with this while the OS is being installed or have remounted them after

Check the UUID of the disk containing the LUKS partition, e.g.

blkid /dev/sda2

This will return something like

/dev/sda2: UUID="1234567a-123-a3b2-23a3-30ccb02abda1" ...

Copy the UUID entry above (from your system)

Edit "/target/etc/crypttab". Add an entry for the disk in question

#target                 source          key             opts
cryptvol                UUID=1234567a-123-a3b2-23a3-30ccb02abda1       none            luks,initramfs,loud,noauto

Rebuild the ramdisk (initrd/initramfs)

At this point, you will likely need to rebuild the initramfs. This is because - by default - the LUKS modules are not included in GRUB if not /etc/crypttab is present, and thus booting will fail to mount your encrypted disk

Enter the mountpoint

chroot /target

Update your initramfs (Debian/Ubuntu)

update-initramfs -u

Update the initramfs (Redhat/CentOS)

dracut -f

 

Finish Install/Reboot

If your OS install is done, then feel free to reboot not. Otherwise, finish the OS installation, then reboot

Booting up into the installed OS

Your sytem should boot up and present you with GRUB loading screen. Select your kernel, and if all went well you will be prompted for a password to unlock your encrypted volume. Enter it now

If you're happy to enter a password to unlock the drive, you can stop here. If not, you may auto-decrypt the drive at the expense of some security.

 



Setting up auto-decryption (packaged scripts)

Don't feel like doing the work manually? Here are some packages for common Linux OS's that will handle the majority of the work for you.

The (public) GPG signing keys for my packages are also available are also available here

 

Debian/Ubuntu

For DEB/APT Use this DEB package to setup initramfs based decryption (tested on Debian and Ubuntu)

 


RedHat/CentOS

For RPM/YUM based systems, use this RPM package to setup initramfs based decryption (tested on CentOS 7)

crypt0 UUID=abc123-daf-1123-abae1223ffgh none        luks,noauto

 

Setting up auto-decryption (HOWTO)

As mentioned, doing this step reduces the security of your encrypted volume. The key which will be used to decrypt the disk will be inside your kernel, which is in an unencrypted boot partition. A malicious person cound mount this partition and extract the decryption key.

There is still some benefit to doing this though. If you choose to wipe the device, you can remove the encryption key from the volume, then rendering the encrypted data inaccessible when disposing of or repurposing the drive etc.

With the above in mind, here's how to auto-decrypt.

 

The steps and files vary depending on if you're using a Debian/Ubuntu (initramfs-tools,apt) based system or a RedHat/CentOS (Dracut/RPM) type system, so choose the appropriate section below.

 

 

HOWTO: Debian/Ubuntu Systems (initramfs-tools/apt)

Create a key file

You can use any file as a key. Alternately, you can generate one from random data, e.g. to generate a 2kb key file

dd if=/dev/random of=/crypt.dat bs=1k count=2
chmod 400 /crypt.dat

Add initramfs scripts

Once you have your key file, you need two scripts to work with the initramfs. The first will insert the file into the initramfs when it is built

Create a hook file under: /etc/initramfs-tools/hooks/01-copy-key.sh

#!/bin/bash
PREREQ=""
prereqs()
{
  echo "$PREREQ"
}

case $1 in
  prereqs)
    prereqs
    exit 0
    ;;
esac

. /usr/share/initramfs-tools/hook-functions
# Begin real processing below this line

echo "Copying encryption key"
cp /crypt.dat "${DESTDIR}/"

Next, create a script that will run to decrypt the drive on boot. Put the following in: /etc/initramfs-tools/scripts/init-premount/01-mount-cryptdisk.sh
NOTE: Be sure to put the UUID of your LUKS partition below

#!/bin/sh
PREREQ="lvm"  
prereqs()     
{             
  echo "$PREREQ"   
}             

case $1 in    
  prereqs)    
    prereqs        
    exit 0         
    ;;             
esac          

. /scripts/functions

echo "Decryption..."
#Your UUID goes below
cryptsetup --key-file=/crypt.dat luksOpen UUID=1234567a-123-a3b2-23a3-30ccb02abda1 cryptvol

 

Rebuild the initramfs

You will need to rebuild the active initramfs again to include the encryption key and the premount scrupt

 

 


HOWTO: RedHat/CentOS (dracut/yum) based systems

On RedHat or CentOS based systems, the ramdisk packages are built using the "dracut" tool rather than initramfs-tools. Dracut also using a different - systemd - based initialization system in modern versions

Update /etc/crypttab

Ensure that the entry in /etc/crypttab for your encrypted parition (the one you want to decrypt on boot) has a "noauto" argument in the fourth column. That will prevent the other systemd init scripts from asking for the password after the volume is already decrypted.

 

Create a key file

You can use any file as a key. Alternately, you can generate one from random data, e.g. to generate a 2kb key file

dd if=/dev/random of=/crypt.dat bs=1k count=2
chmod 400 /crypt.dat

 

Update your /etc/crypttab

In /etc/crypttab, prevent the encrypted volume containing your root filesystem from being auto-mounted by other systems

Add a "noauto" stanza to the fourth column, e..g.

# <target name> <source device>         <key file>      <options>
crypt0  UUID=90aaea4e-fb18-4ae4-b54c-59bb31e7d3ad   none    luks,noauto

Create systemd service/path files

/usr/lib/systemd/system/sysinit.target.wants/systemd-key-decrypt-rootfs.path

This file will trigger the related service file when the appropriate path (/dev/disk/by-uuid) exists and contains files

This fixes timing issues where the system would attempt to decrypt a device that handle yet settled or didn't yet exist. Please note that if you are not using "crypt0" as your device name, you'll probably want to modify the ConditionPathExists line below

[Unit]
Description=Attempt to decrypt root volume master Directory Watch
DefaultDependencies=no
Conflicts=shutdown.target
After=plymouth-start.service
Before=paths.target shutdown.target dracut-mount.service systemd-ask-password-console.service systemd-ask-password-wall.service systemd-ask-password-plymouth.service
ConditionPathExists=!/dev/mapper/crypt0

[Path]
DirectoryNotEmpty=/dev/disk/by-uuid

/usr/lib/systemd/system/sysinit.target.wants/systemd-key-decrypt-rootfs.service

This systemd service file will launch the decryption script to begin decryption

[Unit]
Description=Decrypt volume via key
DefaultDependencies=no
After=systemd-udev-settle.service
Requires=systemd-udevd
Before=dracut-mount.service systemd-ask-password-console.service systemd-ask-password-wall.service systemd-ask-password-plymouth.service
ConditionPathExists=/crypt.key

[Service]
Type=simple
ExecStart=/sbin/decrypt_root_bykey.sh

Create systemd symlink

Create a symlink to ensure that the .path file above launches on startup

ln -s ../systemd-key-decrypt-rootfs.path  /usr/lib/systemd/system/sysinit.target.wants/systemd-key-decrypt-rootfs.path 

 

 

Create dracut module directory and config files

Create a new dracut modules directory for our custom decryptor

mkdir /usr/lib/dracut/modules.d/89rootextract/

Create module-setup file

This file should be named: /usr/lib/dracut/modules.d/89rootextract/module-setup.sh
Per the below, it will put the appropriate files in the appropriate paths of the ramdisk

#!/bin/bash

check() {
    local _rootdev
    # if cryptsetup is not installed, then we cannot support encrypted devices.
    require_binaries cryptsetup || return 1

    [[ $hostonly ]] || [[ $mount_needs ]] && {
        for fs in "${host_fs_types[@]}"; do
            [[ $fs = "crypto_LUKS" ]] && return 0
        done
        return 255
    }

    return 0
}

depends() {
    echo dm rootfs-block
    return 0
}

installkernel() {
    instmods dm_crypt =crypto
    hostonly="" instmods drbg
}

cmdline() {
    local dev UUID
    #printf "Attempting to mount LUKS device\n"
}

install() {
    CRYPTKEY=/crypt.dat
    if [[ -f "$CRYPTKEY" ]]
    then
      printf "Found decryption key:&nbsp;%s.\nThis will NOT be added to the volume for trust. Please do this yourself!\n"  $CRYPTKEY
    else
      printf "Generating decryption key:&nbsp;%s\n"
    fi

    printf "SystemD Key-Based Boot Decryption...\n"
    printf "Install decrypt_root script%s\n" "/sbin/decrypt_root_bykey.sh"
    inst_script "$moddir/decrypt_root" /sbin/decrypt_root_bykey.sh

    printf "Install key\n"
    inst_script "$CRYPTKEY" /crypt.key

    printf "Install SystemD services\n"
    inst_multiple -o \
    $systemdsystemunitdir/systemd-key-decrypt-rootfs.service \
    $systemdsystemunitdir/systemd-key-decrypt-rootfs.path \
    $systemdsystemunitdir/sysinit.target.wants/systemd-key-decrypt-rootfs.path
}

Create the decryption script

This file should be named: /usr/lib/dracut/modules.d/89rootextract/decrypt_root

#!/bin/bash
. /etc/initrd-decryptor.conf
echo -n "Now decrypting filesystem '${CRYPTVOLUME}' by key..."
cryptsetup luksOpen --key-file=${CRYPTKEYFILE} ${CRYPTVOLUME} ${CRYPTVOLID} && echo "OK" || echo "FAILED"

Create the config file

This file should be saved under: /etc/initrd-decryptor.conf

Please be sure to substitute in the appropriate values for:

  • CRYPTVOLUME (should contain the UUID - or device - of your encrypted volume, matching the second column of your /etc/crypttab)
  • CRYPTVOLID (the name of the encrypted volume, matching the first column of /etc/crypttab or appropriate line in /etc/fstab)

 

CRYPTKEYFILE='/crypt.dat'
CRYPTVOLUME='UUID=YOUR_UUID_GOES_HERE'
CRYPTVOLID='CRYPT0'

Rebuild initramfs

Now rebuild your current ramdisk with dracut

dracut -f

That's it, reboot!

Assuming you've gotten no errors, you should now be able to reboot, and your encrypted disk will mount without a password.
There are a few more things you can do to lock down your system

 

Preventing grub command-line modification

One way (other than booting from outside media and mounting the /boot partition) for somebody to access your data would be to break out of init process via changes to the GRUB bootloader command-line. You can prevent this by adding some more configs

Add an "admin" user required for GRUB

By adding an admin account to GRUB, you can prevent unauthorized modification.

First, generate a password hash for the user. Make sure to take note of the result

grub-mkpasswd-pbkdf2

This will return something like

PBKDF2 hash of your password is grub.pbkdf2.sha512.10000.21F457C26C10CC06C44FCFF61A03774067CDDA3023FB4C2AD52884D805DD4022A33C24E3F7203A50C10F4F43607B099D7C27C15AA90BA191BC3BBCF653FBE1DB.6E0514BCE2C36F3CF0A790AE2464DC184F1A5E2B5032682E589D9641C28E2CAD72317A55016C821D9A86A6708530170D6369B755E6BA28274D5AE4477E9D6DE0

Take that above string and create a file under /etc/grub.d/40_custom, e.g. for an "admin" user

#!/bin/sh
exec tail -n +3 $0
# This file provides an easy way to add custom menu entries.  Simply type the
# menu entries you want to add after this comment.  Be careful not to change
# the 'exec tail' line above.

set superusers="admin"
password_pbkdf2 admin grub.pbkdf2.sha512.10000.21F457C26C10CC06C44FCFF61A03774067CDDA3023FB4C2AD52884D805DD4022A33C24E3F7203A50C10F4F43607B099D7C27C15AA90BA191BC3BBCF653FBE1DB.6E0514BCE2C36F3CF0A790AE2464DC184F1A5E2B5032682E589D9641C28E2CAD72317A55016C821D9A86A6708530170D6369B755E6BA28274D5AE4477E9D6DE0

 

Exempt actual booting from needing a password

There. Your next GRUB update should now be (somewhat) secured against unauthorized modification by a password, but if you leave it at this, it will also require a password to boot a kernel. You probably don't want that, so let's exempt your normal boot-process

Edit the file /boot/grub/10_linux, and find some lines like

      echo "menuentry '$(echo "$title" | grub_quote)' ${CLASS} \$menuentry_id_option 'gnulinux-$version-$type-$boot_device_id' {" | sed "s/^/$submenu_indentation/"
  else
      echo "menuentry '$(echo "$os" | grub_quote)' ${CLASS} \$menuentry_id_option 'gnulinux-simple-$boot_device_id' {" | sed "s/^/$submenu_indentation/"

Modify these to look like

      echo "menuentry '$(echo "$title" | grub_quote)' --unrestricted ${CLASS} \$menuentry_id_option 'gnulinux-$version-$type-$boot_device_id' {" | sed "s/^/$submenu_indentation/"
  else
      echo "menuentry '$(echo "$os" | grub_quote)' --unrestricted ${CLASS} \$menuentry_id_option 'gnulinux-simple-$boot_device_id' {" | sed "s/^/$submenu_indentation/"

 

Update GRUB

Now you can update GRUB, and only edits shoudl be restricted, so let's do that now

update-grub2

 

And that should be it. Assuming no errors have occurred, then now you've at least prevented people from breaking out of GRUB during the boot process!

 

 


What else can you do to secure the data and keyfile?

Here are some ideas I haven't fully mapped out yet, which may have further secure your drive while still allowing passwordless decryption

Using TPM

Many modern machines/motherboards have options for secure key management via TPM. This is something I'm actively looking into, but haven't implemented yet

Keys in the EFI/UEFI/BIOS firmware

  • Put the key-file in the EFI: This would mean that a user cannot physically remove the disk from the machine and pull your encryption key. It will instead be in the programmable memory of the motherboard, which is seperate from the physical drive
    Windows does interesting things like store the serial # here, so there may be some way to do this without using a full TPM configuration
  • Lock the BIOS/EFI config menu with a password: Putting the keys in the EFI still doesn't prevent a malicious actor from booting your machine with alternate media and accessing the keys so you want to prevent that

Encrypted boot partition

  • It should be possible to encrypt the actual /boot partition, which would allow you to store the initramfs/keys in there fairly securely. The down-side is that it's extra work (that I haven't documented yet) and you're once again back to entering a password to start GRUB

Put the key-file on a USB stick

  • As an alternative to the EFI, you could put the key-file on a USB stick which you remove when not using the drive. This would allow you to physically separate the data and the encryption key, just don't forget the USB in the drive with the machine unattended!

Put the initramfs in the EFI/UEFI

  • Putting the entire initramfs in the UEFI should be possible. I haven't done it yet - and space may be somewhat limited - but this may be one of the more secure options. Alternately, you may be able to go back to the "encrypted-boot-partition" idea but put a key for *that* in the UEFI, or put have a password hardcoded into GRUB config and lock editing (semi-secure)

 

If there's somebody who's had more luck with UEFI's, please feel free to contact me!