Last active
October 16, 2022 03:19
-
-
Save jaydub/fc380581b548057b157b6d0a5e3014e5 to your computer and use it in GitHub Desktop.
How to unlock just one LUKS volume on a btrfs RAID1 inside LUKS include /boot installation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
prepare_grub_to_access_device () | |
{ | |
# If GRUB_BTRFS_RAID1 is set, then this function output fragments of config | |
# for each device surrounded by "if $root = $device", so eg only | |
# one cryptmount command is run, only one path is searched for root etc | |
# However it skips going to the trouble for insmod commands, as they're | |
# already duplicated all over the place and are O(1) if the module is already | |
# loaded. | |
# | |
# We use grub-probe --target=compatibility_hint to translate the devices | |
# handed to the function via $@ into something that's reasonably likely to map | |
# to $root | |
old_ifs="$IFS" | |
IFS=' | |
' | |
partmap="$("${grub_probe}" --device "$@" --target=partmap)" | |
for module in ${partmap} ; do | |
case "${module}" in | |
netbsd | openbsd) | |
echo "insmod part_bsd";; | |
*) | |
echo "insmod part_${module}";; | |
esac | |
done | |
loop_file= | |
case $1 in | |
/dev/loop/*|/dev/loop[0-9]) | |
grub_loop_device="${1#/dev/}" | |
loop_file=$(losetup "$1" | sed -e "s/^[^(]*(\([^)]\+\)).*/\1/") | |
case $loop_file in | |
/dev/*) ;; | |
*) | |
loop_device="$1" | |
shift | |
set -- "$("${grub_probe}" --target=device "${loop_file}")" "$@" || return 0 | |
;; | |
esac | |
;; | |
esac | |
# Abstraction modules aren't auto-loaded. | |
abstraction="$("${grub_probe}" --device "$@" --target=abstraction)" | |
for module in ${abstraction} ; do | |
echo "insmod ${module}" | |
done | |
fs="$("${grub_probe}" --device "$@" --target=fs)" | |
for module in ${fs} ; do | |
echo "insmod ${module}" | |
done | |
if [ "x$GRUB_ENABLE_CRYPTODISK" = xy ]; then | |
if [ "x$GRUB_BTRFS_RAID1" = xy ]; then | |
for local_device in "$@"; do | |
compat_device="$("${grub_probe}" --device "$local_device" --target=compatibility_hint)" | |
echo "if [ x\$root = x$compat_device ]; then"; | |
for uuid in $("${grub_probe}" --device "$local_device" --target=cryptodisk_uuid); do | |
echo " cryptomount -u $uuid" | |
done | |
echo "fi" | |
done | |
else | |
for uuid in $("${grub_probe}" --device "$@" --target=cryptodisk_uuid); do | |
echo "cryptomount -u $uuid" | |
done | |
fi | |
fi | |
# If there's a filesystem UUID that GRUB is capable of identifying, use it; | |
# otherwise set root as per value in device.map; in the GRUB_BTRFS_RAID1 case, | |
# we just trust that the existing root is correct | |
if [ "x$GRUB_BTRFS_RAID1" != xy ]; then | |
fs_hint="$("${grub_probe}" --device "$@" --target=compatibility_hint)" | |
if [ "x$fs_hint" != x ]; then | |
echo "set root='$fs_hint'" | |
fi | |
fi | |
# btrfs raid1 file systems have the same UUID across multiple devices, so the | |
# $@ invocation is fine here | |
if [ "x${GRUB_DISABLE_UUID}" != "xtrue" ] && fs_uuid="$("${grub_probe}" --device "$@" --target=fs_uuid 2> /dev/null)" ; then | |
if [ "x$GRUB_BTRFS_RAID1" = xy ]; then | |
echo "if [ x\$feature_platform_search_hint = xy ]; then" | |
# The specific device cases | |
for local_device in "$@"; do | |
compat_device="$("${grub_probe}" --device "$local_device" --target=compatibility_hint)" | |
hints="$("${grub_probe}" --device "$local_device" --target=hints_string 2> /dev/null)" || hints= | |
if [ "x$hints" != x ]; then | |
some_hints=true | |
echo " if [ x\$root = x$compat_device ]; then" | |
echo " search --no-floppy --fs-uuid --set=root ${hints} ${fs_uuid}" | |
echo " fi" | |
fi | |
done | |
# The case where we got this far but actually had no hints | |
if [ "x$some_hints" != xtrue ]; then | |
echo " search --no-floppy --fs-uuid --set=root ${fs_uuid}" | |
fi | |
# The platform doesn't support hints case | |
echo "else" | |
echo " search --no-floppy --fs-uuid --set=root ${fs_uuid}" | |
echo "fi" | |
else | |
hints="$("${grub_probe}" --device "$@" --target=hints_string 2> /dev/null)" || hints= | |
if [ "x$hints" != x ]; then | |
echo "if [ x\$feature_platform_search_hint = xy ]; then" | |
echo " search --no-floppy --fs-uuid --set=root ${hints} ${fs_uuid}" | |
echo "else" | |
echo " search --no-floppy --fs-uuid --set=root ${fs_uuid}" | |
echo "fi" | |
else | |
echo "search --no-floppy --fs-uuid --set=root ${fs_uuid}" | |
fi | |
fi | |
fi | |
IFS="$old_ifs" | |
if [ "x${loop_file}" != x ]; then | |
loop_mountpoint="$(awk '"'${loop_file}'" ~ "^"$2 && $2 != "/" { print $2 }' /proc/mounts | tail -n1)" | |
if [ "x${loop_mountpoint}" != x ]; then | |
echo "loopback ${grub_loop_device} ${loop_file#$loop_mountpoint}" | |
echo "set root=(${grub_loop_device})" | |
fi | |
fi | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
# Install your own grub.cfg files into various EFI volumes after grub-install | |
# runs | |
# | |
# Set up with: | |
# | |
# dpkg-divert --local --rename --divert /usr/sbin/grub-install.real --add /usr/sbin/grub-install | |
# | |
# Then install the script is /usr/sbin/grub-install | |
if test "$BASH" = "" || "$BASH" -uc "a=();true \"\${a[@]}\"" 2>/dev/null; then | |
# Bash 4.4, Zsh | |
set -euo pipefail | |
else | |
# Bash 4.3 and older chokes on empty arrays with set -u. | |
set -eo pipefail | |
fi | |
shopt -s nullglob globstar | |
require(){ hash "$@" || exit 127; } | |
require grub-install.real | |
CONF_BASE=/etc/grub-efi | |
TARGET=/boot | |
/usr/sbin/grub-install.real "$@" | |
while read -r src; do | |
target="${TARGET}${src#$CONF_BASE}" | |
if [ -e "$target" ]; then | |
cp "$src" "$target" | |
printf 'copying %s to %s\n' "$src" "$target" | |
fi | |
done < <(find "$CONF_BASE" -name grub.cfg) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I have a btrfs RAID1 inside LUKS installation across two drives — with
/boot
inside root inside LUKS. This works, but grub does not know that it only needs to unlock and search for boot on one drive to bootstrap it's way into working kernel, so even if you embed LUKS keys in your initramfs so you only need to type passwords into grub, you're going to have to unlock both LUKS volumes, and endure two rounds of grub's slow PDKDF2 implementation and it's generally painful UX.These two gists try to hack around that problem on Debian-like systems. One part is an override of the
prepare_grub_to_access_device
function fromgrub-mkconfig_lib
which, in the presence ofexport GRUB_BTRFS_RAID1=true
in your/etc/default/grub
, it will produce output that will cryptomount and search only on the volume that matches the root that grub sets based on which volume UEFI has booted into. You can either shove that directly into/etc/grub.d/10_linux
as I did or replace the same function in an override of/usr/lib/grub/grub-mkconfig_lib
so it's used everywhere in the construction of a/boot/grub/grub.cfg
by update-grub.The second part is a dirty hack that overrides grub-install with a wrapper that runs it with whatever arguments it's called with, then just replaces the EFI grub.cfg stubs with your own versions. This presumes you've mounted all your relevant EFI System Partitions under
/boot
somewhere (I named them all after their fat32 ID), and have matching-path grub.cfg files under/etc/grub-efi
eg:/etc/grub-efi/efi_9D28_D486/EFI/ubuntu/grub.cfg
→/boot/efi_9D28_D486/EFI/ubuntu/grub.cfg
(You could hack on these files directly, but grub-install will be invoked during the next package update and will clobber your changes if you don't handle it in some fashion.)
Obviously the world would be a lot better if:
But I doubt we'll see much movement here until distros decide that encrypted
/boot
is important enough to throw resources at grub development to address these problems.