-
Notifications
You must be signed in to change notification settings - Fork 6
sealing blog #40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
sealing blog #40
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 | ||
| 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 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would probably refactor this to say:
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 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This starts to get into bigger picture design, but for And for How about for now:
|
||
|
|
||
| ## 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. | ||
There was a problem hiding this comment.
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