Implement audit enrichments, TUI workflows, and production ISO scaffold

This commit is contained in:
Mikhail Chusavitin
2026-03-06 11:56:26 +03:00
parent bdfb6a0a79
commit 18b8c69bc5
32 changed files with 3187 additions and 9 deletions

97
iso/builder/build.sh Executable file
View File

@@ -0,0 +1,97 @@
#!/bin/sh
# build.sh — production ISO build (unattended mode)
set -e
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
BUILDER_DIR="${REPO_ROOT}/iso/builder"
OVERLAY_DIR="${REPO_ROOT}/iso/overlay"
DIST_DIR="${REPO_ROOT}/dist"
VENDOR_DIR="${REPO_ROOT}/iso/vendor"
. "${BUILDER_DIR}/VERSIONS"
export PATH="$PATH:/usr/local/go/bin"
echo "=== bee production ISO build ==="
echo "Alpine: ${ALPINE_VERSION}, Go: ${GO_VERSION}, NVIDIA: ${NVIDIA_DRIVER_VERSION}"
AUDIT_BIN="${DIST_DIR}/bee-audit-linux-amd64"
mkdir -p "$DIST_DIR"
cd "${REPO_ROOT}/audit"
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 \
go build \
-ldflags "-s -w -X main.Version=${AUDIT_VERSION}" \
-o "$AUDIT_BIN" \
./cmd/audit
mkdir -p "${OVERLAY_DIR}/usr/local/bin"
cp "$AUDIT_BIN" "${OVERLAY_DIR}/usr/local/bin/audit"
chmod +x "${OVERLAY_DIR}/usr/local/bin/audit"
# Copy optional vendor utilities if already fetched.
for tool in storcli64 sas2ircu sas3ircu mstflint; do
if [ -f "${VENDOR_DIR}/${tool}" ]; then
cp "${VENDOR_DIR}/${tool}" "${OVERLAY_DIR}/usr/local/bin/${tool}"
chmod +x "${OVERLAY_DIR}/usr/local/bin/${tool}" || true
echo "vendor tool: ${tool} (included)"
else
echo "vendor tool: ${tool} (not found, skipped)"
fi
done
# Build and inject NVIDIA proprietary modules + userspace tools.
echo "=== building NVIDIA modules ==="
sh "${BUILDER_DIR}/build-nvidia-module.sh" "${NVIDIA_DRIVER_VERSION}" "${DIST_DIR}"
KVER="$(ls /usr/src/ 2>/dev/null | grep '^linux-headers-' | sed 's/linux-headers-//' | head -1)"
NVIDIA_CACHE="${DIST_DIR}/nvidia-${NVIDIA_DRIVER_VERSION}-${KVER}"
mkdir -p "${OVERLAY_DIR}/lib/modules/${KVER}/extra/nvidia"
cp "${NVIDIA_CACHE}/modules/"*.ko "${OVERLAY_DIR}/lib/modules/${KVER}/extra/nvidia/"
mkdir -p "${OVERLAY_DIR}/usr/local/bin" "${OVERLAY_DIR}/usr/lib"
cp "${NVIDIA_CACHE}/bin/nvidia-smi" "${OVERLAY_DIR}/usr/local/bin/"
chmod +x "${OVERLAY_DIR}/usr/local/bin/nvidia-smi"
cp "${NVIDIA_CACHE}/lib/"* "${OVERLAY_DIR}/usr/lib/" 2>/dev/null || true
# Embed build metadata used at runtime.
mkdir -p "${OVERLAY_DIR}/etc"
BUILD_DATE="$(date +%Y-%m-%d)"
GIT_COMMIT="$(git -C "${REPO_ROOT}" rev-parse --short HEAD 2>/dev/null || echo unknown)"
cat > "${OVERLAY_DIR}/etc/bee-release" <<EOF
BEE_ISO_VERSION=${AUDIT_VERSION}
BEE_AUDIT_VERSION=${AUDIT_VERSION}
BUILD_DATE=${BUILD_DATE}
GIT_COMMIT=${GIT_COMMIT}
ALPINE_VERSION=${ALPINE_VERSION}
NVIDIA_DRIVER_VERSION=${NVIDIA_DRIVER_VERSION}
EOF
mkdir -p "${HOME}/.mkimage"
cp "${BUILDER_DIR}/mkimg.bee.sh" "${HOME}/.mkimage/"
cp "${BUILDER_DIR}/genapkovl-bee.sh" "${HOME}/.mkimage/"
export BEE_OVERLAY_DIR="${OVERLAY_DIR}"
if [ -d /var/tmp/bee-iso-work ]; then
find /var/tmp/bee-iso-work -maxdepth 1 -mindepth 1 \
-not -name 'apks_*' -not -name 'kernel_*' \
-not -name 'syslinux_*' -not -name 'grub_*' \
-exec rm -rf {} + 2>/dev/null || true
fi
export TMPDIR=/var/tmp
cp "${BUILDER_DIR}/genapkovl-bee.sh" /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
ISO="${DIST_DIR}/alpine-bee-${ALPINE_VERSION}-x86_64.iso"
echo "=== done ==="
echo "ISO: $ISO"

