Skip to content

stage2: rework /data persistence (XTS, meta_bg) + mkruntime tooling#1

Merged
HarryR merged 3 commits into
mainfrom
data-xts-meta_bg
Jul 14, 2026
Merged

stage2: rework /data persistence (XTS, meta_bg) + mkruntime tooling#1
HarryR merged 3 commits into
mainfrom
data-xts-meta_bg

Conversation

@HarryR

@HarryR HarryR commented Jul 12, 2026

Copy link
Copy Markdown
Collaborator

Summary

Reworks stage2 /data persistence and the payload-forging tooling. Net change from main:

  • /data: dm-crypt aes-xts-plain64 (AES-256-XTS) instead of the planned authenticated dm-integrity + AEAD. Authenticated encryption (no internal hash) makes a never-written sector EIO on read, and ext4 online-resize does read-modify-write of new-group metadata in the region it grows into, so the resize aborts unless the whole grow region is pre-wiped (O(volume), unviable on large volumes). XTS decrypts unwritten sectors to garbage, so resize needs no wipe. Trade: at-rest tamper garbles rather than being detected; media integrity is left to the storage layer, raw-disk adversary out of scope. Key drift still fails closed.
  • PID 1 owns failure: a fatal boot error logs, waits 60s, then ACPI power-offs, instead of a returning init panicking the kernel and spinning a vCPU.
  • meta_bg ext4 base for unbounded online growth (no resize= cap / journal bump).
  • mkruntime.sh built out: env knobs, tar-on-stdin / tar / dir / binary inputs (docker export as an example, not a dependency), <out> as a file or - for stdout, and --bootstrap to emit a ready-to-run packed stage2. Makefile pack uses it.

Verification

  • make ci green (fmt + clippy -D warnings + tests)
  • PIVOT_TEST=PASS (config passthrough, attest chain, resize, persist-across-reboot, ephemeral overlay)
  • GROW_TEST=PASS -- grew /data to ~12 GiB across a meta_bg boundary, no wipe, sparse hole reads zero

Review notes / open threads

  • meta_bg gives unbounded growth; resize_inode + a resize= cap is the bounded alternative if a hard ceiling is ever wanted.
  • The dm-verity salt is a single fixed value namespacing the whole tree (not per-node); a Merkle per-node domain-separation review is parked as a separate long-thought.
  • Backlog (not in this PR): real stage2 TPM quote, docker->erofs make front-end, aarch64, digest-pinned erofs-builder.

Draft for review.

🤖 Generated with Claude Code

/data: replace the planned authenticated dm-integrity + dm-crypt AEAD stack with
confidentiality-only dm-crypt aes-xts-plain64 (AES-256-XTS). Authenticated
encryption has no internal hash, so a never-written sector EIOs on read; ext4
online-resize does read-modify-write of new-group metadata (backup GDT via
ext4_setup_new_descs) in the region it grows into, so the resize aborts unless
the whole grow region is pre-wiped first -- O(volume), unacceptable on large
volumes. XTS decrypts unwritten sectors to garbage instead of erroring, so the
resize needs no wipe (the standard LUKS/dm-crypt pattern) and cold-boot cost is
independent of volume size. Trade: at-rest tampering garbles plaintext rather
than being cryptographically detected -- media integrity is left to the storage
layer (EBS et al.), and a raw-disk-write adversary is out of scope. Key drift
still fails closed (superblock decrypts to garbage -> no ext4 magic -> reinit).
64-byte TPM-derived key, new key label.

loader: as PID 1, own fatal boot errors instead of returning. A returning init
panics the kernel ("Attempted to kill init") and spins a vCPU at 100% forever
(in prod and the QEMU harness). Now: log, 60s grace for console capture, then a
clean ACPI power-off.

ext4 /data base: meta_bg (drop resize_inode) for unbounded online growth; no
resize= ceiling or journal bump needed. Verified growing to ~12 GiB across a
meta_bg boundary with no wipe.

mkruntime.sh: env knobs (SOURCE_DATE_EPOCH, DATA_SIZE_MB, ROOTFS_UUID,
VERITY_SALT) with reproducible defaults; rootfs input now accepts a tar stream
on stdin, a tar file, a directory, or a single binary (docker export documented
as an example, not a dependency); <out> is a file path or - for stdout; and
--bootstrap prepends the loader ELF to emit a ready-to-run packed stage2. The
Makefile pack target uses --bootstrap and forwards the knobs.

tests: pivot-init drops the tamper-detection check (property removed with the
AEAD stack); add grow-probe (large-volume meta_bg growth + sparse-hole). Prune
unused probe examples. make ci green; PIVOT_TEST=PASS and GROW_TEST=PASS.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@HarryR HarryR marked this pull request as ready for review July 13, 2026 18:03
…E restructure

Make lockboot:erofs-builder a one-shot CLI: bake tools/mkruntime.sh in as the
ENTRYPOINT, so packaging no longer needs a repo bind-mount -- mount just the loader
ELF, pipe a rootfs in on stdin, get the stage2 out on stdout. Drop coreutils
(busybox covers touch -d @epoch, stat -c, tar, install, awk); output stays
byte-identical (same root hash), image 20.3 -> 18.2 MB. Makefile pack passes args
straight to the entrypoint.

Restructure the README to lead with what it is and how to use it (the mkruntime
one-liner with the built bootstrap), and move how-it-boots / security model /
testing into a Technical details section.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
HarryR added a commit to lockboot/stage0 that referenced this pull request Jul 13, 2026
## What
Bring the stage0 build image and disk layout in line with the Fedora 44
chain bump, plus stable disk identity.

## Changes
- **rpm + gnupg** added to `Dockerfile.build` so the UKI build can
verify Fedora package GPG signatures (image parity with stage1, kept
byte-identical). No effect on compiled artifacts.
- **Stable lockboot GPT GUIDs + FAT volume-id.** `DISK_GUID` /
`PART_GUID` are now constant across releases (`4c4f434b-424f-4f54-...`,
"LOCKBOOT" + a role index), so the initial GPT is byte-stable (stable
pre-stage2 PCR5) and the boot disk is identifiable by a fixed GUID
rather than by hashing stage0.efi (which changed per release). This is
the stable identity stage2's `find_boot_device` can key on. (stage2
still rewrites the GPT at runtime to add p2/p3, which is why it excludes
PCR5 from its key binding.)
- **Pin `vaportpm-attest` to v0.3.0** (rev + version guard), matching
stage1.
- **Extract the shared `DOCKER_RUN` harness** into `build.mk` (same
refactor as stage1).

## Verification
- Reproducible: GUIDs/timestamps fixed; build output byte-stable.
- No change to the runtime boot path. Pairs with lockboot/stage1#19
(Fedora 44 kernel/stub) and lockboot/stage2#1.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Clean check-context names (no spaces/parens) so the uniform branch ruleset across
the lockboot repos can require the same names everywhere.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@HarryR HarryR merged commit fd1fa37 into main Jul 14, 2026
4 checks passed
@HarryR HarryR deleted the data-xts-meta_bg branch July 14, 2026 11:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant