Real full disk encryption using GRUB on Arch Linux for BIOS

Posted on 30.05.2017 by Kim N. Lesmer.
In this tutorial we're gonna take a look at setting up full disk encryption on a BIOS MBR based system using GRUB on Arch Linux - the KISS way.

When we use GRUB as the boot loader we can setup a full disk LUKS encryption system without any use of a separated unencrypted boot partition.

Normally a separate boot partition needs to remain unencrypted as the bootloader needs to be able to boot the kernel before invoking LUKS, but because GRUB can load encryption modules such as crypto.mod, luks.mod, and cryptodisk.mod we can use GRUB in various settings and still gain a real full disk encryption model without the need for an unencrypted boot partition. This setup is not possible using other boot loaders such as systemd-boot or syslinux, because neither of those boot loaders support loading encryption modules (at least as of writing).

The benefits of running with a real full disk encryption rather than an unencryptet boot partition is that we can migitate numerous attacks that can occur before and during the boot process, such as an attacker installing a modified kernel that is able to harvest your password phrase. This doesn't mean that the system isn't vulnerable to tampering with the BIOS or the bootloader itself, however it does provide yet another level of security that makes it a bit more difficult to gain access to the encrypted information.

It is very difficult to prevent tampering with the BIOS and/or other hardware components if you leave your computer out-of-sight, however you can dump your MBR and take a look at it with a hexedecimal editor and compare it to an old secure dump.

Other more secure options exist such as using UEFI with custom signature keys and Secure Boot instead of MBR, or booting from another medium, but this tutorial is about a legacy BIOS setup.

To keep things as simple as possible we're not going to use LVM (Logical Volume Management). LVM is a system for partitioning and managing logical volumes, or filesystems, but it has nothing to do with encryption in itself. LVM is a much more advanced and flexible system than the traditional method of partitioning a disk. LVM is used for easy resizing and moving partitions. With LVM you can create as many Logical Volumes as you need and you can also use LVM to take snapshots of your filesystem. However, unless you actually need any of these features, adding the extra layer of complexity doesn't provide any benefits.

Our setup will simply consist of two disk partitions, one for swap, and one for our normal filesystem. It will look like this:

sdX1 (LUKS encrypted swap)
sdX2 (LUKS with EXT4, XFS, Btrfs or something else)

Both partitions will be fully encrypted with LUKS.

In this example we'll use EXT4 as the filesystem, but you can easily change it into XFS or Btrfs or something else.

If you choose to go for Btrfs you need to look into how you setup your different subvolumes, but it's not difficult. First you setup your filesystem on partition sdX2 and afterwards you setup the different subvolumes.

You can find futher information in the Arch Wiki.

One minor downside to this setup with GRUB is that you have to enter your encryption password twice. Once for GRUB and another time during the system boot-up when the Linux initrd image is loaded. However, this can be avoided by adding a keyfile to the crypttab file. When the system is booted the keyfile resides in the ramfs, unencrypted, but at this point, so does the LUKS master key, so if an attacker can get a hold of your keyfile in this situation, he might as well get your master key. In such a situation you will need to do a lot more to secure your system, something which is well beyond the scope of this tutorial.

