Build/playbook re-run safety: corp access, netplan rollback, first-boot retry
H1 corp_admin_cidrs (UFW 22/9009 on the corp NIC from these only; refuses
when it would lock out the current SSH client) and corp_ssh_password_auth
(sshd drop-in, passwords only from the imaging subnet). Defaults keep the
current behaviour and print a loud warning.
H2 pxe-netplan-apply.sh: pre-flight refusal (pxe-MAC NIC holds the default
route, a DHCP lease or the route to an SSH client), run from the USB
before anything changes; post-apply checks (names, imaging IPv4, corp
IPv4 back within 30 s, SSH route) and a 120 s systemd-run auto-revert.
H3 restore() puts the previous NIC names back (two-phase rename via
temporary names, so swaps work) before generate + apply.
H4 a failed rename is fatal (restore + exit 1); the play drops the old
any-interface UFW rules only after verifying both NICs and their
per-interface SSH rules.
H5 first-boot checks the playbook exit code, retries at next boot (max 5),
writes /var/lib/pxe-firstboot.failed and /etc/issue + /etc/motd banners.
M1 build-usb.sh verifies iPXE files against the playbook pins, every wheel
against its own package's hashes, drops the unpinned pip fallback,
requires --site-vars or --no-site-vars.
M2 webapp restarts when the code, wheels or unit changed.
M3 ignore-carrier on the imaging NIC; ip_nonlocal_bind kept for the
apache/networkd boot race.
M4 menu suffix must match blancco_kver; missing kernel with no earlier
build is fatal; a live menu with the staged Blancco test entry is not
overwritten.
M5 build-proxmox-iso.sh is unsupported (hard exit); use build-usb --image.
M6 deploy-unattend.sh: password via SSHPASS env and sudo stdin only,
%q-quoted remote args, sha256 check of the live file; lint-unattend.py
reads PXE_SERVER/PXE_USER/PXE_PASS and uses sshpass -e.
M7 the playbook lints the unattend files before the force: yes copies
(lint-unattend.py ships on CIDATA as scripts/); pre-commit ACMR.
M8 Blancco initramfs build: no ignore_errors, pipefail, trap cleanup.
Also: Send-ImagingStage.ps1 deploy, carrier hook install tasks
(pxe-nic-carrier, networkd-dispatcher), L1 imaging-active hold
(allow_disruptive) and no iPXE downgrade below an Updates-page install,
L2 iso-volid, L3 kver pin read from the playbook, L4 cp -rL, L5 refuse
>= 4 GiB WinPE files, L6 PIP_DIR wipe guard, L7 per-package wheel hashes,
no online fallback, L8 check-updates.py (apt_pkg/cached compare, all
components, exit 2 on no debs / bad answers).
This commit is contained in:
@@ -44,13 +44,19 @@ set -euo pipefail
|
||||
SCRIPT_PATH="$(readlink -f "$0")"
|
||||
REPO_DIR="$(cd "$(dirname "$SCRIPT_PATH")"/.. && pwd)"
|
||||
|
||||
# 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
|
||||
# Kernel the Blancco UEFI path boots. The default is read from the playbook
|
||||
# var blancco_kver (the one source; see its comment for the other places).
|
||||
# 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}"
|
||||
# (then set blancco_kver to match, or the playbook will not find it).
|
||||
if [ -z "${BLANCCO_KVER:-}" ]; then
|
||||
BLANCCO_KVER=$(sed -n 's/^[[:space:]]*blancco_kver:[[:space:]]*"\([^"]*\)".*/\1/p' \
|
||||
"$REPO_DIR/playbook/pxe_server_setup.yml" | head -1)
|
||||
fi
|
||||
[ -n "$BLANCCO_KVER" ] || { echo "ERROR: no blancco_kver in playbook/pxe_server_setup.yml (set BLANCCO_KVER)" >&2; exit 1; }
|
||||
|
||||
# Packages installed by the Ansible playbook (pxe_server_setup.yml)
|
||||
PLAYBOOK_PACKAGES=(
|
||||
@@ -66,6 +72,7 @@ PLAYBOOK_PACKAGES=(
|
||||
grub-efi-amd64-bin
|
||||
grub-common
|
||||
conntrack
|
||||
networkd-dispatcher # carrier hook (pxe-nic-carrier); in the base, pinned here anyway
|
||||
busybox-static
|
||||
zstd
|
||||
cpio
|
||||
@@ -300,6 +307,9 @@ docker run --rm \
|
||||
-e HOST_UID="$(id -u)" -e HOST_GID="$(id -g)" \
|
||||
"$TAG" bash /repo/scripts/download-packages.sh
|
||||
|
||||
# The ISO this set was resolved against; build-usb.sh refuses another one.
|
||||
printf '%s\n' "$VOLID" >"$OUT_DIR_ABS/iso-volid"
|
||||
|
||||
# --- 3. offline check: resolve exactly as the server will, no network ---
|
||||
echo "[3/4] Offline check on a fresh base container (no network)..."
|
||||
docker run --rm --network none \
|
||||
@@ -310,8 +320,15 @@ docker run --rm --network none \
|
||||
echo "[4/4] Downloading Python wheels for the webapp..."
|
||||
# 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_HOST" -mindepth 1 -delete
|
||||
# every wheel in it and refuses any wheel that is not pinned. Only a folder
|
||||
# of wheels is emptied: a PIP_DIR pointing anywhere else is refused.
|
||||
NOT_WHEELS=$(find "$PIP_DIR_HOST" -mindepth 1 \( ! -name '*.whl' -o ! -type f \) -print -quit)
|
||||
if [ -n "$NOT_WHEELS" ]; then
|
||||
echo "ERROR: $PIP_DIR_HOST holds more than *.whl files (e.g. $NOT_WHEELS); refusing to empty it." >&2
|
||||
echo " Point PIP_DIR at a wheels-only folder." >&2
|
||||
exit 1
|
||||
fi
|
||||
find "$PIP_DIR_HOST" -mindepth 1 -maxdepth 1 -name '*.whl' -type f -delete
|
||||
docker run --rm -v "$REPO_DIR:/repo:ro" -v "$PIP_DIR_HOST:/pipout" \
|
||||
-e HOST_UID="$(id -u)" -e HOST_GID="$(id -g)" ubuntu:24.04 bash -euc '
|
||||
apt-get update -qq >/dev/null
|
||||
|
||||
Reference in New Issue
Block a user