dracut icon indicating copy to clipboard operation
dracut copied to clipboard

[crypt] Detached LUKS header causes erroneous `/etc/block_uuid.map`

Open atweiden opened this issue 3 years ago • 17 comments

Describe the bug

Any LUKS encrypted device with detached header causes blkid $_dev -s UUID -o value to return nil here in modules.d/90crypt/module-setup.sh.

This results in an erroneous /etc/block_uuid.map being written to the initramfs, which greatly inhibits successful bootup of systems having detached headers on the LUKS root volume. A Dracut rescue shell is spawned.

Distribution used

Void Linux

Dracut version

053

Init system

runit

To Reproduce

In Void Linux live environment:

git clone https://github.com/atweiden/voidvault
cd voidvault
export RAKULIB="$(realpath lib)"
export RAKUDO_HOME=/usr/lib/raku
export PATH="$(realpath bin):$PATH"
# bootstrap void
voidvault new 1fa

Reboot after running script to completion.

Expected behavior

This issue calls into question the validity of /etc/block_uuid.map as an approach. While /etc/block_uuid.map could be made to include something here, rather than useless trailing whitespace, what would work? No UUID is possible to obtain.

Additional context

Unrelated to https://github.com/dracutdevs/dracut/issues/1566 — omitting rd.luks.name doesn’t rectify bootup errors.

atweiden avatar Apr 08 '22 05:04 atweiden

Is there any solution how to use LUKS detached header for root using dracut?

reagentoo avatar May 21 '22 09:05 reagentoo

@atweiden Your problem is using UUID instead of PARTUUID. With a detached header, you should be using the partition's UUID since there can be no filesystem UUID.

@reagentoo As of 5 minutes ago, there is now :) I just submitted a pull request, but until that processes through the system, see https://github.com/SentToDevNull/dracut for the dracut you should use. I store my detached header (e.g. "definitely_not_a_luks_header.img") on a separate disk (e.g. /dev/vda1) and use the following dracut cmdline options in my GRUB config:

menuentry "Gentoo" {
  linux /vmlinuz-superkernel \
    rd.luks.partuuid=3c9d04af-02 \
    rd.luks.header=definitely_not_a_luks_header.img \
    rd.luks.header.disk=/dev/vda1 \
    root=/dev/mapper/luks-3c9d04af-02 \
    rw \
    quiet
  initrd /initramfs-superkernel.img
}

Reminder: use PARTUUID with detached headers because they don't have a UUID. In the above, the PARTUUID of my LUKS partition with the detached header was "3c9d04af-02".

When they accept my pull request, this issue will be resolved.

SentToDevNull avatar Jun 07 '22 12:06 SentToDevNull

Your problem is using UUID instead of PARTUUID. With a detached header, you should be using the partition's UUID since there can be no filesystem UUID

The headerless volume won’t have a PARTUUID if it exists on an unpartitioned device.

Upon reviewing #1838, it doesn’t seem to hinge on having a PARTUUID.

#1838 may provide a workaround, though. If it works for headerless LUKS volumes on partitioned devices, I don’t see why it wouldn’t also work for the same on unpartitioned devices.

When they accept my pull request, this issue will be resolved.

Considering #1838 doesn’t fix the problem with /etc/block_uuid, no, it won’t.

atweiden avatar Jun 19 '22 22:06 atweiden

@atweiden

Have you tried to include /etc/crypttab into the initramfs ?

I understand there are clearly bugs (e.g. invalid /etc/block_uuid.map), but my first approach would be to find at least one way to make this scenario work first.

You might know more about this topic than I do, but I found this post that might be helpful - https://linuxconfig.org/how-to-use-luks-with-a-detached-header

LaszloGombos avatar Dec 04 '22 00:12 LaszloGombos

Have you tried to include /etc/crypttab into the initramfs ?

Yes

atweiden avatar Dec 04 '22 05:12 atweiden

@atweiden Thanks. What happens if you delete the incorrect /etc/block_uuid.map from initramfs, and supply a correct /etc/crypttab ?

LaszloGombos avatar Dec 04 '22 12:12 LaszloGombos

