From a4abd238de14ddfcf4751f9863f030a02c511a4d Mon Sep 17 00:00:00 2001 From: cproudlock Date: Sat, 14 Feb 2026 10:53:26 -0500 Subject: [PATCH] Harden cloud-init disable and rebuild ISO properly in build-usb Autoinstall user-data now disables cloud-init in multiple stages (late-commands + runcmd + systemd masks) to prevent post-install hangs. Also disables networkd-wait-online for air-gapped networks. build-usb.sh switched from in-place ISO patching to full extract and rebuild with xorriso mkisofs for reliable UEFI boot. Co-Authored-By: Claude Opus 4.6 --- autoinstall/user-data | 29 ++++++++++++++++------- build-usb.sh | 55 +++++++++++++++++++++++++++++++------------ 2 files changed, 61 insertions(+), 23 deletions(-) diff --git a/autoinstall/user-data b/autoinstall/user-data index 75137d6..d35ecab 100644 --- a/autoinstall/user-data +++ b/autoinstall/user-data @@ -35,7 +35,7 @@ autoinstall: identity: hostname: pxeserver username: pxe - password: "$6$rounds=656000$TpsuBw0N85085mpx$KtKsCwFlowg4NY41gUqx5ljef8cJ8uPFfgg43MyCPWByfXkhM5XushcdtkNps6lKeQFQZtli/QU.s52AUc7XC." + password: '$6$8AerqUockJh6ycgl$HJFBYjiFqXpzgcU9edto4CMrnaDpEX71Epin.kNTpj57GVjimIDHhcQs0AC4tmkEkKUaj.S/55wsBfMsV0KC71' # Enable SSH ssh: @@ -44,7 +44,7 @@ autoinstall: # Installer-stage late commands late-commands: - # Install deb packages from CIDATA USB + # Install deb packages from CIDATA USB, then disable cloud-init - | curtin in-target --target=/target -- bash -c ' mkdir -p /mnt/cidata @@ -61,11 +61,25 @@ autoinstall: fi umount /mnt/cidata fi + # Disable cloud-init AFTER dpkg (dpkg may overwrite earlier disables) + mkdir -p /etc/cloud + touch /etc/cloud/cloud-init.disabled + ln -sf /dev/null /etc/systemd/system/cloud-init.service + ln -sf /dev/null /etc/systemd/system/cloud-init-local.service + ln -sf /dev/null /etc/systemd/system/cloud-config.service + ln -sf /dev/null /etc/systemd/system/cloud-final.service + ln -sf /dev/null /etc/systemd/system/cloud-init.target + # Disable networkd-wait-online (no gateway on air-gapped network, causes 2min hang) + ln -sf /dev/null /etc/systemd/system/systemd-networkd-wait-online.service ' - # Create first-boot.sh + # Create first-boot.sh + disable cloud-init (in same block we know works) - | curtin in-target --target=/target -- bash -c ' + mkdir -p /etc/cloud/cloud.cfg.d + echo "datasource_list: [None]" > /etc/cloud/cloud.cfg.d/99-nocloud.cfg + touch /etc/cloud/cloud-init.disabled + ln -sf /dev/null /etc/systemd/system/systemd-networkd-wait-online.service cat <<"EOF" > /opt/first-boot.sh #!/bin/bash CIDATA_DEV=$(blkid -L CIDATA) @@ -102,13 +116,12 @@ autoinstall: ' - curtin in-target --target=/target -- chmod +x /etc/rc.local - # Disable cloud-init on the installed system (no longer needed after autoinstall) - - curtin in-target --target=/target -- touch /etc/cloud/cloud-init.disabled - - curtin in-target --target=/target -- dpkg --configure -a - - curtin in-target --target=/target -- apt-get remove -y cloud-init cloud-guest-utils - user-data: disable_root: false + runcmd: + - touch /etc/cloud/cloud-init.disabled + - systemctl disable cloud-init.service cloud-init-local.service cloud-config.service cloud-final.service + - systemctl mask cloud-init.service cloud-init-local.service cloud-config.service cloud-final.service refresh-installer: update: no diff --git a/build-usb.sh b/build-usb.sh index 08c12bd..cfc8ffe 100755 --- a/build-usb.sh +++ b/build-usb.sh @@ -103,25 +103,50 @@ for part in "${USB_DEV}"*; do umount "$part" 2>/dev/null || true done -# --- Write ISO to USB --- -echo "[2/6] Writing Ubuntu ISO to $USB_DEV (this may take several minutes)..." +# --- Rebuild ISO with 'autoinstall' kernel parameter --- +echo "[2/6] Rebuilding ISO with autoinstall kernel parameter..." -# Patch ISO to add 'autoinstall' kernel parameter (skips confirmation prompt) -PATCHED_ISO=$(mktemp /tmp/ubuntu-autoinstall-XXXXXX.iso) -cp "$ISO_PATH" "$PATCHED_ISO" -PATCHED_GRUB=$(mktemp /tmp/grub-XXXXXX.cfg) -xorriso -osirrox on -indev "$ISO_PATH" -extract /boot/grub/grub.cfg "$PATCHED_GRUB" 2>/dev/null -sed -i 's|linux\t/casper/vmlinuz ---|linux\t/casper/vmlinuz autoinstall ---|' "$PATCHED_GRUB" -sed -i 's/^set timeout=30/set timeout=5/' "$PATCHED_GRUB" -xorriso -indev "$PATCHED_ISO" -outdev "$PATCHED_ISO" \ - -map "$PATCHED_GRUB" /boot/grub/grub.cfg \ - -boot_image any replay 2>/dev/null -echo " Patched GRUB: added 'autoinstall' kernel param, reduced timeout to 5s" +ISO_WORK=$(mktemp -d) +7z -y x "$ISO_PATH" -o"$ISO_WORK/iso" >/dev/null 2>&1 +mv "$ISO_WORK/iso/[BOOT]" "$ISO_WORK/BOOT" +chmod -R u+w "$ISO_WORK/iso" -ISO_SIZE=$(stat -c%s "$PATCHED_ISO") +# Patch grub.cfg: add 'autoinstall' to kernel cmdline, reduce timeout +sed -i 's|linux\t/casper/vmlinuz ---|linux\t/casper/vmlinuz autoinstall ---|' "$ISO_WORK/iso/boot/grub/grub.cfg" +sed -i 's/^set timeout=30/set timeout=5/' "$ISO_WORK/iso/boot/grub/grub.cfg" + +PATCHED_ISO="$ISO_WORK/patched.iso" +cd "$ISO_WORK/iso" +xorriso -as mkisofs -r \ + -V 'Ubuntu-Server 24.04.3 LTS amd64' \ + -o "$PATCHED_ISO" \ + --grub2-mbr ../BOOT/1-Boot-NoEmul.img \ + --protective-msdos-label \ + -partition_cyl_align off \ + -partition_offset 16 \ + --mbr-force-bootable \ + -append_partition 2 28732ac11ff8d211ba4b00a0c93ec93b ../BOOT/2-Boot-NoEmul.img \ + -appended_part_as_gpt \ + -iso_mbr_part_type a2a0d0ebe5b9334487c068b6b72699c7 \ + -c '/boot.catalog' \ + -b '/boot/grub/i386-pc/eltorito.img' \ + -no-emul-boot \ + -boot-load-size 4 \ + -boot-info-table \ + --grub2-boot-info \ + -eltorito-alt-boot \ + -e '--interval:appended_partition_2:::' \ + -no-emul-boot \ + -boot-load-size 10160 \ + . 2>/dev/null +cd "$SCRIPT_DIR" +echo " ISO rebuilt with 'autoinstall' kernel param and 5s GRUB timeout" + +echo " Writing patched ISO to $USB_DEV..." dd if="$PATCHED_ISO" of="$USB_DEV" bs=4M status=progress oflag=sync sync -rm -f "$PATCHED_ISO" "$PATCHED_GRUB" +ISO_SIZE=$(stat -c%s "$PATCHED_ISO") +rm -rf "$ISO_WORK" # --- Find the end of the ISO to create CIDATA partition --- echo "[3/6] Creating CIDATA partition after ISO data..."