From c81788b9c3e5ec567bdccc07d24b5a9d61eb6007 Mon Sep 17 00:00:00 2001 From: CyberMind-FR Date: Tue, 23 Dec 2025 01:30:26 +0100 Subject: [PATCH] fix: auto-repair all SecuBox modules --- .secubox-reports/analyze_20251223_012953.log | 4 + .secubox-reports/analyze_20251223_013001.log | 4 + acl/luci-app-vhost-manager.json | 35 + fix-makefiles.sh | 0 generate-rpcd-files.sh | 269 +++ install-rpcd-fix.sh | 129 ++ rpcd/vhost-manager | 334 ++++ secubox-analyzer.sh | 1543 +++++++++++++++++ secubox-debug.sh | 421 +++++ secubox-repair.sh | 1109 ++++++++++++ secubox-tools/acl/luci-app-vhost-manager.json | 35 + secubox-tools/cleanup-packages.sh | 147 ++ secubox-tools/fix-makefiles.sh | 97 ++ secubox-tools/generate-rpcd-files.sh | 269 +++ secubox-tools/install-rpcd-fix.sh | 129 ++ .../makefiles/luci-app-auth-guardian/Makefile | 16 + .../luci-app-bandwidth-manager/Makefile | 16 + .../makefiles/luci-app-media-flow/Makefile | 16 + .../makefiles/luci-app-secubox/Makefile | 16 + .../makefiles/luci-app-vhost-manager/Makefile | 16 + secubox-tools/rpcd/vhost-manager | 334 ++++ secubox-tools/secubox-debug.sh | 421 +++++ secubox-tools/secubox-repair.sh | 1109 ++++++++++++ secubox-tools/templates/Makefile.template | 75 + 24 files changed, 6544 insertions(+) create mode 100644 .secubox-reports/analyze_20251223_012953.log create mode 100644 .secubox-reports/analyze_20251223_013001.log create mode 100644 acl/luci-app-vhost-manager.json mode change 100644 => 100755 fix-makefiles.sh create mode 100755 generate-rpcd-files.sh create mode 100755 install-rpcd-fix.sh create mode 100644 rpcd/vhost-manager create mode 100755 secubox-analyzer.sh create mode 100755 secubox-debug.sh create mode 100755 secubox-repair.sh create mode 100644 secubox-tools/acl/luci-app-vhost-manager.json create mode 100755 secubox-tools/cleanup-packages.sh create mode 100755 secubox-tools/fix-makefiles.sh create mode 100755 secubox-tools/generate-rpcd-files.sh create mode 100755 secubox-tools/install-rpcd-fix.sh create mode 100644 secubox-tools/makefiles/luci-app-auth-guardian/Makefile create mode 100644 secubox-tools/makefiles/luci-app-bandwidth-manager/Makefile create mode 100644 secubox-tools/makefiles/luci-app-media-flow/Makefile create mode 100644 secubox-tools/makefiles/luci-app-secubox/Makefile create mode 100644 secubox-tools/makefiles/luci-app-vhost-manager/Makefile create mode 100644 secubox-tools/rpcd/vhost-manager create mode 100755 secubox-tools/secubox-debug.sh create mode 100755 secubox-tools/secubox-repair.sh create mode 100644 secubox-tools/templates/Makefile.template diff --git a/.secubox-reports/analyze_20251223_012953.log b/.secubox-reports/analyze_20251223_012953.log new file mode 100644 index 0000000..e149886 --- /dev/null +++ b/.secubox-reports/analyze_20251223_012953.log @@ -0,0 +1,4 @@ + Working directory: /home/reepost/CyberMindStudio/_files/secubox + Fix mode: OFF + Test RPC: OFF + Report: OFF diff --git a/.secubox-reports/analyze_20251223_013001.log b/.secubox-reports/analyze_20251223_013001.log new file mode 100644 index 0000000..833949f --- /dev/null +++ b/.secubox-reports/analyze_20251223_013001.log @@ -0,0 +1,4 @@ + Working directory: /home/reepost/CyberMindStudio/_files/secubox + Fix mode: ON + Test RPC: OFF + Report: OFF diff --git a/acl/luci-app-vhost-manager.json b/acl/luci-app-vhost-manager.json new file mode 100644 index 0000000..c5fdce2 --- /dev/null +++ b/acl/luci-app-vhost-manager.json @@ -0,0 +1,35 @@ +{ + "luci-app-vhost-manager": { + "description": "Grant access to VHost Manager", + "read": { + "ubus": { + "luci.vhost-manager": [ + "status", + "get_vhosts", + "get_vhost", + "get_certificates", + "test_config" + ] + }, + "uci": ["vhost_manager", "nginx"], + "file": { + "/etc/nginx/conf.d/*": ["read"], + "/etc/acme/*": ["read"] + } + }, + "write": { + "ubus": { + "luci.vhost-manager": [ + "add_vhost", + "delete_vhost", + "request_certificate", + "reload_nginx" + ] + }, + "uci": ["vhost_manager", "nginx"], + "file": { + "/etc/nginx/conf.d/*": ["write"] + } + } + } +} diff --git a/fix-makefiles.sh b/fix-makefiles.sh old mode 100644 new mode 100755 diff --git a/generate-rpcd-files.sh b/generate-rpcd-files.sh new file mode 100755 index 0000000..b41c962 --- /dev/null +++ b/generate-rpcd-files.sh @@ -0,0 +1,269 @@ +#!/bin/sh +# generate-rpcd-files.sh +# Generate missing RPCD scripts and ACL files for SecuBox modules +# +# Usage: ./generate-rpcd-files.sh +# Example: ./generate-rpcd-files.sh vhost-manager + +MODULE="$1" + +if [ -z "$MODULE" ]; then + echo "Usage: $0 " + echo "Example: $0 vhost-manager" + exit 1 +fi + +# Convert module name for different uses +# vhost-manager -> vhost_manager (for shell variables) +# vhost-manager -> vhost-manager (for ubus) +MODULE_UNDERSCORE=$(echo "$MODULE" | tr '-' '_') +UBUS_NAME="luci.$MODULE" +PKG_NAME="luci-app-$MODULE" + +echo "╔══════════════════════════════════════════════════════════════╗" +echo "║ Generating RPCD files for: $MODULE" +echo "╚══════════════════════════════════════════════════════════════╝" +echo "" + +# ============================================ +# Create RPCD script +# ============================================ +RPCD_SCRIPT="/usr/libexec/rpcd/$MODULE" + +echo "→ Creating RPCD script: $RPCD_SCRIPT" + +cat > "$RPCD_SCRIPT" << 'RPCD_EOF' +#!/bin/sh +# RPCD backend for MODULE_PLACEHOLDER +# Provides ubus interface: luci.MODULE_PLACEHOLDER + +. /lib/functions.sh +. /usr/share/libubox/jshn.sh + +# Initialize JSON +json_init + +case "$1" in + list) + # List available methods + json_add_object "status" + json_close_object + json_add_object "get_config" + json_close_object + json_add_object "set_config" + json_add_string "config" "object" + json_close_object + json_add_object "get_stats" + json_close_object + json_dump + ;; + + call) + case "$2" in + status) + # Return module status + json_add_boolean "enabled" 1 + json_add_string "status" "running" + json_add_string "version" "2.0.0" + json_add_string "module" "MODULE_PLACEHOLDER" + + # Check if service is running (customize per module) + # Example: check nginx for vhost-manager + # if pgrep -x nginx > /dev/null 2>&1; then + # json_add_boolean "service_running" 1 + # else + # json_add_boolean "service_running" 0 + # fi + + json_add_boolean "service_running" 1 + json_dump + ;; + + get_config) + # Return current configuration + json_add_object "config" + + # Read from UCI if available + if [ -f "/etc/config/MODULE_UNDERSCORE_PLACEHOLDER" ]; then + config_load "MODULE_UNDERSCORE_PLACEHOLDER" + # Add config values here + json_add_boolean "enabled" 1 + else + json_add_boolean "enabled" 0 + fi + + json_close_object + json_dump + ;; + + set_config) + # Set configuration + read -r input + + # Parse input JSON + json_load "$input" + json_get_var config config + + # Apply configuration via UCI + # uci set MODULE_UNDERSCORE_PLACEHOLDER.global.enabled="$enabled" + # uci commit MODULE_UNDERSCORE_PLACEHOLDER + + json_init + json_add_boolean "success" 1 + json_add_string "message" "Configuration updated" + json_dump + ;; + + get_stats) + # Return statistics + json_add_object "stats" + json_add_int "uptime" "$(cat /proc/uptime | cut -d. -f1)" + json_add_string "timestamp" "$(date -Iseconds)" + json_close_object + json_dump + ;; + + *) + # Unknown method + json_add_int "error" -32601 + json_add_string "message" "Method not found" + json_dump + ;; + esac + ;; +esac +RPCD_EOF + +# Replace placeholders +sed -i "s/MODULE_PLACEHOLDER/$MODULE/g" "$RPCD_SCRIPT" +sed -i "s/MODULE_UNDERSCORE_PLACEHOLDER/$MODULE_UNDERSCORE/g" "$RPCD_SCRIPT" + +chmod +x "$RPCD_SCRIPT" +echo " ✓ Created and made executable" + +# ============================================ +# Create ACL file +# ============================================ +ACL_FILE="/usr/share/rpcd/acl.d/${PKG_NAME}.json" + +echo "→ Creating ACL file: $ACL_FILE" + +cat > "$ACL_FILE" << ACL_EOF +{ + "luci-app-$MODULE": { + "description": "Grant access to LuCI app $MODULE", + "read": { + "ubus": { + "$UBUS_NAME": ["status", "get_config", "get_stats"] + }, + "uci": ["$MODULE_UNDERSCORE"] + }, + "write": { + "ubus": { + "$UBUS_NAME": ["set_config"] + }, + "uci": ["$MODULE_UNDERSCORE"] + } + } +} +ACL_EOF + +echo " ✓ Created ACL file" + +# ============================================ +# Create Menu file (if not exists) +# ============================================ +MENU_FILE="/usr/share/luci/menu.d/${PKG_NAME}.json" + +if [ ! -f "$MENU_FILE" ]; then + echo "→ Creating Menu file: $MENU_FILE" + + # Convert module name to title + TITLE=$(echo "$MODULE" | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) tolower(substr($i,2))}1') + + cat > "$MENU_FILE" << MENU_EOF +{ + "admin/services/$MODULE_UNDERSCORE": { + "title": "$TITLE", + "order": 50, + "action": { + "type": "view", + "path": "$MODULE/main" + }, + "depends": { + "acl": ["luci-app-$MODULE"], + "uci": { + "$MODULE_UNDERSCORE": true + } + } + } +} +MENU_EOF + + echo " ✓ Created menu file" +else + echo "→ Menu file already exists: $MENU_FILE" +fi + +# ============================================ +# Create UCI config (if not exists) +# ============================================ +UCI_CONFIG="/etc/config/$MODULE_UNDERSCORE" + +if [ ! -f "$UCI_CONFIG" ]; then + echo "→ Creating UCI config: $UCI_CONFIG" + + cat > "$UCI_CONFIG" << UCI_EOF +config global 'global' + option enabled '1' + option version '2.0.0' +UCI_EOF + + echo " ✓ Created UCI config" +else + echo "→ UCI config already exists: $UCI_CONFIG" +fi + +# ============================================ +# Restart services +# ============================================ +echo "" +echo "→ Restarting rpcd..." +/etc/init.d/rpcd restart + +echo "→ Clearing LuCI cache..." +rm -rf /tmp/luci-* + +# Wait for rpcd to initialize +sleep 2 + +# ============================================ +# Verify +# ============================================ +echo "" +echo "╔══════════════════════════════════════════════════════════════╗" +echo "║ Verification" +echo "╚══════════════════════════════════════════════════════════════╝" +echo "" + +# Check ubus registration +if ubus list "$UBUS_NAME" > /dev/null 2>&1; then + echo "✓ $UBUS_NAME is registered in ubus" + echo "" + echo "Available methods:" + ubus -v list "$UBUS_NAME" + + echo "" + echo "Testing status call:" + ubus call "$UBUS_NAME" status +else + echo "✗ $UBUS_NAME is NOT registered" + echo "" + echo "Debug steps:" + echo " 1. Check script: cat $RPCD_SCRIPT" + echo " 2. Test manually: echo '{\"method\":\"list\"}' | $RPCD_SCRIPT" + echo " 3. Check logs: logread | grep rpcd" +fi + +echo "" +echo "Done!" diff --git a/install-rpcd-fix.sh b/install-rpcd-fix.sh new file mode 100755 index 0000000..f988d64 --- /dev/null +++ b/install-rpcd-fix.sh @@ -0,0 +1,129 @@ +#!/bin/sh +# install-rpcd-fix.sh +# Quick installation script for SecuBox RPCD fixes +# +# Upload this script along with rpcd/ and acl/ folders to the router +# then run: sh install-rpcd-fix.sh + +echo "╔══════════════════════════════════════════════════════════════╗" +echo "║ SecuBox RPCD Fix Installer ║" +echo "╚══════════════════════════════════════════════════════════════╝" +echo "" + +SCRIPT_DIR="$(dirname "$(readlink -f "$0")")" + +# Check if running as root +if [ "$(id -u)" != "0" ]; then + echo "Error: This script must be run as root" + exit 1 +fi + +# ============================================ +# Install RPCD scripts +# ============================================ +echo "→ Installing RPCD scripts..." + +if [ -d "$SCRIPT_DIR/rpcd" ]; then + for script in "$SCRIPT_DIR/rpcd"/*; do + [ -f "$script" ] || continue + + NAME=$(basename "$script") + DEST="/usr/libexec/rpcd/$NAME" + + cp "$script" "$DEST" + chmod +x "$DEST" + echo " ✓ Installed: $DEST" + done +else + echo " ⚠ No rpcd/ directory found" +fi + +# ============================================ +# Install ACL files +# ============================================ +echo "" +echo "→ Installing ACL files..." + +mkdir -p /usr/share/rpcd/acl.d + +if [ -d "$SCRIPT_DIR/acl" ]; then + for acl in "$SCRIPT_DIR/acl"/*.json; do + [ -f "$acl" ] || continue + + NAME=$(basename "$acl") + DEST="/usr/share/rpcd/acl.d/$NAME" + + cp "$acl" "$DEST" + echo " ✓ Installed: $DEST" + done +else + echo " ⚠ No acl/ directory found" +fi + +# ============================================ +# Create missing UCI configs +# ============================================ +echo "" +echo "→ Creating UCI configs..." + +# vhost_manager +if [ ! -f /etc/config/vhost_manager ]; then + cat > /etc/config/vhost_manager << 'EOF' +config global 'global' + option enabled '1' + option nginx_dir '/etc/nginx/conf.d' + option acme_dir '/etc/acme' +EOF + echo " ✓ Created: /etc/config/vhost_manager" +fi + +# ============================================ +# Restart services +# ============================================ +echo "" +echo "→ Restarting services..." + +# Restart rpcd +/etc/init.d/rpcd restart +echo " ✓ rpcd restarted" + +# Clear LuCI cache +rm -rf /tmp/luci-* +echo " ✓ LuCI cache cleared" + +# Wait for rpcd to initialize +sleep 2 + +# ============================================ +# Verify installation +# ============================================ +echo "" +echo "╔══════════════════════════════════════════════════════════════╗" +echo "║ Verification ║" +echo "╚══════════════════════════════════════════════════════════════╝" +echo "" + +# List installed modules +echo "Checking ubus registration:" + +MODULES="vhost-manager secubox bandwidth-manager auth-guardian media-flow" + +for module in $MODULES; do + UBUS_NAME="luci.$module" + if ubus list "$UBUS_NAME" > /dev/null 2>&1; then + echo " ✓ $UBUS_NAME" + else + echo " ✗ $UBUS_NAME (not registered)" + fi +done + +echo "" +echo "Testing vhost-manager status:" +ubus call luci.vhost-manager status 2>/dev/null || echo " ✗ Failed" + +echo "" +echo "Installation complete!" +echo "" +echo "If modules are still not working, check:" +echo " logread | grep rpcd" +echo " logread | grep ubus" diff --git a/rpcd/vhost-manager b/rpcd/vhost-manager new file mode 100644 index 0000000..227677d --- /dev/null +++ b/rpcd/vhost-manager @@ -0,0 +1,334 @@ +#!/bin/sh +# /usr/libexec/rpcd/vhost-manager +# RPCD backend for VHost Manager module +# Provides ubus interface: luci.vhost-manager + +. /lib/functions.sh +. /usr/share/libubox/jshn.sh + +# Configuration +UCI_CONFIG="vhost_manager" +NGINX_VHOSTS_DIR="/etc/nginx/conf.d" +ACME_DIR="/etc/acme" + +# Helper: Check if nginx is running +nginx_running() { + pgrep -x nginx > /dev/null 2>&1 +} + +# Helper: Get nginx status +get_nginx_status() { + if nginx_running; then + echo "running" + else + echo "stopped" + fi +} + +# Helper: Count vhosts +count_vhosts() { + if [ -d "$NGINX_VHOSTS_DIR" ]; then + ls -1 "$NGINX_VHOSTS_DIR"/*.conf 2>/dev/null | wc -l + else + echo "0" + fi +} + +# Helper: List vhosts +list_vhosts() { + json_add_array "vhosts" + + if [ -d "$NGINX_VHOSTS_DIR" ]; then + for conf in "$NGINX_VHOSTS_DIR"/*.conf; do + [ -f "$conf" ] || continue + + # Extract server_name from config + SERVER_NAME=$(grep -m1 "server_name" "$conf" | awk '{print $2}' | tr -d ';') + PROXY_PASS=$(grep -m1 "proxy_pass" "$conf" | awk '{print $2}' | tr -d ';') + SSL_ENABLED="false" + + if grep -q "ssl_certificate" "$conf"; then + SSL_ENABLED="true" + fi + + json_add_object "" + json_add_string "name" "$(basename "$conf" .conf)" + json_add_string "domain" "$SERVER_NAME" + json_add_string "backend" "$PROXY_PASS" + json_add_boolean "ssl" "$SSL_ENABLED" + json_add_boolean "enabled" 1 + json_close_object + done + fi + + json_close_array +} + +# Helper: Get SSL certificates +list_certificates() { + json_add_array "certificates" + + if [ -d "$ACME_DIR" ]; then + for cert_dir in "$ACME_DIR"/*/; do + [ -d "$cert_dir" ] || continue + + DOMAIN=$(basename "$cert_dir") + CERT_FILE="$cert_dir/fullchain.cer" + + if [ -f "$CERT_FILE" ]; then + # Get expiry date + EXPIRY=$(openssl x509 -enddate -noout -in "$CERT_FILE" 2>/dev/null | cut -d= -f2) + + json_add_object "" + json_add_string "domain" "$DOMAIN" + json_add_string "expiry" "$EXPIRY" + json_add_boolean "valid" 1 + json_close_object + fi + done + fi + + json_close_array +} + +# Initialize JSON +json_init + +case "$1" in + list) + # List available methods + json_add_object "status" + json_close_object + + json_add_object "get_vhosts" + json_close_object + + json_add_object "get_vhost" + json_add_string "name" "string" + json_close_object + + json_add_object "add_vhost" + json_add_string "domain" "string" + json_add_string "backend" "string" + json_add_boolean "ssl" false + json_close_object + + json_add_object "delete_vhost" + json_add_string "name" "string" + json_close_object + + json_add_object "get_certificates" + json_close_object + + json_add_object "request_certificate" + json_add_string "domain" "string" + json_close_object + + json_add_object "reload_nginx" + json_close_object + + json_add_object "test_config" + json_close_object + + json_dump + ;; + + call) + case "$2" in + status) + # Return module status + json_add_string "module" "vhost-manager" + json_add_string "version" "2.0.0" + json_add_string "nginx_status" "$(get_nginx_status)" + json_add_boolean "nginx_running" $(nginx_running && echo 1 || echo 0) + json_add_int "vhost_count" "$(count_vhosts)" + json_add_string "config_dir" "$NGINX_VHOSTS_DIR" + json_add_string "acme_dir" "$ACME_DIR" + + # Check nginx version + if command -v nginx > /dev/null 2>&1; then + NGINX_VERSION=$(nginx -v 2>&1 | cut -d/ -f2) + json_add_string "nginx_version" "$NGINX_VERSION" + fi + + json_dump + ;; + + get_vhosts) + # Return list of vhosts + list_vhosts + json_dump + ;; + + get_vhost) + # Get single vhost details + read -r input + json_load "$input" + json_get_var vhost_name name + + CONF_FILE="$NGINX_VHOSTS_DIR/${vhost_name}.conf" + + if [ -f "$CONF_FILE" ]; then + json_add_boolean "found" 1 + json_add_string "name" "$vhost_name" + json_add_string "config" "$(cat "$CONF_FILE")" + else + json_add_boolean "found" 0 + json_add_string "error" "VHost not found" + fi + + json_dump + ;; + + add_vhost) + # Add new vhost + read -r input + json_load "$input" + json_get_var domain domain + json_get_var backend backend + json_get_var ssl ssl + + # Validate + if [ -z "$domain" ] || [ -z "$backend" ]; then + json_init + json_add_boolean "success" 0 + json_add_string "error" "Domain and backend are required" + json_dump + exit 0 + fi + + # Create config + VHOST_NAME=$(echo "$domain" | tr '.' '-') + CONF_FILE="$NGINX_VHOSTS_DIR/${VHOST_NAME}.conf" + + mkdir -p "$NGINX_VHOSTS_DIR" + + cat > "$CONF_FILE" << NGINX_EOF +server { + listen 80; + listen [::]:80; + server_name $domain; + + location / { + proxy_pass $backend; + proxy_http_version 1.1; + proxy_set_header Host \$host; + proxy_set_header X-Real-IP \$remote_addr; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto \$scheme; + proxy_set_header Upgrade \$http_upgrade; + proxy_set_header Connection "upgrade"; + } +} +NGINX_EOF + + json_init + json_add_boolean "success" 1 + json_add_string "message" "VHost created" + json_add_string "config_file" "$CONF_FILE" + json_dump + ;; + + delete_vhost) + # Delete vhost + read -r input + json_load "$input" + json_get_var vhost_name name + + CONF_FILE="$NGINX_VHOSTS_DIR/${vhost_name}.conf" + + if [ -f "$CONF_FILE" ]; then + rm -f "$CONF_FILE" + json_init + json_add_boolean "success" 1 + json_add_string "message" "VHost deleted" + else + json_init + json_add_boolean "success" 0 + json_add_string "error" "VHost not found" + fi + + json_dump + ;; + + get_certificates) + # List SSL certificates + list_certificates + json_dump + ;; + + request_certificate) + # Request Let's Encrypt certificate + read -r input + json_load "$input" + json_get_var domain domain + + if [ -z "$domain" ]; then + json_init + json_add_boolean "success" 0 + json_add_string "error" "Domain is required" + json_dump + exit 0 + fi + + # Check if acme.sh is available + if command -v acme.sh > /dev/null 2>&1; then + # Request certificate (async - just start the process) + acme.sh --issue -d "$domain" --webroot /www --keylength ec-256 & + + json_init + json_add_boolean "success" 1 + json_add_string "message" "Certificate request started" + json_add_string "domain" "$domain" + else + json_init + json_add_boolean "success" 0 + json_add_string "error" "acme.sh not installed" + fi + + json_dump + ;; + + reload_nginx) + # Reload nginx configuration + if nginx -t > /dev/null 2>&1; then + /etc/init.d/nginx reload + json_add_boolean "success" 1 + json_add_string "message" "Nginx reloaded" + else + json_add_boolean "success" 0 + json_add_string "error" "Configuration test failed" + json_add_string "details" "$(nginx -t 2>&1)" + fi + json_dump + ;; + + test_config) + # Test nginx configuration + TEST_OUTPUT=$(nginx -t 2>&1) + TEST_RESULT=$? + + if [ $TEST_RESULT -eq 0 ]; then + json_add_boolean "valid" 1 + json_add_string "message" "Configuration OK" + else + json_add_boolean "valid" 0 + json_add_string "error" "$TEST_OUTPUT" + fi + json_dump + ;; + + *) + # Unknown method + json_add_int "error" -32601 + json_add_string "message" "Method not found: $2" + json_dump + ;; + esac + ;; + + *) + echo "Usage: $0 {list|call}" + exit 1 + ;; +esac diff --git a/secubox-analyzer.sh b/secubox-analyzer.sh new file mode 100755 index 0000000..4a0bb65 --- /dev/null +++ b/secubox-analyzer.sh @@ -0,0 +1,1543 @@ +#!/bin/bash +# +# secubox-analyzer.sh +# ==================== +# Outil complet de debug, analyse et correction des sources SecuBox +# Analyse en profondeur le code source, détecte les problèmes et les corrige +# +# Usage: +# ./secubox-analyzer.sh # Analyse complète +# ./secubox-analyzer.sh --fix # Analyse + correction +# ./secubox-analyzer.sh --module NAME # Analyser un module spécifique +# ./secubox-analyzer.sh --report # Générer rapport HTML +# ./secubox-analyzer.sh --test-rpc # Tester les scripts RPCD localement +# +# Auteur: CyberMind.fr +# License: Apache-2.0 +# + +set -e + +# ============================================ +# Configuration +# ============================================ +VERSION="2.1.0" +SCRIPT_NAME=$(basename "$0") +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +WORK_DIR="${WORK_DIR:-$SCRIPT_DIR}" +REPORT_DIR="${REPORT_DIR:-$WORK_DIR/.secubox-reports}" +TIMESTAMP=$(date +%Y%m%d_%H%M%S) + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +CYAN='\033[0;36m' +MAGENTA='\033[0;35m' +WHITE='\033[1;37m' +GRAY='\033[0;90m' +BOLD='\033[1m' +DIM='\033[2m' +NC='\033[0m' + +# Counters +declare -A STATS=( + [modules]=0 + [errors]=0 + [warnings]=0 + [fixes]=0 + [files_checked]=0 + [issues_found]=0 +) + +# Options +DO_FIX=false +DO_REPORT=false +DO_TEST_RPC=false +DO_VERBOSE=false +DO_DEEP=false +TARGET_MODULE="" + +# Issue tracking +declare -a ISSUES=() +declare -a FIXES_APPLIED=() + +# ============================================ +# Module Registry +# ============================================ +declare -A MODULES=( + [secubox]="SecuBox Hub|+luci-base +rpcd +curl +jq" + [crowdsec-dashboard]="CrowdSec Dashboard|+luci-base +rpcd +crowdsec" + [netdata-dashboard]="Netdata Dashboard|+luci-base +rpcd +netdata" + [netifyd-dashboard]="Netifyd Dashboard|+luci-base +rpcd +netifyd" + [wireguard-dashboard]="WireGuard Dashboard|+luci-base +rpcd +wireguard-tools +qrencode" + [network-modes]="Network Modes|+luci-base +rpcd" + [client-guardian]="Client Guardian|+luci-base +rpcd +nodogsplash" + [system-hub]="System Hub|+luci-base +rpcd" + [bandwidth-manager]="Bandwidth Manager|+luci-base +rpcd +tc-full +kmod-sched-cake" + [auth-guardian]="Auth Guardian|+luci-base +rpcd +curl" + [media-flow]="Media Flow|+luci-base +rpcd +netifyd" + [vhost-manager]="VHost Manager|+luci-base +rpcd +nginx-ssl" + [cdn-cache]="CDN Cache|+luci-base +rpcd +nginx" + [traffic-shaper]="Traffic Shaper|+luci-base +rpcd +tc-full" +) + +# ============================================ +# Logging & Output +# ============================================ +log_file="" + +init_logging() { + mkdir -p "$REPORT_DIR" + log_file="$REPORT_DIR/analyze_${TIMESTAMP}.log" + exec > >(tee -a "$log_file") 2>&1 +} + +print_banner() { + echo "" + echo -e "${CYAN}╔═══════════════════════════════════════════════════════════════════════════╗${NC}" + echo -e "${CYAN}║${NC} ${CYAN}║${NC}" + echo -e "${CYAN}║${NC} ${BOLD}${WHITE}SecuBox Source Analyzer${NC} ${CYAN}║${NC}" + echo -e "${CYAN}║${NC} ${DIM}Debug • Analyze • Fix${NC} ${DIM}v${VERSION}${NC} ${CYAN}║${NC}" + echo -e "${CYAN}║${NC} ${CYAN}║${NC}" + echo -e "${CYAN}╚═══════════════════════════════════════════════════════════════════════════╝${NC}" + echo "" +} + +print_section() { + echo "" + echo -e "${BLUE}┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓${NC}" + echo -e "${BLUE}┃${NC} ${BOLD}$1${NC}" + echo -e "${BLUE}┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛${NC}" +} + +print_module_header() { + local name="$1" + local title="${MODULES[$name]%%|*}" + echo "" + echo -e "${MAGENTA}╭───────────────────────────────────────────────────────────────────────────╮${NC}" + echo -e "${MAGENTA}│${NC} 📦 ${BOLD}luci-app-${name}${NC}" + [[ -n "$title" ]] && echo -e "${MAGENTA}│${NC} ${DIM}$title${NC}" + echo -e "${MAGENTA}╰───────────────────────────────────────────────────────────────────────────╯${NC}" +} + +print_subsection() { + echo -e " ${CYAN}▸ $1${NC}" +} + +ok() { echo -e " ${GREEN}✓${NC} $1"; } +warn() { + echo -e " ${YELLOW}⚠${NC} $1" + ((STATS[warnings]++)) + ISSUES+=("WARN|$current_module|$1") +} +error() { + echo -e " ${RED}✗${NC} $1" + ((STATS[errors]++)) + ISSUES+=("ERROR|$current_module|$1") +} +info() { echo -e " ${CYAN}→${NC} $1"; } +debug() { $DO_VERBOSE && echo -e " ${GRAY} $1${NC}"; } +fixed() { + echo -e " ${GREEN}🔧${NC} $1" + ((STATS[fixes]++)) + FIXES_APPLIED+=("$current_module|$1") +} + +# ============================================ +# Utility Functions +# ============================================ + +# Check if command exists +has_cmd() { + command -v "$1" &>/dev/null +} + +# Get module name from directory +get_module_name() { + basename "$1" | sed 's/^luci-app-//' +} + +# Convert module-name to module_name +to_underscore() { + echo "$1" | tr '-' '_' +} + +# Validate JSON syntax +validate_json() { + local file="$1" + local errors="" + + if has_cmd jq; then + errors=$(jq empty "$file" 2>&1) && return 0 + elif has_cmd python3; then + errors=$(python3 -c "import json; json.load(open('$file'))" 2>&1) && return 0 + elif has_cmd node; then + errors=$(node -e "JSON.parse(require('fs').readFileSync('$file'))" 2>&1) && return 0 + else + return 0 # Can't validate + fi + + echo "$errors" + return 1 +} + +# Validate JavaScript syntax +validate_js() { + local file="$1" + local errors="" + + if has_cmd node; then + errors=$(node --check "$file" 2>&1) && return 0 + elif has_cmd eslint; then + errors=$(eslint --no-eslintrc "$file" 2>&1) && return 0 + fi + + # Basic check + if grep -qE "^'use strict'|require\('view'\)|return view\.extend" "$file" 2>/dev/null; then + return 0 + fi + + echo "$errors" + return 1 +} + +# Validate shell script +validate_shell() { + local file="$1" + local errors="" + + # Check shebang + local shebang=$(head -1 "$file") + if [[ ! "$shebang" =~ ^#! ]]; then + echo "Missing shebang" + return 1 + fi + + # Shellcheck if available + if has_cmd shellcheck; then + errors=$(shellcheck -s sh -f gcc "$file" 2>&1) + local exit_code=$? + if [[ $exit_code -ne 0 ]]; then + echo "$errors" + return 1 + fi + fi + + # Basic syntax check + if has_cmd sh; then + errors=$(sh -n "$file" 2>&1) && return 0 + echo "$errors" + return 1 + fi + + return 0 +} + +# ============================================ +# Analysis Functions +# ============================================ + +# Analyze directory structure +analyze_structure() { + local pkg_dir="$1" + local name="$2" + local name_u=$(to_underscore "$name") + + print_subsection "Directory Structure" + + # Required directories + local required_dirs=( + "root/usr/libexec/rpcd" + "root/usr/share/rpcd/acl.d" + "root/usr/share/luci/menu.d" + "htdocs/luci-static/resources/view" + ) + + local missing=0 + for dir in "${required_dirs[@]}"; do + if [[ -d "$pkg_dir/$dir" ]]; then + debug "OK: $dir" + else + warn "Missing directory: $dir" + ((missing++)) + + if $DO_FIX; then + mkdir -p "$pkg_dir/$dir" + fixed "Created: $dir" + fi + fi + done + + [[ $missing -eq 0 ]] && ok "All required directories present" + + # Check for malformed directories + local malformed=$(find "$pkg_dir" -type d -name '{*' -o -name '*}' 2>/dev/null) + if [[ -n "$malformed" ]]; then + error "Found malformed directories" + echo "$malformed" | while read -r dir; do + info " $dir" + if $DO_FIX; then + rm -rf "$dir" + fixed "Removed: $dir" + fi + done + fi + + ((STATS[files_checked]++)) +} + +# Analyze Makefile +analyze_makefile() { + local pkg_dir="$1" + local name="$2" + local makefile="$pkg_dir/Makefile" + + print_subsection "Makefile Analysis" + + if [[ ! -f "$makefile" ]]; then + error "Makefile missing" + if $DO_FIX; then + generate_makefile "$pkg_dir" "$name" + fi + return 1 + fi + + ((STATS[files_checked]++)) + + # Check required fields + local required_fields=("PKG_NAME" "PKG_VERSION" "PKG_RELEASE" "PKG_LICENSE") + local missing_fields=() + + for field in "${required_fields[@]}"; do + if grep -q "^${field}:=" "$makefile"; then + local value=$(grep "^${field}:=" "$makefile" | cut -d'=' -f2) + debug "$field = $value" + else + missing_fields+=("$field") + fi + done + + if [[ ${#missing_fields[@]} -gt 0 ]]; then + warn "Missing fields: ${missing_fields[*]}" + if $DO_FIX; then + generate_makefile "$pkg_dir" "$name" + fi + fi + + # Check PKG_NAME matches directory + local pkg_name=$(grep "^PKG_NAME:=" "$makefile" | cut -d'=' -f2) + if [[ "$pkg_name" != "luci-app-$name" ]]; then + error "PKG_NAME mismatch: expected 'luci-app-$name', got '$pkg_name'" + if $DO_FIX; then + sed -i "s/^PKG_NAME:=.*/PKG_NAME:=luci-app-$name/" "$makefile" + fixed "Corrected PKG_NAME" + fi + else + ok "PKG_NAME correct" + fi + + # Check include statement + if grep -q 'include.*feeds/luci/luci\.mk' "$makefile"; then + ok "Uses luci.mk (correct)" + elif grep -q 'include.*package\.mk' "$makefile"; then + warn "Uses package.mk (should use luci.mk for LuCI apps)" + if $DO_FIX; then + generate_makefile "$pkg_dir" "$name" + fi + else + error "Missing include statement" + if $DO_FIX; then + generate_makefile "$pkg_dir" "$name" + fi + fi + + # Check LUCI_TITLE + if ! grep -q "^LUCI_TITLE:=" "$makefile"; then + warn "Missing LUCI_TITLE" + fi + + # Check LUCI_DEPENDS + if ! grep -q "^LUCI_DEPENDS:=" "$makefile"; then + warn "Missing LUCI_DEPENDS" + fi +} + +# Analyze RPCD script +analyze_rpcd() { + local pkg_dir="$1" + local name="$2" + local rpcd_dir="$pkg_dir/root/usr/libexec/rpcd" + local rpcd_script="$rpcd_dir/$name" + + print_subsection "RPCD Script Analysis" + + # Check if script exists + if [[ ! -f "$rpcd_script" ]]; then + # Try alternative names + local alt_scripts=( + "$rpcd_dir/luci.$name" + "$rpcd_dir/luci-$name" + "$rpcd_dir/${name//-/_}" + ) + + local found=false + for alt in "${alt_scripts[@]}"; do + if [[ -f "$alt" ]]; then + warn "RPCD script at non-standard location: $(basename "$alt")" + if $DO_FIX; then + cp "$alt" "$rpcd_script" + fixed "Copied to standard location" + fi + found=true + break + fi + done + + if ! $found; then + error "RPCD script missing: $name" + info "Expected: $rpcd_script" + if $DO_FIX; then + generate_rpcd "$pkg_dir" "$name" + fi + return 1 + fi + fi + + ((STATS[files_checked]++)) + + # Check permissions + if [[ ! -x "$rpcd_script" ]]; then + warn "RPCD script not executable" + if $DO_FIX; then + chmod +x "$rpcd_script" + fixed "Made executable" + fi + else + ok "Script is executable" + fi + + # Validate shell syntax + local shell_errors=$(validate_shell "$rpcd_script") + if [[ -n "$shell_errors" ]]; then + error "Shell syntax errors:" + echo "$shell_errors" | head -5 | while read -r line; do + info " $line" + done + else + ok "Shell syntax valid" + fi + + # Check shebang + local shebang=$(head -1 "$rpcd_script") + if [[ "$shebang" != "#!/bin/sh" && "$shebang" != "#!/bin/bash" ]]; then + warn "Non-standard shebang: $shebang" + if $DO_FIX; then + sed -i '1s|^.*$|#!/bin/sh|' "$rpcd_script" + fixed "Fixed shebang" + fi + fi + + # Check for required components + local components=( + "json_init:JSON initialization" + "json_add:JSON building" + "json_dump:JSON output" + 'case.*list:list handler' + 'case.*call:call handler' + ) + + for comp in "${components[@]}"; do + local pattern="${comp%%:*}" + local desc="${comp##*:}" + + if grep -qE "$pattern" "$rpcd_script"; then + debug "Has $desc" + else + warn "Missing $desc ($pattern)" + fi + done + + # Check for status method + if grep -q 'status)' "$rpcd_script" || grep -q '"status"' "$rpcd_script"; then + ok "Has 'status' method" + else + error "Missing 'status' method (required for LuCI)" + if $DO_FIX; then + # This is complex - regenerate the whole script + generate_rpcd "$pkg_dir" "$name" + fi + fi + + # Extract and display methods + if $DO_VERBOSE; then + info "Methods found:" + grep -oE '\b[a-z_]+\)' "$rpcd_script" 2>/dev/null | tr -d ')' | sort -u | while read -r method; do + [[ -n "$method" ]] && debug " - $method" + done + fi + + # Check ubus object name consistency + local ubus_name=$(grep -oE "luci\.$name|luci\.${name//-/_}" "$rpcd_script" | head -1) + if [[ -z "$ubus_name" ]]; then + warn "Cannot determine ubus object name from script" + else + debug "ubus name: $ubus_name" + fi +} + +# Analyze ACL file +analyze_acl() { + local pkg_dir="$1" + local name="$2" + local name_u=$(to_underscore "$name") + local acl_dir="$pkg_dir/root/usr/share/rpcd/acl.d" + + print_subsection "ACL File Analysis" + + # Find ACL file + local acl_file="" + local acl_patterns=( + "$acl_dir/luci-app-${name}.json" + "$acl_dir/luci-${name}.json" + "$acl_dir/${name}.json" + "$acl_dir/luci-app-${name_u}.json" + ) + + for pattern in "${acl_patterns[@]}"; do + if [[ -f "$pattern" ]]; then + acl_file="$pattern" + break + fi + done + + if [[ -z "$acl_file" ]]; then + error "ACL file missing" + info "Expected: $acl_dir/luci-app-${name}.json" + if $DO_FIX; then + generate_acl "$pkg_dir" "$name" + fi + return 1 + fi + + ((STATS[files_checked]++)) + + # Check filename + local expected_name="luci-app-${name}.json" + local actual_name=$(basename "$acl_file") + if [[ "$actual_name" != "$expected_name" ]]; then + warn "ACL filename should be: $expected_name (got: $actual_name)" + if $DO_FIX; then + mv "$acl_file" "$acl_dir/$expected_name" + acl_file="$acl_dir/$expected_name" + fixed "Renamed ACL file" + fi + fi + + # Validate JSON + local json_errors=$(validate_json "$acl_file") + if [[ -n "$json_errors" ]]; then + error "Invalid JSON syntax" + echo "$json_errors" | head -3 | while read -r line; do + info " $line" + done + if $DO_FIX; then + generate_acl "$pkg_dir" "$name" + fi + return 1 + else + ok "JSON syntax valid" + fi + + # Check ubus permissions + local ubus_name="luci.$name" + if grep -q "\"$ubus_name\"" "$acl_file"; then + ok "Contains ubus permission for $ubus_name" + else + error "Missing ubus permission for $ubus_name" + if $DO_FIX; then + generate_acl "$pkg_dir" "$name" + fi + fi + + # Check for status method permission + if grep -q '"status"' "$acl_file"; then + ok "Has 'status' method permission" + else + warn "Missing 'status' method in ACL" + fi + + # Deep analysis + if $DO_DEEP && has_cmd jq; then + info "ACL structure:" + jq -r 'keys[]' "$acl_file" 2>/dev/null | while read -r key; do + debug " Section: $key" + done + fi +} + +# Analyze Menu file +analyze_menu() { + local pkg_dir="$1" + local name="$2" + local name_u=$(to_underscore "$name") + local menu_dir="$pkg_dir/root/usr/share/luci/menu.d" + + print_subsection "Menu File Analysis" + + # Find menu file + local menu_file="" + local menu_patterns=( + "$menu_dir/luci-app-${name}.json" + "$menu_dir/luci-${name}.json" + "$menu_dir/${name}.json" + ) + + for pattern in "${menu_patterns[@]}"; do + if [[ -f "$pattern" ]]; then + menu_file="$pattern" + break + fi + done + + if [[ -z "$menu_file" ]]; then + error "Menu file missing" + if $DO_FIX; then + generate_menu "$pkg_dir" "$name" + fi + return 1 + fi + + ((STATS[files_checked]++)) + + # Validate JSON + local json_errors=$(validate_json "$menu_file") + if [[ -n "$json_errors" ]]; then + error "Invalid JSON syntax" + if $DO_FIX; then + generate_menu "$pkg_dir" "$name" + fi + return 1 + else + ok "JSON syntax valid" + fi + + # Check menu path + if has_cmd jq; then + local menu_path=$(jq -r 'keys[0]' "$menu_file" 2>/dev/null) + local view_path=$(jq -r '.[keys[0]].action.path // empty' "$menu_file" 2>/dev/null) + + debug "Menu path: $menu_path" + debug "View path: $view_path" + + # Check if view file exists + if [[ -n "$view_path" ]]; then + local view_file="$pkg_dir/htdocs/luci-static/resources/view/${view_path}.js" + if [[ -f "$view_file" ]]; then + ok "View file exists: ${view_path}.js" + else + warn "View file not found: ${view_path}.js" + fi + fi + fi + + ok "Menu file valid" +} + +# Analyze View files (JavaScript) +analyze_views() { + local pkg_dir="$1" + local name="$2" + local name_u=$(to_underscore "$name") + local view_base="$pkg_dir/htdocs/luci-static/resources/view" + + print_subsection "View Files Analysis" + + # Find JavaScript files + local js_files=$(find "$pkg_dir/htdocs" -name "*.js" 2>/dev/null) + local js_count=$(echo "$js_files" | grep -c . 2>/dev/null || echo 0) + + if [[ $js_count -eq 0 ]]; then + warn "No JavaScript view files found" + if $DO_FIX; then + generate_view "$pkg_dir" "$name" + fi + return 1 + fi + + info "Found $js_count JavaScript file(s)" + + echo "$js_files" | while read -r js_file; do + [[ -z "$js_file" ]] && continue + + ((STATS[files_checked]++)) + local filename=$(basename "$js_file") + + # Validate syntax + local js_errors=$(validate_js "$js_file") + if [[ -n "$js_errors" ]]; then + error "Syntax error in $filename" + echo "$js_errors" | head -3 | while read -r line; do + info " $line" + done + else + ok "Valid: $filename" + fi + + # Check for required patterns + if ! grep -q "'require view'" "$js_file" && ! grep -q '"require view"' "$js_file"; then + if ! grep -q "require('view')" "$js_file"; then + warn "$filename: Missing 'require view'" + fi + fi + + # Check RPC declaration + if grep -q "rpc.declare" "$js_file"; then + local rpc_object=$(grep -oE "object:\s*['\"][^'\"]+['\"]" "$js_file" | head -1) + debug "$filename uses RPC: $rpc_object" + + # Check if RPC object matches module + if ! echo "$rpc_object" | grep -qE "luci\.$name|luci\.${name_u}"; then + warn "RPC object may not match module name" + fi + fi + + # Check for view.extend + if ! grep -q "view.extend" "$js_file"; then + warn "$filename: Missing view.extend" + fi + done +} + +# Analyze UCI config +analyze_config() { + local pkg_dir="$1" + local name="$2" + local name_u=$(to_underscore "$name") + local config_dir="$pkg_dir/root/etc/config" + local config_file="$config_dir/$name_u" + + print_subsection "UCI Config Analysis" + + if [[ ! -f "$config_file" ]]; then + info "No UCI config file (optional)" + return 0 + fi + + ((STATS[files_checked]++)) + + # Basic validation + if head -1 "$config_file" | grep -q "^config "; then + ok "UCI config format valid" + else + warn "UCI config may have invalid format" + fi + + # Check for global section + if grep -q "config global" "$config_file"; then + ok "Has 'global' section" + else + info "No 'global' section (may be intentional)" + fi +} + +# Analyze init script +analyze_init() { + local pkg_dir="$1" + local name="$2" + local name_u=$(to_underscore "$name") + local init_dir="$pkg_dir/root/etc/init.d" + + print_subsection "Init Script Analysis" + + # Find init script + local init_script="" + for pattern in "$init_dir/$name" "$init_dir/$name_u" "$init_dir/luci-app-$name"; do + if [[ -f "$pattern" ]]; then + init_script="$pattern" + break + fi + done + + if [[ -z "$init_script" ]]; then + info "No init script (optional for LuCI apps)" + return 0 + fi + + ((STATS[files_checked]++)) + + # Check permissions + if [[ ! -x "$init_script" ]]; then + warn "Init script not executable" + if $DO_FIX; then + chmod +x "$init_script" + fixed "Made init script executable" + fi + else + ok "Init script executable" + fi + + # Check for required functions + local required_funcs=("start" "stop") + for func in "${required_funcs[@]}"; do + if grep -q "${func}()" "$init_script" || grep -q "${func}_service" "$init_script"; then + debug "Has $func function" + else + warn "Missing $func function" + fi + done +} + +# Cross-reference check +analyze_cross_references() { + local pkg_dir="$1" + local name="$2" + local name_u=$(to_underscore "$name") + + print_subsection "Cross-Reference Check" + + local issues=0 + + # Check RPCD <-> ACL consistency + local rpcd_script="$pkg_dir/root/usr/libexec/rpcd/$name" + local acl_file="$pkg_dir/root/usr/share/rpcd/acl.d/luci-app-${name}.json" + + if [[ -f "$rpcd_script" && -f "$acl_file" ]]; then + # Extract methods from RPCD + local rpcd_methods=$(grep -oE '\b[a-z_]+\)' "$rpcd_script" 2>/dev/null | tr -d ')' | sort -u) + + # Check if they're in ACL + for method in $rpcd_methods; do + [[ "$method" == "list" || "$method" == "call" || "$method" == "esac" ]] && continue + + if ! grep -q "\"$method\"" "$acl_file" 2>/dev/null; then + warn "Method '$method' in RPCD but not in ACL" + ((issues++)) + fi + done + fi + + # Check Menu <-> View consistency + local menu_file="$pkg_dir/root/usr/share/luci/menu.d/luci-app-${name}.json" + + if [[ -f "$menu_file" ]] && has_cmd jq; then + local view_path=$(jq -r '.[keys[0]].action.path // empty' "$menu_file" 2>/dev/null) + + if [[ -n "$view_path" ]]; then + local view_file="$pkg_dir/htdocs/luci-static/resources/view/${view_path}.js" + + if [[ ! -f "$view_file" ]]; then + error "Menu references non-existent view: $view_path" + ((issues++)) + fi + fi + fi + + # Check View <-> RPCD consistency + local view_files=$(find "$pkg_dir/htdocs" -name "*.js" 2>/dev/null) + + for view_file in $view_files; do + [[ -z "$view_file" ]] && continue + + # Extract RPC object names + local rpc_objects=$(grep -oE "object:\s*['\"][^'\"]+['\"]" "$view_file" 2>/dev/null | grep -oE "'[^']+'" | tr -d "'") + + for obj in $rpc_objects; do + local expected_rpcd="${obj#luci.}" + local rpcd_path="$pkg_dir/root/usr/libexec/rpcd/$expected_rpcd" + + if [[ ! -f "$rpcd_path" ]]; then + warn "View uses RPC object '$obj' but RPCD script '$expected_rpcd' not found" + ((issues++)) + fi + done + done + + if [[ $issues -eq 0 ]]; then + ok "All cross-references valid" + fi +} + +# ============================================ +# Generator Functions +# ============================================ + +generate_makefile() { + local pkg_dir="$1" + local name="$2" + local makefile="$pkg_dir/Makefile" + + local title="${MODULES[$name]%%|*}" + local depends="${MODULES[$name]##*|}" + + [[ -z "$title" ]] && title="SecuBox Module" + [[ -z "$depends" ]] && depends="+luci-base +rpcd" + + cat > "$makefile" << EOF +include \$(TOPDIR)/rules.mk + +PKG_NAME:=luci-app-${name} +PKG_VERSION:=${VERSION} +PKG_RELEASE:=1 +PKG_LICENSE:=Apache-2.0 +PKG_MAINTAINER:=CyberMind + +LUCI_TITLE:=LuCI - ${title} +LUCI_DEPENDS:=${depends} +LUCI_PKGARCH:=all + +include \$(TOPDIR)/feeds/luci/luci.mk + +# call BuildPackage - OpenWrt buildroot +EOF + + fixed "Generated Makefile" +} + +generate_rpcd() { + local pkg_dir="$1" + local name="$2" + local name_u=$(to_underscore "$name") + local rpcd_dir="$pkg_dir/root/usr/libexec/rpcd" + local rpcd_script="$rpcd_dir/$name" + + mkdir -p "$rpcd_dir" + + cat > "$rpcd_script" << 'RPCD_EOF' +#!/bin/sh +# RPCD backend for __NAME__ +# Provides ubus interface: luci.__NAME__ + +. /lib/functions.sh +. /usr/share/libubox/jshn.sh + +MODULE_NAME="__NAME__" +MODULE_VERSION="__VERSION__" +UCI_CONFIG="__NAME_U__" + +json_init + +case "$1" in + list) + json_add_object "status" + json_close_object + json_add_object "get_config" + json_close_object + json_add_object "set_config" + json_add_string "config" "object" + json_close_object + json_add_object "get_stats" + json_close_object + json_dump + ;; + + call) + case "$2" in + status) + json_add_string "module" "$MODULE_NAME" + json_add_string "version" "$MODULE_VERSION" + json_add_boolean "enabled" 1 + json_add_string "status" "running" + json_add_string "timestamp" "$(date -Iseconds 2>/dev/null || date)" + json_dump + ;; + + get_config) + json_add_object "config" + if [ -f "/etc/config/$UCI_CONFIG" ]; then + json_add_boolean "enabled" 1 + else + json_add_boolean "enabled" 0 + fi + json_close_object + json_dump + ;; + + set_config) + read -r input + json_add_boolean "success" 1 + json_add_string "message" "Configuration updated" + json_dump + ;; + + get_stats) + json_add_object "stats" + json_add_int "uptime" "$(cat /proc/uptime 2>/dev/null | cut -d. -f1 || echo 0)" + json_close_object + json_dump + ;; + + *) + json_add_int "error" -32601 + json_add_string "message" "Method not found: $2" + json_dump + ;; + esac + ;; +esac +RPCD_EOF + + sed -i "s/__NAME__/$name/g" "$rpcd_script" + sed -i "s/__NAME_U__/$name_u/g" "$rpcd_script" + sed -i "s/__VERSION__/$VERSION/g" "$rpcd_script" + + chmod +x "$rpcd_script" + fixed "Generated RPCD script" +} + +generate_acl() { + local pkg_dir="$1" + local name="$2" + local name_u=$(to_underscore "$name") + local acl_dir="$pkg_dir/root/usr/share/rpcd/acl.d" + local acl_file="$acl_dir/luci-app-${name}.json" + + mkdir -p "$acl_dir" + + cat > "$acl_file" << EOF +{ + "luci-app-${name}": { + "description": "Grant access to LuCI app ${name}", + "read": { + "ubus": { + "luci.${name}": ["status", "get_config", "get_stats"] + }, + "uci": ["${name_u}"] + }, + "write": { + "ubus": { + "luci.${name}": ["set_config"] + }, + "uci": ["${name_u}"] + } + } +} +EOF + + fixed "Generated ACL file" +} + +generate_menu() { + local pkg_dir="$1" + local name="$2" + local name_u=$(to_underscore "$name") + local menu_dir="$pkg_dir/root/usr/share/luci/menu.d" + local menu_file="$menu_dir/luci-app-${name}.json" + + mkdir -p "$menu_dir" + + local title="${MODULES[$name]%%|*}" + [[ -z "$title" ]] && title=$(echo "$name" | sed 's/-/ /g' | sed 's/\b\(.\)/\u\1/g') + + cat > "$menu_file" << EOF +{ + "admin/services/${name_u}": { + "title": "${title}", + "order": 50, + "action": { + "type": "view", + "path": "${name_u}/main" + }, + "depends": { + "acl": ["luci-app-${name}"], + "uci": { + "${name_u}": true + } + } + } +} +EOF + + fixed "Generated Menu file" +} + +generate_view() { + local pkg_dir="$1" + local name="$2" + local name_u=$(to_underscore "$name") + local view_dir="$pkg_dir/htdocs/luci-static/resources/view/${name_u}" + local view_file="$view_dir/main.js" + + mkdir -p "$view_dir" + + local title="${MODULES[$name]%%|*}" + [[ -z "$title" ]] && title="$name" + + cat > "$view_file" << EOF +'use strict'; +'require view'; +'require rpc'; +'require ui'; +'require form'; + +var callStatus = rpc.declare({ + object: 'luci.${name}', + method: 'status', + expect: { } +}); + +return view.extend({ + load: function() { + return Promise.all([ + callStatus() + ]); + }, + + render: function(data) { + var status = data[0] || {}; + var m, s, o; + + m = new form.Map('${name_u}', _('${title}'), + _('SecuBox ${title} module')); + + s = m.section(form.NamedSection, 'global', 'global', _('Status')); + s.anonymous = true; + + o = s.option(form.DummyValue, '_status', _('Module Status')); + o.rawhtml = true; + o.cfgvalue = function() { + var running = status.status === 'running'; + return '' + + (running ? '● Running' : '○ Stopped') + ''; + }; + + o = s.option(form.DummyValue, '_version', _('Version')); + o.cfgvalue = function() { + return status.version || 'Unknown'; + }; + + return m.render(); + }, + + handleSaveApply: null, + handleSave: null, + handleReset: null +}); +EOF + + fixed "Generated View file" +} + +# ============================================ +# RPC Testing +# ============================================ + +test_rpc_local() { + local pkg_dir="$1" + local name="$2" + local rpcd_script="$pkg_dir/root/usr/libexec/rpcd/$name" + + print_subsection "Local RPC Testing" + + if [[ ! -f "$rpcd_script" ]]; then + error "RPCD script not found" + return 1 + fi + + if [[ ! -x "$rpcd_script" ]]; then + chmod +x "$rpcd_script" + fi + + # Create mock environment + local mock_dir=$(mktemp -d) + + # Mock /lib/functions.sh + cat > "$mock_dir/functions.sh" << 'EOF' +config_load() { :; } +config_get() { :; } +config_set() { :; } +EOF + + # Mock jshn.sh + cat > "$mock_dir/jshn.sh" << 'EOF' +json_init() { JSON_OUTPUT="{"; JSON_FIRST=1; } +json_add_string() { + [ "$JSON_FIRST" = 1 ] && JSON_FIRST=0 || JSON_OUTPUT="$JSON_OUTPUT," + JSON_OUTPUT="$JSON_OUTPUT\"$1\":\"$2\"" +} +json_add_boolean() { + [ "$JSON_FIRST" = 1 ] && JSON_FIRST=0 || JSON_OUTPUT="$JSON_OUTPUT," + JSON_OUTPUT="$JSON_OUTPUT\"$1\":$2" +} +json_add_int() { + [ "$JSON_FIRST" = 1 ] && JSON_FIRST=0 || JSON_OUTPUT="$JSON_OUTPUT," + JSON_OUTPUT="$JSON_OUTPUT\"$1\":$2" +} +json_add_object() { + [ "$JSON_FIRST" = 1 ] && JSON_FIRST=0 || JSON_OUTPUT="$JSON_OUTPUT," + JSON_OUTPUT="$JSON_OUTPUT\"$1\":{" + JSON_FIRST=1 +} +json_add_array() { + [ "$JSON_FIRST" = 1 ] && JSON_FIRST=0 || JSON_OUTPUT="$JSON_OUTPUT," + JSON_OUTPUT="$JSON_OUTPUT\"$1\":[" + JSON_FIRST=1 +} +json_close_object() { JSON_OUTPUT="$JSON_OUTPUT}"; JSON_FIRST=0; } +json_close_array() { JSON_OUTPUT="$JSON_OUTPUT]"; JSON_FIRST=0; } +json_dump() { echo "$JSON_OUTPUT}"; } +json_load() { :; } +json_get_var() { :; } +EOF + + # Create wrapper script + local wrapper="$mock_dir/wrapper.sh" + cat > "$wrapper" << EOF +#!/bin/sh +. "$mock_dir/functions.sh" +. "$mock_dir/jshn.sh" +EOF + + # Append the original script (without shebang and includes) + tail -n +2 "$rpcd_script" | grep -v '^\. /lib\|^\. /usr/share' >> "$wrapper" + chmod +x "$wrapper" + + # Test 'list' command + info "Testing 'list' command..." + local list_output=$("$wrapper" list 2>&1) + + if echo "$list_output" | grep -q '"status"'; then + ok "list command works" + if $DO_VERBOSE; then + echo "$list_output" | head -5 | while read -r line; do + debug " $line" + done + fi + else + warn "list command may have issues" + debug "Output: $list_output" + fi + + # Test 'call status' command + info "Testing 'call status' command..." + local status_output=$("$wrapper" call status 2>&1) + + if echo "$status_output" | grep -qE '"status"|"module"|"version"'; then + ok "status method works" + if $DO_VERBOSE; then + echo "$status_output" | while read -r line; do + debug " $line" + done + fi + else + warn "status method may have issues" + debug "Output: $status_output" + fi + + # Cleanup + rm -rf "$mock_dir" +} + +# ============================================ +# Report Generation +# ============================================ + +generate_html_report() { + local report_file="$REPORT_DIR/report_${TIMESTAMP}.html" + + print_section "Generating HTML Report" + + cat > "$report_file" << 'HTML_HEADER' + + + + + + SecuBox Analysis Report + + + +
+

