Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
169 changes: 169 additions & 0 deletions content/blog/2026-apr-27-sealed-images-security-chain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
+++
title = "Sealed images: the security chain from firmware to filesystem"
date = 2026-04-27
slug = "2026-apr-27-sealed-images-security-chain"

[extra]
author = "jeckersb"
+++

# Sealed images: the security chain from firmware to filesystem

This is the first in a series of posts about bootc sealed images. In
this post we'll take a tour through the full security chain that makes
sealed images work, from the moment your system powers on through
every file read at runtime. Future posts will cover key management,
building sealed images, and deploying them to various environments.

## The gap

Many Linux systems in use today don't use
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks similar to some other content I helped edit and would like to change, see https://url.corp.redhat.com/1779017

Secure Boot or any sort of boot-time verification whatsoever. And
even on systems that do use Secure Boot, the verification stops at
the kernel. The bootloader confirms the kernel is signed, and then
the kernel loads an operating system that nobody has verified at all.
Every library loaded, every binary executed, every configuration file
read after that point runs on trust.

This is the gap. The boot chain is verified. The operating system is
not.

Sealed images close this gap by extending cryptographic verification
from the firmware all the way through every file in the operating
system. Not as a one-time check at boot, but continuously, at every
file read, for the entire lifetime of the system.

Let's walk through how each piece fits together.

## Secure Boot

Secure Boot is the foundation of the chain. It's a UEFI firmware
feature that ensures only signed code runs during the boot process.

The firmware maintains a database of trusted keys. When the system
powers on, the firmware loads the bootloader and checks its signature
against those keys. If the signature is valid, the bootloader runs.
If not, the system refuses to boot.

This is well-established technology and not new to sealed images.
What matters for our purposes is that Secure Boot gives us a root of
trust: a guarantee that the first piece of software to execute after
the firmware is something we explicitly chose to trust.

## Unified Kernel Images

Traditionally, the boot chain after Secure Boot looks something like
this: the signed bootloader loads a kernel, an initramfs, and a kernel
command line, often from separate files on a boot partition. The
bootloader may verify the kernel's signature, but the initramfs and
command line are typically not covered by any signature.

A Unified Kernel Image (UKI) changes this by bundling the kernel, the
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth linking to the spec here I think

initramfs, and the kernel command line into a single EFI binary. The
entire bundle is signed as one unit. This means the firmware (or a
signed bootloader like systemd-boot) can verify the whole thing in one
step.

This is important for sealed images because the kernel command line
is no longer an unverified input. Anything embedded in the command
line is covered by the same signature that covers the kernel itself.
As we'll see next, this is where the composefs digest lives.

## The composefs digest
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would probably refactor this to say:

## composefs: EROFS + overlayfs + fsverity

And frame it that way, e.g. the section here is saying that the digest is of the EROFS...


Here's where things get interesting.

When a sealed image is built, the entire operating system filesystem
is processed into a composefs image. composefs represents the filesystem as an
[EROFS](https://docs.kernel.org/filesystems/erofs.html) metadata image
which describes every file, directory, symlink, and permission in the
tree. The actual file contents are stored separately in a
content-addressed object store.

The EROFS image itself has an
[fs-verity](https://docs.kernel.org/filesystems/fsverity.html) digest:
a single cryptographic hash that covers the complete filesystem
description. Every filename, every permission bit, every symlink
target, every file content hash is captured by this one value.

This digest is then embedded in the UKI's kernel command line:

```
composefs=abc123def456... (128-character SHA-512 hex digest)
```

Because the UKI is signed, this digest is now part of the verified
boot chain. The firmware verifies the UKI signature, and the UKI
carries within it a cryptographic commitment to the exact filesystem
that should be mounted as the operating system.

## fs-verity
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And this section could explain briefly not just fsverity but also the overlayfs part.


fs-verity is a Linux kernel feature that provides per-file integrity
verification. When fs-verity is enabled on a file, the kernel
computes a cryptographic hash of the file's contents and stores a
hash tree alongside the file on disk. From that point on, every read
is verified at the block level -- the kernel checks only the blocks
being read, not the entire file, and caches the results so repeated
reads don't pay the cost again.

A key behavior to understand is what happens when verification
fails: the kernel returns an I/O error. The corrupted or tampered
data is never served to any process. The `open()` call on the file
succeeds (the file exists, after all), but `read()` on the corrupted
portion returns `EIO`. The data is simply unreadable.

For sealed images, every file in the content-addressed object store
has fs-verity enabled. The composefs EROFS image records the expected
fs-verity digest for each file it references. At mount time, the
kernel is told to enforce verification via the overlayfs
`verity=require` mount option. This means every file access goes
through two layers of verification: the EROFS image says "this file
should have digest X," and the kernel confirms that the actual file
on disk matches.

## Putting it all together

The full chain looks like this. Each step must succeed before the
next can proceed:

1. The firmware verifies the signed bootloader.
2. The bootloader verifies the signed Unified Kernel Image (UKI).
3. The initramfs (inside the verified UKI) reads the composefs
digest from the kernel command line (also inside the verified UKI).
4. The initramfs mounts the composefs image and verifies the EROFS
image's fs-verity digest matches the digest from the command line.
5. Every subsequent file read from the operating system is
individually verified by the kernel against the fs-verity digest
recorded in the EROFS metadata.

There is no point after firmware where unverified code executes or
unverified data is served. The chain is continuous.

## What sealed images cover

It's worth being precise about scope. The seal covers every file in
the immutable operating system image: the base OS, any packages you
added, your monitoring agents, security tools, application binaries.
Everything that was part of the image when it was built is sealed.

The seal does not cover `/etc` (writable configuration) or `/var`
(runtime state). This is deliberate. The seal protects the OS you
built. Configuration management and runtime security tools protect
the rest.
Comment on lines +152 to +153
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This starts to get into bigger picture design, but for /etc i think we should actually strongly encourage transient or readonly even.

And for /var, this is where PCR-locked LUKS can come into play (and/or dm-integrity).

How about for now:

By default, /etc and /var are mutable and persistent. It is possible to make /etc transient today, and this is a good idea if it matches your use case. More suggestions on this will be in the documentation.


## What's next

This post covered the architecture. In the next posts, we'll get
hands-on:

- **Key management**: generating Secure Boot keys, enrolling them,
and understanding the signing chain
- **Building sealed images**: writing a Containerfile, producing a
signed UKI, and verifying the result
- **Deploying sealed images**: installing to bare metal, virtual
machines, and cloud environments

The signing chain is fully under your control. You generate the keys,
you sign the images, you decide what your systems trust. The next
post will show you how.
Loading