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).
a94c203 added a RunSynchronous Path of 417 chars to both std/eng answer
files. Windows caps it at 259 and then rejects the WHOLE answer file, so
an Engineer build stopped at 'Windows could not parse or process unattend
answer file'. The live files were restored to their pre-a94c203 versions
the same day.
- The status steps now fit: Send-ImagingStage.ps1 -Persist copies itself
out of C:\Deploy (no cmd copy chain), short PowerShell switches, default
arguments left out. Longest Path is 168 chars; lint-unattend passes.
NOT deployed: these files must go live only through deploy-unattend.sh.
- scripts/deploy-unattend.sh: the only route for an answer file to a PXE
server. Lints first and refuses on any error, backs up, swaps
atomically, lints the live copy and restores the backup if that fails.
- .githooks/pre-commit (git config core.hooksPath .githooks): refuses a
commit whose staged unattend fails the linter. Verified by planting a
352-char Path (blocked) and against the exact a94c203 file (rejected).
- Boot menu: also accept SecureBoot read as hex 01, and show the raw values
in the warning so a wrong read can be diagnosed from the screen.