Introspection (the headline): detection is now probe-based. Instead of
grepping raw sectors for filename strings, we walk the ISO9660
directory tree and check whether the well-known boot files actually
exist — and the same probes run over NFS READ3 / SFTP seek-reads, so
share-hosted ISOs finally classify instead of registering as Unknown.
iso-store:
- New iso_fs module: the read-only ISO9660 walker (generalized from
http-api) over an IsoReadAt trait — local files, NFS, SFTP, and the
in-memory test images all share it. Iterative walk, 4 MiB directory
cap, strict-mastering trailing-dot normalization (VMLINUZ.;1 now
matches /vmlinuz), CachingReadAt collapses repeated directory reads
during the probe pass (~60 → ~6 round-trips per remote ISO).
- introspect.rs rewritten (INTROSPECT_REV 2): PVD label → El Torito →
/sources/boot.wim probe → verified Linux kernel+initrd probe table →
local-only 16 MiB UDF-Windows scan → filename-token fallback.
* Fixes the false-Windows bug: any Linux ISO shipping GRUB/syslinux
chainload modules contains the literal "bootmgr", so gparted-live
classified as WindowsPe. Linux probes now run first; the byte scan
only sees ISOs nothing else claimed. Local ISOs re-probe once on
startup via the rev bump — no re-upload.
* Kernel entries are emitted only when kernel+initrd verifiably
exist (no more guessed paths that 404 at boot). Debian-live /
d-i netinst / CoreOS shapes classify for the UI but keep their
working sanboot entries (their boot protocols need args we don't
render yet; CoreOS additionally needs its embedded ignition).
* Label + filename vocab extended: rhcos/coreos/openshift/okd,
gparted/clonezilla/kali/tails, almalinux/rocky, sles, manjaro.
- NFS + SFTP managers: per-ISO IsoReadAt readers (READ3-at-offset with
short-read looping / seek+read_exact), background introspection pass
after each scan — entries register instantly with a provisional
filename-based report (rev 0, optimistic sanboot preserved) and
upgrade in place as probes land (30s/ISO timeout, failures keep the
provisional). locate_in_iso() exposes the walker to the HTTP layer.
- remote_cache: introspection results persisted per protocol keyed
share/path@size and gated on INTROSPECT_REV — container restarts
re-probe only new/replaced ISOs; upgrades re-probe exactly once.
- SMB: smbclient can't seek, so SMB ISOs get the filename-token family
(rev stays 0 → sanboot entry + "awaiting introspection" label).
- IsoStore::update_external_introspection swaps in completed reports
and regenerates boot entries, preserving category/password.
http-api:
- /iso/{id}/{*path} now serves files from inside NFS/SFTP-hosted ISOs
(remote ISO9660 lookup + ranged share stream) — verified kernel
entries on remote Linux ISOs are actually bootable, end to end.
- iso_fs.rs deleted in favor of the shared iso-store module.
- full_flow fixtures build real directory trees via the shared
test-image builder (new iso-store feature) — a label-only blob no
longer earns a kernel entry, by design.
webui:
- Available images: paged 5 per page with a quiet footer pager
(Showing X–Y of N · Prev/Next), filter-then-paginate, page resets on
search input. Fifty images is five clean pages, not a scroll wall.
- Hosts/Queue profile: "Unattended file (in Storage → Advanced)" so
the picker says where the files live.
- Row badge keys on introspect_rev: probed remote ISOs read like local
ones; un-probed say "awaiting introspection".
Validation: clippy pedantic clean, fmt clean, 316 workspace tests
green (+17: walker, probe shapes incl. gparted regression + CoreOS,
filename table, cache round-trips), webui syntax-checked.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
148 lines
5.6 KiB
TOML
148 lines
5.6 KiB
TOML
[workspace]
|
|
resolver = "2"
|
|
members = [
|
|
"crates/core",
|
|
"crates/dhcp-proxy",
|
|
"crates/tftp",
|
|
"crates/http-api",
|
|
"crates/iso-store",
|
|
"crates/ipxe-assets",
|
|
"crates/webui",
|
|
"crates/openpxe",
|
|
]
|
|
|
|
[workspace.package]
|
|
version = "0.7.4"
|
|
edition = "2021"
|
|
rust-version = "1.95"
|
|
license = "MIT OR Apache-2.0"
|
|
repository = "https://gitea.milesward.dev/mward4/OpenPXE"
|
|
authors = ["OpenPXE contributors"]
|
|
|
|
[workspace.dependencies]
|
|
tokio = { version = "1.52", features = ["full"] }
|
|
tokio-util = { version = "0.7", features = ["io"] }
|
|
tokio-stream = { version = "0.1", features = ["sync"] }
|
|
futures = "0.3"
|
|
async-trait = "0.1"
|
|
|
|
# v0.6.2: dhcproto 0.15 drops the deprecated trust-dns-proto dependency
|
|
# (replaced by hickory-proto) and carries three releases of DHCP option
|
|
# coverage accumulated upstream — both directly relevant to the proxy core.
|
|
dhcproto = "0.15"
|
|
socket2 = { version = "0.6", features = ["all"] }
|
|
bytes = "1.7"
|
|
|
|
axum = { version = "0.8", features = ["macros", "multipart", "http2"] }
|
|
tower = "0.5"
|
|
tower-http = { version = "0.6", features = ["fs", "trace", "cors", "limit"] }
|
|
hyper = "1.9"
|
|
reqwest = { version = "0.12", default-features = false, features = ["rustls-tls", "stream", "json"] }
|
|
|
|
serde = { version = "1.0", features = ["derive"] }
|
|
serde_json = "1.0"
|
|
toml = "0.8"
|
|
# v0.5.4: layered config (TOML file + env). Pure-Rust, no C deps; keeps the
|
|
# static-musl build OpenSSL-free. Replaces the hand-rolled apply_env mapping.
|
|
figment = { version = "0.10", features = ["toml", "env"] }
|
|
|
|
tracing = "0.1"
|
|
tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
|
|
|
|
anyhow = "1.0"
|
|
thiserror = "2.0"
|
|
clap = { version = "4.5", features = ["derive", "env"] }
|
|
uuid = { version = "1.10", features = ["v4", "serde"] }
|
|
time = { version = "0.3", features = ["serde", "serde-human-readable", "formatting", "macros"] }
|
|
# sha2 stays 0.10 deliberately: bergshamra-crypto requires ^0.10, and
|
|
# bumping to 0.11 would split the RustCrypto digest stack in the tree.
|
|
sha2 = "0.10"
|
|
hex = "0.4"
|
|
bcrypt = "0.19"
|
|
parking_lot = "0.12"
|
|
rust-embed = { version = "8.5", features = ["include-exclude"] }
|
|
|
|
# v0.4.67: pure-Rust NFSv3 client. Replaces the (deleted-in-v0.4.65)
|
|
# kernel-mount NFS path with an in-process implementation that works
|
|
# in any container — no kernel modules, no CAP_SYS_ADMIN, no
|
|
# subprocess. Ships alongside the userspace SMB consumer; operators
|
|
# pick whichever protocol their NAS prefers.
|
|
nfs3_client = { version = "0.9", features = ["tokio"] }
|
|
nfs3_types = "0.5"
|
|
|
|
# v0.5.0: SMTP for webhook notifications (Slack/Teams/Discord go over
|
|
# plain HTTP via reqwest; email needs a real SMTP client). rustls TLS
|
|
# to match reqwest and stay musl-static-friendly — no OpenSSL.
|
|
lettre = { version = "0.11", default-features = false, features = ["smtp-transport", "tokio1-rustls-tls", "builder", "hostname"] }
|
|
|
|
# v0.5.1: pure-Rust SAML 2.0 Service Provider. bergshamra does XML-DSig
|
|
# verification + exclusive c14n with RustCrypto (no OpenSSL/xmlsec/libxml2
|
|
# C deps), so the static musl binary stays OpenSSL-free — samael was
|
|
# rejected precisely because it hard-requires OpenSSL. We build the thin
|
|
# SP layer (AuthnRequest, metadata parse, SAMLResponse semantics) on top.
|
|
bergshamra = "0.5"
|
|
roxmltree = "0.21"
|
|
quick-xml = "0.40"
|
|
x509-parser = "0.18"
|
|
# flate2 default backend is miniz_oxide (pure Rust) — do NOT enable the
|
|
# zlib/zlib-ng C backends, which would break the musl-static build.
|
|
flate2 = "1.1"
|
|
base64 = "0.22"
|
|
|
|
# v0.5.5: pure-Rust SSH/SFTP client for reading remote ISO libraries
|
|
# over SFTP without a kernel mount.
|
|
#
|
|
# CRITICAL #1 — crypto backend: `default-features = false` +
|
|
# `features = ["ring"]`. russh's *default* backend is `aws-lc-rs`, which
|
|
# pulls `aws-lc-sys` (C code, fiddly under musl); the `ring` feature
|
|
# instead reuses `ring 0.17` — the exact crate+version already in the
|
|
# binary via rustls + bergshamra — so SFTP adds ZERO new C/crypto deps
|
|
# and the static-musl build stays OpenSSL-free.
|
|
#
|
|
# CRITICAL #2 — history: this was pinned to =0.55.0 from v0.5.5 until
|
|
# v0.6.3 because bergshamra-crypto pinned release-candidate RustCrypto
|
|
# crates that conflicted with the stable generation russh 0.56+ pulls.
|
|
# bergshamra 0.5 (2026-06) moved to the stable generation (pkcs8 0.11),
|
|
# lifting the pin. v0.6.3 bumps to 0.61+, which also closes a batch of
|
|
# RUSTSEC advisories reachable from the SFTP *client* path (unbounded
|
|
# allocations in packet parsing — CVE-2026-48110/-46702/-46673 et al.)
|
|
# and drops mlock on non-secret buffers (~21% SSH throughput upstream).
|
|
#
|
|
# SCP was deliberately rejected: the protocol is sequential-only (no
|
|
# random access → no HTTP Range, unlike SFTP/NFS) and the mature SCP
|
|
# crates wrap libssh2 (C + OpenSSL), which would break this build.
|
|
russh = { version = "0.61", default-features = false, features = ["ring"] }
|
|
russh-sftp = "2.3"
|
|
|
|
openpxe-core = { path = "crates/core" }
|
|
openpxe-dhcp-proxy = { path = "crates/dhcp-proxy" }
|
|
openpxe-tftp = { path = "crates/tftp" }
|
|
openpxe-http-api = { path = "crates/http-api" }
|
|
openpxe-iso-store = { path = "crates/iso-store" }
|
|
openpxe-ipxe-assets = { path = "crates/ipxe-assets" }
|
|
openpxe-webui = { path = "crates/webui" }
|
|
|
|
[workspace.lints.rust]
|
|
unsafe_code = "deny"
|
|
rust_2018_idioms = { level = "warn", priority = -1 }
|
|
|
|
[workspace.lints.clippy]
|
|
pedantic = { level = "warn", priority = -1 }
|
|
module_name_repetitions = "allow"
|
|
missing_errors_doc = "allow"
|
|
missing_panics_doc = "allow"
|
|
must_use_candidate = "allow"
|
|
doc_markdown = "allow"
|
|
items_after_statements = "allow"
|
|
cast_possible_truncation = "allow"
|
|
cast_lossless = "allow"
|
|
cast_sign_loss = "allow"
|
|
similar_names = "allow"
|
|
too_many_lines = "allow"
|
|
|
|
[profile.release]
|
|
lto = "thin"
|
|
codegen-units = 1
|
|
strip = "symbols"
|
|
opt-level = 3
|