feat(iso): 2.1-2.3 — debug ISO builder with SSH access
Builder setup: - iso/builder/VERSIONS: pinned Alpine 3.21, Go 1.23.6, NVIDIA 550.54.15 - iso/builder/setup-builder.sh: installs build deps + Go on Alpine VM, verifies packages - iso/builder/build-debug.sh: compiles audit binary, injects SSH keys, builds ISO - iso/builder/mkimg.bee_debug.sh: Alpine mkimage profile (all audit packages + dropbear) SSH access (same Ed25519 key as release signing): - auto-collects ~/.keys/*.key.pub into authorized_keys at build time - fallback: user bee / password eeb when no keys available - bee-sshsetup init.d service: creates bee user, sets password, logs status Debug overlay: - bee-network: DHCP on all physical interfaces before SSH/audit - bee-audit-debug: runs audit on boot, leaves SSH up after - bee-sshsetup: key/password SSH setup - motd: shows log paths, re-run command, SSH access info Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
542
PLAN.md
Normal file
542
PLAN.md
Normal file
@@ -0,0 +1,542 @@
|
|||||||
|
# BEE — Build Plan
|
||||||
|
|
||||||
|
Hardware audit LiveCD for offline server inventory.
|
||||||
|
Produces `HardwareIngestRequest` JSON compatible with core/reanimator.
|
||||||
|
|
||||||
|
**Principle:** OS-level collection — reads hardware directly, not through BMC.
|
||||||
|
Fully unattended — no user interaction required at any stage. Boot → update → audit → output → done.
|
||||||
|
All errors are logged, never presented interactively. Every failure path has a silent fallback.
|
||||||
|
Fills the gaps where logpile/Redfish is blind: NVMe, DIMM serials, GPU serials, physical disks behind RAID, full SMART, NIC firmware.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 1 — Go Audit Binary
|
||||||
|
|
||||||
|
Self-contained static binary. Runs on any Linux (including Alpine LiveCD).
|
||||||
|
Calls system utilities, parses their output, produces `HardwareIngestRequest` JSON.
|
||||||
|
|
||||||
|
### 1.1 — Project scaffold
|
||||||
|
|
||||||
|
- `audit/go.mod` — module `bee/audit`
|
||||||
|
- `audit/cmd/audit/main.go` — CLI entry point: flags, orchestration, JSON output
|
||||||
|
- `audit/internal/schema/` — copy of `HardwareIngestRequest` types from core (no import dependency)
|
||||||
|
- `audit/internal/collector/` — empty package stubs for all collectors
|
||||||
|
- `const Version = "1.0"` in main
|
||||||
|
- Output modes: stdout (default), file path flag `--output /path/to/file.json`
|
||||||
|
- Tests: none yet (stubs only)
|
||||||
|
|
||||||
|
### 1.2 — Board collector
|
||||||
|
|
||||||
|
Source: `dmidecode -t 0` (BIOS), `-t 1` (System), `-t 2` (Baseboard)
|
||||||
|
|
||||||
|
Collects:
|
||||||
|
- `board.serial_number` — from System Information
|
||||||
|
- `board.manufacturer`, `board.product_name` — from System Information
|
||||||
|
- `board.part_number` — from Baseboard
|
||||||
|
- `board.uuid` — from System Information
|
||||||
|
- `firmware[BIOS]` — vendor, version, release date from BIOS Information
|
||||||
|
|
||||||
|
Tests: table tests with `testdata/dmidecode_*.txt` fixtures
|
||||||
|
|
||||||
|
### 1.3 — CPU collector
|
||||||
|
|
||||||
|
Source: `dmidecode -t 4`
|
||||||
|
|
||||||
|
Collects:
|
||||||
|
- socket index, model, manufacturer, status
|
||||||
|
- cores, threads, current/max frequency
|
||||||
|
- firmware: microcode version from `/sys/devices/system/cpu/cpu0/microcode/version`
|
||||||
|
- serial: not available on Intel Xeon → fallback `<board_serial>-CPU-<socket>` (matches core logic)
|
||||||
|
|
||||||
|
Tests: table tests with dmidecode fixtures
|
||||||
|
|
||||||
|
### 1.4 — Memory collector
|
||||||
|
|
||||||
|
Source: `dmidecode -t 17`
|
||||||
|
|
||||||
|
Collects:
|
||||||
|
- slot, location, present flag
|
||||||
|
- size_mb, type (DDR4/DDR5), max_speed_mhz, current_speed_mhz
|
||||||
|
- manufacturer, serial_number, part_number
|
||||||
|
- status from "Data Width" / "No Module Installed" detection
|
||||||
|
|
||||||
|
Tests: table tests with dmidecode fixtures (populated + empty slots)
|
||||||
|
|
||||||
|
### 1.5 — Storage collector
|
||||||
|
|
||||||
|
Sources:
|
||||||
|
- `lsblk -J -o NAME,TYPE,SIZE,SERIAL,MODEL,TRAN,MOUNTPOINT` — device enumeration
|
||||||
|
- `smartctl -j -i /dev/X` — serial, model, firmware, interface per device
|
||||||
|
- `nvme id-ctrl /dev/nvmeX -o json` — NVMe: serial (sn), firmware (fr), model (mn), size
|
||||||
|
|
||||||
|
Collects per device:
|
||||||
|
- type: SSD/HDD/NVMe
|
||||||
|
- model, serial_number, manufacturer, firmware
|
||||||
|
- size_gb, interface (SATA/SAS/NVMe)
|
||||||
|
- slot: from lsblk HCTL where available
|
||||||
|
|
||||||
|
Tests: table tests with `smartctl -j` JSON fixtures and `nvme id-ctrl` JSON fixtures
|
||||||
|
|
||||||
|
### 1.6 — PCIe collector
|
||||||
|
|
||||||
|
Sources:
|
||||||
|
- `lspci -vmm -D` — slot, vendor, device, class
|
||||||
|
- `lspci -vvv -D` — link width/speed (LnkSta, LnkCap)
|
||||||
|
- embedded pci.ids (same submodule as logpile: `third_party/pciids`) — model name lookup
|
||||||
|
- `/sys/bus/pci/devices/<bdf>/` — actual negotiated link state from kernel
|
||||||
|
|
||||||
|
Collects per device:
|
||||||
|
- bdf, vendor_id, device_id, device_class
|
||||||
|
- manufacturer, model (via pciids lookup if empty)
|
||||||
|
- link_width, link_speed, max_link_width, max_link_speed
|
||||||
|
- serial_number: device-specific (see per-type enrichment in 1.8, 1.9)
|
||||||
|
|
||||||
|
Dedup: by serial → bdf (mirrors logpile canonical device repository logic)
|
||||||
|
|
||||||
|
Tests: table tests with lspci fixtures
|
||||||
|
|
||||||
|
### 1.7 — PSU collector
|
||||||
|
|
||||||
|
Source: `ipmitool fru` — primary (only source for PSU data from OS)
|
||||||
|
Fallback: `dmidecode -t 39` (System Power Supply, limited availability)
|
||||||
|
|
||||||
|
Collects:
|
||||||
|
- slot, present, model, vendor, serial_number, part_number
|
||||||
|
- wattage_w from FRU or dmidecode
|
||||||
|
- firmware from ipmitool FRU `Board Extra` fields
|
||||||
|
- input_power_w, output_power_w, input_voltage from `ipmitool sdr`
|
||||||
|
|
||||||
|
Tests: table tests with ipmitool fru text fixtures
|
||||||
|
|
||||||
|
### 1.8 — NVIDIA GPU enrichment
|
||||||
|
|
||||||
|
Prerequisite: NVIDIA driver loaded (checked via `nvidia-smi -L` exit code)
|
||||||
|
|
||||||
|
Sources:
|
||||||
|
- `nvidia-smi --query-gpu=index,name,serial,vbios_version,temperature.gpu,power.draw,ecc.errors.uncorrected.aggregate.total --format=csv,noheader,nounits`
|
||||||
|
- BDF correlation: `nvidia-smi --query-gpu=index,pci.bus_id --format=csv,noheader` → match to PCIe collector records
|
||||||
|
|
||||||
|
Enriches PCIe records for NVIDIA devices:
|
||||||
|
- `serial_number` — real GPU serial from nvidia-smi
|
||||||
|
- `firmware` — VBIOS version
|
||||||
|
- `status` — derived from ECC uncorrected errors (0 = OK, >0 = WARNING)
|
||||||
|
- telemetry: temperature_c, power_w added to PCIe record attributes
|
||||||
|
|
||||||
|
Fallback (no driver): PCIe record stays as-is with serial fallback `<board_serial>-PCIE-<slot>`
|
||||||
|
|
||||||
|
Tests: table tests with nvidia-smi CSV fixtures
|
||||||
|
|
||||||
|
### 1.8b — Component wear / age telemetry
|
||||||
|
|
||||||
|
Every component that stores its own usage history must have that data collected and placed in the `attributes` / `telemetry` map of the respective record. This is a cross-cutting concern applied on top of the per-collector steps.
|
||||||
|
|
||||||
|
**Storage (SATA/SAS) — smartctl:**
|
||||||
|
- `Power_On_Hours` (attr 9) — total hours powered on
|
||||||
|
- `Power_Cycle_Count` (attr 12)
|
||||||
|
- `Reallocated_Sector_Ct` (attr 5) — wear indicator
|
||||||
|
- `Wear_Leveling_Count` (attr 177, SSD) — NAND wear
|
||||||
|
- `Total_LBAs_Written` (attr 241) — bytes written lifetime
|
||||||
|
- `SSD_Life_Left` (attr 231) — % remaining if reported
|
||||||
|
- Collected as `telemetry` map keys: `power_on_hours`, `power_cycles`, `reallocated_sectors`, `wear_leveling_pct`, `total_lba_written`, `life_remaining_pct`
|
||||||
|
|
||||||
|
**NVMe — nvme smart-log:**
|
||||||
|
- `power_on_hours` — lifetime hours
|
||||||
|
- `power_cycles`
|
||||||
|
- `unsafe_shutdowns` — abnormal power loss count
|
||||||
|
- `percentage_used` — % of rated lifetime consumed (0–100)
|
||||||
|
- `data_units_written` — 512KB units written lifetime
|
||||||
|
- `controller_busy_time` — hours controller was busy
|
||||||
|
- Collected via `nvme smart-log /dev/nvmeX -o json`
|
||||||
|
|
||||||
|
**NVIDIA GPU — nvidia-smi:**
|
||||||
|
- `ecc.errors.uncorrected.aggregate.total` — lifetime uncorrected ECC errors
|
||||||
|
- `ecc.errors.corrected.aggregate.total` — lifetime corrected ECC errors
|
||||||
|
- `clocks_throttle_reasons.hw_slowdown` — thermal/power throttle state
|
||||||
|
- Stored in PCIe device `telemetry`
|
||||||
|
|
||||||
|
**NIC SFP/QSFP transceivers — ethtool:**
|
||||||
|
- `ethtool -m <iface>` — DOM (Digital Optical Monitoring) if supported
|
||||||
|
- Extracts: TX power, RX power, temperature, voltage, bias current
|
||||||
|
- Also: `ethtool -i <iface>` → firmware version
|
||||||
|
- `ip -s link show <iface>` → tx_packets, rx_packets, tx_errors, rx_errors (uptime proxy)
|
||||||
|
- Stored in PCIe device `telemetry`: `sfp_temperature_c`, `sfp_tx_power_dbm`, `sfp_rx_power_dbm`
|
||||||
|
|
||||||
|
**PSU — ipmitool sdr:**
|
||||||
|
- Input/output power readings over time not stored by BMC (point-in-time only)
|
||||||
|
- `ipmitool fru` may include manufacture date for age estimation
|
||||||
|
- Stored: `input_power_w`, `output_power_w`, `input_voltage` (already in PSU schema)
|
||||||
|
|
||||||
|
**All wear telemetry placement rules:**
|
||||||
|
- Numeric wear indicators go into `telemetry` map (machine-readable, importable by core)
|
||||||
|
- Boolean flags (throttle_active, ecc_errors_present) go into `attributes` map
|
||||||
|
- Never flatten into named top-level fields not in the schema — use maps
|
||||||
|
|
||||||
|
Tests: table tests for each SMART parser (JSON fixtures from smartctl/nvme smart-log)
|
||||||
|
|
||||||
|
### 1.9 — Mellanox/NVIDIA NIC enrichment
|
||||||
|
|
||||||
|
Source: `mstflint -d <bdf> q` — if mstflint present and device is Mellanox (vendor_id 0x15b3)
|
||||||
|
Fallback: `ethtool -i <iface>` — firmware-version field
|
||||||
|
|
||||||
|
Enriches PCIe/NIC records:
|
||||||
|
- `firmware` — from mstflint `FW Version` or ethtool `firmware-version`
|
||||||
|
- `serial_number` — from mstflint `Board Serial Number` if available
|
||||||
|
|
||||||
|
Detection: by PCI vendor_id (0x15b3 = Mellanox/NVIDIA Networking) from PCIe collector
|
||||||
|
|
||||||
|
Tests: table tests with mstflint output fixtures
|
||||||
|
|
||||||
|
### 1.10 — RAID controller enrichment
|
||||||
|
|
||||||
|
Source: tool selected by PCI vendor_id:
|
||||||
|
|
||||||
|
| PCI vendor_id | Tool | Controller |
|
||||||
|
|---|---|---|
|
||||||
|
| 0x1000 | `storcli64 /c<n> show all J` | Broadcom MegaRAID |
|
||||||
|
| 0x1000 (SAS) | `sas2ircu <n> display` / `sas3ircu <n> display` | LSI SAS 2.x/3.x |
|
||||||
|
| 0x9005 | `arcconf getconfig <n>` | Adaptec |
|
||||||
|
| 0x103c | `ssacli ctrl slot=<n> pd all show detail` | HPE Smart Array |
|
||||||
|
|
||||||
|
Collects physical drives behind controller (not visible to OS as block devices):
|
||||||
|
- serial_number, model, manufacturer, firmware
|
||||||
|
- size_gb, interface (SAS/SATA), slot/bay
|
||||||
|
- status (Online/Failed/Rebuilding → OK/CRITICAL/WARNING)
|
||||||
|
|
||||||
|
No hardcoded vendor names in detection logic — pure PCI vendor_id map.
|
||||||
|
|
||||||
|
Tests: table tests with storcli/sas2ircu text fixtures
|
||||||
|
|
||||||
|
### 1.11 — Output and USB write
|
||||||
|
|
||||||
|
`--output stdout` (default): pretty-printed JSON to stdout
|
||||||
|
`--output file:<path>`: write JSON to explicit path
|
||||||
|
`--output usb`: auto-detect first removable block device, mount it, write `audit-<board_serial>-<YYYYMMDD-HHMMSS>.json`
|
||||||
|
|
||||||
|
USB detection: scan `/sys/block/*/removable`, pick first `1`, mount to `/tmp/bee-usb`
|
||||||
|
|
||||||
|
QR summary to stdout (always): board serial + model + component counts — fits in one QR code
|
||||||
|
Uses `qrencode` if present, else skips silently
|
||||||
|
|
||||||
|
### 1.12 — Integration test (local)
|
||||||
|
|
||||||
|
`scripts/test-local.sh` — runs audit binary on developer machine (Linux), captures JSON,
|
||||||
|
validates required fields are present (board.serial_number non-empty, cpus non-empty, etc.)
|
||||||
|
|
||||||
|
Not a unit test — requires real hardware access. Documents how to run for verification.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 2 — Alpine LiveCD
|
||||||
|
|
||||||
|
ISO image bootable via BMC virtual media. Runs audit binary automatically on boot.
|
||||||
|
|
||||||
|
### 2.1 — Builder environment
|
||||||
|
|
||||||
|
`iso/builder/Dockerfile` — Alpine 3.21 build environment with:
|
||||||
|
- `alpine-sdk`, `abuild`, `squashfs-tools`, `xorriso`
|
||||||
|
- Go toolchain (for binary compilation inside builder)
|
||||||
|
- NVIDIA driver `.run` pre-fetched during image build
|
||||||
|
|
||||||
|
`iso/builder/build.sh` — orchestrates full ISO build:
|
||||||
|
1. Compile Go binary (static, `CGO_ENABLED=0`)
|
||||||
|
2. Compile NVIDIA kernel module against Alpine 3.21 LTS kernel headers
|
||||||
|
3. Run `mkimage.sh` with bee profile
|
||||||
|
4. Output: `dist/bee-<version>.iso`
|
||||||
|
|
||||||
|
### 2.2 — NVIDIA driver build
|
||||||
|
|
||||||
|
Alpine 3.21, LTS kernel 6.6 — fixed versions in builder.
|
||||||
|
|
||||||
|
`iso/builder/build-nvidia.sh`:
|
||||||
|
- Download `NVIDIA-Linux-x86_64-<ver>.run` (version pinned in `iso/builder/VERSIONS`)
|
||||||
|
- Extract kernel module sources
|
||||||
|
- Compile against `linux-lts-dev` headers
|
||||||
|
- Strip and package as `nvidia-<ver>-k6.6.ko.tar.gz` for inclusion in overlay
|
||||||
|
|
||||||
|
`iso/overlay/usr/local/bin/load-nvidia.sh`:
|
||||||
|
- `insmod` sequence: nvidia.ko → nvidia-modeset.ko → nvidia-uvm.ko
|
||||||
|
- Verify: `nvidia-smi -L` → log result
|
||||||
|
- On failure: log warning, continue (audit runs without GPU enrichment)
|
||||||
|
|
||||||
|
### 2.3 — Alpine mkimage profile
|
||||||
|
|
||||||
|
`iso/builder/mkimg.bee.sh` — Alpine mkimage profile:
|
||||||
|
- Base: `alpine-base`
|
||||||
|
- Kernel: `linux-lts`
|
||||||
|
- Packages: `dmidecode smartmontools nvme-cli pciutils ipmitool util-linux e2fsprogs qrencode`
|
||||||
|
- Overlay: `iso/overlay/` included as apkovl
|
||||||
|
|
||||||
|
### 2.4 — Network bring-up on boot
|
||||||
|
|
||||||
|
`iso/overlay/usr/local/bin/bee-network.sh`:
|
||||||
|
- Enumerate all network interfaces: `ip link show` → filter out loopback and virtual (docker/bridge)
|
||||||
|
- For each physical interface: `ip link set <iface> up` + `udhcpc -i <iface> -t 5 -T 3 -n`
|
||||||
|
- Log each interface result (got IP / timeout / no carrier)
|
||||||
|
- Continue regardless — network is best-effort for auto-update
|
||||||
|
|
||||||
|
`iso/overlay/etc/init.d/bee-network`:
|
||||||
|
- runlevel: default, before: bee-update
|
||||||
|
- Calls bee-network.sh
|
||||||
|
- Does not block boot if DHCP fails on all interfaces
|
||||||
|
|
||||||
|
### 2.5 — OpenRC boot service (bee-audit)
|
||||||
|
|
||||||
|
`iso/overlay/etc/init.d/bee-audit`:
|
||||||
|
- runlevel: default, after: bee-update
|
||||||
|
- start(): load-nvidia.sh → /usr/local/bin/audit --output usb
|
||||||
|
- on completion: print QR summary to /dev/tty1 (always, even if USB write failed)
|
||||||
|
- log everything to /var/log/bee-audit.log
|
||||||
|
- exits 0 regardless of partial failures — unattended, no prompts, no waits
|
||||||
|
|
||||||
|
Unattended invariants:
|
||||||
|
- No TTY prompts ever. All decisions are automatic.
|
||||||
|
- Missing USB: output goes to /tmp/bee-audit-<serial>-<date>.json, QR shown on screen.
|
||||||
|
- Missing NVIDIA driver: GPU records have status UNKNOWN, audit continues.
|
||||||
|
- Missing ipmitool/storcli/any tool: that collector is skipped, rest continue.
|
||||||
|
- Timeout on any external command: 30s hard limit via `timeout` wrapper, then skip.
|
||||||
|
- Boot never hangs waiting for user input.
|
||||||
|
|
||||||
|
`iso/overlay/etc/runlevels/default/bee-audit` symlink
|
||||||
|
|
||||||
|
### 2.6 — Vendor utilities in overlay
|
||||||
|
|
||||||
|
`iso/overlay/usr/local/bin/` includes pre-fetched proprietary tools:
|
||||||
|
- `storcli64` (Broadcom)
|
||||||
|
- `sas2ircu`, `sas3ircu` (Broadcom/LSI)
|
||||||
|
- `mstflint` (NVIDIA Networking / Mellanox)
|
||||||
|
|
||||||
|
`scripts/fetch-vendor.sh` — downloads and places these before ISO build.
|
||||||
|
Checksums verified. Tools not committed to git — fetched at build time.
|
||||||
|
|
||||||
|
`iso/vendor/.gitkeep` — placeholder, directory gitignored except .gitkeep
|
||||||
|
|
||||||
|
### 2.7 — Auto-update of audit binary (USB + network)
|
||||||
|
|
||||||
|
Two update paths, tried in order on every boot:
|
||||||
|
|
||||||
|
**Path A — USB (no network required, higher priority):**
|
||||||
|
|
||||||
|
`bee-update.sh` scans mounted removable media for an update package before checking network.
|
||||||
|
|
||||||
|
Looks for: `<usb>/bee-update/bee-audit-linux-amd64` + `<usb>/bee-update/bee-audit-linux-amd64.sha256`
|
||||||
|
|
||||||
|
Steps:
|
||||||
|
1. Find USB mount point (same detection as audit output: `/sys/block/*/removable`)
|
||||||
|
2. Check for `bee-update/bee-audit-linux-amd64` on the USB root
|
||||||
|
3. Read version from `bee-update/VERSION` file (plain text, e.g. `1.3`)
|
||||||
|
4. Compare with running binary version (`/usr/local/bin/audit --version`)
|
||||||
|
5. If USB version > running: verify SHA256 checksum, replace binary, log update
|
||||||
|
6. Re-run audit if updated
|
||||||
|
|
||||||
|
**Authenticity verification — Ed25519 multi-key trust (stdlib only, no external tools):**
|
||||||
|
|
||||||
|
Problem: SHA256 alone does not prevent a crafted attack — an attacker places their binary
|
||||||
|
and a matching SHA256 next to it. The LiveCD would accept it.
|
||||||
|
|
||||||
|
Solution: Ed25519 asymmetric signatures via Go stdlib `crypto/ed25519`.
|
||||||
|
Multiple developer public keys are supported. A binary update is accepted if its signature
|
||||||
|
verifies against ANY of the embedded trusted public keys.
|
||||||
|
|
||||||
|
This mirrors the SSH authorized_keys model: add a developer → add their public key.
|
||||||
|
Remove a developer → rebuild without their key.
|
||||||
|
|
||||||
|
**Key management — centralized across all projects:**
|
||||||
|
|
||||||
|
Public keys live in a dedicated repo at git.mchus.pro/mchus/keys (or similar):
|
||||||
|
```
|
||||||
|
keys/
|
||||||
|
developers/
|
||||||
|
mchusavitin.pub ← Ed25519 public key, base64, one line
|
||||||
|
developer2.pub
|
||||||
|
README.md ← how to generate a key pair
|
||||||
|
```
|
||||||
|
|
||||||
|
Public keys are safe to commit — they are not secret.
|
||||||
|
Private keys stay on each developer's machine, never committed anywhere.
|
||||||
|
|
||||||
|
Key generation (one-time per developer, run locally):
|
||||||
|
```sh
|
||||||
|
# scripts/keygen.sh — also lives in the keys repo
|
||||||
|
openssl genpkey -algorithm ed25519 -out ~/.bee-release.key
|
||||||
|
openssl pkey -in ~/.bee-release.key -pubout -outform DER \
|
||||||
|
| tail -c 32 | base64 > mchusavitin.pub
|
||||||
|
```
|
||||||
|
|
||||||
|
**Embedding public keys at release time (not compile time):**
|
||||||
|
|
||||||
|
Public keys are injected via `-ldflags` at build time from the keys repo.
|
||||||
|
The binary does not hardcode keys — they are provided by the release script.
|
||||||
|
|
||||||
|
```go
|
||||||
|
// audit/internal/updater/trust.go
|
||||||
|
// trustedKeysRaw is injected at build time via -ldflags
|
||||||
|
// format: base64(key1):base64(key2):...
|
||||||
|
var trustedKeysRaw string
|
||||||
|
|
||||||
|
func trustedKeys() ([]ed25519.PublicKey, error) {
|
||||||
|
if trustedKeysRaw == "" {
|
||||||
|
return nil, fmt.Errorf("binary built without trusted keys — updates disabled")
|
||||||
|
}
|
||||||
|
var keys []ed25519.PublicKey
|
||||||
|
for _, enc := range strings.Split(trustedKeysRaw, ":") {
|
||||||
|
b, err := base64.StdEncoding.DecodeString(strings.TrimSpace(enc))
|
||||||
|
if err != nil || len(b) != ed25519.PublicKeySize {
|
||||||
|
return nil, fmt.Errorf("invalid trusted key: %w", err)
|
||||||
|
}
|
||||||
|
keys = append(keys, ed25519.PublicKey(b))
|
||||||
|
}
|
||||||
|
return keys, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifySignature(binaryPath, sigPath string) error {
|
||||||
|
keys, err := trustedKeys()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
data, _ := os.ReadFile(binaryPath)
|
||||||
|
sig, _ := os.ReadFile(sigPath) // 64 bytes raw Ed25519 signature
|
||||||
|
for _, key := range keys {
|
||||||
|
if ed25519.Verify(key, data, sig) {
|
||||||
|
return nil // any trusted key accepts → pass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("signature verification failed: no trusted key matched")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Release build injects keys:
|
||||||
|
```sh
|
||||||
|
# scripts/build-release.sh
|
||||||
|
KEYS=$(paste -sd: keys/developers/*.pub)
|
||||||
|
go build -ldflags "-X bee/audit/internal/updater/trust.trustedKeysRaw=${KEYS}" \
|
||||||
|
-o dist/bee-audit-linux-amd64 ./cmd/audit
|
||||||
|
```
|
||||||
|
|
||||||
|
Signing (release engineer signs with their private key):
|
||||||
|
```sh
|
||||||
|
# scripts/sign-release.sh <binary>
|
||||||
|
openssl pkeyutl -sign -inkey ~/.bee-release.key \
|
||||||
|
-rawin -in "$1" -out "$1.sig"
|
||||||
|
```
|
||||||
|
|
||||||
|
Binary built without `-ldflags` injection (e.g. local dev build) has `trustedKeysRaw=""`
|
||||||
|
→ updates are disabled, logged as INFO, audit continues normally.
|
||||||
|
|
||||||
|
Update rejected silently (logged as WARNING, audit continues with current binary) if:
|
||||||
|
- `.sig` file missing
|
||||||
|
- Signature does not match any trusted key
|
||||||
|
- `trustedKeysRaw` empty (dev build)
|
||||||
|
|
||||||
|
Update package layout on USB:
|
||||||
|
```
|
||||||
|
/bee-update/
|
||||||
|
bee-audit-linux-amd64 ← new binary (also signed with embedded keys)
|
||||||
|
bee-audit-linux-amd64.sig ← Ed25519 signature (64 bytes raw)
|
||||||
|
VERSION ← plain version string e.g. "1.3"
|
||||||
|
```
|
||||||
|
|
||||||
|
Admin workflow: download `bee-audit-linux-amd64` + `bee-audit-linux-amd64.sig` from Gitea
|
||||||
|
release assets, place in `bee-update/` on USB.
|
||||||
|
|
||||||
|
**Path B — Network (requires DHCP on at least one interface):**
|
||||||
|
1. Check network: ping git.mchus.pro -c 1 -W 3 || skip
|
||||||
|
2. Fetch: `GET https://git.mchus.pro/api/v1/repos/<org>/bee/releases/latest`
|
||||||
|
3. Parse tag_name, asset URLs for `bee-audit-linux-amd64` + `bee-audit-linux-amd64.sig`
|
||||||
|
4. Compare tag with running version
|
||||||
|
5. If newer: download both files to /tmp, verify Ed25519 signature against all trusted keys
|
||||||
|
6. Replace binary on pass, log and skip on fail
|
||||||
|
7. Re-run audit if updated
|
||||||
|
|
||||||
|
**Ordering:** USB update checked first, network checked second.
|
||||||
|
If USB update applied and verified, network check is skipped.
|
||||||
|
|
||||||
|
`iso/overlay/etc/init.d/bee-update`:
|
||||||
|
- runlevel: default
|
||||||
|
- after: bee-network (network path needs interfaces up)
|
||||||
|
- before: bee-audit (audit runs with latest binary)
|
||||||
|
- Calls bee-update.sh
|
||||||
|
|
||||||
|
Triggered after bee-audit completes, only if network is available.
|
||||||
|
|
||||||
|
`iso/overlay/usr/local/bin/bee-update.sh`:
|
||||||
|
|
||||||
|
```
|
||||||
|
1. Check network: ping git.mchus.pro -c 1 -W 3 || exit 0
|
||||||
|
2. Fetch latest release metadata:
|
||||||
|
GET https://git.mchus.pro/api/v1/repos/<org>/bee/releases/latest
|
||||||
|
3. Parse: extract tag_name, asset URL for bee-audit-linux-amd64
|
||||||
|
4. Compare tag_name with /usr/local/bin/audit --version output
|
||||||
|
5. If newer: download to /tmp/bee-audit-new, verify SHA256 checksum from release assets
|
||||||
|
6. Replace /usr/local/bin/audit (tmpfs — survives until reboot)
|
||||||
|
7. Log: updated from vX.Y to vX.Z
|
||||||
|
8. Re-run audit if update happened: /usr/local/bin/audit --output usb
|
||||||
|
```
|
||||||
|
|
||||||
|
`iso/overlay/etc/init.d/bee-update`:
|
||||||
|
- runlevel: default
|
||||||
|
- after: bee-audit, network
|
||||||
|
- Calls bee-update.sh
|
||||||
|
|
||||||
|
Release naming convention: binary asset named `bee-audit-linux-amd64` per release tag.
|
||||||
|
|
||||||
|
### 2.8 — Release workflow
|
||||||
|
|
||||||
|
`iso/builder/VERSIONS` — pinned versions:
|
||||||
|
```
|
||||||
|
AUDIT_VERSION=1.0
|
||||||
|
ALPINE_VERSION=3.21
|
||||||
|
KERNEL_VERSION=6.6
|
||||||
|
NVIDIA_DRIVER_VERSION=550.54.15
|
||||||
|
```
|
||||||
|
|
||||||
|
LiveCD release = full ISO rebuild. Binary-only patch = new Gitea release with binary asset.
|
||||||
|
On boot with network: ISO auto-patches its binary without full rebuild.
|
||||||
|
|
||||||
|
ISO version embedded in `/etc/bee-release`:
|
||||||
|
```
|
||||||
|
BEE_ISO_VERSION=1.0
|
||||||
|
BEE_AUDIT_VERSION=1.0
|
||||||
|
BUILD_DATE=2026-03-05
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Eating order
|
||||||
|
|
||||||
|
Builder environment is set up early (after 1.3) so every subsequent collector
|
||||||
|
is developed and tested directly on real hardware in the actual Alpine environment.
|
||||||
|
No "works on my Mac" drift.
|
||||||
|
|
||||||
|
```
|
||||||
|
1.0 keys repo setup → git.mchus.pro/mchus/keys, keygen.sh, developer pubkeys
|
||||||
|
1.1 scaffold + schema types → binary runs, outputs empty JSON
|
||||||
|
1.2 board collector → first real data
|
||||||
|
1.3 CPU collector → +CPUs
|
||||||
|
|
||||||
|
--- BUILDER + DEBUG ISO (unblock real-hardware testing) ---
|
||||||
|
|
||||||
|
2.1 builder VM setup → Alpine VM with build deps + Go toolchain
|
||||||
|
2.2 debug ISO profile → minimal Alpine ISO: audit binary + dropbear SSH + all packages
|
||||||
|
2.3 boot on real server → SSH in, verify packages present, run audit manually
|
||||||
|
|
||||||
|
--- CONTINUE COLLECTORS (tested on real hardware from here) ---
|
||||||
|
|
||||||
|
1.4 memory collector → +DIMMs
|
||||||
|
1.5 storage collector → +disks (SATA/SAS/NVMe)
|
||||||
|
1.6 PCIe collector → +all PCIe devices
|
||||||
|
1.7 PSU collector → +power supplies
|
||||||
|
1.8 NVIDIA GPU enrichment → +GPU serial/VBIOS
|
||||||
|
1.8b wear/age telemetry → +SMART hours, NVMe % used, SFP DOM, ECC
|
||||||
|
1.9 Mellanox NIC enrichment → +NIC firmware/serial
|
||||||
|
1.10 RAID enrichment → +physical disks behind RAID
|
||||||
|
1.11 output + USB write → production-ready output
|
||||||
|
|
||||||
|
--- PRODUCTION ISO ---
|
||||||
|
|
||||||
|
2.4 NVIDIA driver build → driver compiled into overlay
|
||||||
|
2.5 network bring-up on boot → DHCP on all interfaces
|
||||||
|
2.6 OpenRC boot service → audit runs on boot automatically
|
||||||
|
2.7 vendor utilities → storcli/sas2ircu/mstflint in image
|
||||||
|
2.8 auto-update → binary self-patches from Gitea
|
||||||
|
2.9 release workflow → versioning + release notes
|
||||||
|
```
|
||||||
2
bible
2
bible
Submodule bible updated: 34b457d654...456c1f022c
4
iso/builder/VERSIONS
Normal file
4
iso/builder/VERSIONS
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
ALPINE_VERSION=3.21
|
||||||
|
KERNEL_VERSION=6.6
|
||||||
|
NVIDIA_DRIVER_VERSION=550.54.15
|
||||||
|
GO_VERSION=1.23.6
|
||||||
107
iso/builder/build-debug.sh
Normal file
107
iso/builder/build-debug.sh
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# build-debug.sh — build bee debug ISO with SSH access
|
||||||
|
#
|
||||||
|
# Debug ISO purpose: test audit binary on real hardware.
|
||||||
|
# Includes dropbear SSH, all audit packages, audit binary.
|
||||||
|
# Does NOT include NVIDIA driver (added in production build).
|
||||||
|
#
|
||||||
|
# Run on Alpine builder VM as root after setup-builder.sh.
|
||||||
|
# Usage:
|
||||||
|
# sh iso/builder/build-debug.sh [--authorized-keys /path/to/authorized_keys]
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||||
|
BUILDER_DIR="${REPO_ROOT}/iso/builder"
|
||||||
|
OVERLAY_DIR="${REPO_ROOT}/iso/overlay-debug"
|
||||||
|
DIST_DIR="${REPO_ROOT}/dist"
|
||||||
|
AUTH_KEYS=""
|
||||||
|
|
||||||
|
# parse args
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--authorized-keys) AUTH_KEYS="$2"; shift 2 ;;
|
||||||
|
*) echo "unknown arg: $1"; exit 1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
. "${BUILDER_DIR}/VERSIONS"
|
||||||
|
export PATH="$PATH:/usr/local/go/bin"
|
||||||
|
|
||||||
|
echo "=== bee debug ISO build ==="
|
||||||
|
echo "Alpine: ${ALPINE_VERSION}, Go: ${GO_VERSION}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# --- compile audit binary (static, Linux amd64) ---
|
||||||
|
echo "=== building audit binary ==="
|
||||||
|
cd "${REPO_ROOT}/audit"
|
||||||
|
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 \
|
||||||
|
go build \
|
||||||
|
-ldflags "-s -w -X main.Version=debug-$(date +%Y%m%d)" \
|
||||||
|
-o "${DIST_DIR}/bee-audit-linux-amd64" \
|
||||||
|
./cmd/audit
|
||||||
|
echo "binary: ${DIST_DIR}/bee-audit-linux-amd64"
|
||||||
|
echo "size: $(du -sh "${DIST_DIR}/bee-audit-linux-amd64" | cut -f1)"
|
||||||
|
|
||||||
|
# --- 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"
|
||||||
|
|
||||||
|
if [ -n "$AUTH_KEYS" ]; then
|
||||||
|
cp "$AUTH_KEYS" "$AUTHORIZED_KEYS_FILE"
|
||||||
|
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
|
||||||
|
[ -f "$ssh_pub" ] || continue
|
||||||
|
cat "$ssh_pub" >> "$AUTHORIZED_KEYS_FILE"
|
||||||
|
echo "SSH: added $(basename "$ssh_pub" .key.pub)"
|
||||||
|
FOUND=$((FOUND + 1))
|
||||||
|
done
|
||||||
|
if [ "$FOUND" -gt 0 ]; then
|
||||||
|
chmod 600 "$AUTHORIZED_KEYS_FILE"
|
||||||
|
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 " root password will be set to: bee / eeb"
|
||||||
|
echo " (generate a key with: sh keys/scripts/keygen.sh <your-name>)"
|
||||||
|
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"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- copy audit binary into overlay ---
|
||||||
|
mkdir -p "${OVERLAY_DIR}/usr/local/bin"
|
||||||
|
cp "${DIST_DIR}/bee-audit-linux-amd64" "${OVERLAY_DIR}/usr/local/bin/audit"
|
||||||
|
chmod +x "${OVERLAY_DIR}/usr/local/bin/audit"
|
||||||
|
|
||||||
|
# --- build ISO using mkimage ---
|
||||||
|
mkdir -p "${DIST_DIR}"
|
||||||
|
echo ""
|
||||||
|
echo "=== building ISO ==="
|
||||||
|
|
||||||
|
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 /tmp/bee-iso-work \
|
||||||
|
--profile bee_debug
|
||||||
|
|
||||||
|
ISO="${DIST_DIR}/alpine-bee_debug-${ALPINE_VERSION}-x86_64.iso"
|
||||||
|
echo ""
|
||||||
|
echo "=== done ==="
|
||||||
|
echo "ISO: $ISO"
|
||||||
|
echo "Size: $(du -sh "$ISO" 2>/dev/null | cut -f1 || echo 'not found')"
|
||||||
|
echo ""
|
||||||
|
echo "Boot via BMC virtual media and SSH to the server IP on port 22 as root."
|
||||||
60
iso/builder/mkimg.bee_debug.sh
Normal file
60
iso/builder/mkimg.bee_debug.sh
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Alpine mkimage profile: bee_debug
|
||||||
|
# Minimal LiveCD with audit binary + SSH for development/testing.
|
||||||
|
# No NVIDIA driver. SSH root login enabled.
|
||||||
|
|
||||||
|
profile_bee_debug() {
|
||||||
|
title="Bee Hardware Audit (debug)"
|
||||||
|
desc="Hardware audit LiveCD with SSH access for testing"
|
||||||
|
image_ext="iso"
|
||||||
|
output_format="iso"
|
||||||
|
kernel_flavors="lts"
|
||||||
|
kernel_addons=""
|
||||||
|
syslinux_serial="0 115200"
|
||||||
|
apks="
|
||||||
|
alpine-base
|
||||||
|
linux-lts
|
||||||
|
linux-firmware-none
|
||||||
|
|
||||||
|
dmidecode
|
||||||
|
smartmontools
|
||||||
|
nvme-cli
|
||||||
|
pciutils
|
||||||
|
ipmitool
|
||||||
|
util-linux
|
||||||
|
lsblk
|
||||||
|
e2fsprogs
|
||||||
|
lshw
|
||||||
|
|
||||||
|
dropbear
|
||||||
|
udhcpc
|
||||||
|
openrc
|
||||||
|
qrencode
|
||||||
|
tzdata
|
||||||
|
ca-certificates
|
||||||
|
|
||||||
|
strace
|
||||||
|
procps
|
||||||
|
lsof
|
||||||
|
file
|
||||||
|
less
|
||||||
|
vim
|
||||||
|
"
|
||||||
|
|
||||||
|
# overlay is applied after package install
|
||||||
|
# contains: audit binary, dropbear init, authorized_keys
|
||||||
|
}
|
||||||
|
|
||||||
|
build_bee_debug() {
|
||||||
|
# copy overlay files into rootfs
|
||||||
|
local overlay="${SRCDIR}/../../overlay-debug"
|
||||||
|
if [ -d "$overlay" ]; then
|
||||||
|
cp -r "${overlay}/." "${ROOTFS}/"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# enable services
|
||||||
|
_bootscript default bee-sshsetup
|
||||||
|
_bootscript default dropbear
|
||||||
|
_bootscript default bee-network
|
||||||
|
_bootscript default bee-audit-debug
|
||||||
|
}
|
||||||
105
iso/builder/setup-builder.sh
Normal file
105
iso/builder/setup-builder.sh
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# setup-builder.sh — prepare Alpine 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.
|
||||||
|
#
|
||||||
|
# Usage (on Alpine VM):
|
||||||
|
# wget -O- https://git.mchus.pro/mchus/bee/raw/branch/main/iso/builder/setup-builder.sh | sh
|
||||||
|
# or: sh setup-builder.sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
. "$(dirname "$0")/VERSIONS" 2>/dev/null || true
|
||||||
|
GO_VERSION="${GO_VERSION:-1.23.6}"
|
||||||
|
|
||||||
|
echo "=== bee builder setup ==="
|
||||||
|
echo "Alpine: $(cat /etc/alpine-release)"
|
||||||
|
echo "Go target: ${GO_VERSION}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# --- system packages ---
|
||||||
|
apk update
|
||||||
|
apk add \
|
||||||
|
alpine-sdk \
|
||||||
|
abuild \
|
||||||
|
squashfs-tools \
|
||||||
|
xorriso \
|
||||||
|
mtools \
|
||||||
|
grub \
|
||||||
|
grub-efi \
|
||||||
|
grub-bios \
|
||||||
|
git \
|
||||||
|
wget \
|
||||||
|
curl \
|
||||||
|
tar \
|
||||||
|
xz
|
||||||
|
|
||||||
|
# --- 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
|
||||||
|
|
||||||
|
# --- Go toolchain ---
|
||||||
|
echo ""
|
||||||
|
echo "=== installing Go ${GO_VERSION} ==="
|
||||||
|
if [ -d /usr/local/go ] && /usr/local/go/bin/go version 2>/dev/null | grep -q "${GO_VERSION}"; then
|
||||||
|
echo "Go ${GO_VERSION} already installed"
|
||||||
|
else
|
||||||
|
ARCH=$(uname -m)
|
||||||
|
case "$ARCH" in
|
||||||
|
x86_64) GOARCH=amd64 ;;
|
||||||
|
aarch64) GOARCH=arm64 ;;
|
||||||
|
*) echo "unsupported arch: $ARCH"; exit 1 ;;
|
||||||
|
esac
|
||||||
|
wget -O /tmp/go.tar.gz \
|
||||||
|
"https://go.dev/dl/go${GO_VERSION}.linux-${GOARCH}.tar.gz"
|
||||||
|
rm -rf /usr/local/go
|
||||||
|
tar -C /usr/local -xzf /tmp/go.tar.gz
|
||||||
|
rm /tmp/go.tar.gz
|
||||||
|
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
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== builder setup complete ==="
|
||||||
|
echo "Next: sh iso/builder/build-debug.sh"
|
||||||
1
iso/overlay-debug/etc/dropbear/dropbear.conf
Normal file
1
iso/overlay-debug/etc/dropbear/dropbear.conf
Normal file
@@ -0,0 +1 @@
|
|||||||
|
DROPBEAR_OPTS="-p 22 -R -B"
|
||||||
21
iso/overlay-debug/etc/init.d/bee-audit-debug
Normal file
21
iso/overlay-debug/etc/init.d/bee-audit-debug
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#!/sbin/openrc-run
|
||||||
|
|
||||||
|
description="Bee: run hardware audit (debug mode — SSH stays up after)"
|
||||||
|
|
||||||
|
depend() {
|
||||||
|
need localmount
|
||||||
|
after bee-network
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
ebegin "Running hardware audit"
|
||||||
|
/usr/local/bin/audit --output stdout > /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
|
||||||
|
}
|
||||||
15
iso/overlay-debug/etc/init.d/bee-network
Normal file
15
iso/overlay-debug/etc/init.d/bee-network
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#!/sbin/openrc-run
|
||||||
|
|
||||||
|
description="Bee: bring up network interfaces via DHCP"
|
||||||
|
|
||||||
|
depend() {
|
||||||
|
need localmount
|
||||||
|
before bee-audit-debug
|
||||||
|
before dropbear
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
ebegin "Bringing up network interfaces"
|
||||||
|
/usr/local/bin/bee-network.sh >> /var/log/bee-network.log 2>&1
|
||||||
|
eend 0
|
||||||
|
}
|
||||||
28
iso/overlay-debug/etc/init.d/bee-sshsetup
Normal file
28
iso/overlay-debug/etc/init.d/bee-sshsetup
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#!/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
|
||||||
|
}
|
||||||
19
iso/overlay-debug/etc/motd
Normal file
19
iso/overlay-debug/etc/motd
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
|
||||||
|
██████╗ ███████╗███████╗ ██████╗ ███████╗██████╗ ██╗ ██╗ ██████╗
|
||||||
|
██╔══██╗██╔════╝██╔════╝ ██╔══██╗██╔════╝██╔══██╗██║ ██║██╔════╝
|
||||||
|
██████╔╝█████╗ █████╗ ██║ ██║█████╗ ██████╔╝██║ ██║██║ ███╗
|
||||||
|
██╔══██╗██╔══╝ ██╔══╝ ██║ ██║██╔══╝ ██╔══██╗██║ ██║██║ ██║
|
||||||
|
██████╔╝███████╗███████╗ ██████╔╝███████╗██████╔╝╚██████╔╝╚██████╔╝
|
||||||
|
╚═════╝ ╚══════╝╚══════╝ ╚═════╝ ╚══════╝╚═════╝ ╚═════╝ ╚═════╝
|
||||||
|
|
||||||
|
Hardware Audit LiveCD — DEBUG MODE
|
||||||
|
|
||||||
|
Audit result: /var/log/bee-audit.json
|
||||||
|
Audit log: /var/log/bee-audit.log
|
||||||
|
Network log: /var/log/bee-network.log
|
||||||
|
|
||||||
|
Re-run audit: /usr/local/bin/audit --output stdout | less
|
||||||
|
Check package: which dmidecode smartctl nvme ipmitool lspci
|
||||||
|
|
||||||
|
SSH access: key auth (developers) or bee/eeb (password fallback)
|
||||||
|
|
||||||
0
iso/overlay-debug/root/.ssh/.gitkeep
Normal file
0
iso/overlay-debug/root/.ssh/.gitkeep
Normal file
36
iso/overlay-debug/usr/local/bin/bee-network.sh
Normal file
36
iso/overlay-debug/usr/local/bin/bee-network.sh
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# bee-network.sh — bring up all physical network interfaces via DHCP
|
||||||
|
# Unattended: runs silently, logs results, never blocks.
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
LOG_PREFIX="bee-network"
|
||||||
|
|
||||||
|
log() { echo "[$LOG_PREFIX] $*"; }
|
||||||
|
|
||||||
|
# find physical interfaces: exclude lo and virtual (docker/virbr/veth/tun/tap)
|
||||||
|
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
|
||||||
|
log "bringing up $iface"
|
||||||
|
ip link set "$iface" up 2>/dev/null || { log "WARN: could not bring up $iface"; continue; }
|
||||||
|
|
||||||
|
# DHCP: 3 retries, 5s timeout per try, exit without blocking if no offer
|
||||||
|
if udhcpc -i "$iface" -t 3 -T 5 -n -q 2>/dev/null; then
|
||||||
|
IP=$(ip -4 addr show "$iface" | awk '/inet /{print $2}' | head -1)
|
||||||
|
log "OK: $iface got $IP"
|
||||||
|
else
|
||||||
|
log "WARN: $iface — no DHCP offer"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
log "done"
|
||||||
Reference in New Issue
Block a user