Skip to content

Commit

Permalink
blog/content/post/2024-08-19-secureboot-freebsd.md: review corrections
Browse files Browse the repository at this point in the history
Signed-off-by: Filip Lewiński <[email protected]>
  • Loading branch information
filipleple committed Oct 14, 2024
1 parent ce9b5bc commit 4531a18
Showing 1 changed file with 104 additions and 85 deletions.
189 changes: 104 additions & 85 deletions blog/content/post/2024-08-19-secureboot-freebsd.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ cover: /covers/image-file.png
author: filip.lewinski
layout: post
published: true
date: 2024-08-19
date: 2024-10-14
archives: "2024"

tags:
Expand All @@ -34,28 +34,33 @@ categories:

UEFI Secure Boot is a security feature designed to ensure that only trusted,
signed software can execute during the boot process, protecting against malware
and unauthorized code execution. It verifies the signatures of bootloaders and
and unauthorized code execution. It verifies the signatures of bootloaders and
kernel binaries against trusted certificates embedded in the firmware.

### Linux

On Linux, Secure Boot is supported using a small, signed bootloader called "
shim," which is pre-signed by Microsoft. Shim loads the GRUB bootloader, which
is verified against a certificate embedded in shim, and GRUB then loads the
Linux kernel, also ensuring it is signed and trusted.
Most distributions took the approach of using a small, signed bootloader called
`shim`, which is pre-signed by Microsoft. Shim loads the bootloader, which
is verified against a certificate embedded in shim, and the bootloader then
loads the Linux kernel, also ensuring it is signed and trusted. This way, they
can sign their kernel themselves, instead of having to ask Microsoft to sign
each kernel update.