82
iso/builder/genapkovl-bee.sh Executable file
View File

@@ -0,0 +1,82 @@
#!/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" <<EOT
$HOSTNAME
EOT
mkdir -p "$tmp/etc/network"
makefile root:root 0644 "$tmp/etc/network/interfaces" <<EOT
auto lo
iface lo inet loopback
EOT
mkdir -p "$tmp/etc/apk"
makefile root:root 0644 "$tmp/etc/apk/world" <<EOT
alpine-base
dmidecode
smartmontools
nvme-cli
pciutils
ipmitool
util-linux
lsblk
e2fsprogs
lshw
openrc
ca-certificates
tzdata
jq
wget
EOT
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-network default
rc_add bee-update 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
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/lib" ]; then
mkdir -p "$tmp/lib"
cp -r "$OVERLAY/lib/." "$tmp/lib/"
fi
tar -c -C "$tmp" etc usr lib 2>/dev/null | gzip -9n > "$HOSTNAME.apkovl.tar.gz"

View File

@@ -89,6 +89,11 @@ if [ -d "$OVERLAY/root" ]; then
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
@@ -97,4 +102,4 @@ DROPBEAR_OPTS="-R -B"
EOF
tar -c -C "$tmp" etc usr root 2>/dev/null | gzip -9n > "$HOSTNAME.apkovl.tar.gz"
tar -c -C "$tmp" etc usr root lib 2>/dev/null | gzip -9n > "$HOSTNAME.apkovl.tar.gz"

47
iso/builder/mkimg.bee.sh Executable file
View File

@@ -0,0 +1,47 @@
#!/bin/sh
# Alpine mkimage profile: bee (production)
profile_bee() {
title="Bee Hardware Audit"
desc="Hardware audit LiveCD (production unattended mode)"
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"
apks="
alpine-base
linux-lts
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
openrc
ca-certificates
tzdata
jq
wget
"
}

View File

@@ -11,9 +11,6 @@
Logs: /var/log/bee-audit.json /var/log/bee-network.log
Re-run audit: audit --output stdout | less
Restart net: bee-net-restart
Check tools: which dmidecode smartctl nvme ipmitool lspci
Open TUI: bee-tui
SSH access: key auth (developers) or bee/eeb (password fallback)

View File

@@ -1 +1,12 @@
export PATH="$PATH:/usr/local/bin"
# Auto-open TUI on local tty1 after boot.
# Exiting TUI returns to this shell (console prompt).
if [ -z "${BEE_TUI_AUTO_LAUNCHED:-}" ] \
&& [ -z "${SSH_CONNECTION:-}" ] \
&& [ -z "${SSH_TTY:-}" ] \
&& [ "$(tty 2>/dev/null)" = "/dev/tty1" ] \
&& [ -x /usr/local/bin/bee-tui ]; then
export BEE_TUI_AUTO_LAUNCHED=1
/usr/local/bin/bee-tui
fi

View File

