• v0.3.1 91848e02e3

    mward4 released this 2026-05-06 22:35:11 -04:00 | 49 commits to main since this release

    v0.3.1 — per-ISO boot password gate

    Operators can now lock individual ISOs behind a password set in the WebUI. Picking a locked image at the PXE menu prompts the operator on the client console; the boot script is only released after a correct match. The plaintext never leaves the request — server stores bcrypt hashes, scripts never echo the candidate.

    Pull

    docker pull gitea.milesward.dev/mward4/openpxe:0.3.1
    

    How it looks

    In the WebUI's Storage tab, each image row gains an Auth column (open / protected) and a per-row "Set password" / "Password ✎" button that toggles an inline editor — checkbox + masked input + Save. A 🔒 appears next to the filename when locked.

    In the PXE menu, locked entries get a leading * next to the size box:

    item --key 1 win11-test-winpe *[ 5234 MB] Windows 11 Test
    

    When a locked entry is selected, iPXE shows a "This image requires a password" banner, accepts input via read --secret password (no echo), and chains back with ?token=${password:uristring} so passwords with &, ?, =, spaces, etc. survive transport. Wrong password sleeps 2s, prints Wrong password., then re-prompts. Empty input bails to the main menu.

    API

    PUT /api/isos/:id/password body { "password": "..." } to set, { "password": null } (or empty string) to clear.
    DELETE /api/isos/:id/password explicit clear.

    Both 204 on success, 404 for unknown ids.

    Security posture

    • bcrypt cost = DEFAULT_COST (10). One verify takes ~50ms on modern x86 — acceptable interactive latency, hostile to brute force on a leaked meta.json.
    • Server logs only that a wrong-password attempt happened plus the entry id. Never the candidate, never the correct value, never the bcrypt hash.
    • Boot scripts never echo any password value back to the client console.
    • The token rides in the query string — acceptable on a trusted boot VLAN, but if you reverse-proxy OpenPXE the token will end up in proxy access logs. Don't expose this to untrusted networks.

    Tests

    74 passing across the workspace (was 66 in v0.3.0):

    • 3 store unit tests (bcrypt round-trip, unknown-id error, meta.json persistence across restart)
    • 2 ipxe_script unit tests (prompt + auth-fail invariants: read --secret, :uristring, no candidate echo)
    • 3 HTTP integration tests (full upload → set → prompt → fail → success → clear flow, null/empty bodies, 404 on unknown id)

    cargo clippy --workspace --all-targets clean.

    Local smoke verified end-to-end on a real release binary: upload Win11_Test.iso → PUT password → menu shows the lock indicator → no token returns prompt → wrong token returns Wrong password. → correct token returns the wimboot chain → password values do not appear in the server log.

    Image details

    • Base: debian:12-slim
    • Architecture: linux/amd64
    • Size: ~97 MB
    • Digest: sha256:f3e5ee78079c25f55cb826837b8dda9428bf6b9592d9f15a2f6b413c5642fa1e
    • Runs as uid 10001
    • Required cap: NET_BIND_SERVICE
    • Optional cap: SYS_ADMIN for NFS share mounting

    Upgrade from v0.3.0

    Drop-in. New password_hash field on IsoMeta is Option<String> and skipped during serialize when None — existing meta.json files are forward-compatible. Existing settings.json carries through. :latest now points at 0.3.1.

    Downloads