🛡️ SecuBox Analysis Report

+

Generated: TIMESTAMP_PLACEHOLDER

+ +
+
+
MODULES_COUNT
+
Modules
+
+
+
FILES_COUNT
+
Files Checked
+
+
+
ERRORS_COUNT
+
Errors
+
+
+
WARNINGS_COUNT
+
Warnings
+
+
+
FIXES_COUNT
+
Fixes Applied
+
+
+HTML_HEADER + + # Issues section + if [[ ${#ISSUES[@]} -gt 0 ]]; then + echo '

⚠️ Issues Found

' >> "$report_file" + + for issue in "${ISSUES[@]}"; do + local type="${issue%%|*}" + local rest="${issue#*|}" + local module="${rest%%|*}" + local message="${rest#*|}" + + local class="warn" + local icon="⚠️" + [[ "$type" == "ERROR" ]] && class="error" && icon="❌" + + echo "
$icon$module:$message
" >> "$report_file" + done + + echo '
' >> "$report_file" + fi + + # Fixes section + if [[ ${#FIXES_APPLIED[@]} -gt 0 ]]; then + echo '

🔧 Fixes Applied

' >> "$report_file" + + for fix in "${FIXES_APPLIED[@]}"; do + local module="${fix%%|*}" + local message="${fix#*|}" + echo "
$module: $message
" >> "$report_file" + done + + echo '
' >> "$report_file" + fi + + # Footer + cat >> "$report_file" << 'HTML_FOOTER' +

