diff --git a/diskless.pkr.hcl b/diskless.pkr.hcl index 0854930..720a79d 100644 --- a/diskless.pkr.hcl +++ b/diskless.pkr.hcl @@ -957,17 +957,25 @@ build { # hard-needs it) and mask as a backstop against future weak-dep reinstalls. "dnf remove -y firewalld 2>/dev/null || true", "systemctl mask firewalld.service 2>/dev/null || true", - # nss-mdns: the mDNS/.local NSS resolver, and the ONLY reverse-dep of avahi - # (confirmed 2026-07-03: `rpm -e --test avahi` → "avahi is needed by nss-mdns", - # and avahi is absent from `ldd libavformat` so it's unrelated to Chromium's - # media stack — unlike samba/librabbitmq/librist/libsrt/libzmq, which ARE - # libavformat DT_NEEDED and must stay for future HTML5 media). This fleet - # resolves via DHCP/DNS (boot.fleet.lab), never .local, so mDNS is dead weight - # AND a network-listening service unwanted on a locked-down kiosk. Removing - # nss-mdns lets dnf's clean_requirements_on_remove reap avahi/avahi-glib/ - # avahi-libs as orphans. (install_weak_deps=False may already drop nss-mdns if - # it's a Recommends; this is the belt-and-braces for the hard-dep case.) - "dnf remove -y nss-mdns 2>/dev/null || true", + # nss-mdns / avahi: DO NOT REMOVE — the EPEL `chromium` package hard-Requires + # nss-mdns (Chromium resolves `.local` mDNS hostnames for WebRTC ICE + # candidates, to avoid leaking local IPs in peer connections). `dnf remove + # nss-mdns` therefore takes CHROMIUM with it as a dependent package, then + # reaps its whole orphaned GUI stack (avahi, at-spi2, atk, adwaita, + # chromium-common, ...) — a ~300 MB drop that ships a browserless, bricked + # kiosk (flashing cursor; kiosk-session dies at `${BROWSER:?}`). This bit us + # 2026-07-03: the earlier commit checked what nss-mdns NEEDS (avahi) but not + # what NEEDS nss-mdns (chromium). The FINAL GUARD below now asserts the kiosk + # stack survived so this can never ship silently again. + # + # We still don't want the avahi mDNS DAEMON listening on :5353 on a + # locked-down kiosk (that was the real attack-surface concern). nss-mdns is + # only an NSS module (libnss_mdns.so) — it needs no daemon; if avahi-daemon + # isn't running, `.local` lookups just fall through. So keep the PACKAGES + # (satisfy chromium's Requires) but mask the listener: no socket, no daemon, + # dep satisfied, browser intact. ~1.5 MB kept — negligible vs a working kiosk. + "systemctl mask avahi-daemon.service avahi-daemon.socket 2>/dev/null || true", + "systemctl disable avahi-daemon.service avahi-daemon.socket 2>/dev/null || true", # (No Go toolchain to remove — fleet-hostapi + fleet-enrol are built OUT OF # the VM on the CI runner and file-provisioned in as static binaries.) # The build-time STIG reports must not ship inside the read-only rootfs. @@ -987,6 +995,22 @@ build { # (linux-firmware slimming moved to step 2, BEFORE the dracut build, so the # UKI initramfs shrinks too — see TODO #18. Nothing re-adds firmware after # that point, so the rootfs erofs is already trimmed by the time we get here.) + # + # ---- FINAL GUARD: assert the kiosk stack survived the whole pipeline ---- + # 2026-07-03 incident: chromium installed cleanly in the 2b kiosk step (the + # footprint audit showed chromium-149 + chromium-common, 523 pkgs), but by + # the final image it was GONE (472 pkgs) — `dnf remove -y nss-mdns` in 2d + # removed chromium AS A DEPENDENT PACKAGE (EPEL chromium hard-Requires + # nss-mdns for WebRTC mDNS ICE candidates), then reaped its orphaned GUI + # stack. Result: a ~500 MB image whose kiosk died at the `${BROWSER:?}` guard + # in kiosk-session — a flashing-cursor boot with no weston, shipped green. + # The nss-mdns removal is fixed above (kept + avahi listener masked), but a + # missing browser must fail the BUILD, never brick the fleet: assert every + # kiosk-critical package is still installed at the very end of the pipeline + # (after remediation AND all 2d removes). This catches ANY future silent drop + # — a new hard-Requires cascade, an oscap package_*_removed rule, autoremove. + # enable_kiosk=false legitimately ships no browser, so gate on it. + "if [ \"${var.enable_kiosk}\" = \"true\" ] && [ -n \"${var.kiosk_url}\" ]; then for p in chromium chromium-common weston mesa-dri-drivers mesa-libEGL mesa-libgbm; do rpm -q \"$p\" >/dev/null 2>&1 || { echo \"ERROR: kiosk-critical package '$p' missing from the FINAL image — a later step removed it after the 2b kiosk install (e.g. a dnf remove whose dependency cascade took chromium, cf. the 2026-07-03 nss-mdns incident). Refusing to ship a bricked kiosk.\" >&2; exit 1; }; done; echo '==> guard: kiosk stack intact (chromium/weston/mesa present after remediation + hygiene)'; fi", ] }