Compare commits

...

2 Commits

Author SHA1 Message Date
Mikhail Chusavitin
fa553c3f20 fix: update DEBIAN_KERNEL_ABI to 6.1.0-43 (actual kernel on build host)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-08 18:35:44 +03:00
Mikhail Chusavitin
345a93512a migrate ISO build from Alpine to Debian 12 (Bookworm)
Replace the entire live CD build pipeline:
- Alpine SDK + mkimage + genapkovl → Debian live-build (lb config/build)
- OpenRC init scripts → systemd service units
- dropbear → openssh-server (native to Debian live)
- udhcpc → dhclient for DHCP
- apk → apt-get in setup-builder.sh and build-nvidia-module.sh
- Add auto/config (lb config options) and auto/build wrapper
- Add config/package-lists/bee.list.chroot replacing Alpine apks
- Add config/hooks/normal/9000-bee-setup.hook.chroot to enable services
- Add bee-nvidia-load and bee-sshsetup helper scripts
- Keep NVIDIA pre-compile pipeline (Option B): compile on builder VM against
  pinned Debian kernel headers (DEBIAN_KERNEL_ABI), inject .ko into includes.chroot
- Fixes: native glibc (no gcompat shims), proper udev, writable /lib/modules,
  no Alpine modloop read-only constraint, no stale apk cache issues

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-08 18:01:38 +03:00
26 changed files with 362 additions and 582 deletions

View File

@@ -1,4 +1,5 @@
ALPINE_VERSION=3.21
DEBIAN_VERSION=12
DEBIAN_KERNEL_ABI=6.1.0-43
NVIDIA_DRIVER_VERSION=590.48.01
GO_VERSION=1.23.6
AUDIT_VERSION=0.1.0

5
iso/builder/auto/build Executable file
View File

@@ -0,0 +1,5 @@
#!/bin/sh
# auto/build — live-build build wrapper for bee ISO
set -e
lb build noauto "${@}" 2>&1

30
iso/builder/auto/config Executable file
View File

@@ -0,0 +1,30 @@
#!/bin/sh
# auto/config — live-build configuration for bee ISO
# Runs automatically when lb config is called.
# See: man lb_config
set -e
. "$(dirname "$0")/../VERSIONS"
lb config noauto \
--distribution bookworm \
--architectures amd64 \
--binary-images iso-hybrid \
--bootloaders "grub-efi,syslinux" \
--debian-installer none \
--archive-areas "main contrib non-free non-free-firmware" \
--mirror-bootstrap "https://deb.debian.org/debian" \
--mirror-chroot "https://deb.debian.org/debian" \
--mirror-binary "https://deb.debian.org/debian" \
--security true \
--linux-flavours "amd64" \
--linux-packages "linux-image-${DEBIAN_KERNEL_ABI}" \
--memtest none \
--iso-volume "BEE-DEBUG" \
--iso-application "Bee Hardware Audit" \
--hostname "bee-debug" \
--username "root" \
--bootappend-live "boot=live components quiet splash" \
--apt-recommends false \
"${@}"

View File

@@ -1,5 +1,5 @@
#!/bin/sh
# build-nvidia-module.sh — install NVIDIA proprietary driver into ISO overlay
# build-nvidia-module.sh — compile NVIDIA proprietary driver modules for Debian 12
#
# Downloads the official NVIDIA .run installer, extracts kernel modules and
# userspace tools (nvidia-smi, libnvidia-ml). Everything is proprietary NVIDIA.
@@ -16,33 +16,25 @@ set -e
NVIDIA_VERSION="$1"
DIST_DIR="$2"
ALPINE_VERSION="$3"
DEBIAN_KERNEL_ABI="$3"
[ -n "$NVIDIA_VERSION" ] || { echo "usage: $0 <nvidia-version> <dist-dir> <alpine-version>"; exit 1; }
[ -n "$DIST_DIR" ] || { echo "usage: $0 <nvidia-version> <dist-dir> <alpine-version>"; exit 1; }
[ -n "$ALPINE_VERSION" ] || { echo "usage: $0 <nvidia-version> <dist-dir> <alpine-version>"; exit 1; }
[ -n "$NVIDIA_VERSION" ] || { echo "usage: $0 <nvidia-version> <dist-dir> <debian-kernel-abi>"; exit 1; }
[ -n "$DIST_DIR" ] || { echo "usage: $0 <nvidia-version> <dist-dir> <debian-kernel-abi>"; exit 1; }
[ -n "$DEBIAN_KERNEL_ABI" ] || { echo "usage: $0 <nvidia-version> <dist-dir> <debian-kernel-abi>"; exit 1; }
# Install linux-lts-dev (no version pin — always use whatever is current in Alpine 3.21 main).
# This ensures modules are compiled for the same kernel that mkimage will install in the ISO.
# Both use dl-cdn.alpinelinux.org, so they see the same package state at build time.
echo "=== installing linux-lts-dev (latest from dl-cdn) ==="
apk add --quiet --update \
--repository "https://dl-cdn.alpinelinux.org/alpine/v${ALPINE_VERSION}/main" \
linux-lts-dev
# Detect kernel version from installed headers (pick highest version if multiple).
detect_kver() {
ls /usr/src/ 2>/dev/null \
| grep '^linux-headers-' \
| sed 's/linux-headers-//' \
| sort -V \
| tail -1
}
KVER="$(detect_kver)"
KVER="${DEBIAN_KERNEL_ABI}-amd64"
KDIR="/usr/src/linux-headers-${KVER}"
echo "=== NVIDIA ${NVIDIA_VERSION} (proprietary) for kernel ${KVER} ==="
if [ ! -d "$KDIR" ]; then
echo "=== installing linux-headers-${KVER} ==="
DEBIAN_FRONTEND=noninteractive apt-get install -y \
"linux-headers-${KVER}" \
gcc make perl
fi
echo "kernel headers: $KDIR"
CACHE_DIR="${DIST_DIR}/nvidia-${NVIDIA_VERSION}-${KVER}"
if [ -d "$CACHE_DIR/modules" ] && [ -f "$CACHE_DIR/bin/nvidia-smi" ]; then
echo "=== NVIDIA cached, skipping build ==="
@@ -51,12 +43,7 @@ if [ -d "$CACHE_DIR/modules" ] && [ -f "$CACHE_DIR/bin/nvidia-smi" ]; then
exit 0
fi
# Install build dependencies (linux-lts-dev already at correct version from above)
apk add --quiet \
--repository "https://dl-cdn.alpinelinux.org/alpine/v${ALPINE_VERSION}/main" \
gcc make perl linux-lts-dev wget
# Download official NVIDIA .run installer (proprietary) with sha256 verification
# Download official NVIDIA .run installer with sha256 verification
BASE_URL="https://download.nvidia.com/XFree86/Linux-x86_64/${NVIDIA_VERSION}"
RUN_FILE="/var/tmp/NVIDIA-Linux-x86_64-${NVIDIA_VERSION}.run"
SHA_FILE="/var/tmp/NVIDIA-Linux-x86_64-${NVIDIA_VERSION}.run.sha256sum"
@@ -96,7 +83,7 @@ done
[ -n "$KERNEL_SRC" ] || { echo "ERROR: kernel source dir not found in:"; ls "$EXTRACT_DIR/"; exit 1; }
echo "kernel source: $KERNEL_SRC"
# Build kernel modules from extracted source
# Build kernel modules
echo "=== building kernel modules ($(nproc) cores) ==="
cd "$KERNEL_SRC"
make -j$(nproc) KERNEL_UNAME="$KVER" SYSSRC="$KDIR" modules 2>&1 | tail -5
@@ -112,32 +99,31 @@ done
cp "$EXTRACT_DIR/nvidia-smi" "$CACHE_DIR/bin/"
cp "$EXTRACT_DIR/nvidia-bug-report.sh" "$CACHE_DIR/bin/" 2>/dev/null || true
# Copy userspace libraries — use find to handle any versioning scheme (libnvidia-ml.so.X.Y.Z or .so.1)
# Copy ALL userspace library files
for lib in libnvidia-ml libcuda; do
found=$(find "$EXTRACT_DIR" -maxdepth 1 -name "${lib}.so.*" | head -1)
if [ -z "$found" ]; then
count=0
for f in $(find "$EXTRACT_DIR" -maxdepth 1 -name "${lib}.so.*" 2>/dev/null); do
cp "$f" "$CACHE_DIR/lib/" && count=$((count+1))
done
if [ "$count" -eq 0 ]; then
echo "ERROR: ${lib}.so.* not found in $EXTRACT_DIR"
ls "$EXTRACT_DIR/"*.so* 2>/dev/null | head -20 || true
exit 1
fi
cp "$found" "$CACHE_DIR/lib/"
done
# Verify .ko files were actually built
# Verify .ko files were built
ko_count=$(ls "$CACHE_DIR/modules/"*.ko 2>/dev/null | wc -l)
[ "$ko_count" -gt 0 ] || { echo "ERROR: no .ko files built in $CACHE_DIR/modules/"; exit 1; }
# Create soname symlinks required by nvidia-smi on Alpine (musl/glibc via gcompat + libc6-compat)
# Create soname symlinks: use [0-9][0-9]* to avoid circular symlink (.so.1 has single digit)
for lib in libnvidia-ml libcuda; do
versioned=$(ls "$CACHE_DIR/lib/${lib}.so."* 2>/dev/null | grep -v '\.so\.1$' | head -1)
[ -n "$versioned" ] || versioned=$(ls "$CACHE_DIR/lib/${lib}.so."* 2>/dev/null | head -1)
versioned=$(ls "$CACHE_DIR/lib/${lib}.so."[0-9][0-9]* 2>/dev/null | head -1)
[ -n "$versioned" ] || continue
base=$(basename "$versioned")
# Only create .so.1 if versioned file is not already named .so.1
if [ "$base" != "${lib}.so.1" ]; then
ln -sf "$base" "$CACHE_DIR/lib/${lib}.so.1"
fi
ln -sf "$base" "$CACHE_DIR/lib/${lib}.so.1"
ln -sf "${lib}.so.1" "$CACHE_DIR/lib/${lib}.so" 2>/dev/null || true
echo "${lib}: .so.1 -> $base"
done
echo "=== NVIDIA build complete ==="

View File

