From 5cf69c0ce1149073e321932b4d9ee0fb829be4e4 Mon Sep 17 00:00:00 2001 From: CyberMind-FR Date: Mon, 27 Apr 2026 10:13:31 +0200 Subject: [PATCH] feat(multiboot): Add multi-architecture boot system with shared data Create a multi-boot storage system for Pi Zero Eye Remote that supports: - ARM64: ESPRESSObin/MOCHAbin via U-Boot boot.scr - AMD64: Any x86_64 UEFI system via GRUB - Shared data: Cross-architecture application data on partition 4 Partition layout (16GB+ recommended): - P1: EFI/FAT32 (512MB) - Boot files for both architectures - P2: ext4 (3GB) - ARM64 rootfs - P3: ext4 (3GB) - AMD64 rootfs - P4: ext4 (remaining) - Shared SecuBox configs, state, logs Features: - Automatic bind mounts for /etc/secubox, /var/lib/secubox, /srv/secubox - eMMC flasher image included in EFI partition - Debootstrap-based AMD64 rootfs builder - U-Boot script with USB/MMC detection Co-Authored-By: Claude Opus 4.5 --- image/multiboot/README.md | 100 +++++ image/multiboot/build-amd64-rootfs.sh | 295 ++++++++++++++ image/multiboot/build-multiboot.sh | 529 ++++++++++++++++++++++++++ 3 files changed, 924 insertions(+) create mode 100644 image/multiboot/README.md create mode 100755 image/multiboot/build-amd64-rootfs.sh create mode 100755 image/multiboot/build-multiboot.sh diff --git a/image/multiboot/README.md b/image/multiboot/README.md new file mode 100644 index 00000000..aa4b0784 --- /dev/null +++ b/image/multiboot/README.md @@ -0,0 +1,100 @@ +# SecuBox Multi-Boot Storage System + +## Overview + +Multi-architecture bootable storage for Pi Zero Eye Remote that supports: +- **ARM64**: ESPRESSObin/MOCHAbin via U-Boot +- **AMD64**: Any x86_64 UEFI system +- **Shared Data**: Cross-architecture application data + +## Partition Layout (16GB+ recommended) + +| Part | Type | Size | Mount | Purpose | +|------|------|------|-------|---------| +| 1 | EFI (FAT32) | 512MB | /boot/efi | UEFI + U-Boot boot files | +| 2 | ext4 | 3GB | / (ARM64) | SecuBox ARM64 live rootfs | +| 3 | ext4 | 3GB | / (AMD64) | SecuBox AMD64 live rootfs | +| 4 | ext4 | 8GB+ | /srv/data | Shared application data | + +## Boot Files (Partition 1) + +``` +/boot/efi/ +├── EFI/ +│ └── BOOT/ +│ ├── BOOTX64.EFI # GRUB for AMD64 +│ └── grub.cfg # GRUB config +├── Image # ARM64 kernel +├── initrd.img # ARM64 initramfs +├── dtbs/ # ARM64 device trees +├── boot.scr # U-Boot script (ARM64) +├── grub/ +│ └── grub.cfg # GRUB config (AMD64) +├── vmlinuz # AMD64 kernel +├── initrd-amd64.img # AMD64 initramfs +└── flash/ + └── secubox-emmc.img.gz # eMMC flasher image +``` + +## Shared Data Structure (Partition 4) + +``` +/srv/data/ +├── etc/ +│ └── secubox/ # Shared configs +│ ├── api.toml +│ ├── users.json +│ ├── tls/ +│ └── modules/ +├── var/ +│ └── lib/ +│ └── secubox/ # Application state +│ ├── crowdsec/ +│ ├── haproxy/ +│ ├── wireguard/ +│ └── dpi/ +├── srv/ +│ └── secubox/ # Service data +│ ├── mitmproxy/ +│ ├── nginx/ +│ └── certs/ +└── log/ + └── secubox/ # Shared logs +``` + +## Boot Flow + +### ARM64 (ESPRESSObin) +1. U-Boot loads `boot.scr` from partition 1 +2. Kernel + initrd from partition 1 +3. Rootfs from partition 2 +4. Mounts partition 4 as /srv/data +5. Bind-mounts shared paths + +### AMD64 (UEFI) +1. UEFI loads GRUB from EFI/BOOT/BOOTX64.EFI +2. GRUB loads vmlinuz + initrd from partition 1 +3. Rootfs from partition 3 +4. Mounts partition 4 as /srv/data +5. Bind-mounts shared paths + +### eMMC Flash +From either architecture: +```bash +secubox-flash-emmc # Interactive +# or +gunzip -c /boot/efi/flash/secubox-emmc.img.gz | dd of=/dev/mmcblk0 bs=4M status=progress +``` + +## Build + +```bash +# Build complete multi-boot image +./build-multiboot.sh --size 16G --output secubox-multiboot.img + +# Build AMD64 rootfs only +./build-amd64-rootfs.sh --output rootfs-amd64/ + +# Build ARM64 rootfs only +./build-arm64-rootfs.sh --output rootfs-arm64/ +``` diff --git a/image/multiboot/build-amd64-rootfs.sh b/image/multiboot/build-amd64-rootfs.sh new file mode 100755 index 00000000..6190dc05 --- /dev/null +++ b/image/multiboot/build-amd64-rootfs.sh @@ -0,0 +1,295 @@ +#!/usr/bin/env bash +# SecuBox AMD64 Live Rootfs Builder +# Creates Debian bookworm AMD64 rootfs with SecuBox packages +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" + +OUTPUT_DIR="" +MINIMAL=false +INCLUDE_DESKTOP=false +MIRROR="http://deb.debian.org/debian" +SECUBOX_REPO="https://apt.secubox.in" + +usage() { + cat <&2; exit 1; } + +parse_args() { + while [[ $# -gt 0 ]]; do + case $1 in + -o|--output) OUTPUT_DIR="$2"; shift 2 ;; + --minimal) MINIMAL=true; shift ;; + --desktop) INCLUDE_DESKTOP=true; shift ;; + --mirror) MIRROR="$2"; shift 2 ;; + -h|--help) usage ;; + *) err "Unknown option: $1" ;; + esac + done + + [[ -z "$OUTPUT_DIR" ]] && err "Output directory required (-o)" +} + +check_deps() { + local deps=(debootstrap chroot) + for cmd in "${deps[@]}"; do + command -v "$cmd" &>/dev/null || err "Missing: $cmd" + done + [[ $EUID -eq 0 ]] || err "Must run as root" +} + +bootstrap_rootfs() { + log "Bootstrapping Debian bookworm AMD64..." + + mkdir -p "$OUTPUT_DIR" + + debootstrap --arch=amd64 \ + --include=systemd,systemd-sysv,dbus,locales,apt-transport-https,ca-certificates,curl,gnupg \ + bookworm "$OUTPUT_DIR" "$MIRROR" +} + +configure_rootfs() { + log "Configuring rootfs..." + + # Set hostname + echo "secubox-amd64" > "$OUTPUT_DIR/etc/hostname" + + # Configure hosts + cat > "$OUTPUT_DIR/etc/hosts" < "$OUTPUT_DIR/etc/locale.gen" + chroot "$OUTPUT_DIR" locale-gen + + # Configure timezone + ln -sf /usr/share/zoneinfo/UTC "$OUTPUT_DIR/etc/localtime" + + # Configure apt sources + cat > "$OUTPUT_DIR/etc/apt/sources.list" < "$OUTPUT_DIR/etc/network/interfaces" < "$OUTPUT_DIR/etc/fstab" < +LABEL=secubox-amd64 / ext4 errors=remount-ro 0 1 +LABEL=SECUBOX-EFI /boot/efi vfat umask=0077 0 1 +EOF +} + +install_kernel() { + log "Installing Linux kernel..." + + chroot "$OUTPUT_DIR" apt-get update + chroot "$OUTPUT_DIR" apt-get install -y \ + linux-image-amd64 \ + linux-headers-amd64 \ + firmware-linux \ + firmware-linux-nonfree +} + +install_base_packages() { + log "Installing base packages..." + + chroot "$OUTPUT_DIR" apt-get install -y \ + openssh-server \ + sudo \ + vim \ + nano \ + htop \ + tmux \ + git \ + wget \ + net-tools \ + iproute2 \ + iputils-ping \ + dnsutils \ + tcpdump \ + iptables \ + nftables \ + nginx \ + python3 \ + python3-pip \ + python3-venv \ + python3-uvicorn \ + python3-fastapi \ + python3-jose \ + python3-httpx \ + python3-psutil \ + python3-jinja2 +} + +add_secubox_repo() { + if [[ "$MINIMAL" == "true" ]]; then + log "Skipping SecuBox repo (minimal mode)" + return + fi + + log "Adding SecuBox repository..." + + # Add SecuBox GPG key + mkdir -p "$OUTPUT_DIR/etc/apt/keyrings" + curl -fsSL "${SECUBOX_REPO}/gpg.key" | gpg --dearmor -o "$OUTPUT_DIR/etc/apt/keyrings/secubox.gpg" 2>/dev/null || true + + # Add SecuBox repo + cat > "$OUTPUT_DIR/etc/apt/sources.list.d/secubox.list" < "$OUTPUT_DIR/etc/sudoers.d/secubox" + chmod 440 "$OUTPUT_DIR/etc/sudoers.d/secubox" +} + +setup_boot() { + log "Setting up boot configuration..." + + # Install GRUB for UEFI + chroot "$OUTPUT_DIR" apt-get install -y grub-efi-amd64 || true + + # Create boot directory structure + mkdir -p "$OUTPUT_DIR/boot/efi" +} + +setup_secubox_branding() { + log "Adding SecuBox branding..." + + # MOTD + cat > "$OUTPUT_DIR/etc/motd" <<'MOTD' + + ██████ ███████ ██████ ██ ██ ██████ ██████ ██ ██ + ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ + ███████ █████ ██ ██ ██ ██████ ██ ██ ███ + ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ + ███████ ███████ ██████ ██████ ██████ ██████ ██ ██ + + SecuBox AMD64 Live System + https://secubox.in + +MOTD + + # Issue + cat > "$OUTPUT_DIR/etc/issue" <<'ISSUE' +SecuBox AMD64 \n \l + +ISSUE +} + +cleanup_rootfs() { + log "Cleaning up rootfs..." + + chroot "$OUTPUT_DIR" apt-get clean + rm -rf "$OUTPUT_DIR/var/lib/apt/lists/"* + rm -rf "$OUTPUT_DIR/tmp/"* + rm -f "$OUTPUT_DIR/var/log/"*.log +} + +main() { + parse_args "$@" + check_deps + + log "Building SecuBox AMD64 Rootfs" + log "Output: $OUTPUT_DIR" + log "Minimal: $MINIMAL" + + bootstrap_rootfs + configure_rootfs + install_kernel + install_base_packages + add_secubox_repo + install_secubox_packages + install_desktop + setup_users + setup_boot + setup_secubox_branding + cleanup_rootfs + + log "============================================" + log "AMD64 Rootfs Complete: $OUTPUT_DIR" + log "============================================" +} + +main "$@" diff --git a/image/multiboot/build-multiboot.sh b/image/multiboot/build-multiboot.sh new file mode 100755 index 00000000..1ff53e25 --- /dev/null +++ b/image/multiboot/build-multiboot.sh @@ -0,0 +1,529 @@ +#!/usr/bin/env bash +# SecuBox Multi-Boot Storage Builder +# Creates bootable image for ARM64 + AMD64 with shared data +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +OUTPUT_DIR="${REPO_ROOT}/output" +CACHE_DIR="${REPO_ROOT}/cache" + +# Defaults +IMAGE_SIZE="16G" +OUTPUT_FILE="" +ARM64_ROOTFS="" +AMD64_ROOTFS="" +EMMC_IMAGE="" +INCLUDE_EMMC=true +VERBOSE=false + +# Partition sizes (in MB) +EFI_SIZE=512 +ARM64_SIZE=3072 +AMD64_SIZE=3072 +# DATA_SIZE = remaining space + +usage() { + cat <&2; exit 1; } + +parse_args() { + while [[ $# -gt 0 ]]; do + case $1 in + -s|--size) IMAGE_SIZE="$2"; shift 2 ;; + -o|--output) OUTPUT_FILE="$2"; shift 2 ;; + --arm64-rootfs) ARM64_ROOTFS="$2"; shift 2 ;; + --amd64-rootfs) AMD64_ROOTFS="$2"; shift 2 ;; + --emmc-image) EMMC_IMAGE="$2"; shift 2 ;; + --no-emmc) INCLUDE_EMMC=false; shift ;; + -v|--verbose) VERBOSE=true; shift ;; + -h|--help) usage ;; + *) err "Unknown option: $1" ;; + esac + done + + [[ -z "$OUTPUT_FILE" ]] && OUTPUT_FILE="${OUTPUT_DIR}/secubox-multiboot-$(date +%Y%m%d).img" +} + +check_deps() { + local deps=(parted mkfs.vfat mkfs.ext4 debootstrap grub-mkimage losetup) + for cmd in "${deps[@]}"; do + command -v "$cmd" &>/dev/null || err "Missing: $cmd" + done + [[ $EUID -eq 0 ]] || err "Must run as root" +} + +create_image() { + log "Creating ${IMAGE_SIZE} image: ${OUTPUT_FILE}" + mkdir -p "$(dirname "$OUTPUT_FILE")" + truncate -s "$IMAGE_SIZE" "$OUTPUT_FILE" +} + +partition_image() { + log "Partitioning image..." + + parted -s "$OUTPUT_FILE" mklabel gpt + + local start=1 + local end=$((start + EFI_SIZE)) + + # Partition 1: EFI System Partition + parted -s "$OUTPUT_FILE" mkpart ESP fat32 ${start}MiB ${end}MiB + parted -s "$OUTPUT_FILE" set 1 esp on + parted -s "$OUTPUT_FILE" set 1 boot on + start=$end + end=$((start + ARM64_SIZE)) + + # Partition 2: ARM64 rootfs + parted -s "$OUTPUT_FILE" mkpart arm64-root ext4 ${start}MiB ${end}MiB + start=$end + end=$((start + AMD64_SIZE)) + + # Partition 3: AMD64 rootfs + parted -s "$OUTPUT_FILE" mkpart amd64-root ext4 ${start}MiB ${end}MiB + start=$end + + # Partition 4: Shared data (rest of disk) + parted -s "$OUTPUT_FILE" mkpart shared-data ext4 ${start}MiB 100% + + parted -s "$OUTPUT_FILE" print +} + +setup_loop() { + log "Setting up loop device..." + LOOP_DEV=$(losetup -f --show -P "$OUTPUT_FILE") + log "Loop device: $LOOP_DEV" + sleep 1 +} + +format_partitions() { + log "Formatting partitions..." + + mkfs.vfat -F 32 -n SECUBOX-EFI "${LOOP_DEV}p1" + mkfs.ext4 -L secubox-arm64 "${LOOP_DEV}p2" + mkfs.ext4 -L secubox-amd64 "${LOOP_DEV}p3" + mkfs.ext4 -L secubox-data "${LOOP_DEV}p4" +} + +mount_partitions() { + log "Mounting partitions..." + + MNT_EFI=$(mktemp -d) + MNT_ARM64=$(mktemp -d) + MNT_AMD64=$(mktemp -d) + MNT_DATA=$(mktemp -d) + + mount "${LOOP_DEV}p1" "$MNT_EFI" + mount "${LOOP_DEV}p2" "$MNT_ARM64" + mount "${LOOP_DEV}p3" "$MNT_AMD64" + mount "${LOOP_DEV}p4" "$MNT_DATA" +} + +install_efi_boot() { + log "Installing EFI boot files..." + + mkdir -p "$MNT_EFI/EFI/BOOT" + mkdir -p "$MNT_EFI/grub" + mkdir -p "$MNT_EFI/dtbs/marvell" + mkdir -p "$MNT_EFI/flash" + + # Install GRUB for AMD64 UEFI + if command -v grub-mkimage &>/dev/null; then + grub-mkimage -o "$MNT_EFI/EFI/BOOT/BOOTX64.EFI" \ + -O x86_64-efi \ + -p /grub \ + part_gpt part_msdos fat ext2 normal linux boot \ + configfile loopback chain efifwsetup efi_gop \ + efi_uga ls search search_label search_fs_uuid \ + search_fs_file gfxterm gfxterm_background gfxmenu test all_video + fi + + # GRUB config for AMD64 + cat > "$MNT_EFI/grub/grub.cfg" <<'GRUBCFG' +set timeout=5 +set default=0 + +menuentry "SecuBox AMD64 Live" { + linux /vmlinuz root=LABEL=secubox-amd64 ro quiet + initrd /initrd-amd64.img +} + +menuentry "SecuBox AMD64 Live (Recovery)" { + linux /vmlinuz root=LABEL=secubox-amd64 ro single + initrd /initrd-amd64.img +} + +menuentry "Flash SecuBox to eMMC (ARM64 only)" { + echo "This option is for ARM64 systems only" + echo "Boot the ARM64 system and run: secubox-flash-emmc" + sleep 5 +} +GRUBCFG + + # Copy GRUB config to EFI/BOOT as well + cp "$MNT_EFI/grub/grub.cfg" "$MNT_EFI/EFI/BOOT/" +} + +install_uboot_boot() { + log "Installing U-Boot boot files for ARM64..." + + # Create boot.scr for ARM64 (ESPRESSObin) + cat > /tmp/boot.cmd <<'BOOTCMD' +# SecuBox Multi-Boot U-Boot Script +# For ESPRESSObin/MOCHAbin ARM64 + +echo "============================================" +echo "SecuBox Multi-Boot — ARM64" +echo "============================================" + +# Detect boot device +if test "${devtype}" = "usb"; then + setenv bootpart "usb 0:1" + setenv rootpart "/dev/sda2" + echo "Booting from USB storage" +elif test "${devtype}" = "mmc"; then + setenv bootpart "mmc ${devnum}:1" + setenv rootpart "/dev/mmcblk${devnum}p2" + echo "Booting from MMC/SD" +else + # Default to USB + usb start + setenv bootpart "usb 0:1" + setenv rootpart "/dev/sda2" +fi + +# Load kernel +echo "Loading ARM64 kernel..." +load ${bootpart} ${kernel_addr_r} Image + +# Load device tree +echo "Loading device tree..." +if load ${bootpart} ${fdt_addr_r} dtbs/marvell/armada-3720-espressobin-v7-emmc.dtb; then + echo "Loaded ESPRESSObin v7 eMMC DTB" +else + load ${bootpart} ${fdt_addr_r} dtbs/marvell/armada-3720-espressobin-v7.dtb +fi + +# Load initramfs +echo "Loading initramfs..." +if load ${bootpart} ${ramdisk_addr_r} initrd.img; then + setenv initrd_size ${filesize} + setenv use_initrd 1 +else + setenv use_initrd 0 +fi + +# Boot arguments +# Data partition is partition 4 +setenv bootargs "root=${rootpart} rootfstype=ext4 rootwait rootdelay=5 console=ttyMV0,115200 net.ifnames=0 secubox.data=/dev/sda4" + +echo "Booting SecuBox ARM64..." +if test "${use_initrd}" = "1"; then + booti ${kernel_addr_r} ${ramdisk_addr_r}:${initrd_size} ${fdt_addr_r} +else + booti ${kernel_addr_r} - ${fdt_addr_r} +fi +BOOTCMD + + # Compile boot.scr if mkimage available + if command -v mkimage &>/dev/null; then + mkimage -A arm64 -T script -C none -d /tmp/boot.cmd "$MNT_EFI/boot.scr" + else + cp /tmp/boot.cmd "$MNT_EFI/boot.cmd" + fi +} + +install_arm64_rootfs() { + log "Installing ARM64 rootfs..." + + if [[ -n "$ARM64_ROOTFS" && -d "$ARM64_ROOTFS" ]]; then + log "Copying from $ARM64_ROOTFS" + rsync -aHAX "$ARM64_ROOTFS/" "$MNT_ARM64/" + else + # Check for existing built image + local arm64_img="${OUTPUT_DIR}/secubox-espressobin-v7-bookworm.img" + if [[ -f "$arm64_img" ]]; then + log "Extracting rootfs from $arm64_img" + local tmp_loop=$(losetup -f --show -P "$arm64_img") + local tmp_mnt=$(mktemp -d) + mount "${tmp_loop}p2" "$tmp_mnt" + rsync -aHAX "$tmp_mnt/" "$MNT_ARM64/" + umount "$tmp_mnt" + losetup -d "$tmp_loop" + rmdir "$tmp_mnt" + + # Also copy kernel/initrd/dtbs to EFI partition + mount "${tmp_loop}p1" "$tmp_mnt" 2>/dev/null || true + if [[ -f "$tmp_mnt/Image" ]]; then + cp "$tmp_mnt/Image" "$MNT_EFI/" + cp "$tmp_mnt/initrd.img" "$MNT_EFI/" 2>/dev/null || true + cp -r "$tmp_mnt/dtbs" "$MNT_EFI/" 2>/dev/null || true + fi + umount "$tmp_mnt" 2>/dev/null || true + else + log "WARNING: No ARM64 rootfs source found, partition will be empty" + fi + fi + + # Setup shared data mounts in fstab + setup_shared_mounts "$MNT_ARM64" +} + +install_amd64_rootfs() { + log "Installing AMD64 rootfs..." + + if [[ -n "$AMD64_ROOTFS" && -d "$AMD64_ROOTFS" ]]; then + log "Copying from $AMD64_ROOTFS" + rsync -aHAX "$AMD64_ROOTFS/" "$MNT_AMD64/" + else + # Build AMD64 rootfs with debootstrap + log "Building AMD64 rootfs with debootstrap..." + "${SCRIPT_DIR}/build-amd64-rootfs.sh" --output "$MNT_AMD64" --minimal + fi + + # Copy AMD64 kernel to EFI partition + if [[ -f "$MNT_AMD64/boot/vmlinuz-"* ]]; then + cp "$MNT_AMD64/boot/vmlinuz-"* "$MNT_EFI/vmlinuz" + cp "$MNT_AMD64/boot/initrd.img-"* "$MNT_EFI/initrd-amd64.img" 2>/dev/null || true + fi + + # Setup shared data mounts in fstab + setup_shared_mounts "$MNT_AMD64" +} + +setup_shared_mounts() { + local rootfs="$1" + + log "Setting up shared data mounts for $rootfs" + + # Create mount points + mkdir -p "$rootfs/srv/data" + + # Add to fstab + cat >> "$rootfs/etc/fstab" <<'FSTAB' + +# SecuBox Shared Data Partition +LABEL=secubox-data /srv/data ext4 defaults,noatime 0 2 +FSTAB + + # Create systemd mount for bind mounts + mkdir -p "$rootfs/etc/systemd/system" + cat > "$rootfs/etc/systemd/system/secubox-shared-data.service" <<'SERVICE' +[Unit] +Description=SecuBox Shared Data Bind Mounts +After=srv-data.mount +Requires=srv-data.mount + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=/usr/local/bin/secubox-mount-shared + +[Install] +WantedBy=multi-user.target +SERVICE + + # Create mount script + mkdir -p "$rootfs/usr/local/bin" + cat > "$rootfs/usr/local/bin/secubox-mount-shared" <<'SCRIPT' +#!/bin/bash +# Mount shared SecuBox data directories + +DATA_ROOT="/srv/data" + +# Create shared directories if they don't exist +mkdir -p "$DATA_ROOT"/{etc/secubox,var/lib/secubox,srv/secubox,log/secubox} + +# Bind mount shared configs +mkdir -p /etc/secubox +mount --bind "$DATA_ROOT/etc/secubox" /etc/secubox + +# Bind mount application state +mkdir -p /var/lib/secubox +mount --bind "$DATA_ROOT/var/lib/secubox" /var/lib/secubox + +# Bind mount service data +mkdir -p /srv/secubox +mount --bind "$DATA_ROOT/srv/secubox" /srv/secubox + +# Bind mount logs +mkdir -p /var/log/secubox +mount --bind "$DATA_ROOT/log/secubox" /var/log/secubox + +echo "SecuBox shared data mounted" +SCRIPT + chmod +x "$rootfs/usr/local/bin/secubox-mount-shared" + + # Enable the service + ln -sf ../secubox-shared-data.service "$rootfs/etc/systemd/system/multi-user.target.wants/" 2>/dev/null || true +} + +setup_shared_data() { + log "Setting up shared data partition structure..." + + mkdir -p "$MNT_DATA"/{etc/secubox,var/lib/secubox,srv/secubox,log/secubox} + mkdir -p "$MNT_DATA/etc/secubox"/{tls,modules} + mkdir -p "$MNT_DATA/var/lib/secubox"/{crowdsec,haproxy,wireguard,dpi,hub} + mkdir -p "$MNT_DATA/srv/secubox"/{mitmproxy,nginx,certs} + + # Create default configs + cat > "$MNT_DATA/etc/secubox/api.toml" <<'TOML' +# SecuBox API Configuration (Shared) +[api] +jwt_secret = "" # Generated on first boot + +[logging] +level = "INFO" +TOML + + # Create empty users.json + echo '{}' > "$MNT_DATA/etc/secubox/users.json" + + # Set permissions + chmod 750 "$MNT_DATA/etc/secubox" + chmod 750 "$MNT_DATA/var/lib/secubox" +} + +install_emmc_flasher() { + if [[ "$INCLUDE_EMMC" != "true" ]]; then + log "Skipping eMMC flasher image" + return + fi + + log "Installing eMMC flasher image..." + + mkdir -p "$MNT_EFI/flash" + + if [[ -n "$EMMC_IMAGE" && -f "$EMMC_IMAGE" ]]; then + if [[ "$EMMC_IMAGE" == *.gz ]]; then + cp "$EMMC_IMAGE" "$MNT_EFI/flash/secubox-emmc.img.gz" + else + gzip -c "$EMMC_IMAGE" > "$MNT_EFI/flash/secubox-emmc.img.gz" + fi + else + # Look for existing image + local emmc_img="${OUTPUT_DIR}/secubox-espressobin-v7-bookworm.img" + if [[ -f "$emmc_img" ]]; then + log "Compressing $emmc_img for eMMC flash..." + gzip -c "$emmc_img" > "$MNT_EFI/flash/secubox-emmc.img.gz" + else + log "WARNING: No eMMC image found, flash directory will be empty" + fi + fi + + # Create flash script + cat > "$MNT_EFI/flash/flash-emmc.sh" <<'FLASH' +#!/bin/bash +# SecuBox eMMC Flasher +set -e + +EMMC_DEV="/dev/mmcblk0" +IMG="/boot/efi/flash/secubox-emmc.img.gz" + +echo "============================================" +echo "SecuBox eMMC Flasher" +echo "============================================" + +if [[ ! -f "$IMG" ]]; then + echo "ERROR: Image not found: $IMG" + exit 1 +fi + +if [[ ! -b "$EMMC_DEV" ]]; then + echo "ERROR: eMMC device not found: $EMMC_DEV" + echo "Available block devices:" + lsblk + exit 1 +fi + +echo "WARNING: This will erase all data on $EMMC_DEV" +echo "Image: $IMG" +echo "" +read -p "Continue? [y/N] " confirm +if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then + echo "Aborted." + exit 0 +fi + +echo "Flashing to $EMMC_DEV..." +gunzip -c "$IMG" | dd of="$EMMC_DEV" bs=4M status=progress conv=fsync + +echo "Syncing..." +sync + +echo "============================================" +echo "Flash complete! You can now boot from eMMC." +echo "============================================" +FLASH + chmod +x "$MNT_EFI/flash/flash-emmc.sh" +} + +cleanup() { + log "Cleaning up..." + + umount "$MNT_EFI" 2>/dev/null || true + umount "$MNT_ARM64" 2>/dev/null || true + umount "$MNT_AMD64" 2>/dev/null || true + umount "$MNT_DATA" 2>/dev/null || true + + [[ -n "${LOOP_DEV:-}" ]] && losetup -d "$LOOP_DEV" 2>/dev/null || true + + rmdir "$MNT_EFI" "$MNT_ARM64" "$MNT_AMD64" "$MNT_DATA" 2>/dev/null || true +} + +main() { + parse_args "$@" + check_deps + + trap cleanup EXIT + + log "Building SecuBox Multi-Boot Image" + log "Size: $IMAGE_SIZE" + log "Output: $OUTPUT_FILE" + + create_image + partition_image + setup_loop + format_partitions + mount_partitions + + install_efi_boot + install_uboot_boot + install_arm64_rootfs + install_amd64_rootfs + setup_shared_data + install_emmc_flasher + + log "============================================" + log "Multi-Boot Image Complete: $OUTPUT_FILE" + log "============================================" + log "Partitions:" + log " 1: EFI/Boot (FAT32) - UEFI + U-Boot" + log " 2: ARM64 rootfs (ext4)" + log " 3: AMD64 rootfs (ext4)" + log " 4: Shared data (ext4)" +} + +main "$@"