Implement audit enrichments, TUI workflows, and production ISO scaffold
This commit is contained in:
97
iso/builder/build.sh
Executable file
97
iso/builder/build.sh
Executable 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
82
iso/builder/genapkovl-bee.sh
Executable 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"
|
||||
@@ -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
47
iso/builder/mkimg.bee.sh
Executable 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
|
||||
"
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
620
iso/overlay-debug/usr/local/bin/bee-tui
Executable file
620
iso/overlay-debug/usr/local/bin/bee-tui
Executable 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
|
||||
20
iso/overlay/etc/init.d/bee-audit
Executable file
20
iso/overlay/etc/init.d/bee-audit
Executable 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
|
||||
}
|
||||
14
iso/overlay/etc/init.d/bee-network
Executable file
14
iso/overlay/etc/init.d/bee-network
Executable 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
|
||||
}
|
||||
23
iso/overlay/etc/init.d/bee-nvidia
Executable file
23
iso/overlay/etc/init.d/bee-nvidia
Executable 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
|
||||
}
|
||||
15
iso/overlay/etc/init.d/bee-update
Executable file
15
iso/overlay/etc/init.d/bee-update
Executable 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
8
iso/overlay/etc/motd
Normal 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
|
||||
1
iso/overlay/etc/profile.d/bee.sh
Normal file
1
iso/overlay/etc/profile.d/bee.sh
Normal file
@@ -0,0 +1 @@
|
||||
export PATH="$PATH:/usr/local/bin"
|
||||
24
iso/overlay/usr/local/bin/bee-network.sh
Executable file
24
iso/overlay/usr/local/bin/bee-network.sh
Executable 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"
|
||||
108
iso/overlay/usr/local/bin/bee-update.sh
Executable file
108
iso/overlay/usr/local/bin/bee-update.sh
Executable 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
0
iso/vendor/.gitkeep
vendored
Normal file
Reference in New Issue
Block a user