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 !
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 https://geo.mirror.pkgbuild.com/iso/2023.02.01/archlinux-x86_64.iso -O arch.iso
We're going to use Qemu to create the VM for the installation.
$ sudo pacman -Sy qemu-base
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 .
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 \
root@localhost
Now that we have access to the VM with a terminal from our host, let's start the installation.
First thing to do it to format the disk.
The hard drive we created earlier is identified as /dev/sda
# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
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.
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
.
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
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 8G 0 disk
└─sda1 8:1 0 128M 0 part
Now we're going to format the partition
# mkfs.fat -F 32 /dev/sda1
Let's verify the partition filesystem.
# lsblk -f /dev/sda1
NAME FSTYPE FSVER LABEL UUID
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.
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.
Let's setup the encrypted system partition with LUKS.
# cryptsetup luksFormat --type luks1 /dev/sda2
WARNING!
========
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
NAME FSTYPE FSVER LABEL UUID
sda
├─sda1 vfat FAT32 75D8-2F47
└─sda2 crypto_LUKS 1 76c96949-6686-4a83-bf43-78798387d163
└─system
We're going to use Btrfs filesystem
# mkfs.btrfs /dev/mapper/system
So far, we've created two partition:
# lsblk /dev/sda -f
NAME FSTYPE FSVER LABEL UUID
sda
├─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.
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
Let's chroot to the new installed system first.
# arch-chroot /mnt
Setup the root password with passwd
command otherwise we won't be able to log in later once the installation is done.
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:
...
FILES=(/crypto_keyfile.bin)
...
HOOKS=(base udev autodetect modconf kms keyboard keymap consolefont block encrypt filesystems fsck)
...
Finally we generate the initramfs
# mkinitcpio -P
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"
...
GRUB_ENABLE_CRYPTODISK=y
...
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 ...
done
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.
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
.
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 ...