zbm-dev / zfsbootmenu

ZFS Bootloader for root-on-ZFS systems with support for snapshots and native full disk encryption
https://zfsbootmenu.org
MIT License
804 stars 62 forks source link

Request: help with best procedure for using a USB to enter passphrase with fallback to prompt (Debian) #215

Closed Jahfry closed 2 years ago

Jahfry commented 2 years ago

Apologies for posting as an issue. I'm new to these parts. Didn't feel right to post on r/voidlinux. If I'm out of scope here and there's a better place for me to ask this, please chide me with a link and close this.

...

I have a Debian 11 Bullseye system up and running via the Debian 10 Buster guide with appropriate changes.

What am I looking for?

...

I've been reading a bit:

...

Is the method used to set up Debian in the Buster guide already able to use a USB key partition but fallback to the key on the encrypted pool if USB is missing (and prompt for input) as-is?

If not, what's considered the best way for me to patch that in?

I was thinking of editing 'zfs-load-key.sh' to read in a user property (which would be set to /dev/disk/by-id/{USBid}-part1) and, if exists, read that. If it doesn't exist then fall back to the default method.

But given the comments found on the issue I mentioned it reads like maybe I'm being clueless and it may already work.

Is there a guide or post that does what I'm asking?

If not, I'm heavily documenting my entire system setup and, if I get this working, will be including this as an optional step. It will go live on github once I'm done with other configurations.

...

NOTE: I'm preferring to set my passphrase manually rather than a /dev/random so that I can remember it at the prompt. This system is a home server and VFIO host. I'm mostly going through this to add some convenience at the loss of a bit of security. I'm not worried about people in my house (wife, 3 dogs, roommate) accessing the server, I just want to be able to lock it down when we're out of town.

zdykstra commented 2 years ago

Through our recently expanded hook system, we have something very close to what you're wanting to accomplish now.

I would solve the problem in this fashion:

The above will net you a system that can be automatically rebooted with out any user interaction while the USB key is attached. When you travel, you can remove the USB key (and optionally shut down the system) and ZFSBootMenu will instead simply prompt for a passphrase on startup. Debian always has the key file in the initramfs, so it always knows how to find it. This won't require any patches to the Debian initramfs scripts, so there's nothing to continually port / maintain there.

ahesford commented 2 years ago

The only thing to be aware of with the key-in-initramfs approach is that Debian (like Ubuntu and Arch) set a mode of 644 on the image by default. Any user who can read the initramfs can unpack it and gain access to the etc/zfs/zpool.key contained within (even if you set mode 000 on the file, as you should).

Dracut does the right thing here and explicitly sets a umask of 0077 before creating the file so only the superuser can read the image. On Void Linux, which offers mkinitcpio as an alternative to dracut, we work around the problem by forcing the right umask before we ever call the program from the automatic kernel hook.

You will probably want to find a way to always set a umask of 0077 on your initramfs from within Debian, or else you will want to inject some kind of script like the key-loading hook you write for ZBM into your Debian initramfs.

Jahfry commented 2 years ago

Thanks to both quick replies.


@zdykstra Nice. I'll be looking at doing that today. Will reply back with status prior to closing issue.

USB drive with the right UUID / label

I'm curious if there's a problem with just using /dev/disk/by-id rather than UUID or label? I see the by-id fine when using a ZBM rescue shell.

I was thinking of possibly obfuscating the key by creating a -part1 (which would have a debian live image and label) at the normal first sector, a -part2 on the drive (with a generic portable storage region and label) but skipping 512b between the partitions and reading in that un-partitioned sector for the key using by-id for a dd read.

It wouldn't hide it from a forensics specialist if they have access to the USB (which is highly unlikely) but it would be pretty good at hiding from casual experts if they aren't invested in the attack.

I see the problem with that approach as method you gave would be expecting a mount point.

I'll do this round using that method and look into getting fancy later.


@ahesford

Dracut does the right thing here and explicitly sets a umask of 0077 before creating the file

The instructions I went by to install ZFSBootMenu have Debian but using dracut, so I'm hoping that fixes the security issue there?

PS. I did do a bunch of reading on Void and almost went that way with my host OS. Lack of some packages I wanted (laziness not wanting to compile things right now) and wanting Debian's long term support had me stick to my old ways. But I'll be putting up a Void VM to learn more about it and may convert the host in the future. For now the hybrid matches my skill set better :)

ahesford commented 2 years ago

If you've replaced initramfs-tools with dracut, you should be fine. Just make sure /boot/initramfs* is only readable by root.

zdykstra commented 2 years ago

Thanks to both quick replies.

@zdykstra Nice. I'll be looking at doing that today. Will reply back with status prior to closing issue.

USB drive with the right UUID / label

~I'm curious if there's a problem with just using /dev/disk/by-id rather than UUID or label? I see the by-id fine when using a ZBM rescue shell.~

~I was thinking of possibly obfuscating the key by creating a -part1 (which would have a debian live image and label) at the normal first sector, a -part2 on the drive (with a generic portable storage region and label) but skipping 512b between the partitions and reading in that un-partitioned sector for the key using by-id for a dd read.~

~It wouldn't hide it from a forensics specialist if they have access to the USB (which is highly unlikely) but it would be pretty good at hiding from casual experts if they aren't invested in the attack.~

I see the problem with that approach as method you gave would be expecting a mount point.

I'll do this round using that method and look into getting fancy later.

@ahesford

Dracut does the right thing here and explicitly sets a umask of 0077 before creating the file

