Skip to content

engineering: support inline dm-verity in COSI-derived Host Configurations - #754

Draft
Paco Huelsz (frhuelsz) wants to merge 3 commits into
mainfrom
user/frhuelsz/inline-verity
Draft

engineering: support inline dm-verity in COSI-derived Host Configurations#754
Paco Huelsz (frhuelsz) wants to merge 3 commits into
mainfrom
user/frhuelsz/inline-verity

Conversation

@frhuelsz

@frhuelsz Paco Huelsz (frhuelsz) commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

🔍 Description

engineering. Adds support for inline dm-verity to Host Configuration derivation from COSI, so trident grpc-client stream-disk can install Azure Container Linux (ACL) images.

Inline verity stores the hash tree inside the same partition as the data, at a byte offset, rather than in a dedicated hash partition.

🤔 Rationale

Streaming a real ACL image failed during derivation:

Derived Host Configuration is invalid: Referrer 'verity-1' of kind
'verity-device' references target 'partition-2' more than once

Derivation resolved the verity hash partition purely by image path. With inline verity the verity entry points at the same image as the filesystem, so it emitted a VerityDevice whose data and hash devices were the same partition, which the storage graph rejected. Trident also had nowhere to record where the hash tree began, and never passed an offset to veritysetup open.

This was not a regression — inline verity was never supported. git log --all --grep=inline is empty and nothing referenced hashOffset. #673 made ACL work for A/B update, but that path never derives a Host Configuration; it reads the root hash from usrhash= on the UKI cmdline and mounts /usr directly, so it never had to model the layout.

Changes

  • Read hashOffset from COSI verity metadata. Image Customizer already emits this field for inline layouts — it is declared in its cosiapi package and was added upstream by azure-linux-image-tools#702 ("Add support for inline verity"), gated so it appears only when inline, and covered by an IC test. This change consumes what the producer already declares, and brings Trident's COSI reference up to date with it: hashOffset is now documented in Composable-OS-Image.md, the v1.2 JSON Schema, and the changelog. The addition is descriptive and backward compatible — the field is optional and absent for the ordinary separate-hash-partition layout, so every existing COSI stays valid.
  • Carry the offset on the OS image, not the Host Configuration. Like the root hash, the offset is a property of the image, so it is read from image metadata at servicing time. It does not appear in the Host Configuration or Host Status.
  • Allow a verity device to name the same partition as both data and hash device. hashDeviceId stays required, so the Host Configuration schema is completely unchangedhost-config-schema.json and the generated VerityDevice.md are byte-identical to main. The graph models the inline case as the single device it is rather than as a second reference to the same node, which leaves the duplicate-target and referrer-sharing invariants untouched. Verity referrer cardinality becomes 1..=2, and the hash partition-type cross-check is skipped when there is no distinct hash partition.
  • Pass --hash-offset to veritysetup open, so an inline-verity device is actually opened and verified during servicing rather than skipped.

Two documentation sets are treated differently on purpose. The COSI reference describes the format Trident consumes, so it documents hashOffset as a factual property of images IC already produces. The Host Configuration reference describes what users may configure, and inline dm-verity is not an officially supported configuration, so it is deliberately not documented there — host-config-schema.json and the generated VerityDevice.md are byte-identical to main. The only generated-doc change is the verity-device row of the referrer cardinality table in Storage-Rules.md (2..21..2), which states a reference count without mentioning inline verity.

Derivation fails with a clear error naming hashOffset if an image uses inline verity but does not say where the hash tree starts, instead of producing a device that cannot be opened.

📝 Checks

Manually validated end to end on a real ACL image (acl-base-3.0.20260706), installed via stream-disk from an Azure Linux installer ISO built with RPMs from this branch, in QEMU/KVM:

  • derivation succeeds; storage graph builds; full HostConfiguration::validate() passes
  • all five partitions streamed; Servicing completed successfully; reboot
  • verity activated during servicing and reported verified
  • installed disk boots with no install media attached: UEFI → GRUB → ACL kernel 6.6.143.1, with systemd activating dm-verity on /usr from the UKI cmdline (sysusr-usr.mount, /dev/mapper/usr, zero mount failures), 38 targets reached

First boot then stops at emergency.target because Ignition cannot reach Azure IMDS (169.254.169.254) — expected off-Azure for an image carrying flatcar.oem.id=azure, and downstream of everything this PR touches (verity was already mounted and verified before Ignition ran).

Before writing any code, the underlying veritysetup invocation was confirmed against the real disk image:

veritysetup open /dev/loopXp2 <name> /dev/loopXp2 270ed371... --hash-offset=1065345024
→ status: verified