@@ -1,9 +1,9 @@
#!/bin/sh
# build.sh — build bee ISO
# build.sh — build bee ISO (Debian 12 / live-build)
#
# Single build script. Produces a bootable live ISO with SSH access, TUI, NVIDIA drivers.
#
# Run on Alpine builder VM as root after setup-builder.sh.
# Run on Debian 12 builder VM as root after setup-builder.sh.
# Usage:
# sh iso/builder/build.sh [--authorized-keys /path/to/authorized_keys]
@@ -27,15 +27,11 @@ done
. "${BUILDER_DIR}/VERSIONS"
export PATH="$PATH:/usr/local/go/bin"
# NOTE: lz4 compression for modloop is disabled — Alpine initramfs may not support lz4 squashfs.
# Default xz compression is used until lz4 support is confirmed.
echo "=== bee ISO build ==="
echo "Alpine: ${ALPINE_VERSION}, Go: ${GO_VERSION}"
echo "Debian: ${DEBIAN_VERSION}, Kernel ABI: ${DEBIAN_KERNEL_ABI}, Go: ${GO_VERSION}"
echo ""
# --- compile audit binary (static, Linux amd64) ---
# Skip rebuild if binary is newer than all Go source files.
AUDIT_BIN="${DIST_DIR}/bee-audit-linux-amd64"
NEED_BUILD=1
if [ -f "$AUDIT_BIN" ]; then
@@ -58,8 +54,6 @@ else
fi
# --- inject authorized_keys for SSH access ---
# Uses the same Ed25519 keys as release signing (from git.mchus.pro/mchus/keys).
# SSH public keys are stored alongside signing keys as ~/.keys/<name>.key.pub
AUTHORIZED_KEYS_FILE="${OVERLAY_DIR}/root/.ssh/authorized_keys"
mkdir -p "${OVERLAY_DIR}/root/.ssh"
@@ -68,7 +62,6 @@ if [ -n "$AUTH_KEYS" ]; then
chmod 600 "$AUTHORIZED_KEYS_FILE"
echo "SSH authorized_keys: installed from $AUTH_KEYS"
else
# auto-collect all developer SSH public keys from ~/.keys/*.key.pub
> "$AUTHORIZED_KEYS_FILE"
FOUND=0
for ssh_pub in "$HOME"/.keys/*.key.pub; do
@@ -82,15 +75,15 @@ else
echo "SSH authorized_keys: $FOUND key(s) from ~/.keys/*.key.pub"
else
echo "WARNING: no SSH public keys found — falling back to password auth"
echo " SSH login: bee / eeb (user created by bee-sshsetup at boot)"
echo " (generate a key with: sh keys/scripts/keygen.sh <your-name>)"
echo " SSH login: bee / eeb"
USE_PASSWORD_FALLBACK=1
fi
fi
# --- password fallback: write marker file read by init script ---
if [ "${USE_PASSWORD_FALLBACK:-0}" = "1" ]; then
touch "${OVERLAY_DIR}/etc/bee-ssh-password-fallback"
else
rm -f "${OVERLAY_DIR}/etc/bee-ssh-password-fallback"
fi
# --- copy audit binary into overlay ---
@@ -113,20 +106,15 @@ for tool in storcli64 sas2ircu sas3ircu mstflint; do
fi
done
# --- build NVIDIA kernel modules and inject into overlay ---
# --- build NVIDIA kernel modules ---
echo ""
echo "=== building NVIDIA ${NVIDIA_DRIVER_VERSION} modules ==="
sh "${BUILDER_DIR}/build-nvidia-module.sh" "${NVIDIA_DRIVER_VERSION}" "${DIST_DIR}" "${ALPINE_VERSION}"
# Detect kernel version from installed headers (set by build-nvidia-module.sh above)
KVER=$(ls /usr/src/ 2>/dev/null | grep '^linux-headers-' | sed 's/linux-headers-//' | sort -V | tail -1)
[ -n "$KVER" ] || { echo "ERROR: linux-lts-dev not installed — no headers in /usr/src/"; exit 1; }
echo "=== kernel version: ${KVER} ==="
sh "${BUILDER_DIR}/build-nvidia-module.sh" "${NVIDIA_DRIVER_VERSION}" "${DIST_DIR}" "${DEBIAN_KERNEL_ABI}"
KVER="${DEBIAN_KERNEL_ABI}-amd64"
NVIDIA_CACHE="${DIST_DIR}/nvidia-${NVIDIA_DRIVER_VERSION}-${KVER}"
# Inject .ko files into overlay at /usr/local/lib/nvidia/ (not /lib/modules/ — modloop squashfs
# mounts over that path at boot and makes it read-only, so overlay content there is inaccessible)
# Inject .ko files into overlay at /usr/local/lib/nvidia/
OVERLAY_KMOD_DIR="${OVERLAY_DIR}/usr/local/lib/nvidia"
mkdir -p "${OVERLAY_KMOD_DIR}"
cp "${NVIDIA_CACHE}/modules/"*.ko "${OVERLAY_KMOD_DIR}/"
@@ -139,7 +127,6 @@ cp "${NVIDIA_CACHE}/bin/nvidia-bug-report.sh" "${OVERLAY_DIR}/usr/local/bin/" 2>
chmod +x "${OVERLAY_DIR}/usr/local/bin/nvidia-bug-report.sh" 2>/dev/null || true
cp "${NVIDIA_CACHE}/lib/"* "${OVERLAY_DIR}/usr/lib/" 2>/dev/null || true
# --- embed build metadata ---
mkdir -p "${OVERLAY_DIR}/etc"
BUILD_DATE="$(date +%Y-%m-%d)"
@@ -149,60 +136,54 @@ BEE_ISO_VERSION=${AUDIT_VERSION}
BEE_AUDIT_VERSION=${AUDIT_VERSION}
BUILD_DATE=${BUILD_DATE}
GIT_COMMIT=${GIT_COMMIT}
ALPINE_VERSION=${ALPINE_VERSION}
DEBIAN_VERSION=${DEBIAN_VERSION}
DEBIAN_KERNEL_ABI=${DEBIAN_KERNEL_ABI}
NVIDIA_DRIVER_VERSION=${NVIDIA_DRIVER_VERSION}
EOF
# --- export build info for genapkovl to inject into motd ---
BUILD_DATE=$(date +%Y-%m-%d)
GIT_COMMIT=$(git -C "${REPO_ROOT}" rev-parse --short HEAD 2>/dev/null || echo "unknown")
export BEE_BUILD_INFO="${BUILD_DATE} git:${GIT_COMMIT} alpine:${ALPINE_VERSION} nvidia:${NVIDIA_DRIVER_VERSION}"
# --- build ISO using mkimage ---
mkdir -p "${DIST_DIR}"
echo ""
echo "=== building ISO ==="
# Install our mkimage profile where mkimage.sh can find it.
# ~/.mkimage is the user plugin directory loaded by mkimage.sh.
# Clear ~/.mkimage to avoid stale profiles from previous builds being picked up
rm -rf "${HOME}/.mkimage"
mkdir -p "${HOME}/.mkimage"
cp "${BUILDER_DIR}/mkimg.bee.sh" "${HOME}/.mkimage/"
cp "${BUILDER_DIR}/genapkovl-bee.sh" "${HOME}/.mkimage/"
# Export overlay dir so the profile script can find it regardless of SRCDIR.
export BEE_OVERLAY_DIR="${OVERLAY_DIR}"
# Clean workdir: always nuke apks_* (stale packages from old mirror/version cause "unable to select" errors).
# Keep kernel_*, syslinux_*, grub_* — these are large but stable; they only change when KERNEL_PKG_VERSION changes.
if [ -d /var/tmp/bee-iso-work ]; then
find /var/tmp/bee-iso-work -maxdepth 1 -mindepth 1 \
-not -name 'kernel_*' \
-not -name 'syslinux_*' -not -name 'grub_*' \
-exec rm -rf {} + 2>/dev/null || true
# Patch motd with build info
BEE_BUILD_INFO="${BUILD_DATE} git:${GIT_COMMIT} debian:${DEBIAN_VERSION} nvidia:${NVIDIA_DRIVER_VERSION}"
if [ -f "${OVERLAY_DIR}/etc/motd" ]; then
sed "s/%%BUILD_INFO%%/${BEE_BUILD_INFO}/" "${OVERLAY_DIR}/etc/motd" \
> "${OVERLAY_DIR}/etc/motd.patched"
mv "${OVERLAY_DIR}/etc/motd.patched" "${OVERLAY_DIR}/etc/motd"
fi
# Run from /var/tmp: mkimage.sh calls git internally; running from inside /root/bee causes
# "outside repository" errors. /var/tmp is outside the git repo and has enough scratch space.
# genapkovl-bee.sh is found by mkimage via ~/.mkimage/.
# Remove any stale genapkovl from /var/tmp — mkimage checks CWD first, stale files override ~/.mkimage/.
rm -f /var/tmp/genapkovl-*.sh
export TMPDIR=/var/tmp
cd /var/tmp
sh /usr/share/aports/scripts/mkimage.sh \
--tag "v${ALPINE_VERSION}" \
--outdir "${DIST_DIR}" \
--arch x86_64 \
--repository "https://dl-cdn.alpinelinux.org/alpine/v${ALPINE_VERSION}/main" \
--repository "https://dl-cdn.alpinelinux.org/alpine/v${ALPINE_VERSION}/community" \
--workdir /var/tmp/bee-iso-work \
--profile bee
# --- sync overlay into live-build includes.chroot ---
LB_DIR="${BUILDER_DIR}"
LB_INCLUDES="${LB_DIR}/config/includes.chroot"
mkdir -p "${LB_INCLUDES}"
rsync -a "${OVERLAY_DIR}/" "${LB_INCLUDES}/"
ISO="${DIST_DIR}/alpine-bee-${ALPINE_VERSION}-x86_64.iso"
# Ensure SSH authorized_keys perms are correct (rsync may alter)
if [ -f "${LB_INCLUDES}/root/.ssh/authorized_keys" ]; then
chmod 700 "${LB_INCLUDES}/root/.ssh"
chmod 600 "${LB_INCLUDES}/root/.ssh/authorized_keys"
fi
# --- build ISO using live-build ---
mkdir -p "${DIST_DIR}"
echo ""
echo "=== done ==="
echo "ISO: $ISO"
echo "Size: $(du -sh "$ISO" 2>/dev/null | cut -f1 || echo 'not found')"
echo "=== building ISO (live-build) ==="
cd "${LB_DIR}"
lb clean 2>&1 | tail -3
lb config 2>&1 | tail -5
lb build 2>&1
# live-build outputs live-image-amd64.hybrid.iso in LB_DIR
ISO_RAW="${LB_DIR}/live-image-amd64.hybrid.iso"
ISO_OUT="${DIST_DIR}/bee-debian${DEBIAN_VERSION}-v${AUDIT_VERSION}-amd64.iso"
if [ -f "$ISO_RAW" ]; then
cp "$ISO_RAW" "$ISO_OUT"
echo ""
echo "=== done ==="
echo "ISO: $ISO_OUT"
echo "Size: $(du -sh "$ISO_OUT" | cut -f1)"
else
echo "ERROR: ISO not found at $ISO_RAW"
exit 1
fi
echo ""
echo "Boot via BMC virtual media and SSH to the server IP on port 22 as root."

View File

@@ -0,0 +1,29 @@
#!/bin/sh
# 9000-bee-setup.hook.chroot — runs inside Debian chroot during live-build
# Enables bee systemd services and configures the live environment.
set -e
echo "=== bee chroot setup ==="
# Enable bee services
systemctl enable bee-network.service
systemctl enable bee-nvidia.service
systemctl enable bee-audit.service
systemctl enable bee-sshsetup.service
systemctl enable ssh.service
# Ensure scripts are executable
chmod +x /usr/local/bin/bee-network.sh 2>/dev/null || true
chmod +x /usr/local/bin/bee-nvidia-load 2>/dev/null || true
chmod +x /usr/local/bin/bee-sshsetup 2>/dev/null || true
chmod +x /usr/local/bin/bee-smoketest 2>/dev/null || true
chmod +x /usr/local/bin/bee-tui 2>/dev/null || true
chmod +x /usr/local/bin/audit 2>/dev/null || true
# Reload udev rules
udevadm control --reload-rules 2>/dev/null || true
# Create log directory
mkdir -p /var/log
echo "=== bee chroot setup complete ==="

View File

@@ -0,0 +1,12 @@
# Generated at build time — do not commit
usr/local/bin/audit
usr/local/bin/bee-smoketest
usr/local/bin/nvidia-smi
usr/local/bin/nvidia-bug-report.sh
usr/local/lib/
usr/lib/libnvidia-ml*
usr/lib/libcuda*
root/.ssh/authorized_keys
etc/bee-release
etc/bee-ssh-password-fallback
etc/motd

View File

@@ -0,0 +1,35 @@
# Hardware audit tools
dmidecode
smartmontools
nvme-cli
pciutils
ipmitool
util-linux
e2fsprogs
lshw
lsblk
# Network
iproute2
dhclient
iputils-ping
# SSH
openssh-server
# Utilities
procps
lsof
file
less
vim-tiny
dialog
# QR codes (for displaying audit results)
qrencode
# Firmware
firmware-linux-free
# glibc compat helpers (for any external binaries that need it)
libc6

View File

@@ -1,108 +0,0 @@
#!/bin/sh -e
HOSTNAME="$1"
[ -n "$HOSTNAME" ] || { echo "usage: $0 hostname"; exit 1; }
OVERLAY="${BEE_OVERLAY_DIR}"
[ -n "$OVERLAY" ] || { echo "ERROR: BEE_OVERLAY_DIR not set"; exit 1; }
cleanup() { rm -rf "$tmp"; }
tmp="$(mktemp -d)"
trap cleanup EXIT
makefile() { OWNER="$1" PERMS="$2" FILENAME="$3"; cat > "$FILENAME"; chown "$OWNER" "$FILENAME"; chmod "$PERMS" "$FILENAME"; }
rc_add() { mkdir -p "$tmp/etc/runlevels/$2"; ln -sf /etc/init.d/"$1" "$tmp/etc/runlevels/$2/$1"; }
mkdir -p "$tmp/etc"
makefile root:root 0644 "$tmp/etc/hostname" <<EOF
$HOSTNAME
EOF
# Empty interfaces file — prevents ifupdown from erroring, bee-network handles DHCP
mkdir -p "$tmp/etc/network"
makefile root:root 0644 "$tmp/etc/network/interfaces" <<EOF
auto lo
iface lo inet loopback
EOF
mkdir -p "$tmp/etc/apk"
makefile root:root 0644 "$tmp/etc/apk/world" <<EOF
alpine-base
dmidecode
smartmontools
nvme-cli
pciutils
ipmitool
util-linux
lsblk
e2fsprogs
lshw
dropbear
libqrencode-tools
tzdata
ca-certificates
strace
procps
lsof
file
less
vim
dialog
gcompat
libc6-compat
EOF
rc_add devfs sysinit
rc_add dmesg sysinit
rc_add mdev sysinit
rc_add hwdrivers sysinit
rc_add modloop sysinit
rc_add hwclock boot
rc_add modules boot
rc_add sysctl boot
rc_add hostname boot
rc_add bootmisc boot
rc_add syslog boot
rc_add mount-ro shutdown
rc_add killprocs shutdown
rc_add savecache shutdown
rc_add bee-sshsetup default
rc_add bee-network default
rc_add dropbear default
rc_add bee-nvidia default
rc_add bee-audit default
if [ -d "$OVERLAY/etc" ]; then
cp -r "$OVERLAY/etc/." "$tmp/etc/"
chmod +x "$tmp/etc/init.d/"* 2>/dev/null || true
[ -n "$BEE_BUILD_INFO" ] && sed -i "s/%%BUILD_INFO%%/${BEE_BUILD_INFO}/" "$tmp/etc/motd" 2>/dev/null || true
fi
mkdir -p "$tmp/usr"
if [ -d "$OVERLAY/usr" ]; then
cp -r "$OVERLAY/usr/." "$tmp/usr/"
chmod +x "$tmp/usr/local/bin/"* 2>/dev/null || true
fi
if [ -d "$OVERLAY/root" ]; then
mkdir -p "$tmp/root"
cp -r "$OVERLAY/root/." "$tmp/root/"
chmod 700 "$tmp/root/.ssh" 2>/dev/null || true
chmod 600 "$tmp/root/.ssh/authorized_keys" 2>/dev/null || true
fi
if [ -d "$OVERLAY/lib" ]; then
mkdir -p "$tmp/lib"
cp -r "$OVERLAY/lib/." "$tmp/lib/"
fi
mkdir -p "$tmp/etc/dropbear" "$tmp/etc/conf.d"
# -R: auto-generate host keys if missing
# no dependency on networking service — bee-network handles DHCP independently
makefile root:root 0644 "$tmp/etc/conf.d/dropbear" <<EOF
DROPBEAR_OPTS="-R -B"
EOF
tar -c -C "$tmp" etc usr root lib 2>/dev/null | gzip -9n > "$HOSTNAME.apkovl.tar.gz"

View File

@@ -1,58 +0,0 @@
#!/bin/sh
# Alpine mkimage profile: bee
profile_bee() {
title="Bee Hardware Audit"
desc="Hardware audit LiveCD"
arch="x86_64"
hostname="alpine-bee"
apkovl="genapkovl-bee.sh"
image_ext="iso"
output_format="iso"
kernel_flavors="lts"
kernel_addons=""
initfs_cmdline="modules=loop,squashfs,sd-mod,usb-storage modloop=/boot/modloop-lts quiet"
initfs_features="ata base cdrom ext4 mmc nvme raid scsi squashfs usb virtio nfit"
grub_mod="all_video disk part_gpt part_msdos linux normal configfile search search_label efi_gop fat iso9660 cat echo ls test true help gzio multiboot2 efi_uga"
syslinux_serial="0 115200"
apks="
alpine-base
linux-firmware-none
linux-firmware-rtl_nic
linux-firmware-bnx2
linux-firmware-bnx2x
linux-firmware-tigon
linux-firmware-qlogic
linux-firmware-netronome
linux-firmware-mellanox
linux-firmware-intel
linux-firmware-other
dmidecode
smartmontools
nvme-cli
pciutils
ipmitool
util-linux
lsblk
e2fsprogs
lshw
dropbear
openrc
libqrencode-tools
tzdata
ca-certificates
strace
procps
lsof
file
less
vim
dialog
gcompat
libc6-compat
"
}

View File

@@ -1,10 +1,10 @@
#!/bin/sh
# setup-builder.sh — prepare Alpine VM as bee ISO builder
# setup-builder.sh — prepare Debian 12 VM as bee ISO builder
#
# Run once on a fresh Alpine 3.21 VM as root.
# After this script completes, the VM can build ISO images.
# Run once on a fresh Debian 12 (Bookworm) VM as root.
# After this script completes, the VM can build bee ISO images.
#
# Usage (on Alpine VM):
# Usage (on Debian VM):
# wget -O- https://git.mchus.pro/mchus/bee/raw/branch/main/iso/builder/setup-builder.sh | sh
# or: sh setup-builder.sh
@@ -12,65 +12,41 @@ set -e
. "$(dirname "$0")/VERSIONS" 2>/dev/null || true
GO_VERSION="${GO_VERSION:-1.23.6}"
DEBIAN_VERSION="${DEBIAN_VERSION:-12}"
DEBIAN_KERNEL_ABI="${DEBIAN_KERNEL_ABI:-6.1.0-28}"
echo "=== bee builder setup ==="
echo "Alpine: $(cat /etc/alpine-release)"
echo "Debian: $(cat /etc/debian_version)"
echo "Go target: ${GO_VERSION}"
echo "Kernel ABI: ${DEBIAN_KERNEL_ABI}"
echo ""
# --- system packages ---
apk update
# enable community repo if not already enabled
sed -i 's|^#\(.*community\)|\1|' /etc/apk/repositories
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apk update
apk add \
alpine-sdk \
abuild \
apt-get install -y \
live-build \
debootstrap \
squashfs-tools \
xorriso \
grub-pc-bin \
grub-efi-amd64-bin \
mtools \
grub \
grub-efi \
grub-bios \
git \
wget \
curl \
tar \
xz \
screen
xz-utils \
screen \
rsync \
build-essential \
gcc \
make \
perl \
"linux-headers-${DEBIAN_KERNEL_ABI}-amd64"
# --- audit runtime packages (verify they exist in Alpine repos) ---
echo ""
echo "=== verifying audit runtime packages ==="
RUNTIME_PKGS="
dmidecode
smartmontools
nvme-cli
pciutils
ipmitool
util-linux
e2fsprogs
qrencode
dropbear
udhcpc
pciutils-libs
lshw
"
MISSING=""
for pkg in $RUNTIME_PKGS; do
if apk info --quiet "$pkg" 2>/dev/null || apk search --quiet "$pkg" 2>/dev/null | grep -q "^${pkg}-"; then
echo " OK: $pkg"
else
echo " MISSING: $pkg"
MISSING="$MISSING $pkg"
fi
done
if [ -n "$MISSING" ]; then
echo ""
echo "WARNING: missing packages:$MISSING"
echo "These will not be available in the ISO."
fi
echo "linux-headers installed: $(dpkg -l "linux-headers-${DEBIAN_KERNEL_ABI}-amd64" | awk '/^ii/{print $3}')"
# --- Go toolchain ---
echo ""
@@ -93,38 +69,6 @@ fi
export PATH="$PATH:/usr/local/go/bin"
echo "Go: $(go version)"
# --- alpine-conf for mkimage ---
apk add alpine-conf
# --- aports for mkimage.sh ---
if [ ! -d /usr/share/aports ]; then
echo ""
echo "=== cloning aports ==="
git clone --depth=1 --branch "v${ALPINE_VERSION:-3.21}.0" \
https://gitlab.alpinelinux.org/alpine/aports.git \
/usr/share/aports
fi
# --- abuild signing key (required by mkimage.sh) ---
if [ ! -f "${HOME}/.abuild/abuild.conf" ]; then
echo ""
echo "=== generating abuild signing key ==="
mkdir -p "${HOME}/.abuild"
abuild-keygen -a -n 2>/dev/null || true
# abuild-keygen requires doas to install the key system-wide; do it manually
PUB=$(ls "${HOME}/.abuild/"*.pub 2>/dev/null | head -1)
if [ -n "$PUB" ]; then
cp "$PUB" /etc/apk/keys/
PRIV="${PUB%.pub}"
echo "PACKAGER_PRIVKEY=\"${PRIV}\"" > "${HOME}/.abuild/abuild.conf"
echo "abuild key: $PRIV"
else
echo "WARNING: abuild key generation failed"
fi
fi
# NOTE: lz4 compression for modloop is disabled — Alpine initramfs may not support lz4 squashfs.
echo ""
echo "=== builder setup complete ==="
echo "Next: sh iso/builder/build-debug.sh"
echo "Next: sh iso/builder/build.sh"

View File

@@ -24,11 +24,10 @@ echo " date: $(date -u)"
echo "========================================"
echo ""
# --- kernel version ---
KVER=$(uname -r)
info "kernel: $KVER"
# --- PATH ---
# --- PATH & binaries ---
echo "-- PATH & binaries --"
for tool in dmidecode smartctl nvme ipmitool lspci audit; do
if p=$(PATH="/usr/local/bin:$PATH" command -v "$tool" 2>/dev/null); then
@@ -96,30 +95,21 @@ for lib in libnvidia-ml libcuda; do
done
echo ""
echo "-- gcompat (glibc compat for nvidia-smi) --"
if [ -L /lib64/ld-linux-x86-64.so.2 ] || [ -f /lib64/ld-linux-x86-64.so.2 ]; then
ok "gcompat: /lib64/ld-linux-x86-64.so.2 present"
else
fail "gcompat: /lib64/ld-linux-x86-64.so.2 MISSING — nvidia-smi will fail to exec"
fi
echo ""
echo "-- openrc services --"
echo "-- systemd services --"
for svc in bee-nvidia bee-network bee-audit; do
if rc-service "$svc" status >/dev/null 2>&1; then
ok "service running: $svc"
if systemctl is-active --quiet "$svc" 2>/dev/null; then
ok "service active: $svc"
else
fail "service NOT running: $svc"
fail "service NOT active: $svc"
fi
done
for svc in dropbear bee-sshsetup; do
if [ -f "/etc/init.d/$svc" ]; then
if rc-service "$svc" status >/dev/null 2>&1; then
ok "service running: $svc"
else
warn "service not running: $svc (may be one-shot)"
fi
for svc in ssh bee-sshsetup; do
if systemctl is-active --quiet "$svc" 2>/dev/null \
|| systemctl show "$svc" --property=ActiveState 2>/dev/null | grep -q "inactive\|exited"; then
ok "service ok: $svc"
else
warn "service status unknown: $svc"
fi
done
@@ -136,8 +126,6 @@ fi
echo ""
echo "-- audit last run --"
# audit binary logs via slog to stderr (bee-audit.log); JSON output goes to bee-audit.json.
# slog format: time=... level=INFO msg="audit output written" path=...
if [ -f /var/log/bee-audit.json ] && [ -s /var/log/bee-audit.json ]; then
ok "audit: bee-audit.json present and non-empty"
info "size: $(du -sh /var/log/bee-audit.json | cut -f1)"
@@ -148,13 +136,11 @@ fi
if [ -f /var/log/bee-audit.log ]; then
last_line=$(tail -1 /var/log/bee-audit.log)
info "last log line: $last_line"
# slog writes: msg="audit output written" on success
if grep -q "audit output written" /var/log/bee-audit.log 2>/dev/null; then
ok "audit: completed successfully"
else
warn "audit: 'audit output written' not found in log — may have failed"
fi
# check for nvidia enrichment skip (slog message from nvidia collector)
if grep -q "nvidia: enrichment skipped\|nvidia.*skipped\|enrichment skipped" /var/log/bee-audit.log 2>/dev/null; then
reason=$(grep -E "nvidia.*skipped|enrichment skipped" /var/log/bee-audit.log | tail -1)
fail "audit: nvidia enrichment skipped — $reason"

View File

@@ -1 +0,0 @@
DROPBEAR_OPTS="-p 22 -R -B"

View File

@@ -1,21 +0,0 @@
#!/sbin/openrc-run
description="Bee: run hardware audit"
depend() {
need localmount
after bee-network bee-nvidia
}
start() {
ebegin "Running hardware audit"
/usr/local/bin/audit --output "file:/var/log/bee-audit.json" 2>/var/log/bee-audit.log
local rc=$?
if [ $rc -eq 0 ]; then
einfo "Audit complete: /var/log/bee-audit.json"
einfo "SSH in and inspect results. Dropbear is running."
else
ewarn "Audit finished with errors — check /var/log/bee-audit.log"
fi
eend 0
}

View File

@@ -1,14 +0,0 @@
#!/sbin/openrc-run
description="Bee: bring up network interfaces via DHCP"
depend() {
need localmount
before bee-audit
}
start() {
ebegin "Bringing up network interfaces"
/usr/local/bin/bee-network.sh >> /var/log/bee-network.log 2>&1
eend 0
}

View File

@@ -1,79 +0,0 @@
#!/sbin/openrc-run
description="Bee: load NVIDIA kernel modules"
NVIDIA_KO_DIR="/usr/local/lib/nvidia"
depend() {
need localmount
before bee-audit
}
start() {
ebegin "Loading NVIDIA modules"
einfo "kernel: $(uname -r)"
if [ ! -d "$NVIDIA_KO_DIR" ]; then
ewarn "NVIDIA module dir missing: $NVIDIA_KO_DIR"
eend 1
return 1
fi
einfo "module dir: $NVIDIA_KO_DIR"
ls "$NVIDIA_KO_DIR"/*.ko 2>/dev/null | sed 's/^/ /' || true
# Create libnvidia-ml soname symlinks needed by nvidia-smi (glibc binary on Alpine/musl)
for lib in libnvidia-ml libcuda; do
versioned=$(ls /usr/lib/${lib}.so.[0-9]* 2>/dev/null | head -1)
[ -n "$versioned" ] || continue
base=$(basename "$versioned")
ln -sf "$base" "/usr/lib/${lib}.so.1" 2>/dev/null || true
ln -sf "${lib}.so.1" "/usr/lib/${lib}.so" 2>/dev/null || true
done
# Load modules via insmod (bypasses modules.dep — modloop squashfs is read-only)
for mod in nvidia nvidia-modeset nvidia-uvm; do
ko="$NVIDIA_KO_DIR/${mod}.ko"
[ -f "$ko" ] || ko="$NVIDIA_KO_DIR/${mod//-/_}.ko"
if [ -f "$ko" ]; then
if insmod "$ko" 2>/dev/null; then
einfo "loaded: $mod"
else
ewarn "failed to load: $mod"
dmesg | tail -n 5 | sed 's/^/ dmesg: /' || true
fi
else
ewarn "not found: $ko"
fi
done
# Create /dev/nvidia* device nodes — mdev on Alpine does not have NVIDIA rules,
# so the kernel hotplug events are not handled and nodes are never created.
# Without /dev/nvidiactl nvidia-smi returns NVML_ERROR_LIBRARY_NOT_FOUND (exit 12).
nvidia_major=$(grep -m1 ' nvidiactl$' /proc/devices 2>/dev/null | awk '{print $1}')
if [ -n "$nvidia_major" ]; then
mknod -m 666 /dev/nvidiactl c "$nvidia_major" 255 2>/dev/null \
&& einfo "created /dev/nvidiactl (major $nvidia_major)" \
|| ewarn "/dev/nvidiactl already exists or mknod failed"
for i in 0 1 2 3 4 5 6 7; do
mknod -m 666 "/dev/nvidia$i" c "$nvidia_major" "$i" 2>/dev/null || true
done
einfo "created /dev/nvidia{0-7}"
else
ewarn "/dev/nvidiactl: nvidia not in /proc/devices — no GPU hardware present?"
eend 0
return 0
fi
uvm_major=$(grep -m1 ' nvidia-uvm$' /proc/devices 2>/dev/null | awk '{print $1}')
if [ -n "$uvm_major" ]; then
mknod -m 666 /dev/nvidia-uvm c "$uvm_major" 0 2>/dev/null \
&& einfo "created /dev/nvidia-uvm (major $uvm_major)" \
|| ewarn "/dev/nvidia-uvm already exists or mknod failed"
mknod -m 666 /dev/nvidia-uvm-tools c "$uvm_major" 1 2>/dev/null || true
else
ewarn "/dev/nvidia-uvm: nvidia-uvm not in /proc/devices"
fi
eend 0
}

View File

@@ -1,28 +0,0 @@
#!/sbin/openrc-run
description="Bee: configure SSH access (keys or password fallback)"
depend() {
need localmount
before dropbear
}
start() {
# Always create dedicated 'bee' user for password fallback.
# If no SSH keys embedded: login with bee / eeb
if ! id bee > /dev/null 2>&1; then
adduser -D -s /bin/sh bee > /dev/null 2>&1
fi
printf 'eeb\neeb\n' | passwd bee > /dev/null 2>&1
if [ -f /etc/bee-ssh-password-fallback ]; then
ebegin "SSH key auth unavailable — password fallback active"
ewarn "Login: bee / eeb"
ewarn "Generate a key: sh keys/scripts/keygen.sh <name>"
eend 0
else
ebegin "SSH key auth configured"
# bee user exists but password login less useful when keys work
eend 0
fi
}

View File

@@ -1,37 +0,0 @@
#!/sbin/openrc-run
description="Dropbear SSH server"
depend() {
need localmount
after bee-sshsetup
use logger
}
check_config() {
if [ ! -e /etc/dropbear/dropbear_rsa_host_key ]; then
einfo "Generating RSA host key..."
/usr/bin/dropbearkey -t rsa -f /etc/dropbear/dropbear_rsa_host_key
fi
if [ ! -e /etc/dropbear/dropbear_ecdsa_host_key ]; then
einfo "Generating ECDSA host key..."
/usr/bin/dropbearkey -t ecdsa -f /etc/dropbear/dropbear_ecdsa_host_key
fi
if [ ! -e /etc/dropbear/dropbear_ed25519_host_key ]; then
einfo "Generating ED25519 host key..."
/usr/bin/dropbearkey -t ed25519 -f /etc/dropbear/dropbear_ed25519_host_key
fi
}
start() {
check_config || return 1
ebegin "Starting dropbear"
/usr/sbin/dropbear ${DROPBEAR_OPTS}
eend $?
}
stop() {
ebegin "Stopping dropbear"
start-stop-daemon --stop --pidfile /var/run/dropbear.pid
eend $?
}

View File

@@ -1,13 +0,0 @@
::sysinit:/sbin/openrc sysinit
::sysinit:/sbin/openrc boot
::wait:/sbin/openrc default
# Autologin on tty1
tty1::respawn:/sbin/agetty --autologin root --noclear tty1 linux
tty2::respawn:/sbin/getty 38400 tty2
tty3::respawn:/sbin/getty 38400 tty3
ttyS0::respawn:/sbin/getty -L 115200 ttyS0 vt100
::ctrlaltdel:/sbin/reboot
::shutdown:/sbin/openrc shutdown

View File

@@ -0,0 +1,14 @@
[Unit]
Description=Bee: run hardware audit
After=bee-network.service bee-nvidia.service network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/audit --output file:/var/log/bee-audit.json
StandardOutput=append:/var/log/bee-audit.log
StandardError=append:/var/log/bee-audit.log
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,14 @@
[Unit]
Description=Bee: bring up network interfaces via DHCP
After=local-fs.target
Before=network-online.target bee-audit.service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/bee-network.sh
StandardOutput=append:/var/log/bee-network.log
StandardError=append:/var/log/bee-network.log
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,14 @@
[Unit]
Description=Bee: load NVIDIA kernel modules and create device nodes
After=local-fs.target udev.service
Before=bee-audit.service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/bee-nvidia-load
StandardOutput=journal
StandardError=journal
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,12 @@
[Unit]
Description=Bee: configure SSH access (keys or password fallback)
After=local-fs.target
Before=ssh.service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/bee-sshsetup
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target

View File

@@ -22,9 +22,8 @@ for iface in $interfaces; do
log "bringing up $iface"
ip link set "$iface" up 2>/dev/null || { log "WARN: could not bring up $iface"; continue; }
# DHCP in background: -b forks if no immediate lease, & ensures non-blocking always.
# -t 0: unlimited retries, -T 3: 3s per attempt. No -q: stay running to renew lease.
udhcpc -i "$iface" -b -t 0 -T 3 &
# DHCP in background — non-blocking, retries indefinitely
dhclient -nw "$iface" 2>/dev/null &
log "DHCP started for $iface (pid $!)"
done

View File

@@ -0,0 +1,59 @@
#!/bin/sh
# bee-nvidia-load — load NVIDIA kernel modules and create device nodes
# Called by bee-nvidia.service at boot.
NVIDIA_KO_DIR="/usr/local/lib/nvidia"
log() { echo "[bee-nvidia] $*"; }
log "kernel: $(uname -r)"
if [ ! -d "$NVIDIA_KO_DIR" ]; then
log "ERROR: NVIDIA module dir missing: $NVIDIA_KO_DIR"
exit 1
fi
log "module dir: $NVIDIA_KO_DIR"
ls "$NVIDIA_KO_DIR"/*.ko 2>/dev/null | sed 's/^/ /' || true
# Load modules via insmod (direct load — no depmod needed)
for mod in nvidia nvidia-modeset nvidia-uvm; do
ko="$NVIDIA_KO_DIR/${mod}.ko"
[ -f "$ko" ] || ko="$NVIDIA_KO_DIR/${mod//-/_}.ko"
if [ -f "$ko" ]; then
if insmod "$ko" 2>/dev/null; then
log "loaded: $mod"
else
log "WARN: failed to load: $mod"
dmesg | tail -n 5 | sed 's/^/ dmesg: /' || true
fi
else
log "WARN: not found: $ko"
fi
done
# Create /dev/nvidia* device nodes (udev rules absent since we use .run installer)
nvidia_major=$(grep -m1 ' nvidiactl$' /proc/devices 2>/dev/null | awk '{print $1}')
if [ -n "$nvidia_major" ]; then
mknod -m 666 /dev/nvidiactl c "$nvidia_major" 255 2>/dev/null \
&& log "created /dev/nvidiactl (major $nvidia_major)" \
|| log "WARN: /dev/nvidiactl already exists or mknod failed"
for i in 0 1 2 3 4 5 6 7; do
mknod -m 666 "/dev/nvidia$i" c "$nvidia_major" "$i" 2>/dev/null || true
done
log "created /dev/nvidia{0-7}"
else
log "WARN: nvidiactl not in /proc/devices — no GPU hardware present?"
fi
uvm_major=$(grep -m1 ' nvidia-uvm$' /proc/devices 2>/dev/null | awk '{print $1}')
if [ -n "$uvm_major" ]; then
mknod -m 666 /dev/nvidia-uvm c "$uvm_major" 0 2>/dev/null \
&& log "created /dev/nvidia-uvm (major $uvm_major)" \
|| log "WARN: /dev/nvidia-uvm already exists"
mknod -m 666 /dev/nvidia-uvm-tools c "$uvm_major" 1 2>/dev/null || true
else
log "WARN: nvidia-uvm not in /proc/devices"
fi
log "done"

View File

@@ -0,0 +1,18 @@
#!/bin/sh
# bee-sshsetup — configure SSH access
# Called by bee-sshsetup.service before SSH starts.
log() { echo "[bee-sshsetup] $*"; }
# Always create dedicated 'bee' user for password fallback.
if ! id bee > /dev/null 2>&1; then
useradd -m -s /bin/sh bee > /dev/null 2>&1
fi
echo "bee:eeb" | chpasswd > /dev/null 2>&1
if [ -f /etc/bee-ssh-password-fallback ]; then
log "SSH key auth unavailable — password fallback active"
log "Login: bee / eeb"
else
log "SSH key auth configured"
fi