Install Fedora with LUKS Full Disk Encryption and Snapper with Full System Rollback Feature Image

Install Fedora with LUKS Full Disk Encryption and Snapper with Full System Rollback

In this post, I'll show you how to install Fedora with LUKS full disk encryption and snapper with full system rollback capability. For disk encryption, I'll use LUKS version 1.

In my earlier post titled "Install Fedora 35 with LUKS Full Disk Encryption (FDE)", I showed how to install Fedora 35 with full disc encryption using LUKS version 2. However, because the /boot partition was on its own partition in that setup, you couldn't use the snapper to rollback the entire system, including the kernel.

Since support for LUKS2 in GRUB 2.0.6 is limited (Argon2 is not yet supported, only the PBKDF2 is supported), it is recommended to use LUKS1 to encrypt the disk if the /boot directory is in the 'root file system' and not on a separate partition. Hopefully, in the next release of GRUB, support for /boot partition encryption using LUKS2 with Argon2i and Argon2id will be added.

Here's a quick overview of what you can expect.

  1. Install Fedora 35 Workstation with a LUKS1 encrypted BtrFS 'root filesystem' and a separate Ext4 /boot partition, as the Anaconda installer does not support encryption of the /boot partition.
  2. Copy the contents of the /boot partition to the 'root filesystem,' and configure GRUB to boot from it.
  3. Remove the /boot partition and merge the space it used into the main BtrFS file system.
  4. Configure the system to request the passphrase only once by providing the key file.
  5. Install and configure snapper to take snapshots of the root subvolume.
  6. Finally, test the full system rollback by upgrading to the most recent kernel and reverting back to the prior state.

And here's how the final layout of the disk looks.

ESP PARTITIONBTRFS VOLUME
/boot/efi//home/var/log/var/lib/libvirt/images
roothomelogimages
/dev/mapper/luks-XXXX
/dev/vda1/dev/vda2
UEFI SECURE BOOTLUKS1 ENCRYPTED

So let's begin.

Table of Contents

1. Install Fedora 35 Workstation with LUKS1 encryption

Boot your computer using the Fedora 35 Workstation installer in UEFI mode. On the welcome screen, select the Install to Hard Drive option. Next, select your LanguageKeyboard, and configure Time & Date. Following that, from the INSTALLATION SUMMARY screen, select Installation Destination.

You should now be on the INSTALLATION DESTINATION screen. To proceed, pick the Advanced Custom (Blivet-GUI) radio button and then hit the Done button.

Fedora LUKS With FDE And Snapper With Rollback Select Blivet

You will see the BLIVET GUI PARTITIONING screen. Here you will create partitions, file systems, and BtrFS subvolumes necessary to install Fedora 35 Workstation.

First, you need to create and mount the EFI partition. Select the free space and click the + sign to create a partition.

Fedora LUKS With FDE And Snapper With Rollback Blivet Screen

Set the partition Size to 512 MiB, the Filesystem to EFI System Partition, and the Mountpoint to /boot/efi.

Fedora LUKS With FDE And Snapper With Rollback Blivet Efi Partition

Then, you need to create a btrfs volume where you can create all the subvolumes needed to install Fedora Linux.

Select the free space again and click on the + sign to create a Btrfs volume. (1) Set the Device Type to Btrfs Volume. (2) Set the Size of the Btrfs volume. At the end, leave at least 1 GiB for the boot partition. I've left around 1.5 GiB unallocated. (3) Set the name for Btrfs volume. I named the Btrfs volume FEDORA, but you can name it whatever you like. (4) Set the Encryption type to luks1 and (5) provide a strong password (password entropy > 60 bits is suggested). Do not set any Mountpoint here. Leave it blank and click on the OK button.

Fedora LUKS With FDE And Snapper With Rollback Blivet Btrfs Volume

You then need to create and mount the /boot partition. Set the boot partition to use all of the remaining unallocated space (1534 MiB), the Filesystem to ext4, and the Mountpoint to /boot. After the installation is finished, I'll copy the contents of the /boot partition to the main root subvolume and merge this 1.5 GiB space with the main BtrFS volume.

Fedora LUKS With FDE And Snapper With Rollback Blivet Boot Partition

Your partitions should look something like this.

Fedora LUKS With FDE And Snapper With Rollback Blivet All Partitions

Next, you must create subvolumes. (1) Select the Btrfs Volume from the left panel, and (2) click on the + sign on the right panel.

Fedora LUKS With FDE And Snapper With Rollback Blivet Select Btrfs Volume

I’ll create four subvolumes: roothomelog, and images. I’m creating a separate images subvolume because I don’t want the KVM disk images to be included when I take a snapshot of the ‘root file system’.

BtrFS subvolumes are created in LIFO order in the Anaconda installer. As a result, I'll define subvolumes in reverse order.

