Linux full disk encryption

Last update: 27 March 2023

In this post, I'm going to show how to setup a full disk encryption in Linux. I'm going to cover the case where the machine is shipped with UEFI and one hard drive, which is the most common setup these days.

I'm gonna use Archlinux, but the same process applies to any Linux distribution.

Let's get started !

Setup the virtual installation environment

Let's start by creating the installation directory

$ mkdir linux
$ chattr +C linux # only if you are using btrfs filesystem
$ cd linux

Then download Arch installation iso

$ wget -O arch.iso

We're going to use Qemu to create the VM for the installation.

$ sudo pacman -Sy qemu-base
ℹ️ Note
Check the download page from qemu to see how to install it on your system.

Then we need to create the VM hard drive, here we're creating a 8GB disk.

$ qemu-img create -f raw arch_image 8G

To be able to use UEFI as a firmware for the VM, we will need OVMF

$ sudo pacman -Sy edk2-ovmf
$ cp /usr/share/edk2-ovmf/x64/OVMF_VARS.fd .
ℹ️ Note
The OVMF_VARS.fd may be in another location depending on your system.

Then finally create the VM

$ qemu-system-x86_64 \
 -enable-kvm \
 -nic user,hostfwd=tcp::10022-:22 \
 -drive if=pflash,format=raw,readonly=on,file=/usr/share/edk2-ovmf/x64/OVMF_CODE.fd \
 -drive if=pflash,format=raw,file=./OVMF_VARS.fd \
 -drive file=arch_image,format=raw \
 -boot menu=on \
 -m 2G \
 -cdrom arch.iso

Once the VM is created we're gonna connect to it using tigervnc. (You can use any VNC client)

$ vncviewer :5900

Then we choose the first option once we're presented with the installation menu

Now we should have access to the shell for the installation as shown below

We need to setup the root password first as we'll need that to connect to the VM via ssh which is more convenient than working within the VM console.

# passwd

Then we go back to the host and connect to the installation environment via ssh

$ ssh \
    -o StrictHostKeyChecking=no \
    -o "UserKnownHostsFile /dev/null" \
    -p 10022 \

Now that we have access to the VM with a terminal from our host, let's start the installation.

Partition the disk

First thing to do it to format the disk. The hard drive we created earlier is identified as /dev/sda

# lsblk

fd0     2:0    1     4K  0 disk
loop0   7:0    0 710.1M  1 loop /run/archiso/airootfs
sda     8:0    0     8G  0 disk
sr0    11:0    1 824.3M  0 rom  /run/archiso/bootmnt

We'll create a GPT partition table with fdisk command as UEFI only supports GPT partition table type

# fdisk /dev/sda

Command (m for help): g
Created a new GPT disklabel (GUID: EE6C7F08-8852-3349-91D2-0358A3C96522).

Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.

ESP partition

As we're using UEFI, we need to create the ESP partition where the bootloader will be stored. This partition should be formatted with FAT32.

Create the ESP partition

128MB is enough for the ESP partition for our case.

# fdisk /dev/sda

Command (m for help): n
Partition number (1-128, default 1):
First sector (2048-16777182, default 2048):
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-16777182, default 16775167): +128M

Created a new partition 1 of type 'Linux filesystem' and of size 128 MiB.

Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.

Let's check the ESP partition

# lsblk /dev/sda

sda      8:0    0     8G  0 disk
└─sda1   8:1    0   128M  0 part

Format the ESP partition

Now we're going to format the partition

# mkfs.fat -F 32 /dev/sda1

Let's verify the partition filesystem.

# lsblk -f /dev/sda1
sda1 vfat   FAT32       75D8-2F47

So far so good. Now that we have created the ESP partition, let's move to the next step and create the system partition where we're gonna install the OS.

System partition

Create the system partition

We're gonna use the rest of the space of the hard drive for the OS. As this setup is oriented toward the daily use workstations, we're going to put the root / into one partition that we will call system partition.

# fdisk /dev/sda

Command (m for help): n
Partition number (2-128, default 2):
First sector (264192-16777182, default 264192):
Last sector, +/-sectors or +/-size{K,M,G,T,P} (264192-16777182, default 16775167):

Created a new partition 2 of type 'Linux filesystem' and of size 7.9 GiB.

Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.

Encrypt the system partition

Let's setup the encrypted system partition with LUKS.

# cryptsetup luksFormat --type luks1 /dev/sda2

This will overwrite data on /dev/sda2 irrevocably.

Are you sure? (Type 'yes' in capital letters): YES
Enter passphrase for /dev/sda2:
Verify passphrase:
cryptsetup luksFormat --type luks1 /dev/sda2  5.22s user 0.05s system 42% cpu 12.332 total

Then decrypt the system partition to be able to format it later.

# cryptsetup open /dev/sda2 system
Enter passphrase for /dev/sda2:

# lsblk /dev/sda -f
├─sda1     vfat        FAT32       75D8-2F47
└─sda2     crypto_LUKS 1           76c96949-6686-4a83-bf43-78798387d163

Format the system partition

We're going to use Btrfs filesystem

# mkfs.btrfs /dev/mapper/system


So far, we've created two partition:

  1. The ESP partition formatted with FAT32 where the bootloader going to be installed,
  2. The system partition encrypted using LUKS and formatted with Btrfs where the OS going to be installed

# lsblk /dev/sda -f

├─sda1     vfat        FAT32       75D8-2F47
└─sda2     crypto_LUKS 1           76c96949-6686-4a83-bf43-78798387d163
  └─system btrfs                   960a21bd-202a-4dc7-9893-1b7c5d0b946e

Let's move to the next step where we're going to install the OS.

System install

Install the base system

We need first to mount the different partitions (ESP and system)

# mount /dev/mapper/system /mnt
# mount --mkdir /dev/sda1 /mnt/efi

Then install the base system with some tools.

# pacstrap -K /mnt base btrfs-progs neovim linux linux-firmware

Finally generate the fstab file

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

Chroot to the new installed system

Let's chroot to the new installed system first.

# arch-chroot /mnt

Setup a root password

Setup the root password with passwd command otherwise we won't be able to log in later once the installation is done.

Configure initramfs

We need to configure the initramfs to be able to decrypt the system partition. For that, we will be prompted to enter the password, which is tedious cause we will be asked the first time for the password for the bootloader and second time for the initramfs. Fortunately there is a better way using the keyfile.

Let's generate a new keyfile

# dd bs=512 count=4 if=/dev/random of=/crypto_keyfile.bin iflag=fullblock
# chmod 600 /crypto_keyfile.bin
# cryptsetup luksAddKey /dev/sda2 /crypto_keyfile.bin

Then we need to add the encrypt hook and the decryption keyfile in the /etc/mkinitcpio.conf file as follows:

HOOKS=(base udev autodetect modconf kms keyboard keymap consolefont block encrypt filesystems fsck)

Finally we generate the initramfs

# mkinitcpio -P

Install the bootloader

We're going to choose grub as the bootloader because it's capable of booting an encrypted /boot partition.

Let's install it first

# pacman -S grub efibootmgr

Now we need to get the block device id of the encrypted system partition.

# blkid /dev/sda2
/dev/sda2: UUID="a6ec0b94-359d-455f-9395-6d35ec4f6c0f" TYPE="crypto_LUKS" PARTUUID="15c4c28d-9e1e-5747-b0fe-658d6607217e"

Then we configure Grub by editing the file /etc/default/grub as follows:

GRUB_CMDLINE_LINUX="cryptdevice=UUID=2e89e287-3ecb-47d2-ad34-c0f50df47b01:system root=/dev/mapper/system"


Then we install Grub in the ESP partition so the UEFI can load it.

# grub-install --target=x86_64-efi --efi-directory=/efi --bootloader-id=GRUB
Installing for x86_64-efi platform.
Installation finished. No error reported.

Finally we generate configuration that will make Grub aware of the new installed OS.

# grub-mkconfig -o /boot/grub/grub.cfg
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-linux
Found initrd image: /boot/initramfs-linux.img
Found fallback initrd image(s) in /boot:  initramfs-linux-fallback.img
Warning: os-prober will not be executed to detect other bootable partitions.
Systems on them will not be added to the GRUB boot configuration.
Check GRUB_DISABLE_OS_PROBER documentation entry.
Adding boot menu entry for UEFI Firmware Settings ...


We exit from the chroot with exit command, then we run reboot to reboot.

we're going to use the VNC client we've used earlier tigervnc to visualize the boot process.

We will be asked for the password by Grub as shown below

And the whole boot process should continue till we get asked to login

We login with the user root and the password we setup earlier.

Now we'll move to the last section where we will add a new user.

Add new user with systemd-homed

Instead of using useradd to create a new user, we're going to use systemd-homed which is suitable for human-user accounts.

We need first to start and enable the systemd-homed service

# systemctl start systemd-homed
# systemctl enable systemd-homed

Then create a new user as follows:

# homectl create --storage=luks --fs-type=btrfs pythops

This will create a new user called pythops and stores the /home/pythops in a btrfs filesystem inside an encrypted LUKS volume.

we can test it by existing the current session with exit command and login with the new user pythops.

What's next ?

Once you have a new fresh machine, you'll need obviously to install many tools and configure them. Wouldn't be great to have this part automated where in a couple of minutes you can have everything installed and configured properly ? Check my previous post and my github repo where I showed how I automated my setup using ansible.

Read more ...

Kubernetes Security Considerations

Create your own image for jetson nano board

Linux User Authentication