Lets get started.

  1. Boot the Arch Linux install medium.
  2. Verify your network using "ip a" and "ping archlinux.org".
  3. Setup your keyboard:

    # loadkeys dk-latin1
  4. Locate your hard drive:

    # fdisk -l
  5. Overwrite your disk with some random data and partition the disk:

    In this case my drive is sda. Please make sure you validate the device name for your drive, it might be sdb or something else.

    Before you begin partitioning your disk it's a good idea to overwrite the disk with random data. You can do this with the dd command. Please notice that this takes considerable time with a large disk.

    # dd if=/dev/urandom of=/dev/sda

    Remember to verify that you're using the correct disklabel type! Check and change it with fdisk.

    Next, lets partition the disk. If you're not comfortable using fdisk then cfdisk is a nice partitioning tool. We're going to create two partitions.

    # cfdisk /dev/sda

    Create the 2 partitions. One for swap and the other for / (the root filesystem). Remember to make the root filesystem bootable.

  6. Format the root partition using LUKS:

    # cryptsetup luksFormat /dev/sda2
  7. Open the newly formattet LUKS partition:

    # cryptsetup luksOpen /dev/sda2 cryptroot

    In this case I have chosen the name "cryptroot" for the encrypted partition, but you can name it whatever you want, just remember to change it everywhere where I have used "cryptroot" in this tutorial.

  8. Format the root volume with the filesystem of your choice (EXT4, XFS, Btrfs, etc.) In this case we're going to use EXT4.

    # mkfs.ext4 /dev/mapper/cryptroot
  9. Mount the root filesystem:

    # mount /dev/mapper/cryptroot /mnt

    If you have used the Btrfs filesystem now is the time to create subvolumes. Once created unmount the root filesystem and then mount the subvolumes instead.

  10. Select the mirrors by placing the ones you prefer in the top:

    # vi /etc/pacman.d/mirrorlist
  11. Bootstrap the system:

    # pacstrap /mnt base
  12. Generate and verify the fstab:

    # genfstab -U /mnt >> /mnt/etc/fstab
    # cat /mnt/etc/fstab

    Currently the swap partition on sda1 isn't setup. We don't bother with that yet.

  13. chroot into the newly created system and set stuff up:

    # arch-root /mnt
    # echo KEYMAP=dk-latin1 > /etc/vconsole.conf
    # ln -sf /usr/share/zoneinfo/Europe/Copenhagen /etc/localtime
    # hwclock --systohc
    # vi /etc/locale.gen (uncomment your locales)
    # locale-gen
    # echo LANG=da_DK.UTF-8 > /etc/locale.conf
    # echo my-hostname > /etc/hostname
  14. Create the keyfile:

    # dd bs=512 count=4 if=/dev/urandom of=/crypto_keyfile.bin
    # cryptsetup luksAddKey /dev/sda2 /crypto_keyfile.bin
    # chmod 000 /crypto_keyfile.bin
  15. Add "encrypt" to the mkinitcpio hooks and the keyfile to the files section:

    # vi /etc/mkinitcpio.conf
    HOOKS="base udev autodetect modconf block encrypt filesystems keyboard fsck"
    FILES="/crypto_keyfile.bin"
  16. Create the boot image:

    # mkinitcpio -p linux
  17. Set the root password:

    # passwd
  18. Install GRUB:

    # pacman -Sy grub
  19. Enable cryptdisk support in GRUB:

    # vi /etc/default/grub

    Add the following lines:

    GRUB_CMDLINE_LINUX="cryptdevice=/dev/sda2:cryptroot"
    GRUB_ENABLE_CRYPTODISK=y

    If you're using a SSD disk you need to add "allow-discards" in order to enable TRIM support:

    GRUB_CMDLINE_LINUX="cryptdevice=/dev/sda2:cryptroot:allow-discards"
  20. Generate grub.cfg:

    # grub-mkconfig -o /boot/grub/grub.cfg
  21. Install GRUB to the boot disk and reboot:

    # grub-install --target=i386-pc /dev/sda
    # exit
    # reboot
  22. After login we need to setup swap. Update crypttab:

    # vi /etc/crypttab
    swap /dev/sda1 /dev/urandom swap,cipher=aes-cbc-essiv:sha256,size=256
  23. Reboot the system again and after login verify that the encryptet swap partition is mapped correctly:

    # ls -l /dev/mapper/
    ...
    swap -> ../dm1
  24. Enable the swap:

    # swapon /dev/mapper/swap

    You can verify a last time with:

    # free
  25. Update fstab:

    # vi /etc/fstab
    /dev/mapper/swap swap swap defaults 0 0
  26. Now you can setup the network and install additional users and packages:

    # useradd -m -g wheel -s /bin/bash foo
    # passwd foo

    I prefer to run with systemd-resolved and systemd-networkd, so I'll set those up.

    # rm /etc/resolv.conf
    # vi /etc/systemd/network/wired.network
    
    [Match]
    Name=enp2s0
    
    [Network]
    DHCP=yes
    
    # systemctl enable systemd-resolved.service
    # systemctl enable systemd-networkd.service
    # systemctl start systemd-resolved.service
    # systemctl start systemd-networkd.service
    # ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf

    /etc/nsswitch.conf is used by GNU C Library (glibc) and certain other applications to determine the sources from which to obtain name service information in a range of categories and in what order. For example /etc/hosts is used by the gethostbyname() function.

    In my case I need to change the setup of nsswitch in order to avoid a very long resolve time.

    hosts: files mymachines dns myhostname

That's it.

Feel free to email me any suggestions or comments.