From 99c47421bdb444e2eb799c1db1fe11a8ea91dee3 Mon Sep 17 00:00:00 2001 From: tranch Date: Tue, 25 Aug 2026 09:51:14 +0800 Subject: Document local live ISO reproduction path --- installer/build.sh | 18 ++++++++++++++++++ installer/configure-target-gpt-root.sh | 1 - 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/installer/build.sh b/installer/build.sh index 205f18c..dda0706 100755 --- a/installer/build.sh +++ b/installer/build.sh @@ -157,6 +157,24 @@ fi /usr/libexec/aerocore-configure-target-gpt-root \${target_root} %end +# Do not let stale Fedora boot files from an earlier installation collide with +# the bootc payload being installed. This matches the upstream Bazzite +# installer behavior; other vendor EFI directories are left untouched. +%pre-install --erroronfail --log=/tmp/aerocore-efi-cleanup.log +set -eux +efi_root= +for root in /mnt/sysroot /mnt/sysimage /var/mnt/sysroot /var/mnt/sysimage; do + if mountpoint -q \${root}/boot/efi; then + efi_root=\${root}/boot/efi + break + fi +done +if [ x\${efi_root} = x ]; then + echo No mounted EFI system partition found for cleanup >&2 + exit 1 +fi +rm -rf \${efi_root}/EFI/fedora +%end ostreecontainer --url=${INSTALL_IMAGE_PAYLOAD} --transport=containers-storage --no-signature-verification # The local containers-storage deployment is intentionally unverified because diff --git a/installer/configure-target-gpt-root.sh b/installer/configure-target-gpt-root.sh index 470821e..6c1a2d1 100755 --- a/installer/configure-target-gpt-root.sh +++ b/installer/configure-target-gpt-root.sh @@ -17,7 +17,6 @@ if [[ ! "${source_device}" =~ ^/dev/ ]]; then source_maj_min="$(findmnt -no MAJ:MIN --target "${target_root}")" source_device="$(lsblk -nrpo NAME,MAJ:MIN | awk -v wanted="${source_maj_min}" '$2 == wanted { print $1; exit }')" fi - case "${source_device}" in /dev/*[0-9]) ;; *) -- cgit v1.2.3 From ed4bd0d089d41c5d95fe74937acc2a779c6de6d4 Mon Sep 17 00:00:00 2001 From: tranch Date: Tue, 25 Aug 2026 10:44:18 +0800 Subject: fix(installer): validate ISO payload metadata Rewrite system-release atomically and reject stale or NUL-bearing payload metadata before it reaches Anaconda bootupd. --- .github/workflows/build-installer-iso.yml | 20 +++++++++++++++++++ Containerfile | 2 +- build_files/build.sh | 33 +++++++++++++++++++++++++++++++ build_files/image-info | 29 ++++++++++++++++++++++++++- installer/build.sh | 27 +++++++++++++++++++++++++ 5 files changed, 109 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-installer-iso.yml b/.github/workflows/build-installer-iso.yml index da3ef10..2402dfe 100644 --- a/.github/workflows/build-installer-iso.yml +++ b/.github/workflows/build-installer-iso.yml @@ -68,6 +68,26 @@ jobs: run: | sudo podman pull "${IMAGE_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}" + - name: Verify AeroCore install image identity + run: | + sudo podman run --rm --entrypoint /usr/bin/bash \ + "${IMAGE_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}" -ceu ' + system_release_target=$(readlink -f /etc/system-release) + if od -An -v -tx1 "$system_release_target" | awk '\''{ for (i = 1; i <= NF; i++) if ($i == "00") found = 1 } END { exit(found ? 0 : 1) }'\''; then + echo "NUL byte in ${system_release_target}" >&2 + exit 1 + fi + case "$(<"$system_release_target")" in + "AeroCore OS release $(rpm -E %fedora) ("*) ;; + *) + echo "Unexpected system-release: $(<"$system_release_target")" >&2 + exit 1 + ;; + esac + jq -e '\''.["image-name"] == "aerocore-os" and .["image-vendor"] == "aerocore-io"'\'' \ + /usr/share/ublue-os/image-info.json >/dev/null + ' + - name: Build installer ISO payload run: | mkdir -p output diff --git a/Containerfile b/Containerfile index be9737e..71bf132 100644 --- a/Containerfile +++ b/Containerfile @@ -9,7 +9,7 @@ COPY system_files /system_files FROM ${BASE_IMAGE} ARG BASE_IMAGE ARG IMAGE_NAME="aerocore-os" -ARG IMAGE_VENDOR="AeroCore-IO" +ARG IMAGE_VENDOR="aerocore-io" ARG IMAGE_BRANCH="stable" ARG VERSION_TAG="latest" ARG VERSION_PRETTY="latest" diff --git a/build_files/build.sh b/build_files/build.sh index 5618d20..dd58852 100755 --- a/build_files/build.sh +++ b/build_files/build.sh @@ -14,6 +14,39 @@ esac # Copy the contents of system_files/ of the git repo to / cp -avf "/ctx/system_files"/. / +# KDE's Deck presets use distributor-logo-steamdeck.svg for the Kickoff +# launcher. In the Bazzite base image that name (and the related aliases) +# may already exist as links to the upstream distributor-logo.svg. Install +# the AeroCore asset explicitly after the general overlay so the final image +# cannot silently retain the upstream Bazzite logo. +AEROCORE_DISTRIBUTOR_LOGO="/ctx/system_files/usr/share/icons/hicolor/scalable/places/distributor-logo.svg" +AEROCORE_ICON_DIR="/usr/share/icons/hicolor/scalable/places" +mkdir -p "${AEROCORE_ICON_DIR}" +for icon_name in \ + distributor-logo.svg \ + distributor-logo-steamdeck.svg \ + bazzite-logo.svg \ + start-here.svg; do + cp -f "${AEROCORE_DISTRIBUTOR_LOGO}" "${AEROCORE_ICON_DIR}/${icon_name}" +done + +# The Plasma launcher resolves the standard start-here name from both scalable +# and per-size places directories. Populate every existing Bazzite icon size +# in the installed payload, not only in the live installer image. +for icon_dir in /usr/share/icons/hicolor/*/places; do + [[ -d "${icon_dir}" ]] || continue + source_icon="${icon_dir}/bazzite-logo-icon.png" + if [[ -f "${source_icon}" ]]; then + cp -f "${source_icon}" "${icon_dir}/start-here.png" + cp -f "${source_icon}" "${icon_dir}/fedora-logo-icon.png" + fi +done + +if ! cmp -s "${AEROCORE_DISTRIBUTOR_LOGO}" "${AEROCORE_ICON_DIR}/distributor-logo.svg"; then + echo "AeroCore distributor logo was not installed into the image" >&2 + exit 1 +fi + # Default HDMI-CEC to putting the TV into standby when the system sleeps. # `ujust toggle-cec-sleep enable` changes this same setting at runtime. Keep # the default in the base image's config so users do not need to run the diff --git a/build_files/image-info b/build_files/image-info index bd66383..ae4406a 100755 --- a/build_files/image-info +++ b/build_files/image-info @@ -40,6 +40,20 @@ cat > "$IMAGE_INFO" <&2 + exit 1 +fi + +jq -e \ + --arg image_name "$IMAGE_NAME" \ + --arg image_vendor "$IMAGE_VENDOR" \ + '.["image-name"] == $image_name and .["image-vendor"] == $image_vendor and .["image-ref"] != null' \ + "$IMAGE_INFO" >/dev/null + # Keep the display name consistent across desktop settings and os-release. sed -i "s/^VARIANT_ID=.*/VARIANT_ID=$IMAGE_ID/" /usr/lib/os-release sed -i "s/^PRETTY_NAME=.*/PRETTY_NAME=\"$IMAGE_PRETTY_NAME\"/" /usr/lib/os-release @@ -56,7 +70,20 @@ sed -i "s/^ANSI_COLOR=.*/ANSI_COLOR=\"$LOGO_COLOR\"/" /usr/lib/os-release sed -i "/^REDHAT_BUGZILLA_PRODUCT=/d; /^REDHAT_BUGZILLA_PRODUCT_VERSION=/d; /^REDHAT_SUPPORT_PRODUCT=/d; /^REDHAT_SUPPORT_PRODUCT_VERSION=/d" /usr/lib/os-release sed -i "s|^VERSION_CODENAME=.*|VERSION_CODENAME=\"Deck\"|" /usr/lib/os-release -echo "$IMAGE_PRETTY_NAME release $FEDORA_VERSION (Deck)" > /etc/system-release +# /etc/system-release is normally a symlink to the RPM-owned fedora-release. +# Write through that link and verify the final bytes: bootupd uses this value as +# the efibootmgr label, where even a trailing NUL makes Anaconda fail before +# any %post installer hook can run. +SYSTEM_RELEASE="/etc/system-release" +SYSTEM_RELEASE_TARGET="$(readlink -f "$SYSTEM_RELEASE")" +EXPECTED_SYSTEM_RELEASE="$IMAGE_PRETTY_NAME release $FEDORA_VERSION (Deck)" +printf '%s\n' "$EXPECTED_SYSTEM_RELEASE" > "$SYSTEM_RELEASE" + +if ! cmp -s <(printf '%s\n' "$EXPECTED_SYSTEM_RELEASE") "$SYSTEM_RELEASE_TARGET"; then + echo "Failed to write a clean AeroCore system-release to ${SYSTEM_RELEASE_TARGET}" >&2 + od -An -v -tx1 "$SYSTEM_RELEASE_TARGET" >&2 + exit 1 +fi if grep -q '^BUILD_ID=' /usr/lib/os-release; then sed -i "s/^BUILD_ID=.*/BUILD_ID=\"$VERSION_PRETTY\"/" /usr/lib/os-release diff --git a/installer/build.sh b/installer/build.sh index dda0706..dd7b7f5 100755 --- a/installer/build.sh +++ b/installer/build.sh @@ -115,6 +115,33 @@ else podman pull "${INSTALL_IMAGE_PAYLOAD}" fi +# Refuse to build an ISO with a payload that still carries Bazzite release +# bytes. bootupd derives its EFI label from system-release during installation; +# a trailing NUL there makes efibootmgr fail before Anaconda can run %post. +podman run --rm --entrypoint /usr/bin/bash "${INSTALL_IMAGE_PAYLOAD}" -ceu ' + system_release=/etc/system-release + system_release_target=$(readlink -f "$system_release") + + if od -An -v -tx1 "$system_release_target" | awk '\''{ for (i = 1; i <= NF; i++) if ($i == "00") found = 1 } END { exit(found ? 0 : 1) }'\''; then + echo "Install payload has a NUL byte in ${system_release_target}" >&2 + od -An -v -tx1 "$system_release_target" >&2 + exit 1 + fi + + expected_prefix="AeroCore OS release $(rpm -E %fedora) (" + system_release_value=$(<"$system_release_target") + case "$system_release_value" in + "$expected_prefix"*) ;; + *) + echo "Install payload has an unexpected system-release: ${system_release_value@Q}" >&2 + exit 1 + ;; + esac + + jq -e '\''.["image-name"] == "aerocore-os" and .["image-vendor"] == "aerocore-io"'\'' \ + /usr/share/ublue-os/image-info.json >/dev/null +' + # Tell Anaconda to install the bootc container that was loaded above. mkdir -p /var/lib/rpm-state cat >> /usr/share/anaconda/interactive-defaults.ks < Date: Tue, 25 Aug 2026 11:41:20 +0800 Subject: fix(installer): validate payload on build host Move payload inspection out of the nested Podman build so local ISO builds do not require /dev/fuse. Normalize local registry references. --- installer/Containerfile | 2 +- installer/build.sh | 27 --------------------------- just_scripts/build-installer-iso.sh | 29 ++++++++++++++++++++++++++++- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/installer/Containerfile b/installer/Containerfile index 32b327e..a7f1d3b 100644 --- a/installer/Containerfile +++ b/installer/Containerfile @@ -2,7 +2,7 @@ # The image installed to disk is supplied separately as INSTALL_IMAGE_PAYLOAD. ARG BASE_IMAGE=ghcr.io/ublue-os/bazzite:stable -ARG IMAGE_VENDOR=AeroCore-IO +ARG IMAGE_VENDOR=aerocore-io ARG IMAGE_NAME=aerocore-os ARG INSTALL_IMAGE_PAYLOAD=ghcr.io/${IMAGE_VENDOR}/${IMAGE_NAME}:stable ARG INSTALL_IMAGE_PAYLOAD_ARCHIVE=/install-payload.oci diff --git a/installer/build.sh b/installer/build.sh index dd7b7f5..dda0706 100755 --- a/installer/build.sh +++ b/installer/build.sh @@ -115,33 +115,6 @@ else podman pull "${INSTALL_IMAGE_PAYLOAD}" fi -# Refuse to build an ISO with a payload that still carries Bazzite release -# bytes. bootupd derives its EFI label from system-release during installation; -# a trailing NUL there makes efibootmgr fail before Anaconda can run %post. -podman run --rm --entrypoint /usr/bin/bash "${INSTALL_IMAGE_PAYLOAD}" -ceu ' - system_release=/etc/system-release - system_release_target=$(readlink -f "$system_release") - - if od -An -v -tx1 "$system_release_target" | awk '\''{ for (i = 1; i <= NF; i++) if ($i == "00") found = 1 } END { exit(found ? 0 : 1) }'\''; then - echo "Install payload has a NUL byte in ${system_release_target}" >&2 - od -An -v -tx1 "$system_release_target" >&2 - exit 1 - fi - - expected_prefix="AeroCore OS release $(rpm -E %fedora) (" - system_release_value=$(<"$system_release_target") - case "$system_release_value" in - "$expected_prefix"*) ;; - *) - echo "Install payload has an unexpected system-release: ${system_release_value@Q}" >&2 - exit 1 - ;; - esac - - jq -e '\''.["image-name"] == "aerocore-os" and .["image-vendor"] == "aerocore-io"'\'' \ - /usr/share/ublue-os/image-info.json >/dev/null -' - # Tell Anaconda to install the bootc container that was loaded above. mkdir -p /var/lib/rpm-state cat >> /usr/share/anaconda/interactive-defaults.ks <&2 + exit 1 + fi + case "$(<"$system_release_target")" in + "AeroCore OS release $(rpm -E %fedora) ("*) ;; + *) + echo "Unexpected system-release: $(<"$system_release_target")" >&2 + exit 1 + ;; + esac + jq -e '\''.["image-name"] == "aerocore-os" and .["image-vendor"] == "aerocore-io"'\'' \ + /usr/share/ublue-os/image-info.json >/dev/null + ' +} + mkdir -p "${output_dir}" ensure_rootful_image +validate_install_payload "${podman_cmd[@]}" tag "${source_image}" "${payload}" "${podman_cmd[@]}" save --format oci-archive --output "${payload_archive}" "${payload}" -- cgit v1.2.3