Automated: 22 test suites pass workspace-wide, cargo fmt --check clean, cargo clippy introduces no new warnings. Adds test_derive_host_configuration_inner_inline_verity (pinning ACL's real usr partition type GUID, which exercises partition-type rules a generic Linux GUID would mask) and ..._inline_verity_missing_offset.

📌 Follow-ups

TODO:

  • The v1.2 schema requires osPackages to be an array, but real ACL COSI files set it to null and Trident already tolerates that (acl COSI files do not contain ospackages #707). Validating an ACL image's metadata against the checked-in schema fails on osPackages alone — a pre-existing divergence, unrelated to this PR and left alone here.

🗒️ Notes

Opened as a draft: functionally validated end to end, but wanted review on the graph modelling choice before marking ready.

The main design question is how inline verity is represented. Two alternatives were built and discarded:

  1. Drop the verity device entirely, mounting /usr read-only straight off the partition and leaving verity to systemd at boot. Smallest change and it validates, but Trident never verifies the image it just wrote. It also has a trap: the non-verity path drops the ro mount option, and resize is skipped only when is_read_only() || !fs_type.is_ext(). ACL survives on the btrfs clause alone; an ext4 inline-verity image would be resized and have its appended hash tree silently destroyed.
  2. hash_device_id: Option<BlockDeviceId>. Cleaner conceptually, but it is a Host Status wire-format change that rippled through ~30 files.

The approach here keeps the Host Configuration and Host Status untouched while still verifying at install time.

…ions

Azure Container Linux images use *inline* dm-verity: the hash tree lives
inside the same partition as the data, at a byte offset, rather than in a
dedicated hash partition. `trident grpc-client stream-disk` could not
install such an image.

Derivation resolved the verity hash partition purely by image path. With
inline verity the verity entry points at the same image as the filesystem,
so it produced a VerityDevice whose data and hash devices were the same
partition, which the storage graph then rejected:

    Derived Host Configuration is invalid: Referrer 'verity-1' of kind
    'verity-device' references target 'partition-2' more than once

Trident also had nowhere to record where the hash tree began, and never
passed an offset to `veritysetup open`.

Changes:

- Read `hashOffset` from COSI verity metadata. Image Customizer already
  emits this field for inline layouts (see its `cosiapi` package), so this
  only consumes what the producer declares; the COSI spec and schema are
  unchanged.
- Carry the offset on the OS image next to the root hash. Like the root
  hash, it is a property of the image rather than of the Host
  Configuration, so it is read from image metadata at servicing time and
  does not appear in the Host Configuration or Host Status.
- Allow a verity device to name the same partition as both its data and
  hash device. `hashDeviceId` remains required, so the Host Configuration
  schema is unchanged apart from documentation. The storage graph models
  the inline case as the single device it is, rather than as a second
  reference to the same node, which leaves the duplicate-target and
  referrer-sharing invariants untouched. Verity referrer cardinality
  becomes 1..=2, and the hash partition type cross-check is skipped when
  there is no distinct hash partition.
- Pass `--hash-offset` to `veritysetup open`, so an inline-verity device is
  actually opened and verified during servicing.

Derivation fails with a clear error naming `hashOffset` when an image uses
inline verity but does not say where the hash tree starts, rather than
producing a device that cannot be opened.

Tested against a real ACL image end to end: derivation, graph construction
and full Host Configuration validation pass; `stream-disk` writes all five
partitions; verity activates and reports `verified`; and the installed
system boots with systemd activating dm-verity on /usr from the UKI command
line.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
There may be pipelines that require an authorized user to comment /azp run to run.

Paco Huelsz (frhuelsz) and others added 2 commits August 27, 2026 14:27
Inline dm-verity is not an officially supported configuration, so the
public Host Configuration reference should not describe it.

Reverts the `hashDeviceId` doc comment to its original wording. The
generated `VerityDevice.md` and `host-config-schema.json` are now
byte-identical to main, so this change makes no alteration to Trident's
documented public API surface.

Support itself is unaffected: inline verity is still derived, opened with
`--hash-offset`, and verified. The behaviour is simply not advertised.

The only remaining generated-doc difference is the verity-device row of the
referrer cardinality table in Storage-Rules.md (2..2 becomes 1..2). That
table is generated from the cardinality rule itself and states a reference
count without mentioning inline verity; suppressing it would mean reverting
the rule and reintroducing the graph invariant exemptions this design
deliberately avoids.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Image Customizer has emitted `hashOffset` since azure-linux-image-tools#702
("Add support for inline verity"), where it is declared in its `cosiapi`
package and written only for images whose verity hash tree is stored inside
the data partition. COSI 1.2 documents describe the field nowhere, so the
spec has been behind its producer.

Document it: add `hashOffset` to the `VerityConfig` table, note that the
`image` field refers to the same image as the filesystem's own for inline
verity, add a sample, and record the field in the 1.2 changelog.

The addition is descriptive rather than a format change. `hashOffset` is
optional and absent for the ordinary separate-hash-partition layout, so
every existing COSI remains valid and consumers that ignore the field remain
correct.

Extends the schema gate with an inline-verity sample under
tests/cosi/metadata_samples/v1.2/valid/, plus a negative sample asserting
the offset cannot be negative. Verified with check-jsonschema, as the CI
workflow does; also confirmed the metadata of a real inline-verity image
validates against the updated schema.

Note this documents the COSI format Trident consumes. It does not present
inline verity as a supported Host Configuration: the Host Configuration
reference and its schema are unchanged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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