Skip to content

Encrypted images

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 use_luks, like make ... 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 password. However, 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 filesystem 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.

Note:
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 –tpm2` switch.

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.


© Red Hat