Removing block_uuid.map (altering modules.d/90crypt/*.sh to remove references to block_uuid.map, accordingly) doesn’t make a difference.

atweiden avatar Dec 05 '22 23:12 atweiden

Removing block_uuid.map (altering modules.d/90crypt/*.sh to remove references to block_uuid.map, accordingly) doesn’t make a difference.

@atweiden Thanks. I would have just post-process the initramfs (uncompress, remove file, decompress), but probably would have lead to the same result then (dracut also has a --keep option that lets use skip the uncompress part).

So this seems to indicate that fixing the issue regarding block_uuid.map would not actually make this particular scenario work. Perhaps it is better to focus on the validity of the /etc/crypttab file.

Can you please share your /etc/crypttab file ?

LaszloGombos avatar Dec 06 '22 02:12 LaszloGombos

So this seems to indicate that fixing the issue regarding block_uuid.map would not actually make this particular scenario work. Perhaps it is better to focus on the validity of the /etc/crypttab file.

That’d be a happy outcome.

My /etc/crypttab looks like this (in voidvault 1FA/2FA mode):

vault   PARTUUID=88064a44-6e50-4771-8ee4-2a2cf56e3748   /boot/keys/root.key   luks,force,header=/boot/headers/root.header
bootvault   UUID=7e6b1353-eb9c-4d7e-9247-2a35817a29c3   /root/keys/boot.key   luks

The idea is, roughly:

  1. Enter GRUB password (GRUB_ENABLE_CRYTPODISK=y) to unlock LUKS-encrypted boot volume, facilitating initramfs access
  2. Line 1 of /etc/crypttab unlocks LUKS-encrypted root volume using detached header (stored in /boot/headers/root.header, and included in initramfs)
  3. Line 2 of /etc/crypttab unlocks LUKS-encrypted boot volume using key stored in /root/keys/boot.key

atweiden avatar Dec 06 '22 04:12 atweiden

@atweiden Can you help and try to come up with the "minimal" setup to reproduce the problem ?

I would just have one line in /etc/crypttab with detach headers and make it work and once that is working add back more complexity.

In the article, there seems to be 3 scenarios that worked (assuming the article is correct)

1. sdb_crypt /dev/sdb none luks,header=/dev/sdc
2. sdb_crypt /dev/sdb none luks,header=/path/to/header.img:/dev/sdc1
3. sdb_crypt /dev/sdb none luks,header=/path/to/header.img:UUID=<filesystem-uuid>

The examples all use none instead of a key ? Perhaps you can try that and perhaps thats more important than the detached header ?

LaszloGombos avatar Dec 07 '22 02:12 LaszloGombos

I would just have one line in /etc/crypttab with detach headers and make it work

The root volume with detached headers already gets unlocked properly — that part works.

It’s the boot volume (see: “bootvault”, /etc/crypttab line 2) which doesn’t get unlocked.

Can you help and try to come up with the "minimal" setup to reproduce the problem ?

Void + Runit via Voidvault is already rather minimal. I suppose I could rewrite the whole thing in Bash, but it’d be easier to switch initramfs generators, frankly.

It’s been a few months since I last went spelunking through dracut’s source code, but I still find the following two factors a bit curious:

  1. dracut’s /etc/block_uuid.map writer implementation returns nil instead of the root volume UUID whenever the root volume is without headers — the root volume may only have a PARTUUID or serial ID in these cases

  2. When the root volume’s headers are detached, the dracut-related kernel command line (/etc/default/grub in Void) contains rd.luks.partuuid or rd.luks.serial (as opposed to rd.luks.uuid, as is commonplace)

Might this issue be caused by the dracut crypt module’s /etc/block_uuid.map handler inadvertently missing a matching PARTUUID or serial ID in /etc/block_uuid.map? I hypothesize the issue is /etc/block_uuid.map is fundamentally inadequate for handling LUKS volumes with detached headers.

There exists little code examples (or even, aside from a few obscure mailing list discussions, any open discussions) pertaining to detaching headers from the root LUKS volume, as Voidvault achieves. If you have the spare time and inclination, I’d encourage you to bootstrap Voidvault in a VirtualBox VM — the repo contains instructions. Just be sure to run voidvault new 1fa instead of voidvault new to bootstrap the root volume with detached headers.

atweiden avatar Dec 08 '22 07:12 atweiden

There is always a luks UUID even for the drive with no partitions nor the header which is detached. You can take the luks UUID from the detached header stored separately somewhere else by using the command like this cryptsetup luksUUID /boot/header.img or cryptsetup luksDump /boot/header.img and it's a valid UUID for luks.uuid/rd.luks.uuid as the kernel parameter, but you need to specify luks.data too. I set the kernel parameters in /etc/default/grub: GRUB_CMDLINE_LINUX="luks.uuid=51d4d661-9fc4-42ee-8960-424c9f647d35 luks.data=51d4d661-9fc4-42ee-8960-424c9f647d35=/dev/sdb luks.options=51d4d661-9fc4-42ee-8960-424c9f647d35=header=/header.img:UUID=0663-66C7 luks.key=51d4d661-9fc4-42ee-8960-424c9f647d35=/luks-key:UUID=0663-66C7" where 51d4d661-9fc4-42ee-8960-424c9f647d35 is the UUID taken from my header.img which is on a separate flash drive with UUID=0663-66C7 and /dev/sdb is the actual data disk with no partitions. Probably the same principle can be applied to crypttab.

wowpetr avatar Dec 28 '22 07:12 wowpetr

@wowpetr In my case, the headerless (root) LUKS volume is successfully unlocked on startup. It’s the boot volume, a normal LUKS volume, which isn’t unlocked on startup.

atweiden avatar Dec 28 '22 19:12 atweiden

Restated, atweiden/voidvault stores the root LUKS volume detached header in a second, separately LUKS-encrypted “boot” volume. It is this boot volume which is not unlocked. The root volume with detached header is successfully unlocked — my dracut settings appear to be working there.

atweiden avatar Dec 28 '22 19:12 atweiden

@wowpetr, It just dawned on me your idea may be to patch in support for luksUUID in /etc/block_uuid.map. This way, there would at least be something there rather than extraneous whitespace caused by blkid $_dev -s UUID -o value returning nil. This may be a useful direction to go in considering the issue I’m having in Voidvault, where the root headerless volume on an unpartitioned device is successfully unlocked yet the normal LUKS boot volume isn’t — I’m assuming due to the missing element in /etc/block_uuid.map.

I have nothing further to add at the moment. I just didn’t want this thought to fade into the ether, as AFAIK no progress has been made here in several months.

atweiden avatar Apr 14 '23 04:04 atweiden

Here's the workaround I used:

# /usr/lib/dracut/modules.d/90crypt/cryptroot-ask.sh:137

if luksheaderlabel=$(getargs "rd.luks.header.label"); then
    printf "Please plug in the security key.\n"

    while ! test -e /dev/disk/by-label/$luksheaderlabel; do
        sleep 1;
    done

    mkdir -p /tmp/cryptroot-header
    mount /dev/disk/by-label/$luksheaderlabel /tmp/cryptroot-header -o ro
    luksheaderfile=$(getargs "rd.luks.header.file")
    cryptsetupopts="$cryptsetupopts --header=/tmp/cryptroot-header/$luksheaderfile"
fi
unset luksheaderfile

mahalay avatar Jul 23 '23 14:07 mahalay