Fixinf SPDK
This commit is contained in:
@@ -147,3 +147,27 @@ this item builds the rollout machinery around it.
|
|||||||
especially mid-rollout (N versions + reboot bursts).
|
especially mid-rollout (N versions + reboot bursts).
|
||||||
- Note: kiosk *content* changes are server-side (the website) and need no fleet
|
- Note: kiosk *content* changes are server-side (the website) and need no fleet
|
||||||
update — only OS/UKI/rootfs changes require a reboot.
|
update — only OS/UKI/rootfs changes require a reboot.
|
||||||
|
|
||||||
|
## [ ] 7. Harden / evaluate the NVMe-oF target (serving-node resilience)
|
||||||
|
|
||||||
|
There is **no production-grade memory-safe NVMe-oF target** — the Rust NVMe work
|
||||||
|
is all driver/initiator-side (vroom, RedLeaf, Rust-for-Linux); the only
|
||||||
|
production targets are C (Linux kernel `nvmet`, SPDK). So this is **serving-node
|
||||||
|
hardening, not a language swap**. dm-verity already removes the target from the
|
||||||
|
clients' integrity TCB (verify-on-read rejects tampered blocks), so the risk is
|
||||||
|
RCE/DoS on the node that holds the fleet's disk, not client integrity. The
|
||||||
|
export is read-only + single-namespace = inherently small attack surface.
|
||||||
|
|
||||||
|
- **Evaluate kernel `nvmet` vs SPDK** for the target: kernel `nvmet` keeps the
|
||||||
|
data path in the hardened/fuzzed kernel with a tiny userspace footprint
|
||||||
|
(arguably a better attack-surface story); SPDK is a userspace C daemon
|
||||||
|
(current choice, needs hugepages + caps). Decide per environment.
|
||||||
|
- **Confine the target** (whichever): unprivileged, drop caps, seccomp +
|
||||||
|
SELinux/AppArmor profile, read-only container, minimal mounts; minimal
|
||||||
|
immutable serving image, digest-pinned.
|
||||||
|
- **Network-isolate :4420** so only the boot VLAN reaches the target (compose
|
||||||
|
with 802.1X/MACsec segmentation in prod).
|
||||||
|
- **Replicas** behind distinct discovery entries (availability is the real risk
|
||||||
|
— it's the fleet's disk). Ties into #6 (rolling updates) availability.
|
||||||
|
- Revisit a memory-safe target only if/when an off-the-shelf one matures
|
||||||
|
(e.g. a future Rust-for-Linux `nvmet`).
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Copy to .env and edit for your test LAN/bridge, then: docker compose up -d
|
# Copy to .env and edit for your test LAN/bridge, then: docker compose up -d
|
||||||
|
|
||||||
# Release being served. Must match the Packer build (fleet-<version>.efi).
|
# Release being served. Must match the Packer build (fleet-<version>.efi).
|
||||||
IMAGE_VERSION=0.1.0
|
IMAGE_VERSION=0.0.0-e98d881
|
||||||
|
|
||||||
# OCI release-artifact image (built by scripts/build-oci.sh). Mounted directly
|
# OCI release-artifact image (built by scripts/build-oci.sh). Mounted directly
|
||||||
# as a Docker image volume — the default artifact source. Pin by digest in
|
# as a Docker image volume — the default artifact source. Pin by digest in
|
||||||
@@ -10,7 +10,7 @@ OCI_IMAGE=git.bitasylum.org/audiobuzz/fleet-boot
|
|||||||
|
|
||||||
# Fallback only (Docker < 28 / offline): local Packer output dir, used by the
|
# Fallback only (Docker < 28 / offline): local Packer output dir, used by the
|
||||||
# commented bind-mounts in docker-compose.yml. Ignored when image volumes work.
|
# commented bind-mounts in docker-compose.yml. Ignored when image volumes work.
|
||||||
ARTIFACT_SRC=../artifacts/0.1.0
|
ARTIFACT_SRC=../artifacts/0.0.0-e98d881
|
||||||
|
|
||||||
# SPDK NVMe/TCP target image — prebuilt + pushed by CI
|
# SPDK NVMe/TCP target image — prebuilt + pushed by CI
|
||||||
# (.gitea/workflows/spdk-image.yaml); the serving host just pulls it.
|
# (.gitea/workflows/spdk-image.yaml); the serving host just pulls it.
|
||||||
@@ -18,10 +18,10 @@ SPDK_IMAGE=git.bitasylum.org/audiobuzz/spdk-nvmf
|
|||||||
SPDK_VERSION=v24.09
|
SPDK_VERSION=v24.09
|
||||||
|
|
||||||
# --- This serving host on the boot LAN -------------------------------------
|
# --- This serving host on the boot LAN -------------------------------------
|
||||||
BOOT_IFACE=eth0 # interface facing the booting client / test bridge
|
BOOT_IFACE=ens19 # interface facing the booting client / test bridge
|
||||||
SERVER_IP=192.168.50.10 # this host's address on BOOT_IFACE (DHCP/DNS/HTTP/NVMe)
|
SERVER_IP=192.168.50.10 # this host's address on BOOT_IFACE (DHCP/DNS/HTTP/NVMe)
|
||||||
GATEWAY_IP=192.168.50.1
|
GATEWAY_IP=192.168.50.1
|
||||||
UPSTREAM_DNS=1.1.1.1
|
UPSTREAM_DNS=10.42.42.254
|
||||||
|
|
||||||
# --- DHCP scope ------------------------------------------------------------
|
# --- DHCP scope ------------------------------------------------------------
|
||||||
DHCP_RANGE_START=192.168.50.100
|
DHCP_RANGE_START=192.168.50.100
|
||||||
|
|||||||
@@ -45,6 +45,10 @@ services:
|
|||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
command:
|
command:
|
||||||
- --keep-in-foreground
|
- --keep-in-foreground
|
||||||
|
# Log to stderr so `docker compose logs` captures output. Without this,
|
||||||
|
# dnsmasq logs to syslog — which doesn't exist in the container — so
|
||||||
|
# --log-dhcp/--log-queries produce nothing even while dnsmasq works fine.
|
||||||
|
- --log-facility=-
|
||||||
- --log-dhcp
|
- --log-dhcp
|
||||||
- --log-queries
|
- --log-queries
|
||||||
# Bind only to the boot interface so we don't fight the host resolver.
|
# Bind only to the boot interface so we don't fight the host resolver.
|
||||||
|
|||||||
+21
-4
@@ -20,6 +20,22 @@ CPUMASK="${SPDK_CPUMASK:-0x1}"
|
|||||||
TGT=$(command -v nvmf_tgt || echo /usr/libexec/spdk/spdk/build/bin/nvmf_tgt)
|
TGT=$(command -v nvmf_tgt || echo /usr/libexec/spdk/spdk/build/bin/nvmf_tgt)
|
||||||
RPC=$(command -v rpc.py || echo /usr/libexec/spdk/scripts/rpc.py)
|
RPC=$(command -v rpc.py || echo /usr/libexec/spdk/scripts/rpc.py)
|
||||||
|
|
||||||
|
# Ensure hugepages exist — DPDK needs them. vm.nr_hugepages isn't namespaced,
|
||||||
|
# so a privileged container writes the host value; this re-applies after a host
|
||||||
|
# reboot (the container restarts). Best-effort: allocation can fail on a
|
||||||
|
# fragmented host, in which case set vm.nr_hugepages on the host (persistently
|
||||||
|
# via /etc/sysctl.d). Override the count with HUGEPAGES.
|
||||||
|
HUGEPAGES="${HUGEPAGES:-1024}"
|
||||||
|
if [ -w /proc/sys/vm/nr_hugepages ]; then
|
||||||
|
cur=$(cat /proc/sys/vm/nr_hugepages 2>/dev/null || echo 0)
|
||||||
|
if [ "${cur:-0}" -lt "$HUGEPAGES" ]; then
|
||||||
|
echo "==> allocating $HUGEPAGES hugepages (was ${cur:-0})"
|
||||||
|
echo "$HUGEPAGES" > /proc/sys/vm/nr_hugepages 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
free=$(awk '/HugePages_Free/{print $2}' /proc/meminfo 2>/dev/null || echo 0)
|
||||||
|
[ "${free:-0}" -gt 0 ] || echo "WARNING: no free hugepages — SPDK will fail; allocate vm.nr_hugepages on the host." >&2
|
||||||
|
|
||||||
echo "==> starting nvmf_tgt (cpumask ${CPUMASK})"
|
echo "==> starting nvmf_tgt (cpumask ${CPUMASK})"
|
||||||
"$TGT" -m "$CPUMASK" &
|
"$TGT" -m "$CPUMASK" &
|
||||||
TGT_PID=$!
|
TGT_PID=$!
|
||||||
@@ -42,10 +58,11 @@ echo "==> exposing $ROOTFS as bdev 'rootfs' (4 KiB blocks)"
|
|||||||
echo "==> creating subsystem $SUBNQN (allow any host — lab/no-auth)"
|
echo "==> creating subsystem $SUBNQN (allow any host — lab/no-auth)"
|
||||||
"$RPC" nvmf_create_subsystem "$SUBNQN" -a -s SPDKLAB0000000001 -d FleetRootfs
|
"$RPC" nvmf_create_subsystem "$SUBNQN" -a -s SPDKLAB0000000001 -d FleetRootfs
|
||||||
|
|
||||||
echo "==> adding namespace 1 (read-only)"
|
echo "==> adding namespace 1"
|
||||||
# Newer SPDK accepts --read-only on the namespace; fall back if unsupported.
|
# Read-only is enforced by the bdev_aio '-r' open above (this SPDK's add_ns has
|
||||||
"$RPC" nvmf_subsystem_add_ns "$SUBNQN" rootfs --read-only \
|
# no --read-only flag), plus the :ro image-volume mount and the client's erofs
|
||||||
|| "$RPC" nvmf_subsystem_add_ns "$SUBNQN" rootfs
|
# ro + dm-verity.
|
||||||
|
"$RPC" nvmf_subsystem_add_ns "$SUBNQN" rootfs
|
||||||
|
|
||||||
echo "==> adding TCP listener ${TADDR}:${TPORT} (also serves discovery)"
|
echo "==> adding TCP listener ${TADDR}:${TPORT} (also serves discovery)"
|
||||||
"$RPC" nvmf_subsystem_add_listener "$SUBNQN" -t tcp -a "$TADDR" -s "$TPORT"
|
"$RPC" nvmf_subsystem_add_listener "$SUBNQN" -t tcp -a "$TADDR" -s "$TPORT"
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ RUN apt-get update \
|
|||||||
&& /spdk/scripts/pkgdep.sh \
|
&& /spdk/scripts/pkgdep.sh \
|
||||||
&& apt-get install -y --no-install-recommends meson ninja-build python3-pyelftools \
|
&& apt-get install -y --no-install-recommends meson ninja-build python3-pyelftools \
|
||||||
&& cd /spdk \
|
&& cd /spdk \
|
||||||
&& ./configure --disable-tests --disable-unit-tests \
|
&& ./configure --disable-tests --disable-unit-tests --target-arch=x86-64-v2 \
|
||||||
&& make -j"$(nproc)" \
|
&& make -j"$(nproc)" \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user