Linux Mint 19: Secure Boot
Contents
Secure Boot and Linux Mint 19
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: COMEX-IE38M
- OS: Linux Mint 19
- Kernel: 4.15.0-34-generic
- BIOS:
- GRUB: 2.02-2ubuntu8.4
- efitools: 1.8.1
Installation of required tools
# super-user mode required su - apt update apt install -y openssl gnupg gpg sbsigntool uuid-runtime tree # Package efitools must to be compiled and installed manually because Debian repository contains old 1.4.2 version: apt build-dep -y efitools wget https://git.kernel.org/pub/scm/linux/kernel/git/jejb/efitools.git/snapshot/efitools-1.8.1.tar.gz tar -xvzf efitools-1.8.1.tar.gz cd efitools-1.8.1 make make install cd ..
Secure Boot setup
- Generate your own keys for Secure Boot: PK, KEK, db
CN="Your Name" O="Your Organisation Name" 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
GRUB_PASSWORD="Your GRUB password" GRUB_PASSWORD_HASH=$(echo -e "$GRUB_PASSWORD\n$GRUB_PASSWORD" | grub-mkpasswd-pbkdf2 | grep -o "grub.*") EFI_UUID=$(lsblk -f | grep -i efi | grep -E -o "[A-Z0-9]{4}-[A-Z0-9]{4}")
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 reboot EOF ### 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: Your Name Name-Email: your.name@address.com Expire-Date: 0 Passphrase: $GPG_PASSWORD # Do a commit here, so that we can later print "done" :-) %commit %echo done EOF 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 }') echo GPG_KEY=$GPG_KEY gpg --export $GPG_KEY > gpg.key
- Sing grub.init.cfg with your GPG key
# you will be required to enter value of $GPG_PASSWORD manually 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 ### as result grubx64.efi will be created
- Sign grubx64.efi with your db key
sbsign --key db.key --cert db.crt grubx64.efi ### as result grubx64.efi.signed will be created, it will be your bootloader
Prepare signed grub.cfg kernel and initrd on the EFI partition
- Mount your EFI partition to /boot/efi if not mounted yet
[ "$(mount | grep /boot/efi)" ] || mount /dev/disk/by-uuid/$EFI_UUID /boot/efi
- Copy your existing config, kernel and initrd to the EFI partition
KERNEL_VERSION=$(uname -r) cp /boot/grub/grub.cfg /boot/efi/ cp /boot/vmlinuz-$KERNEL_VERSION /boot/efi/ cp /boot/initrd.img-$KERNEL_VERSION /boot/efi/
- Create a secure boot "menuentry" for the grub.cfg on the EFI partition
KERNEL_CMDLINE=$(cat /proc/cmdline | awk '{ $1=""; print $0 }') DIST_NAME=$(grep 'PRETTY_NAME' /etc/os-release | cut -d'"' -f 2) cat > secure_menuentry << EOF menuentry '$DIST_NAME (secure boot)' --unrestricted --class ubuntu --class gnu-linux --class gnu { recordfail load_video gfxmode $linux_gfx_mode insmod gzio if [ x$grub_platform = xxen ]; then insmod xzio; insmod lzopio; fi linux /vmlinuz-$KERNEL_VERSION $KERNEL_CMDLINE initrd /initrd.img-$KERNEL_VERSION } EOF ### as result secure_menuentry will be created, please review it
- Add the secure boot "menuentry" to grub.cfg on the EFI partition
sed -i '/^menuentry/{e cat secure_menuentry :a;n;ba}' /boot/efi/grub.cfg
- 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
- Replace your existent bootloader with signed one
# find your existent bootloader and make a copy of it BOOTLOADER=$(find /boot/efi/EFI/ -name "*x64.efi") echo $BOOTLOADER cp $BOOTLOADER $BOOTLOADER".orig" # place grubx64.efi.signed you've created before as default bootloader cp grubx64.efi.signed $BOOTLOADER
Testing
- At this stage you should see the following in your /boot/efi
tree /boot/efi/
# You should see the following: /boot/efi/ ├── EFI │ └── ubuntu │ ├── grubx64.efi │ └── grubx64.efi.orig ├── grub.cfg ├── grub.cfg.sig ├── initrd.img-4.15.0-34-generic ├── initrd.img-4.15.0-34-generic.sig ├── vmlinuz-4.15.0-34-generic └── vmlinuz-4.15.0-34-generic.sig 2 directories, 8 files
- Reboot once to see if chain from bootloader to the OS works correctly
reboot
- Login as super-user again
su -
Enable Secure Boot
- Install keys into EFI (PK last as it will enable Custom Mode locking out further unsigned changes):
efi-updatevar -f db.auth db efi-updatevar -f KEK.auth KEK efi-updatevar -f PK.auth PK
- The EFI variables may be immutable (i-flag in lsattr output) in recent kernels (e.g. 4.5.4). Use chattr -i to make them mutable again if you can’t update the variables with the commands above:
chattr -i /sys/firmware/efi/efivars/{PK,KEK,db,dbx}-*
- Review installed certificates
efi-readvar
# You should see the following: Variable PK, length 874 PK: List 0, type X509 Signature 0, size 846, owner 101f003f-a976-4802-bc12-bbb2238fe111 Subject: CN=Your Name PK, O=Your Organisation Name Issuer: CN=Your Name PK, O=Your Organisation Name Variable KEK, length 874 KEK: List 0, type X509 Signature 0, size 846, owner 101f003f-a976-4802-bc12-bbb2238fe111 Subject: CN=Your Name KEK, O=Your Organisation Name Issuer: CN=Your Name KEK, O=Your Organisation Name Variable db, length 874 db: List 0, type X509 Signature 0, size 846, owner 101f003f-a976-4802-bc12-bbb2238fe111 Subject: CN=Your Name db, O=Your Organisation Name Issuer: CN=Your Name db, O=Your Organisation Name Variable dbx has no entries Variable MokList has no entries
- From now on only EFI binaries signed with any db key can be loaded
reboot
See also
- Original article: Secure Boot with GRUB 2 and signed Linux images and initrds