v0.4.69: PNG boot-menu background (iPXE built from source), NFS AUTH_SYS, FleetDM logo

Three things, headlined by the long-blocked graphical PXE menu.

## 1. Graphical PXE boot background — the iVentoy feature, finally

iVentoy paints a PNG background on the PXE screen using stock iPXE
built with CONSOLE_FRAMEBUFFER + IMAGE_PNG + CONSOLE_CMD; the public
iPXE binaries omit those, so `console --picture` is a no-op on them.
We now build our own iPXE from upstream with that thin config delta
(deploy/ipxe/local/{general,console}.h).

The 8-release blocker was cc1 segfaulting when an amd64 gcc ran under
QEMU emulation on the arm64 build host. Fix: a new `ipxe-build`
Dockerfile stage pinned to $BUILDPLATFORM (native arch — no emulation)
that cross-compiles x86_64 iPXE with CROSS_COMPILE=x86_64-linux-gnu-.
The compiler runs native and emits x86_64. Validated end-to-end:
png.o + fbcon.o + pixbuf.o all compile and link (confirmed via the
linked-ELF symbol table, not just strings), ~112s, no segfault. Host
tools needed libc6-dev (dropped by --no-install-recommends; without
it the native host compile falls through to iPXE's freestanding
headers and dies on bits/stdint.h — fixed).

Server side:
- pxe_logo.rs is now a full-screen background compositor: a dark field
  (matching the WebUI theme) with the operator's uploaded logo across
  the top, or — with no upload — a default OpenPXE rainbow disc drawn
  with pure pixel math (no font/SVG deps). Always 1024x768 (iPXE
  doesn't scale; this is the universal mode). WebP/JPEG/GIF/PNG in,
  PNG out (iPXE only eats PNG).
- /branding/pxe-logo always returns a PNG now (default when no logo,
  default when SVG) so the menu always has a background.
- render_menu uses `console --picture … --top 290 || console`: paints
  the background and reserves the logo band on PNG-capable binaries
  (x86_64 UEFI), cleanly falls back to text on the others. The ASCII
  wordmark is GONE.

Only x86_64 UEFI is built from source (host-arch-agnostic cross build);
BIOS/i386/arm64 keep upstream-fetched no-PNG binaries + text fallback.
Modern clients are overwhelmingly x86_64 UEFI.

## 2. NFS AUTH_SYS credential — fixes NFS3ERR_ACCES

v0.4.68's privileged-port fix got past MNT3ERR_ACCES (mount); operators
then hit NFS3ERR_ACCES on READDIR because nfs3_client defaults to
AUTH_NONE and virtually every server exports sec=sys. We now present an
AUTH_UNIX credential (uid 0 / gid 0): no_root_squash servers treat us
as root, root_squash servers map us to anon which reads any
world-readable ISO share. Kept fixed (no UI knob) to stay dead-simple.
Hint updated: a remaining NFS3ERR_ACCES is now a server-side
permission/squash issue, not IP/auth-flavor.

## 3. FleetDM-style full-width logo (top-left)

When a custom logo is uploaded the sidebar header drops the bundled
mark + "OpenPXE" wordmark and lets the logo span the header
(left-aligned, capped 200x50, contain). Rendered server-side via a
brand-class in index_html (has_custom_logo) so there's no flash of the
default. The bundled-default case is unchanged.

Tests: 164 passing. clippy -D warnings clean. iPXE build stage
validated in isolation before the full image build.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
Miles Ward
2026-05-29 03:11:35 -04:00
co-authored by Claude Opus 4.8
parent 2f12a2ae84
commit 1eb41288c3
14 changed files with 569 additions and 214 deletions
+32 -3
View File
@@ -66,6 +66,7 @@ use nfs3_types::nfs3::{
self as nfs3, diropargs3, entry3, filename3, nfs_fh3, GETATTR3args, LOOKUP3args,
Nfs3Result, READ3args, READDIR3args,
};
use nfs3_types::rpc::{auth_unix, opaque_auth};
use nfs3_types::xdr_codec::Opaque;
use openpxe_core::{Error, Result};
use parking_lot::Mutex;
@@ -741,9 +742,28 @@ async fn connect_once(
nfs3_client::Nfs3Connection<nfs3_client::tokio::TokioIo<tokio::net::TcpStream>>,
NfsClientError,
> {
// v0.4.69: present an AUTH_SYS (AUTH_UNIX) credential instead of
// the crate default (AUTH_NONE). This is the fix for the
// `NFS3ERR_ACCES` operators hit *after* the v0.4.68 privileged-port
// fix got them past the mount: nearly every NFS server exports
// `sec=sys` and rejects AUTH_NONE callers on the actual file ops
// (READDIR/LOOKUP/READ) even when MOUNT succeeded. We send uid 0 /
// gid 0 — a server with `no_root_squash` treats us as root (full
// read), and the far more common `root_squash` maps us to the
// anonymous user, which can still read any world-readable ISO
// share (the normal case). We deliberately keep this fixed rather
// than a UI knob: ISO libraries are read-only shared data, and a
// uid field is exactly the kind of thing that makes a "dead simple"
// tool confusing for an L1 tech.
let cred = opaque_auth::auth_unix(&auth_unix {
uid: 0,
gid: 0,
..Default::default()
});
let fut = Nfs3ConnectionBuilder::new(TokioConnector, server, export)
.connect_from_privileged_port(privileged)
.nfs3_port(port)
.credential(cred)
.mount();
match tokio::time::timeout(CONNECT_TIMEOUT, fut).await {
Ok(Ok(conn)) => Ok(conn),
@@ -839,10 +859,19 @@ fn hint_for(text: &str) -> Option<String> {
.into(),
)
} else if s.contains("nfs3err_acces") || s.contains("permission denied") {
// The mount succeeded but a file op was denied. OpenPXE
// already presents an AUTH_SYS uid-0 credential, so this is a
// server-side permission/squash issue, not an IP or auth-flavor
// one. Point the operator at the share's filesystem permissions.
Some(
"the NFS server rejected this client. Most likely your export \
is restricted by client IP — add this OpenPXE host (or its \
subnet) to the export's allowed-clients list on the server."
"the mount succeeded but the server denied reading the share \
(NFS3ERR_ACCES). OpenPXE connects as AUTH_SYS uid 0, so this is \
a server-side permission issue: make sure the export's \
directory is readable (most ISO shares are world-readable / \
0755), and that the export isn't restricted to a specific \
non-root user via all_squash/anonuid. On UniFi UNAS, confirm \
the share's NFS permission for this host is Read-Write (or \
Read-Only) and that the share itself grants read access."
.into(),
)
} else if s.contains("nfs3err_noent")