- webapp/requirements.txt pins all eight wheels with sha256, at the fixed releases (lxml 6.1.3 for GHSA-vfmq-68hx-4jfw / PYSEC-2026-87, werkzeug 3.1.9 for GHSA-g6x2-hccm-hh4m). - download-packages.sh fetches exactly that set for Python 3.12 / x86_64 into an emptied folder; PIP_DIR is honoured inside the container (own mount), files are handed back to the calling user, USE_DOCKER=1 forces the container on a noble host, an unknown package name now prints apt's error instead of failing silently, and linux-modules-extra-7.0.* is gone (it does not exist; 7.0 ships those modules in linux-modules). - Playbook: the wheel installer refuses any wheel whose sha256 is not pinned, needs exactly one wheel per pin, and removes an older installed version by its RECORD first. The webapp copy is a full rsync every run (was cp with creates: app.py, which never updated an install). - scripts/check-updates.py: offline debs against noble security/updates and the pins against PyPI + osv.dev; exit 1 when something needs updating. Run it before building a USB. Tested: installer in a Python 3.12 venv (clean install, upgrade over lxml 6.0.4 + werkzeug 3.1.8 leaves no old dist-info, re-run is a no-op, a stray wheel is refused); check-updates.py flags the April bundle (94 security updates) and passes the pins.
183 lines
6.9 KiB
Bash
Executable File
183 lines
6.9 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# download-packages.sh - Download all .deb packages needed for offline PXE server setup
|
|
#
|
|
# The PXE server installs Ubuntu 24.04 (Noble), so all packages MUST come from the
|
|
# 24.04 archive. If this script is run on a non-24.04 host (e.g. Zorin 17 / 22.04),
|
|
# it auto-spawns an Ubuntu 24.04 docker container to do the download.
|
|
#
|
|
# Usage:
|
|
# ./download-packages.sh [output_directory]
|
|
#
|
|
# Default output: ./offline-packages/
|
|
|
|
set -euo pipefail
|
|
|
|
OUT_DIR="${1:-./offline-packages}"
|
|
OUT_DIR_ABS="$(cd "$(dirname "$OUT_DIR")" 2>/dev/null && pwd)/$(basename "$OUT_DIR")"
|
|
|
|
# Detect host Ubuntu codename. Run inside the container if not Noble (24.04).
|
|
HOST_CODENAME="$(. /etc/os-release && echo "${UBUNTU_CODENAME:-${VERSION_CODENAME:-}}")"
|
|
|
|
# USE_DOCKER=1 forces the container even on a noble host: no sudo needed,
|
|
# and the host's own apt state is left alone.
|
|
if [ "${IN_DOCKER:-}" != "1" ] && { [ "$HOST_CODENAME" != "noble" ] || [ "${USE_DOCKER:-}" = "1" ]; }; then
|
|
echo "Host is '$HOST_CODENAME'; downloading inside an ubuntu:24.04 container."
|
|
echo "Re-running inside ubuntu:24.04 docker container..."
|
|
echo ""
|
|
|
|
if ! command -v docker >/dev/null; then
|
|
echo "ERROR: docker not installed. Install docker or run on a real Ubuntu 24.04 host."
|
|
exit 1
|
|
fi
|
|
|
|
SCRIPT_PATH="$(readlink -f "$0")"
|
|
REPO_DIR="$(cd "$(dirname "$SCRIPT_PATH")"/.. && pwd)"
|
|
mkdir -p "$OUT_DIR_ABS"
|
|
# Wheels go to their own mount so PIP_DIR is honoured inside the container
|
|
# (the in-container default would be the repo's pip-wheels/).
|
|
PIP_DIR_HOST="$(mkdir -p "${PIP_DIR:-$REPO_DIR/pip-wheels}" && cd "${PIP_DIR:-$REPO_DIR/pip-wheels}" && pwd)"
|
|
|
|
docker run --rm -i \
|
|
-v "$REPO_DIR:/repo" \
|
|
-v "$OUT_DIR_ABS:/out" \
|
|
-v "$PIP_DIR_HOST:/pipout" \
|
|
-e IN_DOCKER=1 \
|
|
-e PIP_DIR=/pipout \
|
|
-e HOST_UID="$(id -u)" -e HOST_GID="$(id -g)" \
|
|
-e BLANCCO_KVER="${BLANCCO_KVER:-}" \
|
|
-w /repo \
|
|
ubuntu:24.04 \
|
|
bash -c "apt-get update -qq && apt-get install -y --no-install-recommends sudo python3-pip python3-setuptools python3-wheel ca-certificates >/dev/null && /repo/scripts/download-packages.sh /out; rc=\$?; chown -R \$HOST_UID:\$HOST_GID /out /pipout; exit \$rc"
|
|
|
|
echo ""
|
|
echo "============================================"
|
|
echo "Container build complete. Files in: $OUT_DIR_ABS"
|
|
echo "============================================"
|
|
exit 0
|
|
fi
|
|
|
|
mkdir -p "$OUT_DIR"
|
|
|
|
# Kernel the Blancco UEFI path boots (playbook var blancco_kver; keep the two
|
|
# in step). The playbook builds vmlinuz-ubuntu-70, kexec-initrd-70.img and
|
|
# kmod-<kver>.tar.gz from this kernel's /boot and /lib/modules, and the
|
|
# Canonical-signed image is what Ubuntu's shim starts with Secure Boot on.
|
|
# The versioned packages pin it; the HWE metapackage keeps the box on the
|
|
# HWE line. Override: BLANCCO_KVER=7.0.0-40-generic ./download-packages.sh
|
|
BLANCCO_KVER="${BLANCCO_KVER:-7.0.0-34-generic}"
|
|
|
|
# Packages installed by the Ansible playbook (pxe_server_setup.yml)
|
|
PLAYBOOK_PACKAGES=(
|
|
ansible
|
|
dnsmasq
|
|
apache2
|
|
samba
|
|
unzip
|
|
ufw
|
|
cron
|
|
wimtools
|
|
p7zip-full
|
|
grub-efi-amd64-bin
|
|
grub-common
|
|
conntrack
|
|
busybox-static
|
|
zstd
|
|
cpio
|
|
# Updates pages in the webapp (iPXE signature check, Dell pack zips, downloads)
|
|
sbsigntool
|
|
zip
|
|
curl
|
|
# Blancco with Secure Boot on: shimx64.efi.signed + the HWE kernel
|
|
shim-signed
|
|
linux-generic-hwe-24.04
|
|
"linux-image-${BLANCCO_KVER}"
|
|
"linux-modules-${BLANCCO_KVER}"
|
|
# No linux-modules-extra: from 7.0 Ubuntu ships those modules (iwlwifi
|
|
# etc.) in linux-modules, and modules-extra-7.0.* does not exist.
|
|
)
|
|
|
|
# Packages installed during autoinstall late-commands (NetworkManager, WiFi, etc.)
|
|
AUTOINSTALL_PACKAGES=(
|
|
network-manager
|
|
wpasupplicant
|
|
wireless-tools
|
|
linux-firmware
|
|
firmware-sof-signed
|
|
)
|
|
|
|
ALL_PACKAGES=("${PLAYBOOK_PACKAGES[@]}" "${AUTOINSTALL_PACKAGES[@]}")
|
|
|
|
echo "============================================"
|
|
echo "Offline Package Downloader (Ubuntu 24.04 noble)"
|
|
echo "============================================"
|
|
echo "Output directory: $OUT_DIR"
|
|
echo ""
|
|
echo "Packages to resolve:"
|
|
printf ' - %s\n' "${ALL_PACKAGES[@]}"
|
|
echo ""
|
|
|
|
# Update package cache
|
|
echo "[1/4] Updating package cache..."
|
|
sudo apt-get update -qq
|
|
|
|
# Simulate install to find all dependencies
|
|
echo "[2/4] Resolving dependencies..."
|
|
SIM=$(apt-get install --simulate "${ALL_PACKAGES[@]}" 2>&1) || {
|
|
# e.g. a package name that does not exist in noble: say which.
|
|
echo "ERROR: apt could not resolve the package list:" >&2
|
|
echo "$SIM" | grep -E "^(E|W):" >&2
|
|
exit 1
|
|
}
|
|
EXPLICIT_DEPS=$(echo "$SIM" | grep "^Inst " | awk '{print $2}')
|
|
|
|
# ALSO pull every package that would upgrade in a dist-upgrade. This is
|
|
# critical: the Ubuntu ISO ships a point-in-time baseline, but our explicit
|
|
# packages (from noble-updates) may depend on *newer* versions of ISO-baseline
|
|
# packages (e.g. gnupg 17.4 needs matching gpgv 17.4). Without this, offline
|
|
# install fails with dpkg "dependency problems" because transitive version
|
|
# bumps aren't captured by --simulate on the explicit list.
|
|
UPGRADE_DEPS=$(apt-get dist-upgrade --simulate 2>&1 \
|
|
| grep "^Inst " \
|
|
| awk '{print $2}')
|
|
|
|
DEPS=$(printf '%s\n%s\n' "$EXPLICIT_DEPS" "$UPGRADE_DEPS" | sort -u | grep -v '^$')
|
|
|
|
DEP_COUNT=$(echo "$DEPS" | wc -l)
|
|
echo " Found $DEP_COUNT packages (explicit + baseline upgrades)"
|
|
|
|
# Download all packages
|
|
echo "[3/4] Downloading .deb packages to $OUT_DIR..."
|
|
cd "$OUT_DIR"
|
|
apt-get download $DEPS 2>&1 | tail -5
|
|
|
|
DEB_COUNT=$(ls -1 *.deb 2>/dev/null | wc -l)
|
|
TOTAL_SIZE=$(du -sh . | cut -f1)
|
|
|
|
echo " $DEB_COUNT packages ($TOTAL_SIZE)"
|
|
|
|
# Download pip wheels for Flask webapp (offline install)
|
|
echo "[4/4] Downloading Python wheels for webapp..."
|
|
# Place pip-wheels next to the script (or /repo when in docker), not next to OUT_DIR
|
|
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")"/.. && pwd)"
|
|
PIP_DIR="${PIP_DIR:-$REPO_ROOT/pip-wheels}"
|
|
# Exactly the pinned, hashed set in webapp/requirements.txt, for the server's
|
|
# Python 3.12 on x86_64. The folder is emptied first: the playbook installs
|
|
# every wheel in it and refuses any wheel that is not pinned.
|
|
find "$PIP_DIR" -mindepth 1 -delete 2>/dev/null; mkdir -p "$PIP_DIR"
|
|
pip3 download -d "$PIP_DIR" --require-hashes -r "$REPO_ROOT/webapp/requirements.txt" \
|
|
--only-binary=:all: --no-deps --python-version 3.12 --implementation cp --abi cp312 \
|
|
--platform manylinux_2_28_x86_64 --platform manylinux_2_17_x86_64 --platform manylinux2014_x86_64 \
|
|
2>&1 | tail -5
|
|
|
|
WHL_COUNT=$(ls -1 "$PIP_DIR"/*.whl "$PIP_DIR"/*.tar.gz 2>/dev/null | wc -l)
|
|
echo " $WHL_COUNT Python packages downloaded to pip-wheels/"
|
|
|
|
echo ""
|
|
echo "============================================"
|
|
echo "Download complete!"
|
|
echo "============================================"
|
|
echo " .deb packages: $DEB_COUNT ($TOTAL_SIZE) in $OUT_DIR/"
|
|
echo " Python wheels: $WHL_COUNT in $PIP_DIR/"
|
|
echo ""
|