@@ -0,0 +1,620 @@
#!/bin/sh
# bee-tui: interactive text menu for debug LiveCD operations.
set -u
pause() {
echo
printf 'Press Enter to continue... '
read -r _
}
header() {
clear
echo "=============================================="
echo " bee TUI (debug)"
echo "=============================================="
echo
}
list_ifaces() {
ip -o link show \
| awk -F': ' '{print $2}' \
| grep -v '^lo$' \
| grep -vE '^(docker|virbr|veth|tun|tap|br-|bond|dummy)' \
| sort
}
show_network_status() {
header
echo "Network interfaces"
echo
for iface in $(list_ifaces); do
state=$(ip -o link show "$iface" | awk '{print $9}')
ipv4=$(ip -o -4 addr show dev "$iface" | awk '{print $4}' | paste -sd ',')
[ -n "$ipv4" ] || ipv4="(no IPv4)"
echo "- $iface: state=$state ip=$ipv4"
done
echo
ip route | sed 's/^/ route: /'
pause
}
choose_interface() {
ifaces="$(list_ifaces)"
if [ -z "$ifaces" ]; then
echo "No physical interfaces found"
return 1
fi
echo "$ifaces" | nl -w2 -s'. '
echo
printf 'Select interface number: '
read -r idx
iface=$(echo "$ifaces" | sed -n "${idx}p")
if [ -z "$iface" ]; then
echo "Invalid interface selection"
return 1
fi
CHOSEN_IFACE="$iface"
return 0
}
network_dhcp_one() {
header
echo "DHCP on one interface"
echo
choose_interface || { pause; return; }
iface="$CHOSEN_IFACE"
echo
echo "Starting DHCP on $iface..."
ip link set "$iface" up 2>/dev/null || true
udhcpc -i "$iface" -t 5 -T 3
pause
}
network_dhcp_all() {
header
echo "Restarting DHCP on all physical interfaces..."
echo
/usr/local/bin/bee-net-restart
pause
}
network_static_one() {
header
echo "Static IPv4 setup"
echo
choose_interface || { pause; return; }
iface="$CHOSEN_IFACE"
echo
printf 'IPv4 address (example 192.168.1.10): '
read -r ip
if [ -z "$ip" ]; then
echo "IP address is required"
pause
return
fi
printf 'Netmask (example 24 or 255.255.255.0): '
read -r mask
if [ -z "$mask" ]; then
echo "Netmask is required"
pause
return
fi
prefix=$(mask_to_prefix "$mask")
if [ -z "$prefix" ]; then
echo "Invalid netmask: $mask"
pause
return
fi
cidr="$ip/$prefix"
printf 'Default gateway: '
read -r gw
if [ -z "$gw" ]; then
echo "Default gateway is required"
pause
return
fi
printf 'DNS server (optional): '
read -r dns
ip link set "$iface" up 2>/dev/null || true
ip addr flush dev "$iface"
if ! ip addr add "$cidr" dev "$iface"; then
echo "Failed to set IP"
pause
return
fi
if [ -n "$gw" ]; then
ip route del default >/dev/null 2>&1 || true
ip route add default via "$gw" dev "$iface"
fi
if [ -n "$dns" ]; then
printf 'nameserver %s\n' "$dns" > /etc/resolv.conf
fi
echo
echo "Static config applied to $iface"
pause
}
mask_to_prefix() {
mask="$(echo "$1" | tr -d '[:space:]')"
case "$mask" in
0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32)
echo "$mask"
return 0
;;
esac
case "$mask" in
255.0.0.0) echo 8 ;;
255.128.0.0) echo 9 ;;
255.192.0.0) echo 10 ;;
255.224.0.0) echo 11 ;;
255.240.0.0) echo 12 ;;
255.248.0.0) echo 13 ;;
255.252.0.0) echo 14 ;;
255.254.0.0) echo 15 ;;
255.255.0.0) echo 16 ;;
255.255.128.0) echo 17 ;;
255.255.192.0) echo 18 ;;
255.255.224.0) echo 19 ;;
255.255.240.0) echo 20 ;;
255.255.248.0) echo 21 ;;
255.255.252.0) echo 22 ;;
255.255.254.0) echo 23 ;;
255.255.255.0) echo 24 ;;
255.255.255.128) echo 25 ;;
255.255.255.192) echo 26 ;;
255.255.255.224) echo 27 ;;
255.255.255.240) echo 28 ;;
255.255.255.248) echo 29 ;;
255.255.255.252) echo 30 ;;
255.255.255.254) echo 31 ;;
255.255.255.255) echo 32 ;;
*) return 1 ;;
esac
}
network_menu() {
while true; do
header
echo "Network"
echo "1. Show network status"
echo "2. DHCP on all interfaces"
echo "3. DHCP on one interface"
echo "4. Set static IPv4 on one interface"
echo "5. Back"
echo
printf 'Choice: '
read -r choice
case "$choice" in
1) show_network_status ;;
2) network_dhcp_all ;;
3) network_dhcp_one ;;
4) network_static_one ;;
5) return ;;
*) echo "Invalid choice"; pause ;;
esac
done
}
bee_services_list() {
for path in /etc/init.d/bee-*; do
[ -e "$path" ] || continue
basename "$path"
done
}
services_status_all() {
header
echo "bee service status"
echo
for svc in $(bee_services_list); do
if rc-service "$svc" status >/dev/null 2>&1; then
echo "- $svc: running"
else
echo "- $svc: stopped"
fi
done
pause
}
choose_service() {
svcs="$(bee_services_list)"
if [ -z "$svcs" ]; then
echo "No bee-* services found"
return 1
fi
echo "$svcs" | nl -w2 -s'. '
echo
printf 'Select service number: '
read -r idx
svc=$(echo "$svcs" | sed -n "${idx}p")
if [ -z "$svc" ]; then
echo "Invalid service selection"
return 1
fi
CHOSEN_SERVICE="$svc"
return 0
}
service_action_menu() {
header
echo "Service action"
echo
choose_service || { pause; return; }
svc="$CHOSEN_SERVICE"
echo
echo "Selected: $svc"
echo "1. status"
echo "2. restart"
echo "3. start"
echo "4. stop"
echo "5. toggle start/stop"
echo
printf 'Choice: '
read -r act
case "$act" in
1)
rc-service "$svc" status || true
;;
2)
rc-service "$svc" restart || true
;;
3)
rc-service "$svc" start || true
;;
4)
rc-service "$svc" stop || true
;;
5)
if rc-service "$svc" status >/dev/null 2>&1; then
rc-service "$svc" stop || true
else
rc-service "$svc" start || true
fi
;;
*)
echo "Invalid action"
;;
esac
pause
}
services_menu() {
while true; do
header
echo "bee Services"
echo "1. Status of all bee-* services"
echo "2. Manage one service (status/restart/start/stop/toggle)"
echo "3. Back"
echo
printf 'Choice: '
read -r choice
case "$choice" in
1) services_status_all ;;
2) service_action_menu ;;
3) return ;;
*) echo "Invalid choice"; pause ;;
esac
done
}
confirm_phrase() {
phrase="$1"
prompt="$2"
echo
printf '%s (%s): ' "$prompt" "$phrase"
read -r value
[ "$value" = "$phrase" ]
}
shutdown_menu() {
while true; do
header
echo "Shutdown/Reboot Tests"
echo "1. Reboot now"
echo "2. Power off now"
echo "3. Schedule poweroff in 60s"
echo "4. Cancel scheduled shutdown"
echo "5. IPMI chassis power status"
echo "6. IPMI chassis power soft"
echo "7. IPMI chassis power cycle"
echo "8. Back"
echo
printf 'Choice: '
read -r choice
case "$choice" in
1)
confirm_phrase "REBOOT" "Type confirmation" || { echo "Canceled"; pause; continue; }
reboot
;;
2)
confirm_phrase "POWEROFF" "Type confirmation" || { echo "Canceled"; pause; continue; }
poweroff
;;
3)
confirm_phrase "SCHEDULE" "Type confirmation" || { echo "Canceled"; pause; continue; }
shutdown -P +1 "bee test: scheduled poweroff in 60 seconds"
echo "Scheduled"
pause
;;
4)
shutdown -c || true
echo "Canceled (if any schedule existed)"
pause
;;
5)
ipmitool chassis power status || echo "ipmitool power status failed"
pause
;;
6)
confirm_phrase "IPMI-SOFT" "Type confirmation" || { echo "Canceled"; pause; continue; }
ipmitool chassis power soft || echo "ipmitool soft power failed"
pause
;;
7)
confirm_phrase "IPMI-CYCLE" "Type confirmation" || { echo "Canceled"; pause; continue; }
ipmitool chassis power cycle || echo "ipmitool power cycle failed"
pause
;;
8)
return
;;
*)
echo "Invalid choice"
pause
;;
esac
done
}
gpu_burn_10m() {
header
echo "GPU Burn (10 minutes)"
echo
if ! command -v gpu_burn >/dev/null 2>&1; then
echo "gpu_burn binary not found in PATH"
echo "Expected command: gpu_burn"
pause
return
fi
if ! command -v nvidia-smi >/dev/null 2>&1 || ! nvidia-smi -L >/dev/null 2>&1; then
echo "NVIDIA driver/GPU not ready (nvidia-smi failed)"
pause
return
fi
confirm_phrase "GPU-BURN" "Type confirmation to start benchmark" || { echo "Canceled"; pause; return; }
echo "Running: gpu_burn 600"
echo "Log: /var/log/bee-gpuburn.log"
gpu_burn 600 2>&1 | tee /var/log/bee-gpuburn.log
echo
echo "GPU Burn finished"
pause
}
gpu_benchmarks_menu() {
while true; do
header
echo "Benchmarks -> GPU"
echo "1. GPU Burn (10 minutes)"
echo "2. Back"
echo
printf 'Choice: '
read -r choice
case "$choice" in
1) gpu_burn_10m ;;
2) return ;;
*) echo "Invalid choice"; pause ;;
esac
done
}
benchmarks_menu() {
while true; do
header
echo "Benchmarks"
echo "1. GPU"
echo "2. Back"
echo
printf 'Choice: '
read -r choice
case "$choice" in
1) gpu_benchmarks_menu ;;
2) return ;;
*) echo "Invalid choice"; pause ;;
esac
done
}
run_cmd_log() {
label="$1"
cmd="$2"
log_file="$3"
{
echo "=== $label ==="
echo "time: $(date -u '+%Y-%m-%dT%H:%M:%SZ')"
echo "cmd: $cmd"
echo
sh -c "$cmd"
} >"$log_file" 2>&1
return $?
}
run_gpu_nvidia_acceptance_test() {
header
echo "System acceptance tests -> GPU NVIDIA"
echo
confirm_phrase "SAT-GPU" "Type confirmation to start tests" || { echo "Canceled"; pause; return; }
ts="$(date -u '+%Y%m%d-%H%M%S')"
base_dir="/var/log/bee-sat"
run_dir="$base_dir/gpu-nvidia-$ts"
archive="$base_dir/gpu-nvidia-$ts.tar.gz"
mkdir -p "$run_dir"
summary="$run_dir/summary.txt"
: >"$summary"
echo "Running acceptance commands..."
echo "Logs directory: $run_dir"
echo "Archive target: $archive"
echo
c1="nvidia-smi -q"
c2="dmidecode -t baseboard"
c3="dmidecode -t system"
c4="nvidia-bug-report.sh"
run_cmd_log "nvidia_smi_q" "$c1" "$run_dir/01-nvidia-smi-q.log"; rc1=$?
run_cmd_log "dmidecode_baseboard" "$c2" "$run_dir/02-dmidecode-baseboard.log"; rc2=$?
run_cmd_log "dmidecode_system" "$c3" "$run_dir/03-dmidecode-system.log"; rc3=$?
run_cmd_log "nvidia_bug_report" "$c4" "$run_dir/04-nvidia-bug-report.log"; rc4=$?
# Collect any bug report artifact generated in cwd.
bug_report="$(ls -1 nvidia-bug-report.log.gz 2>/dev/null | head -n1 || true)"
if [ -n "$bug_report" ] && [ -f "$bug_report" ]; then
cp -f "$bug_report" "$run_dir/"
fi
{
echo "run_at_utc=$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
echo "cmd_nvidia_smi_q_rc=$rc1"
echo "cmd_dmidecode_baseboard_rc=$rc2"
echo "cmd_dmidecode_system_rc=$rc3"
echo "cmd_nvidia_bug_report_rc=$rc4"
} >>"$summary"
tar -czf "$archive" -C "$base_dir" "gpu-nvidia-$ts"
tar_rc=$?
echo "archive_rc=$tar_rc" >>"$summary"
echo
echo "Done."
echo "- Logs: $run_dir"
echo "- Archive: $archive (rc=$tar_rc)"
pause
}
gpu_nvidia_sat_menu() {
while true; do
header
echo "System acceptance tests -> GPU NVIDIA"
echo "1. Run command pack"
echo "2. Back"
echo
printf 'Choice: '
read -r choice
case "$choice" in
1) run_gpu_nvidia_acceptance_test ;;
2) return ;;
*) echo "Invalid choice"; pause ;;
esac
done
}
system_acceptance_tests_menu() {
while true; do
header
echo "System acceptance tests"
echo "1. GPU NVIDIA"
echo "2. Back"
echo
printf 'Choice: '
read -r choice
case "$choice" in
1) gpu_nvidia_sat_menu ;;
2) return ;;
*) echo "Invalid choice"; pause ;;
esac
done
}
run_audit_now() {
header
echo "Run audit now"
echo
/usr/local/bin/audit --output stdout > /var/log/bee-audit.json 2>/var/log/bee-audit.log
rc=$?
if [ "$rc" -eq 0 ]; then
echo "Audit completed successfully"
else
echo "Audit finished with errors (rc=$rc)"
fi
echo "Logs: /var/log/bee-audit.log, /var/log/bee-audit.json"
pause
}
check_required_tools() {
header
echo "Required tools check"
echo
for tool in dmidecode smartctl nvme ipmitool lspci audit nvidia-smi gpu_burn; do
if command -v "$tool" >/dev/null 2>&1; then
echo "- $tool: OK ($(command -v "$tool"))"
else
echo "- $tool: MISSING"
fi
done
pause
}
main_menu() {
while true; do
header
echo "Main Menu"
echo "1. Network setup"
echo "2. bee service management"
echo "3. Shutdown/reboot tests"
echo "4. Benchmarks"
echo "5. System acceptance tests"
echo "6. Run audit now"
echo "7. Check required tools"
echo "8. Show last audit log tail"
echo "9. Exit to console"
echo
printf 'Choice: '
read -r choice
case "$choice" in
1) network_menu ;;
2) services_menu ;;
3) shutdown_menu ;;
4) benchmarks_menu ;;
5) system_acceptance_tests_menu ;;
6) run_audit_now ;;
7) check_required_tools ;;
8)
header
tail -n 40 /var/log/bee-audit.log 2>/dev/null || echo "No /var/log/bee-audit.log"
echo
tail -n 20 /var/log/bee-audit.json 2>/dev/null || true
pause
;;
9) exit 0 ;;
*) echo "Invalid choice"; pause ;;
esac
done
}
main_menu