Create images subvolume. Enter the Name as images and Mountpoint as /var/lib/libvirt/images. Click OK to finish.

Fedora LUKS With FDE And Snapper With Rollback Blivet Subvolume Images

Create log subvolume. Enter the Name as log and Mountpoint as /var/log. Click OK to finish.

Fedora LUKS With FDE And Snapper With Rollback Blivet Subvolume Log

Click again on the + symbol to create the home subvolume. Enter the Name as home and Mountpoint as /home. Click OK to finish.

Fedora LUKS With FDE And Snapper With Rollback Blivet Subvolume Home

Repeat the same process for the root subvolume as well. Enter the Name as root and Mountpoint as /. Click OK to finish.

Fedora LUKS With FDE And Snapper With Rollback Blivet Subvolume Root

When completed, the subvolumes must look something like this. Click Done to create subvolumes.

Fedora LUKS With FDE And Snapper With Rollback Blivet Subvolume Overview

As with the swap partition, Fedora generates a SwapOnZRAM upon startup, so no separate swap partition is needed.

Verify that the partitions and subvolumes are properly defined on the SUMMARY OF CHANGES screen. To finalize the changes, click the Accept Changes button.

Fedora LUKS With FDE And Snapper With Rollback Confirm Creation

You will be returned to the INSTALLATION SUMMARY screen. Press the Begin Installation button to start the installation process. The installation process will start. Just sit back and relax.

Fedora LUKS With FDE And Snapper With Rollback Installation Process

When the installation is finished, click the Finish installation button and restart the computer. After the computer restarts, you will be prompted for the LUKS passphrase.

Install Fedora 35 with LUKS Full Disk Encryption LUKS2 Passphrase

Enter the LUKS1 passphrase that you provided. The last phase of the installation procedure will start. Click the Start Setup button to complete the remaining customization steps, such as setting a new login, password, and so on.

You will then be logged into the all-new Gnome 41 desktop interface.

Fedora LUKS With FDE And Snapper With Rollback Gnome 41 Desktop

Open the Gnome terminal and check your current setup.

[madhu@fedora ~]$ sudo btrfs subvolume list /
ID 256 gen 86 top level 5 path root
ID 257 gen 86 top level 5 path home
ID 258 gen 88 top level 5 path log
ID 259 gen 38 top level 5 path images
ID 264 gen 83 top level 256 path var/lib/machines
[madhu@fedora ~]$ sudo btrfs filesystem show /
Label: 'FEDORA'  uuid: bd69e80f-fd90-4b70-b13c-47ce9d253605
	Total devices 1 FS bytes used 2.99GiB
	devid    1 size 78.00GiB used 6.02GiB path /dev/mapper/luks-62740414-8b79-4f91-b36f-1c3dd3511adf
[madhu@fedora ~]$ lsblk -p
NAME                                                      MAJ:MIN RM  SIZE RO TYPE  MOUNTPOINTS
/dev/zram0                                                251:0    0  3.8G  0 disk  [SWAP]
/dev/vda                                                  252:0    0   80G  0 disk  
├─/dev/vda1                                               252:1    0  512M  0 part  /boot/efi
├─/dev/vda2                                               252:2    0   78G  0 part  
│ └─/dev/mapper/luks-62740414-8b79-4f91-b36f-1c3dd3511adf 253:0    0   78G  0 crypt /var/log
│                                                                                   /var/lib/libvirt/images
│                                                                                   /home
│                                                                                   /
└─/dev/vda3                                               252:3    0  1.5G  0 part  /boot

Review the device LUKS1 encryption details.

[madhu@fedora ~]$ sudo cryptsetup luksDump /dev/vda2
LUKS header information for /dev/vda2
Version:       	1
Cipher name:   	aes
Cipher mode:   	xts-plain64
Hash spec:     	sha256
Payload offset:	4096
MK bits:       	512
MK digest:     	04 6f 33 e1 0f 9f c8 91 b4 a3 ab 51 a2 4b 89 4c 3e d8 ca 11 
MK salt:       	99 c5 5a 0b 8d a1 21 50 a5 50 91 e3 4f 90 40 e7 
               	85 66 56 a2 47 64 e5 4b 37 e7 25 98 02 36 f0 45 
MK iterations: 	104690
UUID:          	62740414-8b79-4f91-b36f-1c3dd3511adf
Key Slot 0: ENABLED
	Iterations:         	1716162
	Salt:               	f6 45 22 3c ab 77 7d 34 20 66 8c a3 39 aa 22 94 
	                      	14 ee ac d1 3b 8f b5 3e 88 5c 03 79 99 0b 52 3f 
	Key material offset:	8
	AF stripes:            	4000
