Last active
March 10, 2024 22:49
-
-
Save ibressler/338e25b981f8f1a5b9445e685ac5758b to your computer and use it in GitHub Desktop.
Creates an early grub core image for ESP to cryptmount a LUKS+LVM partition where the grub files /boot/ and the kernel is stored
This file contains hidden or 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
#!/bin/dash | |
# Creates a standalone core image with grub to be started by EFI. | |
# It complements the excellent cryptroot tutorial at | |
# https://community.linuxmint.com/tutorial/view/2061 | |
# for grub on BTRFS inside LVM inside LUKS. | |
# | |
# It initiates decryption of the root device which contains | |
# LVM with BTRFS on the root volume where grub is installed. | |
# Therefore, it allows to load grub from an LUKS encrypted BTRFS | |
# root file system. | |
# | |
# This script can be called automatically at the end of grub-update. | |
# Create a file /etc/grub.d/99_earlygrubcryptoimg with the contents: | |
# <-- snip --> | |
# # wait a moment to let grub.cfg be created before parsing it for some info | |
# (sleep 2 && echo && \ | |
# <abs. path to this script> <abs. path to secure boot signing key>) 1>&2 & | |
# <-- snap --> | |
# | |
# Tested with Mint 18.2 on x86_64 | |
# | |
# No Warranty! Use at your own risk! | |
# This script works for me, but it might not work for you! | |
# Oct. 2017, Ingo Breßler (dev at ingobressler.net) | |
# License: GPLv3 | |
EFILABEL="Linux" # label in EFI boot menu | |
KEYFILE="$1" # path to secure boot signing key *.key, assumes certificate *.crt | |
ESPPATH="/boot/efi/" # path to partition containing EFI files | |
GRUBCFG="/boot/grub/grub.cfg" # common grub config generated by update-grub | |
is_btrfs () { | |
[ "x$(stat -f --format=%T "$GRUBCFG")" = "xbtrfs" ] | |
} | |
if ! is_btrfs; then | |
echo "Grub is not installed on BTRFS, this core image might not be needed!" | |
read -p "Continue anyway? [y/N] " answer | |
[ -z "$answer" ] && answer=n # default is no | |
(echo -n "$answer" | grep '^\s*[yY]\s*$') || exit 1 | |
fi | |
echo "Building early grub core image for crypt root access ..." | |
echo "($0)" | |
# get a temporary working directory | |
WD="$(mktemp -d)" | |
# generate the current keyboard layout for grub | |
# get the current keyboard layout | |
XKBLAYOUT="$(setxkbmap -query | grep layout | awk '{print $2}')" | |
LAYOUTFILE="layout.gkb" | |
KEYMAP="" | |
if [ x"$XKBLAYOUT" != x ]; then | |
ckbcomp "$XKBLAYOUT" | grub-mklayout > "$WD/$LAYOUTFILE" | |
KEYMAP="keymap /$LAYOUTFILE" | |
fi | |
# get the graphics mode/resolution | |
GFXMODE="$(grep -o 'set\s\+gfxmode=.*$' "$GRUBCFG")" | |
# get the appropriate cryptomount command&UUID | |
CRYPTTAB="/etc/crypttab" | |
CRYPTDEVUUID="$(cat "$CRYPTTAB" 2> /dev/null \ | |
| awk -F '=' '{print $2}' \ | |
| awk '{gsub("-",""); print $1}')" | |
CRYPTOMOUNT="cryptomount -u $CRYPTDEVUUID" | |
SEARCHFS="$(grep -m1 -o 'search.*fs-uuid.*$' "$GRUBCFG")" | |
if [ "x$CRYPTDEVUUID" = "x" ]; then # no crypted device found | |
echo "No UUID of the crypted root device found in '$CRYPTTAB'!" | |
echo "Skipping early grub core image!" | |
exit 1 | |
fi | |
# get abs. path to grub config dir from grub config | |
# the line typically starts with 'linux ...' | |
PREFIX="$(grep -oEm1 '(/[^/]+)*/boot/' "$GRUBCFG")${GRUBCFG#/boot/}" | |
PREFIX="${PREFIX%/*.*}" # strip the trailing file name component | |
# create the core image grub config | |
CORECFG="grub.cfg" # temp cfg file embedded in grubx64.efi | |
cat <<EOF > "$WD/$CORECFG" | |
root=(memdisk) | |
prefix=(\$root)/ | |
terminal_input at_keyboard | |
$KEYMAP | |
$GFXMODE | |
insmod all_video | |
insmod gfxterm | |
terminal_output gfxterm | |
insmod luks | |
$CRYPTOMOUNT | |
insmod lvm | |
$SEARCHFS | |
set prefix=(\$root)'$PREFIX' | |
configfile \$prefix/grub.cfg | |
EOF | |
# build memdisk for core image | |
MEMDISK="memdisk.tar" | |
(cd "$WD" && tar cf "$MEMDISK" "$CORECFG" "$LAYOUTFILE") | |
# create the core image finally | |
OUTFILE="grubx64.efi" | |
grub-mkimage -c "$WD/${CORECFG}" -o "$WD/${OUTFILE}" -O x86_64-efi \ | |
-m "$WD/${MEMDISK}" all_video at_keyboard boot btrfs cat chain configfile \ | |
crypto cryptodisk disk diskfilter echo efifwsetup efinet ext2 fat font \ | |
gettext gcry_arcfour gcry_blowfish gcry_camellia gcry_cast5 gcry_crc \ | |
gcry_des gcry_dsa gcry_idea gcry_md4 gcry_md5 gcry_rfc2268 gcry_rijndael \ | |
gcry_rmd160 gcry_rsa gcry_seed gcry_serpent gcry_sha1 gcry_sha256 \ | |
gcry_sha512 gcry_tiger gcry_twofish gcry_whirlpool gfxmenu gfxterm \ | |
gfxterm_background gzio halt hfsplus iso9660 jpeg keylayouts keystatus \ | |
loadenv loopback linux linuxefi lsefi lsefimmap lsefisystab lssal luks \ | |
lvm mdraid09 mdraid1x memdisk minicmd normal part_apple part_msdos \ | |
part_gpt password_pbkdf2 png raid5rec raid6rec reboot search \ | |
search_fs_uuid search_fs_file search_label sleep squash4 tar test true \ | |
verify video | |
# sign the created grub image for secure boot possibly | |
if [ ! -z "$KEYFILE" ]; then | |
if [ -f "$KEYFILE" ]; then | |
CRTFILE="${KEYFILE%.*}.crt" | |
sbsign --key "$KEYFILE" --cert "$CRTFILE" --output "$WD/${OUTFILE}" \ | |
"$WD/${OUTFILE}" | |
sbverify --cert "$CRTFILE" "$WD/${OUTFILE}" | |
else echo "Secure Boot keyfile '$1' not found!" | |
fi | |
else echo "No Secure Boot key argument for signing provided! Skipping." | |
fi | |
# copy the core image to the EFI directory | |
if [ ! -d "$ESPPATH" ]; then | |
echo "ESP path '$ESPPATH' does not exist! Giving up." | |
else | |
if [ "x$(stat -f --format=%T "$ESPPATH")" != "xmsdos" ]; then | |
echo "WARNING: ESP path '$ESPPATH' is not FAT formatted!" | |
fi | |
# copy to EFI partition | |
EFIPATH="$ESPPATH/EFI/$EFILABEL" | |
mkdir -p "$EFIPATH" | |
cp "$WD/$OUTFILE" "$EFIPATH/$OUTFILE" | |
# update EFI boot entries | |
DEVPART="$(basename "$(mount | grep "${ESPPATH%/}" | awk '{print $1}')")" | |
DEVEFI="$(basename "$(readlink -f "/sys/class/block/$DEVPART/..")")" | |
BOOTNUM="$(efibootmgr | grep -i " $EFILABEL\$" | grep -oE '[0-9]+')" | |
[ "x$BOOTNUM" != "x" ] && efibootmgr -q -d /dev/$DEVEFI -b $BOOTNUM -B | |
efibootmgr -q -d /dev/$DEVEFI -c -L "$EFILABEL" -l \\EFI\\$EFILABEL\\$OUTFILE | |
fi | |
# clean up temporary files | |
if [ -d "$WD" ]; then | |
# rm -R "$WD" | |
echo "Creating temporary working directory $WD" | |
echo "(removed on next boot)" | |
ls -lah "$WD" | |
fi | |
echo "done." |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment