#!/bin/sh # # SecuBox Seed Script # Bootstrap a fresh OpenWrt installation with SecuBox packages # # Usage: # wget -O- https://repo.secubox.in/seed.sh | sh # OR # curl -fsSL https://repo.secubox.in/seed.sh | sh # # Options (via environment): # SECUBOX_PROFILE=minimal|standard|full (default: standard) # SECUBOX_MIRROR=url (override repo URL) # SECUBOX_SKIP_UPDATE=1 (skip opkg update) # SECUBOX_DRY_RUN=1 (show what would be installed) # set -e # Colors (if terminal supports it) if [ -t 1 ]; then RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' else RED='' GREEN='' YELLOW='' BLUE='' NC='' fi log_info() { printf "${BLUE}[INFO]${NC} %s\n" "$1"; } log_ok() { printf "${GREEN}[OK]${NC} %s\n" "$1"; } log_warn() { printf "${YELLOW}[WARN]${NC} %s\n" "$1"; } log_error() { printf "${RED}[ERROR]${NC} %s\n" "$1"; } # Detect architecture detect_arch() { local arch if [ -f /etc/openwrt_release ]; then arch=$(grep "DISTRIB_ARCH" /etc/openwrt_release | cut -d"'" -f2) fi if [ -z "$arch" ]; then # Fallback: detect from uname case "$(uname -m)" in x86_64) arch="x86_64" ;; aarch64) arch="aarch64_cortex-a72" ;; armv7l) arch="arm_cortex-a7_neon-vfpv4" ;; *) arch="$(uname -m)" ;; esac fi echo "$arch" } # Repository URLs in order of preference REPO_URLS=" https://repo.secubox.in https://secubox.in/repo https://github.com/CyberMind-FR/secubox-openwrt/releases/download/packages " # Check connectivity and find working repo check_connectivity() { log_info "Checking network connectivity..." # First check general connectivity if ! ping -c 1 -W 3 "downloads.openwrt.org" >/dev/null 2>&1; then if ! wget -q -T 5 --spider "https://downloads.openwrt.org" 2>/dev/null; then log_error "No network connectivity. Please check your network configuration." return 1 fi fi log_ok "Network connectivity OK" return 0 } # Find a working SecuBox repository # Outputs only the URL to stdout, all log messages go to stderr find_working_repo() { local arch="$1" # If user specified a mirror, use it directly if [ -n "$SECUBOX_MIRROR" ]; then log_info "Using user-specified mirror: $SECUBOX_MIRROR" >&2 echo "$SECUBOX_MIRROR" return 0 fi # Check local feed first (for bonus package) if [ -d "/www/secubox-feed" ] && [ -f "/www/secubox-feed/Packages" ]; then log_info "Found local SecuBox feed at /www/secubox-feed" >&2 echo "file:///www/secubox-feed" return 0 fi # Try each remote URL with retry logic (GitHub Pages can be flaky) log_info "Searching for working SecuBox repository..." >&2 for base_url in $REPO_URLS; do local test_url="${base_url}/packages/${arch}/Packages.gz" log_info "Trying: $base_url" >&2 # Retry up to 3 times with 2 second delay local retry=0 while [ $retry -lt 3 ]; do rm -f /tmp/pkg_test.gz # Try wget - download and verify it's a valid gzip file if wget -q -T 15 -O /tmp/pkg_test.gz "$test_url" 2>/dev/null; then # Verify it's a valid gzip that can be decompressed and contains package data if [ -s /tmp/pkg_test.gz ] && \ gzip -t /tmp/pkg_test.gz 2>/dev/null && \ zcat /tmp/pkg_test.gz 2>/dev/null | grep -q "^Package:"; then log_ok "Found working repository: $base_url" >&2 rm -f /tmp/pkg_test.gz echo "$base_url" return 0 fi fi # Try curl as fallback if command -v curl >/dev/null 2>&1; then if curl -sf -m 15 -o /tmp/pkg_test.gz "$test_url" 2>/dev/null; then if [ -s /tmp/pkg_test.gz ] && \ gzip -t /tmp/pkg_test.gz 2>/dev/null && \ zcat /tmp/pkg_test.gz 2>/dev/null | grep -q "^Package:"; then log_ok "Found working repository: $base_url" >&2 rm -f /tmp/pkg_test.gz echo "$base_url" return 0 fi fi fi retry=$((retry + 1)) if [ $retry -lt 3 ]; then log_info "Retry $retry/3 for $base_url..." >&2 sleep 2 fi done log_warn "Failed to validate $base_url after 3 attempts" >&2 rm -f /tmp/pkg_test.gz done log_warn "No remote SecuBox repository available" >&2 return 1 } # Configure SecuBox repository configure_repo() { local arch="$1" local feeds_file="/etc/opkg/customfeeds.conf" log_info "Configuring SecuBox package repository..." # Backup existing customfeeds.conf if [ -f "$feeds_file" ]; then cp "$feeds_file" "${feeds_file}.bak" fi # Check if SecuBox feed already configured if grep -q "secubox_packages\|secubox_local\|secubox_luci" "$feeds_file" 2>/dev/null; then log_warn "SecuBox feeds already configured, updating..." sed -i '/secubox_packages/d; /secubox_luci/d; /secubox_local/d; /SecuBox Package/d; /SecuBox Local/d; /Added by secubox-seed/d' "$feeds_file" # Remove empty lines at end of file sed -i -e :a -e '/^\n*$/{$d;N;ba' -e '}' "$feeds_file" 2>/dev/null || true fi # Find a working repository (URL goes to stdout, logs go to stderr) local repo_url repo_url=$(find_working_repo "$arch") || true # Check if we got a valid URL if [ -z "$repo_url" ]; then log_warn "No SecuBox repository found. Packages will need to be installed manually." log_info "You can set SECUBOX_MIRROR to specify a custom repository URL." # Still configure the default URL for future use repo_url="https://repo.secubox.in" SECUBOX_REPO_AVAILABLE=0 else SECUBOX_REPO_AVAILABLE=1 fi # Handle local file:// URLs differently if echo "$repo_url" | grep -q "^file://"; then cat >> "$feeds_file" << EOF # SecuBox Local Package Repository # Added by secubox-seed.sh on $(date +%Y-%m-%d) src secubox_local ${repo_url} EOF else cat >> "$feeds_file" << EOF # SecuBox Package Repository # Added by secubox-seed.sh on $(date +%Y-%m-%d) src/gz secubox_packages ${repo_url}/packages/${arch} src/gz secubox_luci ${repo_url}/luci/${arch} EOF fi log_ok "Repository configured: ${repo_url}" } # Disable signature checking for SecuBox feeds (they are not signed) disable_signature_check() { local opkg_conf="/etc/opkg.conf" if grep -q "^option check_signature" "$opkg_conf" 2>/dev/null; then log_info "Disabling signature checking for unsigned SecuBox feeds..." sed -i '/^option check_signature/d' "$opkg_conf" log_ok "Signature checking disabled" fi } # Update package lists update_packages() { if [ "${SECUBOX_SKIP_UPDATE:-0}" = "1" ]; then log_warn "Skipping package list update (SECUBOX_SKIP_UPDATE=1)" return 0 fi # Disable signature checking (SecuBox feeds are not signed) disable_signature_check log_info "Updating package lists..." # Retry opkg update up to 3 times (GitHub Pages CDN can be flaky) local retry=0 local success=0 while [ $retry -lt 3 ] && [ $success -eq 0 ]; do if opkg update 2>&1; then success=1 else retry=$((retry + 1)) if [ $retry -lt 3 ]; then log_warn "opkg update failed, retry $retry/3..." sleep 3 fi fi done # Check if SecuBox feeds were downloaded if [ -f /var/opkg-lists/secubox_packages ] || [ -f /var/opkg-lists/secubox_luci ]; then log_ok "Package lists updated (SecuBox feeds available)" else log_warn "Some feeds failed to update, continuing anyway..." fi } # Install a package with fallback install_pkg() { local pkg="$1" local optional="${2:-0}" if [ "${SECUBOX_DRY_RUN:-0}" = "1" ]; then log_info "[DRY RUN] Would install: $pkg" return 0 fi # Check if already installed if opkg list-installed | grep -q "^${pkg} "; then log_ok "$pkg already installed" return 0 fi log_info "Installing $pkg..." # Retry up to 3 times (GitHub Pages CDN can be flaky) local retry=0 while [ $retry -lt 3 ]; do # Wait for opkg lock to be released while [ -f /var/lock/opkg.lock ]; do sleep 1 done if opkg install "$pkg" 2>&1; then log_ok "$pkg installed successfully" return 0 fi retry=$((retry + 1)) if [ $retry -lt 3 ]; then log_warn "$pkg download failed, retry $retry/3..." sleep 3 fi done # All retries failed if [ "$optional" = "1" ]; then log_warn "$pkg installation failed (optional, continuing)" return 0 else log_error "$pkg installation failed after 3 attempts" return 1 fi } # Install package group with error handling install_group() { local group_name="$1" shift local packages="$@" local failed="" log_info "Installing $group_name packages..." for pkg in $packages; do # Check if package is optional (prefixed with ?) local optional=0 if echo "$pkg" | grep -q "^?"; then optional=1 pkg="${pkg#?}" fi if ! install_pkg "$pkg" "$optional"; then failed="$failed $pkg" fi done if [ -n "$failed" ]; then log_warn "Some packages failed to install:$failed" return 1 fi log_ok "$group_name packages installed" return 0 } # Define package profiles get_profile_packages() { local profile="${1:-standard}" case "$profile" in minimal) # Minimal: Just theme + basic apps echo "THEME:luci-theme-secubox" echo "LUCI:luci-app-secubox" ;; standard) # Standard: Theme + Security + Basic LuCI apps echo "THEME:luci-theme-secubox" echo "SECURITY:?secubox-app-ipblocklist" echo "NETWORK:?secubox-app-haproxy" echo "LUCI:luci-app-secubox ?luci-app-haproxy ?luci-app-crowdsec-dashboard" ;; full) # Full: Everything available echo "THEME:luci-theme-secubox" echo "SECURITY:?secubox-app-ipblocklist ?secubox-app-mitmproxy" echo "NETWORK:?secubox-app-haproxy ?secubox-app-dns-master ?secubox-app-exposure" echo "MONITORING:?secubox-app-glances ?secubox-app-watchdog" echo "LUCI:luci-app-secubox ?luci-app-haproxy ?luci-app-exposure ?luci-app-dns-master" echo "LUCI:?luci-app-crowdsec-dashboard ?luci-app-glances ?luci-app-bandwidth-manager" ;; *) log_error "Unknown profile: $profile" log_info "Available profiles: minimal, standard, full" return 1 ;; esac } # Post-installation setup post_install() { log_info "Running post-installation setup..." # Initialize SecuBox if secubox-core is installed if [ -x /usr/sbin/secuboxctl ]; then log_info "Initializing SecuBox..." /usr/sbin/secuboxctl init 2>/dev/null || true fi # Enable and start key services local services="secubox haproxy" for svc in $services; do if [ -f "/etc/init.d/$svc" ]; then log_info "Enabling $svc service..." /etc/init.d/$svc enable 2>/dev/null || true fi done # Reload rpcd for new RPC methods if [ -f /etc/init.d/rpcd ]; then log_info "Reloading RPCD..." /etc/init.d/rpcd restart 2>/dev/null || true fi log_ok "Post-installation setup complete" } # Print summary print_summary() { local profile="$1" echo "" echo "==========================================" printf "${GREEN}SecuBox Installation Complete${NC}\n" echo "==========================================" echo "" echo "Profile: $profile" echo "Architecture: $(detect_arch)" echo "" echo "Access LuCI at: http://$(uci get network.lan.ipaddr 2>/dev/null || echo '192.168.1.1')" echo "" echo "Installed packages:" opkg list-installed | grep -E "^secubox-|^luci-.*secubox|^luci-theme-secubox" | while read line; do echo " - $line" done echo "" echo "For more information:" echo " https://docs.secubox.in" echo "" } # Main installation flow main() { local profile="${SECUBOX_PROFILE:-standard}" echo "" echo "==========================================" printf "${BLUE}SecuBox Seed Installer${NC}\n" echo "==========================================" echo "" log_info "Profile: $profile" # Pre-flight checks if [ "$(id -u)" != "0" ]; then log_error "This script must be run as root" exit 1 fi if ! command -v opkg >/dev/null 2>&1; then log_error "opkg not found. Is this an OpenWrt system?" exit 1 fi # Detect architecture local arch arch=$(detect_arch) log_info "Detected architecture: $arch" # Check connectivity if ! check_connectivity; then exit 1 fi # Configure repository configure_repo "$arch" # Update package lists update_packages # Check if repo is available before trying to install if [ "${SECUBOX_REPO_AVAILABLE:-1}" = "0" ]; then log_warn "SecuBox repository not available. Skipping package installation." log_info "Repository has been configured for future use." log_info "When packages are available, run: opkg update && opkg install secubox-core" if [ "${SECUBOX_DRY_RUN:-0}" != "1" ]; then echo "" echo "==========================================" printf "${YELLOW}SecuBox Setup Incomplete${NC}\n" echo "==========================================" echo "" echo "Repository configured but packages not available." echo "To complete installation later:" echo " opkg update" echo " opkg install secubox-core secubox-base luci-theme-secubox" echo "" fi exit 0 fi # Get packages for selected profile local profile_data profile_data=$(get_profile_packages "$profile") if [ -z "$profile_data" ]; then exit 1 fi # Install each package group local install_failed=0 echo "$profile_data" | while IFS=: read group packages; do if ! install_group "$group" $packages; then install_failed=1 fi done # Post-installation if [ "${SECUBOX_DRY_RUN:-0}" != "1" ]; then post_install print_summary "$profile" else log_info "[DRY RUN] Skipping post-installation" fi if [ "$install_failed" = "1" ]; then log_warn "Installation completed with some errors" exit 1 fi log_ok "SecuBox installation completed successfully!" } # Handle command-line arguments while [ $# -gt 0 ]; do case "$1" in --profile=*) SECUBOX_PROFILE="${1#*=}" ;; --mirror=*) SECUBOX_MIRROR="${1#*=}" ;; --dry-run) SECUBOX_DRY_RUN=1 ;; --skip-update) SECUBOX_SKIP_UPDATE=1 ;; --help|-h) echo "SecuBox Seed Installer" echo "" echo "Usage: $0 [OPTIONS]" echo "" echo "Options:" echo " --profile=PROFILE Installation profile (minimal|standard|full)" echo " --mirror=URL Override repository URL" echo " --dry-run Show what would be installed" echo " --skip-update Skip opkg update" echo " --help Show this help" echo "" echo "Environment variables:" echo " SECUBOX_PROFILE Same as --profile" echo " SECUBOX_MIRROR Same as --mirror" echo " SECUBOX_DRY_RUN=1 Same as --dry-run" echo " SECUBOX_SKIP_UPDATE=1 Same as --skip-update" echo "" exit 0 ;; *) log_error "Unknown option: $1" exit 1 ;; esac shift done main