Key Slot 1: DISABLED
Key Slot 2: DISABLED
Key Slot 3: DISABLED
Key Slot 4: DISABLED
Key Slot 5: DISABLED
Key Slot 6: DISABLED
Key Slot 7: DISABLED

When you use QCOW2 images as virtual disks in KVM, the IO performance suffers. This is because both the Btrfs filesystem and the QCOW2 images employ Copy-on-Write (CoW). When there is a lot of IO activity on the guest, the KVM guest may even freeze.

So you need to disable the CoW feature in the /var/lib/libvirt/images directory.

[madhu@fedora ~]$ sudo chattr -R +C /var/lib/libvirt/images/

[madhu@fedora ~]$ sudo lsattr -d /var/lib/libvirt/images/
---------------C------ /var/lib/libvirt/images/

2. Move the /boot to the 'root filesystem'

Now that the installation is complete, transfer the contents of the /boot partition to the 'root filesystem,' and configure GRUB to boot from it.

Log in as root and unmount the EFI partition.

[madhu@fedora ~]$ sudo -i

[root@fedora ~]# umount -v /dev/vda1
umount: /boot/efi (/dev/vda1) unmounted

Unmount the boot partition from the /boot directory as well, then mount it into the /mnt directory temporarily.

[root@fedora ~]# umount -v /dev/vda3
umount: /boot (/dev/vda3) unmounted

[root@fedora ~]# mount -v /dev/vda3 /mnt
mount: /dev/vda3 mounted on /mnt.

Copy all files from /mnt to the /boot directory.

[root@fedora ~]# cp -arv /mnt/. /boot

Mount the EFI partition (/dev/vda1) back to /boot/efi.

[root@fedora ~]# mount -v /dev/vda1 /boot/efi
mount: /dev/vda1 mounted on /boot/efi.

Restore the SELinux labels.

[root@fedora ~]# restorecon -RFv /boot

Unmount the /dev/vda3 partition from /mnt and delete its entry from the /etc/fstab file. You no longer need the /dev/vda3 partition.

[root@fedora ~]# umount -v /mnt
umount: /mnt unmounted

[root@fedora ~]# sed -i.original '/\/boot.*ext4/d' /etc/fstab

[root@fedora ~]# mount -a

Finally, enable the CRYPTODISK option in GRUB. When enabled, it will check the encrypted disks and generate the additional commands needed to access them during boot.

[root@fedora ~]# echo "GRUB_ENABLE_CRYPTODISK=y" >> /etc/default/grub

3. Configure Fedora for rollback

Because we're also prepping Fedora for full system rollback, we need to make a few other modifications before we can regenerate the grub.cfg file.

Allow snapshot booting by appending the SUSE_BTRFS_SNAPSHOT_BOOTING option to the /etc/default/grub file.

[root@fedora ~]# echo "SUSE_BTRFS_SNAPSHOT_BOOTING=true" >> /etc/default/grub

In Fedora, the default subvolume is set to top-level (FS_TREE). You must explicitly set the root subvolume to be configured as default. This will be the default if you do not boot from another snapshot.

[root@fedora ~]# btrfs subvolume get-default /
ID 5 (FS_TREE)

[root@fedora ~]# btrfs subvolume list /
ID 256 gen 117 top level 5 path root
ID 257 gen 88  top level 5 path home
ID 258 gen 118 top level 5 path log
ID 259 gen 89  top level 5 path images
ID 264 gen 83  top level 256 path var/lib/machines

[root@fedora ~]# btrfs subvolume set-default 256 /

[root@fedora ~]# btrfs subvolume get-default /
ID 256 gen 119 top level 5 path root

The kernel and initrd paths are configured to look in the parent directory of the previous /boot partition (/dev/vda3). This must be changed to the /boot directory in the 'root file system' (/dev/sda2). Also, now that we've enabled snapshot booting, we should delete the rootflags=subvol=root from the args line.

[root@fedora ~]# grubby --info=DEFAULT
index=0
kernel="/vmlinuz-5.14.10-300.fc35.x86_64"
args="ro rootflags=subvol=root rd.luks.uuid=luks-62740414-8b79-4f91-b36f-1c3dd3511adf rhgb quiet"
root="UUID=bd69e80f-fd90-4b70-b13c-47ce9d253605"
initrd="/initramfs-5.14.10-300.fc35.x86_64.img"
title="Fedora Linux (5.14.10-300.fc35.x86_64) 35 (Workstation Edition)"
id="ae883586b86845dcb60a087f9dc8b808-5.14.10-300.fc35.x86_64"

To do this, first, remove the old Boot Loader Specification (BLS) configuration files.

[root@fedora ~]# rm /boot/loader/entries/*.conf

Then recreate the BLS config file by adding the kernel image.