The instructions I went by to install ZFSBootMenu have Debian but using dracut, so I'm hoping that fixes the security issue there?

PS. I did do a bunch of reading on Void and almost went that way with my host OS. Lack of some packages I wanted (laziness not wanting to compile things right now) and wanting Debian's long term support had me stick to my old ways. But I'll be putting up a Void VM to learn more about it and may convert the host in the future. For now the hybrid matches my skill set better :)

A mount point was just an idea based on the existing LUKS logic. If you know exactly where the the passphrase is on the USB drive at the sector level, you could give dd the right offset and length and read the passphrase out into /etc/zfs/zpool.key that way.

Basically the sky is the limit here for how you feed ZFSBootMenu the key file at /etc/zfs/zpool.key. You can run arbitrary shell code with any tooling needed. It will be excuted before any pool imports take place. The OS initramfs will just have the key embedded in it already for you, so you don't have to fight with Dracut or initramfs-tools to magic up a key for your OS / boot environment. This is nice because it won't require any custom initramfs patches.

Feel free to drop on by our IRC channel - libera.chat#zfsbootmenu - We'd be happy to help brain storm in real time. Whatever you do end up going with would also make a great script in contrib/ for other users (sanitized if need be).

zdykstra commented 2 years ago

If I'm understanding correctly what you'd like to do, I've mocked up / tested a bare-bones script to accomplish what you want. You'll want to create a partition on the USB drive, set a GPT label and then write junk to the entire partition. You can then pick a random offset and write your password into the drive.

#!/bin/bash

# fill it with junk
# dd if=/dev/urandom of=/dev/disk/by-partlabel/primary bs=1M
# write our password to a "random" location
# echo -n zfsbootmenu | dd of=/dev/disk/by-partlabel/primary bs=11 seek=1100

# shellcheck disable=SC1091
[ -f /lib/zfsbootmenu-lib.sh ] && source /lib/zfsbootmenu-lib.sh

plain="/dev/disk/by-partlabel/primary"
pass=0

while true; do
  if [ "${pass}" -gt 10 ]; then
    zinfo "timed out waiting for ${plain}"
    exit
  fi

  if [ -b "${plain}" ] ; then
    mkdir /etc/zfs
    dd if=/dev/disk/by-partlabel/primary skip=1100 bs=11 count=1 2>/dev/null > /etc/zfs/ztest.key
    exit
  else
    if delay=5 prompt="Checking for drive in %0.2d seconds" timed_prompt "[RETURN] check now" "[ESCAPE] cancel unlock" "" ; then
      pass=$((pass+1))
      continue
    else
      exit
    fi
  fi
done

You'll need to build ZFSBootMenu from master, and add this script to your image via:

zfsbootmenu_early_setup="/path/to/script/raw-unlock.sh"

Just make sure it's +x . You'll also need to add dd to your image for the key extraction to work.

Jahfry commented 2 years ago

Wow.

I spent most of this time (over) building a script to sanity check a LIVE USB prior to partitioning, so I hadn't gotten to where I needed to make an unlock script. So, that's great synergy for me.

I am going under the assumption that most LIVE USBs have a ton of wasted space (example, on my 10 year old 16GB USB drive, LIVE only uses 4GB). So I'm using that extra space for more partitions.

The creation script will work on any USB with at least 1 partition (ie, -part1) and unused free space after it.

Your method is better than what I was going for. In mine I was going to add a 1 sector unpartitioned space before a last "filler" partition. The advantage for me was knowing where to look. The disadvantage is ... so would everyone else.

I think I'm going to have my process do these things:

This won't completely protect the passphrase, as the logic for determining it's location will be in the initrd where it can be accessed during the ZBM hook, but you'd have to have both the root drive and USB to get the info ... at which point you'd have it just by plugging in the USB.

I'll document and post the final USB creation script when done along with any modifications to your prototype.

Thanks for the assist.

Jahfry commented 2 years ago

@zdykstra ...

Since I'd rather the location on the partition relate to the zroot, and not be invalidated if the current disk is replaced, I'm thinking of basing the location of the phrase on the USB on zroot's GUID.

Any thoughts about that being good/bad?

Would allow the USB key to hide the location when it isn't attached, rather than being able to find the passphrase on the USB if you only have it but can gain access to the server later.

I'm able to query the GUID from an emergency shell and from the booted system, so it seems like the most stateful breadcrumb that doesn't rely on the disk ID but isn't tied to the USB.

zdykstra commented 2 years ago

@zdykstra ...

Since I'd rather the location on the partition relate to the zroot, and not be invalidated if the current disk is replaced, I'm thinking of basing the location of the phrase on the USB on zroot's GUID.

Any thoughts about that being good/bad?

Would allow the USB key to hide the location when it isn't attached, rather than being able to find the passphrase on the USB if you only have it but can gain access to the server later.

I'm able to query the GUID from an emergency shell and from the booted system, so it seems like the most stateful breadcrumb that doesn't rely on the disk ID but isn't tied to the USB.

That seems overly complicated for no real gain - though almost all of this is over-complicated for very little gain. Personally, I'd just put the ESP on a USB drive, and put your zpool key in the ZFSBootMenu EFI. When you travel / are otherwise not present, pull the USB drive out and physically secure it - in a safe, on your person, etc.

Jahfry commented 2 years ago

I am close to finishing up the overly complicated method I wanted, but, have a family emergency for the next week or two. I'm closing just to keep your issues cleaned up. I'll happily toss in the final as a possible contrib when I'm able to set up and polish a bit.

Thanks for the help, sorry, forgot to do this before I hit the road.