Encryption of rootfs A&B partitions unattended using TPM

I’m evaluating Mender for encryption and security of applications from theft and exploitation. I understand the that the current Mender Secure boot, will not protect against attackers mounting a removable hard drive and mounting either partition to read/copy information of it.

Therefore I would like to encrypt the rootfs and data partitions with a Key stored on a TPM device after SB. An example is The ultimate guide to Full Disk Encryption with TPM and Secure Boot (with hibernation support!)

I am wondering if this has been successfully implemented and what modifications to the quoted recipe would be needed to handle atomic updates and provisioning. (presumably some new measurement to be conducted and updating of TPM during the update process)

I’m sure someone has looked at this as I see some Yocto metas for encrypting a data partition and a couple of questions on this site. Any suggestions on how to proceed?


Hi @petermilani80,

Indeed, this topic is being brought up now and then. Sadly, no blueprint for general usage has been shared so far. My personal opinion is that given the variety in hardware and first stage bootloaders this is only solved on a per case base at the moment. I’m happy to discuss this in more depth next week or so, I’m only semi-available at the moment - and be warned, the topic is quite complicated (read: more problems than solutions).


Thanks I am dealing with that. I found out that Ubuntu has some bugs that prevent the cryptsetup enrolled pcrs from unlocking a luks volume. A Solution was provided by one of the bug contributors here. I’ve tried it without success in that it still asks me for the password when booting, though it does work for others. I am still trying to clarify what isn’t working for me. The Arch Linux tutorials were informative and I did learn a lot from the tpm2-tools github but it seems these methods are lower level than cryptsetup . An interesting option is to use the pcrs to seal a secret in the tpm and using that secret to unlock the luks encryption, it is contained in the Software updates with authorized policies.

If I understand it, that is interesting as it allows a common secret across all devices and all updates to be available but hidden in the tpm. And it allows that to be established on provisioning and be available on the life of the device and independent of kernel updates.

That being said, there is still a question of what mender hooks and initrd are available to ensure that each luks partition is unencrypted. Alternatively working with the lowerlevel tpm2 policies, to just encrypt the sensitive parts of the system and mount them in after the rootfs has been loaded, though I think that is pretty insecure as someone with the hardware will still have access to the majority of the rootfs.

Would a workable solution be to setup an automatically decrypting LUKS volume based on the TPM pcrs 0,2,4,7 or at least 0 and 7. The A, B and data partitions are logical partitions of the Luks volume. Is it feasible that all the mender operations happen on logical partitions?

Hi @petermilani80,

Not sure what you mean by “logical partitions”. Mender needs to be able to communicate the root partition and a persistent data partition. What actual strings are in there should not matter that much, its just important that especially A and B can be switched through the bootloader (or what ever early mechanisms you can use).


By Logical Partitions, I meant logical volumes (LVM2) package. It basically what ubuntu uses when you install ubuntu with LUKS encrypted physical device. An example of doing this manually is here.

Now using the ubuntu installer, I can do this which might be good for a “Golden Device” however it will be tied to the “Golden device” hardware.

What I envisage is to keep the Golden Device unencrypted, and do the encryption during each provisioning, establishing a boot partition and then a luks partition which holds a rootfsA, rootfsB, data and swap partitions as logical partitions using LVM. Such that the lsblk might look something like this:

   |- sda1    \boot
   |- sda2
      |-  dms_crypt
           |- rootfsA
           |- rootfsB
           |- swap
           |- data

The provisioning step could be generation of this partition layout, but would require booting and setting the tpm pcrs using systemd-cryptenroll on each device once booted. In ubuntu with a luks encrypted LVM during the initrd the user is prompted for the decryption password. That allows you to decrypt the block device, boot, login and then the enroll the tpm pcrs for automatic decryption.

Some of my questions would be what would be the changes to mender-convert to create the provisioning image using LUKS and LVM? EDIT: somewhere near line 289 in mender-convert-package in mender-convert repo, I think you won’t just be able to use the img_path but mount it and setup the logical partitions in leiu of a lot of the stuff from 280 - 298, potentially the rest of it might still just work
Should any changes be required to the mender boot?

So I’ve scripted up a test to make the provisioning image encrypted and dd the rootfs, data parts into it. I’ll look to modify the mender-package script to include this and test with a live golden-image to see if it works.

This script is putting dummy rootfs.img and data.img images into a provisioning image named test.img which has an unencrypted boot part and an a luks encrypted LVM partition of the same size.

One question I have is whether it is necessary for execute lines 307-312 in mender-convert-package, not sure what it is doing there.



# Write file at offset of another file
# $1 - file to write
# $2 - destination file
# $3 - offset in number of 512 sectors
disk_write_at_offset() {
    run_and_log_cmd "dd if=${1} of=${2} seek=${3} conv=notrunc status=none"

# Align value (result is number of 512 sectors)
# $1 - value to align (number of 512 sectors)
# $2 - alignment in bytes
disk_align_sectors() {
    local size_in_bytes=$((${1} * 512))
    local reminder=$((${size_in_bytes} % ${2}))

    if [ $reminder -ne 0 ]; then
        size_in_bytes=$(($size_in_bytes - $reminder + ${2}))

    echo "$(($size_in_bytes / 512))"

# Convert MiB to number of 512 sectors
#  $1 - MiB value
disk_mb_to_sectors() {
    echo "$(((${1} * 1024 * 1024) / 512))"

# Convert 512 sectors to MiB
#  $1 - number of 512 sectors
disk_sectors_to_mb() {
    echo "$(((${1} * 512) / 1024 / 1024))"

img_sect=$(disk_mb_to_sectors $img_mb)

dd if=/dev/zero of=${img_path} bs=512 count=0 seek=${img_sect} 

dd if=/dev/zero of=rootfs.img bs=512 count=0 seek=$(disk_mb_to_sectors ${rootfs_size_mb} )
dd if=/dev/zero of=data.img bs=512 count=0 seek=$(disk_mb_to_sectors ${rootfs_size_mb} )

mkfs.ext4 rootfs.img
mkfs.ext4 data.img

rootfs_lo=$(sudo losetup --find rootfs.img)
data_lo=$(sudo losetup --find data.img)

mkdir -p mnt/rootfs
sudo mount ${rootfs_lo} mnt/rootfs
sudo touch  mnt/rootfs/message.txt
sudo umount ${rootfs_lo}
sudo losetup -d ${rootfs_lo}

sudo mount ${data_lo} mnt/rootfs
sudo touch  mnt/rootfs/data.txt
sudo umount ${data_lo}
sudo losetup -d ${data_lo}

echo "Setting partiion variables"
boot_part_sectors=$(disk_mb_to_sectors ${boot_size_mb})
rootfs_part_sectors=$(disk_mb_to_sectors ${rootfs_size_mb})
data_part_sectors=$(disk_mb_to_sectors ${data_size_mb})

boot_part_end=$((${boot_part_start} + ${boot_part_sectors} - 1))

rootfsa_start=$(disk_align_sectors ${boot_part_end} ${MENDER_PARTITION_ALIGNMENT})
rootfsa_end=$((${rootfsa_start} + ${rootfs_part_sectors} - 1))

rootfsb_start=$(disk_align_sectors ${rootfsa_end} ${MENDER_PARTITION_ALIGNMENT})
rootfsb_end=$((${rootfsb_start} + ${rootfs_part_sectors} - 1))

data_start=$(disk_align_sectors ${rootfsb_end} ${MENDER_PARTITION_ALIGNMENT})
data_end=$((${data_start} + ${data_part_sectors} - 1))

echo "Partition Variables Set"

#Create a partition table with two primary partiions a boot and encrypted
#LUKS partition which is the size of the rootfs and data.
echo "Create Partition table"
parted -s ${img_path} mklabel gpt
parted -s ${img_path} unit s mkpart ESP fat32 ${boot_part_start} ${boot_part_end}
parted -s ${img_path} -- unit s mkpart primary ext4 ${rootfsa_start} ${data_end} 
parted -s ${img_path} print

echo "Parttion Table created"

loop_device=$(sudo losetup --offset $(( ${rootfsa_start} * 512 )) --sizelimit $((( 2 * ${rootfs_part_sectors} + ${data_part_sectors} * 2 ) * 512 )) --show --find ${img_path})

echo "Loop Device used ${loop_device}, offset ${rootfsa_start} sizelimit $((2 * ${rootfs_part_sectors} + ${data_part_sectors} * 2))"

sudo cryptsetup luksFormat --hash=sha512 --key-size=512 --cipher=aes-xts-plain64 --verify-passphrase ${loop_device}

echo "Openning LUKS "
sudo cryptsetup luksOpen ${loop_device} mender_crypt

echo "Creating Physical volume"
sudo pvcreate /dev/mapper/mender_crypt
#sudo pvs

echo "Creating a logical volume Group"
sudo vgcreate ${volume_group_name} /dev/mapper/mender_crypt
# sudo vgs

echo "Creating logical volumes"
sudo lvcreate -n ${rootfsa_name} -L "${rootfs_size_mb}M" ${volume_group_name}
sudo lvcreate -n ${rootfsb_name} -L "${rootfs_size_mb}M" ${volume_group_name}
sudo lvcreate -n ${data_name} -L "${data_size_mb}M" ${volume_group_name}
#sudo lvs

#write partitions 

#use dd here with output filename 
sudo dd if=rootfs.img of="${rootfsa_filepath}"
sudo dd if=rootfs.img of="${rootfsb_filepath}"
sudo dd if=data.img of="${data_filepath}"

#To Close the cryptvolume
sudo vgchange -a n ${volume_group_name}
sudo cryptsetup luksClose mender_crypt
sudo losetup -d ${loop_device}

I’m expecting when I do this with a real golden-image that I should get a prompt when booting for the luks passphrase.

1 Like

Modifies Mender-convert script to produce an encrypted image as above. still some issues around booting. PR is here Add luks encryption by PeterQFR · Pull Request #564 · mendersoftware/mender-convert · GitHub

1 Like