Skip to content

Commit 5ef2ca5

Browse files
committed
blog: add sealed images security chain overview
First post in a series about bootc sealed images, covering the full trust chain from Secure Boot firmware through per-file fs-verity verification at runtime. Assisted-by: OpenCode (claude-opus-4-6) Signed-off-by: John Eckersberg <jeckersb@redhat.com>
1 parent 94a97b1 commit 5ef2ca5

1 file changed

Lines changed: 168 additions & 0 deletions

File tree

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
+++
2+
title = "Sealed images: the security chain from firmware to filesystem"
3+
date = 2026-04-27
4+
slug = "2026-apr-27-sealed-images-security-chain"
5+
6+
[extra]
7+
author = "jeckersb"
8+
+++
9+
10+
# Sealed images: the security chain from firmware to filesystem
11+
12+
This is the first in a series of posts about bootc sealed images. In
13+
this post we'll take a tour through the full security chain that makes
14+
sealed images work, from the moment your system powers on through
15+
every file read at runtime. Future posts will cover key management,
16+
building sealed images, and deploying them to various environments.
17+
18+
## The gap
19+
20+
Typically a default Linux install comes "unlocked", allowing you to
21+
be root and make persistent changes. While many distributions
22+
support Secure Boot, it's typically only the kernel that's signed.
23+
The cryptographic chain of trust from firmware doesn't cover the
24+
root filesystem or applications. Every library loaded, every binary
25+
executed, every configuration file read after the kernel boots runs
26+
on trust.
27+
28+
This is the gap. The boot chain may be verified. The operating
29+
system is not.
30+
31+
Sealed images close this gap by extending cryptographic verification
32+
from the firmware all the way through every file in the operating
33+
system. Not as a one-time check at boot, but continuously, at every
34+
file read, for the entire lifetime of the system.
35+
36+
Let's walk through how each piece fits together.
37+
38+
## Secure Boot
39+
40+
Secure Boot is the foundation of the chain. It's a UEFI firmware
41+
feature that ensures only signed code runs during the boot process.
42+
43+
The firmware maintains a database of trusted keys. When the system
44+
powers on, the firmware loads the bootloader and checks its signature
45+
against those keys. If the signature is valid, the bootloader runs.
46+
If not, the system refuses to boot.
47+
48+
This is well-established technology and not new to sealed images.
49+
What matters for our purposes is that Secure Boot gives us a root of
50+
trust: a guarantee that the first piece of software to execute after
51+
the firmware is something we explicitly chose to trust.
52+
53+
## Unified Kernel Images
54+
55+
Traditionally, the boot chain after Secure Boot looks something like
56+
this: the signed bootloader loads a kernel, an initramfs, and a kernel
57+
command line, often from separate files on a boot partition. The
58+
bootloader may verify the kernel's signature, but the initramfs and
59+
command line are typically not covered by any signature.
60+
61+
A [Unified Kernel Image (UKI)](https://uapi-group.org/specifications/specs/unified_kernel_image/)
62+
changes this by bundling the kernel, the initramfs, and the kernel
63+
command line into a single EFI binary. The entire bundle is signed
64+
as one unit. This means the firmware (or a signed bootloader like
65+
systemd-boot) can verify the whole thing in one step.
66+
67+
This is important for sealed images because the kernel command line
68+
is no longer an unverified input. Anything embedded in the command
69+
line is covered by the same signature that covers the kernel itself.
70+
As we'll see next, this is where the composefs digest lives.
71+
72+
## composefs: EROFS + overlayfs + fs-verity
73+
74+
Here's where things get interesting. composefs brings together
75+
three kernel technologies to provide cryptographic verification of
76+
the entire filesystem.
77+
78+
When a sealed image is built, the operating system filesystem is
79+
processed into an
80+
[EROFS](https://docs.kernel.org/filesystems/erofs.html) metadata
81+
image. This image describes every file, directory, symlink, and
82+
permission in the tree, but it does not contain the actual file
83+
contents. Instead, each file entry in the EROFS image records the
84+
[fs-verity](https://docs.kernel.org/filesystems/fsverity.html)
85+
digest of that file's contents. The actual file data is stored
86+
separately in a content-addressed object store, where each file has
87+
fs-verity enabled by the kernel.
88+
89+
At mount time, the kernel stitches these pieces together using
90+
[overlayfs](https://docs.kernel.org/filesystems/overlayfs.html).
91+
The EROFS image serves as a metadata layer, and the object store
92+
provides the file data. The overlayfs `verity=require` mount option
93+
tells the kernel to enforce that every file served through the mount
94+
has an fs-verity digest matching what the EROFS metadata expects.
95+
96+
fs-verity itself is a kernel feature that provides per-file integrity
97+
verification. When fs-verity is enabled on a file, the kernel
98+
computes a cryptographic hash of the file's contents and stores a
99+
hash tree alongside the file on disk. From that point on, every
100+
read is verified at the block level -- the kernel checks only the
101+
blocks being read, not the entire file, and caches the results so
102+
repeated reads don't pay the cost again.
103+
104+
A key behavior to understand is what happens when verification
105+
fails: the kernel returns an I/O error. The corrupted or tampered
106+
data is never served to any process. The `open()` call on the file
107+
succeeds (the file exists, after all), but `read()` on the corrupted
108+
portion returns `EIO`. The data is simply unreadable.
109+
110+
The EROFS metadata image itself also has an fs-verity digest: a
111+
single cryptographic hash that covers the complete filesystem
112+
description. This is the composefs digest. It is embedded in the
113+
UKI's kernel command line:
114+
115+
```
116+
composefs=abc123def456... (128-character SHA-512 hex digest)
117+
```
118+
119+
Because the UKI is signed, this digest is now part of the verified
120+
boot chain. The firmware verifies the UKI signature, and the UKI
121+
carries within it a cryptographic commitment to the exact filesystem
122+
that should be mounted as the operating system.
123+
124+
## Putting it all together
125+
126+
The full chain looks like this. Each step must succeed before the
127+
next can proceed:
128+
129+
1. The firmware verifies the signed bootloader.
130+
2. The bootloader verifies the signed Unified Kernel Image (UKI).
131+
3. The initramfs (inside the verified UKI) reads the composefs
132+
digest from the kernel command line (also inside the verified UKI).
133+
4. The initramfs mounts the composefs image and verifies the EROFS
134+
image's fs-verity digest matches the digest from the command line.
135+
5. Every subsequent file read from the operating system is
136+
individually verified by the kernel against the fs-verity digest
137+
recorded in the EROFS metadata.
138+
139+
There is no point after firmware where unverified code executes or
140+
unverified data is served. The chain is continuous.
141+
142+
## What sealed images cover
143+
144+
It's worth being precise about scope. The seal covers every file in
145+
the immutable operating system image: the base OS, any packages you
146+
added, your monitoring agents, security tools, application binaries.
147+
Everything that was part of the image when it was built is sealed.
148+
149+
By default, `/etc` and `/var` are mutable and persistent. It is
150+
possible to make `/etc` transient today, and this is a good idea if
151+
it matches your use case. More suggestions on this will be in the
152+
documentation.
153+
154+
## What's next
155+
156+
This post covered the architecture. In the next posts, we'll get
157+
hands-on:
158+
159+
- **Key management**: generating Secure Boot keys, enrolling them,
160+
and understanding the signing chain
161+
- **Building sealed images**: writing a Containerfile, producing a
162+
signed UKI, and verifying the result
163+
- **Deploying sealed images**: installing to bare metal, virtual
164+
machines, and cloud environments
165+
166+
The signing chain is fully under your control. You generate the keys,
167+
you sign the images, you decide what your systems trust. The next
168+
post will show you how.

0 commit comments

Comments
 (0)