[root@fedora ~]# kernel-install -v add $(uname -r) \
  /lib/modules/$(uname -r)/vmlinuz

Now, regenerate the grub.cfg file.

[root@fedora ~]# grub2-mkconfig -o /boot/grub2/grub.cfg

Examine the newly created BLS configuration files. The '/boot' directory should be added to the kernel and initrd lines, and the args line should have 'rootflags=subvol=root' deleted.

[root@fedora ~]# grubby --info=DEFAULT
index=0
kernel="/boot/vmlinuz-5.14.10-300.fc35.x86_64"
args="ro rd.luks.uuid=luks-62740414-8b79-4f91-b36f-1c3dd3511adf rhgb quiet ${extra_cmdline}"
root="UUID=bd69e80f-fd90-4b70-b13c-47ce9d253605"
initrd="/boot/initramfs-5.14.10-300.fc35.x86_64.img"
title="Fedora Linux (5.14.10-300.fc35.x86_64) 35 (Workstation Edition)"
id="ae883586b86845dcb60a087f9dc8b808-5.14.10-300.fc35.x86_64"

Because encrypted GRUB and snapshot booting are now enabled, you must make changes to the /boot/efi/EFI/fedora/grub.cfg file as well.

From this:

search --no-floppy --fs-uuid --set=dev edefa42a-440b-44fc-9851-d7c3813f6201
set prefix=($dev)/grub2

export $prefix
configfile $prefix/grub.cfg

To this:

set btrfs_relative_path="yes"
cryptomount -u 627404148b794f91b36f1c3dd3511adf
search --no-floppy --fs-uuid --set=dev bd69e80f-fd90-4b70-b13c-47ce9d253605
set prefix=($dev)/boot/grub2
export $prefix
configfile $prefix/grub.cfg

Changes are in orange color.

  1. Sets the path relative to the subvolume from which you are booting.
  2. Setup access to encrypted boot device. The UUID here is of the /dev/vda2 partition without dashes. Replace UUID with the value that corresponds to your configuration.
  3. Change the UUID to the mapped LUKS1 device. Replace UUID with the value that corresponds to your configuration.
  4. Insert the '/boot' directory into the 'set prefix' line.
[root@fedora ~]# lsblk -po name,uuid
NAME                                                      UUID
/dev/zram0                                                
/dev/vda                                                  
├─/dev/vda1                                               44C8-163B
├─/dev/vda2                                               62740414-8b79-4f91-b36f-1c3dd3511adf
│ └─/dev/mapper/luks-62740414-8b79-4f91-b36f-1c3dd3511adf bd69e80f-fd90-4b70-b13c-47ce9d253605
└─/dev/vda3                                               edefa42a-440b-44fc-9851-d7c3813f6201

Finally, disable the GRUB menu auto-hide feature. When Fedora is the only operating system installed on the system, Fedora implements a feature called ‘Hidden Grub Menu‘. The goal is to keep the interface as simple as possible, providing information only when it is necessary. However, you need to visually see the GRUB menu to verify whether the rollback is executed correctly.

This feature can be disabled by doing the following.

[root@fedora ~]# grub2-editenv - list
saved_entry=ae883586b86845dcb60a087f9dc8b808-5.14.10-300.fc35.x86_64
menu_auto_hide=1
boot_success=1
boot_indeterminate=0
[root@fedora ~]# grub2-editenv - unset menu_auto_hide

And reboot.

[root@fedora ~]# reboot

Reboot the computer and verify if it worked. You will be prompted for the password twice. The first time is before loading GRUB, and the second time is before mounting the / file system. Depending on your computer’s CPU, the GRUB may take up to 30 seconds to display the menu after you enter the first passphrase. So please be patient.

4. Delete the /dev/vda3 partition and reclaim the space

We no longer need the /dev/vda3 partition since we moved the /boot files from /dev/vda3 partition to the /boot directory in the root subvolume (/dev/vda2). As a result, we can safely delete the /dev/vda3 partition from the partition table and transfer the space it occupies (about 1.5 GiB) to the btrfs volume.

Space used up by the BtrFS volume (before).

[madhu@fedora ~]$ sudo btrfs filesystem show /
Label: 'FEDORA'  uuid: bd69e80f-fd90-4b70-b13c-47ce9d253605
	Total devices 1 FS bytes used 3.11GiB
	devid    1 size 78.00GiB used 6.02GiB path /dev/mapper/luks-62740414-8b79-4f91-b36f-1c3dd3511adf

Run the gdisk utility.

[madhu@fedora ~]$ sudo gdisk /dev/vda

On the command input prompt, enter the key p to print the partition table.

