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 boot loaders support loading encryption modules (at least 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 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.
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.
Let's get started.
Boot the Void Linux install medium.
Verify your network using
I prefer to use Bash, so I change my shell to that, then setup the keyboard:
# bash # loadkeys dk
Locate your hard drive:
# fdisk -l
Overwrite your disk with some random data and partition the disk:
In this case my drive is
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, and you can skip this step if you want to.
# dd if=/dev/urandom of=/dev/sda
Remember to verify that you're using the correct disklabel type! Check and change it with
Next, let's partition the disk.
cfdisk is a nice partitioning tool. We're going to create two partitions.
# cfdisk /dev/sda
Since this is a BIOS setup choose "dos" as the label type and create the 2 partitions. One for swap and the other for / (the root filesystem). Remember to make the root filesystem bootable.
If you're using a disk that has a GPT label, you can change that with
fdisk /dev/sda, then choose
dos, and then
I have made a "sda1" at 8GB with the id type "82 Linux swap / Solaris" and a "sda2" with the rest of the space with the id type "83 Linux" and bootable. Remember to "write" before you quit cfdisk.
Format the root partition using LUKS:
# cryptsetup luksFormat --type luks1 /dev/sda2
cryptsetup currently defaults to v2 of the LUKS header. Until GRUB version 2.06 is released GRUB only supports LUKS1. Make sure to specify
--type luks1 when creating the encrypted partition using
cryptsetup luksFormat. See bug 55093 for details.
Open the newly formatted 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.
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
Bootstrap the system (ignore any complaints from dracut):
# xbps-install -S -R https://alpha.de.repo.voidlinux.org/current/ -r /mnt base-system cryptsetup grub
Answer yes to import the key and install the packages.
Chroot into the newly created system and set it up:
# 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
rc.conf. Don't set the hostname in it.
# vi /etc/rc.conf TIMEZONE="Europe/Copenhagen" KEYMAP="dk"
Create the decryption keyfile:
Let's use a persistent block device naming. I prefer the UUID.
# lsblk -f sda ├─sda1 └─sda2 crypto_LUKS 1 a3269d46-cf1b-46da-89bb-ec4ee3007432 └─cryptroot ext4 1.0 9bbca4ad-9942-452f-9c8d-e042818398eb
It's the UUID that is listed besides the "sda2 crypto_LUKS 1" part we need to use, not the one for the cryptroot.
# 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
It is a really bad idea to use the kernel's simple naming for a swap device, because the naming order (e.g. /dev/sda, /dev/sdb) changes upon each boot. The best option in IMHO is to use the UUID, but by default that does not work because
mkswap would simply overwrite any content on that partition which would remove the UUID, that goes for LABELs too.
However, it is possible to specify a swap offset. This allows us to create a very small, empty, filesystem with no other purpose than providing a persistent UUID for the swap encryption.
Create a filesystem:
# mkfs.ext2 /dev/sda1 1M
The parameter after the device name limits the filesystem size to 1 MiB, leaving room for encrypted swap behind it. Now we have a UUID we can use for the swap partition, we just have to remember to use the
offset option in
In order to make it a bit easier to get the UUID's for
crypttab we can use
lsblk in combination with
grep and then output the result to
crypttab. Afterwards we open
crypttab and edit the content.
# lsblk -f | grep sda >> /etc/crypttab # vi /etc/crypttab swap UUID=fedd0941-f63a-4343-9446-088546843791 /dev/urandom swap,offset=2048,cipher=aes-cbc-essiv:sha256,size=256 cryptroot UUID=a3269d46-cf1b-46da-89bb-ec4ee3007432 /crypto_keyfile.bin luks
# vi /etc/dracut.conf.d/10-crypt.conf install_items+="/crypto_keyfile.bin /etc/crypttab"
Enable encryption support in GRUB:
Again I like to pipe the output of
lsblk in order to get the UUID.
# lsblk -f | grep LUKS >> /etc/default/grub # vi /etc/default/grub
Edit lines to match:
If you're using a SSD disk you need to add "allow-discards" in order to enable TRIM support:
Add "rd.auto=1" to the default command line (this isn't needed on Arch or Debian because they are not using dracut).
GRUB_CMDLINE_LINUX_DEFAULT="loglevel=4 slub_debug=P page_poison=1 rd.auto=1"
The "rd.auto" option 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:
Install GRUB to the disk, generate the configuration file, and setup the kernel hooks (ignore grup complaints about sdc or similar):
# grub-install /dev/sda --recheck # grub-mkconfig -o /boot/grub/grub.cfg
Check the kernel version by looking in /boot:
# ls /boot ... vmlinuz-5.4.24_1
Then setup the kernel hook:
# xbps-reconfigure -f linux5.4
Time to reboot:
# exit # reboot
After login verify that the encryptet swap partition is mapped correctly:
# ls -l /dev/mapper/ ... swap -> ../dm1
Enable the swap:
# swapon /dev/mapper/swap
You can verify a last time with:
# vi /etc/fstab /dev/mapper/swap swap swap defaults 0 0
You can also verify that TRIM support is enabled with:
# dmsetup table
You should see
allow_discards if it is enabled.
Now you can setup the network, add users, and install additional packages packages.
First I setup the network using
# 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. 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:
# 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 properly wont need all the six running tty's so you can remove some of them from
That's it! Feel free to email me any suggestions, correction, or comments.
Thank you William Skeith for emailing me about the "rd.luks.allow-discards" part!