v0.5.0
11
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
fc99973ac3 |
v0.5.0: Wake-on-LAN, webhook notifications, Advanced tab, login logo, update check
Closes the v0.4.x chapter — NFS works end to end. Five additions: ## Wake-on-LAN (Hosts → Bound hosts) - New core::wol module: parse any MAC form, build the 102-byte magic packet, broadcast it. No special capability needed (ephemeral source port; SO_BROADCAST). Sends to the limited broadcast (255.255.255.255) AND the server's own subnet broadcast (computed from advertised IP + detected mask) so it reaches the right VLAN. - POST /api/hosts/:mac/wol — only fires for *bound* MACs (404 otherwise) so it's not an open packet sprayer. - Bound-hosts table grows a "Wake" button with inline Waking…/Sent ✓ state. ## Webhook notifications (Advanced tab) - core::notify: NotifyConfig + NotifyStore (notify.json), one provider at a time — Slack / Discord / Teams (incoming-webhook JSON) or SMTP. SMTP password is persisted but redacted on GET behind a __keep__ sentinel the UI round-trips so the secret never leaves the box. - http-api::notify: delivery — reqwest POST for chat (provider-shaped bodies), lettre for SMTP (rustls, STARTTLS/implicit TLS, no plaintext). 10s timeout; every send is best-effort. - GET/PUT /api/notify, POST /api/notify/test. - Fired fire-and-forget on the canonical "machine is imaging" boot event and on WoL — never blocks the boot path. ## UI: Advanced tab - New nav item. Holds the webhook config card and the API reference block (relocated from the bottom of Settings). ## UI: login/setup logo (FleetDM treatment) - /api/me now returns has_custom_logo + logo_rev (public bootstrap). The login, setup, and connection-error cards render the uploaded logo full-width with the "OpenPXE" wordmark dropped — matching the sidebar. ## About: update check + licenses - "Check for updates" button → GET /api/updates/check queries the Gitea releases API (derived from CARGO_PKG_REPOSITORY) and compares to the running version. Strictly on-demand — no background polling, keeps the air-gapped promise. - License card documents the MIT OR Apache-2.0 dual license with links, plus a note on bundled components (iPXE GPLv2/UBDL, samba, wimtools). Deps: lettre (SMTP, rustls) + reqwest gains the json feature. Both rustls so the static musl binary stays OpenSSL-free. Tests: 179 passing (+notify round-trip/redaction, webhook validation, WoL-unbound-404, WoL packet loopback, version-compare). clippy clean. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> |
||
|
|
3f9d8568f0 |
v0.4.67: NFSv3 alongside SMB (in-process via nfs3_client crate)
NFS is back — done right this time. v0.4.67 ships a pure-Rust NFSv3
client (`nfs3_client` 0.9 from the xetdata/Vaiz crate family) running
in-process inside the openpxe binary. No `mount.nfs`, no kernel
modules, no `CAP_SYS_ADMIN`, no subprocess. Works in every container
that the v0.4.65 SMB path works in (Unraid included).
The v0.4.65 SMB path stays as-is. Operators get both protocols
side-by-side and pick whichever their NAS prefers — or use both
together. NFSv3 has one architectural advantage over the SMB
userspace path: HTTP Range requests work for NFS-sourced ISOs
because NFSv3 READ3 takes an explicit offset. SMB-sourced ISOs still
return 416 for ranges (smbclient CLI can't seek mid-stream).
## What's new
- `crates/iso-store/src/nfs_share.rs` — `NfsShareManager` mirroring
`SmbShareManager` structurally. Lists ISOs via READDIR3+LOOKUP3+
GETATTR3, streams files via READ3 in 64 KiB chunks piped to axum
body streams. Uses `connect_from_privileged_port(false)` because
the openpxe binary runs as uid 10001 — most modern NFS servers
allow that; a server that demands privileged ports needs
`insecure` in /etc/exports, and the hint translation calls that
out specifically.
- `IsoSource::Nfs { share_id, relative_path }` variant alongside the
existing `Smb`. `IsoStore::iso_path_for` returns None for both;
the HTTP handler dispatches to the right share manager.
- `/api/nfs-shares` CRUD + scan endpoints, parallel to
`/api/smb-shares`. `POST` body: `{ server, export, port? }`.
- `nfs` terminal command back (this time as in-process, not kernel
mount): `list | add <srv>:<export> [port] | remove | scan`. The
v0.4.64 `nfs` command name pointing at kernel mount is moot
history — same name, completely different mechanism.
- Storage tab: a new NFS shares card sits directly below the SMB
shares card. The form is simpler (no auth fields) since NFSv3
uses AUTH_SYS and access is gated server-side by client IP.
- Dashboard "Images available" tile sums SMB + NFS reachable shares
into a generic "N remote shares" line.
## What's the same
- The structured `{error, stderr, hint}` JSON shape on failures
matches the SMB API exactly, so the UI's error banner renders
identically.
- Hint translation: NFS3ERR_ACCES → "exports list", NFS3ERR_NOENT →
"export path doesn't exist", `mount denied` → "/etc/exports may
need `insecure`", timeouts → "check IP/port/firewall".
- Persistence: `<work_dir>/nfs_shares.json`. No conflict with the
long-dead v0.4.64 `nfs.json`.
## Why nfs3_client
User picked it: pure-Rust matches the architecture, NFSv3 covers the
real-world cases, AUTH_SYS keeps the UI simple. The crate is at
0.9.0, MIT/Unlicense, rust-version 1.88 (we're on 1.95). Tokio
feature flag enabled. Image size unchanged at compile time — single
musl static binary, no extra OS packages.
## Tests
160 passing (was 150 in v0.4.66, +10):
- nfs_share parser: stable share ids, server normalization (smb://,
cifs://, \\, // all stripped).
- hint_for(): NFS3ERR_ACCES, NFS3ERR_NOENT, mount denied, unknown.
- status_label() covers the common nfsstat3 codes.
- HTTP integration: nfs-shares list starts empty, missing server
rejected, export without leading slash rejected.
`cargo clippy --workspace --all-targets -- -D warnings` clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
|
||
|
|
900b65b3ec |
v0.4.65: swap kernel-mount NFS for userspace SMB (smbclient)
v0.4.64's NFS path didn't work on Unraid even with --privileged
because Unraid's base kernel ships without the nfs/nfsv4 client
modules — and no container-side configuration can load a host kernel
module. SMB has the same kernel-mount problem (`mount -t cifs` needs
the cifs module) but it also has a usable *userspace* client: Samba's
`smbclient` CLI, which speaks the SMB protocol over a plain TCP socket
with no kernel involvement. This is the same approach Bootimus uses,
and works in every container regardless of host kernel modules or
container capabilities.
What's gone:
* `crates/iso-store/src/nfs.rs` (in entirety)
* `NfsManager`, `NfsMount`, `NfsAddRequest`, `NfsVersion` types
* `IsoSource::Nfs` variant
* `IsoStore::nfs_root` / `IsoStore::set_nfs_root`
* `/api/nfs`, `/api/nfs/:id`, `/api/nfs/:id/scan` routes
* `nfs` terminal command
* Storage tab's NFS shares card and the v0.4.64 fstab-options
diagnostics work (the whole error path is moot now)
What's new:
* `crates/iso-store/src/smb_share.rs` — `SmbShareManager` that drives
`smbclient` as a subprocess. Indexes shares via `smbclient -c "ls
*.iso"` and streams files via `smbclient -c "get file -"` piped
straight into HTTP response bodies. No local cache, no double disk
usage.
* `IsoSource::Smb { share_id, relative_path }` variant.
* `IsoStore::iso_path_for` returns None for SMB sources — the HTTP
ISO download handler dispatches on the source kind and streams via
the SmbShareManager when it's SMB.
* `/api/smb-shares` + `/api/smb-shares/:id` + `/api/smb-shares/:id/scan`
routes.
* `share` terminal command (`list | add //srv/share [auth] | remove |
scan`). Auth spec is `guest` or `user:password`.
* Storage tab: SMB shares card replaces the NFS one. Two-column form
for server + share name, three-column form for guest checkbox /
username / password. Username and password fields auto-disable when
Guest is checked.
* Credentials live under <work_dir>/smb_creds/<id>.cred at 0600
permissions so they don't leak through `ps`. Persisted state at
<work_dir>/smb_shares.json (sans password — re-entered on add /
re-scan).
Why subprocess and not a Rust crate:
* The Debian runtime image already ships the `samba` package
(Dockerfile line 84) — `smbclient` is right there.
* Library options (pavao, etc.) wrap libsmbclient so they still pull
in the same C library at runtime.
* Subprocess gives operators a verifiable mental model — anything
OpenPXE can do over SMB, they can reproduce by running `smbclient`
manually at a shell.
Range-request limitation, called out in the smb_share.rs module docs
and the UI explainer: `smbclient -c 'get file -'` is a sequential
whole-file stream. HTTP range requests on SMB-sourced ISOs return
416. PXE workloads (iPXE chain, casper sanboot, wimboot) do
whole-file sequential reads, so this works in practice. A follow-up
release can add libsmbclient-based seek if a real workload needs it.
Stderr-to-hint translation patterns mirror v0.4.64's NFS work:
NT_STATUS_LOGON_FAILURE → "check credentials", BAD_NETWORK_NAME →
"check share name", connection refused / timeout → "verify
reachability + firewall", etc. UI renders the raw smbclient error
plus the hint as two lines.
Tests (149 total, was 142 in v0.4.64):
* smb_share parser tests covering ISO + skipped directory, filenames
with spaces, non-ISO filtering.
* hint_for() translation tests for the dominant NT_STATUS codes.
* Server normalization (smb://, cifs://, \\, // prefixes all stripped).
* HTTP integration: shares list starts empty, invalid server / missing
username / path in share name all rejected with actionable hints.
`cargo clippy --workspace --all-targets -- -D warnings` clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
|
||
|
|
07e7c18698 |
Revert "v0.4.65: Local directory ISO source (bind-mount workaround for Unraid)"
This reverts commit
|
||
|
|
761489761c |
v0.4.65: Local directory ISO source (bind-mount workaround for Unraid)
Field report: even with CAP_SYS_ADMIN and full --privileged, NFS mounts
inside the OpenPXE container fail on Unraid with the same
"failed to apply fstab options" error v0.4.64 added diagnostics for.
The root cause is the host kernel: Unraid's base kernel ships without
the nfs/nfsv4 client modules loaded. Capabilities are necessary but
not sufficient; the modules have to be present on the host kernel for
in-container mount(2) to do anything. No container-side change can
fix that.
This is exactly the case every other PXE/imaging tool sidesteps
(Bootimus uses SMB; iVentoy, FOG, MAAS, Cobbler all rely on the host
to mount network storage and bind-mount the path into the imaging
service). v0.4.65 brings OpenPXE in line with that pattern.
What's new:
* `IsoSource::LocalDir { dir_id, relative_path }` — third source kind
alongside `Local` (uploaded) and `Nfs` (in-container mount).
* `LocalDirManager` (crates/iso-store/src/local_dir.rs) — registers
bind-mounted directories, validates them (absolute path, exists, is
a directory, readable), scans for *.iso files, registers them with
IsoStore. Persisted to <work_dir>/local_dirs.json so the relationship
survives restarts.
* `NfsHostCaps::detect()` — pure read of /proc/filesystems on startup.
Surfaced via GET /api/nfs/capabilities and used by the Storage tab to
show a prominent red banner above the NFS form when in-container
mounts cannot possibly work, pointing the operator at the Local
Directories card as the recommended path.
* Four new API routes:
GET /api/nfs/capabilities
GET /api/local-dirs
POST /api/local-dirs { path, label? }
DELETE /api/local-dirs/:id
POST /api/local-dirs/:id/scan
UI changes (crates/webui/src/app.js):
* Storage tab: new "Local directories" card under the NFS card with
the bind-mount form, an explainer paragraph (with the Docker
`-v /mnt/user/isos:/mnt/external-isos` command), and the list of
registered directories with rescan + remove actions.
* When NFS host caps are unavailable, the NFS card sprouts a red
banner explaining what's wrong and pointing at the local-dir
workaround. The card sub-header also flips to "N registered ·
recommended on this host".
* ISO table: new "dir:<id>" source badge; on-disk ISOs show "on disk"
in the actions column instead of a delete button (same pattern as
NFS — OpenPXE doesn't own those bytes).
* API reference table picks up the four new endpoints + a hint about
the new `port` field on NFS add.
Tests (+12, total 162):
* iso-store: 7 local_dir unit tests covering relative-path rejection,
missing path, non-directory file, empty-directory success, default
label, idempotent re-add, remove + iso-path-resolution clear.
* iso-store: 1 nfs unit test confirming NfsHostCaps::detect() never
panics and the boolean accessors are consistent.
* http-api: 4 integration tests covering /api/nfs/capabilities,
/api/local-dirs list/add/remove + relative-path 400.
`cargo clippy --workspace --all-targets -- -D warnings` clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
|
||
|
|
a1518110ed |
v0.4.5: VMware UEFI fix, static musl binary, Forms auth + SSO config
VMware UEFI / Casper boot fix:
- Linux cmdline for Debian/Ubuntu/Mint/Pop!_OS/elementary now uses the
canonical Casper `iso-url=` option and `ds=nocloud`, matching the
fix Bootimus shipped in v0.1.67. The previous
`boot=casper netboot=url url=… ip=dhcp ---` form booted fine on
bare-metal UEFI but hung at "cloud-init running" on VMware guests
because subiquity / cloud-init can't reach a metadata datasource
through PXE.
Static binary (matches Bootimus v0.1.70):
- Dockerfile build stage now compiles against
x86_64-unknown-linux-musl. The resulting /openpxe has no glibc
dependency at all; the runtime stage still ships Debian slim for the
samba/wimtools/nfs-common shellouts, but a future scratch/distroless
variant is now a one-line swap. Cuts a class of "GLIBC_2.39 not
found" surprises on older RHEL/Rocky hosts.
Forms auth (Sonarr/Radarr-style):
- New AdminStore in openpxe-core: single admin record persisted to
<work_dir>/auth.json, bcrypt-hashed credentials, rotation requires
current password.
- New SessionStore in openpxe-http-api: in-memory UUID-keyed sessions
with 24h sliding TTL, openpxe_session HttpOnly cookie.
- Endpoints: POST /api/setup (first-run), POST /api/login, POST
/api/logout, GET /api/me, PUT /api/me/credentials (rotates and
revokes every other session).
- Auth middleware gates /api/* once the admin is configured;
passes through entirely until then (tests + fresh installs ride this
path). Allowlists PXE-essential paths (/boot.ipxe, /iso/*, /ipxe/*,
/api/queue/join, /api/queue/poll/*) so iPXE clients still work
without a cookie they can't send.
- WebUI: first-run setup card, login card, logout chip in the sidebar
footer, Account card in Settings for rotating creds. Auth screen is
fully styled (centered narrow card, matches Sonarr layout).
SSO config (FleetDM-shaped, storage-only):
- New SsoStore in openpxe-core: { enabled, idp_name, metadata,
metadata_url } persisted to <work_dir>/sso.json with size caps and
URL-scheme validation.
- Endpoints: GET /api/sso, PUT /api/sso. Validation: enabling SSO
without either metadata or metadata_url returns 400.
- WebUI: SSO card in Settings with a URL-vs-XML mode switch and an
inert "Sign in with X" button on the login screen while runtime
flow is pending. Per the brief: no Entity ID field (defaults to the
advertised public_base_url internally when SAML wiring lands).
Quality:
- 132 tests passing (was 106 in v0.4.4): +5 auth unit tests, +5 SSO
unit tests, +7 auth integration tests, +1 SSO integration test, +1
regression guard pinning the new Casper cmdline.
- cargo clippy --workspace --all-targets clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
|
||
|
|
7b972dc049 |
v0.4.4: Settings tab, API reference, ISO category, branding, disk space
Settings:
- New top-level Settings tab. Carries a placeholder for the planned
LDAP / OIDC / user-management work, the new branding controls, and
the API reference at the bottom.
- Custom logo upload (PNG/SVG/JPEG/WebP/GIF up to 2 MB) replaces the
bundled brand mark via /assets/logo.svg; bytes live at
<work_dir>/branding/ and survive restart. The original "OpenPXE
v<x.y.z>" pins to the sidebar footer for support.
- API reference rendered from a new GET /api/docs into a per-method
coloured pill list grouped by area.
ISO category (Storage):
- New IsoCategory { Os, Tools } on IsoMeta with PUT
/api/isos/:id/category. Storage table's Type cell becomes a
dropdown; selecting Tools moves the ISO into the Tools submenu next
to memtest / shell / NIC info and removes it from the OS Installers
family submenu. Family detection still drives BIOS/UEFI / kernel
args; only the menu placement changes.
Storage telemetry:
- New IsoStore::disk_usage (libc::statvfs, lives in iso-store so the
http-api crate stays #![forbid(unsafe_code)]) and GET
/api/storage/disk. The Storage tab now shows free/used/total for
the volume hosting the ISO directory with an 80%/95% colour ramp.
UI polish:
- Brand block in the sidebar now matches the topbar height exactly,
so the divider runs straight across the top of the app rather than
stepping; version label moved out of the brand and pinned to the
sidebar footer ("OpenPXE v0.4.4").
- Light-mode terminal: --terminal-bg + per-level text colours track
the active theme rather than being hard-coded dark.
- About: lead paragraph spans the full content width; new Docs row
links to https://openpxe.com/.
106 tests passing (was 89 in v0.4.1, +17 across branding unit tests
and new integration coverage for category / disk / docs / branding).
cargo clippy --workspace --all-targets clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
|
||
|
|
2c1c80a7ca |
v0.4.1: harden ISO uploads and beta UI polish
Add browser-safe chunked ISO uploads with progress, partial-file visibility, offset validation, and abort cleanup while keeping the legacy multipart endpoint for API clients. Record host-log validation coverage, keep the queue/status UI copy clean, move release docs to 0.4.1, and tighten the dark theme to a near-black Netbox-style palette. |
||
|
|
ec171ede47 |
v0.4.0: upload telemetry, host log, jet-black UI
- Upload reliability + diagnostics:
- api_upload_iso now distinguishes clean EOF from mid-stream errors;
a truncated multipart body (proxy buffer cap, network drop) returns
400 with the cause and a "try the LAN IP" hint instead of silently
finalising a partial file.
- Per-stage tracing (begin/MB-watermark/finish/abort) so a stuck
upload is debuggable from the Terminal tab.
- Web upload UI surfaces bytes/total, percent, throughput, ETA, and
maps 413/502/504/network-drop to actionable hints.
- New BootLog feature under Hosts:
- openpxe-core::BootLog — bounded in-memory ring (500) + append-only
JSONL on disk, recording (timestamp, mac, ip, target_id,
target_title) every time a boot entry script is served.
- iPXE per-entry chain URLs grow ?mac=${mac}; password prompt
submission carries it through; host-binding short-circuit uses the
bound MAC. ConnectInfo<SocketAddr> wired for peer IP capture (with
optional fallback so tower::oneshot in tests still works).
- GET /api/boot-log endpoint + Host log table under the Hosts tab.
- UI changes:
- Queue card header "Forge" → "Status".
- Removed Tinkerbell attribution sentence from Hosts tab.
- Topbar readiness chip moved into the sidebar footer as
"Service status: Ready / Advertised to clients / <url>", grouping
advertised PXE URL with operator-relevant status.
- Jet-black dark palette (#000 / #0a0a0a / #141414 / #1c1c1c)
replacing the blue-tinted ramp; terminal toolbar/input recoloured
to match.
- 89 tests passing (was 85 in v0.3.2); cargo clippy --workspace
--all-targets clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
|
||
|
|
115ba779da | Name update | ||
|
|
20c585e3ed | Name update |