For privacy reasons you sometimes want to use a disk encryption for the root filesystem. The images support this by using LUKS2 encryption.
To enable encryption, define the variable
... DEFINES="use_luks=true", or add
use_luks: true to the mpp-vars
section in your image.
When encryption is enabled, the root filesystem partition will be made a dm-crypt partition, using LVM2, and the right dependencies is added to the system to allow this to be unlocked.
The passphrase used for the initial encryption is taken from the
luks_passphrase variable, which defaults to
once the systems is booted, other passphrases can be added and
this one removed as required.
Automatic unlocking with initial passphrase¶
By default, the passphrase must be entered interactively from the
initrd during boot, which is not always possible. So there is support
for automatic unlocking. Enable this by setting the variable
luks_auto_unlock to true.
If enabled, the initial passphrase (whatever was in the
luks_passphrase variable) will be copied into the root filesystem as
/usr/.auto-unlock-key, and then propagated into the initrd. During
boot, this key will be automatically used to unlock the root partition.
It should be noted that this setup on its own is not secure, as the password is visible in plaintext in the initrd which is not encrypted. This feature is meant as an initial step to have an encrypted filesystem that can boot non-interactively from which point you can later add a new secure passphrase.
Hardware TPM based unlocking¶
One option when it comes to unlocking root filesytem is to use the hardware in modern CPUs called the TPM. This allows using tokens specific to the individual hardware instance to unlock the device.
The idea here is that we build an image that is encrypted with a throwaway passphrase, and on the first boot we unlock it using that. Then we run some code that enrolls a hardware-specific key and removes the throwaways passphrase. Then on further boots, the TPM automatically unlocks the filesystem with a key that nobody knows (or can know).
This can be achived using the clevis-luks support in cs9.
osbuild requires additional packages to support org.osbuild.luks2 and org.osbuild.lvm2
sudo dnf install osbuild-luks2 osbuild-lvm2
To demonstrate how this works, there is an example image called
encrypted.mpp.yml'. In order to try this you need a hardware TPM,
which is possible with qemu, which can be enabled with therunvm
To test this, try:
$ make cs9-qemu-encrypted-regular.x86_64.qcow2 $ ./runvm --tpm2 cs9-qemu-encrypted-regular.x86_64.qcow2
This will boot the VM first, auto-unlock using the initial passphrase, and then replace the key with a TPM based one. Once booted you can verify this:
# cryptsetup luksDump /dev/vda3 LUKS header information Version: 2 Epoch: 6 Metadata area: 16384 [bytes] Keyslots area: 16744448 [bytes] UUID: aedd1eef-f24e-425e-a9f3-bb5a1c996a95 Label: luks-rootfs 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: sha256 Iterations: 1000 Salt: 16 1a 97 25 40 5c 13 df 98 03 e4 a9 7f c4 f1 d8 51 44 09 15 ec 5d 8a 8b ab 85 c3 4b a1 d1 29 cc AF stripes: 4000 AF hash: sha256 Area offset:290816 [bytes] Area length:258048 [bytes] Digest ID: 0 Tokens: 0: clevis Keyslot: 1 Digests: 0: pbkdf2 Hash: sha256 Iterations: 1000 Salt: 22 2f cc 82 7c 18 d6 27 59 c3 af 70 b0 9a 7e d0 c9 e2 f3 f8 a0 cc a5 a5 38 5e ed 27 3a 7e 07 99 Digest: cd b3 89 8b 33 d9 7b db 9f 73 36 bb 19 28 cc 1b a4 f1 45 8f c7 7b 19 80 a5 64 62 eb 12 b9 c8 cc
Which is different from what you would see normally:
# cryptsetup luksDump /dev/vda3 LUKS header information Version: 2 Epoch: 3 Metadata area: 16384 [bytes] Keyslots area: 16744448 [bytes] UUID: aedd1eef-f24e-425e-a9f3-bb5a1c996a95 Label: luks-rootfs Subsystem: (no subsystem) Flags: (no flags) Data segments: 0: crypt offset: 16777216 [bytes] length: (whole device) cipher: aes-xts-plain64 sector: 512 [bytes] Keyslots: 0: luks2 Key: 512 bits Priority: normal Cipher: aes-xts-plain64 Cipher key: 512 bits PBKDF: argon2i Time cost: 4 Memory: 32 Threads: 1 Salt: 31 e4 a1 3b df 08 aa d9 71 1c be 26 7c 45 15 0b 22 69 51 35 a4 58 04 09 2b 96 a8 91 c1 3d c6 4e AF stripes: 4000 AF hash: sha256 Area offset:32768 [bytes] Area length:258048 [bytes] Digest ID: 0 Tokens: Digests: 0: pbkdf2 Hash: sha256 Iterations: 1000 Salt: f7 09 53 c5 8b 7e e6 59 31 ec d7 4e bd ce 81 c1 f6 42 7b ba e4 dc 1e 16 64 e8 67 69 91 a0 9a 81 Digest: bc 5e f2 21 41 6a 7c 10 d6 2f b1 2e 28 01 e0 e9 67 85 53 00 43 49 03 69 a7 39 83 2c a5 55 51 2d
Note how the earlier example uses keyslot 1 instead of keyslot 0 and how there is a token for keyslot 1, and how there is no keyslot 0 anymore.
If at this point you reboot the vm, it will unlock the root filesystem using this token.