- Add aarch64-generic (QEMU/Proxmox ARM) - Add Raspberry Pi 4/400/CM4 (bcm27xx/bcm2711) - Add Rockchip ARM64 (NanoPi R4S/R5S/R6S) - Download arch-specific prebuilt packages - Add architecture-specific kernel modules - Create local build script with same logic - Handle different image formats per arch Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1042 lines
38 KiB
YAML
1042 lines
38 KiB
YAML
name: Build C3Box VM Appliance
|
|
|
|
# Builds ready-to-use C3Box VM images (VMDK, VDI, QCOW2) for VMware, VirtualBox, Proxmox
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
version:
|
|
description: 'Version tag (e.g. v1.0.0-beta)'
|
|
required: true
|
|
default: 'v1.0.0-beta'
|
|
type: string
|
|
openwrt_version:
|
|
description: 'OpenWrt version'
|
|
required: true
|
|
default: '24.10.5'
|
|
type: choice
|
|
options:
|
|
- '24.10.5'
|
|
- '24.10.4'
|
|
- '23.05.5'
|
|
disk_size:
|
|
description: 'Virtual disk size (GB)'
|
|
required: true
|
|
default: '8'
|
|
type: choice
|
|
options:
|
|
- '4'
|
|
- '8'
|
|
- '16'
|
|
- '32'
|
|
memory:
|
|
description: 'Recommended RAM (GB)'
|
|
required: true
|
|
default: '2'
|
|
type: choice
|
|
options:
|
|
- '1'
|
|
- '2'
|
|
- '4'
|
|
- '8'
|
|
|
|
push:
|
|
tags:
|
|
- 'v*.*.*'
|
|
- 'v*.*.*-*'
|
|
|
|
env:
|
|
OPENWRT_VERSION: ${{ github.event.inputs.openwrt_version || '24.10.5' }}
|
|
DISK_SIZE: ${{ github.event.inputs.disk_size || '8' }}
|
|
C3BOX_VERSION: ${{ github.event.inputs.version || github.ref_name }}
|
|
|
|
permissions:
|
|
contents: write
|
|
|
|
jobs:
|
|
build-vm:
|
|
runs-on: ubuntu-latest
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
# x86-64 targets
|
|
- name: x86-64-efi
|
|
target: x86/64
|
|
boot: efi
|
|
pkg_arch: x86-64
|
|
description: "x86/64 EFI (Modern UEFI systems)"
|
|
- name: x86-64-bios
|
|
target: x86/64
|
|
boot: bios
|
|
pkg_arch: x86-64
|
|
description: "x86/64 BIOS (Legacy systems)"
|
|
# ARM64 targets
|
|
- name: aarch64-generic
|
|
target: armsr/armv8
|
|
boot: efi
|
|
pkg_arch: aarch64-generic
|
|
description: "ARM64 Generic (QEMU/Proxmox ARM)"
|
|
- name: rpi4
|
|
target: bcm27xx/bcm2711
|
|
boot: native
|
|
pkg_arch: aarch64-cortex-a72
|
|
description: "Raspberry Pi 4/400/CM4"
|
|
- name: rockchip-armv8
|
|
target: rockchip/armv8
|
|
boot: native
|
|
pkg_arch: rockchip-armv8
|
|
description: "Rockchip ARM64 (NanoPi R4S/R5S/R6S)"
|
|
|
|
name: VM ${{ matrix.description }}
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Free disk space
|
|
run: |
|
|
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc
|
|
sudo docker image prune --all --force
|
|
df -h
|
|
|
|
- name: Install dependencies
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y build-essential libncurses5-dev zlib1g-dev \
|
|
gawk git gettext libssl-dev xsltproc rsync wget unzip python3 \
|
|
qemu-utils parted dosfstools e2fsprogs
|
|
|
|
- name: Download Image Builder
|
|
run: |
|
|
VERSION="${{ env.OPENWRT_VERSION }}"
|
|
TARGET="${{ matrix.target }}"
|
|
TARGET_SAFE=$(echo "$TARGET" | tr '/' '-')
|
|
|
|
IB_URL="https://downloads.openwrt.org/releases/${VERSION}/targets/${TARGET}/openwrt-imagebuilder-${VERSION}-${TARGET_SAFE}.Linux-x86_64.tar.zst"
|
|
|
|
echo "📥 Downloading Image Builder for $TARGET..."
|
|
echo " URL: $IB_URL"
|
|
|
|
wget -q "$IB_URL" -O imagebuilder.tar.zst 2>/dev/null || {
|
|
IB_URL="${IB_URL%.zst}.xz"
|
|
echo " Trying .xz: $IB_URL"
|
|
wget -q "$IB_URL" -O imagebuilder.tar.xz
|
|
tar -xf imagebuilder.tar.xz
|
|
mv openwrt-imagebuilder-* imagebuilder
|
|
}
|
|
|
|
if [[ -f imagebuilder.tar.zst ]]; then
|
|
tar --zstd -xf imagebuilder.tar.zst
|
|
mv openwrt-imagebuilder-* imagebuilder
|
|
fi
|
|
|
|
echo "✅ Image Builder ready for ${{ matrix.name }}"
|
|
|
|
- name: Download prebuilt SecuBox packages
|
|
run: |
|
|
echo "📥 Downloading prebuilt SecuBox packages for ${{ matrix.pkg_arch }}..."
|
|
|
|
# Download architecture-specific packages from release
|
|
C3BOX_VER="${{ env.C3BOX_VERSION }}"
|
|
PKG_ARCH="${{ matrix.pkg_arch }}"
|
|
|
|
# Try downloading from release artifacts
|
|
PKG_URL="https://github.com/gkerma/secubox-openwrt/releases/download/${C3BOX_VER}/secubox-${C3BOX_VER#v}-${PKG_ARCH}.tar.gz"
|
|
|
|
mkdir -p imagebuilder/packages/secubox
|
|
|
|
if wget -q "$PKG_URL" -O /tmp/secubox-packages.tar.gz 2>/dev/null; then
|
|
echo "✅ Downloaded release packages: $PKG_URL"
|
|
tar -xzf /tmp/secubox-packages.tar.gz -C imagebuilder/packages/secubox/ --strip-components=1 2>/dev/null || \
|
|
tar -xzf /tmp/secubox-packages.tar.gz -C imagebuilder/packages/secubox/
|
|
else
|
|
echo "⚠️ Release packages not found, will use feed installation"
|
|
fi
|
|
|
|
# Count packages
|
|
IPK_COUNT=$(find imagebuilder/packages/secubox/ -name "*.ipk" 2>/dev/null | wc -l)
|
|
echo "📦 Found $IPK_COUNT prebuilt packages"
|
|
|
|
# Create local repository if packages exist
|
|
if [[ $IPK_COUNT -gt 0 ]]; then
|
|
echo "🔧 Creating local package repository..."
|
|
cd imagebuilder/packages/secubox
|
|
# Generate Packages index (required for opkg)
|
|
if command -v gzip &>/dev/null; then
|
|
# Simple package index
|
|
for ipk in *.ipk; do
|
|
[ -f "$ipk" ] || continue
|
|
PKG_NAME=$(echo "$ipk" | sed 's/_.*//; s/^.*\///')
|
|
echo "Package: $PKG_NAME"
|
|
echo "Version: 1.0.0"
|
|
echo "Filename: $ipk"
|
|
echo ""
|
|
done > Packages
|
|
gzip -k Packages
|
|
fi
|
|
cd ../../..
|
|
echo "✅ Local repository created"
|
|
fi
|
|
|
|
- name: Create preseed configuration
|
|
run: |
|
|
mkdir -p imagebuilder/files/etc/uci-defaults
|
|
mkdir -p imagebuilder/files/etc/c3box
|
|
mkdir -p imagebuilder/files/etc/opkg
|
|
mkdir -p imagebuilder/files/etc/secubox
|
|
|
|
# SecuBox feed configuration for opkg
|
|
cat > imagebuilder/files/etc/opkg/customfeeds.conf << 'FEED_EOF'
|
|
# SecuBox Official Package Feed
|
|
src/gz secubox_packages https://repo.secubox.org/packages/aarch64_generic
|
|
src/gz secubox_luci https://repo.secubox.org/luci/aarch64_generic
|
|
FEED_EOF
|
|
|
|
# Preseed script for first boot - Network & System
|
|
cat > imagebuilder/files/etc/uci-defaults/10-c3box-network << 'PRESEED_EOF'
|
|
#!/bin/sh
|
|
# C3Box VM Network Configuration - Devel/Beta Test
|
|
|
|
# Set hostname
|
|
uci set system.@system[0].hostname='c3box'
|
|
uci set system.@system[0].timezone='UTC'
|
|
uci set system.@system[0].zonename='UTC'
|
|
|
|
# LAN: br-lan on eth0 - 192.168.200.x subnet for testing
|
|
uci set network.lan.ipaddr='192.168.200.1'
|
|
uci set network.lan.netmask='255.255.255.0'
|
|
uci set network.lan.proto='static'
|
|
|
|
# WAN: br-wan DHCP on eth1
|
|
uci set network.wan=interface
|
|
uci set network.wan.device='eth1'
|
|
uci set network.wan.proto='dhcp'
|
|
|
|
# Enable DHCP server on LAN
|
|
uci set dhcp.lan.start='100'
|
|
uci set dhcp.lan.limit='150'
|
|
uci set dhcp.lan.leasetime='12h'
|
|
|
|
# Firewall: secure defaults
|
|
uci set firewall.@zone[0].input='ACCEPT'
|
|
uci set firewall.@zone[0].output='ACCEPT'
|
|
uci set firewall.@zone[0].forward='REJECT'
|
|
uci set firewall.@zone[1].input='REJECT'
|
|
uci set firewall.@zone[1].output='ACCEPT'
|
|
uci set firewall.@zone[1].forward='REJECT'
|
|
uci set firewall.@zone[1].masq='1'
|
|
|
|
# Enable HTTPS for LuCI
|
|
uci set uhttpd.main.redirect_https='1'
|
|
|
|
uci commit
|
|
|
|
exit 0
|
|
PRESEED_EOF
|
|
chmod 755 imagebuilder/files/etc/uci-defaults/10-c3box-network
|
|
|
|
# SecuBox Core configuration preseed
|
|
cat > imagebuilder/files/etc/uci-defaults/20-secubox-config << 'PRESEED_EOF'
|
|
#!/bin/sh
|
|
# SecuBox Core Configuration - matching c3box.local
|
|
|
|
# Create secubox config
|
|
touch /etc/config/secubox
|
|
|
|
uci set secubox.main=core
|
|
uci set secubox.main.enabled='1'
|
|
uci set secubox.main.log_level='info'
|
|
uci set secubox.main.appstore_url='https://repo.secubox.org/catalog'
|
|
uci set secubox.main.appstore_fallback_local='1'
|
|
uci set secubox.main.health_check_interval='300'
|
|
uci set secubox.main.watchdog_interval='60'
|
|
uci set secubox.main.led_heartbeat='1'
|
|
uci set secubox.main.ai_enabled='0'
|
|
uci set secubox.main.ai_mode='copilot'
|
|
|
|
uci set secubox.enforcement=security
|
|
uci set secubox.enforcement.sandboxing='1'
|
|
uci set secubox.enforcement.module_signature_check='0'
|
|
uci set secubox.enforcement.allowed_repos='official'
|
|
uci set secubox.enforcement.auto_update_check='1'
|
|
|
|
uci set secubox.settings=diagnostics
|
|
uci set secubox.settings.collect_metrics='1'
|
|
uci set secubox.settings.retain_days='7'
|
|
uci set secubox.settings.alert_enabled='1'
|
|
uci set secubox.settings.health_threshold_cpu='80'
|
|
uci set secubox.settings.health_threshold_memory='90'
|
|
uci set secubox.settings.health_threshold_storage='85'
|
|
|
|
uci set secubox.remote=wan_access
|
|
uci set secubox.remote.enabled='1'
|
|
uci set secubox.remote.https_enabled='1'
|
|
uci set secubox.remote.https_port='443'
|
|
uci set secubox.remote.http_enabled='0'
|
|
uci set secubox.remote.ssh_enabled='1'
|
|
uci set secubox.remote.ssh_port='22'
|
|
|
|
uci set secubox.external=settings
|
|
uci set secubox.external.enabled='1'
|
|
uci set secubox.external.wildcard_enabled='1'
|
|
uci set secubox.external.default_landing='1'
|
|
|
|
uci set secubox.local=domain
|
|
uci set secubox.local.enabled='1'
|
|
uci set secubox.local.base_domain='sb.local'
|
|
uci set secubox.local.suffix='_local'
|
|
|
|
uci commit secubox
|
|
|
|
exit 0
|
|
PRESEED_EOF
|
|
chmod 755 imagebuilder/files/etc/uci-defaults/20-secubox-config
|
|
|
|
# SecuBox package installation script (runs after network is up)
|
|
cat > imagebuilder/files/etc/uci-defaults/90-secubox-packages << 'PRESEED_EOF'
|
|
#!/bin/sh
|
|
# SecuBox Package Installation
|
|
|
|
# Create installation script for first boot with network
|
|
mkdir -p /etc/secubox
|
|
|
|
cat > /etc/secubox/install-packages.sh << 'INSTALL_EOF'
|
|
#!/bin/sh
|
|
# SecuBox Full Package Suite Installation
|
|
# Run: /etc/secubox/install-packages.sh
|
|
|
|
LOG="/var/log/secubox-install.log"
|
|
exec > >(tee -a "$LOG") 2>&1
|
|
|
|
echo "=========================================="
|
|
echo "SecuBox Package Installation"
|
|
echo "Date: $(date)"
|
|
echo "=========================================="
|
|
|
|
# Wait for network
|
|
echo "Waiting for network..."
|
|
for i in $(seq 1 30); do
|
|
if ping -c1 repo.secubox.org >/dev/null 2>&1; then
|
|
echo "Network ready"
|
|
break
|
|
fi
|
|
sleep 2
|
|
done
|
|
|
|
# Update package lists
|
|
echo "Updating package lists..."
|
|
opkg update
|
|
|
|
# Core SecuBox packages
|
|
CORE_PACKAGES="
|
|
secubox-core
|
|
secubox-identity
|
|
secubox-master-link
|
|
secubox-p2p
|
|
secubox-app
|
|
secubox-app-bonus
|
|
luci-theme-secubox
|
|
"
|
|
|
|
# Security packages
|
|
SECURITY_PACKAGES="
|
|
crowdsec
|
|
crowdsec-firewall-bouncer
|
|
secubox-app-crowdsec-custom
|
|
secubox-app-mitmproxy
|
|
secubox-app-auth-logger
|
|
secubox-threat-analyst
|
|
secubox-dns-guard
|
|
luci-app-crowdsec-dashboard
|
|
luci-app-mitmproxy
|
|
luci-app-secubox-security-threats
|
|
luci-app-threat-analyst
|
|
luci-app-dnsguard
|
|
luci-app-auth-guardian
|
|
luci-app-exposure
|
|
luci-app-mac-guardian
|
|
luci-app-ipblocklist
|
|
"
|
|
|
|
# Network packages
|
|
NETWORK_PACKAGES="
|
|
haproxy
|
|
secubox-app-haproxy
|
|
secubox-vortex-dns
|
|
secubox-app-dns-provider
|
|
netifyd
|
|
secubox-app-ndpid
|
|
secubox-app-netifyd
|
|
luci-app-haproxy
|
|
luci-app-wireguard-dashboard
|
|
luci-app-vhost-manager
|
|
luci-app-network-modes
|
|
luci-app-network-tweaks
|
|
luci-app-dns-provider
|
|
luci-app-vortex-dns
|
|
luci-app-ndpid
|
|
luci-app-secubox-netifyd
|
|
luci-app-traffic-shaper
|
|
luci-app-bandwidth-manager
|
|
"
|
|
|
|
# Services packages
|
|
SERVICES_PACKAGES="
|
|
secubox-app-jabber
|
|
secubox-app-matrix
|
|
secubox-app-jitsi
|
|
secubox-app-jellyfin
|
|
secubox-app-gitea
|
|
secubox-app-nextcloud
|
|
secubox-app-streamlit
|
|
secubox-app-ollama
|
|
secubox-app-localai
|
|
secubox-app-hexojs
|
|
secubox-app-metablogizer
|
|
secubox-app-lyrion
|
|
secubox-app-magicmirror2
|
|
secubox-app-glances
|
|
luci-app-jabber
|
|
luci-app-matrix
|
|
luci-app-jitsi
|
|
luci-app-jellyfin
|
|
luci-app-gitea
|
|
luci-app-nextcloud
|
|
luci-app-streamlit
|
|
luci-app-ollama
|
|
luci-app-localai
|
|
luci-app-hexojs
|
|
luci-app-metablogizer
|
|
luci-app-lyrion
|
|
luci-app-magicmirror2
|
|
luci-app-glances
|
|
luci-app-picobrew
|
|
"
|
|
|
|
# Dashboard & Admin packages
|
|
ADMIN_PACKAGES="
|
|
luci-app-secubox
|
|
luci-app-secubox-admin
|
|
luci-app-secubox-portal
|
|
luci-app-secubox-p2p
|
|
luci-app-secubox-netdiag
|
|
luci-app-system-hub
|
|
luci-app-netdata-dashboard
|
|
luci-app-service-registry
|
|
luci-app-device-intel
|
|
luci-app-master-link
|
|
luci-app-media-flow
|
|
luci-app-cyberfeed
|
|
"
|
|
|
|
# AI & Advanced packages
|
|
AI_PACKAGES="
|
|
secubox-mcp-server
|
|
secubox-app-device-intel
|
|
luci-app-ai-gateway
|
|
luci-app-ai-insights
|
|
luci-app-localrecall
|
|
"
|
|
|
|
echo ""
|
|
echo "Installing Core packages..."
|
|
for pkg in $CORE_PACKAGES; do
|
|
opkg install "$pkg" 2>/dev/null || echo " Skip: $pkg"
|
|
done
|
|
|
|
echo ""
|
|
echo "Installing Security packages..."
|
|
for pkg in $SECURITY_PACKAGES; do
|
|
opkg install "$pkg" 2>/dev/null || echo " Skip: $pkg"
|
|
done
|
|
|
|
echo ""
|
|
echo "Installing Network packages..."
|
|
for pkg in $NETWORK_PACKAGES; do
|
|
opkg install "$pkg" 2>/dev/null || echo " Skip: $pkg"
|
|
done
|
|
|
|
echo ""
|
|
echo "Installing Services packages..."
|
|
for pkg in $SERVICES_PACKAGES; do
|
|
opkg install "$pkg" 2>/dev/null || echo " Skip: $pkg"
|
|
done
|
|
|
|
echo ""
|
|
echo "Installing Admin packages..."
|
|
for pkg in $ADMIN_PACKAGES; do
|
|
opkg install "$pkg" 2>/dev/null || echo " Skip: $pkg"
|
|
done
|
|
|
|
echo ""
|
|
echo "Installing AI packages..."
|
|
for pkg in $AI_PACKAGES; do
|
|
opkg install "$pkg" 2>/dev/null || echo " Skip: $pkg"
|
|
done
|
|
|
|
# Enable core services
|
|
echo ""
|
|
echo "Enabling services..."
|
|
for svc in secubox-core crowdsec haproxy rpcd uhttpd; do
|
|
[ -x /etc/init.d/$svc ] && /etc/init.d/$svc enable 2>/dev/null
|
|
done
|
|
|
|
# Generate identity if available
|
|
if [ -x /usr/sbin/identityctl ]; then
|
|
echo "Generating SecuBox identity..."
|
|
/usr/sbin/identityctl keygen 2>/dev/null || true
|
|
fi
|
|
|
|
# Mark installation complete
|
|
touch /etc/secubox/packages-installed
|
|
echo ""
|
|
echo "=========================================="
|
|
echo "SecuBox installation complete!"
|
|
echo "=========================================="
|
|
|
|
# Restart services
|
|
/etc/init.d/rpcd restart
|
|
/etc/init.d/uhttpd restart
|
|
|
|
INSTALL_EOF
|
|
chmod 755 /etc/secubox/install-packages.sh
|
|
|
|
# Create quick-install info
|
|
cat > /etc/secubox/README << 'README_EOF'
|
|
C3Box SecuBox VM Appliance
|
|
|
|
This is a minimal base installation. To install the full SecuBox suite:
|
|
|
|
/etc/secubox/install-packages.sh
|
|
|
|
This will install 100+ SecuBox modules from the official repository.
|
|
|
|
Network Configuration:
|
|
LAN: 192.168.200.1/24 (br-lan/eth0)
|
|
WAN: DHCP (br-wan/eth1)
|
|
|
|
Web UI: https://192.168.200.1
|
|
README_EOF
|
|
|
|
exit 0
|
|
PRESEED_EOF
|
|
chmod 755 imagebuilder/files/etc/uci-defaults/90-secubox-packages
|
|
|
|
# Filesystem resize script
|
|
cat > imagebuilder/files/etc/uci-defaults/99-c3box-resize << 'PRESEED_EOF'
|
|
#!/bin/sh
|
|
# Expand root filesystem on first boot
|
|
|
|
if [ ! -f /etc/c3box/resized ]; then
|
|
ROOT_DEV=$(mount | grep ' / ' | cut -d' ' -f1)
|
|
if [ -n "$ROOT_DEV" ]; then
|
|
DISK_DEV=$(echo "$ROOT_DEV" | sed 's/[0-9]*$//')
|
|
PART_NUM=$(echo "$ROOT_DEV" | grep -o '[0-9]*$')
|
|
|
|
if command -v parted >/dev/null 2>&1; then
|
|
parted -s "$DISK_DEV" resizepart "$PART_NUM" 100% 2>/dev/null || true
|
|
fi
|
|
|
|
if command -v resize2fs >/dev/null 2>&1; then
|
|
resize2fs "$ROOT_DEV" 2>/dev/null || true
|
|
fi
|
|
|
|
mkdir -p /etc/c3box
|
|
touch /etc/c3box/resized
|
|
fi
|
|
fi
|
|
|
|
touch /etc/c3box/configured
|
|
exit 0
|
|
PRESEED_EOF
|
|
chmod 755 imagebuilder/files/etc/uci-defaults/99-c3box-resize
|
|
|
|
# C3Box release info
|
|
cat > imagebuilder/files/etc/c3box/release << EOF
|
|
C3BOX_VERSION="${{ env.C3BOX_VERSION }}"
|
|
C3BOX_BUILD_DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
OPENWRT_VERSION="${{ env.OPENWRT_VERSION }}"
|
|
VM_TYPE="${{ matrix.boot }}"
|
|
DISK_SIZE="${{ env.DISK_SIZE }}GB"
|
|
SECUBOX_MODULES="101"
|
|
EOF
|
|
|
|
# MOTD banner
|
|
cat > imagebuilder/files/etc/banner << 'BANNER_EOF'
|
|
|
|
██████╗██████╗ ██████╗ ██████╗ ██╗ ██╗
|
|
██╔════╝╚════██╗██╔══██╗██╔═══██╗╚██╗██╔╝
|
|
██║ █████╔╝██████╔╝██║ ██║ ╚███╔╝
|
|
██║ ╚═══██╗██╔══██╗██║ ██║ ██╔██╗
|
|
╚██████╗██████╔╝██████╔╝╚██████╔╝██╔╝ ██╗
|
|
╚═════╝╚═════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝
|
|
|
|
C3Box - CyberMind Security Appliance
|
|
SecuBox 101+ Modules | Master Target Mesh
|
|
|
|
Web UI: https://192.168.200.1
|
|
Install: /etc/secubox/install-packages.sh
|
|
|
|
Documentation: https://github.com/gkerma/secubox-openwrt/wiki
|
|
|
|
BANNER_EOF
|
|
|
|
echo "✅ Preseed configuration created with SecuBox package installer"
|
|
|
|
- name: Build firmware image
|
|
run: |
|
|
cd imagebuilder
|
|
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo "🔨 Building C3Box VM Image (${{ matrix.boot }})"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
|
|
# Add local SecuBox repository to Image Builder
|
|
if [[ -d packages/secubox ]] && [[ $(find packages/secubox -name "*.ipk" | wc -l) -gt 0 ]]; then
|
|
echo "📦 Adding local SecuBox package repository..."
|
|
echo "src secubox file://$(pwd)/packages/secubox" >> repositories.conf
|
|
cat repositories.conf
|
|
fi
|
|
|
|
# Base OpenWrt packages
|
|
PACKAGES="luci luci-ssl luci-app-opkg luci-theme-openwrt-2020"
|
|
PACKAGES="$PACKAGES curl wget-ssl htop iftop tcpdump nano"
|
|
PACKAGES="$PACKAGES openssh-sftp-server"
|
|
PACKAGES="$PACKAGES block-mount kmod-fs-ext4 kmod-fs-vfat kmod-fs-btrfs"
|
|
PACKAGES="$PACKAGES parted e2fsprogs resize2fs"
|
|
PACKAGES="$PACKAGES git rsync screen tmux bash jq"
|
|
PACKAGES="$PACKAGES docker dockerd containerd"
|
|
PACKAGES="$PACKAGES wireguard-tools kmod-wireguard luci-proto-wireguard"
|
|
|
|
# Remove conflicting dnsmasq
|
|
PACKAGES="$PACKAGES -dnsmasq dnsmasq-full"
|
|
|
|
# Network and security packages
|
|
PACKAGES="$PACKAGES haproxy bind-server bind-tools"
|
|
PACKAGES="$PACKAGES kmod-nf-conntrack kmod-nf-nat kmod-ipt-nat"
|
|
|
|
# Architecture-specific packages
|
|
case "${{ matrix.target }}" in
|
|
x86/64)
|
|
if [[ "${{ matrix.boot }}" == "efi" ]]; then
|
|
PACKAGES="$PACKAGES grub2-efi"
|
|
fi
|
|
PACKAGES="$PACKAGES qemu-ga" # QEMU guest agent for Proxmox
|
|
;;
|
|
armsr/armv8)
|
|
# ARM64 generic - virtio support for QEMU/Proxmox
|
|
PACKAGES="$PACKAGES kmod-virtio-net kmod-virtio-blk"
|
|
;;
|
|
bcm27xx/bcm2711)
|
|
# Raspberry Pi 4 specific
|
|
PACKAGES="$PACKAGES kmod-usb-net-asix-ax88179 kmod-usb-net-rtl8152"
|
|
;;
|
|
rockchip/armv8)
|
|
# Rockchip ARM64
|
|
PACKAGES="$PACKAGES kmod-usb-net-rtl8152"
|
|
;;
|
|
esac
|
|
|
|
# SecuBox packages from prebuilt artifacts (if available)
|
|
IPK_COUNT=$(find packages/secubox -name "*.ipk" 2>/dev/null | wc -l)
|
|
if [[ $IPK_COUNT -gt 0 ]]; then
|
|
echo "📦 Including $IPK_COUNT prebuilt SecuBox packages"
|
|
|
|
# Core SecuBox packages
|
|
SECUBOX_PKGS="secubox-core secubox-identity secubox-master-link secubox-p2p"
|
|
SECUBOX_PKGS="$SECUBOX_PKGS secubox-app secubox-app-bonus luci-theme-secubox"
|
|
|
|
# Security
|
|
SECUBOX_PKGS="$SECUBOX_PKGS crowdsec crowdsec-firewall-bouncer"
|
|
SECUBOX_PKGS="$SECUBOX_PKGS secubox-app-crowdsec-custom secubox-app-mitmproxy"
|
|
SECUBOX_PKGS="$SECUBOX_PKGS secubox-threat-analyst secubox-dns-guard"
|
|
SECUBOX_PKGS="$SECUBOX_PKGS luci-app-crowdsec-dashboard luci-app-mitmproxy"
|
|
SECUBOX_PKGS="$SECUBOX_PKGS luci-app-secubox-security-threats luci-app-threat-analyst"
|
|
SECUBOX_PKGS="$SECUBOX_PKGS luci-app-auth-guardian luci-app-exposure"
|
|
|
|
# Network
|
|
SECUBOX_PKGS="$SECUBOX_PKGS secubox-app-haproxy secubox-vortex-dns secubox-app-dns-provider"
|
|
SECUBOX_PKGS="$SECUBOX_PKGS netifyd secubox-app-ndpid secubox-app-netifyd"
|
|
SECUBOX_PKGS="$SECUBOX_PKGS luci-app-haproxy luci-app-wireguard-dashboard"
|
|
SECUBOX_PKGS="$SECUBOX_PKGS luci-app-vhost-manager luci-app-network-modes"
|
|
SECUBOX_PKGS="$SECUBOX_PKGS luci-app-vortex-dns luci-app-ndpid luci-app-secubox-netifyd"
|
|
|
|
# Services
|
|
SECUBOX_PKGS="$SECUBOX_PKGS secubox-app-streamlit secubox-app-hexojs"
|
|
SECUBOX_PKGS="$SECUBOX_PKGS secubox-app-glances secubox-app-metablogizer"
|
|
SECUBOX_PKGS="$SECUBOX_PKGS luci-app-streamlit luci-app-hexojs"
|
|
SECUBOX_PKGS="$SECUBOX_PKGS luci-app-glances luci-app-metablogizer"
|
|
|
|
# Dashboard & Admin
|
|
SECUBOX_PKGS="$SECUBOX_PKGS luci-app-secubox luci-app-secubox-admin"
|
|
SECUBOX_PKGS="$SECUBOX_PKGS luci-app-secubox-portal luci-app-secubox-p2p"
|
|
SECUBOX_PKGS="$SECUBOX_PKGS luci-app-system-hub luci-app-netdata-dashboard"
|
|
SECUBOX_PKGS="$SECUBOX_PKGS luci-app-service-registry luci-app-device-intel"
|
|
SECUBOX_PKGS="$SECUBOX_PKGS luci-app-master-link luci-app-cyberfeed"
|
|
SECUBOX_PKGS="$SECUBOX_PKGS luci-app-metrics-dashboard luci-app-config-vault"
|
|
|
|
# Add SecuBox packages to build list
|
|
for pkg in $SECUBOX_PKGS; do
|
|
if find packages/secubox -name "${pkg}_*.ipk" | head -1 | grep -q .; then
|
|
PACKAGES="$PACKAGES $pkg"
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# Calculate root partition size (disk size minus 64MB for boot)
|
|
ROOT_SIZE=$(( ${{ env.DISK_SIZE }} * 1024 - 64 ))
|
|
|
|
echo "📦 Packages: $PACKAGES"
|
|
echo "💾 Root partition: ${ROOT_SIZE}MB"
|
|
echo ""
|
|
|
|
# Select profile based on target
|
|
case "${{ matrix.target }}" in
|
|
x86/64)
|
|
PROFILE="generic"
|
|
;;
|
|
armsr/armv8)
|
|
PROFILE="generic"
|
|
;;
|
|
bcm27xx/bcm2711)
|
|
PROFILE="rpi-4"
|
|
;;
|
|
rockchip/armv8)
|
|
# List available profiles and pick first one
|
|
PROFILE=$(make info 2>/dev/null | grep -oP 'Default profile:\s+\K\S+' || echo "nanopi-r4s")
|
|
;;
|
|
*)
|
|
PROFILE="generic"
|
|
;;
|
|
esac
|
|
|
|
echo "🎯 Profile: $PROFILE"
|
|
|
|
make image \
|
|
PROFILE="$PROFILE" \
|
|
PACKAGES="$PACKAGES" \
|
|
FILES="files" \
|
|
ROOTFS_PARTSIZE="$ROOT_SIZE" \
|
|
2>&1 | tee build.log
|
|
|
|
echo ""
|
|
echo "📦 Generated images:"
|
|
ls -lh bin/targets/x86/64/
|
|
|
|
- name: Convert to VM formats
|
|
run: |
|
|
mkdir -p artifacts
|
|
|
|
TARGET="${{ matrix.target }}"
|
|
TARGET_DIR="imagebuilder/bin/targets/${TARGET}"
|
|
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo "🔄 Converting to VM formats (${{ matrix.name }})"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
|
|
# Find the correct image based on boot type and architecture
|
|
case "${{ matrix.boot }}" in
|
|
efi)
|
|
IMG_PATTERN="*-combined-efi.img.gz"
|
|
;;
|
|
bios)
|
|
IMG_PATTERN="*-combined-ext4.img.gz"
|
|
;;
|
|
native)
|
|
# ARM boards use different image formats
|
|
IMG_PATTERN="*-ext4-factory.img.gz"
|
|
;;
|
|
esac
|
|
|
|
# Find and extract the image
|
|
IMG_FILE=$(find "$TARGET_DIR" -name "$IMG_PATTERN" 2>/dev/null | head -1)
|
|
|
|
if [[ -z "$IMG_FILE" ]]; then
|
|
# Fallback patterns for different architectures
|
|
IMG_FILE=$(find "$TARGET_DIR" -name "*combined*.img.gz" 2>/dev/null | head -1)
|
|
fi
|
|
|
|
if [[ -z "$IMG_FILE" ]]; then
|
|
# Try ext4-sysupgrade for ARM
|
|
IMG_FILE=$(find "$TARGET_DIR" -name "*-ext4-sysupgrade.img.gz" -o -name "*-squashfs-sysupgrade.img.gz" 2>/dev/null | head -1)
|
|
fi
|
|
|
|
if [[ -z "$IMG_FILE" ]]; then
|
|
echo "❌ No firmware image found!"
|
|
echo "Available files in $TARGET_DIR:"
|
|
ls -la "$TARGET_DIR/" 2>/dev/null || find imagebuilder/bin -type f -name "*.img*"
|
|
exit 1
|
|
fi
|
|
|
|
echo "📦 Source image: $IMG_FILE"
|
|
|
|
# Extract (ignore "trailing garbage" warning - normal for firmware images)
|
|
# gunzip returns exit code 2 for warnings, which we can safely ignore
|
|
gunzip -c "$IMG_FILE" > /tmp/openwrt.img 2>/dev/null || {
|
|
# Check if extraction actually produced output
|
|
if [[ ! -s /tmp/openwrt.img ]]; then
|
|
echo "❌ Failed to extract image"
|
|
exit 1
|
|
fi
|
|
echo " (gunzip warning ignored - normal for firmware images)"
|
|
}
|
|
IMG_SIZE=$(stat -c%s /tmp/openwrt.img)
|
|
echo " Size: $(numfmt --to=iec $IMG_SIZE)"
|
|
|
|
# Expand to target disk size
|
|
TARGET_BYTES=$(( ${{ env.DISK_SIZE }} * 1024 * 1024 * 1024 ))
|
|
if [[ $IMG_SIZE -lt $TARGET_BYTES ]]; then
|
|
echo "📏 Expanding to ${{ env.DISK_SIZE }}GB..."
|
|
truncate -s ${TARGET_BYTES} /tmp/openwrt.img
|
|
fi
|
|
|
|
# Base filename - use version input or tag name
|
|
VERSION="${{ env.C3BOX_VERSION }}"
|
|
BASENAME="c3box-vm-${VERSION}-${{ matrix.name }}"
|
|
|
|
# Convert to VMDK (VMware)
|
|
echo "🔄 Creating VMDK (VMware)..."
|
|
qemu-img convert -f raw -O vmdk /tmp/openwrt.img "artifacts/${BASENAME}.vmdk"
|
|
echo " ✅ ${BASENAME}.vmdk ($(du -h "artifacts/${BASENAME}.vmdk" | cut -f1))"
|
|
|
|
# Convert to VDI (VirtualBox)
|
|
echo "🔄 Creating VDI (VirtualBox)..."
|
|
qemu-img convert -f raw -O vdi /tmp/openwrt.img "artifacts/${BASENAME}.vdi"
|
|
echo " ✅ ${BASENAME}.vdi ($(du -h "artifacts/${BASENAME}.vdi" | cut -f1))"
|
|
|
|
# Convert to QCOW2 (Proxmox/KVM)
|
|
echo "🔄 Creating QCOW2 (Proxmox/KVM)..."
|
|
qemu-img convert -f raw -O qcow2 -c /tmp/openwrt.img "artifacts/${BASENAME}.qcow2"
|
|
echo " ✅ ${BASENAME}.qcow2 ($(du -h "artifacts/${BASENAME}.qcow2" | cut -f1))"
|
|
|
|
# Keep raw image compressed
|
|
echo "🔄 Compressing raw image..."
|
|
gzip -c /tmp/openwrt.img > "artifacts/${BASENAME}.img.gz"
|
|
echo " ✅ ${BASENAME}.img.gz ($(du -h "artifacts/${BASENAME}.img.gz" | cut -f1))"
|
|
|
|
rm /tmp/openwrt.img
|
|
|
|
echo ""
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo "✅ VM images created successfully"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
|
|
- name: Create documentation
|
|
run: |
|
|
VERSION="${{ env.C3BOX_VERSION }}"
|
|
BASENAME="c3box-vm-${VERSION}-${{ matrix.name }}"
|
|
|
|
cat > artifacts/README.md << EOF
|
|
# C3Box VM Appliance - ${{ matrix.description }}
|
|
|
|
**Full SecuBox Suite Pre-installed** - 101+ Security & Privacy Modules
|
|
|
|
Pre-configured OpenWrt ${{ env.OPENWRT_VERSION }} virtual machine - CyberMind Security Appliance with complete SecuBox package suite matching production c3box.local.
|
|
|
|
## VM Images
|
|
|
|
| Format | File | Platform |
|
|
|--------|------|----------|
|
|
| VMDK | \`${BASENAME}.vmdk\` | VMware Workstation/ESXi |
|
|
| VDI | \`${BASENAME}.vdi\` | VirtualBox |
|
|
| QCOW2 | \`${BASENAME}.qcow2\` | Proxmox/KVM/QEMU |
|
|
| Raw | \`${BASENAME}.img.gz\` | Any hypervisor |
|
|
|
|
## Quick Start
|
|
|
|
### VMware
|
|
1. Create new VM → Other Linux 64-bit
|
|
2. Use existing disk → Select \`.vmdk\` file
|
|
3. RAM: 2GB minimum (4GB recommended)
|
|
4. Network: 2 adapters (LAN: bridged, WAN: NAT)
|
|
|
|
### VirtualBox
|
|
1. Create new VM → Linux → Other Linux 64-bit
|
|
2. Use existing disk → Select \`.vdi\` file
|
|
3. RAM: 2GB minimum (4GB recommended)
|
|
4. Network: Adapter 1 (bridged), Adapter 2 (NAT)
|
|
|
|
### Proxmox
|
|
\`\`\`bash
|
|
# Upload QCOW2 to Proxmox
|
|
qm create 100 --name c3box --memory 2048 --cores 2 \\
|
|
--net0 virtio,bridge=vmbr0 --net1 virtio,bridge=vmbr1
|
|
qm importdisk 100 ${BASENAME}.qcow2 local-lvm
|
|
qm set 100 --scsi0 local-lvm:vm-100-disk-0
|
|
qm set 100 --boot order=scsi0
|
|
qm start 100
|
|
\`\`\`
|
|
|
|
## Default Configuration
|
|
|
|
| Setting | Value |
|
|
|---------|-------|
|
|
| LAN IP | 192.168.200.1 |
|
|
| WAN | DHCP (eth1) |
|
|
| Username | root |
|
|
| Password | (none - set on first login) |
|
|
| Web UI | https://192.168.200.1 |
|
|
| SSH | Enabled |
|
|
|
|
## Network Interfaces
|
|
|
|
- **eth0 (br-lan)**: 192.168.200.1/24, DHCP server (100-250)
|
|
- **eth1 (br-wan)**: DHCP client for internet access
|
|
|
|
## Included SecuBox Modules
|
|
|
|
### Security (16 modules)
|
|
- CrowdSec Dashboard - Threat intelligence
|
|
- Mitmproxy WAF - HTTPS inspection
|
|
- Auth Guardian - OAuth2/OIDC
|
|
- Threat Analyst - AI-powered analysis
|
|
- DNS Guard - DNS anomaly detection
|
|
- MAC Guardian - WiFi spoofing detection
|
|
|
|
### Network (15 modules)
|
|
- HAProxy - Load balancer with SSL/ACME
|
|
- WireGuard Dashboard - VPN management
|
|
- Vortex DNS - Mesh DNS resolution
|
|
- Network Modes - Sniffer/AP/Relay
|
|
- Traffic Shaper - QoS/CAKE
|
|
|
|
### Services (20+ modules)
|
|
- Matrix/Jabber/Jitsi - Communication
|
|
- Jellyfin/Lyrion - Media
|
|
- Gitea/Hexo - Content platforms
|
|
- LocalAI/Ollama - AI inference
|
|
|
|
### Mesh Features
|
|
- Master Link - Node onboarding
|
|
- P2P Hub - Peer discovery
|
|
- Service Registry - Catalog sync
|
|
- Vortex Firewall - Mesh security
|
|
|
|
## Post-Install (if minimal image)
|
|
|
|
If SecuBox packages weren't included in the image:
|
|
\`\`\`bash
|
|
/etc/secubox/install-packages.sh
|
|
\`\`\`
|
|
|
|
## Disk Resize
|
|
|
|
The root filesystem will automatically expand on first boot.
|
|
For manual expansion:
|
|
\`\`\`bash
|
|
parted /dev/sda resizepart 2 100%
|
|
resize2fs /dev/sda2
|
|
\`\`\`
|
|
|
|
## Build Information
|
|
|
|
- OpenWrt: ${{ env.OPENWRT_VERSION }}
|
|
- SecuBox: ${{ env.C3BOX_VERSION }}
|
|
- Boot: ${{ matrix.boot }}
|
|
- Disk: ${{ env.DISK_SIZE }}GB
|
|
- Modules: 101+
|
|
- Built: $(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
EOF
|
|
|
|
# Create checksums
|
|
cd artifacts
|
|
sha256sum *.vmdk *.vdi *.qcow2 *.img.gz > SHA256SUMS
|
|
|
|
echo "📋 Artifacts ready:"
|
|
ls -lh
|
|
|
|
- name: Upload artifacts
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: c3box-vm-${{ env.C3BOX_VERSION }}-${{ matrix.name }}
|
|
path: artifacts/
|
|
retention-days: 30
|
|
|
|
- name: Generate summary
|
|
run: |
|
|
echo "# 🖥️ VM Appliance: ${{ matrix.description }}" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Format | Size |" >> $GITHUB_STEP_SUMMARY
|
|
echo "|--------|------|" >> $GITHUB_STEP_SUMMARY
|
|
for f in artifacts/*.vmdk artifacts/*.vdi artifacts/*.qcow2 artifacts/*.img.gz; do
|
|
if [[ -f "$f" ]]; then
|
|
echo "| $(basename "$f") | $(du -h "$f" | cut -f1) |" >> $GITHUB_STEP_SUMMARY
|
|
fi
|
|
done
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "**Disk size:** ${{ env.DISK_SIZE }}GB" >> $GITHUB_STEP_SUMMARY
|
|
echo "**Boot type:** ${{ matrix.boot }}" >> $GITHUB_STEP_SUMMARY
|
|
|
|
# ============================================
|
|
# Create release
|
|
# ============================================
|
|
release:
|
|
needs: build-vm
|
|
runs-on: ubuntu-latest
|
|
if: startsWith(github.ref, 'refs/tags/v')
|
|
|
|
steps:
|
|
- name: Download all artifacts
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
path: vms
|
|
pattern: c3box-vm-*
|
|
|
|
- name: Organize release
|
|
run: |
|
|
mkdir -p release
|
|
|
|
for dir in vms/c3box-vm-*/; do
|
|
cp "$dir"/*.vmdk "$dir"/*.vdi "$dir"/*.qcow2 "$dir"/*.img.gz release/ 2>/dev/null || true
|
|
cp "$dir"/README.md release/VM-README.md 2>/dev/null || true
|
|
done
|
|
|
|
cd release
|
|
sha256sum *.vmdk *.vdi *.qcow2 *.img.gz > SHA256SUMS 2>/dev/null || true
|
|
|
|
cat > RELEASE_NOTES.md << 'EOF'
|
|
# C3Box VM Appliance
|
|
|
|
Ready-to-use virtual machine images - CyberMind Security Appliance.
|
|
|
|
## Downloads
|
|
|
|
Choose the format for your hypervisor:
|
|
- **VMDK** - VMware Workstation, ESXi, Fusion
|
|
- **VDI** - VirtualBox
|
|
- **QCOW2** - Proxmox, KVM, QEMU
|
|
|
|
## Quick Start
|
|
|
|
1. Download the appropriate image for your hypervisor
|
|
2. Import/create VM with the disk image
|
|
3. Boot and access https://192.168.200.1
|
|
4. Login as `root` (no password initially)
|
|
5. Set a password and start configuring
|
|
|
|
## System Requirements
|
|
|
|
- 1GB RAM minimum (2GB recommended)
|
|
- 1 vCPU minimum
|
|
- Network adapter (bridged or NAT)
|
|
|
|
EOF
|
|
|
|
- name: Create release
|
|
uses: softprops/action-gh-release@v2
|
|
with:
|
|
name: "C3Box VM Appliance ${{ github.ref_name }}"
|
|
tag_name: ${{ github.ref_name }}
|
|
body_path: release/RELEASE_NOTES.md
|
|
files: |
|
|
release/*.vmdk
|
|
release/*.vdi
|
|
release/*.qcow2
|
|
release/*.img.gz
|
|
release/SHA256SUMS
|
|
draft: false
|
|
prerelease: ${{ contains(github.ref_name, 'alpha') || contains(github.ref_name, 'beta') || contains(github.ref_name, 'rc') }}
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|