Linux: Secure Boot

From fit-PC wiki
Revision as of 13:18, 5 June 2018 by Denis (Talk | contribs) (Created page with "== Secure Boot and Linux == The idea is to create a signed GRUB EFI binary with required modules built-in. Secure Boot verifies this binary during boot. GRUB then reads the s...")

(change visibility) (diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Secure Boot and Linux

The idea is to create a signed GRUB EFI binary with required modules built-in. Secure Boot verifies this binary during boot. GRUB then reads the signed grub.cfg which contains the list of available kernels and then loads the signed kernel and initrd. GRUB's verification is based on GPG which is independent of Secure Boot.

The guide was tested on a Debian system with the specs listed below, but should be easily adaptable.

  • Device: fitlet2
  • OS: Debian GNU/Linux testing (buster)
  • Kernel: 4.13.0-1-amd64
  • BIOS: 05/14/2018 American Megatrends Inc. FLT2.
  • GRUB: 2.02-2
  • efitools: 1.8.1

Installation of required tools

sudo apt update
sudo apt install -y openssl gnupg gpg sbsigntool uuid-runtime
# Package efitools must to be compiled and installed manually because Debian repository contains old 1.4.2 version:
sudo apt build-dep efitools
tar -xvzf efitools-1.8.1.tar.gz
cd efitools-1.8.1
sudo make install
cd -

Secure Boot setup

  • Generate your own keys for Secure Boot: PK, KEK, db
CN="Your Common Name"
O="Your Organisation"
openssl req -new -x509 -newkey rsa:2048 -subj "/CN=$CN PK,  O=$O/"  -keyout PK.key  -out PK.crt  -days 7300 -nodes -sha256
openssl req -new -x509 -newkey rsa:2048 -subj "/CN=$CN KEK, O=$O/"  -keyout KEK.key -out KEK.crt -days 7300 -nodes -sha256
openssl req -new -x509 -newkey rsa:2048 -subj "/CN=$CN db,  O=$O/"  -keyout db.key  -out db.crt  -days 7300 -nodes -sha256
  • Convert open part of the keys to the ESL format understood for UEFI
UUID=$(uuidgen --random)
cert-to-efi-sig-list -g $UUID PK.crt PK.esl
cert-to-efi-sig-list -g $UUID KEK.crt KEK.esl
cert-to-efi-sig-list -g $UUID db.crt db.esl
  • Sign ESL files
sign-efi-sig-list -k PK.key -c PK.crt PK PK.esl PK.auth
sign-efi-sig-list -k PK.key -c PK.crt KEK KEK.esl KEK.auth
sign-efi-sig-list -k KEK.key -c KEK.crt db db.esl db.auth
  • At this stage your are ready to sign GRUB EFI binary and add it to the list of binaries allowed by Secure Boot

GRUB EFI setup

GRUB EFI supports loading of GPG signed files only e.g. config or kernels through the verify module. The grub-mkstandalone command can be used to create a single GRUB binary with built-in modules and initial configuration script. The GRUB password is required to restrict access to the GRUB shell which allows running arbitrary commands. Your grub.cfg, kernel and initrd and their signatures would be placed on the EFI partition.

  • Create initial GRUB configuration script grub.init.cfg
EFI_UUID=$(lsblk -f | grep -i efi | grep -E -o "[A-Z0-9]{4}-[A-Z0-9]{4}")
GRUB_PASSWORD="Your GRUB password"
GRUB_PASSWORD_HASH=$(echo -e "$GRUB_PASSWORD\n$GRUB_PASSWORD" | grub-mkpasswd-pbkdf2 | grep -o "grub.*")
cat > grub.init.cfg << EOF
set check_signatures=enforce
export check_signatures
set superusers=root
export superusers
password_pbkdf2 root $GRUB_PASSWORD_HASH
search --no-floppy --fs-uuid --set=root $EFI_UUID
configfile /grub.cfg
echo /grub.cfg did not boot the system, rebooting the system in 10 seconds..
sleep 10
### as result grub.init.cfg will be created
  • Generate your GPG key
GPG_PASSWORD="Your GPG password"
cat > gpg.batch.file << EOF
%echo Generating a basic OpenPGP key
Key-Type: DSA
Key-Length: 1024
Subkey-Type: ELG-E
Subkey-Length: 1024
Name-Real: Denis Turischev
Name-Comment: Compulab LTD
Expire-Date: 0
Passphrase: "$GPG_PASSWORD"
# Do a commit here, so that we can later print "done" :-)
%echo done
gpg --batch --gen-key gpg.batch.file 2>&1 | tee gpg.log
GPG_KEY=$(cat gpg.log | grep -o "gpg: key [0-9A-Z]* marked as ultimately trusted" | awk '{ print $3 }')
gpg --export $GPG_KEY > gpg.key
  • Sing grub.init.cfg with your GPG key
export GPG_TTY=$(tty)
# gpg: signing failed: Inappropriate ioctl for device #2798
echo -e "$GPG_PASSWORD" | gpg --yes --default-key $GPG_KEY --detach-sign grub.init.cfg
### as result grub.init.cfg.sig will be created
  • Creating single GRUB EFI binary with buit-in modules and signed grub.init.cfg
MODULES="all_video archelp boot bufio configfile crypto echo efi_gop efi_uga ext2 extcmd  \
fat font fshelp gcry_dsa gcry_rsa gcry_sha1 gcry_sha512 gettext gfxterm linux linuxefi ls \
memdisk minicmd mmap mpi normal part_gpt part_msdos password_pbkdf2 pbkdf2 reboot relocator \
search search_fs_file search_fs_uuid search_label sleep tar terminal verify video_fb"
grub-mkstandalone -d /usr/lib/grub/x86_64-efi -O x86_64-efi --modules "$MODULES" --pubkey gpg.key --output grubx64.efi  boot/grub/grub.cfg=grub.init.cfg boot/grub/grub.cfg.sig=grub.init.cfg.sig -v
  • Sign grubx64.efi with your db key
sbsign --key db.key --cert db.crt grubx64.efi
### as result grubx64.efi.signed will be created
### this file will be your bootloader

Prepare signed grub.cfg kernel and initrd on the EFI partition

  • Mount your EFI partition to /boot/efi
mount /dev/disk/by-uuid/$EFI_UUID /boot/efi
  • Create grub.cfg on the EFI partition
KERNEL_VERSION=$(uname -r)
KERNEL_CMDLINE=$(cat /proc/cmdline | awk '{ $1=""; print $0 }')
sudo cat > /boot/efi/grub.cfg << EOF
set timeout_style=menu
set timeout=2
set gfxmode=auto
set gfxpayload=keep
terminal_output gfxterm
menuentry 'Debian GNU/Linux, with Linux $KERNEL_VERSION' --unrestricted {
   echo   'Loading Linux $KERNEL_VERSION ...'
   echo   'Loading initial ramdisk ...'
   initrd /initrd.img-$KERNEL_VERSION
### as result grub.cfg will be created, please review it
  • Copy your existent kernel and initrd to the EFI partition
sudo cp /boot/vmlinuz-$KERNEL_VERSION /boot/efi/
sudo cp /boot/initrd.img-$KERNEL_VERSION /boot/efi/
  • Sign grub.cfg, kernel and initrd with your GPG key
gpg --default-key "$GPG_KEY" --detach-sign /boot/efi/grub.cfg
gpg --default-key "$GPG_KEY" --detach-sign /boot/efi/vmlinuz-$KERNEL_VERSION
gpg --default-key "$GPG_KEY" --detach-sign /boot/efi/initrd.img-$KERNEL_VERSION
### as result signatures of these files will be created

Final steps

  • Replace your existent bootloader with signed one
# make a copy of your existent bootloader
sudo cp /boot/efi/EFI/boot/bootx64.efi /boot/efi/EFI/boot/bootx64.efi.orig
# place grubx64.efi.signed you've created before as default bootloader
sudo cp grubx64.efi.signed /boot/efi/EFI/boot/bootx64.efi
  • Install keys into EFI (PK last as it will enable Custom Mode locking out further unsigned changes):
sudo efi-updatevar -f db.auth db
sudo efi-updatevar -f KEK.auth KEK
sudo efi-updatevar -f PK.auth PK
  • From now on only EFI binaries signed with any db key can be loaded
  • Reboot