Command (? for help): p
Disk /dev/vda: 167772160 sectors, 80.0 GiB
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): 9E44DF03-4754-4819-96D3-7D6AA4520272
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 167772126
Partitions will be aligned on 2048-sector boundaries
Total free space is 4029 sectors (2.0 MiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048         1050623   512.0 MiB   EF00  EFI System Partition
   2         1050624       164628479   78.0 GiB    8300  
   3       164628480       167770111   1.5 GiB     8300  

Delete partition #3 (/dev/vda3) by pressing the key d and then the number 3.

Command (? for help): d
Partition number (1-3): 3

Command (? for help): p
Disk /dev/vda: 167772160 sectors, 80.0 GiB
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): 9E44DF03-4754-4819-96D3-7D6AA4520272
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 167772126
Partitions will be aligned on 2048-sector boundaries
Total free space is 3145661 sectors (1.5 GiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048         1050623   512.0 MiB   EF00  EFI System Partition
   2         1050624       164628479   78.0 GiB    8300  

Then, delete partition #2 (/dev/vda2) as well by pressing the key d and then the number 2. Don't worry, until you permanently write the modifications, the data will not be lost.

Command (? for help): d
Partition number (1-2): 2
Command (? for help): p
Disk /dev/vda: 167772160 sectors, 80.0 GiB
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): 9E44DF03-4754-4819-96D3-7D6AA4520272
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 167772126
Partitions will be aligned on 2048-sector boundaries
Total free space is 166723517 sectors (79.5 GiB)
Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048         1050623   512.0 MiB   EF00  EFI System Partition

Now, create a new partition by pressing the key n. Choose the entire disc space by pressing the [Enter] key four times. Don't input any values; instead, let it use the defaults. Your data will remain intact with the new expanded size.

Command (? for help): n
Partition number (2-128, default 2): 
First sector (34-167772126, default = 1050624) or {+-}size{KMGTP}: 
Last sector (1050624-167772126, default = 167772126) or {+-}size{KMGTP}: 
Current type is 8300 (Linux filesystem)
Hex code or GUID (L to show codes, Enter = 8300): 
Changed type of partition to 'Linux filesystem'
Command (? for help): p
Disk /dev/vda: 167772160 sectors, 80.0 GiB
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): 9E44DF03-4754-4819-96D3-7D6AA4520272
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 167772126
Partitions will be aligned on 2048-sector boundaries
Total free space is 2014 sectors (1007.0 KiB)
Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048         1050623   512.0 MiB   EF00  EFI System Partition
   2         1050624       167772126   79.5 GiB    8300  Linux filesystem

Optionally, you may also assign a more descriptive name to your partition using the c command.

Command (? for help): c
Partition number (1-2): 2
Enter name: Fedora 35 Workstation
Command (? for help): p
Disk /dev/vda: 167772160 sectors, 80.0 GiB
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): 9E44DF03-4754-4819-96D3-7D6AA4520272
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 167772126
Partitions will be aligned on 2048-sector boundaries
Total free space is 2014 sectors (1007.0 KiB)
Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048         1050623   512.0 MiB   EF00  EFI System Partition
   2         1050624       167772126   79.5 GiB    8300  Fedora 35 Workstation

Finally, hit the w key to permanently save the table to disk and quit.

Command (? for help): w

Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING
PARTITIONS!!

Do you want to proceed? (Y/N): y
OK; writing new GUID partition table (GPT) to /dev/vda.
Warning: The kernel is still using the old partition table.
The new table will be used at the next reboot or after you
run partprobe(8) or kpartx(8)
The operation has completed successfully.

Because you are making modifications to the / file system, you must reboot the system for the changes to take effect.

[madhu@fedora ~]$ sudo reboot

After rebooting the system, open the gnome-terminal and type the following command to reclaim all free space into the BtrFS volume.

[madhu@fedora ~]$ sudo btrfs filesystem resize max /
Resize device id 1 (/dev/mapper/luks-62740414-8b79-4f91-b36f-1c3dd3511adf) from 78.00GiB to max

Now check the space used up by the BtrFS volume (after).

[madhu@fedora ~]$ sudo btrfs filesystem show /
Label: 'FEDORA'  uuid: bd69e80f-fd90-4b70-b13c-47ce9d253605
	Total devices 1 FS bytes used 3.11GiB
	devid    1 size 79.50GiB used 6.02GiB path /dev/mapper/luks-62740414-8b79-4f91-b36f-1c3dd3511adf
[madhu@fedora ~]$ lsblk -p
NAME                                                      MAJ:MIN RM  SIZE RO TYPE  MOUNTPOINTS
/dev/zram0                                                251:0    0  3.8G  0 disk  [SWAP]
/dev/vda                                                  252:0    0   80G  0 disk  
├─/dev/vda1                                               252:1    0  512M  0 part  /boot/efi
└─/dev/vda2                                               252:2    0 79.5G  0 part  
  └─/dev/mapper/luks-62740414-8b79-4f91-b36f-1c3dd3511adf 253:0    0 79.5G  0 crypt /var/log
                                                                                    /var/lib/libvirt/images
                                                                                    /home
                                                                                    /

5. Bypass the additional passphrase prompt

While GRUB asks for a passphrase to unlock the encrypted /boot files, this information is not passed on to initramfs. As a result, during the initramfs stage, the root (/) must be unlocked once again. This means that either the user must input his passphrase twice, or the initramfs image must include a file with the root partition's passphrase. So let's create a key file so that the passphrase is only asked once.

Create /etc/cryptsetup-keys.d directory if it is not already there.

[root@fedora ~]# mkdir /etc/cryptsetup-keys.d

Create a key file with 4KiB of random data. The key file has to be in volume.key format.

[root@fedora ~]# dd if=/dev/random \
  of=/etc/cryptsetup-keys.d/luks-$(cryptsetup luksUUID /dev/vda2).key \
  bs=512 count=8

Ensure that only the root user has read access to the key file.

[root@fedora ~]# chmod 0400 /etc/cryptsetup-keys.d/*.key

[root@fedora ~]# ls -lh /etc/cryptsetup-keys.d/
-r--------. 1 root root 4.0K Dec 26 11:41 luks-62740414-8b79-4f91-b36f-1c3dd3511adf.key

Attach the created key to the encrypted device's available key slot. When prompted for a passphrase, enter the normal LUKS passphrase.

[root@fedora ~]# cryptsetup luksAddKey /dev/vda2 \
  /etc/cryptsetup-keys.d/luks-62740414-8b79-4f91-b36f-1c3dd3511adf.key
Enter any existing passphrase: 

Include the key in the initramfs image as well. To do so, navigate to the /etc/dracut.conf.d/ directory and open (or create) the cryptodisk.conf file.

[root@fedora ~]# vim /etc/dracut.conf.d/cryptodisk.conf

And add the following line.

install_items+=" /etc/cryptsetup-keys.d/* "

Finally, rebuild the initramfs image.

[root@fedora ~]# dracut -vf

Reboot the computer and verify that everything is operating correctly. The LUKS passphrase should only be asked once.

6. Install and configure snapper for the ‘root file system’

Install snapper and the optional package python3-dnf-plugin-snapper. The python3-dnf-plugin-snapper package allows you to generate pre and post snapshots every time you install a package on the system with the dnf command-line tool.

[madhu@fedora ~]$ sudo dnf install snapper python3-dnf-plugin-snapper

Now, create a new snapper configuration named root for the Btrfs subvolume at /.

[madhu@fedora ~]$ sudo snapper -c root create-config /

This will create a configuration file at /etc/snapper/configs/root, a new subvolume .snapshots, and the directory /.snapshots.

[madhu@fedora ~]$ ls -l /etc/snapper/configs/
-rw-r-----. 1 root root 1169 Dec 26 12:04 root

[madhu@fedora ~]$ sudo btrfs subvolume list /
ID 256 gen 184 top level 5 path root
ID 257 gen 178 top level 5 path home
ID 258 gen 184 top level 5 path log
ID 259 gen 38  top level 5 path images
ID 264 gen 178 top level 256 path var/lib/machines
ID 267 gen 184 top level 256 path .snapshots

[madhu@fedora ~]$ ls -ld /.snapshots
drwxr-x---. 1 root root 0 Dec 26 12:04 /.snapshots

However, as seen in the above listing, the subvolume .snapshots is not a top-level subvolume (ID=5). To successfully roll back the 'root file system,' the .snapshots subvolume must be changed to a top-level subvolume.

So first delete the existing .snapshots subvolume.

[madhu@fedora ~]$ sudo btrfs subvolume delete /.snapshots
Delete subvolume (no-commit): '//.snapshots'

Then recreate a new snapshots subvolume with an ID of 5 (top-level). I’ve named the subvolume snapshots rather than .snapshots, but that’s fine; you may call it whatever you like. The directory in which the snapshots are saved, however, must be kept .snapshots only.

[madhu@fedora ~]$ sudo mkdir /.snapshots

[madhu@fedora ~]$ sudo mount -o subvolid=5 \
  /dev/mapper/luks-62740414-8b79-4f91-b36f-1c3dd3511adf \
  /mnt

[madhu@fedora ~]$ sudo btrfs subvolume create /mnt/snapshots

[madhu@fedora ~]$ ls /mnt
home  images  log  root  snapshots

[madhu@fedora ~]$ sudo umount /mnt

Now, connect the snapshots subvolume to the /.snapshots directory and then mount the snapshots subvolume. To do this, open the /etc/fstab file and append the orange-colored line. Replace UUID with your setup’s UUID.

