435 lines
23 KiB
HCL
435 lines
23 KiB
HCL
# =============================================================================
|
|
# Diskless fleet image build
|
|
#
|
|
# Builds a RHEL9-family root filesystem + fleet-generic initramfs for
|
|
# PXE/HTTP-booted diskless hosts authenticating with 802.1X EAP-TLS
|
|
# (TPM-held key, cert in TPM NV) and MACsec link encryption.
|
|
# Root filesystem: erofs + dm-verity over a shared read-only NVMe/TCP
|
|
# namespace, with a tmpfs overlay for runtime state.
|
|
#
|
|
# Outputs:
|
|
# ${var.artifact_dir}/${var.image_version}/
|
|
# vmlinuz, initramfs.img - intermediate (consumed by ukify)
|
|
# fleet-<version>.efi - signed UKI (kernel+initramfs+cmdline+roothash)
|
|
# rootfs.erofs - erofs + appended verity tree -> nvmet RO namespace
|
|
# verity.json, cmdline.txt, SHA256SUMS
|
|
# OCI image ${var.oci_image}:${var.image_version} (FROM scratch)
|
|
# /artifacts/{fleet-<version>.efi, rootfs.erofs, verity.json, SHA256SUMS}
|
|
# = the atomic fleet release unit; deploy as initContainer / image volume
|
|
# feeding the SPDK + HTTPS serving containers, pinned by digest.
|
|
#
|
|
# Usage:
|
|
# packer init .
|
|
# packer build -var iso_url=... -var iso_checksum=... .
|
|
# =============================================================================
|
|
|
|
packer {
|
|
required_version = ">= 1.10.0"
|
|
required_plugins {
|
|
qemu = {
|
|
source = "github.com/hashicorp/qemu"
|
|
version = ">= 1.1.0"
|
|
}
|
|
}
|
|
}
|
|
|
|
# ------------------------------------------------------------------ variables
|
|
|
|
variable "iso_url" {
|
|
type = string
|
|
description = "RHEL 9 / Rocky 9 / Alma 9 install ISO URL or local path. The minimal ISO is sufficient: ks.cfg installs only core+sshd from it and the provisioner pulls the diskless tooling from the online repos. Defaults to the fast internal mirror (version-pinned to match iso_checksum)."
|
|
default = "http://iso-server.bitasylum.org/isos/Rocky-9.8-x86_64-minimal.iso"
|
|
}
|
|
|
|
variable "iso_checksum" {
|
|
type = string
|
|
# Pinned sha256 of Rocky-9.8-x86_64-minimal.iso on the internal mirror.
|
|
# When moving to a new point release, bump iso_url AND this hash together:
|
|
# curl -fsSL http://iso-server.bitasylum.org/isos/Rocky-<ver>-x86_64-minimal.iso | sha256sum
|
|
# (or `sha256sum` the file on iso-server). Set "none" to skip verification.
|
|
description = "ISO checksum as sha256:<hex> (or file:<CHECKSUM-url>, or none)."
|
|
default = "sha256:d338032cd1cdd41c67139f2f71b4c832c8e4a21943106519db9c7137df7a63d4"
|
|
}
|
|
|
|
variable "image_version" {
|
|
type = string
|
|
description = "Version stamp for the published artifact directory"
|
|
default = "0.1.0"
|
|
}
|
|
|
|
variable "artifact_dir" {
|
|
type = string
|
|
description = "Where extracted boot artifacts and rootfs tar are written"
|
|
default = "artifacts"
|
|
}
|
|
|
|
variable "nic_drivers" {
|
|
type = string
|
|
description = "Kernel modules for the fleet's real NICs (build VM only has virtio!)"
|
|
default = "igb igc ixgbe i40e ice mlx5_core"
|
|
}
|
|
|
|
variable "build_password" {
|
|
type = string
|
|
description = "Throwaway root password used only during the Packer build (must match http/ks.cfg)"
|
|
default = "packer-build-only"
|
|
sensitive = true
|
|
}
|
|
|
|
variable "oci_image" {
|
|
type = string
|
|
description = "OCI image ref for the release-artifact image (FROM scratch, holds UKI + rootfs.erofs)"
|
|
default = "registry.example.org/fleet-boot"
|
|
}
|
|
|
|
variable "oci_push" {
|
|
type = bool
|
|
description = "Push the release-artifact image to the registry after building"
|
|
default = false
|
|
}
|
|
|
|
variable "disable_dot1x" {
|
|
type = bool
|
|
description = "TEST ONLY: bake rd.dot1x=0 into the UKI cmdline so the host boots with 802.1X + MACsec disabled (open-port NVMe-oF boot test). Leave false for any real/secure build."
|
|
default = false
|
|
}
|
|
|
|
variable "nvmet_traddr" {
|
|
type = string
|
|
description = "NVMe/TCP discovery address baked into the UKI cmdline (rd.nvmf.discover). Use a per-environment DNS name resolved at boot so one UKI works everywhere; the lab serving stack's dnsmasq resolves this name to the serving host. Immutable once the UKI is signed."
|
|
default = "nvme.fleet.lab"
|
|
}
|
|
|
|
variable "ssh_authorized_key" {
|
|
type = string
|
|
description = "Single SSH public key authorized for the sudo-enabled admin user. Fleet-generic (same on every host — not per-host state). Baked into the immutable rootfs, so rotating it needs a rebuild. EMPTY = no admin user is created and root is left UNLOCKED (lab convenience, avoids a no-way-in image)."
|
|
default = ""
|
|
}
|
|
|
|
variable "admin_user" {
|
|
type = string
|
|
description = "Username of the sudo-enabled, key-only admin account baked into the image (wheel group, NOPASSWD sudo since the account has no password). Created only when ssh_authorized_key is set."
|
|
default = "fleetadmin"
|
|
}
|
|
|
|
variable "enable_kiosk" {
|
|
type = bool
|
|
description = "Build-time feature flag for the graphical kiosk (weston + Chromium + mesa). false (default, current) skips the kiosk provisioner entirely — much faster builds and a far smaller image — while iterating on the boot/network stack. Set true to bake the kiosk back in. (kiosk_url=\"\" also skips it; this flag lets you disable without losing the configured URL.)"
|
|
default = true
|
|
}
|
|
|
|
variable "kiosk_url" {
|
|
type = string
|
|
description = "URL the fullscreen Chromium kiosk loads at boot (weston session on tty1, baked into the immutable root). Defaults to the self-contained test page served by the lab boot HTTP server at /kiosk/ (boot.fleet.lab resolves to the serving host via the stack's dnsmasq) — validates the boot end-to-end without external connectivity. Empty disables the kiosk install entirely. A runtime config-agent could override it later via /etc/kiosk/kiosk.env."
|
|
default = "http://boot.fleet.lab/kiosk/"
|
|
}
|
|
|
|
variable "boot_splash" {
|
|
type = bool
|
|
description = "true (default): bake 'rhgb quiet' into the UKI cmdline so Plymouth shows a graphical boot splash (covers the network-auth + NVMe/TCP + verity phase). false: bake 'plymouth.enable=0' for a fully verbose console boot — use while debugging the boot chain. Plymouth itself is always in the image; this only flips the cmdline. (The cmdline is immutable once signed, so this is a build-time choice.)"
|
|
default = true
|
|
}
|
|
|
|
variable "sb_key" {
|
|
type = string
|
|
description = "Secure Boot signing key (PEM) for the UKI. Empty = unsigned lab build. Production: CI/HSM-held; never enters the build VM."
|
|
default = ""
|
|
sensitive = true
|
|
}
|
|
|
|
variable "sb_cert" {
|
|
type = string
|
|
description = "Secure Boot signing certificate (PEM) matching sb_key"
|
|
default = ""
|
|
}
|
|
|
|
# --------------------------------------------------------------------- source
|
|
|
|
source "qemu" "diskless" {
|
|
iso_url = var.iso_url
|
|
iso_checksum = var.iso_checksum
|
|
|
|
output_directory = "output-qemu"
|
|
vm_name = "diskless-build.qcow2"
|
|
disk_size = "12G"
|
|
format = "qcow2"
|
|
accelerator = "kvm"
|
|
memory = 4096
|
|
cpus = 2
|
|
headless = true
|
|
vnc_bind_address = "0.0.0.0"
|
|
qemuargs = [["-cpu", "host"],
|
|
[ "-serial", "stdio" ]]
|
|
|
|
http_directory = "http"
|
|
|
|
# BIOS/isolinux boot. For an OVMF/UEFI build VM, replace with the grub
|
|
# variant: ["c<wait>", "linuxefi /images/pxeboot/vmlinuz inst.text ",
|
|
# "inst.ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/ks.cfg<enter>",
|
|
# "initrdefi /images/pxeboot/initrd.img<enter>", "boot<enter>"]
|
|
boot_wait = "5s"
|
|
boot_command = [
|
|
"<up><wait><tab>",
|
|
" inst.text inst.ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/ks.cfg",
|
|
"<enter><wait>",
|
|
]
|
|
|
|
ssh_username = "root"
|
|
ssh_password = var.build_password
|
|
ssh_timeout = "60m"
|
|
shutdown_command = "shutdown -P now"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------- build
|
|
|
|
build {
|
|
sources = ["source.qemu.diskless"]
|
|
|
|
# --- 1. Stage the dracut module, configs, and CA placeholder -------------
|
|
# Pre-create the staging dir. Packer's directory-contents upload (a file
|
|
# provisioner whose source ends in "/") requires the destination to already
|
|
# exist; otherwise the uploaded tree lands outside /tmp/etc-stage and the
|
|
# install steps below can't find it.
|
|
provisioner "shell" {
|
|
inline = ["mkdir -p /tmp/etc-stage"]
|
|
}
|
|
|
|
provisioner "file" {
|
|
source = "files/dracut/95dot1x-macsec"
|
|
destination = "/usr/lib/dracut/modules.d/"
|
|
}
|
|
|
|
provisioner "file" {
|
|
source = "files/etc/"
|
|
destination = "/tmp/etc-stage"
|
|
}
|
|
|
|
# Kiosk unit + session wrapper (installed by the kiosk provisioner below).
|
|
# Dir-without-trailing-slash upload -> /tmp/kiosk/ (mirrors the dracut module
|
|
# upload form, which stages reliably).
|
|
provisioner "file" {
|
|
source = "files/kiosk"
|
|
destination = "/tmp/"
|
|
}
|
|
|
|
# --- 2. Packages, configs, diskless adaptations --------------------------
|
|
provisioner "shell" {
|
|
inline = [
|
|
"set -euxo pipefail",
|
|
|
|
# No dhcp-client: the initramfs now uses the 'network-manager' dracut
|
|
# module (90-diskless.conf), not 'network-legacy'/dhclient. NM renews the
|
|
# DHCP lease in the running system after switch-root, fixing the lease-
|
|
# expiry outage (legacy's one-shot lease was never renewed -> address
|
|
# dropped -> NVMe/TCP root died). NetworkManager is already pulled in by
|
|
# @^minimal-environment, so nothing extra is needed for it here.
|
|
# zstd: dracut compresses the initramfs with compress="zstd"
|
|
# (90-diskless.conf); the minimal ISO lacks the zstd binary, so without it
|
|
# dracut warns and silently falls back to gzip (a larger initramfs).
|
|
"dnf -y install wpa_supplicant tpm2-tools tpm2-tss nvme-cli cryptsetup dracut-network NetworkManager zstd openssl",
|
|
|
|
# Plymouth boot splash. plymouth-scripts provides plymouth-set-default-theme;
|
|
# spinner is the clean modern theme. Set the default theme BEFORE the dracut
|
|
# build below so the (-no-hostonly) initramfs bundles it; no -R here because
|
|
# we rebuild the initramfs explicitly. Whether the splash shows at boot is
|
|
# decided by the cmdline (boot_splash var), not by installing this.
|
|
"dnf -y install plymouth plymouth-scripts plymouth-theme-spinner",
|
|
"plymouth-set-default-theme spinner",
|
|
|
|
# Stage etc files into place
|
|
"install -D -m0644 /tmp/etc-stage/dot1x/radius-ca.pem /etc/dot1x/radius-ca.pem",
|
|
"install -D -m0644 /tmp/etc-stage/dot1x/wpa_supplicant-dot1x.conf /etc/dot1x/wpa_supplicant-dot1x.conf",
|
|
"install -D -m0644 /tmp/etc-stage/dracut.conf.d/90-diskless.conf /etc/dracut.conf.d/90-diskless.conf",
|
|
# NB: the old static /etc/NetworkManager/conf.d/99-dot1x-unmanaged.conf is
|
|
# GONE. It can't be mode-aware (it wrongly unmanaged en* in test mode too,
|
|
# blocking renewal). The 95dot1x-macsec cmdline hook nm-unmanaged.sh now
|
|
# writes /run/NetworkManager/conf.d/10-dot1x-unmanaged.conf at boot, only in
|
|
# secure mode, reserving the physical NIC for wpa_supplicant.
|
|
"chmod 0755 /usr/lib/dracut/modules.d/95dot1x-macsec/*.sh",
|
|
|
|
# Inject the fleet NIC drivers into the dracut config
|
|
"sed -i 's/@NIC_DRIVERS@/${var.nic_drivers}/' /etc/dracut.conf.d/90-diskless.conf",
|
|
|
|
# ---- Diskless adaptations ------------------------------------------
|
|
# Per-host machine-id is generated at boot into the overlay
|
|
"truncate -s 0 /etc/machine-id",
|
|
# Read-only erofs/verity root; runtime state on a tmpfs overlay provided
|
|
# by systemd.volatile=overlay (set in the cmdline by extract-artifacts.sh).
|
|
# RHEL/Rocky dracut has no 'overlayfs'/'rootovl' module.
|
|
"printf '%s\\n' '# diskless: root is provided by netroot; no local mounts' > /etc/fstab",
|
|
# No local bootloader regeneration wanted on kernel updates inside CI
|
|
"command -v grub2-mkconfig >/dev/null && echo 'GRUB updates not used on diskless hosts' || true",
|
|
|
|
# ---- Build the fleet-generic initramfs ------------------------------
|
|
# %%{ escapes the rpm query tags so Packer's HCL template engine treats
|
|
# them as literals (a bare %{...} is parsed as a template directive).
|
|
"KVER=$(rpm -q --qf '%%{VERSION}-%%{RELEASE}.%%{ARCH}\\n' kernel | sort -V | tail -1)",
|
|
"dracut -f --no-hostonly --kver \"$KVER\" /boot/initramfs-generic.img",
|
|
# Capture the listing to a file and grep THAT — never `lsinitrd | grep -q`.
|
|
# grep -q closes the pipe on first match, lsinitrd dies of SIGPIPE, and
|
|
# under pipefail the pipeline reports failure even when the file is
|
|
# present (a false negative that wrongly failed the build for many runs).
|
|
"lsinitrd /boot/initramfs-generic.img > /tmp/lsinitrd.txt",
|
|
"grep -E 'macsec|wpa_supplicant|tpm2_nvread|nvme.tcp|erofs|veritysetup' /tmp/lsinitrd.txt",
|
|
# Assert NetworkManager is in the initramfs — the network-manager dracut
|
|
# module is what renews the DHCP lease post-switch-root. A missing/renamed
|
|
# network module would silently regress to no-renewal, so fail loudly.
|
|
"grep -q 'NetworkManager' /tmp/lsinitrd.txt || { echo 'ERROR: NetworkManager missing from initramfs (network-manager dracut module not installed)' >&2; exit 1; }",
|
|
# Assert the secure-mode NM unmanaged hook + test-mode NVMe-oF connect unit
|
|
# made it in (systemd-driven initramfs → the connect is a systemd unit, not
|
|
# a dracut hook).
|
|
"grep -q 'nm-unmanaged.sh' /tmp/lsinitrd.txt || { echo 'ERROR: nm-unmanaged.sh hook missing from initramfs' >&2; exit 1; }",
|
|
"grep -q 'nvmf-connect.service' /tmp/lsinitrd.txt || { echo 'ERROR: nvmf-connect.service missing from initramfs' >&2; exit 1; }",
|
|
# Plymouth must be in the initramfs to splash during the early boot phase.
|
|
"grep -q 'plymouthd' /tmp/lsinitrd.txt || { echo 'ERROR: plymouth missing from initramfs (plymouth dracut module not installed)' >&2; exit 1; }",
|
|
|
|
# ---- Build hygiene ---------------------------------------------------
|
|
"dnf clean all",
|
|
"rm -rf /tmp/etc-stage /var/log/anaconda /root/anaconda-ks.cfg",
|
|
"truncate -s 0 /var/log/messages /var/log/secure 2>/dev/null || true",
|
|
]
|
|
}
|
|
|
|
# --- 2b. Kiosk mode: Wayland (weston kiosk-shell) + fullscreen Chromium --
|
|
# Post-pivot graphical kiosk (no initramfs impact). Baked into the immutable
|
|
# root, fleet-generic. Empty kiosk_url skips it. EPEL is enabled for weston +
|
|
# chromium — a supply-chain surface (accepted for the lab; see TODO #4).
|
|
# weston kiosk-shell is used because cage isn't packaged for EL9; weston 8.0
|
|
# (EPEL 9) ships kiosk-shell.so and is far lighter than GDM+gnome-kiosk.
|
|
# NOTE: pulls in Chromium + Mesa + weston (~hundreds of MB) — grows the
|
|
# uncompressed erofs namespace noticeably. Graphical bring-up needs a display
|
|
# to verify; likely tweak points: chromium ozone/wayland, --disable-gpu vs a
|
|
# real GPU, /dev/dri present in the VM, and SELinux AVCs for the GUI stack.
|
|
provisioner "shell" {
|
|
environment_vars = [
|
|
"ENABLE_KIOSK=${var.enable_kiosk}",
|
|
"KIOSK_URL=${var.kiosk_url}",
|
|
]
|
|
inline = [
|
|
"set -e",
|
|
"if [ \"$ENABLE_KIOSK\" != \"true\" ]; then echo 'enable_kiosk=false -> skipping kiosk install (smaller/faster build).'; exit 0; fi",
|
|
"if [ -z \"$KIOSK_URL\" ]; then echo 'kiosk_url empty -> skipping kiosk install.'; exit 0; fi",
|
|
"echo '==> Enabling EPEL + installing kiosk stack (weston, chromium, mesa)'",
|
|
"dnf install -y epel-release",
|
|
# tsflags=nodocs: the kickstart's --excludedocs only covers the anaconda
|
|
# install, not these provisioner transactions; without it chromium/mesa lay
|
|
# down man/doc into the read-only erofs. (mesa-dri-drivers + llvm-libs are
|
|
# the heaviest items here and are likely unnecessary given weston
|
|
# --use-pixman + chromium --disable-gpu — candidate to drop once the kiosk
|
|
# is display-verified; see TODO #4.)
|
|
"dnf install -y --setopt=tsflags=nodocs weston chromium mesa-dri-drivers mesa-libEGL mesa-libgbm dejavu-sans-fonts",
|
|
# Unprivileged, passwordless kiosk user; video/input as a seat fallback.
|
|
"id kiosk >/dev/null 2>&1 || useradd -m -G video,input kiosk",
|
|
# Install the staged unit, weston config, and session wrapper.
|
|
"install -D -m0755 /tmp/kiosk/kiosk-session /usr/local/bin/kiosk-session",
|
|
"install -D -m0644 /tmp/kiosk/kiosk.service /etc/systemd/system/kiosk.service",
|
|
"install -D -m0644 /tmp/kiosk/weston.ini /etc/xdg/weston/weston.ini",
|
|
# Chromium managed policy: stops the browser phoning home to Google
|
|
# (sign-in, variations, optimization-guide, component/auto update, metrics,
|
|
# safe browsing). EPEL chromium reads /etc/chromium/policies/managed/*.json.
|
|
# Pairs with the hardening flags in kiosk-session.
|
|
"install -D -m0644 /tmp/kiosk/chromium-kiosk-policy.json /etc/chromium/policies/managed/10-kiosk-lockdown.json",
|
|
# URL from the var (weston renderer is set via --use-pixman in the unit).
|
|
"install -d -m0755 /etc/kiosk",
|
|
"printf 'KIOSK_URL=%s\\n' \"$KIOSK_URL\" > /etc/kiosk/kiosk.env",
|
|
"chmod 0644 /etc/kiosk/kiosk.env",
|
|
# Label the new files so they survive into the enforcing erofs.
|
|
"restorecon -R /usr/local/bin/kiosk-session /etc/systemd/system/kiosk.service /etc/xdg/weston /etc/kiosk /etc/chromium",
|
|
# Boot graphical and start the kiosk (replaces the tty1 getty).
|
|
"systemctl enable kiosk.service",
|
|
"systemctl set-default graphical.target",
|
|
"rm -rf /tmp/kiosk",
|
|
"echo \"==> kiosk enabled: $KIOSK_URL\"",
|
|
]
|
|
}
|
|
|
|
# --- 2c. Admin access: sudo-enabled key user + locked root --------------
|
|
# Fleet-generic (same user + key on every host => NOT per-host state). The
|
|
# key comes from var.ssh_authorized_key (CI secret). If no key is supplied we
|
|
# skip the whole thing AND leave root unlocked, so a lab build can't end up
|
|
# with no way in. Runs LAST: locking root is safe because Packer drives
|
|
# shutdown over the already-authenticated SSH session (no re-auth as root).
|
|
provisioner "shell" {
|
|
environment_vars = [
|
|
"SSH_AUTHORIZED_KEY=${var.ssh_authorized_key}",
|
|
"ADMIN_USER=${var.admin_user}",
|
|
]
|
|
inline = [
|
|
"set -e",
|
|
"if [ -z \"$SSH_AUTHORIZED_KEY\" ]; then echo 'WARNING: ssh_authorized_key empty -> not creating admin user, leaving root UNLOCKED (lab build).'; exit 0; fi",
|
|
"id \"$ADMIN_USER\" >/dev/null 2>&1 || useradd -m -G wheel \"$ADMIN_USER\"",
|
|
"install -d -m0700 -o \"$ADMIN_USER\" -g \"$ADMIN_USER\" \"/home/$ADMIN_USER/.ssh\"",
|
|
"printf '%s\\n' \"$SSH_AUTHORIZED_KEY\" > \"/home/$ADMIN_USER/.ssh/authorized_keys\"",
|
|
"chmod 0600 \"/home/$ADMIN_USER/.ssh/authorized_keys\"",
|
|
"chown \"$ADMIN_USER:$ADMIN_USER\" \"/home/$ADMIN_USER/.ssh/authorized_keys\"",
|
|
"restorecon -R \"/home/$ADMIN_USER/.ssh\"",
|
|
# Key-only account (useradd leaves no password), so sudo must not prompt
|
|
# for one. NOPASSWD is a STIG finding (tracked in TODO #2) — acceptable for
|
|
# a key-only admin; reconcile when hardening lands.
|
|
"printf '%s ALL=(ALL) NOPASSWD:ALL\\n' \"$ADMIN_USER\" > \"/etc/sudoers.d/$ADMIN_USER\"",
|
|
"chmod 0440 \"/etc/sudoers.d/$ADMIN_USER\"",
|
|
"visudo -cf \"/etc/sudoers.d/$ADMIN_USER\"",
|
|
"restorecon \"/etc/sudoers.d/$ADMIN_USER\"",
|
|
# Image sshd: no root login, key-only. A drop-in (read via Include before
|
|
# the main config) overrides the ks's PermitRootLogin yes. Takes effect on
|
|
# the next boot only (sshd isn't restarted), so the live build session is
|
|
# unaffected.
|
|
"printf 'PermitRootLogin no\\nPasswordAuthentication no\\nKbdInteractiveAuthentication no\\n' > /etc/ssh/sshd_config.d/50-fleet-hardening.conf",
|
|
"chmod 0644 /etc/ssh/sshd_config.d/50-fleet-hardening.conf",
|
|
"restorecon /etc/ssh/sshd_config.d/50-fleet-hardening.conf",
|
|
# Guarantee sshd is enabled (it's our ONLY way in once root is locked and
|
|
# the admin user is key-only). Don't rely on the distro preset. Validate
|
|
# the full sshd config so a bad drop-in fails the BUILD, not the boot.
|
|
"systemctl enable sshd.service",
|
|
"sshd -t",
|
|
# Lock root in the image. Done last; admin access is via the key user.
|
|
"passwd -l root",
|
|
"echo \"root locked; admin user $ADMIN_USER created (wheel + NOPASSWD sudo, key-only).\"",
|
|
]
|
|
}
|
|
|
|
# --- 2d. Final image hygiene: strip caches/logs after the LAST dnf step --
|
|
# MUST be the last provisioner. The `dnf clean all` in step 2 runs BEFORE the
|
|
# kiosk (2b) and admin (2c) provisioners, and 2b enables EPEL + installs
|
|
# weston/chromium/mesa — repopulating /var/cache/dnf (EPEL metadata alone is
|
|
# ~100MB+). This tree becomes the read-only, UNCOMPRESSED erofs served over
|
|
# NVMe/TCP, so every cache byte is dead weight read on every boot of every
|
|
# host. Clear the repo *cache* dirs only — never /var/lib/rpm or
|
|
# /var/lib/dnf/history (the rpmdb keeps the image introspectable/patchable).
|
|
provisioner "shell" {
|
|
inline = [
|
|
"set -eux",
|
|
"dnf clean all",
|
|
"rm -rf /var/cache/dnf/* /var/cache/PackageKit /var/cache/ldconfig/*",
|
|
"rm -rf /var/lib/dnf/repos/* /var/log/dnf.* /var/log/hawkey.log",
|
|
"rm -rf /var/tmp/* /tmp/*",
|
|
"truncate -s 0 /var/log/messages /var/log/secure 2>/dev/null || true",
|
|
# Docs/man/info: the kickstart used --excludedocs but the provisioner dnf
|
|
# installs (kiosk + admin) did not, so strip what they laid down.
|
|
"rm -rf /usr/share/doc/* /usr/share/man/* /usr/share/info/*",
|
|
# Chromium ships ~50 locale .pak files; a single-locale kiosk needs one.
|
|
"if [ -d /usr/lib64/chromium-browser/locales ]; then find /usr/lib64/chromium-browser/locales -type f -name '*.pak' ! -name 'en-US.pak' -delete; fi",
|
|
# App translation catalogs (.mo) for locales this fleet never uses.
|
|
"[ -d /usr/share/locale ] && find /usr/share/locale -mindepth 1 -maxdepth 1 -type d ! -name 'en*' ! -name 'C*' -exec rm -rf {} + || true",
|
|
]
|
|
}
|
|
|
|
# --- 3. Extract kernel, initramfs, rootfs from the built disk ------------
|
|
# --- 4. Assemble + sign the UKI; build the OCI release-artifact image ----
|
|
post-processor "shell-local" {
|
|
inline = [
|
|
# extract-artifacts MUST run as root so tar/mkfs.erofs preserve the build
|
|
# tree's real ownership (root:ssh_keys, tty/crontab setgid groups, user
|
|
# home dirs) — we dropped mkfs.erofs --all-root, which used to mask this by
|
|
# flattening everything to root:root. sudo is a no-op if already root, and
|
|
# the runner has passwordless sudo (cf. the `sudo apt-get` install step).
|
|
"sudo bash -c \"NVMET_TRADDR='${var.nvmet_traddr}' BOOT_SPLASH='${var.boot_splash}' bash scripts/extract-artifacts.sh output-qemu/diskless-build.qcow2 ${var.artifact_dir}/${var.image_version} ${var.disable_dot1x}\"",
|
|
# Hand the OUTPUT FILES back to the build user (their in-erofs ownership is
|
|
# already baked correctly) so build-oci.sh can write the UKI and push with
|
|
# the build user's `buildah login`.
|
|
"sudo chown -R \"$(id -u):$(id -g)\" ${var.artifact_dir}",
|
|
"bash scripts/build-oci.sh ${var.artifact_dir}/${var.image_version} ${var.oci_image} ${var.image_version} '${var.sb_key}' '${var.sb_cert}' ${var.oci_push}",
|
|
]
|
|
}
|
|
}
|