SecuBox Analyzer v__VERSION__

+
+ + +HTML_FOOTER + + # Replace placeholders + sed -i "s/TIMESTAMP_PLACEHOLDER/$(date)/g" "$report_file" + sed -i "s/MODULES_COUNT/${STATS[modules]}/g" "$report_file" + sed -i "s/FILES_COUNT/${STATS[files_checked]}/g" "$report_file" + sed -i "s/ERRORS_COUNT/${STATS[errors]}/g" "$report_file" + sed -i "s/WARNINGS_COUNT/${STATS[warnings]}/g" "$report_file" + sed -i "s/FIXES_COUNT/${STATS[fixes]}/g" "$report_file" + sed -i "s/__VERSION__/$VERSION/g" "$report_file" + + ok "Report generated: $report_file" + + # Try to open in browser + if has_cmd xdg-open; then + xdg-open "$report_file" 2>/dev/null & + elif has_cmd open; then + open "$report_file" 2>/dev/null & + fi +} + +# ============================================ +# Main Analysis +# ============================================ + +analyze_module() { + local pkg_dir="$1" + local name=$(get_module_name "$pkg_dir") + + current_module="$name" + ((STATS[modules]++)) + + print_module_header "$name" + + # Run all analyses + analyze_structure "$pkg_dir" "$name" + analyze_makefile "$pkg_dir" "$name" + analyze_rpcd "$pkg_dir" "$name" + analyze_acl "$pkg_dir" "$name" + analyze_menu "$pkg_dir" "$name" + analyze_views "$pkg_dir" "$name" + analyze_config "$pkg_dir" "$name" + analyze_init "$pkg_dir" "$name" + analyze_cross_references "$pkg_dir" "$name" + + # Optional RPC testing + if $DO_TEST_RPC; then + test_rpc_local "$pkg_dir" "$name" + fi +} + +print_summary() { + print_section "Analysis Summary" + + echo "" + echo -e " ${BOLD}Modules analyzed:${NC} ${STATS[modules]}" + echo -e " ${BOLD}Files checked:${NC} ${STATS[files_checked]}" + echo "" + echo -e " ${RED}Errors:${NC} ${STATS[errors]}" + echo -e " ${YELLOW}Warnings:${NC} ${STATS[warnings]}" + echo -e " ${GREEN}Fixes applied:${NC} ${STATS[fixes]}" + echo "" + + if [[ ${STATS[errors]} -gt 0 ]]; then + echo -e " ${RED}✗ Some errors need attention${NC}" + if ! $DO_FIX; then + echo -e " ${DIM}Run with --fix to attempt automatic repairs${NC}" + fi + elif [[ ${STATS[warnings]} -gt 0 ]]; then + echo -e " ${YELLOW}⚠ Some warnings to review${NC}" + else + echo -e " ${GREEN}✓ All modules look good!${NC}" + fi + + echo "" + + if [[ -n "$log_file" ]]; then + echo -e " ${DIM}Log file: $log_file${NC}" + fi +} + +# ============================================ +# CLI +# ============================================ + +show_help() { + cat << EOF +${BOLD}SecuBox Source Analyzer v${VERSION}${NC} + +Analyze, debug, and fix SecuBox LuCI module sources. + +${BOLD}USAGE:${NC} + $SCRIPT_NAME [OPTIONS] [MODULE] + +${BOLD}OPTIONS:${NC} + --fix Apply automatic fixes for detected issues + --report Generate HTML report + --test-rpc Test RPCD scripts locally (mock environment) + --deep Enable deep analysis + --verbose, -v Show detailed output + --help, -h Show this help + +${BOLD}EXAMPLES:${NC} + $SCRIPT_NAME Analyze all modules + $SCRIPT_NAME --fix Analyze and fix all modules + $SCRIPT_NAME --module vhost-manager Analyze specific module + $SCRIPT_NAME --fix --report Fix and generate report + $SCRIPT_NAME --test-rpc Test RPC scripts locally + +${BOLD}ENVIRONMENT:${NC} + WORK_DIR Directory containing luci-app-* packages + REPORT_DIR Directory for reports (default: .secubox-reports) + +EOF +} + +parse_args() { + while [[ $# -gt 0 ]]; do + case "$1" in + --fix) + DO_FIX=true + shift + ;; + --report) + DO_REPORT=true + shift + ;; + --test-rpc) + DO_TEST_RPC=true + shift + ;; + --deep) + DO_DEEP=true + shift + ;; + --verbose|-v) + DO_VERBOSE=true + shift + ;; + --module) + TARGET_MODULE="$2" + shift 2 + ;; + --help|-h) + show_help + exit 0 + ;; + -*) + echo "Unknown option: $1" + show_help + exit 1 + ;; + *) + TARGET_MODULE="$1" + shift + ;; + esac + done +} + +main() { + parse_args "$@" + + print_banner + init_logging + + echo -e " ${CYAN}Working directory:${NC} $WORK_DIR" + echo -e " ${CYAN}Fix mode:${NC} $($DO_FIX && echo 'ON' || echo 'OFF')" + echo -e " ${CYAN}Test RPC:${NC} $($DO_TEST_RPC && echo 'ON' || echo 'OFF')" + echo -e " ${CYAN}Report:${NC} $($DO_REPORT && echo 'ON' || echo 'OFF')" + + cd "$WORK_DIR" || exit 1 + + # Find and analyze modules + if [[ -n "$TARGET_MODULE" ]]; then + local pkg_dir="$WORK_DIR/luci-app-$TARGET_MODULE" + [[ ! -d "$pkg_dir" ]] && pkg_dir="$WORK_DIR/$TARGET_MODULE" + + if [[ -d "$pkg_dir" ]]; then + analyze_module "$pkg_dir" + else + error "Module not found: $TARGET_MODULE" + exit 1 + fi + else + for pkg_dir in luci-app-*/; do + [[ -d "$pkg_dir" ]] && analyze_module "${pkg_dir%/}" + done + fi + + # Generate report if requested + $DO_REPORT && generate_html_report + + # Print summary + print_summary + + # Exit code + [[ ${STATS[errors]} -gt 0 ]] && exit 1 + exit 0 +} + +main "$@" diff --git a/secubox-debug.sh b/secubox-debug.sh new file mode 100755 index 0000000..7d3b898 --- /dev/null +++ b/secubox-debug.sh @@ -0,0 +1,421 @@ +#!/bin/sh +# secubox-debug.sh +# Debug and analysis script for SecuBox LuCI modules RPC/ubus issues +# +# Usage: ./secubox-debug.sh [module-name] +# Example: ./secubox-debug.sh vhost-manager +# ./secubox-debug.sh all + +set -e + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +CYAN='\033[0;36m' +NC='\033[0m' # No Color + +# SecuBox modules list +MODULES=" +secubox +crowdsec-dashboard +netdata-dashboard +netifyd-dashboard +wireguard-dashboard +network-modes +client-guardian +system-hub +bandwidth-manager +auth-guardian +media-flow +vhost-manager +cdn-cache +traffic-shaper +" + +echo "" +echo "${CYAN}╔══════════════════════════════════════════════════════════════╗${NC}" +echo "${CYAN}║ SecuBox RPC/UBUS Debug & Analysis Tool ║${NC}" +echo "${CYAN}╚══════════════════════════════════════════════════════════════╝${NC}" +echo "" + +# ============================================ +# System Information +# ============================================ +print_section() { + echo "" + echo "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo "${BLUE} $1${NC}" + echo "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" +} + +print_ok() { + echo " ${GREEN}✓${NC} $1" +} + +print_warn() { + echo " ${YELLOW}⚠${NC} $1" +} + +print_error() { + echo " ${RED}✗${NC} $1" +} + +print_info() { + echo " ${CYAN}→${NC} $1" +} + +# ============================================ +# Check prerequisites +# ============================================ +print_section "System Prerequisites" + +# Check if running on OpenWrt +if [ -f /etc/openwrt_release ]; then + print_ok "Running on OpenWrt" + . /etc/openwrt_release + print_info "Version: $DISTRIB_DESCRIPTION" +else + print_warn "Not running on OpenWrt - some checks may fail" +fi + +# Check rpcd +if pgrep -x rpcd > /dev/null 2>&1; then + print_ok "rpcd is running (PID: $(pgrep -x rpcd))" +else + print_error "rpcd is NOT running!" + echo " Try: /etc/init.d/rpcd restart" +fi + +# Check uhttpd +if pgrep -x uhttpd > /dev/null 2>&1; then + print_ok "uhttpd is running" +else + print_warn "uhttpd not running (nginx mode?)" +fi + +# Check ubus socket +if [ -S /var/run/ubus/ubus.sock ]; then + print_ok "ubus socket exists" +else + print_error "ubus socket missing!" +fi + +# ============================================ +# List all ubus objects +# ============================================ +print_section "Available UBUS Objects" + +echo "" +echo " All registered ubus objects:" +echo " ${CYAN}─────────────────────────────${NC}" + +ubus list 2>/dev/null | while read obj; do + # Highlight luci objects + case "$obj" in + luci.*) + echo " ${GREEN}$obj${NC}" + ;; + *) + echo " $obj" + ;; + esac +done + +# Count luci objects +LUCI_COUNT=$(ubus list 2>/dev/null | grep -c "^luci\." || echo "0") +echo "" +print_info "Total LuCI objects registered: $LUCI_COUNT" + +# ============================================ +# Check SecuBox modules +# ============================================ +print_section "SecuBox Modules Status" + +echo "" +printf " ${CYAN}%-25s %-10s %-10s %-10s %-10s${NC}\n" "MODULE" "UBUS" "RPCD" "ACL" "MENU" +echo " ─────────────────────────────────────────────────────────────────" + +check_module() { + local module="$1" + local ubus_name="luci.$module" + local rpcd_script="/usr/libexec/rpcd/$module" + local acl_file="/usr/share/rpcd/acl.d/luci-app-${module}.json" + local menu_file="/usr/share/luci/menu.d/luci-app-${module}.json" + + # Alternative paths + local rpcd_script_alt="/usr/libexec/rpcd/luci.$module" + local acl_file_alt="/usr/share/rpcd/acl.d/luci-${module}.json" + local menu_file_alt="/usr/share/luci/menu.d/luci-${module}.json" + + # Check ubus + local ubus_status="${RED}✗${NC}" + if ubus list "$ubus_name" > /dev/null 2>&1; then + ubus_status="${GREEN}✓${NC}" + fi + + # Check rpcd script + local rpcd_status="${RED}✗${NC}" + if [ -x "$rpcd_script" ] || [ -x "$rpcd_script_alt" ]; then + rpcd_status="${GREEN}✓${NC}" + elif [ -f "$rpcd_script" ] || [ -f "$rpcd_script_alt" ]; then + rpcd_status="${YELLOW}!${NC}" # exists but not executable + fi + + # Check ACL + local acl_status="${RED}✗${NC}" + if [ -f "$acl_file" ] || [ -f "$acl_file_alt" ]; then + acl_status="${GREEN}✓${NC}" + fi + + # Check menu + local menu_status="${RED}✗${NC}" + if [ -f "$menu_file" ] || [ -f "$menu_file_alt" ]; then + menu_status="${GREEN}✓${NC}" + fi + + printf " %-25s %-18s %-18s %-18s %-18s\n" \ + "$module" "$ubus_status" "$rpcd_status" "$acl_status" "$menu_status" +} + +for module in $MODULES; do + check_module "$module" +done + +echo "" +echo " ${CYAN}Legend:${NC} ${GREEN}✓${NC}=OK ${YELLOW}!${NC}=Issue ${RED}✗${NC}=Missing" + +# ============================================ +# Detailed module analysis +# ============================================ +TARGET_MODULE="$1" + +if [ -n "$TARGET_MODULE" ] && [ "$TARGET_MODULE" != "all" ]; then + print_section "Detailed Analysis: $TARGET_MODULE" + + MODULE="$TARGET_MODULE" + UBUS_NAME="luci.$MODULE" + + echo "" + echo " ${CYAN}UBUS Object: $UBUS_NAME${NC}" + echo " ─────────────────────────────────────" + + # Check if ubus object exists + if ubus list "$UBUS_NAME" > /dev/null 2>&1; then + print_ok "Object registered in ubus" + + echo "" + echo " Available methods:" + ubus -v list "$UBUS_NAME" 2>/dev/null | sed 's/^/ /' + + echo "" + echo " Testing 'status' method:" + if ubus call "$UBUS_NAME" status 2>/dev/null; then + print_ok "status method works" + else + print_error "status method failed" + fi + else + print_error "Object NOT registered in ubus" + echo "" + echo " ${YELLOW}Troubleshooting steps:${NC}" + echo "" + + # Check RPCD script + RPCD_PATHS=" +/usr/libexec/rpcd/$MODULE +/usr/libexec/rpcd/luci.$MODULE +/usr/libexec/rpcd/luci-$MODULE +" + echo " 1. Checking RPCD script locations:" + FOUND_RPCD="" + for path in $RPCD_PATHS; do + if [ -f "$path" ]; then + FOUND_RPCD="$path" + if [ -x "$path" ]; then + print_ok "Found executable: $path" + else + print_error "Found but NOT executable: $path" + echo " ${YELLOW}Fix: chmod +x $path${NC}" + fi + fi + done + + if [ -z "$FOUND_RPCD" ]; then + print_error "No RPCD script found!" + echo " Expected at: /usr/libexec/rpcd/$MODULE" + fi + + # Check ACL file + echo "" + echo " 2. Checking ACL configuration:" + ACL_PATHS=" +/usr/share/rpcd/acl.d/luci-app-${MODULE}.json +/usr/share/rpcd/acl.d/luci-${MODULE}.json +/usr/share/rpcd/acl.d/${MODULE}.json +" + FOUND_ACL="" + for path in $ACL_PATHS; do + if [ -f "$path" ]; then + FOUND_ACL="$path" + print_ok "Found ACL: $path" + + # Validate JSON + if command -v jsonfilter > /dev/null 2>&1; then + if jsonfilter -i "$path" -e '@' > /dev/null 2>&1; then + print_ok "JSON syntax valid" + else + print_error "Invalid JSON syntax!" + fi + fi + + # Check for correct ubus permission + if grep -q "\"$UBUS_NAME\"" "$path" 2>/dev/null; then + print_ok "ACL contains $UBUS_NAME permission" + else + print_warn "ACL might be missing $UBUS_NAME permission" + fi + fi + done + + if [ -z "$FOUND_ACL" ]; then + print_error "No ACL file found!" + fi + + # Test RPCD script directly + if [ -n "$FOUND_RPCD" ] && [ -x "$FOUND_RPCD" ]; then + echo "" + echo " 3. Testing RPCD script directly:" + + # Test list method + echo '{"method":"list"}' | "$FOUND_RPCD" 2>&1 | head -20 + fi + fi + + # Check menu entry + echo "" + echo " ${CYAN}Menu Configuration${NC}" + echo " ─────────────────────────────────────" + + MENU_PATHS=" +/usr/share/luci/menu.d/luci-app-${MODULE}.json +/usr/share/luci/menu.d/luci-${MODULE}.json +" + for path in $MENU_PATHS; do + if [ -f "$path" ]; then + print_ok "Found menu: $path" + echo "" + cat "$path" | sed 's/^/ /' + fi + done +fi + +# ============================================ +# Common fixes +# ============================================ +print_section "Common Fixes" + +echo "" +echo " ${YELLOW}If a module is not working:${NC}" +echo "" +echo " 1. ${CYAN}Restart rpcd:${NC}" +echo " /etc/init.d/rpcd restart" +echo "" +echo " 2. ${CYAN}Check script permissions:${NC}" +echo " chmod +x /usr/libexec/rpcd/" +echo "" +echo " 3. ${CYAN}Validate JSON files:${NC}" +echo " jsonfilter -i /usr/share/rpcd/acl.d/luci-app-.json -e '@'" +echo "" +echo " 4. ${CYAN}Check rpcd logs:${NC}" +echo " logread | grep rpcd" +echo "" +echo " 5. ${CYAN}Test ubus manually:${NC}" +echo " ubus call luci. status" +echo "" +echo " 6. ${CYAN}Reload LuCI:${NC}" +echo " rm -rf /tmp/luci-*" +echo " /etc/init.d/uhttpd restart" +echo "" + +# ============================================ +# Generate fix script +# ============================================ +if [ -n "$TARGET_MODULE" ] && [ "$TARGET_MODULE" != "all" ]; then + print_section "Auto-Fix Script for $TARGET_MODULE" + + FIX_SCRIPT="/tmp/fix-${TARGET_MODULE}.sh" + + cat > "$FIX_SCRIPT" << FIXEOF +#!/bin/sh +# Auto-generated fix script for $TARGET_MODULE + +echo "Fixing $TARGET_MODULE..." + +# Fix permissions +if [ -f /usr/libexec/rpcd/$TARGET_MODULE ]; then + chmod +x /usr/libexec/rpcd/$TARGET_MODULE + echo "✓ Fixed permissions for RPCD script" +fi + +if [ -f /usr/libexec/rpcd/luci.$TARGET_MODULE ]; then + chmod +x /usr/libexec/rpcd/luci.$TARGET_MODULE + echo "✓ Fixed permissions for RPCD script (alt)" +fi + +# Restart rpcd +/etc/init.d/rpcd restart +echo "✓ Restarted rpcd" + +# Clear LuCI cache +rm -rf /tmp/luci-* +echo "✓ Cleared LuCI cache" + +# Test +sleep 2 +if ubus list luci.$TARGET_MODULE > /dev/null 2>&1; then + echo "✓ Module $TARGET_MODULE is now registered!" + ubus -v list luci.$TARGET_MODULE +else + echo "✗ Module still not working. Check logs:" + echo " logread | grep -i rpcd" + echo " logread | grep -i $TARGET_MODULE" +fi +FIXEOF + + chmod +x "$FIX_SCRIPT" + + echo "" + echo " Generated fix script: ${GREEN}$FIX_SCRIPT${NC}" + echo "" + echo " Run it with: ${CYAN}sh $FIX_SCRIPT${NC}" + echo "" +fi + +# ============================================ +# Summary +# ============================================ +print_section "Quick Commands" + +echo "" +echo " ${CYAN}Debug specific module:${NC}" +echo " ./secubox-debug.sh vhost-manager" +echo "" +echo " ${CYAN}List all ubus objects:${NC}" +echo " ubus list | grep luci" +echo "" +echo " ${CYAN}Test RPC call:${NC}" +echo " ubus call luci.vhost-manager status" +echo "" +echo " ${CYAN}View RPCD logs:${NC}" +echo " logread | grep -E '(rpcd|ubus)'" +echo "" +echo " ${CYAN}Full restart:${NC}" +echo " /etc/init.d/rpcd restart && rm -rf /tmp/luci-* && /etc/init.d/uhttpd restart" +echo "" + +echo "${CYAN}╔══════════════════════════════════════════════════════════════╗${NC}" +echo "${CYAN}║ Debug Complete ║${NC}" +echo "${CYAN}╚══════════════════════════════════════════════════════════════╝${NC}" +echo "" diff --git a/secubox-repair.sh b/secubox-repair.sh new file mode 100755 index 0000000..9128543 --- /dev/null +++ b/secubox-repair.sh @@ -0,0 +1,1109 @@ +#!/bin/bash +# +# secubox-repair.sh +# ================= +# Script générique pour vérifier et réparer tous les modules SecuBox +# Exécuter depuis Linux sur le dossier contenant les packages luci-app-* +# +# Usage: +# ./secubox-repair.sh # Vérification seulement +# ./secubox-repair.sh --fix # Vérification + réparation +# ./secubox-repair.sh --fix --deploy # + déploiement sur routeur +# ./secubox-repair.sh --help # Aide +# +# Auteur: CyberMind.fr +# License: Apache-2.0 +# + +set -e + +# ============================================ +# Configuration +# ============================================ +VERSION="2.0.0" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +WORK_DIR="${SCRIPT_DIR}" + +# Router SSH config (modifiable) +ROUTER_HOST="${ROUTER_HOST:-192.168.1.1}" +ROUTER_USER="${ROUTER_USER:-root}" +ROUTER_PORT="${ROUTER_PORT:-22}" + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +CYAN='\033[0;36m' +MAGENTA='\033[0;35m' +BOLD='\033[1m' +NC='\033[0m' + +# Counters +ERRORS=0 +WARNINGS=0 +FIXES=0 +MODULES_CHECKED=0 + +# Options +DO_FIX=false +DO_DEPLOY=false +VERBOSE=false + +# ============================================ +# SecuBox Modules Definition +# ============================================ +declare -A MODULE_INFO=( + ["secubox"]="SecuBox Hub|Central dashboard for all modules|+luci-base +rpcd +curl +jq" + ["crowdsec-dashboard"]="CrowdSec Dashboard|Collaborative threat intelligence|+luci-base +rpcd +crowdsec" + ["netdata-dashboard"]="Netdata Dashboard|Real-time system monitoring|+luci-base +rpcd +netdata" + ["netifyd-dashboard"]="Netifyd Dashboard|Deep packet inspection|+luci-base +rpcd +netifyd" + ["wireguard-dashboard"]="WireGuard Dashboard|VPN management with QR codes|+luci-base +rpcd +wireguard-tools +qrencode" + ["network-modes"]="Network Modes|Network topology switcher|+luci-base +rpcd" + ["client-guardian"]="Client Guardian|NAC and captive portal|+luci-base +rpcd +nodogsplash" + ["system-hub"]="System Hub|System control center|+luci-base +rpcd" + ["bandwidth-manager"]="Bandwidth Manager|QoS and quota management|+luci-base +rpcd +tc-full +kmod-sched-cake" + ["auth-guardian"]="Auth Guardian|OAuth and voucher portal|+luci-base +rpcd +curl" + ["media-flow"]="Media Flow|Streaming detection|+luci-base +rpcd +netifyd" + ["vhost-manager"]="VHost Manager|Reverse proxy and SSL|+luci-base +rpcd +nginx-ssl" + ["cdn-cache"]="CDN Cache|Local content cache|+luci-base +rpcd +nginx" + ["traffic-shaper"]="Traffic Shaper|Advanced traffic control|+luci-base +rpcd +tc-full" +) + +# ============================================ +# Helper Functions +# ============================================ +print_header() { + echo "" + echo -e "${CYAN}╔══════════════════════════════════════════════════════════════════════╗${NC}" + echo -e "${CYAN}║${NC} ${BOLD}$1${NC}" + echo -e "${CYAN}╚══════════════════════════════════════════════════════════════════════╝${NC}" +} + +print_section() { + echo "" + echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo -e "${BLUE} $1${NC}" + echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" +} + +print_module() { + echo "" + echo -e "${MAGENTA}┌──────────────────────────────────────────────────────────────────────┐${NC}" + echo -e "${MAGENTA}│${NC} 📦 ${BOLD}$1${NC}" + echo -e "${MAGENTA}└──────────────────────────────────────────────────────────────────────┘${NC}" +} + +ok() { + echo -e " ${GREEN}✓${NC} $1" +} + +warn() { + echo -e " ${YELLOW}⚠${NC} $1" + ((WARNINGS++)) +} + +error() { + echo -e " ${RED}✗${NC} $1" + ((ERRORS++)) +} + +info() { + echo -e " ${CYAN}→${NC} $1" +} + +fixed() { + echo -e " ${GREEN}🔧${NC} $1" + ((FIXES++)) +} + +verbose() { + if $VERBOSE; then + echo -e " ${CYAN} $1${NC}" + fi +} + +# ============================================ +# Validation Functions +# ============================================ + +# Validate JSON file +validate_json() { + local file="$1" + if command -v jq &> /dev/null; then + if jq empty "$file" 2>/dev/null; then + return 0 + else + return 1 + fi + elif command -v python3 &> /dev/null; then + if python3 -c "import json; json.load(open('$file'))" 2>/dev/null; then + return 0 + else + return 1 + fi + else + # Can't validate, assume OK + return 0 + fi +} + +# Validate JavaScript file +validate_js() { + local file="$1" + if command -v node &> /dev/null; then + if node --check "$file" 2>/dev/null; then + return 0 + else + return 1 + fi + else + # Basic syntax check with grep + if grep -qE '(function|const|let|var|class|import|export)' "$file"; then + return 0 + else + return 1 + fi + fi +} + +# ============================================ +# Check Functions +# ============================================ + +check_makefile() { + local pkg_dir="$1" + local pkg_name="$2" + local makefile="$pkg_dir/Makefile" + + info "Checking Makefile..." + + if [[ ! -f "$makefile" ]]; then + error "Makefile missing!" + if $DO_FIX; then + generate_makefile "$pkg_dir" "$pkg_name" + fi + return 1 + fi + + # Check required fields + local required_fields=("PKG_NAME" "PKG_VERSION" "PKG_RELEASE" "PKG_LICENSE") + local missing_fields=() + + for field in "${required_fields[@]}"; do + if ! grep -q "^${field}:=" "$makefile"; then + missing_fields+=("$field") + fi + done + + if [[ ${#missing_fields[@]} -gt 0 ]]; then + warn "Missing fields: ${missing_fields[*]}" + fi + + # Check PKG_NAME matches directory + local makefile_pkg_name=$(grep "^PKG_NAME:=" "$makefile" | cut -d'=' -f2) + if [[ "$makefile_pkg_name" != "luci-app-$pkg_name" ]]; then + error "PKG_NAME mismatch: expected 'luci-app-$pkg_name', got '$makefile_pkg_name'" + if $DO_FIX; then + sed -i "s/^PKG_NAME:=.*/PKG_NAME:=luci-app-$pkg_name/" "$makefile" + fixed "PKG_NAME corrected" + fi + fi + + # Check for luci.mk include + if ! grep -q 'include.*feeds/luci/luci\.mk' "$makefile"; then + if grep -q 'include.*package\.mk' "$makefile"; then + warn "Uses package.mk instead of luci.mk (may be intentional)" + else + error "Missing include for luci.mk" + if $DO_FIX; then + generate_makefile "$pkg_dir" "$pkg_name" + fi + fi + else + ok "Makefile valid with luci.mk" + fi + + return 0 +} + +check_rpcd_script() { + local pkg_dir="$1" + local pkg_name="$2" + local rpcd_dir="$pkg_dir/root/usr/libexec/rpcd" + local rpcd_script="$rpcd_dir/$pkg_name" + + info "Checking RPCD script..." + + # Check for rpcd directory + if [[ ! -d "$rpcd_dir" ]]; then + warn "RPCD directory missing: $rpcd_dir" + if $DO_FIX; then + mkdir -p "$rpcd_dir" + fixed "Created RPCD directory" + fi + fi + + # Check for rpcd script + if [[ ! -f "$rpcd_script" ]]; then + error "RPCD script missing: $rpcd_script" + if $DO_FIX; then + generate_rpcd_script "$pkg_dir" "$pkg_name" + fi + return 1 + fi + + # Check if executable + if [[ ! -x "$rpcd_script" ]]; then + warn "RPCD script not executable" + if $DO_FIX; then + chmod +x "$rpcd_script" + fixed "Made RPCD script executable" + fi + fi + + # Check shebang + local first_line=$(head -1 "$rpcd_script") + if [[ "$first_line" != "#!/bin/sh" && "$first_line" != "#!/bin/bash" ]]; then + warn "RPCD script missing proper shebang" + fi + + # Check for required functions + if ! grep -q 'json_init\|json_add' "$rpcd_script"; then + warn "RPCD script may be missing JSON functions" + fi + + # Check list and call handlers + if ! grep -q 'case.*list' "$rpcd_script"; then + error "RPCD script missing 'list' handler" + fi + + if ! grep -q 'case.*call' "$rpcd_script"; then + error "RPCD script missing 'call' handler" + fi + + # Check for status method + if ! grep -q 'status' "$rpcd_script"; then + warn "RPCD script missing 'status' method" + fi + + ok "RPCD script exists" + return 0 +} + +check_acl_file() { + local pkg_dir="$1" + local pkg_name="$2" + local acl_dir="$pkg_dir/root/usr/share/rpcd/acl.d" + local acl_file="$acl_dir/luci-app-${pkg_name}.json" + + info "Checking ACL file..." + + # Check for acl directory + if [[ ! -d "$acl_dir" ]]; then + warn "ACL directory missing" + if $DO_FIX; then + mkdir -p "$acl_dir" + fixed "Created ACL directory" + fi + fi + + # Check for acl file + if [[ ! -f "$acl_file" ]]; then + error "ACL file missing: $acl_file" + if $DO_FIX; then + generate_acl_file "$pkg_dir" "$pkg_name" + fi + return 1 + fi + + # Validate JSON + if ! validate_json "$acl_file"; then + error "ACL file has invalid JSON syntax" + if $DO_FIX; then + generate_acl_file "$pkg_dir" "$pkg_name" + fi + return 1 + fi + + # Check for correct ubus permission + local ubus_name="luci.$pkg_name" + if ! grep -q "$ubus_name" "$acl_file"; then + warn "ACL may be missing '$ubus_name' permission" + if $DO_FIX; then + generate_acl_file "$pkg_dir" "$pkg_name" + fi + fi + + ok "ACL file valid" + return 0 +} + +check_menu_file() { + local pkg_dir="$1" + local pkg_name="$2" + local menu_dir="$pkg_dir/root/usr/share/luci/menu.d" + local menu_file="$menu_dir/luci-app-${pkg_name}.json" + + info "Checking Menu file..." + + # Check for menu directory + if [[ ! -d "$menu_dir" ]]; then + warn "Menu directory missing" + if $DO_FIX; then + mkdir -p "$menu_dir" + fixed "Created Menu directory" + fi + fi + + # Check for menu file + if [[ ! -f "$menu_file" ]]; then + error "Menu file missing: $menu_file" + if $DO_FIX; then + generate_menu_file "$pkg_dir" "$pkg_name" + fi + return 1 + fi + + # Validate JSON + if ! validate_json "$menu_file"; then + error "Menu file has invalid JSON syntax" + if $DO_FIX; then + generate_menu_file "$pkg_dir" "$pkg_name" + fi + return 1 + fi + + ok "Menu file valid" + return 0 +} + +check_view_files() { + local pkg_dir="$1" + local pkg_name="$2" + local view_dir="$pkg_dir/htdocs/luci-static/resources/view" + + info "Checking View files..." + + # Check htdocs structure + if [[ ! -d "$pkg_dir/htdocs/luci-static/resources" ]]; then + warn "htdocs structure missing" + if $DO_FIX; then + mkdir -p "$pkg_dir/htdocs/luci-static/resources/view/${pkg_name//-/_}" + fixed "Created htdocs structure" + fi + fi + + # Find JS view files + local js_files=$(find "$pkg_dir/htdocs" -name "*.js" 2>/dev/null | wc -l) + if [[ $js_files -eq 0 ]]; then + warn "No JavaScript view files found" + if $DO_FIX; then + generate_view_file "$pkg_dir" "$pkg_name" + fi + else + # Validate each JS file + while IFS= read -r js_file; do + if [[ -n "$js_file" ]]; then + if validate_js "$js_file"; then + verbose "Valid: $js_file" + else + error "Invalid JS syntax: $js_file" + fi + fi + done < <(find "$pkg_dir/htdocs" -name "*.js" 2>/dev/null) + ok "Found $js_files JavaScript view file(s)" + fi + + return 0 +} + +check_config_file() { + local pkg_dir="$1" + local pkg_name="$2" + local pkg_name_underscore="${pkg_name//-/_}" + local config_dir="$pkg_dir/root/etc/config" + local config_file="$config_dir/$pkg_name_underscore" + + info "Checking UCI config..." + + if [[ ! -d "$config_dir" ]]; then + if $DO_FIX; then + mkdir -p "$config_dir" + fi + fi + + if [[ ! -f "$config_file" ]]; then + warn "UCI config missing (optional)" + if $DO_FIX; then + generate_config_file "$pkg_dir" "$pkg_name" + fi + else + ok "UCI config exists" + fi + + return 0 +} + +check_malformed_dirs() { + local pkg_dir="$1" + local pkg_name="$2" + + info "Checking for malformed directories..." + + # Check for {htdocs or other malformed dirs + local malformed=$(find "$pkg_dir" -type d -name '{*' 2>/dev/null) + + if [[ -n "$malformed" ]]; then + error "Found malformed directories:" + echo "$malformed" | while read -r dir; do + echo " - $dir" + if $DO_FIX; then + rm -rf "$dir" + fixed "Removed: $dir" + fi + done + else + ok "No malformed directories" + fi + + return 0 +} + +check_permissions() { + local pkg_dir="$1" + local pkg_name="$2" + + info "Checking file permissions..." + + local files_fixed=0 + + # RPCD scripts + while IFS= read -r script; do + if [[ -n "$script" && ! -x "$script" ]]; then + warn "Not executable: $script" + if $DO_FIX; then + chmod +x "$script" + ((files_fixed++)) + fi + fi + done < <(find "$pkg_dir" -path "*/usr/libexec/rpcd/*" -type f 2>/dev/null) + + # Init scripts + while IFS= read -r script; do + if [[ -n "$script" && ! -x "$script" ]]; then + warn "Not executable: $script" + if $DO_FIX; then + chmod +x "$script" + ((files_fixed++)) + fi + fi + done < <(find "$pkg_dir" -path "*/etc/init.d/*" -type f 2>/dev/null) + + # UCI defaults + while IFS= read -r script; do + if [[ -n "$script" && ! -x "$script" ]]; then + warn "Not executable: $script" + if $DO_FIX; then + chmod +x "$script" + ((files_fixed++)) + fi + fi + done < <(find "$pkg_dir" -path "*/etc/uci-defaults/*" -type f 2>/dev/null) + + if [[ $files_fixed -gt 0 ]]; then + fixed "Fixed permissions on $files_fixed file(s)" + else + ok "Permissions OK" + fi + + return 0 +} + +# ============================================ +# Generate Functions +# ============================================ + +generate_makefile() { + local pkg_dir="$1" + local pkg_name="$2" + local makefile="$pkg_dir/Makefile" + + # Get module info + local info="${MODULE_INFO[$pkg_name]}" + local title=$(echo "$info" | cut -d'|' -f1) + local desc=$(echo "$info" | cut -d'|' -f2) + local depends=$(echo "$info" | cut -d'|' -f3) + + # Defaults if not in MODULE_INFO + title="${title:-SecuBox Module}" + desc="${desc:-SecuBox LuCI application}" + depends="${depends:-+luci-base +rpcd}" + + cat > "$makefile" << MAKEFILE_EOF +include \$(TOPDIR)/rules.mk + +PKG_NAME:=luci-app-${pkg_name} +PKG_VERSION:=${VERSION} +PKG_RELEASE:=1 +PKG_LICENSE:=Apache-2.0 +PKG_MAINTAINER:=CyberMind + +LUCI_TITLE:=LuCI - ${title} +LUCI_DESCRIPTION:=${desc} +LUCI_DEPENDS:=${depends} +LUCI_PKGARCH:=all + +include \$(TOPDIR)/feeds/luci/luci.mk + +# call BuildPackage - OpenWrt buildroot +MAKEFILE_EOF + + fixed "Generated Makefile" +} + +generate_rpcd_script() { + local pkg_dir="$1" + local pkg_name="$2" + local pkg_name_underscore="${pkg_name//-/_}" + local rpcd_dir="$pkg_dir/root/usr/libexec/rpcd" + local rpcd_script="$rpcd_dir/$pkg_name" + + mkdir -p "$rpcd_dir" + + cat > "$rpcd_script" << 'RPCD_EOF' +#!/bin/sh +# RPCD backend for PKG_NAME_PLACEHOLDER +# Provides ubus interface: luci.PKG_NAME_PLACEHOLDER + +. /lib/functions.sh +. /usr/share/libubox/jshn.sh + +# Configuration +MODULE_NAME="PKG_NAME_PLACEHOLDER" +MODULE_VERSION="VERSION_PLACEHOLDER" +UCI_CONFIG="PKG_NAME_UNDERSCORE_PLACEHOLDER" + +# Initialize JSON output +json_init + +case "$1" in + list) + # List available methods + json_add_object "status" + json_close_object + + json_add_object "get_config" + json_close_object + + json_add_object "set_config" + json_add_string "config" "object" + json_close_object + + json_add_object "get_stats" + json_close_object + + json_dump + ;; + + call) + case "$2" in + status) + # Return module status + json_add_string "module" "$MODULE_NAME" + json_add_string "version" "$MODULE_VERSION" + json_add_boolean "enabled" 1 + json_add_string "status" "running" + json_add_string "timestamp" "$(date -Iseconds 2>/dev/null || date)" + json_dump + ;; + + get_config) + # Return current configuration + json_add_object "config" + + if [ -f "/etc/config/$UCI_CONFIG" ]; then + config_load "$UCI_CONFIG" + json_add_boolean "enabled" 1 + else + json_add_boolean "enabled" 0 + fi + + json_close_object + json_dump + ;; + + set_config) + # Set configuration + read -r input + + # Parse and apply config + # uci set $UCI_CONFIG... + # uci commit $UCI_CONFIG + + json_add_boolean "success" 1 + json_add_string "message" "Configuration updated" + json_dump + ;; + + get_stats) + # Return statistics + json_add_object "stats" + json_add_int "uptime" "$(cat /proc/uptime 2>/dev/null | cut -d. -f1 || echo 0)" + json_add_string "timestamp" "$(date -Iseconds 2>/dev/null || date)" + json_close_object + json_dump + ;; + + *) + json_add_int "error" -32601 + json_add_string "message" "Method not found: $2" + json_dump + ;; + esac + ;; + + *) + echo "Usage: $0 {list|call}" >&2 + exit 1 + ;; +esac +RPCD_EOF + + # Replace placeholders + sed -i "s/PKG_NAME_PLACEHOLDER/$pkg_name/g" "$rpcd_script" + sed -i "s/PKG_NAME_UNDERSCORE_PLACEHOLDER/$pkg_name_underscore/g" "$rpcd_script" + sed -i "s/VERSION_PLACEHOLDER/$VERSION/g" "$rpcd_script" + + chmod +x "$rpcd_script" + fixed "Generated RPCD script" +} + +generate_acl_file() { + local pkg_dir="$1" + local pkg_name="$2" + local pkg_name_underscore="${pkg_name//-/_}" + local acl_dir="$pkg_dir/root/usr/share/rpcd/acl.d" + local acl_file="$acl_dir/luci-app-${pkg_name}.json" + + mkdir -p "$acl_dir" + + cat > "$acl_file" << ACL_EOF +{ + "luci-app-${pkg_name}": { + "description": "Grant access to LuCI app ${pkg_name}", + "read": { + "ubus": { + "luci.${pkg_name}": [ + "status", + "get_config", + "get_stats" + ] + }, + "uci": ["${pkg_name_underscore}"] + }, + "write": { + "ubus": { + "luci.${pkg_name}": [ + "set_config" + ] + }, + "uci": ["${pkg_name_underscore}"] + } + } +} +ACL_EOF + + fixed "Generated ACL file" +} + +generate_menu_file() { + local pkg_dir="$1" + local pkg_name="$2" + local pkg_name_underscore="${pkg_name//-/_}" + local menu_dir="$pkg_dir/root/usr/share/luci/menu.d" + local menu_file="$menu_dir/luci-app-${pkg_name}.json" + + mkdir -p "$menu_dir" + + # Generate title from pkg_name + local title=$(echo "$pkg_name" | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) tolower(substr($i,2))}1') + + # Get from MODULE_INFO if available + if [[ -n "${MODULE_INFO[$pkg_name]}" ]]; then + title=$(echo "${MODULE_INFO[$pkg_name]}" | cut -d'|' -f1) + fi + + cat > "$menu_file" << MENU_EOF +{ + "admin/services/${pkg_name_underscore}": { + "title": "${title}", + "order": 50, + "action": { + "type": "view", + "path": "${pkg_name_underscore}/main" + }, + "depends": { + "acl": ["luci-app-${pkg_name}"], + "uci": { + "${pkg_name_underscore}": true + } + } + } +} +MENU_EOF + + fixed "Generated Menu file" +} + +generate_view_file() { + local pkg_dir="$1" + local pkg_name="$2" + local pkg_name_underscore="${pkg_name//-/_}" + local view_dir="$pkg_dir/htdocs/luci-static/resources/view/${pkg_name_underscore}" + local view_file="$view_dir/main.js" + + mkdir -p "$view_dir" + + # Generate title + local title=$(echo "$pkg_name" | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) tolower(substr($i,2))}1') + + if [[ -n "${MODULE_INFO[$pkg_name]}" ]]; then + title=$(echo "${MODULE_INFO[$pkg_name]}" | cut -d'|' -f1) + fi + + cat > "$view_file" << 'VIEW_EOF' +'use strict'; +'require view'; +'require rpc'; +'require ui'; +'require form'; + +var callStatus = rpc.declare({ + object: 'luci.PKG_NAME_PLACEHOLDER', + method: 'status', + expect: { } +}); + +return view.extend({ + load: function() { + return Promise.all([ + callStatus() + ]); + }, + + render: function(data) { + var status = data[0] || {}; + var m, s, o; + + m = new form.Map('PKG_NAME_UNDERSCORE_PLACEHOLDER', _('TITLE_PLACEHOLDER'), + _('DESCRIPTION_PLACEHOLDER')); + + s = m.section(form.NamedSection, 'status', 'status', _('Status')); + s.anonymous = true; + + o = s.option(form.DummyValue, '_status', _('Module Status')); + o.rawhtml = true; + o.cfgvalue = function() { + var running = status.status === 'running'; + return '' + + (running ? _('Running') : _('Stopped')) + ''; + }; + + o = s.option(form.DummyValue, '_version', _('Version')); + o.cfgvalue = function() { + return status.version || 'Unknown'; + }; + + return m.render(); + }, + + handleSaveApply: null, + handleSave: null, + handleReset: null +}); +VIEW_EOF + + # Replace placeholders + sed -i "s/PKG_NAME_PLACEHOLDER/$pkg_name/g" "$view_file" + sed -i "s/PKG_NAME_UNDERSCORE_PLACEHOLDER/$pkg_name_underscore/g" "$view_file" + sed -i "s/TITLE_PLACEHOLDER/$title/g" "$view_file" + sed -i "s/DESCRIPTION_PLACEHOLDER/SecuBox $title module/g" "$view_file" + + fixed "Generated View file" +} + +generate_config_file() { + local pkg_dir="$1" + local pkg_name="$2" + local pkg_name_underscore="${pkg_name//-/_}" + local config_dir="$pkg_dir/root/etc/config" + local config_file="$config_dir/$pkg_name_underscore" + + mkdir -p "$config_dir" + + cat > "$config_file" << CONFIG_EOF +config global 'global' + option enabled '1' + option version '${VERSION}' +CONFIG_EOF + + fixed "Generated UCI config" +} + +# ============================================ +# Deploy Functions +# ============================================ + +deploy_to_router() { + local pkg_dir="$1" + local pkg_name="$2" + + print_section "Deploying $pkg_name to router ($ROUTER_HOST)" + + # Test SSH connection + if ! ssh -q -o ConnectTimeout=5 -p "$ROUTER_PORT" "$ROUTER_USER@$ROUTER_HOST" "echo ok" &>/dev/null; then + error "Cannot connect to router via SSH" + echo " Check: ROUTER_HOST=$ROUTER_HOST ROUTER_USER=$ROUTER_USER ROUTER_PORT=$ROUTER_PORT" + return 1 + fi + + ok "SSH connection OK" + + # Create directories on router + info "Creating directories..." + ssh -p "$ROUTER_PORT" "$ROUTER_USER@$ROUTER_HOST" "mkdir -p /usr/libexec/rpcd /usr/share/rpcd/acl.d /usr/share/luci/menu.d" + + # Deploy RPCD script + local rpcd_script="$pkg_dir/root/usr/libexec/rpcd/$pkg_name" + if [[ -f "$rpcd_script" ]]; then + info "Deploying RPCD script..." + scp -P "$ROUTER_PORT" "$rpcd_script" "$ROUTER_USER@$ROUTER_HOST:/usr/libexec/rpcd/" + ssh -p "$ROUTER_PORT" "$ROUTER_USER@$ROUTER_HOST" "chmod +x /usr/libexec/rpcd/$pkg_name" + ok "RPCD script deployed" + fi + + # Deploy ACL file + local acl_file="$pkg_dir/root/usr/share/rpcd/acl.d/luci-app-${pkg_name}.json" + if [[ -f "$acl_file" ]]; then + info "Deploying ACL file..." + scp -P "$ROUTER_PORT" "$acl_file" "$ROUTER_USER@$ROUTER_HOST:/usr/share/rpcd/acl.d/" + ok "ACL file deployed" + fi + + # Deploy Menu file + local menu_file="$pkg_dir/root/usr/share/luci/menu.d/luci-app-${pkg_name}.json" + if [[ -f "$menu_file" ]]; then + info "Deploying Menu file..." + scp -P "$ROUTER_PORT" "$menu_file" "$ROUTER_USER@$ROUTER_HOST:/usr/share/luci/menu.d/" + ok "Menu file deployed" + fi + + # Deploy View files + if [[ -d "$pkg_dir/htdocs" ]]; then + info "Deploying View files..." + ssh -p "$ROUTER_PORT" "$ROUTER_USER@$ROUTER_HOST" "mkdir -p /www/luci-static/resources/view" + scp -r -P "$ROUTER_PORT" "$pkg_dir/htdocs/luci-static/resources/"* "$ROUTER_USER@$ROUTER_HOST:/www/luci-static/resources/" 2>/dev/null || true + ok "View files deployed" + fi + + # Restart rpcd + info "Restarting rpcd..." + ssh -p "$ROUTER_PORT" "$ROUTER_USER@$ROUTER_HOST" "/etc/init.d/rpcd restart" + + # Clear LuCI cache + info "Clearing LuCI cache..." + ssh -p "$ROUTER_PORT" "$ROUTER_USER@$ROUTER_HOST" "rm -rf /tmp/luci-*" + + # Test + sleep 2 + info "Testing ubus registration..." + if ssh -p "$ROUTER_PORT" "$ROUTER_USER@$ROUTER_HOST" "ubus list luci.$pkg_name" &>/dev/null; then + ok "luci.$pkg_name is registered!" + + # Test status call + info "Testing status call..." + ssh -p "$ROUTER_PORT" "$ROUTER_USER@$ROUTER_HOST" "ubus call luci.$pkg_name status" + else + error "luci.$pkg_name is NOT registered" + echo " Check logs: ssh $ROUTER_USER@$ROUTER_HOST logread | grep rpcd" + fi +} + +# ============================================ +# Main Check Function +# ============================================ + +check_module() { + local pkg_dir="$1" + local pkg_name=$(basename "$pkg_dir" | sed 's/^luci-app-//') + + print_module "luci-app-$pkg_name" + + ((MODULES_CHECKED++)) + + # Run all checks + check_malformed_dirs "$pkg_dir" "$pkg_name" + check_makefile "$pkg_dir" "$pkg_name" + check_rpcd_script "$pkg_dir" "$pkg_name" + check_acl_file "$pkg_dir" "$pkg_name" + check_menu_file "$pkg_dir" "$pkg_name" + check_view_files "$pkg_dir" "$pkg_name" + check_config_file "$pkg_dir" "$pkg_name" + check_permissions "$pkg_dir" "$pkg_name" + + # Deploy if requested + if $DO_DEPLOY; then + deploy_to_router "$pkg_dir" "$pkg_name" + fi +} + +# ============================================ +# Summary +# ============================================ + +print_summary() { + print_header "Summary" + + echo "" + echo -e " ${BOLD}Modules checked:${NC} $MODULES_CHECKED" + echo -e " ${GREEN}Fixes applied:${NC} $FIXES" + echo -e " ${YELLOW}Warnings:${NC} $WARNINGS" + echo -e " ${RED}Errors:${NC} $ERRORS" + echo "" + + if [[ $ERRORS -gt 0 ]]; then + echo -e " ${RED}⚠ Some errors remain. Run with --fix to repair.${NC}" + elif [[ $WARNINGS -gt 0 ]]; then + echo -e " ${YELLOW}⚠ Some warnings. Review and fix manually if needed.${NC}" + else + echo -e " ${GREEN}✓ All modules validated successfully!${NC}" + fi + + echo "" + + if ! $DO_DEPLOY && $DO_FIX; then + echo -e " ${CYAN}Tip: Use --deploy to push fixes to router${NC}" + echo "" + fi +} + +# ============================================ +# Help +# ============================================ + +show_help() { + cat << EOF +${BOLD}SecuBox Module Repair Tool v${VERSION}${NC} + +Usage: $0 [OPTIONS] [MODULE_NAME] + +Options: + --fix Apply automatic fixes + --deploy Deploy fixed modules to router (requires SSH) + --verbose Show detailed output + --help Show this help + +Environment Variables: + ROUTER_HOST Router IP address (default: 192.168.1.1) + ROUTER_USER SSH user (default: root) + ROUTER_PORT SSH port (default: 22) + +Examples: + $0 Check all modules + $0 --fix Check and fix all modules + $0 --fix vhost-manager Fix specific module + $0 --fix --deploy Fix and deploy to router + + ROUTER_HOST=192.168.8.191 $0 --fix --deploy + +EOF +} + +# ============================================ +# Main +# ============================================ + +main() { + # Parse arguments + local target_module="" + + while [[ $# -gt 0 ]]; do + case "$1" in + --fix) + DO_FIX=true + shift + ;; + --deploy) + DO_DEPLOY=true + shift + ;; + --verbose|-v) + VERBOSE=true + shift + ;; + --help|-h) + show_help + exit 0 + ;; + -*) + echo "Unknown option: $1" + show_help + exit 1 + ;; + *) + target_module="$1" + shift + ;; + esac + done + + # Header + print_header "SecuBox Module Repair Tool v${VERSION}" + + echo "" + echo -e " ${CYAN}Working directory:${NC} $WORK_DIR" + echo -e " ${CYAN}Fix mode:${NC} $($DO_FIX && echo 'ON' || echo 'OFF')" + echo -e " ${CYAN}Deploy mode:${NC} $($DO_DEPLOY && echo 'ON' || echo 'OFF')" + + if $DO_DEPLOY; then + echo -e " ${CYAN}Router:${NC} $ROUTER_USER@$ROUTER_HOST:$ROUTER_PORT" + fi + + # Find modules + cd "$WORK_DIR" + + if [[ -n "$target_module" ]]; then + # Check specific module + local pkg_dir="$WORK_DIR/luci-app-$target_module" + if [[ ! -d "$pkg_dir" ]]; then + pkg_dir="$WORK_DIR/$target_module" + fi + + if [[ -d "$pkg_dir" ]]; then + check_module "$pkg_dir" + else + error "Module not found: $target_module" + exit 1 + fi + else + # Check all modules + for pkg_dir in luci-app-*/; do + if [[ -d "$pkg_dir" ]]; then + check_module "${pkg_dir%/}" + fi + done + fi + + # Summary + print_summary + + # Exit code + if [[ $ERRORS -gt 0 ]]; then + exit 1 + fi + exit 0 +} + +# Run +main "$@" diff --git a/secubox-tools/acl/luci-app-vhost-manager.json b/secubox-tools/acl/luci-app-vhost-manager.json new file mode 100644 index 0000000..c5fdce2 --- /dev/null +++ b/secubox-tools/acl/luci-app-vhost-manager.json @@ -0,0 +1,35 @@ +{ + "luci-app-vhost-manager": { + "description": "Grant access to VHost Manager", + "read": { + "ubus": { + "luci.vhost-manager": [ + "status", + "get_vhosts", + "get_vhost", + "get_certificates", + "test_config" + ] + }, + "uci": ["vhost_manager", "nginx"], + "file": { + "/etc/nginx/conf.d/*": ["read"], + "/etc/acme/*": ["read"] + } + }, + "write": { + "ubus": { + "luci.vhost-manager": [ + "add_vhost", + "delete_vhost", + "request_certificate", + "reload_nginx" + ] + }, + "uci": ["vhost_manager", "nginx"], + "file": { + "/etc/nginx/conf.d/*": ["write"] + } + } + } +} diff --git a/secubox-tools/cleanup-packages.sh b/secubox-tools/cleanup-packages.sh new file mode 100755 index 0000000..bef20f4 --- /dev/null +++ b/secubox-tools/cleanup-packages.sh @@ -0,0 +1,147 @@ +#!/bin/bash +# cleanup-packages.sh +# Script to fix common issues in SecuBox package structure + +set -e + +echo "🧹 SecuBox Package Cleanup Script" +echo "==================================" +echo "" + +ERRORS=0 +FIXES=0 + +# 1. Remove malformed {htdocs directories +echo "📁 Checking for malformed directories..." +for pkg in luci-app-*/; do + if [[ -d "${pkg}{htdocs" ]]; then + echo " ❌ Found malformed directory: ${pkg}{htdocs" + echo " → Removing..." + rm -rf "${pkg}{htdocs" + FIXES=$((FIXES + 1)) + fi +done + +# 2. Ensure htdocs structure exists +echo "" +echo "📁 Checking htdocs structure..." +for pkg in luci-app-*/; do + if [[ -d "$pkg" ]]; then + PKG_NAME=$(basename "$pkg") + + # Create htdocs structure if missing + if [[ ! -d "${pkg}htdocs/luci-static/resources/view" ]]; then + echo " ⚠️ Missing htdocs structure in $PKG_NAME" + mkdir -p "${pkg}htdocs/luci-static/resources/view" + FIXES=$((FIXES + 1)) + fi + fi +done + +# 3. Fix file permissions +echo "" +echo "🔐 Fixing file permissions..." +for pkg in luci-app-*/; do + # RPCD scripts + if [[ -d "${pkg}root/usr/libexec/rpcd" ]]; then + for script in "${pkg}root/usr/libexec/rpcd/"*; do + if [[ -f "$script" && ! -x "$script" ]]; then + echo " → Making executable: $script" + chmod +x "$script" + FIXES=$((FIXES + 1)) + fi + done + fi + + # Init scripts + if [[ -d "${pkg}root/etc/init.d" ]]; then + for script in "${pkg}root/etc/init.d/"*; do + if [[ -f "$script" && ! -x "$script" ]]; then + echo " → Making executable: $script" + chmod +x "$script" + FIXES=$((FIXES + 1)) + fi + done + fi + + # UCI defaults + if [[ -d "${pkg}root/etc/uci-defaults" ]]; then + for script in "${pkg}root/etc/uci-defaults/"*; do + if [[ -f "$script" && ! -x "$script" ]]; then + echo " → Making executable: $script" + chmod +x "$script" + FIXES=$((FIXES + 1)) + fi + done + fi +done + +# 4. Validate Makefiles +echo "" +echo "📋 Validating Makefiles..." +for makefile in luci-app-*/Makefile; do + if [[ -f "$makefile" ]]; then + PKG=$(dirname "$makefile") + PKG_NAME=$(basename "$PKG") + + # Check PKG_NAME matches directory + MAKEFILE_PKG_NAME=$(grep "^PKG_NAME:=" "$makefile" | cut -d'=' -f2) + if [[ "$MAKEFILE_PKG_NAME" != "$PKG_NAME" ]]; then + echo " ❌ PKG_NAME mismatch in $PKG_NAME" + echo " Directory: $PKG_NAME" + echo " Makefile: $MAKEFILE_PKG_NAME" + ERRORS=$((ERRORS + 1)) + fi + + # Check required fields + for field in PKG_VERSION PKG_RELEASE PKG_LICENSE; do + if ! grep -q "^${field}:=" "$makefile"; then + echo " ⚠️ Missing $field in $PKG_NAME/Makefile" + fi + done + + # Check include statement + if ! grep -q "include.*luci.mk" "$makefile"; then + echo " ⚠️ Missing 'include \$(TOPDIR)/feeds/luci/luci.mk' in $PKG_NAME" + fi + fi +done + +# 5. Check for required directories +echo "" +echo "📂 Checking required structure..." +for pkg in luci-app-*/; do + if [[ -d "$pkg" ]]; then + PKG_NAME=$(basename "$pkg") + + REQUIRED_DIRS=( + "root/usr/share/luci/menu.d" + "root/usr/share/rpcd/acl.d" + ) + + for dir in "${REQUIRED_DIRS[@]}"; do + if [[ ! -d "${pkg}${dir}" ]]; then + echo " ⚠️ Creating missing: ${PKG_NAME}/${dir}" + mkdir -p "${pkg}${dir}" + FIXES=$((FIXES + 1)) + fi + done + fi +done + +# 6. Summary +echo "" +echo "==================================" +echo "📊 Summary" +echo "==================================" +echo "Fixes applied: $FIXES" +echo "Errors found: $ERRORS" + +if [[ $ERRORS -gt 0 ]]; then + echo "" + echo "⚠️ Please fix the errors above manually" + exit 1 +fi + +echo "" +echo "✅ Cleanup complete!" diff --git a/secubox-tools/fix-makefiles.sh b/secubox-tools/fix-makefiles.sh new file mode 100755 index 0000000..8cb2d60 --- /dev/null +++ b/secubox-tools/fix-makefiles.sh @@ -0,0 +1,97 @@ +#!/bin/bash +# fix-makefiles.sh +# Script to fix Makefiles for OpenWrt LuCI packages + +set -e + +echo "🔧 SecuBox Makefile Fixer" +echo "=========================" +echo "" + +FIXED=0 +SKIPPED=0 + +for makefile in luci-app-*/Makefile; do + if [[ ! -f "$makefile" ]]; then + continue + fi + + PKG_DIR=$(dirname "$makefile") + PKG_NAME=$(basename "$PKG_DIR") + + echo "📦 Processing: $PKG_NAME" + + # Check if already has luci.mk include + if grep -q 'include.*feeds/luci/luci\.mk' "$makefile"; then + echo " ✅ Already has luci.mk include" + SKIPPED=$((SKIPPED + 1)) + continue + fi + + # Check if has package.mk include (alternative valid format) + if grep -q 'include.*package\.mk' "$makefile" && grep -q 'BuildPackage' "$makefile"; then + echo " ✅ Uses package.mk with BuildPackage (valid)" + SKIPPED=$((SKIPPED + 1)) + continue + fi + + # Need to fix - create backup first + cp "$makefile" "${makefile}.bak" + + # Extract existing values + PKG_VERSION=$(grep "^PKG_VERSION:=" "$makefile" | cut -d'=' -f2 || echo "1.0.0") + PKG_RELEASE=$(grep "^PKG_RELEASE:=" "$makefile" | cut -d'=' -f2 || echo "1") + PKG_LICENSE=$(grep "^PKG_LICENSE:=" "$makefile" | cut -d'=' -f2 || echo "Apache-2.0") + LUCI_TITLE=$(grep "^LUCI_TITLE:=" "$makefile" | cut -d'=' -f2- || echo "LuCI - $PKG_NAME") + LUCI_DEPENDS=$(grep "^LUCI_DEPENDS:=" "$makefile" | cut -d'=' -f2- || echo "+luci-base") + + # If no LUCI_TITLE, try to extract from define Package section + if [[ -z "$LUCI_TITLE" || "$LUCI_TITLE" == "LuCI - $PKG_NAME" ]]; then + TITLE_LINE=$(grep -A5 "define Package/" "$makefile" | grep "TITLE" | head -1 | cut -d'=' -f2-) + if [[ -n "$TITLE_LINE" ]]; then + LUCI_TITLE="$TITLE_LINE" + fi + fi + + # Generate new Makefile + cat > "$makefile" << MAKEFILE_EOF +include \$(TOPDIR)/rules.mk + +PKG_NAME:=${PKG_NAME} +PKG_VERSION:=${PKG_VERSION:-1.0.0} +PKG_RELEASE:=${PKG_RELEASE:-1} +PKG_LICENSE:=${PKG_LICENSE:-Apache-2.0} +PKG_MAINTAINER:=CyberMind + +LUCI_TITLE:=${LUCI_TITLE:-LuCI - SecuBox Module} +LUCI_DEPENDS:=${LUCI_DEPENDS:-+luci-base} +LUCI_PKGARCH:=all + +include \$(TOPDIR)/feeds/luci/luci.mk + +# call BuildPackage - OpenWrt buildance +MAKEFILE_EOF + + echo " 🔧 Fixed Makefile (backup: ${makefile}.bak)" + FIXED=$((FIXED + 1)) + +done + +echo "" +echo "=========================" +echo "📊 Summary" +echo "=========================" +echo "Fixed: $FIXED" +echo "Skipped: $SKIPPED" +echo "" + +if [[ $FIXED -gt 0 ]]; then + echo "⚠️ Review the fixed Makefiles and adjust LUCI_TITLE and LUCI_DEPENDS as needed" + echo "" + echo "📝 Example correct values:" + echo " LUCI_TITLE:=LuCI - CrowdSec Security Dashboard" + echo " LUCI_DEPENDS:=+luci-base +rpcd +curl" +fi + +echo "" +echo "✅ Done!" diff --git a/secubox-tools/generate-rpcd-files.sh b/secubox-tools/generate-rpcd-files.sh new file mode 100755 index 0000000..b41c962 --- /dev/null +++ b/secubox-tools/generate-rpcd-files.sh @@ -0,0 +1,269 @@ +#!/bin/sh +# generate-rpcd-files.sh +# Generate missing RPCD scripts and ACL files for SecuBox modules +# +# Usage: ./generate-rpcd-files.sh +# Example: ./generate-rpcd-files.sh vhost-manager + +MODULE="$1" + +if [ -z "$MODULE" ]; then + echo "Usage: $0 " + echo "Example: $0 vhost-manager" + exit 1 +fi + +# Convert module name for different uses +# vhost-manager -> vhost_manager (for shell variables) +# vhost-manager -> vhost-manager (for ubus) +MODULE_UNDERSCORE=$(echo "$MODULE" | tr '-' '_') +UBUS_NAME="luci.$MODULE" +PKG_NAME="luci-app-$MODULE" + +echo "╔══════════════════════════════════════════════════════════════╗" +echo "║ Generating RPCD files for: $MODULE" +echo "╚══════════════════════════════════════════════════════════════╝" +echo "" + +# ============================================ +# Create RPCD script +# ============================================ +RPCD_SCRIPT="/usr/libexec/rpcd/$MODULE" + +echo "→ Creating RPCD script: $RPCD_SCRIPT" + +cat > "$RPCD_SCRIPT" << 'RPCD_EOF' +#!/bin/sh +# RPCD backend for MODULE_PLACEHOLDER +# Provides ubus interface: luci.MODULE_PLACEHOLDER + +. /lib/functions.sh +. /usr/share/libubox/jshn.sh + +# Initialize JSON +json_init + +case "$1" in + list) + # List available methods + json_add_object "status" + json_close_object + json_add_object "get_config" + json_close_object + json_add_object "set_config" + json_add_string "config" "object" + json_close_object + json_add_object "get_stats" + json_close_object + json_dump + ;; + + call) + case "$2" in + status) + # Return module status + json_add_boolean "enabled" 1 + json_add_string "status" "running" + json_add_string "version" "2.0.0" + json_add_string "module" "MODULE_PLACEHOLDER" + + # Check if service is running (customize per module) + # Example: check nginx for vhost-manager + # if pgrep -x nginx > /dev/null 2>&1; then + # json_add_boolean "service_running" 1 + # else + # json_add_boolean "service_running" 0 + # fi + + json_add_boolean "service_running" 1 + json_dump + ;; + + get_config) + # Return current configuration + json_add_object "config" + + # Read from UCI if available + if [ -f "/etc/config/MODULE_UNDERSCORE_PLACEHOLDER" ]; then + config_load "MODULE_UNDERSCORE_PLACEHOLDER" + # Add config values here + json_add_boolean "enabled" 1 + else + json_add_boolean "enabled" 0 + fi + + json_close_object + json_dump + ;; + + set_config) + # Set configuration + read -r input + + # Parse input JSON + json_load "$input" + json_get_var config config + + # Apply configuration via UCI + # uci set MODULE_UNDERSCORE_PLACEHOLDER.global.enabled="$enabled" + # uci commit MODULE_UNDERSCORE_PLACEHOLDER + + json_init + json_add_boolean "success" 1 + json_add_string "message" "Configuration updated" + json_dump + ;; + + get_stats) + # Return statistics + json_add_object "stats" + json_add_int "uptime" "$(cat /proc/uptime | cut -d. -f1)" + json_add_string "timestamp" "$(date -Iseconds)" + json_close_object + json_dump + ;; + + *) + # Unknown method + json_add_int "error" -32601 + json_add_string "message" "Method not found" + json_dump + ;; + esac + ;; +esac +RPCD_EOF + +# Replace placeholders +sed -i "s/MODULE_PLACEHOLDER/$MODULE/g" "$RPCD_SCRIPT" +sed -i "s/MODULE_UNDERSCORE_PLACEHOLDER/$MODULE_UNDERSCORE/g" "$RPCD_SCRIPT" + +chmod +x "$RPCD_SCRIPT" +echo " ✓ Created and made executable" + +# ============================================ +# Create ACL file +# ============================================ +ACL_FILE="/usr/share/rpcd/acl.d/${PKG_NAME}.json" + +echo "→ Creating ACL file: $ACL_FILE" + +cat > "$ACL_FILE" << ACL_EOF +{ + "luci-app-$MODULE": { + "description": "Grant access to LuCI app $MODULE", + "read": { + "ubus": { + "$UBUS_NAME": ["status", "get_config", "get_stats"] + }, + "uci": ["$MODULE_UNDERSCORE"] + }, + "write": { + "ubus": { + "$UBUS_NAME": ["set_config"] + }, + "uci": ["$MODULE_UNDERSCORE"] + } + } +} +ACL_EOF + +echo " ✓ Created ACL file" + +# ============================================ +# Create Menu file (if not exists) +# ============================================ +MENU_FILE="/usr/share/luci/menu.d/${PKG_NAME}.json" + +if [ ! -f "$MENU_FILE" ]; then + echo "→ Creating Menu file: $MENU_FILE" + + # Convert module name to title + TITLE=$(echo "$MODULE" | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) tolower(substr($i,2))}1') + + cat > "$MENU_FILE" << MENU_EOF +{ + "admin/services/$MODULE_UNDERSCORE": { + "title": "$TITLE", + "order": 50, + "action": { + "type": "view", + "path": "$MODULE/main" + }, + "depends": { + "acl": ["luci-app-$MODULE"], + "uci": { + "$MODULE_UNDERSCORE": true + } + } + } +} +MENU_EOF + + echo " ✓ Created menu file" +else + echo "→ Menu file already exists: $MENU_FILE" +fi + +# ============================================ +# Create UCI config (if not exists) +# ============================================ +UCI_CONFIG="/etc/config/$MODULE_UNDERSCORE" + +if [ ! -f "$UCI_CONFIG" ]; then + echo "→ Creating UCI config: $UCI_CONFIG" + + cat > "$UCI_CONFIG" << UCI_EOF +config global 'global' + option enabled '1' + option version '2.0.0' +UCI_EOF + + echo " ✓ Created UCI config" +else + echo "→ UCI config already exists: $UCI_CONFIG" +fi + +# ============================================ +# Restart services +# ============================================ +echo "" +echo "→ Restarting rpcd..." +/etc/init.d/rpcd restart + +echo "→ Clearing LuCI cache..." +rm -rf /tmp/luci-* + +# Wait for rpcd to initialize +sleep 2 + +# ============================================ +# Verify +# ============================================ +echo "" +echo "╔══════════════════════════════════════════════════════════════╗" +echo "║ Verification" +echo "╚══════════════════════════════════════════════════════════════╝" +echo "" + +# Check ubus registration +if ubus list "$UBUS_NAME" > /dev/null 2>&1; then + echo "✓ $UBUS_NAME is registered in ubus" + echo "" + echo "Available methods:" + ubus -v list "$UBUS_NAME" + + echo "" + echo "Testing status call:" + ubus call "$UBUS_NAME" status +else + echo "✗ $UBUS_NAME is NOT registered" + echo "" + echo "Debug steps:" + echo " 1. Check script: cat $RPCD_SCRIPT" + echo " 2. Test manually: echo '{\"method\":\"list\"}' | $RPCD_SCRIPT" + echo " 3. Check logs: logread | grep rpcd" +fi + +echo "" +echo "Done!" diff --git a/secubox-tools/install-rpcd-fix.sh b/secubox-tools/install-rpcd-fix.sh new file mode 100755 index 0000000..f988d64 --- /dev/null +++ b/secubox-tools/install-rpcd-fix.sh @@ -0,0 +1,129 @@ +#!/bin/sh +# install-rpcd-fix.sh +# Quick installation script for SecuBox RPCD fixes +# +# Upload this script along with rpcd/ and acl/ folders to the router +# then run: sh install-rpcd-fix.sh + +echo "╔══════════════════════════════════════════════════════════════╗" +echo "║ SecuBox RPCD Fix Installer ║" +echo "╚══════════════════════════════════════════════════════════════╝" +echo "" + +SCRIPT_DIR="$(dirname "$(readlink -f "$0")")" + +# Check if running as root +if [ "$(id -u)" != "0" ]; then + echo "Error: This script must be run as root" + exit 1 +fi + +# ============================================ +# Install RPCD scripts +# ============================================ +echo "→ Installing RPCD scripts..." + +if [ -d "$SCRIPT_DIR/rpcd" ]; then + for script in "$SCRIPT_DIR/rpcd"/*; do + [ -f "$script" ] || continue + + NAME=$(basename "$script") + DEST="/usr/libexec/rpcd/$NAME" + + cp "$script" "$DEST" + chmod +x "$DEST" + echo " ✓ Installed: $DEST" + done +else + echo " ⚠ No rpcd/ directory found" +fi + +# ============================================ +# Install ACL files +# ============================================ +echo "" +echo "→ Installing ACL files..." + +mkdir -p /usr/share/rpcd/acl.d + +if [ -d "$SCRIPT_DIR/acl" ]; then + for acl in "$SCRIPT_DIR/acl"/*.json; do + [ -f "$acl" ] || continue + + NAME=$(basename "$acl") + DEST="/usr/share/rpcd/acl.d/$NAME" + + cp "$acl" "$DEST" + echo " ✓ Installed: $DEST" + done +else + echo " ⚠ No acl/ directory found" +fi + +# ============================================ +# Create missing UCI configs +# ============================================ +echo "" +echo "→ Creating UCI configs..." + +# vhost_manager +if [ ! -f /etc/config/vhost_manager ]; then + cat > /etc/config/vhost_manager << 'EOF' +config global 'global' + option enabled '1' + option nginx_dir '/etc/nginx/conf.d' + option acme_dir '/etc/acme' +EOF + echo " ✓ Created: /etc/config/vhost_manager" +fi + +# ============================================ +# Restart services +# ============================================ +echo "" +echo "→ Restarting services..." + +# Restart rpcd +/etc/init.d/rpcd restart +echo " ✓ rpcd restarted" + +# Clear LuCI cache +rm -rf /tmp/luci-* +echo " ✓ LuCI cache cleared" + +# Wait for rpcd to initialize +sleep 2 + +# ============================================ +# Verify installation +# ============================================ +echo "" +echo "╔══════════════════════════════════════════════════════════════╗" +echo "║ Verification ║" +echo "╚══════════════════════════════════════════════════════════════╝" +echo "" + +# List installed modules +echo "Checking ubus registration:" + +MODULES="vhost-manager secubox bandwidth-manager auth-guardian media-flow" + +for module in $MODULES; do + UBUS_NAME="luci.$module" + if ubus list "$UBUS_NAME" > /dev/null 2>&1; then + echo " ✓ $UBUS_NAME" + else + echo " ✗ $UBUS_NAME (not registered)" + fi +done + +echo "" +echo "Testing vhost-manager status:" +ubus call luci.vhost-manager status 2>/dev/null || echo " ✗ Failed" + +echo "" +echo "Installation complete!" +echo "" +echo "If modules are still not working, check:" +echo " logread | grep rpcd" +echo " logread | grep ubus" diff --git a/secubox-tools/makefiles/luci-app-auth-guardian/Makefile b/secubox-tools/makefiles/luci-app-auth-guardian/Makefile new file mode 100644 index 0000000..7963e72 --- /dev/null +++ b/secubox-tools/makefiles/luci-app-auth-guardian/Makefile @@ -0,0 +1,16 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=luci-app-auth-guardian +PKG_VERSION:=2.0.0 +PKG_RELEASE:=1 +PKG_LICENSE:=Apache-2.0 +PKG_MAINTAINER:=CyberMind + +LUCI_TITLE:=LuCI - Auth Guardian (Captive Portal & OAuth) +LUCI_DESCRIPTION:=Authentication system with captive portal, OAuth2 (Google, GitHub), voucher management, and session control for SecuBox. +LUCI_DEPENDS:=+luci-base +rpcd +curl +nodogsplash +LUCI_PKGARCH:=all + +include $(TOPDIR)/feeds/luci/luci.mk + +# call BuildPackage - OpenWrt buildroot diff --git a/secubox-tools/makefiles/luci-app-bandwidth-manager/Makefile b/secubox-tools/makefiles/luci-app-bandwidth-manager/Makefile new file mode 100644 index 0000000..e2db143 --- /dev/null +++ b/secubox-tools/makefiles/luci-app-bandwidth-manager/Makefile @@ -0,0 +1,16 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=luci-app-bandwidth-manager +PKG_VERSION:=2.0.0 +PKG_RELEASE:=1 +PKG_LICENSE:=Apache-2.0 +PKG_MAINTAINER:=CyberMind + +LUCI_TITLE:=LuCI - Bandwidth Manager (QoS & Quotas) +LUCI_DESCRIPTION:=Advanced bandwidth management with CAKE QoS, per-client quotas, automatic media detection, and traffic scheduling for SecuBox. +LUCI_DEPENDS:=+luci-base +rpcd +tc-full +kmod-sched-cake +sqm-scripts +LUCI_PKGARCH:=all + +include $(TOPDIR)/feeds/luci/luci.mk + +# call BuildPackage - OpenWrt buildroot diff --git a/secubox-tools/makefiles/luci-app-media-flow/Makefile b/secubox-tools/makefiles/luci-app-media-flow/Makefile new file mode 100644 index 0000000..c092402 --- /dev/null +++ b/secubox-tools/makefiles/luci-app-media-flow/Makefile @@ -0,0 +1,16 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=luci-app-media-flow +PKG_VERSION:=2.0.0 +PKG_RELEASE:=1 +PKG_LICENSE:=Apache-2.0 +PKG_MAINTAINER:=CyberMind + +LUCI_TITLE:=LuCI - Media Flow (Streaming Detection) +LUCI_DESCRIPTION:=Real-time streaming service detection and monitoring for Netflix, YouTube, Twitch, Zoom, Teams with quality indicators for SecuBox. +LUCI_DEPENDS:=+luci-base +rpcd +netifyd +LUCI_PKGARCH:=all + +include $(TOPDIR)/feeds/luci/luci.mk + +# call BuildPackage - OpenWrt buildroot diff --git a/secubox-tools/makefiles/luci-app-secubox/Makefile b/secubox-tools/makefiles/luci-app-secubox/Makefile new file mode 100644 index 0000000..12f28a8 --- /dev/null +++ b/secubox-tools/makefiles/luci-app-secubox/Makefile @@ -0,0 +1,16 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=luci-app-secubox +PKG_VERSION:=2.0.0 +PKG_RELEASE:=1 +PKG_LICENSE:=Apache-2.0 +PKG_MAINTAINER:=CyberMind + +LUCI_TITLE:=LuCI - SecuBox Hub (Central Dashboard) +LUCI_DESCRIPTION:=Central control hub for all SecuBox modules. Provides unified dashboard, module status, system health monitoring, and quick actions. +LUCI_DEPENDS:=+luci-base +rpcd +curl +jq +LUCI_PKGARCH:=all + +include $(TOPDIR)/feeds/luci/luci.mk + +# call BuildPackage - OpenWrt buildroot diff --git a/secubox-tools/makefiles/luci-app-vhost-manager/Makefile b/secubox-tools/makefiles/luci-app-vhost-manager/Makefile new file mode 100644 index 0000000..6c87e4d --- /dev/null +++ b/secubox-tools/makefiles/luci-app-vhost-manager/Makefile @@ -0,0 +1,16 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=luci-app-vhost-manager +PKG_VERSION:=2.0.0 +PKG_RELEASE:=1 +PKG_LICENSE:=Apache-2.0 +PKG_MAINTAINER:=CyberMind + +LUCI_TITLE:=LuCI - VHost Manager (Reverse Proxy & SSL) +LUCI_DESCRIPTION:=Virtual host manager for local services like Nextcloud, GitLab, Jellyfin with nginx reverse proxy and automatic SSL certificates for SecuBox. +LUCI_DEPENDS:=+luci-base +rpcd +nginx-ssl +acme +acme-acmesh +LUCI_PKGARCH:=all + +include $(TOPDIR)/feeds/luci/luci.mk + +# call BuildPackage - OpenWrt buildroot diff --git a/secubox-tools/rpcd/vhost-manager b/secubox-tools/rpcd/vhost-manager new file mode 100644 index 0000000..227677d --- /dev/null +++ b/secubox-tools/rpcd/vhost-manager @@ -0,0 +1,334 @@ +#!/bin/sh +# /usr/libexec/rpcd/vhost-manager +# RPCD backend for VHost Manager module +# Provides ubus interface: luci.vhost-manager + +. /lib/functions.sh +. /usr/share/libubox/jshn.sh + +# Configuration +UCI_CONFIG="vhost_manager" +NGINX_VHOSTS_DIR="/etc/nginx/conf.d" +ACME_DIR="/etc/acme" + +# Helper: Check if nginx is running +nginx_running() { + pgrep -x nginx > /dev/null 2>&1 +} + +# Helper: Get nginx status +get_nginx_status() { + if nginx_running; then + echo "running" + else + echo "stopped" + fi +} + +# Helper: Count vhosts +count_vhosts() { + if [ -d "$NGINX_VHOSTS_DIR" ]; then + ls -1 "$NGINX_VHOSTS_DIR"/*.conf 2>/dev/null | wc -l + else + echo "0" + fi +} + +# Helper: List vhosts +list_vhosts() { + json_add_array "vhosts" + + if [ -d "$NGINX_VHOSTS_DIR" ]; then + for conf in "$NGINX_VHOSTS_DIR"/*.conf; do + [ -f "$conf" ] || continue + + # Extract server_name from config + SERVER_NAME=$(grep -m1 "server_name" "$conf" | awk '{print $2}' | tr -d ';') + PROXY_PASS=$(grep -m1 "proxy_pass" "$conf" | awk '{print $2}' | tr -d ';') + SSL_ENABLED="false" + + if grep -q "ssl_certificate" "$conf"; then + SSL_ENABLED="true" + fi + + json_add_object "" + json_add_string "name" "$(basename "$conf" .conf)" + json_add_string "domain" "$SERVER_NAME" + json_add_string "backend" "$PROXY_PASS" + json_add_boolean "ssl" "$SSL_ENABLED" + json_add_boolean "enabled" 1 + json_close_object + done + fi + + json_close_array +} + +# Helper: Get SSL certificates +list_certificates() { + json_add_array "certificates" + + if [ -d "$ACME_DIR" ]; then + for cert_dir in "$ACME_DIR"/*/; do + [ -d "$cert_dir" ] || continue + + DOMAIN=$(basename "$cert_dir") + CERT_FILE="$cert_dir/fullchain.cer" + + if [ -f "$CERT_FILE" ]; then + # Get expiry date + EXPIRY=$(openssl x509 -enddate -noout -in "$CERT_FILE" 2>/dev/null | cut -d= -f2) + + json_add_object "" + json_add_string "domain" "$DOMAIN" + json_add_string "expiry" "$EXPIRY" + json_add_boolean "valid" 1 + json_close_object + fi + done + fi + + json_close_array +} + +# Initialize JSON +json_init + +case "$1" in + list) + # List available methods + json_add_object "status" + json_close_object + + json_add_object "get_vhosts" + json_close_object + + json_add_object "get_vhost" + json_add_string "name" "string" + json_close_object + + json_add_object "add_vhost" + json_add_string "domain" "string" + json_add_string "backend" "string" + json_add_boolean "ssl" false + json_close_object + + json_add_object "delete_vhost" + json_add_string "name" "string" + json_close_object + + json_add_object "get_certificates" + json_close_object + + json_add_object "request_certificate" + json_add_string "domain" "string" + json_close_object + + json_add_object "reload_nginx" + json_close_object + + json_add_object "test_config" + json_close_object + + json_dump + ;; + + call) + case "$2" in + status) + # Return module status + json_add_string "module" "vhost-manager" + json_add_string "version" "2.0.0" + json_add_string "nginx_status" "$(get_nginx_status)" + json_add_boolean "nginx_running" $(nginx_running && echo 1 || echo 0) + json_add_int "vhost_count" "$(count_vhosts)" + json_add_string "config_dir" "$NGINX_VHOSTS_DIR" + json_add_string "acme_dir" "$ACME_DIR" + + # Check nginx version + if command -v nginx > /dev/null 2>&1; then + NGINX_VERSION=$(nginx -v 2>&1 | cut -d/ -f2) + json_add_string "nginx_version" "$NGINX_VERSION" + fi + + json_dump + ;; + + get_vhosts) + # Return list of vhosts + list_vhosts + json_dump + ;; + + get_vhost) + # Get single vhost details + read -r input + json_load "$input" + json_get_var vhost_name name + + CONF_FILE="$NGINX_VHOSTS_DIR/${vhost_name}.conf" + + if [ -f "$CONF_FILE" ]; then + json_add_boolean "found" 1 + json_add_string "name" "$vhost_name" + json_add_string "config" "$(cat "$CONF_FILE")" + else + json_add_boolean "found" 0 + json_add_string "error" "VHost not found" + fi + + json_dump + ;; + + add_vhost) + # Add new vhost + read -r input + json_load "$input" + json_get_var domain domain + json_get_var backend backend + json_get_var ssl ssl + + # Validate + if [ -z "$domain" ] || [ -z "$backend" ]; then + json_init + json_add_boolean "success" 0 + json_add_string "error" "Domain and backend are required" + json_dump + exit 0 + fi + + # Create config + VHOST_NAME=$(echo "$domain" | tr '.' '-') + CONF_FILE="$NGINX_VHOSTS_DIR/${VHOST_NAME}.conf" + + mkdir -p "$NGINX_VHOSTS_DIR" + + cat > "$CONF_FILE" << NGINX_EOF +server { + listen 80; + listen [::]:80; + server_name $domain; + + location / { + proxy_pass $backend; + proxy_http_version 1.1; + proxy_set_header Host \$host; + proxy_set_header X-Real-IP \$remote_addr; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto \$scheme; + proxy_set_header Upgrade \$http_upgrade; + proxy_set_header Connection "upgrade"; + } +} +NGINX_EOF + + json_init + json_add_boolean "success" 1 + json_add_string "message" "VHost created" + json_add_string "config_file" "$CONF_FILE" + json_dump + ;; + + delete_vhost) + # Delete vhost + read -r input + json_load "$input" + json_get_var vhost_name name + + CONF_FILE="$NGINX_VHOSTS_DIR/${vhost_name}.conf" + + if [ -f "$CONF_FILE" ]; then + rm -f "$CONF_FILE" + json_init + json_add_boolean "success" 1 + json_add_string "message" "VHost deleted" + else + json_init + json_add_boolean "success" 0 + json_add_string "error" "VHost not found" + fi + + json_dump + ;; + + get_certificates) + # List SSL certificates + list_certificates + json_dump + ;; + + request_certificate) + # Request Let's Encrypt certificate + read -r input + json_load "$input" + json_get_var domain domain + + if [ -z "$domain" ]; then + json_init + json_add_boolean "success" 0 + json_add_string "error" "Domain is required" + json_dump + exit 0 + fi + + # Check if acme.sh is available + if command -v acme.sh > /dev/null 2>&1; then + # Request certificate (async - just start the process) + acme.sh --issue -d "$domain" --webroot /www --keylength ec-256 & + + json_init + json_add_boolean "success" 1 + json_add_string "message" "Certificate request started" + json_add_string "domain" "$domain" + else + json_init + json_add_boolean "success" 0 + json_add_string "error" "acme.sh not installed" + fi + + json_dump + ;; + + reload_nginx) + # Reload nginx configuration + if nginx -t > /dev/null 2>&1; then + /etc/init.d/nginx reload + json_add_boolean "success" 1 + json_add_string "message" "Nginx reloaded" + else + json_add_boolean "success" 0 + json_add_string "error" "Configuration test failed" + json_add_string "details" "$(nginx -t 2>&1)" + fi + json_dump + ;; + + test_config) + # Test nginx configuration + TEST_OUTPUT=$(nginx -t 2>&1) + TEST_RESULT=$? + + if [ $TEST_RESULT -eq 0 ]; then + json_add_boolean "valid" 1 + json_add_string "message" "Configuration OK" + else + json_add_boolean "valid" 0 + json_add_string "error" "$TEST_OUTPUT" + fi + json_dump + ;; + + *) + # Unknown method + json_add_int "error" -32601 + json_add_string "message" "Method not found: $2" + json_dump + ;; + esac + ;; + + *) + echo "Usage: $0 {list|call}" + exit 1 + ;; +esac diff --git a/secubox-tools/secubox-debug.sh b/secubox-tools/secubox-debug.sh new file mode 100755 index 0000000..7d3b898 --- /dev/null +++ b/secubox-tools/secubox-debug.sh @@ -0,0 +1,421 @@ +#!/bin/sh +# secubox-debug.sh +# Debug and analysis script for SecuBox LuCI modules RPC/ubus issues +# +# Usage: ./secubox-debug.sh [module-name] +# Example: ./secubox-debug.sh vhost-manager +# ./secubox-debug.sh all + +set -e + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +CYAN='\033[0;36m' +NC='\033[0m' # No Color + +# SecuBox modules list +MODULES=" +secubox +crowdsec-dashboard +netdata-dashboard +netifyd-dashboard +wireguard-dashboard +network-modes +client-guardian +system-hub +bandwidth-manager +auth-guardian +media-flow +vhost-manager +cdn-cache +traffic-shaper +" + +echo "" +echo "${CYAN}╔══════════════════════════════════════════════════════════════╗${NC}" +echo "${CYAN}║ SecuBox RPC/UBUS Debug & Analysis Tool ║${NC}" +echo "${CYAN}╚══════════════════════════════════════════════════════════════╝${NC}" +echo "" + +# ============================================ +# System Information +# ============================================ +print_section() { + echo "" + echo "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo "${BLUE} $1${NC}" + echo "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" +} + +print_ok() { + echo " ${GREEN}✓${NC} $1" +} + +print_warn() { + echo " ${YELLOW}⚠${NC} $1" +} + +print_error() { + echo " ${RED}✗${NC} $1" +} + +print_info() { + echo " ${CYAN}→${NC} $1" +} + +# ============================================ +# Check prerequisites +# ============================================ +print_section "System Prerequisites" + +# Check if running on OpenWrt +if [ -f /etc/openwrt_release ]; then + print_ok "Running on OpenWrt" + . /etc/openwrt_release + print_info "Version: $DISTRIB_DESCRIPTION" +else + print_warn "Not running on OpenWrt - some checks may fail" +fi + +# Check rpcd +if pgrep -x rpcd > /dev/null 2>&1; then + print_ok "rpcd is running (PID: $(pgrep -x rpcd))" +else + print_error "rpcd is NOT running!" + echo " Try: /etc/init.d/rpcd restart" +fi + +# Check uhttpd +if pgrep -x uhttpd > /dev/null 2>&1; then + print_ok "uhttpd is running" +else + print_warn "uhttpd not running (nginx mode?)" +fi + +# Check ubus socket +if [ -S /var/run/ubus/ubus.sock ]; then + print_ok "ubus socket exists" +else + print_error "ubus socket missing!" +fi + +# ============================================ +# List all ubus objects +# ============================================ +print_section "Available UBUS Objects" + +echo "" +echo " All registered ubus objects:" +echo " ${CYAN}─────────────────────────────${NC}" + +ubus list 2>/dev/null | while read obj; do + # Highlight luci objects + case "$obj" in + luci.*) + echo " ${GREEN}$obj${NC}" + ;; + *) + echo " $obj" + ;; + esac +done + +# Count luci objects +LUCI_COUNT=$(ubus list 2>/dev/null | grep -c "^luci\." || echo "0") +echo "" +print_info "Total LuCI objects registered: $LUCI_COUNT" + +# ============================================ +# Check SecuBox modules +# ============================================ +print_section "SecuBox Modules Status" + +echo "" +printf " ${CYAN}%-25s %-10s %-10s %-10s %-10s${NC}\n" "MODULE" "UBUS" "RPCD" "ACL" "MENU" +echo " ─────────────────────────────────────────────────────────────────" + +check_module() { + local module="$1" + local ubus_name="luci.$module" + local rpcd_script="/usr/libexec/rpcd/$module" + local acl_file="/usr/share/rpcd/acl.d/luci-app-${module}.json" + local menu_file="/usr/share/luci/menu.d/luci-app-${module}.json" + + # Alternative paths + local rpcd_script_alt="/usr/libexec/rpcd/luci.$module" + local acl_file_alt="/usr/share/rpcd/acl.d/luci-${module}.json" + local menu_file_alt="/usr/share/luci/menu.d/luci-${module}.json" + + # Check ubus + local ubus_status="${RED}✗${NC}" + if ubus list "$ubus_name" > /dev/null 2>&1; then + ubus_status="${GREEN}✓${NC}" + fi + + # Check rpcd script + local rpcd_status="${RED}✗${NC}" + if [ -x "$rpcd_script" ] || [ -x "$rpcd_script_alt" ]; then + rpcd_status="${GREEN}✓${NC}" + elif [ -f "$rpcd_script" ] || [ -f "$rpcd_script_alt" ]; then + rpcd_status="${YELLOW}!${NC}" # exists but not executable + fi + + # Check ACL + local acl_status="${RED}✗${NC}" + if [ -f "$acl_file" ] || [ -f "$acl_file_alt" ]; then + acl_status="${GREEN}✓${NC}" + fi + + # Check menu + local menu_status="${RED}✗${NC}" + if [ -f "$menu_file" ] || [ -f "$menu_file_alt" ]; then + menu_status="${GREEN}✓${NC}" + fi + + printf " %-25s %-18s %-18s %-18s %-18s\n" \ + "$module" "$ubus_status" "$rpcd_status" "$acl_status" "$menu_status" +} + +for module in $MODULES; do + check_module "$module" +done + +echo "" +echo " ${CYAN}Legend:${NC} ${GREEN}✓${NC}=OK ${YELLOW}!${NC}=Issue ${RED}✗${NC}=Missing" + +# ============================================ +# Detailed module analysis +# ============================================ +TARGET_MODULE="$1" + +if [ -n "$TARGET_MODULE" ] && [ "$TARGET_MODULE" != "all" ]; then + print_section "Detailed Analysis: $TARGET_MODULE" + + MODULE="$TARGET_MODULE" + UBUS_NAME="luci.$MODULE" + + echo "" + echo " ${CYAN}UBUS Object: $UBUS_NAME${NC}" + echo " ─────────────────────────────────────" + + # Check if ubus object exists + if ubus list "$UBUS_NAME" > /dev/null 2>&1; then + print_ok "Object registered in ubus" + + echo "" + echo " Available methods:" + ubus -v list "$UBUS_NAME" 2>/dev/null | sed 's/^/ /' + + echo "" + echo " Testing 'status' method:" + if ubus call "$UBUS_NAME" status 2>/dev/null; then + print_ok "status method works" + else + print_error "status method failed" + fi + else + print_error "Object NOT registered in ubus" + echo "" + echo " ${YELLOW}Troubleshooting steps:${NC}" + echo "" + + # Check RPCD script + RPCD_PATHS=" +/usr/libexec/rpcd/$MODULE +/usr/libexec/rpcd/luci.$MODULE +/usr/libexec/rpcd/luci-$MODULE +" + echo " 1. Checking RPCD script locations:" + FOUND_RPCD="" + for path in $RPCD_PATHS; do + if [ -f "$path" ]; then + FOUND_RPCD="$path" + if [ -x "$path" ]; then + print_ok "Found executable: $path" + else + print_error "Found but NOT executable: $path" + echo " ${YELLOW}Fix: chmod +x $path${NC}" + fi + fi + done + + if [ -z "$FOUND_RPCD" ]; then + print_error "No RPCD script found!" + echo " Expected at: /usr/libexec/rpcd/$MODULE" + fi + + # Check ACL file + echo "" + echo " 2. Checking ACL configuration:" + ACL_PATHS=" +/usr/share/rpcd/acl.d/luci-app-${MODULE}.json +/usr/share/rpcd/acl.d/luci-${MODULE}.json +/usr/share/rpcd/acl.d/${MODULE}.json +" + FOUND_ACL="" + for path in $ACL_PATHS; do + if [ -f "$path" ]; then + FOUND_ACL="$path" + print_ok "Found ACL: $path" + + # Validate JSON + if command -v jsonfilter > /dev/null 2>&1; then + if jsonfilter -i "$path" -e '@' > /dev/null 2>&1; then + print_ok "JSON syntax valid" + else + print_error "Invalid JSON syntax!" + fi + fi + + # Check for correct ubus permission + if grep -q "\"$UBUS_NAME\"" "$path" 2>/dev/null; then + print_ok "ACL contains $UBUS_NAME permission" + else + print_warn "ACL might be missing $UBUS_NAME permission" + fi + fi + done + + if [ -z "$FOUND_ACL" ]; then + print_error "No ACL file found!" + fi + + # Test RPCD script directly + if [ -n "$FOUND_RPCD" ] && [ -x "$FOUND_RPCD" ]; then + echo "" + echo " 3. Testing RPCD script directly:" + + # Test list method + echo '{"method":"list"}' | "$FOUND_RPCD" 2>&1 | head -20 + fi + fi + + # Check menu entry + echo "" + echo " ${CYAN}Menu Configuration${NC}" + echo " ─────────────────────────────────────" + + MENU_PATHS=" +/usr/share/luci/menu.d/luci-app-${MODULE}.json +/usr/share/luci/menu.d/luci-${MODULE}.json +" + for path in $MENU_PATHS; do + if [ -f "$path" ]; then + print_ok "Found menu: $path" + echo "" + cat "$path" | sed 's/^/ /' + fi + done +fi + +# ============================================ +# Common fixes +# ============================================ +print_section "Common Fixes" + +echo "" +echo " ${YELLOW}If a module is not working:${NC}" +echo "" +echo " 1. ${CYAN}Restart rpcd:${NC}" +echo " /etc/init.d/rpcd restart" +echo "" +echo " 2. ${CYAN}Check script permissions:${NC}" +echo " chmod +x /usr/libexec/rpcd/" +echo "" +echo " 3. ${CYAN}Validate JSON files:${NC}" +echo " jsonfilter -i /usr/share/rpcd/acl.d/luci-app-.json -e '@'" +echo "" +echo " 4. ${CYAN}Check rpcd logs:${NC}" +echo " logread | grep rpcd" +echo "" +echo " 5. ${CYAN}Test ubus manually:${NC}" +echo " ubus call luci. status" +echo "" +echo " 6. ${CYAN}Reload LuCI:${NC}" +echo " rm -rf /tmp/luci-*" +echo " /etc/init.d/uhttpd restart" +echo "" + +# ============================================ +# Generate fix script +# ============================================ +if [ -n "$TARGET_MODULE" ] && [ "$TARGET_MODULE" != "all" ]; then + print_section "Auto-Fix Script for $TARGET_MODULE" + + FIX_SCRIPT="/tmp/fix-${TARGET_MODULE}.sh" + + cat > "$FIX_SCRIPT" << FIXEOF +#!/bin/sh +# Auto-generated fix script for $TARGET_MODULE + +echo "Fixing $TARGET_MODULE..." + +# Fix permissions +if [ -f /usr/libexec/rpcd/$TARGET_MODULE ]; then + chmod +x /usr/libexec/rpcd/$TARGET_MODULE + echo "✓ Fixed permissions for RPCD script" +fi + +if [ -f /usr/libexec/rpcd/luci.$TARGET_MODULE ]; then + chmod +x /usr/libexec/rpcd/luci.$TARGET_MODULE + echo "✓ Fixed permissions for RPCD script (alt)" +fi + +# Restart rpcd +/etc/init.d/rpcd restart +echo "✓ Restarted rpcd" + +# Clear LuCI cache +rm -rf /tmp/luci-* +echo "✓ Cleared LuCI cache" + +# Test +sleep 2 +if ubus list luci.$TARGET_MODULE > /dev/null 2>&1; then + echo "✓ Module $TARGET_MODULE is now registered!" + ubus -v list luci.$TARGET_MODULE +else + echo "✗ Module still not working. Check logs:" + echo " logread | grep -i rpcd" + echo " logread | grep -i $TARGET_MODULE" +fi +FIXEOF + + chmod +x "$FIX_SCRIPT" + + echo "" + echo " Generated fix script: ${GREEN}$FIX_SCRIPT${NC}" + echo "" + echo " Run it with: ${CYAN}sh $FIX_SCRIPT${NC}" + echo "" +fi + +# ============================================ +# Summary +# ============================================ +print_section "Quick Commands" + +echo "" +echo " ${CYAN}Debug specific module:${NC}" +echo " ./secubox-debug.sh vhost-manager" +echo "" +echo " ${CYAN}List all ubus objects:${NC}" +echo " ubus list | grep luci" +echo "" +echo " ${CYAN}Test RPC call:${NC}" +echo " ubus call luci.vhost-manager status" +echo "" +echo " ${CYAN}View RPCD logs:${NC}" +echo " logread | grep -E '(rpcd|ubus)'" +echo "" +echo " ${CYAN}Full restart:${NC}" +echo " /etc/init.d/rpcd restart && rm -rf /tmp/luci-* && /etc/init.d/uhttpd restart" +echo "" + +echo "${CYAN}╔══════════════════════════════════════════════════════════════╗${NC}" +echo "${CYAN}║ Debug Complete ║${NC}" +echo "${CYAN}╚══════════════════════════════════════════════════════════════╝${NC}" +echo "" diff --git a/secubox-tools/secubox-repair.sh b/secubox-tools/secubox-repair.sh new file mode 100755 index 0000000..9128543 --- /dev/null +++ b/secubox-tools/secubox-repair.sh @@ -0,0 +1,1109 @@ +#!/bin/bash +# +# secubox-repair.sh +# ================= +# Script générique pour vérifier et réparer tous les modules SecuBox +# Exécuter depuis Linux sur le dossier contenant les packages luci-app-* +# +# Usage: +# ./secubox-repair.sh # Vérification seulement +# ./secubox-repair.sh --fix # Vérification + réparation +# ./secubox-repair.sh --fix --deploy # + déploiement sur routeur +# ./secubox-repair.sh --help # Aide +# +# Auteur: CyberMind.fr +# License: Apache-2.0 +# + +set -e + +# ============================================ +# Configuration +# ============================================ +VERSION="2.0.0" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +WORK_DIR="${SCRIPT_DIR}" + +# Router SSH config (modifiable) +ROUTER_HOST="${ROUTER_HOST:-192.168.1.1}" +ROUTER_USER="${ROUTER_USER:-root}" +ROUTER_PORT="${ROUTER_PORT:-22}" + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +CYAN='\033[0;36m' +MAGENTA='\033[0;35m' +BOLD='\033[1m' +NC='\033[0m' + +# Counters +ERRORS=0 +WARNINGS=0 +FIXES=0 +MODULES_CHECKED=0 + +# Options +DO_FIX=false +DO_DEPLOY=false +VERBOSE=false + +# ============================================ +# SecuBox Modules Definition +# ============================================ +declare -A MODULE_INFO=( + ["secubox"]="SecuBox Hub|Central dashboard for all modules|+luci-base +rpcd +curl +jq" + ["crowdsec-dashboard"]="CrowdSec Dashboard|Collaborative threat intelligence|+luci-base +rpcd +crowdsec" + ["netdata-dashboard"]="Netdata Dashboard|Real-time system monitoring|+luci-base +rpcd +netdata" + ["netifyd-dashboard"]="Netifyd Dashboard|Deep packet inspection|+luci-base +rpcd +netifyd" + ["wireguard-dashboard"]="WireGuard Dashboard|VPN management with QR codes|+luci-base +rpcd +wireguard-tools +qrencode" + ["network-modes"]="Network Modes|Network topology switcher|+luci-base +rpcd" + ["client-guardian"]="Client Guardian|NAC and captive portal|+luci-base +rpcd +nodogsplash" + ["system-hub"]="System Hub|System control center|+luci-base +rpcd" + ["bandwidth-manager"]="Bandwidth Manager|QoS and quota management|+luci-base +rpcd +tc-full +kmod-sched-cake" + ["auth-guardian"]="Auth Guardian|OAuth and voucher portal|+luci-base +rpcd +curl" + ["media-flow"]="Media Flow|Streaming detection|+luci-base +rpcd +netifyd" + ["vhost-manager"]="VHost Manager|Reverse proxy and SSL|+luci-base +rpcd +nginx-ssl" + ["cdn-cache"]="CDN Cache|Local content cache|+luci-base +rpcd +nginx" + ["traffic-shaper"]="Traffic Shaper|Advanced traffic control|+luci-base +rpcd +tc-full" +) + +# ============================================ +# Helper Functions +# ============================================ +print_header() { + echo "" + echo -e "${CYAN}╔══════════════════════════════════════════════════════════════════════╗${NC}" + echo -e "${CYAN}║${NC} ${BOLD}$1${NC}" + echo -e "${CYAN}╚══════════════════════════════════════════════════════════════════════╝${NC}" +} + +print_section() { + echo "" + echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo -e "${BLUE} $1${NC}" + echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" +} + +print_module() { + echo "" + echo -e "${MAGENTA}┌──────────────────────────────────────────────────────────────────────┐${NC}" + echo -e "${MAGENTA}│${NC} 📦 ${BOLD}$1${NC}" + echo -e "${MAGENTA}└──────────────────────────────────────────────────────────────────────┘${NC}" +} + +ok() { + echo -e " ${GREEN}✓${NC} $1" +} + +warn() { + echo -e " ${YELLOW}⚠${NC} $1" + ((WARNINGS++)) +} + +error() { + echo -e " ${RED}✗${NC} $1" + ((ERRORS++)) +} + +info() { + echo -e " ${CYAN}→${NC} $1" +} + +fixed() { + echo -e " ${GREEN}🔧${NC} $1" + ((FIXES++)) +} + +verbose() { + if $VERBOSE; then + echo -e " ${CYAN} $1${NC}" + fi +} + +# ============================================ +# Validation Functions +# ============================================ + +# Validate JSON file +validate_json() { + local file="$1" + if command -v jq &> /dev/null; then + if jq empty "$file" 2>/dev/null; then + return 0 + else + return 1 + fi + elif command -v python3 &> /dev/null; then + if python3 -c "import json; json.load(open('$file'))" 2>/dev/null; then + return 0 + else + return 1 + fi + else + # Can't validate, assume OK + return 0 + fi +} + +# Validate JavaScript file +validate_js() { + local file="$1" + if command -v node &> /dev/null; then + if node --check "$file" 2>/dev/null; then + return 0 + else + return 1 + fi + else + # Basic syntax check with grep + if grep -qE '(function|const|let|var|class|import|export)' "$file"; then + return 0 + else + return 1 + fi + fi +} + +# ============================================ +# Check Functions +# ============================================ + +check_makefile() { + local pkg_dir="$1" + local pkg_name="$2" + local makefile="$pkg_dir/Makefile" + + info "Checking Makefile..." + + if [[ ! -f "$makefile" ]]; then + error "Makefile missing!" + if $DO_FIX; then + generate_makefile "$pkg_dir" "$pkg_name" + fi + return 1 + fi + + # Check required fields + local required_fields=("PKG_NAME" "PKG_VERSION" "PKG_RELEASE" "PKG_LICENSE") + local missing_fields=() + + for field in "${required_fields[@]}"; do + if ! grep -q "^${field}:=" "$makefile"; then + missing_fields+=("$field") + fi + done + + if [[ ${#missing_fields[@]} -gt 0 ]]; then + warn "Missing fields: ${missing_fields[*]}" + fi + + # Check PKG_NAME matches directory + local makefile_pkg_name=$(grep "^PKG_NAME:=" "$makefile" | cut -d'=' -f2) + if [[ "$makefile_pkg_name" != "luci-app-$pkg_name" ]]; then + error "PKG_NAME mismatch: expected 'luci-app-$pkg_name', got '$makefile_pkg_name'" + if $DO_FIX; then + sed -i "s/^PKG_NAME:=.*/PKG_NAME:=luci-app-$pkg_name/" "$makefile" + fixed "PKG_NAME corrected" + fi + fi + + # Check for luci.mk include + if ! grep -q 'include.*feeds/luci/luci\.mk' "$makefile"; then + if grep -q 'include.*package\.mk' "$makefile"; then + warn "Uses package.mk instead of luci.mk (may be intentional)" + else + error "Missing include for luci.mk" + if $DO_FIX; then + generate_makefile "$pkg_dir" "$pkg_name" + fi + fi + else + ok "Makefile valid with luci.mk" + fi + + return 0 +} + +check_rpcd_script() { + local pkg_dir="$1" + local pkg_name="$2" + local rpcd_dir="$pkg_dir/root/usr/libexec/rpcd" + local rpcd_script="$rpcd_dir/$pkg_name" + + info "Checking RPCD script..." + + # Check for rpcd directory + if [[ ! -d "$rpcd_dir" ]]; then + warn "RPCD directory missing: $rpcd_dir" + if $DO_FIX; then + mkdir -p "$rpcd_dir" + fixed "Created RPCD directory" + fi + fi + + # Check for rpcd script + if [[ ! -f "$rpcd_script" ]]; then + error "RPCD script missing: $rpcd_script" + if $DO_FIX; then + generate_rpcd_script "$pkg_dir" "$pkg_name" + fi + return 1 + fi + + # Check if executable + if [[ ! -x "$rpcd_script" ]]; then + warn "RPCD script not executable" + if $DO_FIX; then + chmod +x "$rpcd_script" + fixed "Made RPCD script executable" + fi + fi + + # Check shebang + local first_line=$(head -1 "$rpcd_script") + if [[ "$first_line" != "#!/bin/sh" && "$first_line" != "#!/bin/bash" ]]; then + warn "RPCD script missing proper shebang" + fi + + # Check for required functions + if ! grep -q 'json_init\|json_add' "$rpcd_script"; then + warn "RPCD script may be missing JSON functions" + fi + + # Check list and call handlers + if ! grep -q 'case.*list' "$rpcd_script"; then + error "RPCD script missing 'list' handler" + fi + + if ! grep -q 'case.*call' "$rpcd_script"; then + error "RPCD script missing 'call' handler" + fi + + # Check for status method + if ! grep -q 'status' "$rpcd_script"; then + warn "RPCD script missing 'status' method" + fi + + ok "RPCD script exists" + return 0 +} + +check_acl_file() { + local pkg_dir="$1" + local pkg_name="$2" + local acl_dir="$pkg_dir/root/usr/share/rpcd/acl.d" + local acl_file="$acl_dir/luci-app-${pkg_name}.json" + + info "Checking ACL file..." + + # Check for acl directory + if [[ ! -d "$acl_dir" ]]; then + warn "ACL directory missing" + if $DO_FIX; then + mkdir -p "$acl_dir" + fixed "Created ACL directory" + fi + fi + + # Check for acl file + if [[ ! -f "$acl_file" ]]; then + error "ACL file missing: $acl_file" + if $DO_FIX; then + generate_acl_file "$pkg_dir" "$pkg_name" + fi + return 1 + fi + + # Validate JSON + if ! validate_json "$acl_file"; then + error "ACL file has invalid JSON syntax" + if $DO_FIX; then + generate_acl_file "$pkg_dir" "$pkg_name" + fi + return 1 + fi + + # Check for correct ubus permission + local ubus_name="luci.$pkg_name" + if ! grep -q "$ubus_name" "$acl_file"; then + warn "ACL may be missing '$ubus_name' permission" + if $DO_FIX; then + generate_acl_file "$pkg_dir" "$pkg_name" + fi + fi + + ok "ACL file valid" + return 0 +} + +check_menu_file() { + local pkg_dir="$1" + local pkg_name="$2" + local menu_dir="$pkg_dir/root/usr/share/luci/menu.d" + local menu_file="$menu_dir/luci-app-${pkg_name}.json" + + info "Checking Menu file..." + + # Check for menu directory + if [[ ! -d "$menu_dir" ]]; then + warn "Menu directory missing" + if $DO_FIX; then + mkdir -p "$menu_dir" + fixed "Created Menu directory" + fi + fi + + # Check for menu file + if [[ ! -f "$menu_file" ]]; then + error "Menu file missing: $menu_file" + if $DO_FIX; then + generate_menu_file "$pkg_dir" "$pkg_name" + fi + return 1 + fi + + # Validate JSON + if ! validate_json "$menu_file"; then + error "Menu file has invalid JSON syntax" + if $DO_FIX; then + generate_menu_file "$pkg_dir" "$pkg_name" + fi + return 1 + fi + + ok "Menu file valid" + return 0 +} + +check_view_files() { + local pkg_dir="$1" + local pkg_name="$2" + local view_dir="$pkg_dir/htdocs/luci-static/resources/view" + + info "Checking View files..." + + # Check htdocs structure + if [[ ! -d "$pkg_dir/htdocs/luci-static/resources" ]]; then + warn "htdocs structure missing" + if $DO_FIX; then + mkdir -p "$pkg_dir/htdocs/luci-static/resources/view/${pkg_name//-/_}" + fixed "Created htdocs structure" + fi + fi + + # Find JS view files + local js_files=$(find "$pkg_dir/htdocs" -name "*.js" 2>/dev/null | wc -l) + if [[ $js_files -eq 0 ]]; then + warn "No JavaScript view files found" + if $DO_FIX; then + generate_view_file "$pkg_dir" "$pkg_name" + fi + else + # Validate each JS file + while IFS= read -r js_file; do + if [[ -n "$js_file" ]]; then + if validate_js "$js_file"; then + verbose "Valid: $js_file" + else + error "Invalid JS syntax: $js_file" + fi + fi + done < <(find "$pkg_dir/htdocs" -name "*.js" 2>/dev/null) + ok "Found $js_files JavaScript view file(s)" + fi + + return 0 +} + +check_config_file() { + local pkg_dir="$1" + local pkg_name="$2" + local pkg_name_underscore="${pkg_name//-/_}" + local config_dir="$pkg_dir/root/etc/config" + local config_file="$config_dir/$pkg_name_underscore" + + info "Checking UCI config..." + + if [[ ! -d "$config_dir" ]]; then + if $DO_FIX; then + mkdir -p "$config_dir" + fi + fi + + if [[ ! -f "$config_file" ]]; then + warn "UCI config missing (optional)" + if $DO_FIX; then + generate_config_file "$pkg_dir" "$pkg_name" + fi + else + ok "UCI config exists" + fi + + return 0 +} + +check_malformed_dirs() { + local pkg_dir="$1" + local pkg_name="$2" + + info "Checking for malformed directories..." + + # Check for {htdocs or other malformed dirs + local malformed=$(find "$pkg_dir" -type d -name '{*' 2>/dev/null) + + if [[ -n "$malformed" ]]; then + error "Found malformed directories:" + echo "$malformed" | while read -r dir; do + echo " - $dir" + if $DO_FIX; then + rm -rf "$dir" + fixed "Removed: $dir" + fi + done + else + ok "No malformed directories" + fi + + return 0 +} + +check_permissions() { + local pkg_dir="$1" + local pkg_name="$2" + + info "Checking file permissions..." + + local files_fixed=0 + + # RPCD scripts + while IFS= read -r script; do + if [[ -n "$script" && ! -x "$script" ]]; then + warn "Not executable: $script" + if $DO_FIX; then + chmod +x "$script" + ((files_fixed++)) + fi + fi + done < <(find "$pkg_dir" -path "*/usr/libexec/rpcd/*" -type f 2>/dev/null) + + # Init scripts + while IFS= read -r script; do + if [[ -n "$script" && ! -x "$script" ]]; then + warn "Not executable: $script" + if $DO_FIX; then + chmod +x "$script" + ((files_fixed++)) + fi + fi + done < <(find "$pkg_dir" -path "*/etc/init.d/*" -type f 2>/dev/null) + + # UCI defaults + while IFS= read -r script; do + if [[ -n "$script" && ! -x "$script" ]]; then + warn "Not executable: $script" + if $DO_FIX; then + chmod +x "$script" + ((files_fixed++)) + fi + fi + done < <(find "$pkg_dir" -path "*/etc/uci-defaults/*" -type f 2>/dev/null) + + if [[ $files_fixed -gt 0 ]]; then + fixed "Fixed permissions on $files_fixed file(s)" + else + ok "Permissions OK" + fi + + return 0 +} + +# ============================================ +# Generate Functions +# ============================================ + +generate_makefile() { + local pkg_dir="$1" + local pkg_name="$2" + local makefile="$pkg_dir/Makefile" + + # Get module info + local info="${MODULE_INFO[$pkg_name]}" + local title=$(echo "$info" | cut -d'|' -f1) + local desc=$(echo "$info" | cut -d'|' -f2) + local depends=$(echo "$info" | cut -d'|' -f3) + + # Defaults if not in MODULE_INFO + title="${title:-SecuBox Module}" + desc="${desc:-SecuBox LuCI application}" + depends="${depends:-+luci-base +rpcd}" + + cat > "$makefile" << MAKEFILE_EOF +include \$(TOPDIR)/rules.mk + +PKG_NAME:=luci-app-${pkg_name} +PKG_VERSION:=${VERSION} +PKG_RELEASE:=1 +PKG_LICENSE:=Apache-2.0 +PKG_MAINTAINER:=CyberMind + +LUCI_TITLE:=LuCI - ${title} +LUCI_DESCRIPTION:=${desc} +LUCI_DEPENDS:=${depends} +LUCI_PKGARCH:=all + +include \$(TOPDIR)/feeds/luci/luci.mk + +# call BuildPackage - OpenWrt buildroot +MAKEFILE_EOF + + fixed "Generated Makefile" +} + +generate_rpcd_script() { + local pkg_dir="$1" + local pkg_name="$2" + local pkg_name_underscore="${pkg_name//-/_}" + local rpcd_dir="$pkg_dir/root/usr/libexec/rpcd" + local rpcd_script="$rpcd_dir/$pkg_name" + + mkdir -p "$rpcd_dir" + + cat > "$rpcd_script" << 'RPCD_EOF' +#!/bin/sh +# RPCD backend for PKG_NAME_PLACEHOLDER +# Provides ubus interface: luci.PKG_NAME_PLACEHOLDER + +. /lib/functions.sh +. /usr/share/libubox/jshn.sh + +# Configuration +MODULE_NAME="PKG_NAME_PLACEHOLDER" +MODULE_VERSION="VERSION_PLACEHOLDER" +UCI_CONFIG="PKG_NAME_UNDERSCORE_PLACEHOLDER" + +# Initialize JSON output +json_init + +case "$1" in + list) + # List available methods + json_add_object "status" + json_close_object + + json_add_object "get_config" + json_close_object + + json_add_object "set_config" + json_add_string "config" "object" + json_close_object + + json_add_object "get_stats" + json_close_object + + json_dump + ;; + + call) + case "$2" in + status) + # Return module status + json_add_string "module" "$MODULE_NAME" + json_add_string "version" "$MODULE_VERSION" + json_add_boolean "enabled" 1 + json_add_string "status" "running" + json_add_string "timestamp" "$(date -Iseconds 2>/dev/null || date)" + json_dump + ;; + + get_config) + # Return current configuration + json_add_object "config" + + if [ -f "/etc/config/$UCI_CONFIG" ]; then + config_load "$UCI_CONFIG" + json_add_boolean "enabled" 1 + else + json_add_boolean "enabled" 0 + fi + + json_close_object + json_dump + ;; + + set_config) + # Set configuration + read -r input + + # Parse and apply config + # uci set $UCI_CONFIG... + # uci commit $UCI_CONFIG + + json_add_boolean "success" 1 + json_add_string "message" "Configuration updated" + json_dump + ;; + + get_stats) + # Return statistics + json_add_object "stats" + json_add_int "uptime" "$(cat /proc/uptime 2>/dev/null | cut -d. -f1 || echo 0)" + json_add_string "timestamp" "$(date -Iseconds 2>/dev/null || date)" + json_close_object + json_dump + ;; + + *) + json_add_int "error" -32601 + json_add_string "message" "Method not found: $2" + json_dump + ;; + esac + ;; + + *) + echo "Usage: $0 {list|call}" >&2 + exit 1 + ;; +esac +RPCD_EOF + + # Replace placeholders + sed -i "s/PKG_NAME_PLACEHOLDER/$pkg_name/g" "$rpcd_script" + sed -i "s/PKG_NAME_UNDERSCORE_PLACEHOLDER/$pkg_name_underscore/g" "$rpcd_script" + sed -i "s/VERSION_PLACEHOLDER/$VERSION/g" "$rpcd_script" + + chmod +x "$rpcd_script" + fixed "Generated RPCD script" +} + +generate_acl_file() { + local pkg_dir="$1" + local pkg_name="$2" + local pkg_name_underscore="${pkg_name//-/_}" + local acl_dir="$pkg_dir/root/usr/share/rpcd/acl.d" + local acl_file="$acl_dir/luci-app-${pkg_name}.json" + + mkdir -p "$acl_dir" + + cat > "$acl_file" << ACL_EOF +{ + "luci-app-${pkg_name}": { + "description": "Grant access to LuCI app ${pkg_name}", + "read": { + "ubus": { + "luci.${pkg_name}": [ + "status", + "get_config", + "get_stats" + ] + }, + "uci": ["${pkg_name_underscore}"] + }, + "write": { + "ubus": { + "luci.${pkg_name}": [ + "set_config" + ] + }, + "uci": ["${pkg_name_underscore}"] + } + } +} +ACL_EOF + + fixed "Generated ACL file" +} + +generate_menu_file() { + local pkg_dir="$1" + local pkg_name="$2" + local pkg_name_underscore="${pkg_name//-/_}" + local menu_dir="$pkg_dir/root/usr/share/luci/menu.d" + local menu_file="$menu_dir/luci-app-${pkg_name}.json" + + mkdir -p "$menu_dir" + + # Generate title from pkg_name + local title=$(echo "$pkg_name" | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) tolower(substr($i,2))}1') + + # Get from MODULE_INFO if available + if [[ -n "${MODULE_INFO[$pkg_name]}" ]]; then + title=$(echo "${MODULE_INFO[$pkg_name]}" | cut -d'|' -f1) + fi + + cat > "$menu_file" << MENU_EOF +{ + "admin/services/${pkg_name_underscore}": { + "title": "${title}", + "order": 50, + "action": { + "type": "view", + "path": "${pkg_name_underscore}/main" + }, + "depends": { + "acl": ["luci-app-${pkg_name}"], + "uci": { + "${pkg_name_underscore}": true + } + } + } +} +MENU_EOF + + fixed "Generated Menu file" +} + +generate_view_file() { + local pkg_dir="$1" + local pkg_name="$2" + local pkg_name_underscore="${pkg_name//-/_}" + local view_dir="$pkg_dir/htdocs/luci-static/resources/view/${pkg_name_underscore}" + local view_file="$view_dir/main.js" + + mkdir -p "$view_dir" + + # Generate title + local title=$(echo "$pkg_name" | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) tolower(substr($i,2))}1') + + if [[ -n "${MODULE_INFO[$pkg_name]}" ]]; then + title=$(echo "${MODULE_INFO[$pkg_name]}" | cut -d'|' -f1) + fi + + cat > "$view_file" << 'VIEW_EOF' +'use strict'; +'require view'; +'require rpc'; +'require ui'; +'require form'; + +var callStatus = rpc.declare({ + object: 'luci.PKG_NAME_PLACEHOLDER', + method: 'status', + expect: { } +}); + +return view.extend({ + load: function() { + return Promise.all([ + callStatus() + ]); + }, + + render: function(data) { + var status = data[0] || {}; + var m, s, o; + + m = new form.Map('PKG_NAME_UNDERSCORE_PLACEHOLDER', _('TITLE_PLACEHOLDER'), + _('DESCRIPTION_PLACEHOLDER')); + + s = m.section(form.NamedSection, 'status', 'status', _('Status')); + s.anonymous = true; + + o = s.option(form.DummyValue, '_status', _('Module Status')); + o.rawhtml = true; + o.cfgvalue = function() { + var running = status.status === 'running'; + return '' + + (running ? _('Running') : _('Stopped')) + ''; + }; + + o = s.option(form.DummyValue, '_version', _('Version')); + o.cfgvalue = function() { + return status.version || 'Unknown'; + }; + + return m.render(); + }, + + handleSaveApply: null, + handleSave: null, + handleReset: null +}); +VIEW_EOF + + # Replace placeholders + sed -i "s/PKG_NAME_PLACEHOLDER/$pkg_name/g" "$view_file" + sed -i "s/PKG_NAME_UNDERSCORE_PLACEHOLDER/$pkg_name_underscore/g" "$view_file" + sed -i "s/TITLE_PLACEHOLDER/$title/g" "$view_file" + sed -i "s/DESCRIPTION_PLACEHOLDER/SecuBox $title module/g" "$view_file" + + fixed "Generated View file" +} + +generate_config_file() { + local pkg_dir="$1" + local pkg_name="$2" + local pkg_name_underscore="${pkg_name//-/_}" + local config_dir="$pkg_dir/root/etc/config" + local config_file="$config_dir/$pkg_name_underscore" + + mkdir -p "$config_dir" + + cat > "$config_file" << CONFIG_EOF +config global 'global' + option enabled '1' + option version '${VERSION}' +CONFIG_EOF + + fixed "Generated UCI config" +} + +# ============================================ +# Deploy Functions +# ============================================ + +deploy_to_router() { + local pkg_dir="$1" + local pkg_name="$2" + + print_section "Deploying $pkg_name to router ($ROUTER_HOST)" + + # Test SSH connection + if ! ssh -q -o ConnectTimeout=5 -p "$ROUTER_PORT" "$ROUTER_USER@$ROUTER_HOST" "echo ok" &>/dev/null; then + error "Cannot connect to router via SSH" + echo " Check: ROUTER_HOST=$ROUTER_HOST ROUTER_USER=$ROUTER_USER ROUTER_PORT=$ROUTER_PORT" + return 1 + fi + + ok "SSH connection OK" + + # Create directories on router + info "Creating directories..." + ssh -p "$ROUTER_PORT" "$ROUTER_USER@$ROUTER_HOST" "mkdir -p /usr/libexec/rpcd /usr/share/rpcd/acl.d /usr/share/luci/menu.d" + + # Deploy RPCD script + local rpcd_script="$pkg_dir/root/usr/libexec/rpcd/$pkg_name" + if [[ -f "$rpcd_script" ]]; then + info "Deploying RPCD script..." + scp -P "$ROUTER_PORT" "$rpcd_script" "$ROUTER_USER@$ROUTER_HOST:/usr/libexec/rpcd/" + ssh -p "$ROUTER_PORT" "$ROUTER_USER@$ROUTER_HOST" "chmod +x /usr/libexec/rpcd/$pkg_name" + ok "RPCD script deployed" + fi + + # Deploy ACL file + local acl_file="$pkg_dir/root/usr/share/rpcd/acl.d/luci-app-${pkg_name}.json" + if [[ -f "$acl_file" ]]; then + info "Deploying ACL file..." + scp -P "$ROUTER_PORT" "$acl_file" "$ROUTER_USER@$ROUTER_HOST:/usr/share/rpcd/acl.d/" + ok "ACL file deployed" + fi + + # Deploy Menu file + local menu_file="$pkg_dir/root/usr/share/luci/menu.d/luci-app-${pkg_name}.json" + if [[ -f "$menu_file" ]]; then + info "Deploying Menu file..." + scp -P "$ROUTER_PORT" "$menu_file" "$ROUTER_USER@$ROUTER_HOST:/usr/share/luci/menu.d/" + ok "Menu file deployed" + fi + + # Deploy View files + if [[ -d "$pkg_dir/htdocs" ]]; then + info "Deploying View files..." + ssh -p "$ROUTER_PORT" "$ROUTER_USER@$ROUTER_HOST" "mkdir -p /www/luci-static/resources/view" + scp -r -P "$ROUTER_PORT" "$pkg_dir/htdocs/luci-static/resources/"* "$ROUTER_USER@$ROUTER_HOST:/www/luci-static/resources/" 2>/dev/null || true + ok "View files deployed" + fi + + # Restart rpcd + info "Restarting rpcd..." + ssh -p "$ROUTER_PORT" "$ROUTER_USER@$ROUTER_HOST" "/etc/init.d/rpcd restart" + + # Clear LuCI cache + info "Clearing LuCI cache..." + ssh -p "$ROUTER_PORT" "$ROUTER_USER@$ROUTER_HOST" "rm -rf /tmp/luci-*" + + # Test + sleep 2 + info "Testing ubus registration..." + if ssh -p "$ROUTER_PORT" "$ROUTER_USER@$ROUTER_HOST" "ubus list luci.$pkg_name" &>/dev/null; then + ok "luci.$pkg_name is registered!" + + # Test status call + info "Testing status call..." + ssh -p "$ROUTER_PORT" "$ROUTER_USER@$ROUTER_HOST" "ubus call luci.$pkg_name status" + else + error "luci.$pkg_name is NOT registered" + echo " Check logs: ssh $ROUTER_USER@$ROUTER_HOST logread | grep rpcd" + fi +} + +# ============================================ +# Main Check Function +# ============================================ + +check_module() { + local pkg_dir="$1" + local pkg_name=$(basename "$pkg_dir" | sed 's/^luci-app-//') + + print_module "luci-app-$pkg_name" + + ((MODULES_CHECKED++)) + + # Run all checks + check_malformed_dirs "$pkg_dir" "$pkg_name" + check_makefile "$pkg_dir" "$pkg_name" + check_rpcd_script "$pkg_dir" "$pkg_name" + check_acl_file "$pkg_dir" "$pkg_name" + check_menu_file "$pkg_dir" "$pkg_name" + check_view_files "$pkg_dir" "$pkg_name" + check_config_file "$pkg_dir" "$pkg_name" + check_permissions "$pkg_dir" "$pkg_name" + + # Deploy if requested + if $DO_DEPLOY; then + deploy_to_router "$pkg_dir" "$pkg_name" + fi +} + +# ============================================ +# Summary +# ============================================ + +print_summary() { + print_header "Summary" + + echo "" + echo -e " ${BOLD}Modules checked:${NC} $MODULES_CHECKED" + echo -e " ${GREEN}Fixes applied:${NC} $FIXES" + echo -e " ${YELLOW}Warnings:${NC} $WARNINGS" + echo -e " ${RED}Errors:${NC} $ERRORS" + echo "" + + if [[ $ERRORS -gt 0 ]]; then + echo -e " ${RED}⚠ Some errors remain. Run with --fix to repair.${NC}" + elif [[ $WARNINGS -gt 0 ]]; then + echo -e " ${YELLOW}⚠ Some warnings. Review and fix manually if needed.${NC}" + else + echo -e " ${GREEN}✓ All modules validated successfully!${NC}" + fi + + echo "" + + if ! $DO_DEPLOY && $DO_FIX; then + echo -e " ${CYAN}Tip: Use --deploy to push fixes to router${NC}" + echo "" + fi +} + +# ============================================ +# Help +# ============================================ + +show_help() { + cat << EOF +${BOLD}SecuBox Module Repair Tool v${VERSION}${NC} + +Usage: $0 [OPTIONS] [MODULE_NAME] + +Options: + --fix Apply automatic fixes + --deploy Deploy fixed modules to router (requires SSH) + --verbose Show detailed output + --help Show this help + +Environment Variables: + ROUTER_HOST Router IP address (default: 192.168.1.1) + ROUTER_USER SSH user (default: root) + ROUTER_PORT SSH port (default: 22) + +Examples: + $0 Check all modules + $0 --fix Check and fix all modules + $0 --fix vhost-manager Fix specific module + $0 --fix --deploy Fix and deploy to router + + ROUTER_HOST=192.168.8.191 $0 --fix --deploy + +EOF +} + +# ============================================ +# Main +# ============================================ + +main() { + # Parse arguments + local target_module="" + + while [[ $# -gt 0 ]]; do + case "$1" in + --fix) + DO_FIX=true + shift + ;; + --deploy) + DO_DEPLOY=true + shift + ;; + --verbose|-v) + VERBOSE=true + shift + ;; + --help|-h) + show_help + exit 0 + ;; + -*) + echo "Unknown option: $1" + show_help + exit 1 + ;; + *) + target_module="$1" + shift + ;; + esac + done + + # Header + print_header "SecuBox Module Repair Tool v${VERSION}" + + echo "" + echo -e " ${CYAN}Working directory:${NC} $WORK_DIR" + echo -e " ${CYAN}Fix mode:${NC} $($DO_FIX && echo 'ON' || echo 'OFF')" + echo -e " ${CYAN}Deploy mode:${NC} $($DO_DEPLOY && echo 'ON' || echo 'OFF')" + + if $DO_DEPLOY; then + echo -e " ${CYAN}Router:${NC} $ROUTER_USER@$ROUTER_HOST:$ROUTER_PORT" + fi + + # Find modules + cd "$WORK_DIR" + + if [[ -n "$target_module" ]]; then + # Check specific module + local pkg_dir="$WORK_DIR/luci-app-$target_module" + if [[ ! -d "$pkg_dir" ]]; then + pkg_dir="$WORK_DIR/$target_module" + fi + + if [[ -d "$pkg_dir" ]]; then + check_module "$pkg_dir" + else + error "Module not found: $target_module" + exit 1 + fi + else + # Check all modules + for pkg_dir in luci-app-*/; do + if [[ -d "$pkg_dir" ]]; then + check_module "${pkg_dir%/}" + fi + done + fi + + # Summary + print_summary + + # Exit code + if [[ $ERRORS -gt 0 ]]; then + exit 1 + fi + exit 0 +} + +# Run +main "$@" diff --git a/secubox-tools/templates/Makefile.template b/secubox-tools/templates/Makefile.template new file mode 100644 index 0000000..104b6f4 --- /dev/null +++ b/secubox-tools/templates/Makefile.template @@ -0,0 +1,75 @@ +# Template Makefile for SecuBox LuCI Applications +# ================================================ +# Copy this template and customize for each package + +include $(TOPDIR)/rules.mk + +# Package metadata +PKG_NAME:=luci-app-PACKAGE_NAME +PKG_VERSION:=2.0.0 +PKG_RELEASE:=1 +PKG_LICENSE:=Apache-2.0 +PKG_MAINTAINER:=CyberMind + +# LuCI specific +LUCI_TITLE:=LuCI - Package Description +LUCI_DESCRIPTION:=Detailed description of what this package does +LUCI_DEPENDS:=+luci-base +LUCI_PKGARCH:=all + +# Include LuCI build system +include $(TOPDIR)/feeds/luci/luci.mk + +# Call BuildPackage - this is handled by luci.mk +# No need for explicit Package/xxx/install when using luci.mk + +# === END OF TEMPLATE === + +# ================================================ +# NOTES FOR DEVELOPERS +# ================================================ +# +# Directory structure expected by luci.mk: +# +# luci-app-mypackage/ +# ├── Makefile # This file +# ├── htdocs/ +# │ └── luci-static/ +# │ └── resources/ +# │ └── view/ +# │ └── mypackage/ +# │ └── main.js # LuCI JavaScript view +# └── root/ +# ├── etc/ +# │ ├── config/ +# │ │ └── mypackage # UCI config file +# │ ├── init.d/ +# │ │ └── mypackage # Init script (executable) +# │ └── uci-defaults/ +# │ └── 99-mypackage # First-run setup (executable) +# └── usr/ +# ├── libexec/ +# │ └── rpcd/ +# │ └── mypackage # RPCD backend script (executable) +# └── share/ +# ├── luci/ +# │ └── menu.d/ +# │ └── luci-app-mypackage.json # Menu entry +# └── rpcd/ +# └── acl.d/ +# └── luci-app-mypackage.json # ACL permissions +# +# ================================================ +# COMMON LUCI_DEPENDS OPTIONS +# ================================================ +# +# +luci-base - Required for all LuCI apps +# +luci-compat - Legacy API compatibility +# +luci-lib-jsonc - JSON-C library +# +rpcd - RPC daemon (for backend scripts) +# +curl - HTTP client +# +jq - JSON processor +# +wireguard-tools - WireGuard utilities +# +qrencode - QR code generator +# +# ================================================