[madhu@fedora ~]$ sudo vim /etc/fstab
UUID=bd69e80f-fd90-4b70-b13c-47ce9d253605 /                       btrfs   subvol=root,compress=zstd:1,x-systemd.device-timeout=0 0 0
UUID=44C8-163B                            /boot/efi               vfat    umask=0077,shortname=winnt 0 2
UUID=bd69e80f-fd90-4b70-b13c-47ce9d253605 /home                   btrfs   subvol=home,compress=zstd:1,x-systemd.device-timeout=0 0 0
UUID=bd69e80f-fd90-4b70-b13c-47ce9d253605 /var/lib/libvirt/images btrfs   subvol=images,compress=zstd:1,x-systemd.device-timeout=0 0 0
UUID=bd69e80f-fd90-4b70-b13c-47ce9d253605 /var/log                btrfs   subvol=log,compress=zstd:1,x-systemd.device-timeout=0 0 0
UUID=bd69e80f-fd90-4b70-b13c-47ce9d253605 /.snapshots             btrfs   subvol=snapshots,compress=zstd:1,x-systemd.device-timeout=0 0 0

Then mount the snapshots subvolume.

[madhu@fedora ~]$ sudo mount -a

Your setup must look something like this.

[madhu@fedora ~]$ lsblk -p /dev/vda2
NAME                                                    MAJ:MIN RM  SIZE RO TYPE  MOUNTPOINTS
/dev/vda2                                               252:2    0 79.5G  0 part  
└─/dev/mapper/luks-62740414-8b79-4f91-b36f-1c3dd3511adf 253:0    0 79.5G  0 crypt /.snapshots
                                                                                  /var/log
                                                                                  /var/lib/libvirt/images
                                                                                  /home
                                                                                  /

Now check to see if the snapshots subvolume has been elevated to a top-level (ID=5) subvolume.

[madhu@fedora ~]$ sudo btrfs subvolume list /
ID 256 gen 209 top level 5 path root
ID 257 gen 178 top level 5 path home
ID 258 gen 205 top level 5 path log
ID 259 gen 38  top level 5 path images
ID 264 gen 178 top level 256 path var/lib/machines
ID 268 gen 196 top level 5 path snapshots

You’re almost done; all that’s left is for the normal user to be able to list snapshots.

Open the /etc/snapper/configs/root file and add your user name.

[madhu@fedora ~]$ sudo vim /etc/snapper/configs/root
...
# users and groups allowed to work with config
ALLOW_USERS="madhu"
ALLOW_GROUPS=""
...

And change the group permission of the /.snapshots directory.

[madhu@fedora ~]$ sudo chown -R :madhu /.snapshots

The root subvolume’s snapper configuration is now complete. Now list the snapshots.

[madhu@fedora ~]$ snapper ls
 # | Type   | Pre # | Date | User | Cleanup | Description | Userdata
---+--------+-------+------+------+---------+-------------+---------
0  | single |       |      | root |         | current     |         

At this stage, you do not have any snapshots, we will create one later.

7. Test snapper rollback feature

To test if the rollback function works properly, I'll upgrade to the most latest kernel and then rollback to the current kernel.

Examine the current kernel and subvolumes information.

[root@fedora ~]# uname -r
5.14.10-300.fc35.x86_64

[root@fedora ~]# ls -lh /boot/vmlinuz-*
-rwxr-xr-x. 1 root root 11M Dec 25 17:52 /boot/vmlinuz-0-rescue-ae883586b86845dcb60a087f9dc8b808
-rwxr-xr-x. 1 root root 11M Oct  8 02:35 /boot/vmlinuz-5.14.10-300.fc35.x86_64

Now update to the latest kernel.

[root@fedora ~]# dnf update kernel
Last metadata expiration check: 0:15:26 ago on Sunday 26 December 2021 12:53:08 PM.
Dependencies resolved.
===============================================================================
 Package              Architecture Version                 Repository     Size
===============================================================================
Installing:
 kernel               x86_64       5.15.11-200.fc35        updates        15 k
 kernel-modules       x86_64       5.15.11-200.fc35        updates        33 M
Installing dependencies:
 kernel-core          x86_64       5.15.11-200.fc35        updates        35 M
Transaction Summary
===============================================================================
Install  3 Packages
Total download size: 67 M
Installed size: 108 M
Is this ok [Y/n]: 

After the kernel update is finished, reboot the computer.

GRUB menu after the kernel update.

Fedora LUKS With FDE And Snapper With Rollback After Kernel Update

Now examine the system with updated kernel and subvolumes information.

[root@fedora ~]# uname -r
5.15.11-200.fc35.x86_64

