Implementing Secure Boot on top of FDE with grub/luks2/argon2

Just want to share my configuration for getting Garuda dr460nized started with Secure Boot enabled. Before that, I set it up with full disk encryption using luks2 (over btrfs) and argon2 (GRUB - ArchWiki).

Set up Secure Boot on Garuda Linux (Arch)

With grub, shim and key signing, basically following the info in the Arch wiki first:
https://wiki.archlinux.org/title/Unified_Extensible_Firmware_Interface/Secure_Boot#shim

Install shim-signed package

cd /usr/src
git clone https://aur.archlinux.org/shim-signed.git
cd shim-signed
makepkg -si

And copy the signed loader to the EFI directory of the current loader (which can be checked by running efibootmgr). For me, it’s the EFI/Garuda directory.

sudo cp /usr/share/shim-signed/shimx64.efi /boot/efi/EFI/Garuda/
sudo cp /usr/share/shim-signed/mmx64.efi /boot/efi/EFI/Garuda/

Create an UEFI boot menu entry for shim (use a label of your choice):

sudo efibootmgr --unicode --disk /dev/nvme0n1 --part 1 --create --label "Garuda-Shim" --loader /EFI/Garuda/shimx64.efi

Shim loads then the grubx64.efi created later from the same folder on the EFI partition.

Generate a new Machine Owner Key (MOK)

Store the new keys in roots home dir under /root/bootkeys/.

cd /root/
mkdir bootkeys && cd bootkeys && chmod 700 .
openssl req -newkey rsa:2048 -nodes -keyout MOK.key -new -x509 -sha256 -days 3650 -subj "/CN=My Machine Owner Key/" -out MOK.crt
openssl x509 -outform DER -in MOK.crt -out MOK.cer

And sign the boot loader as described in the Arch Wiki and make sure to install the mkinitcpio post hook as described (/etc/initcpio/post/ might need to be created first). There is a script for that below.
As an example, the kernel and grub can be signed manually like this:

cd /root/bootkeys/
sbsign --key MOK.key --cert MOK.crt --output /boot/vmlinuz-linux-zen /boot/vmlinuz-linux-zen
sbsign --key MOK.key --cert MOK.crt --output /boot/efi/EFI/Garuda/grubx64.efi /boot/efi/EFI/Garuda/grubx64.efi

Check that Grub is signed:

sbverify --cert /root/bootkeys/MOK.crt /boot/efi/EFI/Garuda/grubx64.efi

Copy MOK.cer to the EFI system partition to be enrolled either by MokManager or from within your BIOS manually:

cp MOK.cer /boot/efi/

This is required once, so that Secure Boot accepts the binaries signed with that custom key later on.

Get sbat.csv for grub

Grub needs to be installed with an SBAT and signed by the previously created machine owner key on each update, as described in the h**ps://wiki.archlinux.org/title/GRUB#Shim-lock.

Note: Turned out that the patched grub package for argon2 support does not include the sbat.csv file shipped with the original grub package in Arch. Get it and install along grubs files:

wget "https://gitlab.archlinux.org/archlinux/packaging/packages/grub/-/blob/main/sbat.csv"
set GRUBVER="$(pacman -Q | grep grub-improved | cut -d' ' -f2 | cut -d. -f1-2)"
sed -i "s#%PKGVER%#$GRUBVER#" sbat.csv
sudo mv sbat.csv /usr/share/grub/sbat.csv

Example: Install Grub with SBAT

grub-install --sbat /usr/share/grub/sbat.csv

Check that Grub has a SBAT section, run:

objdump -j .sbat -s /boot/efi/EFI/Garuda/grubx64.efi | head

Finally, a build script

To be used as grub-install replacement. It installs the kernel signing hook and builds a grub image containing a memdisk for embedding the font and config files which can not be signed like the binaries above:

cat /root/bootkeys/install+sign.sh:

#!/bin/sh
set -e

certpath="/root/bootkeys"
grub_efi="/boot/efi/EFI/Garuda/grubx64.efi"

# for secure boot, make sure an EFI boot entry for shim exists, do this manually once
#efibootmgr --unicode --disk /dev/nvme0n1 --part 1 --create --label "Shim" --loader /EFI/Garuda/shimx64.efi

