Note

The Funtoo Linux project has transitioned to "Hobby Mode" and this wiki is now read-only.

Secure Boot

From Funtoo
Revision as of 06:22, January 8, 2023 by Pnoecker (talk | contribs) (→‎See Also: add NSA pdf)
Jump to navigation Jump to search

Introduction

This page describes how to set up secure boot on Funtoo Linux with and without GRUB 2, using genkernel and boot-update. The resulting secure boot chain is:

  • The firmware setup is password protected.
  • The firmware has UEFI secure boot certificates installed, and you hold the corresponding private keys.
  • GRUB is signed by your key registered in the UEFI secure boot signature database.
  • GRUB config file, kernels and initramfs images are signed with the PGP key embedded in the GRUB image.
  • GRUB requires a password to do anything besides booting, e.g., to edit boot parameters or to use the command line.
   Note

Many Linux secure boot guides on the Net recommend using Fedora's "shim" boot loader. I would not recommend it, for the following reasons:

  • Shim only checks signatures of the boot loader and kernel, but not the GRUB config file or initramfs, which opens your machine to attacks.
  • At the time of writing (early 2019), GRUB's shim support (needed to check kernels) has been committed to the GRUB repository but is not yet in any released version.
  • I don't understand the point of MOKs and am not certain about their security model.

That said, shim may be useful if your machine's firmware doesn't allow you to install your own secure boot certificates.

Prerequisites

This guide assumes that:

  • You have a bootable rescue medium in case something goes wrong.
  • Your machine's firmware allows you to install your own secure boot certificates.
  • You have a working Funtoo installation with full disk encryption.
  • You are using the GRUB 2 bootloader installed on the EFI boot partition.
  • The boot partition is mounted on /boot.
  • The EFI boot partition is separate and is mounted on /boot/efi.
   Important

Having full disk encryption is critical; without it, the attacker can take out your hard disk and change anything but kernel, initramfs and bootloader.

Generating and Installing Secure Boot Certificates

Enter the firmware setup utility and put secure boot in setup mode.

Install efitools and sbsigntools:

root # emerge -av app-crypt/efitools app-crypt/sbsigntools

Decide where you want to keep your keys. You may keep them on the hard disk (not recommended), on another machine or on an external drive.

   Note

If you keep the keys on an external drive, be aware that gpg creates a socket for gpg-agent in its config directory, so it should reside on a filesystem that supports sockets (i.e., not FAT) and be mounted read-write for signing.

Create the directory in which you will keep the keys:

root # mkdir -p 700 /path/to/keys
root # cd /path/to/keys

Save old secure boot certificates:

root # efi-readvar -v PK  -o old_PK.esl
root # efi-readvar -v KEK -o old_KEK.esl
root # efi-readvar -v db  -o old_db.esl
root # efi-readvar -v dbx -o old_dbx.esl

Generate new certificates valid for 27 years:

root # openssl req -new -x509 -newkey rsa:2048 -subj "/CN=PK/"  -keyout PK.key  -out PK.crt  -days 10000 -nodes -sha256
root # openssl req -new -x509 -newkey rsa:2048 -subj "/CN=KEK/" -keyout KEK.key -out KEK.crt -days 10000 -nodes -sha256
root # openssl req -new -x509 -newkey rsa:2048 -subj "/CN=db/"  -keyout db.key  -out db.crt  -days 10000 -nodes -sha256

Prepare certificate lists:

root # cert-to-efi-sig-list PK.crt  PK.esl
root # cert-to-efi-sig-list KEK.crt KEK.esl
root # cert-to-efi-sig-list db.crt  db.esl

If you want to dual boot preinstalled OSes, add old KEK and db certificates to the new lists:

root # cat old_KEK.esl >>KEK.esl
root # cat old_db.esl  >>db.esl

Sign the certificate lists:

