Skip to content

feat: enable ABUpdate for ACL - #28

Open
bfjelds (bfjelds) wants to merge 27 commits into
aclmainfrom
user/bfjelds/ab-verity-partuuid-uki-addons-20260708
Open

feat: enable ABUpdate for ACL#28
bfjelds (bfjelds) wants to merge 27 commits into
aclmainfrom
user/bfjelds/ab-verity-partuuid-uki-addons-20260708

Conversation

@bfjelds

@bfjelds bfjelds (bfjelds) commented Jul 9, 2026

Copy link
Copy Markdown
Member

Summary

Enables Trident-driven A/B updates on ACL images by giving /usr dm-verity a slot-agnostic boot path, installing/enabling Trident in the image, and expanding the ESP to support A/B UKI staging on arm64.

Changes

Verity: dedicated hash partitions + slot-agnostic boot

  • disk_layout_uki.json: add dedicated verity hash partitions HASH-A and HASH-B (type dps-usr-verity) paired to USR-A/USR-B via a new verity_hash cross-reference; renumber OEM/ROOT accordingly.
  • disk_util: add dps-usr-verity partition-type GUID resolution and the verity_hash layout key; add a separate-hash-partition verity mode (hash tree written to the dedicated partition instead of an inline hash-offset); emit the btrfs filesystem UUID and verity superblock UUID via new --fs_uuid/--verity_uuid flags; only the active (prioritized) slot is formatted at build time.
  • build_image_util.sh: capture the new UUID outputs and pass them to bootloader install.

UKI / systemd-boot (the boot path for ACL images). rpm/uki_install.sh:

  • The main UKI cmdline carries only slot-independent args (mount.usr=/dev/mapper/usr, root=LABEL=ROOT, ...).
  • Slot-specific verity identity is delivered via a systemd-stub addon (verity.addon.efi in <uki>.efi.extra.d/) built per slot with systemd.verity_usr_data=PARTUUID=<usr> and systemd.verity_usr_hash=PARTUUID=<hash> plus the slot root hash. Slot A is active by default; Trident switches slots by swapping the active addon. This lets one UKI boot either slot without re-signing.

GRUB (secondary bootloader path). grub_install.sh (declare --fs_uuid / --verity_uuid), rpm/grub_install.sh (read and inject the values), and rpm/grub.cfg (template @@FSUUID@@ / @@VERITYUUID@@): build the verity cmdline from systemd.verity_usr_data=UUID=<fs> and systemd.verity_usr_hash=UUID=<verity> instead of a hard-coded PARTUUID + hash-offset.

ESP capacity for A/B UKI staging

  • disk_layout_uki.json: increase the EFI System Partition from 128 MiB (262144 blocks) to 256 MiB (524288 blocks).
  • The arm64 UKI is approximately 82 MiB, compared with approximately 49 MiB on amd64. During an A/B update, the new UKI is staged on the ESP alongside the running UKI; the 128 MiB ESP runs out of space on arm64. The 256 MiB ESP accommodates two arm64 UKIs plus bootloader files and headroom, while remaining 4096-block aligned.

Trident enablement

  • rpm/package_catalog.yaml: add the trident package.
  • rpm/build_image_util.sh: enable tridentd.socket.

arm64 test-image boot reliability

  • vm_image_util.sh: add install_uki_timeout_addon, a UKI addon that raises systemd.default_device_timeout_sec=120. Scoped to the arm64 kola test image only; it fixes a CI-only QEMU-TCG emulation flake where udev cannot initialise the ESP/OEM/usr-verity devices within the default device timeout and the VM drops to an emergency shell. Production and amd64 boot behaviour are unchanged.

Docs

  • acl/docs/architecture.md: document the dedicated hash partition and slot-agnostic verity boot.

Required PRs

Validation

@bfjelds
bfjelds (bfjelds) changed the base branch from user/bfjelds/ab-trident-verity-hash-partitions-20260708 to aclmain July 13, 2026 20:38
@bfjelds bfjelds (bfjelds) changed the title feat: move verity kernel command line params into A/B PARTUUID-based UKI addons feat: enable ABUpdate for ACL Jul 13, 2026
@bfjelds
bfjelds (bfjelds) requested a lite review from Copilot July 14, 2026 18:09

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR enables Trident-driven A/B updates for Azure Container Linux (ACL) images by making /usr dm-verity boot slot-agnostic (primary UKI path via systemd-stub addons) and by updating the GRUB fallback cmdline construction to avoid hard-coding partition identity/inline hash offsets. It also adds Trident to the image and enables its socket.

Changes:

  • Introduce dedicated /usr dm-verity hash partitions (HASH-A / HASH-B) and plumb a verity_hash cross-reference through disk_util + the UKI disk layout.
  • Switch UKI/systemd-boot to a slot-independent main cmdline plus per-slot verity.addon.efi addons, and switch GRUB to UUID-based systemd.verity_usr_* parameters injected at build time.
  • Add trident to the RPM package catalog and enable tridentd.socket in the built image.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
build_library/rpm/uki_install.sh Reworks UKI cmdline to be slot-agnostic and adds generation of per-slot verity addons.
build_library/rpm/rpm_install.sh Fixes punctuation in a log message.
build_library/rpm/package_catalog.yaml Adds the trident RPM to the package set.
build_library/rpm/grub.cfg Updates GRUB verity cmdline template to use injected FS/verity UUIDs instead of inline hash-offset logic.
build_library/rpm/grub_install.sh Injects FS UUID + verity UUID into GRUB template during provisioning.
build_library/rpm/build_image_util.sh Enables tridentd.socket via a sockets.target wants symlink.
build_library/disk_util Adds dps-usr-verity type resolution, supports separate hash partitions, and emits FS/verity UUIDs.
build_library/disk_layout_uki.json Adds HASH-A/HASH-B partitions and links USR-A/USR-B to them via verity_hash.
build_library/build_image_util.sh Plumbs new UUID output files into bootloader install arguments.
acl/docs/architecture.md Updates architecture docs for dedicated hash partitions / slot-agnostic verity boot.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread build_library/build_image_util.sh
Comment thread build_library/rpm/grub_install.sh
Comment thread build_library/rpm/grub_install.sh
Comment thread build_library/rpm/uki_install.sh
Comment thread build_library/rpm/uki_install.sh
Comment thread build_library/disk_util Outdated
Comment thread acl/docs/architecture.md Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.

Comment thread build_library/disk_util Outdated
Comment thread build_library/rpm/uki_install.sh Outdated
bfjelds (bfjelds) added a commit that referenced this pull request Jul 14, 2026
Copilot re-review follow-ups on PR #28:

- disk_util: the --fs_uuid output was only written in separate-hash-partition
  mode, so legacy inline-verity layouts produced no FS UUID file and now hard-
  fail in grub.cfg generation. Collapse the two near-identical verity branches
  into a single contextlib.ExitStack path that reads the FS UUID in both modes,
  removing the duplicated veritysetup/blkid logic.
- rpm/uki_install.sh: correct the stale UUID-validation comment (grub_install.sh
  now dies rather than warns; uki_install does not pass UUIDs through itself).

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

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.

Comment thread build_library/disk_util
Comment thread build_library/rpm/grub_install.sh

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Comment thread build_library/rpm/uki_install.sh Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Comment thread build_library/disk_util

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated no new comments.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 5 comments.

Comment thread build_library/disk_util Outdated
Comment thread build_library/disk_util Outdated
Comment thread build_library/disk_util Outdated
Comment thread build_library/rpm/uki_install.sh
Comment thread build_library/rpm/grub_install.sh
bfjelds (bfjelds) added a commit that referenced this pull request Jul 20, 2026
disk_util: normalize root-hash/verity-uuid block indentation and use
`is not None` for root_hash.

uki_install.sh: validate UUID file content (non-empty), not just
existence, so a blank UUID file fails fast.

grub_install.sh: hard-fail when verity is enabled but no hash file is
provided, matching the sibling FS/verity UUID checks (a warn + empty
substitution produces an unbootable grub.cfg).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 72f4b84a-8f5c-4df6-9f8e-fb4850c520e2

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Comment thread build_library/rpm/uki_install.sh Outdated
bfjelds (bfjelds) and others added 14 commits August 14, 2026 14:52
disk_util: normalize root-hash/verity-uuid block indentation and use
`is not None` for root_hash.

uki_install.sh: validate UUID file content (non-empty), not just
existence, so a blank UUID file fails fast.

grub_install.sh: hard-fail when verity is enabled but no hash file is
provided, matching the sibling FS/verity UUID checks (a warn + empty
substitution produces an unbootable grub.cfg).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 72f4b84a-8f5c-4df6-9f8e-fb4850c520e2
The "verity enabled but no hash file" else-branch (warn + empty
@@USRHASH@@ substitution) predates this PR and is a working fallback.
Changing it to a hard die altered pre-existing behavior that was not
introduced by this change, so restore the original warn.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 72f4b84a-8f5c-4df6-9f8e-fb4850c520e2
uki_install.sh runs under switch_to_strict_mode (set -e + ERR trap). With
jq -e, a missing/null .uuid makes jq exit 1, tripping the ERR trap with a
generic error before the explicit [[ -z || == null ]] check can emit the
clear "Partition has no uuid field" die message. Dropping -e lets jq emit
null (exit 0) so the explicit check produces the intended message. Also
matches the sibling matches=jq-r lookup which omits -e.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 72f4b84a-8f5c-4df6-9f8e-fb4850c520e2
Install a UKI addon setting systemd.default_device_timeout_sec=120 on the
arm64 kola test image only (gated on ARCH=arm64 and INJECT_DOCKER_SYSEXT=true).

Under kola's parallel QEMU-TCG emulation on aarch64, CPU contention can keep
udev from initialising the ESP/OEM/usr-verity devices within the default
initrd device timeout, dropping the VM to an emergency shell. Raising the
timeout via a test-image ESP addon fixes this. Verified against a parallel=24
repro: 18/18 boot-to-login vs 18/18 emergency shells at baseline.

Scoped to the test image, so production and amd64 boot behaviour is unchanged;
the failure is a CI-only emulation artefact that never occurs on real hardware.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 72f4b84a-8f5c-4df6-9f8e-fb4850c520e2
- install_uki_timeout_addon: require exactly one UKI on the ESP (die on
  multiple) instead of silently picking one via sort -V | head, so the
  addon can never land in the wrong <uki>.efi.extra.d. Mirrors the
  single-UKI rule in rpm/uki_install.sh.
- RPM-mode tridentd.socket enable: hard-fail if tridentd.socket is absent.
  trident is required for ACL images, so a missing unit is a broken image;
  fail the build rather than leave a dangling wants-symlink.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 72f4b84a-8f5c-4df6-9f8e-fb4850c520e2
A present-but-empty verity_hash (e.g. "verity_hash": "") previously fell
through to legacy inline-verity mode instead of separate-hash-partition
mode, producing an invalid layout that is hard to debug. Strip and raise
InvalidLayout when the value is empty, consistent with the other malformed-
layout guards in this function.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 72f4b84a-8f5c-4df6-9f8e-fb4850c520e2
…ID wording

Under switch_to_strict_mode (set -e), a non-zero exit from `sudo ukify build`
aborted the script before the following existence check could emit the
intended die message or clean up the temp dir. Wrap every ukify invocation
(main UKI + firstboot/fips/kdump/debug/verity addons in uki_install.sh and
the timeout addon in vm_image_util.sh) in an explicit `if ! ...; then
rm -rf <tmp>; die; fi`, and clean up the temp dir on the produce-check path
too. Failures now surface with a clear message and no leaked temp dirs.

Also reword disk_util Verity's fs_uuid comment/exception from "btrfs FS UUID"
to "filesystem UUID": the blkid read is filesystem-agnostic. (ACL only
verities btrfs today, so this is wording-only.)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 72f4b84a-8f5c-4df6-9f8e-fb4850c520e2
sudo ukify build can leave root-owned artifacts in the mktemp temp dirs, so a
plain `rm -rf` could fail and (under strict mode) abort before the intended
die. Use `sudo rm -rf` for every temp-dir cleanup in the UKI/addon builders
(uki_install.sh main UKI + firstboot/fips/kdump/debug/verity addons, and the
timeout addon in vm_image_util.sh), matching the existing sudo rm -rf
convention used throughout build_library. Each temp dir is assigned via
mktemp -d immediately before use, so the rm target is always a real path.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 72f4b84a-8f5c-4df6-9f8e-fb4850c520e2
GRUB's disk_layout.json previously used the legacy single-partition
inline-hash verity scheme. The UKI-side changes in this branch switched
grub.cfg's verity cmdline construction to UUID=-based hash identification
(dropping hash-offset=), which only works when the hash tree lives on its
own independently-discoverable partition.

Mirror disk_layout_uki.json's structure: add dedicated HASH-A/HASH-B
partitions (dps-usr-verity type) cross-referenced via verity_hash, so
blkid/udev can resolve systemd.verity_usr_hash=UUID=<verity_uuid> the same
way UKI does. ACL-GRUB still cannot do Trident A/B updates (no addon-swap
equivalent), but the partitioning/verity mechanics are now consistent
between the two boot paths instead of silently broken on GRUB.

ESP stays at 128MiB (no AB-staging need on GRUB). BIOS-BOOT partition is
kept (shared with Portage/flatcar builds; RPM/GRUB mode already skips
installing to it).
install_uki_timeout_addon already works around a CI-only QEMU-TCG flake on
arm64 kola test images: heavy parallel-VM CPU contention can prevent udev
from initialising ESP/OEM/usr-verity devices within the default initrd
device timeout, dropping the VM to an emergency shell. That fix was
UKI-only (built as a signed .efi addon dropped into <uki>.efi.extra.d/).

GRUB has no addon mechanism, so add install_grub_timeout_override: since
grub.cfg is plain text (not signed/embedded like a UKI), patch the
already-written grub.cfg copies on the test image's ESP directly via sed,
appending systemd.default_device_timeout_sec=120 to the shared
linux_cmdline. Same scoping as the UKI addon: arm64 test image only
(INJECT_DOCKER_SYSEXT=true), never applied to production images or amd64.
Addresses Copilot review comment on PR #28: install_grub_timeout_override
logged \ patched\ unconditionally even if the sed substitution silently
did not match (e.g. linux_cmdline format changed), leaving the timeout
override ineffective without any signal. Now verifies the substitution
took effect and dies with a clear message if not.
…check