View File

@@ -0,0 +1,20 @@
#!/sbin/openrc-run
description="Bee: run hardware audit (production unattended mode)"
depend() {
need localmount
after bee-update bee-nvidia
}
start() {
ebegin "Running hardware audit"
/usr/local/bin/audit --output usb > /var/log/bee-audit.json 2>/var/log/bee-audit.log
rc=$?
if [ "$rc" -eq 0 ]; then
einfo "Audit complete"
else
ewarn "Audit finished with errors"
fi
eend 0
}

View File

@@ -0,0 +1,14 @@
#!/sbin/openrc-run
description="Bee: bring up network interfaces via DHCP"
depend() {
need localmount
before bee-update 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

@@ -0,0 +1,23 @@
#!/sbin/openrc-run
description="Bee: load NVIDIA kernel modules"
depend() {
need localmount
before bee-audit
}
start() {
ebegin "Loading NVIDIA modules"
depmod -a 2>/dev/null || true
for mod in nvidia nvidia-modeset nvidia-uvm; do
if modprobe "$mod" 2>/dev/null; then
einfo "loaded: $mod"
else
ewarn "failed to load: $mod"
fi
done
eend 0
}

View File

@@ -0,0 +1,15 @@
#!/sbin/openrc-run
description="Bee: update audit binary from USB/network"
depend() {
need localmount
after bee-network
before bee-audit
}
start() {
ebegin "Checking for audit binary update"
/usr/local/bin/bee-update.sh >> /var/log/bee-update.log 2>&1
eend 0
}

8
iso/overlay/etc/motd Normal file
View File

@@ -0,0 +1,8 @@
Bee Hardware Audit LiveCD
Mode: Production unattended
Logs:
/var/log/bee-network.log
/var/log/bee-update.log
/var/log/bee-audit.log
/var/log/bee-audit.json

View File

@@ -0,0 +1 @@
export PATH="$PATH:/usr/local/bin"

View File

@@ -0,0 +1,24 @@
#!/bin/sh
# bee-network.sh — bring up all physical interfaces via DHCP (non-blocking)
LOG_PREFIX="bee-network"
log() { echo "[$LOG_PREFIX] $*"; }
interfaces=$(ip -o link show \
| awk -F': ' '{print $2}' \
| grep -v '^lo$' \
| grep -vE '^(docker|virbr|veth|tun|tap|br-|bond|dummy)' \
| sort)
if [ -z "$interfaces" ]; then
log "no physical interfaces found"
exit 0
fi
for iface in $interfaces; do
ip link set "$iface" up 2>/dev/null || { log "WARN: failed to bring up $iface"; continue; }
udhcpc -i "$iface" -b -t 0 -T 3 >/dev/null 2>&1 &
log "dhcp started for $iface"
done
log "done"

View File

@@ -0,0 +1,108 @@
#!/bin/sh
# bee-update.sh — production update path: USB first, then network.
# Unattended: logs only, never blocks boot.
set -u
LOG_PREFIX="bee-update"
log() { echo "[$LOG_PREFIX] $*"; }
AUDIT_BIN="/usr/local/bin/audit"
TMP_BIN="/tmp/bee-audit-new"
TMP_SIG="/tmp/bee-audit-new.sig"
REPO_API="${BEE_RELEASE_API:-https://git.mchus.pro/api/v1/repos/<org>/bee/releases/latest}"
version_of() {
"$1" --version 2>/dev/null | head -n1 | tr -d '[:space:]'
}
apply_update() {
src_bin="$1"
src_sig="$2"
src_ver="$3"
if [ ! -x "$src_bin" ] || [ ! -f "$src_sig" ]; then
log "missing binary or signature"
return 1
fi
# NOTE: strict signature verification should be implemented in audit updater module.
# Here we keep shell side minimal and fail-open for now.
cp "$src_bin" "$AUDIT_BIN" || return 1
chmod +x "$AUDIT_BIN" || return 1
log "updated audit binary to $src_ver"
return 0
}
check_usb_update() {
for root in /media/* /mnt/* /tmp/bee-usb /run/media/*/*; do
[ -d "$root" ] || continue
base="$root/bee-update"
bin="$base/bee-audit-linux-amd64"
sig="$base/bee-audit-linux-amd64.sig"
ver_file="$base/VERSION"
[ -f "$bin" ] || continue
[ -f "$sig" ] || continue
[ -f "$ver_file" ] || continue
new_ver=$(cat "$ver_file" 2>/dev/null | tr -d '[:space:]')
cur_ver=$(version_of "$AUDIT_BIN")
[ -n "$new_ver" ] || continue
if [ "$new_ver" = "$cur_ver" ]; then
log "usb update found but version is same ($new_ver)"
return 0
fi
log "usb update candidate: $new_ver"
apply_update "$bin" "$sig" "$new_ver" && return 0
return 1
done
return 1
}
check_network_update() {
if ! ping -c 1 -W 3 git.mchus.pro >/dev/null 2>&1; then
log "network unavailable; skip release check"
return 1
fi
if ! command -v wget >/dev/null 2>&1; then
log "wget not found; skip network update"
return 1
fi
if ! command -v jq >/dev/null 2>&1; then
log "jq not found; skip network update"
return 1
fi
meta="/tmp/bee-release-latest.json"
wget -q -O "$meta" "$REPO_API" || { log "failed to fetch release metadata"; return 1; }
tag=$(jq -r '.tag_name // empty' "$meta")
[ -n "$tag" ] || { log "release metadata missing tag_name"; return 1; }
cur_ver=$(version_of "$AUDIT_BIN")
if [ "$tag" = "$cur_ver" ]; then
log "already latest ($tag)"
return 0
fi
bin_url=$(jq -r '.assets[]? | select(.name=="bee-audit-linux-amd64") | .browser_download_url // empty' "$meta")
sig_url=$(jq -r '.assets[]? | select(.name=="bee-audit-linux-amd64.sig") | .browser_download_url // empty' "$meta")
[ -n "$bin_url" ] && [ -n "$sig_url" ] || { log "missing release asset URLs"; return 1; }
wget -q -O "$TMP_BIN" "$bin_url" || return 1
wget -q -O "$TMP_SIG" "$sig_url" || return 1
chmod +x "$TMP_BIN"
log "network update candidate: $tag"
apply_update "$TMP_BIN" "$TMP_SIG" "$tag"
}
main() {
if check_usb_update; then
exit 0
fi
check_network_update || true
}
main "$@"

0
iso/vendor/.gitkeep vendored Normal file
View File