root # sign-efi-sig-list -k PK.key  -c PK.crt  PK  PK.esl      PK.auth
root # sign-efi-sig-list -k PK.key  -c PK.crt  KEK KEK.esl     KEK.auth
root # sign-efi-sig-list -k KEK.key -c KEK.crt db  db.esl      db.auth
root # sign-efi-sig-list -k KEK.key -c KEK.crt dbx old_dbx.esl old_dbx.auth

Remount the efivars partition read-write:

root # mount -o remount,rw /sys/firmware/efi/efivars

Install the certificates into EFI:

root # efi-updatevar -f old_dbx.auth dbx 
root # efi-updatevar -f db.auth      db
root # efi-updatevar -f KEK.auth     KEK
root # efi-updatevar -f PK.auth      PK

Create a Rescue Kernel (Optional)

Before you start playing with GRUB, it's a good idea to prepare a kernel that is bootable directly from EFI.

Enable the following kernel configuration variables:

   
CONFIG_EFI_STUB=y
CONFIG_CMDLINE_BOOL=y
CONFIG_CMDLINE="..."
CONFIG_CMDLINE_OVERRIDE=y
CONFIG_FB_SIMPLE=y

Under Processor type and features:

[*] EFI runtime service support
[*]   EFI stub support
[*] Built-in kernel command line
(...) Built-in kernel command string
[*]   Built-in command line overrides boot loader arguments

Set the value of Built-in kernel command string to the current kernel command line (from /proc/cmdline), but delete the rand_id parameter. (After you run boot-update, the random id for the new kernel will appear in /etc/boot.d/config/kernel/random.map.)

Under Device Drivers-->Graphics support-->Frame buffer Devices-->Support for frame buffer devices:

[*]   Simple framebuffer support

If you have early microcode, extract it to a temporary location:

root # mount /boot
root # mkdir /tmp/early
root # cd /tmp/early
root # cpio -i </boot/early_ucode.cpio

Compile and install the kernel with integrated initramfs. This command will further modify the kernel config file.

root # genkernel --no-clean --no-save-config --integrated-initramfs --initramfs-overlay=/tmp/early --fullname=rescue all

Sign the kernel with the secure boot key and copy it to the EFI partition:

root # sbsign --key /path/to/db.key --cert /path/to/db.crt -o /boot/kernel-rescue /boot/kernel-rescue
root # mount /boot/efi
root # cp /boot/kernel-rescue /boot/efi/EFI/Funtoo\ Linux\ \[GRUB\]/rescue.efi

Add the kernel to the EFI boot order. Replace EFIBOOTDEVICE below with the device mounted on /boot/efi.

root # efibootmgr -c -l '\EFI\Funtoo Linux [GRUB]\rescue.efi' -L 'Funtoo Linux [Rescue Kernel]' -d /dev/EFIBOOTDEVICE

Shutdown, enter the firmware setup, enable secure boot and boot using the rescue kernel to make sure that it works.

   Note

In fact, you can use the scheme above to install new kernels and forego GRUB entirely. The downside is that you won't be able to use the command line, e.g., to enter the single user mode, in case anything goes wrong.

Installing GRUB

Generate a PGP key pair and export the public key:

root # mkdir -m 700 /path/to/gpg
root # gpg --homedir=/path/to/gpg --quick-generate-key grub2 default default never
root # gpg --homedir=/path/to/gpg --export grub2 >/path/to/grub.pub

Create the initial GRUB config file which will be embedded into the GRUB image:

   grub-initial.cfg - Initial GRUB config
set superusers="root"
export superusers
password_pbkdf2 root grub.pbkdf2.sha512.10000.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

set root=ROOTDISK
search --no-floppy --fs-uuid --set FSUUID
configfile /grub/grub.cfg

echo grub.cfg did not boot the system but returned to initial.cfg.
echo Exiting in 10 seconds.
sleep 10
exit

