From fab2a5d19c06a333ad1c574641452783aabeabf6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 27 Aug 2026 05:32:35 -0400 Subject: [PATCH] fix(installer): enable PXE network DDI downloads Enable opt-in PXE installation using inst.ddi_url, with optional SHA256 verification and explicit target disk selection. Downloaded DDIs use temporary systemd-repart overrides; embedded installer-media behavior remains the default. Add curl, sed, zstd tooling to installer stack, and document the network flow. Fixes #14 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/skills/ddi-installer.md | 31 +++++++++++--- elements/installer/installer-stack.bst | 4 ++ elements/oci/bluefin-server-installer.bst | 52 +++++++++++++++++++++-- 3 files changed, 77 insertions(+), 10 deletions(-) diff --git a/docs/skills/ddi-installer.md b/docs/skills/ddi-installer.md index f6fdfcf..37f4b77 100644 --- a/docs/skills/ddi-installer.md +++ b/docs/skills/ddi-installer.md @@ -28,9 +28,9 @@ metadata: ## Architecture -The installer is offline, self-contained, and systemd-native. The OS DDI payload -(`bluefin-server-ddi.bst`) is embedded as a data partition on the installer -media at build time. No network access is required at install time. +The installer is systemd-native and offline by default. The OS DDI payload +(`bluefin-server-ddi.bst`) is embedded as a data partition on installer media +at build time, while PXE users may opt into downloading it over HTTPS. The installer UI is systemd's built-in `systemd-sysinstall` which provides a terminal-based interactive installation that: @@ -154,10 +154,29 @@ clients can add `unattended` to the installer command line, for example: systemd.unit=system-install.target console=tty0 console=ttyS0,115200 rw unattended ``` -The DDI is still required on `bluefin-installer-data`; standalone network DDI -fetching is not supported yet. Use the raw installer image for complete, +For offline installs, the DDI remains on `bluefin-installer-data`; PXE installs +may use `inst.ddi_url` instead. Use the raw installer image for complete, offline installation. +## PXE network installs + +PXE clients may boot standalone kernel and initrd with optional kernel parameters: + +- `inst.ddi_url=` downloads zstd-compressed DDI instead of using + embedded installer partition. +- `inst.ddi_sha256=` verifies download before touching target disk. +- `inst.target_disk=/dev/...` selects explicit target disk. + +Without `inst.ddi_url`, behavior remains unchanged and uses embedded DDI. +Network or checksum failures abort before partitioning. Example: + +```text +kernel bluefin-server-pxe-vmlinuz systemd.unit=system-install.target rw unattended \ + inst.ddi_url=https://pxe.example/ddi.raw.zst inst.ddi_sha256= +initrd bluefin-server-pxe-initrd.cpio.gz +boot +``` + ## Common Rationalizations | Rationalization | Reality | @@ -168,7 +187,7 @@ offline installation. | "Initrd archive tools (gzip, cpio) are in base-stack." | In FSDK 26.08, gzip and cpio are standalone components; elements packing or unpacking initrds must explicitly declare `components/gzip.bst` and `components/cpio.bst` in `build-depends`. | | "Use knuckle instead." | knuckle is deprecated in favor of native `systemd-sysinstall` (systemd 261+). | | "Hardcode `root=/dev/vda2` for QEMU." | Bare metal has different device names. Always use PARTUUID. | -| "Pull the DDI from the network at install time." | Network failures = broken installs. The DDI is embedded in the installer media. | +| "Pull the DDI from the network at install time." | Network pull is opt-in; failures abort before disk changes, while embedded media remains default. | | "Put the DDI in the initrd cpio." | The DDI is 2 GiB+. The initrd cpio step must run before the DDI is placed in `/layer`. | | "Store the DDI in the ESP (FAT32)." | FAT32 has a 4 GiB per-file limit. Use a separate XFS partition. | | "Add an 8 GiB minimum size floor to the DDI." | The rootfs is immutable. It never grows in-place. Content + overhead is enough. | diff --git a/elements/installer/installer-stack.bst b/elements/installer/installer-stack.bst index 3168b51..6766ed9 100644 --- a/elements/installer/installer-stack.bst +++ b/elements/installer/installer-stack.bst @@ -34,6 +34,10 @@ depends: - freedesktop-sdk.bst:components/xfsprogs.bst - freedesktop-sdk.bst:components/dosfstools.bst + # Network DDI fetching for PXE installs + - freedesktop-sdk.bst:components/curl.bst + - freedesktop-sdk.bst:components/sed.bst + - freedesktop-sdk.bst:components/zstd.bst # Kernel (installer boots from this; vmlinuz used as the UKI --linux= arg) - freedesktop-sdk.bst:components/linux.bst diff --git a/elements/oci/bluefin-server-installer.bst b/elements/oci/bluefin-server-installer.bst index a505db1..9338002 100644 --- a/elements/oci/bluefin-server-installer.bst +++ b/elements/oci/bluefin-server-installer.bst @@ -189,11 +189,58 @@ config: modprobe -q nvme_core || true udevadm settle --timeout=15 || true + CMDLINE="$(< /proc/cmdline)" + DDI_URL="" + DDI_SHA256="" + TARGET_DISK_OVERRIDE="" + for arg in ${CMDLINE}; do + case "${arg}" in + inst.ddi_url=*) DDI_URL="${arg#inst.ddi_url=}" ;; + inst.ddi_sha256=*) DDI_SHA256="${arg#inst.ddi_sha256=}" ;; + inst.target_disk=*) TARGET_DISK_OVERRIDE="${arg#inst.target_disk=}" ;; + esac + done + + # PXE installs use a downloaded raw DDI and a temporary repart override. + if [ -n "${DDI_URL}" ]; then + mkdir -p /run/installer /usr/lib/repart.sysinstall.d + echo "==> Fetching DDI from ${DDI_URL}..." + if command -v systemd-networkd-wait-online >/dev/null 2>&1; then + systemd-networkd-wait-online --timeout=60 || { + echo "ERROR: Network did not become ready for DDI download." >&2 + exit 1 + } + fi + curl --fail --silent --show-error --location --retry 3 \ + --output /run/installer/bluefin-server-ddi.raw.zst "${DDI_URL}" + if [ -n "${DDI_SHA256}" ]; then + printf '%s %s\n' "${DDI_SHA256}" /run/installer/bluefin-server-ddi.raw.zst \ + | sha256sum --check --status || { + echo "ERROR: Downloaded DDI SHA256 mismatch." >&2 + exit 1 + } + fi + zstd --decompress --rm /run/installer/bluefin-server-ddi.raw.zst \ + -o /run/installer/bluefin-server-ddi.raw + cp /usr/lib/repart.d/10-esp.conf /usr/lib/repart.sysinstall.d/ + sed 's#CopyBlocks=/dev/disk/by-partlabel/bluefin-installer-data#CopyBlocks=/run/installer/bluefin-server-ddi.raw#' \ + /usr/lib/repart.d/20-root-a.conf \ + > /usr/lib/repart.sysinstall.d/20-root-a.conf + cp /usr/lib/repart.d/30-var.conf /usr/lib/repart.sysinstall.d/ + fi + # Auto-detect target disk for unattended install (any raw disk of type "disk" that is not the installer, is not read-only, and has size > 0) TARGET_DISK="" INSTALLER_PART="$(readlink -f /dev/disk/by-partlabel/bluefin-installer-data 2>/dev/null || true)" + if [ -n "${TARGET_DISK_OVERRIDE}" ]; then + if [ ! -b "${TARGET_DISK_OVERRIDE}" ]; then + echo "ERROR: inst.target_disk is not a block device: ${TARGET_DISK_OVERRIDE}" >&2 + exit 1 + fi + TARGET_DISK="${TARGET_DISK_OVERRIDE}" + fi - while read -r name type ro size; do + while [ -z "${TARGET_DISK}" ] && read -r name type ro size; do [ "${type}" = "disk" ] || continue [ "${ro}" = "0" ] || continue [ "${size}" -gt 0 ] || continue @@ -247,9 +294,6 @@ config: fi fi } - - # Check for unattended in kernel command line - CMDLINE="$(< /proc/cmdline)" if [[ " ${CMDLINE} " == *" unattended "* ]]; then echo "==> Running in UNATTENDED mode..." if [ -n "${TARGET_DISK}" ]; then