Please see [this blogpost](https://mjg59.dreamwidth.org/20303.html) for more
information on this specific subject.

### UEFI Secure Boot status on FreeBSD - brief summary

In short, the [FreeBSD wiki](https://wiki.freebsd.org/SecureBoot) proposes two
stages of Secure Boot implementation.
stages of Secure Boot implementation.

First would be to enable booting the system with Microsoft-signed shim and a
FreeBSD-signed EFI loader, while also offering users to generate their own
First would be to enable booting the system with Microsoft-signed shim and a
FreeBSD-signed EFI loader, while also offering users to generate their own
keys and certificates.

The second stage would involve having all of the drivers and kernel modules to
be signed and authorized, resulting in a locked-down system akin to Fedora's
implementation.
be signed and authorized, resulting in a locked-down system akin to Fedora's
implementation.

As of the time of writing this post, the first stage is mostly complete. It is
possible to create and sign a complete EFI executable containing the bootloader
Expand All @@ -71,102 +76,105 @@ The regular UEFI boot process on FreeBSD is as follows:
* `boot1.efi` in turn loads `loader.efi`
* Finally, `loader.efi` loads the kernel.

The goal scenario, according to the FreeBSD Foundation, would be that a
The target scenario, according to the FreeBSD Foundation, would be that a
cryptographic handshake would verify each transition within the process. That
is still under development, and the currently available option is a little less
modular.

It is possible to skip `boot1.efi` and combine the loader and the kernel into
It is possible to skip `boot1.efi` and combine the loader and the kernel into
one large package, which then can be signed with self-issued keys. The keys
are enrolled into Dasharo firmware, which boots the loader-kernel object
directly.

## Creating a Secure Boot - ready FreeBSD EFI executable

This guide is about migrating your current installation, not from scratch
Please note that this guide assumes that you have a working FreeBSD
installation that you wish to modify for Secure Boot support. Installation of
the system will not be covered here.

### Determine the loader of choice

There are currently several flavors of the `loader.efi` available. The original
one was written in
one was written in
[FORTH](https://hackaday.com/2017/01/27/forth-the-hackers-language/).
The 14.1 release defaults to a more modern Lua one, the final option being
`loader_simp` - a simplified C implementation. This guide will only provide
instructions on using the legacy FORTH loader and the current default.

### Copy the boot filesystem

```
$ mkdir ~/bootfs
$ mkdir ~/bootfs/boot
$ cd ~/bootfs/boot
$ cp -r /boot/kernel .
$ cp -r /boot/defaults .
$ cp /boot/loader.conf .
$ cp /boot/*.rc .
$ cp /boot/device.hints .
$ cp /boot/loader.help* .
```bash
mkdir ~/bootfs
mkdir ~/bootfs/boot
cd ~/bootfs/boot
cp -r /boot/kernel .
cp -r /boot/defaults .
cp /boot/loader.conf .
cp /boot/*.rc .
cp /boot/device.hints .
cp /boot/loader.help* .
```

#### Lua loader

```
$ mkdir lua
$ cp -r /boot/lua ./lua
```bash
mkdir lua
cp -r /boot/lua ./lua
```

#### FORTH loader

```
$ cp /boot/*.4th .
```bash
cp /boot/*.4th .
```

### Copy the fstab

```
$ mkdir ~/bootfs/etc
$ cp /etc/fstab ~/bootfs/etc/fstab
```bash
mkdir ~/bootfs/etc
cp /etc/fstab ~/bootfs/etc/fstab
```

### Create an image of the filesystem

```
$ cd ~/
$ makefs bootfs.img bootfs
```bash
cd ~/
makefs bootfs.img bootfs
```

determine the size of the boot filesystem. This will determine how much memory
Determine the size of the boot filesystem. This will determine how much memory
to reserve in the loader:

```bash
ls -l bootfs.img | awk '{print $5}'
```
$ ls -l bootfs.img | awk '{print $5}'
```
add a safety factor of a couple hundred bytes (~512) to this number, and record

Add a safety factor of a couple hundred bytes (~512) to this number, and record
it.

### Build the Loader With Extra Space

This step requires you to have the source code for your system. You might have
checked the `src` component during OS installation, then the `/usr/src/`
This step requires you to have the source code for your system. You might have
checked the `src` component during OS installation, then the `/usr/src/`
directory should be already populated. If not, clone the source code from
Github, substituting `14.1` with the release you are currently using:
GitHub, substituting `14.1` with the release you are currently using:

```
```bash
git clone -b releng/14.1 https://git.freebsd.org/src.git /usr/src
```

Now, recall your bootfs size and substitute ${BOOTFS_SIZE_PLUS_SAFETY} below:
Now, recall your bootfs size and substitute `${BOOTFS_SIZE_PLUS_SAFETY}` below:

```
$ cd /usr/src/stand
$ make MD_IMAGE_SIZE=${BOOTFS_SIZE_PLUS_SAFETY}
```bash
cd /usr/src/stand
make MD_IMAGE_SIZE=${BOOTFS_SIZE_PLUS_SAFETY}
```

### Embed bootfs image in the loader.efi

The build results will be available under `/usr/obj`.
The build results will be available under `/usr/obj`.

* If you have chosen to use the legacy FORTH loader, the path to the file that
* If you have chosen to use the legacy FORTH loader, the path to the file that
interests you should be similar to
`/usr/obj/usr/src/amd64.amd64/stand/efi/loader_4th/loader_4th.efi`.

Expand All @@ -175,75 +183,75 @@ The build results will be available under `/usr/obj`.

Copy the appropriate file and use the available embedding utility:

```
$ cp /usr/obj/${PATH_TO_LOADER}/loader.efi ~/
$ /usr/src/sys/tools/embed_mfs.sh ~/loader.efi ~/bootfs.img
```bash
cp /usr/obj/${PATH_TO_LOADER}/loader.efi ~/
/usr/src/sys/tools/embed_mfs.sh ~/loader.efi ~/bootfs.img
```

You might need to make `embed_mfs.sh` executable:

```
$ chmod +x /usr/src/sys/tools/embed_mfs.sh
```bash
chmod +x /usr/src/sys/tools/embed_mfs.sh
```

At this point, `loader.efi` is an UEFI-bootable binary, consisting of the
FreeBSD bootloader and kernel. The last remaining step to Secure Boot
At this point, `loader.efi` is an UEFI-bootable binary, consisting of the
FreeBSD bootloader and kernel. The last remaining step to Secure Boot
compatibility is generating keys and signing the binary.

## Signing the binary

FreeBSD includes a tool for signing EFI executables - `uefisign`. There is also
a utility provided to generate example keys and certificates needed to sign an
executable.
executable.

You can generate a self-signed certificate and use it to sign a binary as
You can generate a self-signed certificate and use it to sign a binary as
follows:

```
$ /usr/share/examples/uefisign/uefikeys testcert
$ uefisign -c testcert.pem -k testcert.key -o signed-loader.efi loader.efi
```bash
/usr/share/examples/uefisign/uefikeys testcert
uefisign -c testcert.pem -k testcert.key -o signed-loader.efi loader.efi
```

As earlier, make sure the script is marked as executable.

You should now have a `signed-loader.efi` and a `testkey.cer` file. The loader
file is what we're going to be booting from Dasharo, and the `.cer` file is our
custom certificate we need to enroll, so that Secure Boot can verify the
custom certificate we need to enroll, so that Secure Boot can verify the
loader's signature against it.

## Testing in QEMU

To spare yourself the trouble of recovering a broken OS installation, it is
To spare yourself the trouble of recovering a broken OS installation, it is
recommended to test the binary in an emulated environment.

Make sure you have an up-to-date installation of QEMU on your system, and get
the latest QEMU release of Dasharo
Make sure you have an up-to-date installation of QEMU on your system, and get
the latest QEMU release of Dasharo
[here](https://github.com/Dasharo/edk2/releases).

### Launching QEMU with an emulated filesystem

Create a directory for the QEMU firmware files, and a directory for the test
EFI files:

```
$ mkdir -p ~/qemu_test/efi
```bash
mkdir -p ~/qemu_test/efi
```

Download `OVMF_CODE_RELEASE.fd` and `OVMF_VARS_RELEASE.fd` and place them in
the `~/qemu_test` directory. Place the `signed-loader.efi` and `.cer` files in
the `~/qemu_test` directory. Place the `signed-loader.efi` and `.cer` files in
the `~/qemu_test/efi` directory:

```
$ cp ~/signed-loader.efi ~/qemu_test/efi/
$ cp ~/testcert.cer ~/qemu_test/efi/
$ cd ~/qemu_test
```bash
cp ~/signed-loader.efi ~/qemu_test/efi/
cp ~/testcert.cer ~/qemu_test/efi/
cd ~/qemu_test
```

Run Dasharo firmware in QEMU, mounting the EFI directory as a virtual fat
Run Dasharo firmware in QEMU, mounting the EFI directory as a virtual fat
drive:

```
$ qemu-system-x86_64 -machine q35,smm=on \
```bash
qemu-system-x86_64 -machine q35,smm=on \
-m 1G \
-global driver=cfi.pflash01,property=secure,value=on \
-drive if=pflash,format=raw,unit=0,file=OVMF_CODE_RELEASE.fd,readonly=on \
Expand Down Expand Up @@ -286,7 +294,7 @@ $ qemu-system-x86_64 -machine q35,smm=on \
1. Navigate to `Device Manager/Secure Boot Configuration`
![secure boot enabled](/img/sboot_enabled.png)

1. Enable `Custom Mode` and enter the `Advanced Secure Boot Keys Management`
1. Enable `Custom Mode` and enter the `Advanced Secure Boot Keys Management`
menu again.
![advanced secure boot keys management](/img/advanced_sboot.png)

Expand Down Expand Up @@ -315,24 +323,25 @@ $ qemu-system-x86_64 -machine q35,smm=on \
![EFI shell](/img/efi_shell.png)

1. Enter the filesystem and boot the signed-loader.efi
```

```sh
fs0:
signed-loader.efi
```

![EFI shell commands](/img/ush_commands.png)

1. Say hello to Beastie
![beastie](/img/beastie.png)


## Testing on hardware

Upon making sure that the loader-kernel object boots properly within an
Upon making sure that the loader-kernel object boots properly within an
emulation environment, we can proceed to Secure Booting FreeBSD on hardware.

To do that, follow the exact same steps as with emulation, the only differewnce
being that you will now need to upload the certificate to a USB drive and
enroll the certificate from there.
being that you will now need to upload the certificate to a USB drive and
enroll the certificate from there.

You will also need to place the `signed-loader.efi` file in your EFI partition,
and add it as a custom boot option.
Expand All @@ -343,13 +352,12 @@ and add it as a custom boot option.
* Navigate to `Boot Maintenance Manager/Boot Options/Add Boot Option`
* Choose the appropriate disk label. They might look intimidating, but you
should be able to find the correct one by looking for a familiar keyword.
If you have an NVME drive for example, there should be an entry with
If you have an NVME drive for example, there should be an entry with
`Pci(0x0,0x0)/NVMe`.
* Find the loader-kernel object. It should be located under
* Find the loader-kernel object. It should be located under
`<efi>/<freebsd>/loader.efi`
* Name the entry appropriately, confirm and save the changes.


## Troubleshooting

### Mountroot
Expand All @@ -365,7 +373,18 @@ to your `/boot/loader.conf`.

## Summary

We have learned how to set up UEFI Secure Boot on FreeBSD, a feature
that ensures only trusted, signed software can run during boot to protect
against malware. FreeBSD's implementation involves two stages: using
Microsoft's signed shim bootloader along with a FreeBSD EFI loader, and later
securing all kernel modules.

As our approach, we have chosen to bundle the bootloader and kernel into a
single EFI executable, then sign it using FreeBSD's uefisign tool. We have also
tested the setup in QEMU before deploying on hardware, ensuring that custom
keys are properly enrolled in Dasharo firmware to enable Secure Boot.
---
Unlock the full potential of your hardware and secure your firmware with the
experts at 3mdeb! If you're looking to boost your product's performance and
Expand Down

0 comments on commit 4531a18

Please sign in to comment.