virsh net-info default | grep -q could report a false pipeline failure
under set -o pipefail (inherited via sourcing from validate_common.sh):
grep -q exits as soon as it matches, which can SIGPIPE virsh while it is
still writing, making the whole pipeline non-zero even though the
pattern matched. Capture output first, then grep the captured string.
trident-selinux pulls in policycoreutils-python-utils (for SELinux
module install tooling), which transitively depends on the audit
package. Azure Linux audit RPM enables+starts auditd via its %post
systemd preset, unlike portage-mode builds which ship a blanket
disable-all preset. Remove the enable symlink so RPM-mode images
match upstream ACL behavior: auditd installed but disabled by
default.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ACL images need trident-acl-agent (triggers AB-updates), not the
full trident package. Keep the auditd.service disable workaround in
build_image_util.sh regardless, since trident-acl-agent still
depends on trident, and defense-in-depth is warranted independent of
the trident.spec fix (microsoft/trident#734).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 14, 2026 14:52
@bfjelds
bfjelds (bfjelds) force-pushed the user/bfjelds/ab-verity-partuuid-uki-addons-20260708 branch from 4777b5e to a4ac985 Compare August 14, 2026 14:52

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated no new comments.

Suppressed comments (2)

acl/validate/validate_qemu.sh:57

  • validate_common.sh enables set -euo pipefail, so net_info="$(virsh net-info ...)" will cause the script to exit immediately if virsh net-info returns non-zero (e.g., transient libvirt error), instead of falling back to the existing start-network logic. Also, net_info should be local to avoid leaking a global into other sourced modules.
    # Capture output first instead of piping directly into grep: under `set -o pipefail`
    # (inherited from validate_common.sh, since this file is sourced not executed),
    # grep -q can exit as soon as it matches while virsh is still writing, causing a
    # SIGPIPE in virsh and a false pipeline failure even though the pattern matched.
    net_info="$(virsh net-info default 2>/dev/null)"
    if ! grep -q 'Active:.*yes' <<< "$net_info"; then

build_library/rpm/build_image_util.sh:602

  • The build failure message references a missing “trident RPM”, but this PR adds trident-acl-agent to package_catalog.yaml. Updating the message to name the expected RPM/package makes failures easier to debug.
    # Enable tridentd.socket - listens for trident API requests.
    # trident is a required component of ACL images: a missing unit means the
    # trident RPM was not installed, i.e. a broken image. Fail the build now
    # rather than leave a dangling wants-symlink and ship an image whose
    # provisioning socket never activates.
    if [[ ! -f "${root_fs_dir}/usr/lib/systemd/system/tridentd.socket" ]]; then
        die "tridentd.socket not found in image - trident RPM missing (trident is required for ACL)"
    fi

Copilot AI review requested due to automatic review settings August 24, 2026 18:01

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated no new comments.

Suppressed comments (2)

Previously missed (2) — in code that hasn't changed since the last review.

acl/validate/validate_qemu.sh:56

  • net_info is assigned without local inside ensure_libvirt_network(). Because this file is sourced (not executed), that can leak net_info into the caller’s/global scope and cause hard-to-trace variable collisions later in the validation run.
    net_info="$(virsh net-info default 2>/dev/null)"

build_library/disk_layout_uki.json:13

  • The ESP resize comment says "128 MiB -> 256 MiB", but this file’s previous value was already 192 MiB (393216 blocks). Updating the comment to match the actual delta avoids confusion when auditing layout history.
                "_comment": "128 MiB -> 256 MiB (blocks are 512-byte sectors). UKI boot keeps kernel+initramfs on the ESP; the acl-t UKI rebuilt by Image Customizer and Trident A/B update addons both need more headroom than stock.",

@bfjelds

Copy link
Copy Markdown
Member Author

it looks like github-acl-pr-validation doesn't use mantle ref provided (which would have fixes for verity failures).

github-mantle-pr-validation has both mantle and acr fixes.

azldevel has both mantle and acr fixes as well.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated 3 comments.

Comment thread build_library/disk_layout.json
Comment thread build_library/disk_layout.json
Comment thread build_library/disk_layout.json
- generate_grub_hashes.py: fix PCR policy allowlist entry for the OEM
  partition, which moved from gpt6 to gpt8 in this PR's renumbered
  disk_layout.json. The expected measured GRUB command is the OEM
  search result substituted into "source (hd0,gptN)/grub.cfg"; it must
  track the actual partition number or secure/measured boot policy
  checks will fail against real hardware measurements.

- prod_image_util.sh: create_prod_tar() resolve ROOT and USR-A by GPT
  PARTLABEL via blkid instead of hardcoded partition numbers (p9/p3),
  which broke when ROOT moved from partition 9 to 11 in this PR.

- disk_util / disk_layout.json: add an explicit legacy_verity_offset
  opt-in flag. When set on a verity_hash-bearing partition, Verity()
  now writes the hash tree to both the dedicated hash partition and
  the legacy inline offset within the data partition (same salt, so
  both copies are byte-identical and share one root hash). This keeps
  Portage/bootengine boots working via the existing hardcoded inline
  hash-offset activation, without requiring any bootengine or GRUB
  cmdline changes, while RPM/UKI-style consumers of the dedicated
  HASH-A/HASH-B partitions are unaffected. Verified end-to-end with
  veritysetup that both write targets produce an identical root hash
  and both independently pass veritysetup open.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 27, 2026 21:05

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated no new comments.

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

build_library/disk_layout_uki.json:13

  • This layout history and sizing rationale are inaccurate: the previous value was 393216 sectors (192 MiB), not 128 MiB, and the A/B space pressure comes from staging a second arm64 UKI rather than from the comparatively small addon files. Please correct the comment so future ESP sizing changes are based on the actual baseline.
                "_comment": "128 MiB -> 256 MiB (blocks are 512-byte sectors). UKI boot keeps kernel+initramfs on the ESP; the acl-t UKI rebuilt by Image Customizer and Trident A/B update addons both need more headroom than stock.",

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.

2 participants