New package for joining SecuBox mesh networks from OpenWRT devices. RPCD handler (/usr/libexec/rpcd/luci.masterlink): - status: Current mesh membership state - join: Join mesh with master_ip and token - leave: Leave current mesh network - info: Local node info (fingerprint, hostname, IP) - verify: Verify master before joining CLI tool (/usr/bin/sbx-mesh-join): - URL parsing: sbx-mesh-join 'http://ip:7331/master-link/?token=xxx' - Direct args: sbx-mesh-join 192.168.1.1 token123 - Auto-generates node fingerprint from MAC address - Saves to UCI on success LuCI interface (Services > Master-Link): - Status display (connected/pending/disconnected) - Invite URL/token input with Verify and Join buttons - Leave mesh button when connected - CLI usage help section Also adds screenshot-capture.js for automated LuCI screenshots. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
311 lines
9.2 KiB
Bash
Executable File
311 lines
9.2 KiB
Bash
Executable File
#!/bin/bash
|
|
# SecuBox Screenshot Capture Script
|
|
# Captures screenshots of all LuCI modules using headless Chrome
|
|
|
|
set -e
|
|
|
|
ROUTER="192.168.255.1"
|
|
BASE_URL="https://${ROUTER}/cgi-bin/luci"
|
|
OUTPUT_DIR="$(dirname "$0")/../docs/screenshots/router"
|
|
USERNAME="${LUCI_USER:-root}"
|
|
PASSWORD="${LUCI_PASS:-c3box}"
|
|
WINDOW_SIZE="1920,1080"
|
|
DELAY=3 # seconds to wait for page load
|
|
|
|
# Create output directory
|
|
mkdir -p "$OUTPUT_DIR"
|
|
|
|
# Colors
|
|
GREEN='\033[0;32m'
|
|
RED='\033[0;31m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m'
|
|
|
|
log() { echo -e "${GREEN}[+]${NC} $1"; }
|
|
warn() { echo -e "${YELLOW}[!]${NC} $1"; }
|
|
error() { echo -e "${RED}[-]${NC} $1"; }
|
|
|
|
# Get auth token via curl
|
|
get_auth_token() {
|
|
log "Authenticating to LuCI..."
|
|
|
|
# Get the initial token from login page
|
|
local login_page=$(curl -sk "${BASE_URL}/")
|
|
local token=$(echo "$login_page" | grep -oP 'name="token" value="\K[^"]+' | head -1)
|
|
|
|
if [ -z "$token" ]; then
|
|
# Try alternative method - get sysauth cookie directly
|
|
local response=$(curl -sk -c - -X POST "${BASE_URL}/" \
|
|
-d "luci_username=${USERNAME}" \
|
|
-d "luci_password=${PASSWORD}" \
|
|
-L 2>&1)
|
|
|
|
SYSAUTH=$(echo "$response" | grep sysauth | awk '{print $NF}')
|
|
else
|
|
# Login with token
|
|
local response=$(curl -sk -c - -X POST "${BASE_URL}/" \
|
|
-d "token=${token}" \
|
|
-d "luci_username=${USERNAME}" \
|
|
-d "luci_password=${PASSWORD}" \
|
|
-L 2>&1)
|
|
|
|
SYSAUTH=$(echo "$response" | grep sysauth | awk '{print $NF}')
|
|
fi
|
|
|
|
if [ -z "$SYSAUTH" ]; then
|
|
error "Failed to get auth token"
|
|
return 1
|
|
fi
|
|
|
|
log "Got auth token: ${SYSAUTH:0:20}..."
|
|
echo "$SYSAUTH"
|
|
}
|
|
|
|
# Capture screenshot using headless Chrome
|
|
capture() {
|
|
local name="$1"
|
|
local path="$2"
|
|
local output="${OUTPUT_DIR}/${name}.png"
|
|
local url="${BASE_URL}${path}"
|
|
|
|
log "Capturing: $name -> $output"
|
|
|
|
# Use chromium headless with cookie
|
|
google-chrome --headless --disable-gpu --screenshot="$output" \
|
|
--window-size="$WINDOW_SIZE" \
|
|
--ignore-certificate-errors \
|
|
--disable-web-security \
|
|
--user-data-dir=/tmp/chrome-secubox-$$ \
|
|
"$url" 2>/dev/null || \
|
|
chromium-browser --headless --disable-gpu --screenshot="$output" \
|
|
--window-size="$WINDOW_SIZE" \
|
|
--ignore-certificate-errors \
|
|
--disable-web-security \
|
|
--user-data-dir=/tmp/chrome-secubox-$$ \
|
|
"$url" 2>/dev/null || \
|
|
chromium --headless --disable-gpu --screenshot="$output" \
|
|
--window-size="$WINDOW_SIZE" \
|
|
--ignore-certificate-errors \
|
|
--disable-web-security \
|
|
--user-data-dir=/tmp/chrome-secubox-$$ \
|
|
"$url" 2>/dev/null
|
|
|
|
if [ -f "$output" ]; then
|
|
local size=$(du -h "$output" | cut -f1)
|
|
log " Saved: $output ($size)"
|
|
return 0
|
|
else
|
|
error " Failed to capture: $name"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Capture with puppeteer for better auth handling
|
|
capture_with_puppeteer() {
|
|
local name="$1"
|
|
local path="$2"
|
|
local output="${OUTPUT_DIR}/${name}.png"
|
|
local url="${BASE_URL}${path}"
|
|
|
|
log "Capturing: $name"
|
|
|
|
node - <<EOF
|
|
const puppeteer = require('puppeteer');
|
|
|
|
(async () => {
|
|
const browser = await puppeteer.launch({
|
|
headless: 'new',
|
|
args: [
|
|
'--ignore-certificate-errors',
|
|
'--disable-web-security',
|
|
'--no-sandbox',
|
|
'--disable-setuid-sandbox'
|
|
]
|
|
});
|
|
|
|
const page = await browser.newPage();
|
|
await page.setViewport({ width: 1920, height: 1080 });
|
|
|
|
// Login first
|
|
await page.goto('${BASE_URL}/', { waitUntil: 'networkidle2' });
|
|
await page.type('input[name="luci_username"]', '${USERNAME}');
|
|
await page.type('input[name="luci_password"]', '${PASSWORD}');
|
|
await page.click('button[type="submit"], input[type="submit"]');
|
|
await page.waitForNavigation({ waitUntil: 'networkidle2' });
|
|
|
|
// Navigate to target page
|
|
await page.goto('${url}', { waitUntil: 'networkidle2' });
|
|
await new Promise(r => setTimeout(r, ${DELAY}000));
|
|
|
|
// Take screenshot
|
|
await page.screenshot({ path: '${output}', fullPage: false });
|
|
|
|
await browser.close();
|
|
console.log('Captured: ${output}');
|
|
})();
|
|
EOF
|
|
}
|
|
|
|
# Module definitions: name -> LuCI path
|
|
declare -A MODULES=(
|
|
# Core & Dashboard
|
|
["hub"]="/admin/status/overview"
|
|
["portal"]="/admin/secubox/portal"
|
|
["metrics"]="/admin/secubox/metrics"
|
|
["admin"]="/admin/secubox/admin"
|
|
["login"]="/"
|
|
|
|
# Security
|
|
["crowdsec"]="/admin/secubox/crowdsec"
|
|
["waf"]="/admin/secubox/mitmproxy"
|
|
["threats"]="/admin/secubox/threats"
|
|
["threat-analyst"]="/admin/secubox/threat-analyst"
|
|
["dnsguard"]="/admin/secubox/dnsguard"
|
|
["auth"]="/admin/secubox/auth-guardian"
|
|
["clients"]="/admin/secubox/client-guardian"
|
|
["mac"]="/admin/secubox/mac-guardian"
|
|
["iot"]="/admin/secubox/iot-guard"
|
|
["ipblocklist"]="/admin/secubox/ipblocklist"
|
|
["zkp"]="/admin/secubox/zkp"
|
|
["cve"]="/admin/secubox/cve-triage"
|
|
["cookies"]="/admin/secubox/cookie-tracker"
|
|
["avatar-tap"]="/admin/secubox/avatar-tap"
|
|
["interceptor"]="/admin/secubox/interceptor"
|
|
|
|
# Network
|
|
["netmodes"]="/admin/secubox/network-modes"
|
|
["bandwidth"]="/admin/secubox/bandwidth"
|
|
["traffic"]="/admin/secubox/traffic-shaper"
|
|
["haproxy"]="/admin/secubox/haproxy"
|
|
["vhost"]="/admin/secubox/vhost-manager"
|
|
["cdn"]="/admin/secubox/cdn-cache"
|
|
["tweaks"]="/admin/secubox/network-tweaks"
|
|
["routes"]="/admin/secubox/routes-status"
|
|
["netdiag"]="/admin/secubox/netdiag"
|
|
|
|
# Monitoring
|
|
["netdata"]="/admin/secubox/netdata"
|
|
["dpi"]="/admin/secubox/netifyd"
|
|
["dpi-dual"]="/admin/secubox/dpi-dual"
|
|
["device-intel"]="/admin/secubox/device-intel"
|
|
["mediaflow"]="/admin/secubox/media-flow"
|
|
["watchdog"]="/admin/secubox/watchdog"
|
|
["glances"]="/admin/secubox/glances"
|
|
["anomaly"]="/admin/secubox/network-anomaly"
|
|
|
|
# VPN & Mesh
|
|
["wireguard"]="/admin/secubox/wireguard"
|
|
["mesh"]="/admin/secubox/mesh"
|
|
["p2p"]="/admin/secubox/p2p"
|
|
["mirror"]="/admin/secubox/mirror"
|
|
["master-link"]="/admin/secubox/master-link"
|
|
|
|
# DNS
|
|
["dns"]="/admin/secubox/dns-master"
|
|
["vortex-dns"]="/admin/secubox/vortex-dns"
|
|
["meshname"]="/admin/secubox/meshname-dns"
|
|
["dns-provider"]="/admin/secubox/dns-provider"
|
|
|
|
# Privacy
|
|
["tor"]="/admin/secubox/tor-shield"
|
|
["exposure"]="/admin/secubox/exposure"
|
|
|
|
# Publishing
|
|
["metablogizer"]="/admin/secubox/metablogizer"
|
|
["droplet"]="/admin/secubox/droplet"
|
|
["streamforge"]="/admin/secubox/streamlit-forge"
|
|
["streamlit"]="/admin/secubox/streamlit"
|
|
["metacatalog"]="/admin/secubox/metacatalog"
|
|
|
|
# Apps
|
|
["jellyfin"]="/admin/secubox/jellyfin"
|
|
["lyrion"]="/admin/secubox/lyrion"
|
|
["nextcloud"]="/admin/secubox/nextcloud"
|
|
["gitea"]="/admin/secubox/gitea"
|
|
["peertube"]="/admin/secubox/peertube"
|
|
["photoprism"]="/admin/secubox/photoprism"
|
|
["jitsi"]="/admin/secubox/jitsi"
|
|
["matrix"]="/admin/secubox/matrix"
|
|
["domoticz"]="/admin/secubox/domoticz"
|
|
["zigbee"]="/admin/secubox/zigbee2mqtt"
|
|
|
|
# System
|
|
["settings"]="/admin/secubox/settings"
|
|
["config-vault"]="/admin/secubox/config-vault"
|
|
["config-advisor"]="/admin/secubox/config-advisor"
|
|
["smtp"]="/admin/secubox/smtp-relay"
|
|
["reporter"]="/admin/secubox/reporter"
|
|
["rtty"]="/admin/secubox/rtty-remote"
|
|
["backup"]="/admin/secubox/backup"
|
|
["users"]="/admin/secubox/users"
|
|
|
|
# AI
|
|
["ai-gateway"]="/admin/secubox/ai-gateway"
|
|
["ai-insights"]="/admin/secubox/ai-insights"
|
|
["localai"]="/admin/secubox/localai"
|
|
["ollama"]="/admin/secubox/ollama"
|
|
["localrecall"]="/admin/secubox/localrecall"
|
|
|
|
# Theme
|
|
["theme"]="/admin/system/system"
|
|
)
|
|
|
|
# Main
|
|
main() {
|
|
log "SecuBox Screenshot Capture"
|
|
log "Router: $ROUTER"
|
|
log "Output: $OUTPUT_DIR"
|
|
log "Modules: ${#MODULES[@]}"
|
|
echo
|
|
|
|
# Check if puppeteer is available
|
|
if command -v node &>/dev/null && node -e "require('puppeteer')" 2>/dev/null; then
|
|
log "Using Puppeteer for capture"
|
|
USE_PUPPETEER=1
|
|
else
|
|
log "Using headless Chrome directly"
|
|
USE_PUPPETEER=0
|
|
fi
|
|
|
|
# Capture specific module or all
|
|
if [ -n "$1" ]; then
|
|
if [ -n "${MODULES[$1]}" ]; then
|
|
if [ "$USE_PUPPETEER" = "1" ]; then
|
|
capture_with_puppeteer "$1" "${MODULES[$1]}"
|
|
else
|
|
capture "$1" "${MODULES[$1]}"
|
|
fi
|
|
else
|
|
error "Unknown module: $1"
|
|
echo "Available modules:"
|
|
printf '%s\n' "${!MODULES[@]}" | sort | column
|
|
exit 1
|
|
fi
|
|
else
|
|
# Capture all modules
|
|
local count=0
|
|
local total=${#MODULES[@]}
|
|
|
|
for name in $(printf '%s\n' "${!MODULES[@]}" | sort); do
|
|
((count++))
|
|
echo
|
|
log "[$count/$total] $name"
|
|
|
|
if [ "$USE_PUPPETEER" = "1" ]; then
|
|
capture_with_puppeteer "$name" "${MODULES[$name]}" || true
|
|
else
|
|
capture "$name" "${MODULES[$name]}" || true
|
|
fi
|
|
|
|
sleep 1
|
|
done
|
|
|
|
echo
|
|
log "Screenshot capture complete!"
|
|
log "Output directory: $OUTPUT_DIR"
|
|
ls -la "$OUTPUT_DIR"/*.png 2>/dev/null | wc -l | xargs -I{} log "Captured: {} screenshots"
|
|
fi
|
|
}
|
|
|
|
main "$@"
|