Disk Encryption Support¶
LmP currently supports encrypting the disk used by the root file system on first boot, as a way to guarantee a unique LUKS2 master key per device.
The effort for creating an encrypted root file system during image creation in CI (and required logic for online re-encryption during first boot) can be tracked at https://github.com/foundriesio/meta-lmp/pull/868.
Prerequisites¶
As the process for decrypting the disk needs to be unattended, LmP requires either PKCS#11 (e.g. OP-TEE with RPMB as secure storage) or TPM 2.0 to be available by the target hardware, as they can be leveraged for securely storing the Key Encryption Key (KEK) used for later decrypting the disk during the boot process (via LUKS2 tokens, leveraging systemd-pkcs11 or systemd-tpm2, see systemd-cryptenroll for more information).
For better security, TPM 2.0 support also requires UEFI secure boot to be enabled, as the key is bound to the Platform Configuration Register (PCR) 7, which tracks the secure boot state of the machine.
Enabling Support for Disk Encryption¶
The following options require customizations for disk encryption support:
For adding the required initramfs-module-cryptfs
module to the initramfs
(based on what gets provided by MACHINE_FEATURES
, like tpm2
or optee
):
DISTRO_FEATURES:append = " luks"
For splitting the /boot
content from the ostree deployment in a separated
partition (where kernel/initramfs gets stored, unencrypted). This option is
enabled by default on systems booting with UEFI support.
OSTREE_SPLIT_BOOT = "1"
For supporting copying content from /usr/lib/ostree-boot
(used for
boot firmware updates) into /boot
as part of the ostree deployment step (OTA).
This is required for supporting boot firmware updates on devices with encrypted
root file systems.
OSTREE_DEPLOY_USR_OSTREE_BOOT = "1"
For supporting /boot
in a separated partition at the final image the selected
WKS_FILE
needs to support split boot. UEFI based devices already have such
setup by default, but on most ARM/ARM64 devices a custom WKS might be
required. As an example, iMX8-based devices should use
sdimage-imx8-spl-split-boot-sota.wks.in
instead of the default
sdimage-imx8-spl-sota.wks.ini
file:
WKS_FILE:sota:mx8mm-nxp-bsp = "sdimage-imx8-spl-split-boot-sota.wks.in"
Note
Besides a custom WKS_FILE
for split boot support, make sure to also update
the target fstab to automatically mount /boot
(from the first partition)
at the root file system /boot
folder.
This is not required with UEFI-based systems as systemd is capable of
automatically identifying and mounting the ESP partition during boot.
Implementation Details for OP-TEE PKCS#11 Support¶
A dedicated slot is required to avoid conflicts with the PKCS#11 token slot
normally used by aktualizr-lite
. This dedicated slot is currently hardcoded
to slot 1, with the label lmp
.
During the encryption process the token slot is initialized and a RSA 2048 key is generated, which is later used by systemd-cryptenroll.
Make sure to not erase the token slot or the key during the lifetime of the image, otherwise the system will fail to boot (a recovery key can be created and provided manually if required, but it won’t be an unattended boot).
Testing TPM 2.0 Support With Qemu (x86) and swtpm¶
It is possible to test the disk encryption support with TPM 2.0 with Qemu and swtpm.
Make sure LUKS support is enabled for your x86 target:
$ cat meta-subscriber-overrides/conf/machine/include/lmp-factory-custom.inc
DISTRO_FEATURES:intel-corei7-64:append = " luks"
Then make sure to enroll the UEFI Secure Boot Certificates to enable secure boot support. This is required as the LUKS2 TPM 2.0 token leverages PCR 7, which tracks the secure boot state.
Now install swtpm
in the host machine (if not already installed), and start the swtpm
daemon, which will be later consumed by Qemu and act as the hardware TPM.
$ mkdir -p /tmp/mytpm
$ while true; do swtpm socket --tpmstate dir=/tmp/mytpm --ctrl type=unixio,path=/tmp/mytpm/swtpm-sock --tpm2; done;
Run Qemu with the required extra TPM 2.0 related commands:
$ qemu-system-x86_64 -device virtio-net-pci,netdev=net0,mac=52:54:00:12:35:02 \
-netdev user,id=net0,hostfwd=tcp::2222-:22 \
-object rng-random,filename=/dev/urandom,id=rng0 -device virtio-rng-pci,rng=rng0 \
-drive if=none,id=hd,file=lmp-factory-image-intel-corei7-64.wic,format=raw \
-device virtio-scsi-pci,id=scsi -device scsi-hd,drive=hd \
-drive if=pflash,format=qcow2,file=ovmf.secboot.qcow2 -no-reboot \
-nographic -m 1024 -serial mon:stdio -serial null -cpu host -enable-kvm \
-chardev socket,id=chrtpm,path=/tmp/mytpm/swtpm-sock \
-tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis,tpmdev=tpm0
Now during boot you should see the following during the first boot:
...
Starting version 250.5+
/dev/sda2 not yet encrypted, encrypting with LUKS2
[ 0.699667] e2fsck: otaroot: clean, 15983/934032 files, 447887/933901 blocks
resize2fs 1.46.5 (30-Dec-2021)
Resizing the filesystem on /dev/sda2 to 925709 (4k) blocks.
The filesystem on /dev/sda2 is now 925709 (4k) blocks long.
Key slot 0 created.
Finished, time 00:15.011, 3632 MiB written, speed 240.9 MiB/s
Command successful.
Enrolling LUKS2 keyslot based on tpm2 token
New TPM2 token enrolled as key slot 1.
Wiped slot 0.
[ 44.126792] e2fsck: otaroot: clean, 15983/934032 files, 447887/925709 blocks
...
Verify that LUKS2 is using the TPM 2.0 based systemd token for encryption:
root@intel-corei7-64-unknown:~# cryptsetup luksDump /dev/sda2
LUKS header information
Version: 2
Epoch: 463
Metadata area: 16384 [bytes]
Keyslots area: 16744448 [bytes]
UUID: af0d8a12-5c60-48d1-9f03-a6165906df30
Label: otaroot
Subsystem: (no subsystem)
Flags: (no flags)
Data segments:
0: crypt
offset: 16777216 [bytes]
length: (whole device)
cipher: aes-xts-plain64
sector: 512 [bytes]
Keyslots:
1: luks2
Key: 512 bits
Priority: normal
Cipher: aes-xts-plain64
Cipher key: 512 bits
PBKDF: pbkdf2
Hash: sha512
Iterations: 1000
Salt: d1 2f 37 48 98 37 32 5a f8 3a 45 29 dd 04 03 43
89 d2 ae ed 8e d9 56 2f c1 d0 60 31 12 8e 1d 46
AF stripes: 4000
AF hash: sha512
Area offset:290816 [bytes]
Area length:258048 [bytes]
Digest ID: 0
Tokens:
0: systemd-tpm2
tpm2-pcrs: 7
tpm2-bank: sha256
tpm2-primary-alg: ecc
tpm2-blob: 00 9e 00 20 7f 2c f2 d0 ec 9b 17 a3 7e 48 90 bf
74 1f 43 92 2e d3 45 6d b4 1d 06 6a b8 4c 65 3f
54 64 b6 75 00 10 09 ee 39 3c ce 2a 6f cc b1 1e
f9 e7 50 e2 1b ce 6c 6d 26 1e 2a 39 24 01 e8 39
7b 44 90 62 a2 b9 6b 81 7a 43 9e 76 93 0c 39 d6
76 47 85 67 d8 bc 07 4c 68 b1 43 b8 25 58 ed 97
c7 0f 00 a7 33 43 2d b2 8b e1 94 da ac 80 19 03
1e 06 be 03 7a d5 28 a6 26 cf b5 db f9 63 ee 2a
bb 40 9f b0 b6 08 64 6b 3a 5f b1 31 c0 e9 62 12
17 fc e8 b6 48 94 d0 80 9e f1 5f d3 9a 85 14 0f
00 4e 00 08 00 0b 00 00 00 12 00 20 86 0e d1 f6
e3 49 84 56 16 f1 4e cb cd 56 76 b6 97 0e d2 48
4b 96 c9 af ee 27 a4 f2 de ce 48 84 00 10 00 20
34 85 f5 a4 b1 a4 ca 83 c7 ff ab aa 55 46 a7 4d
89 8b 55 4a 82 36 4a 1d 77 36 3e b7 50 8c 81 4f
tpm2-policy-hash:
86 0e d1 f6 e3 49 84 56 16 f1 4e cb cd 56 76 b6
97 0e d2 48 4b 96 c9 af ee 27 a4 f2 de ce 48 84
Keyslot: 1
Digests:
0: pbkdf2
Hash: sha256
Iterations: 312076
Salt: 6c 91 b1 65 23 2f 70 0d 36 ba 42 cc 3e 97 33 e1
73 48 b4 84 d7 32 7d 1b 81 a5 ed fd 7c 5e 06 4c
Digest: 5c 30 5b f3 59 db fe 6a 71 c4 9a a0 2d 22 cf 6b
18 e7 cc 8d 6a 44 c9 67 97 f8 34 80 96 69 53 7b