• mward4 released this 2026-05-28 11:19:53 -04:00 | 34 commits to main since this release

    OpenPXE v0.4.65

    NFS is gone. SMB shares are in.

    Why

    v0.4.64 added pre-flight probes, retry-with-minimal-options, and a
    catalog of actionable hints for kernel-mount NFS. None of that helps
    when the host kernel doesn't have the nfs client modules loaded —
    mount(2) inside the container returns EOPNOTSUPP / EINVAL and there's
    nothing container-side configuration can do about it. Unraid's base
    kernel ships without those modules. CIFS (kernel-mount SMB) has the
    identical problem.

    The right fix is to not depend on kernel mounts at all. Samba's
    smbclient CLI speaks the SMB protocol entirely in userspace over a
    plain TCP socket — no kernel modules, no CAP_SYS_ADMIN, no
    --privileged. Bootimus uses this approach. iVentoy / FOG / MAAS /
    Cobbler all rely on the host to mount network storage; OpenPXE
    v0.4.65 ships its own SMB consumer so operators don't have to.

    What got removed

    • The whole crates/iso-store/src/nfs.rs module — NfsManager,
      NfsMount, NfsAddRequest, NfsVersion, mount-options retry, TCP
      probe, stderr-to-hint translation.
    • IsoSource::Nfs variant on the ISO source enum.
    • IsoStore::set_nfs_root and the nfs_root field.
    • Every /api/nfs/* route.
    • The nfs terminal command.
    • The Storage tab's "NFS shares" card.

    If you had NFS mounts persisted in <work_dir>/nfs.json they're
    ignored at startup; OpenPXE no longer reads that file.

    What got added

    • crates/iso-store/src/smb_share.rsSmbShareManager that drives
      smbclient as a subprocess. Indexes a share via
      smbclient //server/share -A creds -c "ls *.iso". Streams files
      via smbclient //server/share -A creds -c "get file -" piped
      straight into HTTP response bodies — no local cache, no double
      disk usage.
    • IsoSource::Smb { share_id, relative_path } variant.
    • /api/smb-shares API (list / add / delete / scan).
    • share terminal command:
      share list
      share add //10.0.0.5/isos guest
      share add //nas.lan/isos myuser:mypassword
      share scan <id>
      share remove <id>
      
    • Storage tab: a new SMB shares card with a two-column form for
      server + share name, three-column form for the Guest checkbox /
      username / password. Username & password auto-disable when Guest
      is checked.
    • TCP pre-flight probe (4s timeout) to server:445 before shelling
      out to smbclient. Catches wrong-IP / firewall cases with a clean
      error instead of waiting for smbclient to time out.
    • Operator-friendly hints for the common NT_STATUS codes:
      LOGON_FAILURE → check credentials; BAD_NETWORK_NAME → check
      share name (e.g. //nas/isos, not //nas/shared/isos);
      ACCESS_DENIED → check share-side permissions; CONNECTION REFUSED
      / TIMEOUT → verify smbd is running + firewall.

    Credentials

    • Username and password live in a 0600-permissioned credentials file
      under <work_dir>/smb_creds/<share-id>.cred. Passing them on the
      command line would leak them through ps and the container audit
      log.
    • Guest mode (-N to smbclient) is the default and most NAS
      appliances exposing ISO libraries support it.

    How to use it on Unraid (the original failure case)

    1. Pull :0.4.65 (or :latest).
    2. Restart the container with whatever capabilities you had before —
      none of the NFS workarounds matter anymore. Plain docker run
      with no special caps works.
    3. In your UniFi UNAS Pro / Synology / TrueNAS / whatever, make sure
      SMB is enabled on the share holding your ISOs. (UniFi UNAS Pro:
      Drive → Settings → Services → SMB. Most setups have it on by
      default.)
    4. In OpenPXE's Storage tab, the SMB shares card now has fields:
      • SMB server: 192.168.1.51
      • Share name: isos (just the share name, not a path)
      • Guest: checked if the share allows anonymous read; uncheck
        to enter a username and password.
    5. Click Add share. OpenPXE lists the share's ISOs and surfaces
      them in the Available images table within seconds.

    Known limitation

    Range requests on SMB-sourced ISOs return 416. smbclient -c 'get file -' is a sequential whole-file stream — there's no native seek
    in the CLI. PXE workloads in practice (iPXE chain loading, casper
    sanboot, wimboot) all do sequential whole-file reads, so this works
    for ISO delivery. If a workload comes up that genuinely needs random
    access into SMB-sourced ISOs, a follow-up release can swap to
    libsmbclient-based seek without changing the API or the UI.

    ISO introspection (auto-detecting the distro family, finding kernel

    • initrd paths inside the ISO9660 PVD) is also currently no-op for
      SMB-sourced ISOs since it would require seeking into the file over
      the network. Operators get generic sanboot boot entries that work
      for any bootable ISO. A bounded smbclient get of the first 64 KiB
      would be enough to drive real detection; punted to a follow-up.

    About v0.4.65 (the bind-mount one)

    Earlier today I published a v0.4.65 release that proposed a
    "register a bind-mounted host directory" workaround instead of an
    in-container SMB client. The operator correctly pointed out that's
    not the right architecture — it makes OpenPXE depend on operator-
    managed host plumbing instead of being self-contained. That release
    has been deleted from Gitea (release, tag, and container image all
    removed), and the commit that introduced it has been reverted on
    main. This v0.4.65 is the SMB swap; that's what the version
    number now means.

    Tests

    149 passing, cargo clippy --workspace --all-targets -- -D warnings
    clean. New tests cover:

    • Parser: ISO listing with skipped dot/dotdot, filenames containing
      spaces, non-ISO files correctly skipped.
    • Server normalization (smb://, cifs://, \\, // all stripped).
    • Hint translation for LOGON_FAILURE, BAD_NETWORK_NAME, unknown.
    • HTTP integration: missing server, missing username when not Guest,
      paths in share name all rejected with the right hint.

    Container image

    • gitea.milesward.dev/mward4/openpxe:0.4.65
    • gitea.milesward.dev/mward4/openpxe:latest

    linux/amd64; static-musl /openpxe binary. Image size ~98 MB (the
    samba package was already shipped for the outbound smbd serving
    Windows install media; smbclient comes with it for free).

    Compatibility

    • nfs.json is no longer read. If you had NFS mounts configured,
      re-create them as SMB shares in the new Storage tab.
    • The Prometheus gauge previously named "nfs_active" still exists
      but now tracks reachable SMB shares (same semantic — number of
      external storage sources currently working).
    • The /api/status JSON shape gains smb_share_count and
      smb_share_reachable; the old nfs_count / nfs_active fields
      are gone. Any external monitoring keyed off the old names needs
      updating.
    Downloads