Do not copy rust-toolchain.toml into the Docker build stage so the release image uses the Rust toolchain provided by the base image instead of downloading latest stable inside the container.
103 lines
4.8 KiB
Docker
103 lines
4.8 KiB
Docker
# syntax=docker/dockerfile:1.7
|
|
#
|
|
# OpenPXE — multi-stage build.
|
|
#
|
|
# Design:
|
|
# - stage `fetch`: runs scripts/fetch-ipxe.sh to pull official iPXE binaries
|
|
# into assets/ipxe/ so the rust build can embed them via rust-embed.
|
|
# - stage `build`: compiles the workspace with cargo in release mode.
|
|
# - stage `runtime`: Debian slim image with setcap for NET_BIND_SERVICE,
|
|
# running as a non-root UID. No shell in PATH for the service user;
|
|
# attacker surface is just the openpxe binary + libc.
|
|
#
|
|
# Why not distroless? We want setcap support and easy debug (`oc rsh`).
|
|
# Debian slim at ~75 MB + binary ~25 MB is fine for a PXE server that
|
|
# spends most of its life idle.
|
|
|
|
ARG RUST_VERSION=1.82
|
|
|
|
########## fetch iPXE binaries ##########
|
|
FROM debian:12-slim AS fetch
|
|
RUN apt-get update && apt-get install -y --no-install-recommends curl ca-certificates \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
WORKDIR /src
|
|
COPY scripts/fetch-ipxe.sh scripts/fetch-ipxe.sh
|
|
RUN mkdir -p assets/ipxe && bash scripts/fetch-ipxe.sh
|
|
|
|
########## build openpxe ##########
|
|
FROM rust:${RUST_VERSION}-bookworm AS build
|
|
WORKDIR /src
|
|
|
|
# Copy the whole workspace in one go. We used to do a two-pass "cache-prime
|
|
# with stubs, then real build" dance for dep-compile reuse; that turned out
|
|
# to silently serve stale stub binaries when cargo's fingerprint didn't
|
|
# notice the source swap. A single build is ~1.5 min longer on cold cache
|
|
# but guarantees the binary reflects the sources we copied.
|
|
# Do not copy rust-toolchain.toml into the image. The local workspace pins
|
|
# developer tooling, but inside Docker we intentionally use the Rust version
|
|
# selected by the base image. Copying rust-toolchain.toml with
|
|
# `channel = "stable"` makes rustup download a second full toolchain during
|
|
# `cargo build`, which is slow and can exhaust small Colima/CI disks.
|
|
COPY Cargo.toml ./
|
|
COPY crates/ crates/
|
|
COPY --from=fetch /src/assets/ipxe /src/assets/ipxe
|
|
|
|
# Cache cargo registry + target across builds. The `--no-edit` touch is
|
|
# belt-and-suspenders: cargo occasionally misses mtime-only changes on
|
|
# networked FS; this forces a fingerprint check.
|
|
RUN --mount=type=cache,target=/usr/local/cargo/registry \
|
|
--mount=type=cache,target=/src/target,sharing=locked \
|
|
find crates -name '*.rs' -exec touch {} + && \
|
|
cargo build --release --bin openpxe && \
|
|
cp target/release/openpxe /openpxe && \
|
|
ls -l /openpxe
|
|
|
|
########## runtime ##########
|
|
FROM debian:12-slim AS runtime
|
|
RUN apt-get update \
|
|
&& apt-get install -y --no-install-recommends \
|
|
ca-certificates libcap2-bin tini gosu iproute2 \
|
|
wimtools samba nfs-common \
|
|
&& rm -rf /var/lib/apt/lists/* \
|
|
&& useradd --system --uid 10001 --home-dir /var/lib/openpxe --shell /usr/sbin/nologin openpxe \
|
|
&& mkdir -p /var/lib/openpxe/isos /var/lib/openpxe/work /var/lib/openpxe/smb \
|
|
&& chown -R openpxe:openpxe /var/lib/openpxe
|
|
# Runtime deps explained:
|
|
# wimtools - provides `wimlib-imagex`, used to inject startnet.cmd into boot.wim.
|
|
# samba - `smbd` serves extracted Windows install media on :445 for WinPE
|
|
# to `net use`. Guest read-only, scoped to /var/lib/openpxe/smb.
|
|
# nfs-common - provides `mount.nfs` / `mount.nfs4` for the Storage tab's
|
|
# NFS share manager. Mount also requires the container to run
|
|
# with CAP_SYS_ADMIN — without it, mount(2) returns EPERM and
|
|
# the manager surfaces a clear error in the UI instead of
|
|
# failing silently.
|
|
# iproute2 - `ip addr` / `ip route` for the auto-detected Network tab
|
|
# fields (NIC name, subnet mask, default gateway). Tiny,
|
|
# always available; we don't pull in netlink crates for
|
|
# this one-shot startup probe.
|
|
# gosu - drops privileges cleanly from root after the entrypoint fixes
|
|
# bind-mount ownership (common OpenShift/Docker UX issue).
|
|
# Windows-specific tools only activate when the WebUI toggle is on.
|
|
|
|
COPY --from=build /openpxe /usr/local/bin/openpxe
|
|
COPY deploy/docker/entrypoint.sh /usr/local/bin/entrypoint.sh
|
|
RUN chmod +x /usr/local/bin/entrypoint.sh
|
|
|
|
# Grant the binary the ability to bind <1024 ports as a non-root user.
|
|
# This is the only capability OpenPXE needs for proxy-mode DHCP + TFTP + HTTP.
|
|
RUN setcap cap_net_bind_service=+ep /usr/local/bin/openpxe
|
|
|
|
# IMPORTANT: we do NOT `USER openpxe` here. The entrypoint runs as root,
|
|
# chowns the mounted data dirs, then execs the binary via gosu as openpxe.
|
|
# OpenShift ignores USER directives anyway (it injects its own uid), and
|
|
# there entrypoint.sh's non-root branch just execs directly.
|
|
WORKDIR /var/lib/openpxe
|
|
|
|
ENV OPENPXE_ISO_DIR=/var/lib/openpxe/isos \
|
|
OPENPXE_WORK_DIR=/var/lib/openpxe/work \
|
|
OPENPXE_LOG=info,openpxe=info
|
|
|
|
EXPOSE 67/udp 69/udp 4011/udp 80/tcp 445/tcp
|
|
|
|
ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/entrypoint.sh"]
|