[root@fedora ~]# ls -lh /boot/vmlinuz-*
-rwxr-xr-x. 1 root root 11M Dec 25 17:52 /boot/vmlinuz-0-rescue-ae883586b86845dcb60a087f9dc8b808
-rwxr-xr-x. 1 root root 11M Oct  8 02:35 /boot/vmlinuz-5.14.10-300.fc35.x86_64
-rwxr-xr-x. 1 root root 11M Dec 22 21:28 /boot/vmlinuz-5.15.11-200.fc35.x86_64

[root@fedora ~]# snapper ls
 # | Type   | Pre # | Date                                | User | Cleanup | Description                | Userdata
---+--------+-------+-------------------------------------+------+---------+----------------------------+---------
0  | single |       |                                     | root |         | current                    |         
1  | pre    |       | Sunday 26 December 2021 01:09:45 PM | root | number  | /usr/bin/dnf update kernel |         
2  | post   |     1 | Sunday 26 December 2021 01:10:46 PM | root | number  | /usr/bin/dnf update kernel |         

[root@fedora ~]# btrfs subvolume list /
ID 256 gen 217 top level 5 path root
ID 257 gen 216 top level 5 path home
ID 258 gen 217 top level 5 path log
ID 259 gen 38 top level 5 path images
ID 264 gen 210 top level 256 path var/lib/machines
ID 268 gen 206 top level 5 path snapshots
ID 269 gen 201 top level 268 path snapshots/1/snapshot
ID 270 gen 204 top level 268 path snapshots/2/snapshot

[root@fedora ~]# btrfs subvolume get-default /
ID 256 gen 217 top level 5 path root

For the purposes of the experiment, I'll imagine I've messed up the current system and rendered it useless. So I'd like to go back to the last working snapshot, snapshot #1.

[root@fedora ~]# snapper --ambit classic rollback 1
Ambit is classic.
Creating read-only snapshot of current system. (Snapshot 3.)
Creating read-write snapshot of snapshot 1. (Snapshot 4.)
Setting default subvolume to snapshot 4.

Reboot the system.

[root@fedora ~]# reboot

GRUB menu after the rollback.

Fedora LUKS With FDE And Snapper With Rollback After Rollback

Finally, examine the kernel and subvolumes information after the rollback.

[root@fedora ~]# uname -r
5.14.10-300.fc35.x86_64
[root@fedora ~]# ls -lh /boot/vmlinuz-*
-rwxr-xr-x. 1 root root 11M Dec 25 17:52 /boot/vmlinuz-0-rescue-ae883586b86845dcb60a087f9dc8b808
-rwxr-xr-x. 1 root root 11M Oct  8 02:35 /boot/vmlinuz-5.14.10-300.fc35.x86_64
[root@fedora ~]# snapper ls
 # | Type   | Pre # | Date                                | User | Cleanup | Description                | Userdata     
---+--------+-------+-------------------------------------+------+---------+----------------------------+--------------
0  | single |       |                                     | root |         | current                    |              
1  | pre    |       | Sunday 26 December 2021 01:09:45 PM | root | number  | /usr/bin/dnf update kernel |              
2  | post   |     1 | Sunday 26 December 2021 01:10:46 PM | root | number  | /usr/bin/dnf update kernel |              
3  | single |       | Sunday 26 December 2021 01:23:33 PM | root | number  | rollback backup            | important=yes
4* | single |       | Sunday 26 December 2021 01:23:34 PM | root |         | writable copy of #1        |              
[root@fedora ~]# btrfs subvolume list /
ID 256 gen 227 top level 5 path root
ID 257 gen 229 top level 5 path home
ID 258 gen 235 top level 5 path log
ID 259 gen 38 top level 5 path images
ID 264 gen 210 top level 256 path root/var/lib/machines
ID 268 gen 224 top level 5 path snapshots
ID 269 gen 223 top level 268 path snapshots/1/snapshot
ID 270 gen 204 top level 268 path snapshots/2/snapshot
ID 271 gen 222 top level 268 path snapshots/3/snapshot
ID 272 gen 235 top level 268 path snapshots/4/snapshot
[root@fedora ~]# btrfs subvolume get-default /
ID 272 gen 235 top level 268 path snapshots/4/snapshot

As you can see, we successfully rolled back to the original kernel version.

8. Conclusion

With Fedora installed with LUKS full disk encryption and snapper with complete system rollback functionality, you can now be certain that your data at rest is secure, and if you make a mistake that renders your system useless, you can easily rollback to the prior functioning state without issue.

You can also better secure your machine from Evil Maid attacks by using UEFI Secure Boot custom key enrollment and a self-signed kernel and bootloader. In an upcoming blog, I will show you how to implement it.

If you want to learn more about snapper and how to improve its capabilities, see the Arch Wiki article on the subject.

Watch on YouTube