PSA/ee/appliance/ubuntu-iso/scripts/preinstall-rootfs-in-docker.sh
Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

197 lines
6.7 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
usage() {
cat <<USAGE
Usage:
$(basename "$0") <iso-root>
Remasters the Ubuntu Server install source inside <iso-root> by merging the
base + default server squashfs layers, installing appliance-required packages,
applying security updates from noble-security, and repacking the rootfs.
Environment:
ALGA_APPLIANCE_PREINSTALL_PACKAGES Space-separated package names to install.
ALGA_APPLIANCE_PREINSTALL_IMAGE Docker image used for Linux build tooling.
USAGE
}
if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
usage
exit 0
fi
ISO_ROOT="${1:-}"
if [[ -z "$ISO_ROOT" ]]; then
usage >&2
exit 1
fi
if [[ ! -d "$ISO_ROOT/casper" ]]; then
echo "ISO root does not contain casper directory: $ISO_ROOT" >&2
exit 1
fi
if [[ ! -f "$ISO_ROOT/casper/ubuntu-server-minimal.squashfs" ]]; then
echo "Missing base Ubuntu server squashfs in: $ISO_ROOT/casper" >&2
exit 1
fi
if [[ ! -f "$ISO_ROOT/casper/ubuntu-server-minimal.ubuntu-server.squashfs" ]]; then
echo "Missing default Ubuntu server layer squashfs in: $ISO_ROOT/casper" >&2
exit 1
fi
if ! command -v docker >/dev/null 2>&1; then
echo "docker is required for full rootfs preinstall remastering." >&2
exit 1
fi
if ! docker info >/dev/null 2>&1; then
echo "docker is not running; start Docker/Colima before using --preinstall-rootfs." >&2
exit 1
fi
PACKAGES="${ALGA_APPLIANCE_PREINSTALL_PACKAGES:-ca-certificates curl git jq net-tools nodejs openssh-server unzip ufw}"
BUILDER_IMAGE="${ALGA_APPLIANCE_PREINSTALL_IMAGE:-ubuntu:24.04}"
printf 'Preinstalling packages/security updates into Ubuntu install source:\n'
printf ' ISO root: %s\n' "$ISO_ROOT"
printf ' Builder image: %s\n' "$BUILDER_IMAGE"
printf ' Packages: %s\n' "$PACKAGES"
docker run --rm -i --platform linux/amd64 --privileged \
-e DEBIAN_FRONTEND=noninteractive \
-e PREINSTALL_PACKAGES="$PACKAGES" \
-v "$ISO_ROOT:/iso-root" \
"$BUILDER_IMAGE" \
bash -euo pipefail <<'CONTAINER_SCRIPT'
set -x
apt-get update
apt-get install -y --no-install-recommends ca-certificates squashfs-tools python3
rm -rf /var/lib/apt/lists/*
WORK_DIR="/work/alga-rootfs-preinstall"
ROOTFS="$WORK_DIR/rootfs"
LAYERFS="$WORK_DIR/server-layer"
rm -rf "$WORK_DIR"
mkdir -p "$ROOTFS" "$LAYERFS"
unsquashfs -f -d "$ROOTFS" /iso-root/casper/ubuntu-server-minimal.squashfs
# The default Ubuntu Server layer contains overlayfs whiteout markers as
# character devices. Docker overlay filesystems can reject those outside /dev.
# Extract the layer separately while ignoring those write errors, apply the
# known whiteout, then copy normal layer contents over the base rootfs.
unsquashfs -ignore-errors -no-exit-code -f -d "$LAYERFS" /iso-root/casper/ubuntu-server-minimal.ubuntu-server.squashfs
rm -f "$ROOTFS/etc/dpkg/dpkg.cfg.d/excludes"
cp -a "$LAYERFS"/. "$ROOTFS"/
# Keep package installation from starting daemons inside the chroot.
printf '#!/bin/sh\nexit 101\n' > "$ROOTFS/usr/sbin/policy-rc.d"
chmod 0755 "$ROOTFS/usr/sbin/policy-rc.d"
# Build-time sources are intentionally limited to the release pocket plus
# security pocket. We do not enable noble-updates here because the intent is to
# pre-apply security updates, not drift to all available updates.
APT_BACKUP="$WORK_DIR/apt-backup"
mkdir -p "$APT_BACKUP"
if [[ -f "$ROOTFS/etc/apt/sources.list" ]]; then
mv "$ROOTFS/etc/apt/sources.list" "$APT_BACKUP/sources.list"
fi
if [[ -d "$ROOTFS/etc/apt/sources.list.d" ]]; then
mv "$ROOTFS/etc/apt/sources.list.d" "$APT_BACKUP/sources.list.d"
fi
mkdir -p "$ROOTFS/etc/apt/sources.list.d"
cat > "$ROOTFS/etc/apt/sources.list" <<'APT_SOURCES'
deb http://archive.ubuntu.com/ubuntu noble main restricted universe multiverse
deb http://security.ubuntu.com/ubuntu noble-security main restricted universe multiverse
APT_SOURCES
cp /etc/resolv.conf "$ROOTFS/etc/resolv.conf"
mount --bind /dev "$ROOTFS/dev"
mount -t devpts devpts "$ROOTFS/dev/pts"
mount -t proc proc "$ROOTFS/proc"
mount -t sysfs sysfs "$ROOTFS/sys"
cleanup_mounts() {
set +e
umount -lf "$ROOTFS/dev/pts" 2>/dev/null || true
umount -lf "$ROOTFS/dev" 2>/dev/null || true
umount -lf "$ROOTFS/proc" 2>/dev/null || true
umount -lf "$ROOTFS/sys" 2>/dev/null || true
}
trap cleanup_mounts EXIT
chroot "$ROOTFS" apt-get update
# shellcheck disable=SC2086
chroot "$ROOTFS" apt-get install -y --no-install-recommends $PREINSTALL_PACKAGES
chroot "$ROOTFS" apt-get upgrade -y
chroot "$ROOTFS" apt-get clean
cleanup_mounts
trap - EXIT
rm -f "$ROOTFS/usr/sbin/policy-rc.d"
rm -rf "$ROOTFS/var/lib/apt/lists"/* "$ROOTFS/var/cache/apt/archives"/*.deb "$ROOTFS/tmp"/* "$ROOTFS/var/tmp"/*
# Never bake identity material generated by package postinst scripts into the appliance image.
rm -f "$ROOTFS/etc/ssh"/ssh_host_*
: > "$ROOTFS/etc/machine-id"
rm -f "$ROOTFS/var/lib/dbus/machine-id" "$ROOTFS/var/lib/systemd/random-seed"
rm -f "$ROOTFS/etc/apt/sources.list"
rm -rf "$ROOTFS/etc/apt/sources.list.d"
if [[ -f "$APT_BACKUP/sources.list" ]]; then
mv "$APT_BACKUP/sources.list" "$ROOTFS/etc/apt/sources.list"
fi
if [[ -d "$APT_BACKUP/sources.list.d" ]]; then
mv "$APT_BACKUP/sources.list.d" "$ROOTFS/etc/apt/sources.list.d"
else
mkdir -p "$ROOTFS/etc/apt/sources.list.d"
fi
chroot "$ROOTFS" dpkg-query -W --showformat='${Package} ${Version}\n' \
> /iso-root/casper/ubuntu-server-minimal.manifest
ROOTFS_SIZE="$(du -sx --block-size=1 "$ROOTFS" | awk '{print $1}')"
printf '%s\n' "$ROOTFS_SIZE" > /iso-root/casper/ubuntu-server-minimal.size
rm -f /iso-root/casper/ubuntu-server-minimal.squashfs
mksquashfs "$ROOTFS" /iso-root/casper/ubuntu-server-minimal.squashfs \
-noappend -comp xz -b 1M
cat > /iso-root/casper/install-sources.yaml <<EOF
- default: true
description:
en: AlgaPSA Ubuntu Server with appliance-required packages and security updates preinstalled.
id: alga-ubuntu-server
locale_support: locale-only
name:
en: AlgaPSA Ubuntu Server
path: ubuntu-server-minimal.squashfs
size: ${ROOTFS_SIZE}
type: fsimage
variant: server
EOF
(
cd /iso-root/casper
sha256sum \
ubuntu-server-minimal.squashfs \
ubuntu-server-minimal.ubuntu-server.installer.generic-hwe.squashfs \
ubuntu-server-minimal.ubuntu-server.installer.generic.squashfs \
ubuntu-server-minimal.ubuntu-server.installer.squashfs \
ubuntu-server-minimal.ubuntu-server.squashfs \
> SHA256SUMS
)
rm -f /iso-root/casper/SHA256SUMS.gpg
python3 - <<'PY'
from pathlib import Path
manifest = Path('/iso-root/casper/ubuntu-server-minimal.manifest')
packages = {line.split()[0] for line in manifest.read_text().splitlines() if line.strip()}
required = set((Path('/tmp/required-packages.txt').read_text().split() if Path('/tmp/required-packages.txt').exists() else []))
missing = sorted(required - packages)
if missing:
raise SystemExit(f'Missing expected preinstalled packages: {missing}')
PY
CONTAINER_SCRIPT