Files
diskless-workstation/.gitea/workflows/ipxe-image.yaml
T

125 lines
4.8 KiB
YAML

#
# .gitea/workflows/ipxe-image.yaml
#
# Builds the fleet-ipxe image: the iPXE network bootloader (snponly.efi) for the
# PXE-only chainload path (firmware without UEFI HTTP boot, e.g. EliteBook 820
# G4), SIGNED with the fleet `db` key and packaged as a tiny FROM-scratch OCI
# image. The dnsmasq serving container mounts it at /ipxe and copies snponly.efi
# into its TFTP root — so the serving host never carries a loose, unsigned .efi
# and the bootloader is digest-pinned + signed exactly like the UKI.
#
# Kept separate from the (generic) dnsmasq image so the daemon image stays
# key-agnostic and this one is the only per-Secure-Boot-environment artifact.
# See scripts/build-ipxe-oci.sh, docs/secure-boot-keys.md, CLAUDE.md invariant #9.
#
# Runs only when the iPXE inputs change (path filter) or on demand.
#
# Required Gitea repo secrets: PACKAGE_USER, PACKAGE_TOKEN (registry creds).
# Optional: SB_KEY, SB_CERT (db PRIVATE key + cert, PEM). Unset => UNSIGNED lab
# image (loud warning; Secure-Boot-enforcing hosts will reject it). PK/KEK
# never come near CI — only `db` signs (docs/secure-boot-keys.md).
#
name: Build fleet-ipxe image
run-name: ${{ gitea.actor }} builds the signed fleet-ipxe bootloader image
on:
push:
branches:
- "main"
paths:
- "scripts/build-ipxe-oci.sh"
- ".gitea/workflows/ipxe-image.yaml"
workflow_dispatch:
inputs:
ipxe_version:
description: "Override the image tag (default: sanitized ipxe package version)"
required: false
env:
IPXE_IMAGE: "git.bitasylum.org/audiobuzz/fleet-ipxe"
jobs:
ipxe-build:
name: Build & push fleet-ipxe image
runs-on: ubuntu-latest
container:
image: git.bitasylum.org/audiobuzz/ubuntu-act-runner-qemu:main
steps:
-
name: Checkout
uses: actions/checkout@v6
-
name: Install tooling
# buildah (OCI build), sbsigntool (sbsign/sbverify — Debian singular), and
# the ipxe package which ships the prebuilt snponly.efi.
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends buildah sbsigntool ipxe
-
name: Locate snponly.efi + derive tag
id: src
run: |
# snponly.efi: UEFI x64 iPXE that rides the firmware's NIC driver (SNP),
# so it binds where iPXE's native driver won't (e.g. I219-LM). Ubuntu's
# ipxe package installs it under /usr/lib/ipxe.
SNP=$(find /usr/lib/ipxe /usr/share/ipxe -name 'snponly.efi' 2>/dev/null | head -1)
[ -n "$SNP" ] || { echo "snponly.efi not found in the ipxe package" >&2; exit 1; }
echo "snponly=$SNP" >> "$GITHUB_OUTPUT"
# Tag: dispatch override, else the ipxe package version sanitized to the
# OCI tag charset ([A-Za-z0-9_.-]).
RAW=$(dpkg-query -W -f='${Version}' ipxe 2>/dev/null || echo unknown)
VER="${{ inputs.ipxe_version }}"
[ -n "$VER" ] || VER=$(printf '%s' "$RAW" | sed 's/[^A-Za-z0-9_.-]/-/g')
echo "version=$VER" >> "$GITHUB_OUTPUT"
echo "Using snponly.efi=$SNP tag=$VER (ipxe pkg $RAW)"
-
name: Materialize Secure Boot signing material
# Same db key that signs the UKI; written to a runner-temp file and passed
# to build-ipxe-oci.sh as a path. If SB_KEY is unset this no-ops and the
# build produces an UNSIGNED (lab) image with its loud warning.
if: ${{ secrets.SB_KEY != '' }}
env:
SB_KEY: ${{ secrets.SB_KEY }}
SB_CERT: ${{ secrets.SB_CERT }}
run: |
umask 077
d="$RUNNER_TEMP/sb"; mkdir -p "$d"
printf '%s\n' "$SB_KEY" > "$d/db.key"
printf '%s\n' "$SB_CERT" > "$d/db.crt"
echo "SB_KEY_FILE=$d/db.key" >> "$GITHUB_ENV"
echo "SB_CERT_FILE=$d/db.crt" >> "$GITHUB_ENV"
echo "Secure Boot signing material staged (signed fleet-ipxe build)."
-
name: Registry login
run: |
buildah login \
--username "${{ secrets.PACKAGE_USER }}" \
--password "${{ secrets.PACKAGE_TOKEN }}" \
git.bitasylum.org
-
name: Build & push
env:
STORAGE_DRIVER: vfs
run: |
VER="${{ steps.src.outputs.version }}"
bash scripts/build-ipxe-oci.sh \
"${{ steps.src.outputs.snponly }}" \
"${IPXE_IMAGE}" \
"$VER" \
"${SB_KEY_FILE:-}" \
"${SB_CERT_FILE:-}" \
true
# also publish :latest for lab convenience (pin by digest in production)
buildah tag "${IPXE_IMAGE}:${VER}" "${IPXE_IMAGE}:latest"
buildah push "${IPXE_IMAGE}:latest"
-
name: Shred Secure Boot signing key
if: always()
run: shred -u "$RUNNER_TEMP/sb/db.key" 2>/dev/null || rm -f "$RUNNER_TEMP/sb/db.key" 2>/dev/null || true