# install kernel update signing hook
# from https://wiki.archlinux.org/title/Unified_Extensible_Firmware_Interface/Secure_Boot#shim_with_key
cat > /etc/initcpio/post/kernel-sbsign << EOF
#!/usr/bin/env bash

kernel="\$1"
[[ -n "\$kernel" ]] || exit 0

# use already installed kernel if it exists
[[ ! -f "\$KERNELDESTINATION" ]] || kernel="\$KERNELDESTINATION"

keypairs=("$certpath/MOK.key" "$certpath/MOK.crt")

for (( i=0; i<\${#keypairs[@]}; i+=2 )); do
    key="\${keypairs[\$i]}" cert="\${keypairs[(( i + 1 ))]}"
    if ! sbverify --cert "\$cert" "\$kernel" &>/dev/null; then
        sbsign --key "\$key" --cert "\$cert" --output "\$kernel" "\$kernel"
    fi
done
EOF

# Make sure grub config is recent
grub-mkconfig -o /boot/grub/grub.cfg
# update grub files in /boot/grub, copied to memdisk later
grub-install --no-nvram
cd "$certpath"
efi_vendor="$(basename "$(dirname "$grub_efi")")"
sh -x build-efi-images grub-mkimage /usr/lib/grub/x86_64-efi ./out "" x86_64-efi x64 /usr/share/grub/sbat.csv "$efi_vendor"
mv out/grubx64.efi "$grub_efi"
sbsign --key MOK.key --cert MOK.crt --output "$grub_efi" "$grub_efi"
rmdir out

Using a modified build-efi-images from Ubuntu

The original build-efi-images script is from h**ps://git.launchpad.net/ubuntu/+source/grub2-unsigned/plain/debian/build-efi-images, I modified it for Garuda/Arch and removed the parts not needed (such as CD and netboot images).
cat /root/bootkeys/build-efi-images:

#! /bin/sh
set -e

# Copyright (C) 2010, 2011, 2012 Canonical Ltd.
# Author: Colin Watson <[email protected]>
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.

# Make EFI boot images for signing.

if [ $# -lt 7 ]; then
	echo "usage: $0 GRUB-MKIMAGE GRUB-CORE OUTPUT-DIRECTORY DEB-ARCH PLATFORM EFI-NAME SBAT-CSV [EFI-VENDOR]"
	exit 1
fi

grub_mkimage="$1"
grub_core="$2"
outdir="$3"
deb_arch="$4"
platform="$5"
efi_name="$6"
sbat_csv="$7"
efi_vendor="${8:-$(dpkg-vendor --query vendor | tr '[:upper:]' '[:lower:]')}"

# make sure downstreams didn't mess up sbat.csv
if ! grep -q ^grub.arch $sbat_csv; then
   echo Missing sbat entry for "grub.arch" >&2
   exit 1
fi

# mkfs.msdos may not be on the default PATH.
export PATH="$PATH:/sbin:/usr/sbin"

workdir=

cleanup () {
	[ -z "$workdir" ] || rm -rf "$workdir"
}
# comment this to check the contents of the memdisk
trap cleanup EXIT HUP INT QUIT TERM

rm -rf "$outdir"
mkdir -p "$outdir"

workdir="$(mktemp -d wd-build-efi-images.XXXXXX)"

# use the regular grub preloaded config to unlock the crypto device
cat "/boot/grub/x86_64-efi/load.cfg" > "$workdir/grub-bootstrap.cfg"
# set the right prefix, according to the paths on the memdisk, created next
cat >> "$workdir/grub-bootstrap.cfg" << EOF
set prefix="(memdisk)"
EOF

memdiskpath="$workdir/memdisk/"
mkdir -p "$memdiskpath"
cp -R /boot/grub/fonts /boot/grub/grub* "$memdiskpath/"

mksquashfs "$workdir/memdisk" "$workdir/memdisk.squashfs" -comp xz

CD_MODULES="
	all_video
	boot
	btrfs
	cat
	chain
	configfile
	echo
	efifwsetup
	efinet
	ext2
	fat
	font
	gettext
	gfxmenu
	gfxterm
	gfxterm_background
	gzio
	halt
	help
	hfsplus
	iso9660
	jpeg
	keystatus
	loadenv
	loopback
	linux
	ls
	lsefi
	lsefimmap
	lsefisystab
	lssal
	memdisk
	minicmd
	normal
	ntfs
	part_apple
	part_msdos
	part_gpt
	password_pbkdf2
	png
	probe
	reboot
	regexp
	search
	search_fs_uuid
	search_fs_file
	search_label
	serial
	sleep
	smbios
	squash4
	test
	tpm
	true
	video
	"

# Platform-specific modules
case $platform in
    x86_64-efi|i386-efi)
	CD_MODULES="$CD_MODULES
	cpuid
	play
	"
	;;
    arm64-efi)
	CD_MODULES="$CD_MODULES
	fdt
	"
	;;
esac

GRUB_MODULES="$CD_MODULES
	cryptodisk
	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
	luks2
	argon2
	pgp
	bli
	lvm
	"

# Normal disk boot image
echo "Including modules $GRUB_MODULES in $outdir/grub$efi_name.efi"
"$grub_mkimage" \
    -O "$platform" \
    -o "$outdir/grub$efi_name.efi" \
    -c "$workdir/grub-bootstrap.cfg" \
    -d "$grub_core" \
    -m "$workdir/memdisk.squashfs" \
    --sbat "$sbat_csv" \
    $GRUB_MODULES

exit 0

Lessons learned

Grub check_signatures

Attempting to solve the error ‘prohibited by Secure Boot policy’ by using signature checking with grub on files other than the peimages above. (tldr; It does not help with Secure Boot errors.)

Make sure to select an RSA key when generating one since the default ed25519 was silently ignored by grubs –pubkey argument (gpg 2.4.5, 2.12).
See also: h**ps://bbs.archlinux.org/viewtopic.php?pid=2080017#p2080017

cd /root/bootkeys/
gpg --homedir gpg --full-gen-key
gpg --homedir gpg --export > boot.key

Install grub signature helpers

Kindly provided by Bandie in h**ps://github.com/Bandie/grub2-signing-extension.
Install to /usr/src and symlink into /usr/local/sbin:

cd /usr/src
git clone https://github.com/Bandie/grub2-signing-extension.git
cd /usr/local/sbin
for fn in /usr/src/grub2-signing-extension/sbin/*; do ln -s "$fn" "${fn##*/}"; done

Sign all grub files with the newly created certificate:

export GNUPGHOME=/root/bootkeys/gpg
grub-sign

Finding the modules embedded in a grubx64.efi

By employing rabin2 from the radare2 package and following the idea from here h**ps://askubuntu.com/a/1466507 and with ensuring that the module exists on disk actually:

for mod in $(rabin2 -qq -zz /boot/efi/EFI/Garuda/grubx64.efi | grep -A10 --no-group-separator LICENSE= |grep -v LICENSE=|grep -v '^grub_' |grep -v '^_'|grep -v '^\.'|grep -v '[A-Z ]' | sort -u ); do [ -f /usr/lib/grub/x86_64-efi/$mod.mod ] || continue; echo "$mod"; done | tr '\n' ' '

Hope this helps anyone trying to do this as well!
Best wishes!

3 Likes

HI 1ng0! I see you start your post with explaining how you already have installed Garuda OS with luks1 and have so cinverted it to luks2. How did you do that? Can you give me a step by step tutorial on how to get this done? Kind of like in this here post? you cab send me this answer if you are up for it to [email protected] which is my email adress. I have struggled for a very long time with this converting business from luks1 to luks2 and i understand that its something to do with grub-improved-luks2|-patch or something, but when i try to in stall it following a article it fails. so any help would be fantstic! From Christian

Welcome :slight_smile:
The purpose of a forum is not to give private lessons to an individual.

DR’TL, but it looks like a there is a step-by-step guide in post #1

Hi cs76, Since I did a fresh install of Garuda, I could use a luks2 format from the beginning (and secure boot turned off). I am not sure if the installer did this or if I did this manually while pausing the installer at the device/partition selection step.
However, there is a good guide on conversion which I used for reference when testing different grub setup approaches but I did not need to convert:

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.