You will have to edit the config file in three places:

  1. Use grub-mkpasswd-pbkdf2 to generate a password hash to replace the zeroes.
  2. Replace ROOTDISK with the value of the root variable from /boot/grub/grub.cfg.
  3. Replace FSUUID with the filesystem UUID from a search line in /boot/grub/grub.cfg.

Mount /boot and /boot/efi:

root # mount /boot
root # mount /boot/efi

Make a standalone GRUB image. Replace ROOTDISK below with the same value as above.

root # grub-mkimage -O x86_64-efi -p "ROOTDISK/grub" -c /path/to/grub-initial.cfg -k /path/to/grub.pub -o "/boot/efi/EFI/Funtoo Linux [GRUB]/grubx64.efi" configfile loadenv part_gpt ext2 linux gcry_rsa gcry_sha256 password_pbkdf2 all_video gfxterm videoinfo search minicmd test echo reboot sleep
root # sbsign --key /path/to/db.key --cert /path/to/db.crt -o "/boot/efi/EFI/Funtoo Linux [GRUB]/grubx64.efi" "/boot/efi/EFI/Funtoo Linux [GRUB]/grubx64.efi"

Sign some kernel and initramfs images:

root # gpg --homedir=/path/to/gpg -b /boot/kernel-rescue
root # gpg --homedir=/path/to/gpg -b /boot/early_ucode.cpio
root # gpg --homedir=/path/to/gpg -b /boot/kernel-genkernel-x86_64-4.20.0-gentoo
root # gpg --homedir=/path/to/gpg -b /boot/initramfs-genkernel-x86_64-4.20.0-gentoo

You may leave some the kernels or initrd images unsigned for testing.

Edit /etc/boot.conf:

  • Add the rescue kernel (optionally).
  • Only kernel-genkernel-* in the "Funtoo Linux genkernel" group.
  • Skip kernels ending with .sig.
  • Allow booting configured kernels without authentication.
   Warning

This relies on the code not yet in boot-update; see FL-6103 and FL-6104.

   /etc/boot.conf - Boot config fragments
"Funtoo Linux rescue" {
        menuflags --unrestricted
        kernel kernel-rescue
}

"Funtoo Linux genkernel" {
        menuflags --unrestricted
        kernel kernel-genkernel[-v] -*.sig
        initrd initramfs-genkernel[-v]
#       params += ...
# ... the rest of the section remains unchanged
}

Generate and sign grub.cfg, and sign the font used by GRUB:

root # boot-update
root # gpg --homedir=/path/to/gpg -b /boot/grub/grub.cfg
root # gpg --homedir=/path/to/gpg -b /boot/grub/fonts/unicode.pf2

You should already have the Funtoo Linux [GRUB] EFI boot entry. If you're not certain, check it with:

root # efibootmgr

If no such entry is found, add it. Replace EFIBOOTDEVICE below with the device mounted on /boot/efi.

root # efibootmgr -c -l '\EFI\Funtoo Linux [GRUB]\grubx64.efi' -L 'Funtoo Linux [GRUB]' -d /dev/EFIBOOTDEVICE

Look for the number of the entry and where it appears in the boot order. If it's not the first, you may want to change the boot order. Copy the BootOrder line from the efibootmgr output and rearrange the numbers to your liking:

root # efibootmgr -o 0001,0002,0000,0018,0019,001A,001B,001C,001D,001E,001F,0024

Shutdown, enter the firmware setup, enable secure boot and make sure that everything works. Particularly:

  • You can boot the system without entering a password.
  • You can not boot unsigned EFI images.
  • You can not boot unsigned kernels or load unsigned initramfs images.
  • If you try to edit kernel parameters or drop to the GRUB command line, you get a password prompt.

Set Up the Firmware Password

Shutdown, enter the firmware setup and enable the password. While you're there, look at the security-related options.

See Also

Secure Boot with GRUB 2 and signed Linux images and initrds NSA pdf detailing the secureboot process