Initial Commit
This commit is contained in:
Executable
+115
@@ -0,0 +1,115 @@
|
||||
#!/bin/bash
|
||||
# =====================================================================
|
||||
# Builds the frozen Ubuntu base image, unattended, from the stock ISO.
|
||||
#
|
||||
# sudo ./build.sh # ~25 minutes, mostly apt
|
||||
#
|
||||
# HOW THE INSTALL IS MADE UNATTENDED
|
||||
#
|
||||
# Subiquity will read an autoinstall config from a NoCloud volume, but it
|
||||
# stops to ask for confirmation unless `autoinstall` is on the kernel
|
||||
# command line. There is no way to pass a kernel argument to a CD-ROM, so
|
||||
# the ISO is repacked with that one word added to its grub menu entry.
|
||||
# The alternative - booting the ISO's kernel directly with -kernel - was
|
||||
# rejected because it bypasses OVMF, and an image installed outside UEFI
|
||||
# will not boot under owuivm-launch, which uses it.
|
||||
#
|
||||
# The repack is `xorriso ... -boot_image any replay`, which copies the
|
||||
# original boot records across verbatim. Anything else produces an ISO
|
||||
# that is subtly not bootable, usually only under UEFI, usually only on
|
||||
# the second attempt.
|
||||
#
|
||||
# THE BUILD RUNS AS owuivm ON PURPOSE
|
||||
#
|
||||
# Same uid, same egress filter, same everything as a real session. If the
|
||||
# sandbox's network rules would break `apt`, this is where that is found
|
||||
# - not three phases later with an agent in the loop to confuse it.
|
||||
# =====================================================================
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$(readlink -f "$0")")"
|
||||
|
||||
[ "$(id -u)" -eq 0 ] || { echo "run as root" >&2; exit 1; }
|
||||
|
||||
ISO_DIR=/var/lib/owuivm/iso
|
||||
BASE_DIR=/var/lib/owuivm/base
|
||||
WORK=$(mktemp -d /var/tmp/owuivm-build.XXXXXX)
|
||||
trap 'rm -rf "$WORK"' EXIT
|
||||
|
||||
SRC_ISO="$ISO_DIR/$(awk '{print $1}' "$ISO_DIR/ubuntu.manifest")"
|
||||
AUTO_ISO="$WORK/ubuntu-autoinstall.iso"
|
||||
SEED_ISO="$WORK/seed.iso"
|
||||
DISK="$BASE_DIR/ubuntu.qcow2"
|
||||
VARS="$BASE_DIR/ubuntu-OVMF_VARS.fd"
|
||||
LOG=/var/log/owuivm-build-ubuntu.log
|
||||
|
||||
[ -f "$SRC_ISO" ] || { echo "no source ISO - run ../fetch.sh ubuntu first" >&2; exit 1; }
|
||||
|
||||
if [ -e "$DISK" ]; then
|
||||
echo "$DISK already exists."
|
||||
echo "Refusing to overwrite a base image: unfreeze and edit it through the"
|
||||
echo "maintenance tool, or delete it deliberately and re-run."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "==> patching the ISO's boot menu"
|
||||
xorriso -osirrox on -indev "$SRC_ISO" -extract /boot/grub/grub.cfg "$WORK/grub.cfg" >/dev/null 2>&1
|
||||
# `autoinstall` - skips subiquity's confirmation prompt.
|
||||
# `console=ttyS0`- so the install is watchable in $LOG instead of being a
|
||||
# 25-minute black box that either works or does not.
|
||||
sed -i -e 's|^\(\s*linux\s*/casper/vmlinuz\)\s*---|\1 autoinstall console=ttyS0,115200 ---|' \
|
||||
-e 's|^set timeout=.*|set timeout=1|' "$WORK/grub.cfg"
|
||||
grep -q 'autoinstall' "$WORK/grub.cfg" || { echo "grub.cfg patch did not apply" >&2; exit 1; }
|
||||
|
||||
xorriso -indev "$SRC_ISO" -outdev "$AUTO_ISO" \
|
||||
-boot_image any replay \
|
||||
-map "$WORK/grub.cfg" /boot/grub/grub.cfg \
|
||||
-commit >/dev/null 2>&1
|
||||
[ -s "$AUTO_ISO" ] || { echo "repack produced nothing" >&2; exit 1; }
|
||||
|
||||
echo "==> building the NoCloud seed"
|
||||
# cloud-init finds this by volume label alone; no ds= argument needed.
|
||||
mkdir -p "$WORK/seed" && cp user-data meta-data "$WORK/seed/"
|
||||
xorriso -as mkisofs -o "$SEED_ISO" -V CIDATA -J -r "$WORK/seed" >/dev/null 2>&1
|
||||
|
||||
echo "==> creating the disk"
|
||||
install -d -o owuivmadm -g owuivmadm -m 0755 "$BASE_DIR"
|
||||
qemu-img create -f qcow2 "$DISK" 40G >/dev/null
|
||||
cp /usr/share/OVMF/OVMF_VARS_4M.fd "$VARS"
|
||||
chown owuivm:owuivm "$DISK" "$VARS"
|
||||
chmod 0644 "$DISK" "$VARS"
|
||||
chmod 0777 "$WORK"; chmod 0644 "$AUTO_ISO" "$SEED_ISO"
|
||||
|
||||
echo "==> installing (watch: tail -f $LOG)"
|
||||
: > "$LOG"; chown owuivm:owuivm "$LOG"
|
||||
# -no-reboot: subiquity reboots when it is done, and that is our signal
|
||||
# that the install finished. Without it the fresh system boots and the
|
||||
# build never returns.
|
||||
setpriv --reuid=owuivm --regid=owuivm --groups=kvm --inh-caps=-all -- \
|
||||
qemu-system-x86_64 \
|
||||
-no-user-config -nodefaults -no-reboot \
|
||||
-machine q35,accel=kvm -cpu host -smp 4 -m 4096 \
|
||||
-drive "if=pflash,format=raw,unit=0,readonly=on,file=/usr/share/OVMF/OVMF_CODE_4M.fd" \
|
||||
-drive "if=pflash,format=raw,unit=1,file=$VARS" \
|
||||
-drive "file=$DISK,if=none,id=disk0,format=qcow2,cache=unsafe" \
|
||||
-device virtio-blk-pci,drive=disk0,bootindex=1 \
|
||||
-drive "file=$AUTO_ISO,if=none,id=cd0,media=cdrom,readonly=on" \
|
||||
-device ide-cd,drive=cd0,bus=ide.0,bootindex=2 \
|
||||
-drive "file=$SEED_ISO,if=none,id=cd1,media=cdrom,readonly=on" \
|
||||
-device ide-cd,drive=cd1,bus=ide.1 \
|
||||
-netdev user,id=net0,ipv6=off -device virtio-net-pci,netdev=net0,romfile= \
|
||||
-device VGA -display none \
|
||||
-serial "file:$LOG"
|
||||
|
||||
echo "==> freezing"
|
||||
# The write bit IS the freeze. owuivm is neither the owner nor in the
|
||||
# group, so the uid that runs every session cannot clear it; only the
|
||||
# maintenance unit, which runs as owuivmadm behind a polkit check, can.
|
||||
chown owuivmadm:owuivmadm "$DISK" "$VARS"
|
||||
chmod 0444 "$DISK" "$VARS"
|
||||
sha256sum "$DISK" "$VARS" > "$BASE_DIR/ubuntu.sha256"
|
||||
chown owuivmadm:owuivmadm "$BASE_DIR/ubuntu.sha256"
|
||||
|
||||
echo
|
||||
echo "base image built and frozen:"
|
||||
ls -l "$DISK" "$VARS"
|
||||
cat "$BASE_DIR/ubuntu.sha256"
|
||||
Reference in New Issue
Block a user