# LUKS on LVM (encrypted, cached, Desktop)

LUKS (Linux Unified Key Setup) is the standard for Linux hard disk encryption. By providing a standard on-disk-format, it does not only facilitate compatibility among distributions, but also provides secure management of multiple user passwords. LUKS stores all necessary setup information in the partition header, enabling to transport or migrate data seamlessly.

Management of LUKS encrypted devices is done via the [`cryptsetup`](https://gitlab.com/cryptsetup/cryptsetup) utility.

## Nomenclature

| Term                 | Description                                                                                                            |
|----------------------|------------------------------------------------------------------------------------------------------------------------|
| Physical Volume (PV) | On-disk partitioning format to be combined in a VG to a common storage pool                                            |
| Volume Group (VG)    | Grouping of one or more PVs to provide a combined storage pool from which storage can be requested in the form of LVs. |
| Logical Volume (LV)  | Logical partition format which can be accessed like a block device to hold file systems and data.                      |
| Cache device         | Fast storage used for caching reads/writes to slow storage                                                             |
| Origin device        | Slow primary storage holding the actual data                                                                           |

## Partitioning Setup

LUKS on LVM has the benefit of a LUKS container being able to span multiple disks, thanks to the machanisms of the underlying LVM. This, however, comes with the downside that if you want to have multiple volumes (e.g. for your root volume and a separate home volume or encrypted SWAP) you will have to take extra steps to unlock these volumes during the boot process.

<p class="callout info"><strong>NOTE:</strong> If you want to utilize LVM cache this is the desired partioning scheme to use, as the encrypted LUKS container will reside inside an LVM LV and the LVM caching mechanism will cache the LV instead of the unlocked LUKS container, thus not leaking any secrets into the cache.</p>

This guide assumes the following:

* This is used on a desktop computer without the need to resume (no SWAP partition)
* There are multiple drives: `/dev/nvme0n1` (SSD) and `/dev/sda` (HDD)
* The HDD will be cached by the SSD
* The root file system will be btrfs, with subvolumes for `/` and `/home`
* To tighten security, this setup assumes a [unified kernel image](https://wiki.archlinux.org/title/Systemd-boot#Preparing_a_unified_kernel_image) and booting via [EFISTUB](https://wiki.archlinux.org/title/EFISTUB), with the _ESP_ mounted at `/efi`. [Extra](/books/arch-linux/page/boot-loader) [steps](/books/arch-linux/page/secure-boot) will be necessary to make the machine bootable.

### Preparing partition layout

Start by listing available disks:

~~~bash
fdisk -l
~~~

Create a partition layout with `cfdisk` by pointing it to the first disk, e.g. `/dev/nvme0n1`:

<p class="callout warning"><strong>ATTENTION:</strong> <code>cfdisk</code> expects a device file, not a partition.</p>

~~~bash
cfdisk /dev/nvme0n1
~~~

If `cfdisk` asks you about the partition table scheme to use, select `gpt`.

Create the following partition layout:

| FS Type | Size        | Mount Point | Comment    |
|---------|-------------|-------------|------------|
| vfat    | 1G          | /efi        | EFI System |
| LVM     | (remaining) |             | Linux LVM  |

Start `cfdisk` for the second disk, e.g. `/dev/sda`:

~~~bash
cfdisk /dev/sda
~~~

Create the following partition layout:

| FS Type | Size  | Mount Point | Comment   |
|---------|-------|-------------|-----------|
| LVM     | (all) |             | Linux LVM |

### Setting up LVM

Start by creating LVM PVs on the partitions we just laid out:

~~~bash
pvcreate /dev/nvme0n1p2   # SSD
pvcreate /dev/sda1        # HDD
~~~

Next, create a VG spanning both PVs:

<p class="callout info"><strong>NOTE:</strong> <code>vg0</code> is used as an example here. Name your VG whatever you like.</p>

~~~bash
vgcreate vg0 /dev/nvme0n1p2 /dev/sda1
~~~

Create an LV inside `vg0`, using 100% of the available space on the PV at `/dev/sda1` and label it `lv_root`:

~~~bash
lvcreate -l 100%FREE -n lv_root vg0 /dev/sda1
~~~

Create an LV inside `vg0`, using 100% of the available space on the PV at `/dev/nvme0n1p2` and label it `lv_cache`:

~~~bash
lvcreate -l 100%FREE -n lv_cache --type cache-pool vg0 /dev/nvme0n1p2
~~~

Finally, link both LVs together so that the LV on the HDD is being cached by the pool on the SSD:

~~~bash
lvconvert --type cache --cachepool vg0/lv_cache vg0/lv_root
~~~

### Creating the LUKS container

Create the LUKS container inside the LV of the origin device:

<p class="callout danger"><strong>WARNING:</strong> Do <strong>NOT</strong> forget your passphrase! In case of loss you won't be able to access the data inside the container anymore!</p>

~~~bash
cryptsetup luksFormat /dev/mapper/vg0-lv_root
~~~

Open the newly created LUKS container and supply the passphrase you just set:

<p class="callout info"><strong>NOTE:</strong> <code>cryptroot</code> is used as an example here. Use whatever you like.</p>

~~~bash
cryptsetup open /dev/mapper/vg0-lv_root cryptroot
~~~

### Formatting and mounting partitions

Create file systems for the ESP and the root file system:

~~~bash
mkfs.fat -F 32 /dev/nvme0n1p1
mkfs.btrfs /dev/mapper/cryptroot
~~~

Mount the root btrfs file system and create the subvolumes:

~~~bash
mount /dev/mapper/cryptroot /mnt

btrfs subvolume create /mnt/@
btrfs subvolume create /mnt/@home
~~~

Unmount the root btrfs file system:

~~~bash
umount -R /mnt
~~~

Mount the `@` subvolume:

~~~bash
mount /dev/mapper/cryptroot -o noatime,compress-force=zstd,space_cache=v2,subvol=@ /mnt
~~~

Create mount points for `/efi` and `/home`:

~~~bash
mkdir -p /mnt/{efi,home}
~~~

Mount the remaining partitions and subvolumes:

~~~bash
mount /dev/nvme0n1p1 /mnt/efi
mount /dev/mapper/cryptroot -o noatime,compress-force=zstd,space_cache=v2,subvol=@home /mnt/home
~~~