Real full disk encryption using GRUB on Void Linux for BIOS
Published on 2018-03-30. Modified on 2021-08-30.
In this tutorial we're going to take a look at setting up full disk encryption on the Void Linux base system using GRUB for a BIOS/MBR based setup. While the choice to install in UEFI mode is encouraging, vendor UEFI implementations still carry more bugs than their BIOS/MBR counterparts. Contrary to "modern" advice I still haven't found any compelling reason to use UEFI yet.
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
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 support loading encryption modules (as of writing).
The benefits of running with a real full disk encryption rather than an unencrypted boot partition is that we can mitigate 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 UEFI boot loader itself, however it does provide yet another level of security that makes it a bit more difficult to gain access to the encrypted information.
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.
For BIOS our setup will simply consist of one disk partition, it will look like this:
sdX1 (LUKS with EXT4)
Everything will be fully encrypted with LUKS.
Instead of using a partition for swap we will use a swap file. Not only is this a much more simple setup, but it also eliminates the need to encrypt the swap partition independently. By using a swap file we automatically get the encryption we need.
If you need to be able to put your machine into hibernation, you need a proper swap partition. I never use hibernation so I am not going to go through that in this tutorial.
I'll use EXT4 as the filesystem, but you can change that into something else.
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
mkinitcpio - which we'll do. 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.
Let's get started.
Boot the Void Linux install medium. And login as
rootwith the password
Verify your network is up and running with the command:
# ip a
And try to ping something:
# ping voidlinux.org
If you prefer Bash you can change the shell to that before you begin:
Setup your keyboard:
# loadkeys KEYMAP
Where "KEYMAP" corresponds to your keyboard layout. For a list of all the available keymaps, use the command:
# ls /usr/share/kbd/keymaps/i386/qwerty/
In my case I am using a Danish keyboard layout so I am using the following command:
# loadkeys dk
If you find that your keymap is represented with and without a "latin" version, like in
dk-latin, then the latin version is the one that enables dead keys while the one without the latin part doesn't.
Dead keys are those keyboard keys that do not type anything until you hit the key twice or a combination of two keys. Tildes and umlauts are like this by default under plain Linux. This is the default behavior for these keys under Microsoft Windows as well.
Remember that the above keyboard setup might change if you use a graphical window system (such as Xfce4 or Gnome for example) because such systems often has their own keyboard layout setup. The above setup is for usage in the terminal without the X Window System running.
Also if you use another keyboard type than QWERTY, then take a look in
/usr/share/kbd/keymaps/i386/for the supported types.
Locate your harddrive:
# fdisk -l
Before you begin partitioning your disk you may want to write random data to the drive first with something like the following:
# dd if=/dev/urandom of=/dev/sdX
Please note that this can be a very time-consuming process, depending on the speed of your CPU and disk, as well as the size of the disk. If you don't write random data to the whole device, it may be possible for an adversary to deduce how much space is actually being used.
Personally I don't write random data to the disk as I just want to protect the disk from being accessed in case the computer is ever stolen.
Next, partition the disk. If you're not comfortable using
cfdiskis a nice partitioning tool. We're going to create one partition for BIOS or two partitions for UEFI.
# cfdisk /dev/sdX
Create the relevant partitions.
Since this is a BIOS setup choose "dos" as the label type and remember to make the root filesystem bootable.
If you're using a disk that already has a GPT label, you can change that with
fdisk /dev/sdX, then choose
dos, and then
Format the root partition using LUKS:
# cryptsetup luksFormat --type luks1 /dev/sdX1
cryptsetupcurrently defaults to v2 of the LUKS header. There has been great work at getting GRUB version 2.06 to support LUKS2, but there still is a bug that prevents this from working. Make sure you specify
--type luks1when creating the encrypted partition. See bug 55093 and the Encrypted boot section at the Arch Linux wiki for details.
Open the newly formatted LUKS partition:
# cryptsetup open /dev/sdX1 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.
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
Mount the root filesystem and
# mount /dev/mapper/cryptroot /mnt # mkdir /mnt/dev /mnt/proc /mnt/sys # mount --rbind /dev /mnt/dev # mount --rbind /proc /mnt/proc # mount --rbind /sys /mnt/sys
Now we're going to install the base system and a couple of other packages. I use Vim as my text editor, so I also install that. You can change that into something else. You can search on the Void Linux Packages page to see if your favorite editor is available.
# xbps-install -Sy -R https://alpha.de.repo.voidlinux.org/current -r /mnt base-system cryptsetup grub vim
Answer yes to import the key and install the packages.
Chroot into the newly created system and set it up (change the zoneinfo to your location):
# chroot /mnt /bin/bash # passwd # chsh -s /bin/bash # ln -sf /usr/share/zoneinfo/Europe/Copenhagen /etc/localtime # echo LANG=en_US.UTF-8 > /etc/locale.conf # echo "en_US.UTF-8 UTF-8" >> /etc/default/libc-locales # xbps-reconfigure -f glibc-locales # echo my-hostname > /etc/hostname
/etc/rc.confto suit your needs.
Create the keyfile (in order to avoid having to type the encryption password twice). Let's use a persistent block device naming. I prefer the UUID.
# blkid -o value -s UUID /dev/sdX1 a3269d46-cf1b-46da-89bb-ec4ee3007432
Then create the keyfile:
# dd bs=512 count=4 if=/dev/urandom of=/crypto_keyfile.bin # cryptsetup luksAddKey /dev/disk/by-uuid/a3269d46-cf1b-46da-89bb-ec4ee3007432 /crypto_keyfile.bin # chmod 000 /crypto_keyfile.bin # chmod -R g-rwx,o-rwx /boot
crypttab. In order to avoid typing the UUID manually, you can direct the output of
crypttabfile and then edit it afterwards.
# blkid -o value -s UUID /dev/sdX1 >> /etc/crypttab
crypttaband set it up correctly.
# vim /etc/crypttab cryptroot UUID=a3269d46-cf1b-46da-89bb-ec4ee3007432 /crypto_keyfile.bin luks
Then we need to setup Dracut:
# vim /etc/dracut.conf.d/10-crypt.conf install_items+=" /crypto_keyfile.bin /etc/crypttab "
Notice the surrounding white spaces, you need those!
Enable encryption support in GRUB:
Again I like to direct the output of
blkidin order to get the UUID.
# blkid -o value -s UUID /dev/sdX1 >> /etc/default/grub # vim /etc/default/grub
Edit lines to match your UUID:
Add "rd.auto=1" to the default command line.
GRUB_CMDLINE_LINUX_DEFAULT="loglevel=4 slub_debug=P page_poison=1 rd.auto=1"
rd.autooption enables autoassembly of special devices like LUKS, dmraid, mdraid or lvm. Default is off as of dracut version >= 024.
I also prefer to set the timeout to something less than 5:
If you're using an SSD disk you need to consider whether you want to add the "discard" option in order to enable continues TRIM support.
On traditional magnetic drives, deleted files are not completely removed from the disk at the time of deletion (this is why you can recover deleted files), instead the filesystem references the location of a file on the disk, and when a file is deleted, that reference is erased, allowing you to write new data over old data in these spaces.
With SSDs this is different. New data can only be written on completely new or erased cells of the drive. Because the space must be cleared prior to a write, if enough free space is not already available at the time a file is being written, it must be erased first. This can negatively affect performance.
TRIM allows the SSD to erase unused cells in the background so that the SSD does not have to erase the cell later when it has to write, thus speeding up the write process.
Most recent SSDs have their own internal garbage collection process that does this very effectively, so TRIM isn't necessary to maintain write performance anymore.
Without enabling TRIM (either periodic TRIM or continues TRIM) garbage collection can become write-amplified in the edge case where your hard drive is almost full. This problem can be mostly mitigated by over-provisioning the SSD's unused space (leave about 20% of the drive free).
Please see the Relevant reading section for further information regarding TRIM, especially the links regarding OpenBSD.
From a security point of view, enabling TRIM allows an attacker to get an idea of how full the volume is.
If you're using a SSD disk and you want to enable continues TRIM you need to add "allow-discards":
Install GRUB to the disk and generate the configuration file:
# grub-install /dev/sdX --recheck # grub-mkconfig -o /boot/grub/grub.cfg
Ensure an initramfs is generated:
# xbps-reconfigure -fa
Exit the chroot, unmount the filesystems, and reboot the system:
# exit # umount -R /mnt # reboot
You should now be presented with the GRUB prompt for the password. Once the system is finished booting, log in.
Now you can setup the network, add users, and install additional packages.
First I setup the network with dhcpcd. The
dhcpcdpackage is included in the
base-systemmeta package, so it is already installed.
# ln -s /etc/sv/dhcpcd /var/service/
This will automatically start the service in your current runlevel. Once a service is linked it will always start on boot and restart if it stops. If you need to keep an enabled service from starting automatically at boot, create a file named "down" in the service directory like this:
# touch /etc/sv/service_name/down
Then I'll add a normal user (called foo in this example) and add him to the
# useradd -m -g wheel -s /bin/bash foo # passwd foo
I also like to install the chrony daemon to keep my clock in sync and socklog for logging and to enable ACPI events:
# xbps-install -Su # xbps-install chrony socklog-void # ln -s /etc/sv/chronyd /var/service/ # ln -s /etc/sv/socklog-unix /var/service/ # ln -s /etc/sv/nanoklogd /var/service/ # ln -s /etc/sv/acpid /var/service
You probably wont need all the six running tty's so you can remove some of them from
Then it's time to setup the relevant entries to the
# vim /etc/hosts 127.0.0.1 localhost ::1 localhost 127.0.1.1 myhostname.localdomain myhostname
Last we need to create a swap file (if you want that). Use
ddto create a file the size of your choosing. E.g. creating a 4GB swap file:
# dd if=/dev/zero of=/swapfile bs=1M count=4096 status=progress
Set the right permissions:
# chmod 600 /swapfile
Format the file to swap:
# mkswap /swapfile
Activate the swap file:
# swapon /swapfile
Finally, edit the
/etc/fstabconfiguration to add an entry for the swap file:
# vim /etc/fstab /swapfile none swap defaults 0 0
You can check that the swapfile is up and running with:
That's it. You can now proceed and install all the rest of your favorite programs :)