Skip to content

Commit 8ef271d

Browse files
committed
blog: add sealed images key management post
Second post in the sealed images series, covering the UEFI Secure Boot key hierarchy, key generation, signing boot artifacts, and key enrollment methods. Also adds a link_checker skip for freedesktop.org which returns 418 to bots. Assisted-by: OpenCode (claude-opus-4-6) Signed-off-by: John Eckersberg <jeckersb@redhat.com>
1 parent 5ef2ca5 commit 8ef271d

2 files changed

Lines changed: 235 additions & 1 deletion

File tree

config.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@ feed_filenames = ["rss.xml"]
2828
#mode
2929
#output_dir
3030
#preserve_dotfiles_in_output
31-
#link_checker
31+
[link_checker]
32+
# freedesktop.org returns 418 to bots but works fine in browsers
33+
skip_prefixes = [
34+
"https://www.freedesktop.org/",
35+
]
3236
#slugify
3337
#search
3438
generate_sitemap = true
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
+++
2+
title = "Sealed images: key management for Secure Boot"
3+
date = 2026-04-28
4+
slug = "2026-apr-28-sealed-images-key-management"
5+
6+
[extra]
7+
author = "jeckersb"
8+
+++
9+
10+
# Sealed images: key management for Secure Boot
11+
12+
In the [previous post](@/blog/2026-apr-27-sealed-images-security-chain.md),
13+
we walked through the security chain that sealed images provide, from
14+
firmware through runtime file verification. That chain depends on
15+
cryptographic keys at every step. In this post, we'll cover the keys
16+
involved, how to generate them, and how they get enrolled into your
17+
system's firmware.
18+
19+
## The UEFI Secure Boot key hierarchy
20+
21+
UEFI Secure Boot uses a hierarchy of three key types, each serving a
22+
distinct role:
23+
24+
**Platform Key (PK)** -- The root of the Secure Boot trust hierarchy.
25+
There is exactly one PK. The PK controls who can modify the Secure
26+
Boot key databases. It is used to sign updates to the KEK database.
27+
When the PK is enrolled, the firmware transitions from Setup Mode to
28+
User Mode, enabling Secure Boot enforcement.
29+
30+
**Key Exchange Key (KEK)** -- One or more KEKs can be enrolled. A KEK
31+
is authorized to sign updates to the signature database (db). The
32+
KEK exists to allow delegation: the PK owner can authorize other
33+
parties to manage the set of trusted signing keys without giving them
34+
full control over the Secure Boot configuration.
35+
36+
**Signature Database (db)** -- The db contains the keys or hashes
37+
that the firmware uses to verify boot binaries. When the firmware
38+
loads a bootloader or UKI, it checks the binary's signature against
39+
the keys in the db. If the signature matches a db entry, the binary
40+
is allowed to execute.
41+
42+
In practice, many organizations will only need to generate a single
43+
set of these keys. The db key is the one that does the day-to-day
44+
work: it signs your UKIs and bootloader. The PK and KEK exist
45+
primarily to control who can modify the db.
46+
47+
If you already have Secure Boot keys provisioned in your
48+
infrastructure, you don't need to generate new ones. You only need
49+
access to a db key (or the ability to add your signing key to the
50+
existing db) in order to sign your UKIs. The rest of this post
51+
assumes you're starting from scratch, but the signing steps apply
52+
regardless of where your keys came from.
53+
54+
## Generating keys
55+
56+
The following commands generate a complete set of Secure Boot keys.
57+
These examples use `openssl` directly, but you should use whatever
58+
key management practices are appropriate for your organization.
59+
Hardware security modules (HSMs), vault systems, or other key
60+
management infrastructure may be more appropriate for production
61+
use.
62+
63+
First, generate a GUID to identify the key owner. This is embedded
64+
in the key enrollment data and can be any unique identifier:
65+
66+
```
67+
$ uuidgen --random > GUID.txt
68+
```
69+
70+
Generate the Platform Key:
71+
72+
```
73+
$ openssl req -newkey rsa:2048 -nodes -keyout PK.key \
74+
-new -x509 -sha256 -days 3650 \
75+
-subj '/CN=My Platform Key/' -out PK.crt
76+
$ openssl x509 -outform DER -in PK.crt -out PK.cer
77+
```
78+
79+
Generate the Key Exchange Key:
80+
81+
```
82+
$ openssl req -newkey rsa:2048 -nodes -keyout KEK.key \
83+
-new -x509 -sha256 -days 3650 \
84+
-subj '/CN=My Key Exchange Key/' -out KEK.crt
85+
$ openssl x509 -outform DER -in KEK.crt -out KEK.cer
86+
```
87+
88+
Generate the Signature Database key:
89+
90+
```
91+
$ openssl req -newkey rsa:2048 -nodes -keyout db.key \
92+
-new -x509 -sha256 -days 3650 \
93+
-subj '/CN=My Signature Database Key/' -out db.crt
94+
$ openssl x509 -outform DER -in db.crt -out db.cer
95+
```
96+
97+
Each command produces three files: a private key (`.key`), a PEM
98+
certificate (`.crt`), and a DER-encoded certificate (`.cer`). The
99+
private keys are what you need to protect. The certificates are
100+
public and are what gets enrolled into the firmware.
101+
102+
## Signing boot artifacts
103+
104+
With a db key in hand, you can sign the two boot binaries that need
105+
Secure Boot verification: the bootloader (systemd-boot) and the
106+
Unified Kernel Image.
107+
108+
Signing systemd-boot:
109+
110+
```
111+
$ sbsign --key db.key --cert db.crt \
112+
--output systemd-bootx64.efi.signed \
113+
/usr/lib/systemd/boot/efi/systemd-bootx64.efi
114+
```
115+
116+
The UKI signing process is more involved because the UKI embeds the
117+
composefs digest, which must be computed from the filesystem first.
118+
We'll cover building sealed images (including UKI generation and
119+
signing) in detail in the next post. For now, the important thing
120+
to know is that `ukify` (the tool that assembles UKIs) accepts the
121+
same `--secureboot-private-key` and `--secureboot-certificate`
122+
options and uses `sbsign` under the hood to sign the final EFI
123+
binary.
124+
125+
## Enrolling keys into firmware
126+
127+
The keys need to be enrolled into the UEFI firmware's Secure Boot
128+
database before the firmware will use them for verification. There
129+
are several ways to do this depending on your environment.
130+
131+
### Manual enrollment via firmware setup
132+
133+
Most UEFI firmware implementations provide a setup menu (accessed
134+
during early boot, typically via a key like F2 or Del) where Secure
135+
Boot keys can be managed. The DER-encoded certificates (`.cer`
136+
files) can be enrolled from a USB drive or the EFI System Partition.
137+
This is straightforward for individual systems but does not scale
138+
well.
139+
140+
### Programmatic enrollment for virtual machines
141+
142+
For virtual machines using OVMF (the open source UEFI firmware for
143+
QEMU/KVM), keys can be enrolled directly into the firmware variable
144+
store using the `virt-fw-vars` tool from the
145+
[virt-firmware](https://gitlab.com/kraxel/virt-firmware) project:
146+
147+
```
148+
$ GUID=$(cat GUID.txt)
149+
$ virt-fw-vars \
150+
--input /usr/share/edk2/ovmf/OVMF_VARS.fd \
151+
--secure-boot \
152+
--set-pk $GUID PK.crt \
153+
--add-kek $GUID KEK.crt \
154+
--add-db $GUID db.crt \
155+
-o OVMF_VARS_ENROLLED.fd
156+
```
157+
158+
This produces a firmware variable store with your keys pre-enrolled.
159+
When QEMU starts with this variable store, Secure Boot is active and
160+
will only accept binaries signed with your db key. This is
161+
particularly useful for testing and for VM-based deployments where
162+
you control the firmware image.
163+
164+
### Auto-enrollment via systemd-boot
165+
166+
systemd-boot supports automatic key enrollment when the firmware is
167+
in Setup Mode (no Platform Key enrolled). This works by placing
168+
signed UEFI authenticated variable files (`.auth` files) on the EFI
169+
System Partition.
170+
171+
Creating `.auth` files requires converting your certificates into
172+
UEFI signature lists and signing them with the appropriate parent
173+
key:
174+
175+
```
176+
$ GUID=$(cat GUID.txt)
177+
$ attr=NON_VOLATILE,RUNTIME_ACCESS,BOOTSERVICE_ACCESS,TIME_BASED_AUTHENTICATED_WRITE_ACCESS
178+
179+
# Create EFI signature lists
180+
$ sbsiglist --owner $GUID --type x509 --output PK.esl PK.cer
181+
$ sbsiglist --owner $GUID --type x509 --output KEK.esl KEK.cer
182+
$ sbsiglist --owner $GUID --type x509 --output db.esl db.cer
183+
184+
# Sign the variables (PK and KEK signed by PK, db signed by KEK)
185+
$ sbvarsign --attr $attr --key PK.key --cert PK.crt --output PK.auth PK PK.esl
186+
$ sbvarsign --attr $attr --key PK.key --cert PK.crt --output KEK.auth KEK KEK.esl
187+
$ sbvarsign --attr $attr --key KEK.key --cert KEK.crt --output db.auth db db.esl
188+
```
189+
190+
These `.auth` files can then be placed on the ESP where systemd-boot
191+
expects them. bootc can automate this: if you place the `.auth`
192+
files in your container image at
193+
`/usr/lib/bootc/install/secureboot-keys/<name>/`, bootc will copy
194+
them to the ESP during installation. On the next boot, systemd-boot
195+
can enroll them into the firmware.
196+
197+
The enrollment behavior is controlled by the `secure-boot-enroll`
198+
setting in `loader.conf`. See the
199+
[systemd-boot documentation](https://www.freedesktop.org/software/systemd/man/latest/loader.conf.html)
200+
for details on the available modes.
201+
202+
## Key security considerations
203+
204+
A few things worth keeping in mind:
205+
206+
**Protect your private keys.** The `.key` files are what allow
207+
signing boot artifacts. Anyone with access to your db private key
208+
can sign binaries that your firmware will trust. Store private keys
209+
with appropriate access controls, and consider using an HSM or key
210+
management system for production deployments.
211+
212+
**Key rotation.** The examples above use 10-year expiry (`-days
213+
3650`), which is generous. Plan for key rotation before your
214+
certificates expire. The UEFI key hierarchy makes this manageable:
215+
you can use your KEK to authorize a new db key without needing to
216+
re-enroll the PK.
217+
218+
**Separation of concerns.** In a CI/CD pipeline, the signing step
219+
should ideally be isolated from the build step. The build process
220+
produces an unsigned UKI, and a separate signing service (with access
221+
to the private key) signs it. This limits the blast radius if the
222+
build environment is compromised.
223+
224+
## What's next
225+
226+
With keys generated and an understanding of how they fit into the
227+
trust chain, the next post will walk through building a sealed image
228+
end-to-end: writing a Containerfile, computing the composefs digest,
229+
generating and signing the UKI, and producing a deployable container
230+
image.

0 commit comments

Comments
 (0)