Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support multiple encrypted devices #54

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

sbrudenell
Copy link

@sbrudenell sbrudenell commented Jun 20, 2019

I use btrfs on multiple LUKS-encrypted disks. In order to support single password entry, I have a keyfile that is a LUKS-encrypted image that, once decrypted, also decrypts the other volumes.

I made some changes to init to support this. I feel they're in line with the current design and don't interfere with other use cases. Let me know if I should make any changes to support this goal.

  1. cryptkey=... has special behavior if the key matches *.img: We'll treat it as a LUKS-encrypted file (with embedded header), and try to unlock it. We use the unlocked key as a later cryptkey argument. Note: I couldn't figure out a way to get nlplug-findfs to do this with a single invocation, so I invoke cryptsetup directly, so it needs to be included as a feature. However I still need to invoke nlplug-findfs to do hotplugging, for e.g. USB keyboards to enter the passphrase, so I do a "no-op" nlplug-findfs.

  2. cryptroot=... supports multiple arguments. If multiple arguments are detected, we unlock each explicitly with nlplug-findfs.

  3. We now support multiple entries of a single argument, e.g. cryptroot=UUID=a cryptroot=UUID=b. This will accumulate the arguments joined by whitespace, such that KOPT_cryptroot="UUID=a UUID=b". This matches the way one passes multiple arg entries to the kernel, so hopefully it makes sense to users.

Note: I made change 3 really because the apparent intended use was broken for me. Code comments imply that I should be able to pass cryptroot="UUID=a UUID=b". I did this in my grub.cfg and verified it in the command list at boot time, but once booted, /proc/cmdline looked like ... "cryptroot=UUID=a UUID=b" ..., and init did not parse this as intended. I'm not sure if this is a known bug. I'm using grub-efi-2.02-r14 and linux-vanilla-4.19.41-r0.

@ncopa
Copy link
Contributor

ncopa commented Oct 9, 2019

how does dracut solve this problem? how do you specify multiple cryptroots?

@sbrudenell
Copy link
Author

Dracut allows

rd.luks.uuid=<uuid> rd.luks.uuid=<uuid>...

So I provided something analogous with this patch.

Let me walk through my usage more directly. My boot command line looks like this:

cryptroot=UUID=<luksdev uuid> cryptroot=UUID=<luksdev uuid> \
cryptroot=UUID=<luksdev uuid> cryptroot=UUID=<luksdev uuid> \
cryptkey=/boot/key.img root=UUID=<volume uuid> rootfstype=btrfs

I also have /boot/key.img included in my initfs.

With this patch, the init script:

  • Unlocks /boot/key.img as a loopback luks device, as /dev/mapper/__boot_key
  • Uses /dev/mapper/__boot_key as a keyfile to unlock the four luks devices, each as /dev/mapper/luks-<uuid>, in the style of dracut
  • Calls /sbin/btrfs device scan to discover btrfs devices and collect volumes
  • Mounts root via mount UUID=<volume uuid>, pivots and execs the real init.

My goals were:

  • Support single password entry for encrypted-multi-device root filesystems, especially btrfs
  • Be secure-ish while doing so (I am not a pro security engineer, but I think I achieved this)
  • Don't disrupt any current use cases
  • When in doubt, use a similar style to other existing software (i.e. dracut)

@ncopa
Copy link
Contributor

ncopa commented Dec 18, 2019

I had another look at this and #57 which solves same problem but differently.

I sort of like that you can specify cryptroot=UUID=... multiple times, similar to dracut. But I think I'd like to solve this in nlplug-findfs.c. So i wonder if we could do something like:
cryptroot=UUID=a,UUID=b cryptdm=a,b and then let nlplug-findfs take a list of devices.

Basically, I'd like nlplug-findfs handle multiple crypt devices.

@sbrudenell sbrudenell changed the title Support multiple cryptroot= devices. Support multiple encrypted devices Feb 6, 2022
@sbrudenell
Copy link
Author

It's been a minute, but I need to retool my boot process, so I'm taking another look at this.

I had another look at this and #57 which solves same problem but differently.

I sort of like that you can specify cryptroot=UUID=... multiple times, similar to dracut. But I think I'd like to solve this in nlplug-findfs.c. So i wonder if we could do something like: cryptroot=UUID=a,UUID=b cryptdm=a,b and then let nlplug-findfs take a list of devices.

Basically, I'd like nlplug-findfs handle multiple crypt devices.

I'm not sure this works for multi-encrypted-device btrfs. nlplug-findfs terminates after finding a target fs once, somewhere, unlocking crypt devices in hopes of finding its target. But in a multi-device btrfs volume the label and uuid are the same on each device, so it terminates after finding just one device of the volume. We need to ensure all devices get unlocked before mounting.

(The initial version of this PR wrongly called nlplug-findfs with the root volume as the target while varying the crypt parameters; I believe this was wrong)

It's not clear to me that nlplug-findfs could be reasonably modified to find "the right set of devices" for a multi-device btrfs volume. In any case, it seems simpler to just unconditionally call cryptsetup open for a given set of devices. This is how this case is handled with dracut.

Calling the cryptsetup binary directly has some other advantages. In LUKS2, cryptsetup will check the kernel keyring for a passphrase, which allows single password entry for multiple devices. Integrating this into nlplug-findfs would be a little more work, and the work increasingly looks like a copypasta of cryptsetup.

I updated this PR with a simple implementation of these ideas. This satisfies my use case of multiple-encrypted-device btrfs root, with single password entry. I use rootfstype=btrfs root=UUID=zzz lukskeydesc=my:key luksuuid=aaa luksuuid=bbb. I chose luksuuid as analogous to dracut's rd.luks.uuid. I'm open to ideas about whether we should reuse cryptroot instead (with a flag to unconditionally unlock?), or how it should interact with other crypt* args.

Out of curiosity, why does alpine not package dracut? It would be nice to have access to it for these more complex cases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants