Instantly share code, notes, and snippets.

Embed
What would you like to do?
How to enable SecureBoot with own keys in KVM and on a laptop (T450s)

UEFI SecureBoot on ArchLinux

For KVM and Laptop

Rationale

I want full control what boots the computer to avoid the so called evil maid attack. That requires setting SecureBoot with only my own keys.

Intro

To simplify, I boot Linux directly from UEFI (no intermediate bootloaders).

Now UEFI can only boot a single efi executable, but to boot Linux you also need one or more initramfs (including intel micro-code) and a command line[1]. So all of these things have to be combined with objcopy. The combined file is then signed.

Alternatively I'd need to use grub2 or some other bootloader that knows about SecureBoot - that kind of scares me since it increases the attack surface.

[1] command line: the boot command line maybe could be avoided with auto-discovery. AFAIK Arch is not fully ready for that yet.

Three keys/certificates are needed for UEFI SecureBoot (PK, KEK, DB). They are created with openssl.

MAKE SURE YOU KEEP YOU .key files and access to UEFI secure!!!

Note: the below script doesn't do anything when executed. Needs to be copy pasted for now.

ASSUMPTIONS: /boot/ is the ESP (EFI System Partition)

Required packages: efibootmgr and from AUR: sbsigntools and efitools. pesign was recommended in some docs, didn't work at all for me when signing files.

KVM

You need an OVMF (the opensource UEFI firmware) binary. Since the one in Arch repos doesn't support SecureBoot, and the one in AUR doesn't compile (yet), I took a prebuilt one from a Fedora repository, and unpacked it with bsdtar. Download the edk2.git-ovmf-x64*rpm file and get the OVMF-pure-efi.fd file from it (or alternatively OVMF_CODE-pure-efi.fd and OVMF_VARS-pure-efi.fd).

To run QEMU/KVM with the OVMF firmware, run it as:

qemu-system-x86_64 -enable-kvm -bios OVMF-pure-efi.fd -hda vfat:/usr/share/efitools/efi/

or just install some Linux from .iso. Don't forget, UEFI requires GPT.

Laptop

My Thinkpad T450s doesn't have key management in the firmware (the bios), so a third-party one needs to be used. efitools has KeyTool.efi, so I copied it and the *.auth files in /boot/keys and set it up to boot on next-boot with efibootmgr. In the firmware first choose the Enter Setup mode option, that will clear keys, and allow you to replace them. Save and reset, and now KeyTool.efi will be able to replace the db, KEK and PK certificates (in that order). I didn't just add the certificate because I wanted only my own keys there. If that is ok, reboot and enable SecureBoot.

On the next reboot KeyTool.efi can't run since it's not signed, so the boot will continue to my own combined and signed Linux image.

Don't forget to upgrade the firmware before starting. Bugs are often fixed and not even not documented.

References

BOOTDIR := /boot/Efi/Secure
CERTDIR := /root/secure-boot-keys
SIGNER := Damjan SecureBoot
KERNEL := /boot/vmlinuz-linux
INITRAMFS := /boot/intel-ucode.img /boot/initramfs-linux.img
EFISTUB := /usr/lib/systemd/boot/efi/linuxx64.efi.stub
BUILDDIR := _build
# or use uuidgen
GUUID := 1737c18a-2142-4723-a62d-36945aedc25e
all:
@echo Make targets: keys, build, sign, install, update, clean
build: $(BUILDDIR)/combined-boot.efi
sign: $(BUILDDIR)/combined-boot-signed.efi
update: $(BOOTDIR)/combined-boot-signed.efi
install: update
efibootmgr -c -l '\Efi\Secure\combined-boot-signed.efi' -L 'Linux SecureBoot'
clean:
rm -rf $(BUILDDIR)
$(BUILDDIR)/cmdline.txt:
@mkdir -p $(BUILDDIR)
echo -n `</proc/cmdline` > $(BUILDDIR)/cmdline.txt
$(BUILDDIR)/initramfs.img: $(INITRAMFS)
@mkdir -p $(BUILDDIR)
cat $(INITRAMFS) > $(BUILDDIR)/initramfs.img
$(BUILDDIR)/combined-boot.efi: $(BUILDDIR)/cmdline.txt $(BUILDDIR)/initramfs.img $(EFISTUB) /etc/os-release
objcopy \
--add-section .osrel=/etc/os-release --change-section-vma .osrel=0x20000 \
--add-section .cmdline=$(BUILDDIR)/cmdline.txt --change-section-vma .cmdline=0x30000 \
--add-section .linux=$(KERNEL) --change-section-vma .linux=0x40000 \
--add-section .initrd=$(BUILDDIR)/initramfs.img --change-section-vma .initrd=0x3000000 \
$(EFISTUB) $(BUILDDIR)/combined-boot.efi
$(BUILDDIR)/combined-boot-signed.efi: $(BUILDDIR)/combined-boot.efi $(CERTDIR)/db.key
sbsign --key $(CERTDIR)/db.key --cert $(CERTDIR)/db.crt --output $(BUILDDIR)/combined-boot-signed.efi \
$(BUILDDIR)/combined-boot.efi
$(BOOTDIR)/combined-boot-signed.efi: $(BUILDDIR)/combined-boot-signed.efi
@mkdir -p $(BOOTDIR)
cp $(BUILDDIR)/combined-boot-signed.efi $(BOOTDIR)/combined-boot-signed.efi
### setup keys.
### need only run once.
keys: $(CERTDIR)/PK.auth $(CERTDIR)/KEK.auth $(CERTDIR)/db.auth
$(CERTDIR)/%.crt: $(CERTDIR)/%.key;
$(CERTDIR)/%.key: COMMONNAME = $(SIGNER) $(basename $(notdir $@))
$(CERTDIR)/%.key:
@mkdir -p $(CERTDIR)
openssl req -new -x509 -newkey rsa:2048 -subj "/CN=$(COMMONNAME)/" \
-keyout $@ -out $(@:.key=.crt) -days 3650 -nodes -sha256
$(CERTDIR)/%.esl: $(CERTDIR)/%.key
cert-to-efi-sig-list -g $(GUUID) $(@:.esl=.crt) $@
$(CERTDIR)/PK.auth: $(CERTDIR)/PK.crt $(CERTDIR)/PK.esl
sign-efi-sig-list -k $(CERTDIR)/PK.key -c $(CERTDIR)/PK.crt PK $(CERTDIR)/PK.esl $(CERTDIR)/PK.auth
$(CERTDIR)/KEK.auth: $(CERTDIR)/PK.crt $(CERTDIR)/KEK.esl
sign-efi-sig-list -c $(CERTDIR)/PK.crt -k $(CERTDIR)/PK.key KEK $(CERTDIR)/KEK.esl $(CERTDIR)/KEK.auth
$(CERTDIR)/db.auth: $(CERTDIR)/KEK.crt $(CERTDIR)/db.esl
sign-efi-sig-list -c $(CERTDIR)/KEK.crt -k $(CERTDIR)/KEK.key db $(CERTDIR)/db.esl $(CERTDIR)/db.auth
.PRECIOUS: $(CERTDIR)/%.key $(CERTDIR)/%.crt $(CERTDIR)/%.esl
.PHONY: all clean
[Trigger]
Operation = Install
Operation = Upgrade
Type = Package
Target = linux
[Action]
When = PostTransaction
Exec = /usr/bin/make -C /root/secure-boot update
Depends = sbsigntools
Depends = make
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment