Was timing out with 199 HAProxy vhosts due to ~600 UCI calls. Optimizations: - get_haproxy_vhosts(): Single uci show + awk parsing instead of per-vhost uci -q get calls (600 calls → 1 call) - get_init_services(): Check only key services, use symlink detection instead of executing init scripts - get_metrics_summary(): Read CrowdSec data from cache file instead of slow cscli commands Result: Handler now responds in <1s with 204 published services. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
339 lines
8.8 KiB
Bash
Executable File
339 lines
8.8 KiB
Bash
Executable File
#!/bin/sh
|
|
# RPCD backend for SecuBox Dynamic Services Registry
|
|
# Aggregates all SecuBox services with their published URLs, emojis, and metrics
|
|
|
|
. /lib/functions.sh
|
|
. /usr/share/libubox/jshn.sh
|
|
|
|
# Service emoji registry
|
|
get_service_emoji() {
|
|
case "$1" in
|
|
mitmproxy*) echo "🔍" ;;
|
|
crowdsec*) echo "🛡️" ;;
|
|
haproxy*) echo "⚡" ;;
|
|
squid*|cdn*) echo "💾" ;;
|
|
wireguard*|wg*) echo "🔐" ;;
|
|
tor*) echo "🧅" ;;
|
|
dnsmasq*) echo "🌐" ;;
|
|
netdata*) echo "📊" ;;
|
|
glances*) echo "📈" ;;
|
|
sshd*|ssh*) echo "🔑" ;;
|
|
uhttpd*) echo "🖥️" ;;
|
|
iot*) echo "📡" ;;
|
|
vortex*) echo "🌀" ;;
|
|
interceptor*) echo "🧙" ;;
|
|
cookie*) echo "🍪" ;;
|
|
system*) echo "⚙️" ;;
|
|
*) echo "📦" ;;
|
|
esac
|
|
}
|
|
|
|
# Get service category
|
|
get_service_category() {
|
|
case "$1" in
|
|
mitmproxy*|crowdsec*|firewall*|nftables*) echo "security" ;;
|
|
haproxy*|nginx*) echo "routing" ;;
|
|
squid*|cdn*) echo "caching" ;;
|
|
wireguard*|wg*|vpn*) echo "vpn" ;;
|
|
tor*) echo "anonymity" ;;
|
|
dnsmasq*|dns*|vortex*) echo "dns" ;;
|
|
netdata*|glances*|monitoring*) echo "monitoring" ;;
|
|
uhttpd*|luci*) echo "web" ;;
|
|
iot*) echo "iot" ;;
|
|
*) echo "other" ;;
|
|
esac
|
|
}
|
|
|
|
# Get HAProxy vhosts as published services (optimized single-pass)
|
|
get_haproxy_vhosts() {
|
|
local count=0
|
|
local tmpfile="/tmp/haproxy_vhosts_$$"
|
|
|
|
# Single UCI call, parse with awk into temp file
|
|
uci show haproxy 2>/dev/null | awk -F'[.=]' '
|
|
/=vhost$/ { section=$2; vhosts[section]=1 }
|
|
/\.domain=/ { gsub(/\047/, "", $NF); domain[$2]=$NF }
|
|
/\.backend=/ { gsub(/\047/, "", $NF); backend[$2]=$NF }
|
|
/\.ssl_enabled=/ { gsub(/\047/, "", $NF); ssl[$2]=$NF }
|
|
END {
|
|
for (s in vhosts) {
|
|
if (domain[s] != "") {
|
|
print s "|" domain[s] "|" backend[s] "|" ssl[s]
|
|
}
|
|
}
|
|
}' > "$tmpfile"
|
|
|
|
# Process temp file with jshn
|
|
while IFS='|' read -r vhost domain backend ssl; do
|
|
[ -z "$domain" ] && continue
|
|
local proto="http"
|
|
[ "$ssl" = "1" ] && proto="https"
|
|
|
|
json_add_object
|
|
json_add_string "id" "haproxy-$vhost"
|
|
json_add_string "name" "$domain"
|
|
json_add_string "type" "vhost"
|
|
json_add_string "url" "${proto}://${domain}"
|
|
json_add_string "backend" "$backend"
|
|
json_add_string "emoji" "🌐"
|
|
json_add_string "category" "published"
|
|
json_add_boolean "ssl" "${ssl:-0}"
|
|
json_close_object
|
|
count=$((count + 1))
|
|
done < "$tmpfile"
|
|
|
|
rm -f "$tmpfile"
|
|
echo "$count"
|
|
}
|
|
|
|
# Get Tor hidden services
|
|
get_tor_services() {
|
|
local count=0
|
|
local onion_dir="/var/lib/tor/hidden_services"
|
|
|
|
if [ -d "$onion_dir" ]; then
|
|
for svc in "$onion_dir"/*/hostname; do
|
|
[ -f "$svc" ] || continue
|
|
local name=$(dirname "$svc" | xargs basename)
|
|
local onion=$(cat "$svc" 2>/dev/null)
|
|
[ -z "$onion" ] && continue
|
|
|
|
json_add_object
|
|
json_add_string "id" "tor-$name"
|
|
json_add_string "name" "$name"
|
|
json_add_string "type" "onion"
|
|
json_add_string "url" "http://${onion}"
|
|
json_add_string "onion" "$onion"
|
|
json_add_string "emoji" "🧅"
|
|
json_add_string "category" "anonymity"
|
|
json_close_object
|
|
count=$((count + 1))
|
|
done
|
|
fi
|
|
|
|
echo "$count"
|
|
}
|
|
|
|
# Get mitmproxy instances
|
|
get_mitmproxy_instances() {
|
|
local count=0
|
|
|
|
for inst in $(uci show mitmproxy 2>/dev/null | grep "=instance" | cut -d'.' -f2 | cut -d'=' -f1); do
|
|
local enabled=$(uci -q get mitmproxy.$inst.enabled || echo 0)
|
|
local desc=$(uci -q get mitmproxy.$inst.description || echo "")
|
|
local web_port=$(uci -q get mitmproxy.$inst.web_port || echo "8081")
|
|
local proxy_port=$(uci -q get mitmproxy.$inst.proxy_port || echo "8888")
|
|
local container=$(uci -q get mitmproxy.$inst.container_name || echo "mitmproxy-$inst")
|
|
local mode=$(uci -q get mitmproxy.$inst.mode || echo "regular")
|
|
|
|
# Check if running
|
|
local running=0
|
|
lxc-info -n "$container" -s 2>/dev/null | grep -q "RUNNING" && running=1
|
|
|
|
json_add_object
|
|
json_add_string "id" "mitmproxy-$inst"
|
|
json_add_string "name" "mitmproxy-$inst"
|
|
json_add_string "type" "proxy"
|
|
json_add_string "url" "http://c3box.sblocal:$web_port"
|
|
json_add_string "description" "$desc"
|
|
json_add_string "emoji" "🔍"
|
|
json_add_string "category" "security"
|
|
json_add_string "mode" "$mode"
|
|
json_add_int "proxy_port" "$proxy_port"
|
|
json_add_int "web_port" "$web_port"
|
|
json_add_boolean "enabled" "$enabled"
|
|
json_add_boolean "running" "$running"
|
|
json_close_object
|
|
count=$((count + 1))
|
|
done
|
|
|
|
echo "$count"
|
|
}
|
|
|
|
# Get init.d services with status (optimized - skip slow checks)
|
|
get_init_services() {
|
|
local count=0
|
|
|
|
# Only check key SecuBox services (fast list)
|
|
for name in crowdsec haproxy mitmproxy wireguard tor dnsmasq uhttpd rpcd netifyd sshd; do
|
|
local svc="/etc/init.d/$name"
|
|
[ -x "$svc" ] || continue
|
|
|
|
local enabled=0
|
|
local running=0
|
|
|
|
# Check enabled via symlink (faster than calling script)
|
|
[ -L "/etc/rc.d/S"*"$name" ] 2>/dev/null && enabled=1
|
|
|
|
# Check running via pgrep
|
|
pgrep -x "$name" >/dev/null 2>&1 && running=1
|
|
|
|
local emoji=$(get_service_emoji "$name")
|
|
local category=$(get_service_category "$name")
|
|
|
|
json_add_object
|
|
json_add_string "id" "initd-$name"
|
|
json_add_string "name" "$name"
|
|
json_add_string "type" "service"
|
|
json_add_string "emoji" "$emoji"
|
|
json_add_string "category" "$category"
|
|
json_add_boolean "enabled" "$enabled"
|
|
json_add_boolean "running" "$running"
|
|
json_close_object
|
|
count=$((count + 1))
|
|
done
|
|
|
|
echo "$count"
|
|
}
|
|
|
|
# Get LuCI apps as dashboard links
|
|
get_luci_apps() {
|
|
local count=0
|
|
|
|
# Parse menu.d files for SecuBox apps
|
|
for menu in /usr/share/luci/menu.d/luci-app-*.json; do
|
|
[ -f "$menu" ] || continue
|
|
|
|
local app_name=$(basename "$menu" .json | sed 's/luci-app-//')
|
|
local title=$(jsonfilter -i "$menu" -e '@.admin.*.title' 2>/dev/null | head -1)
|
|
local path=$(jsonfilter -i "$menu" -e '@.admin.*.action.path' 2>/dev/null | head -1)
|
|
|
|
[ -z "$title" ] && title="$app_name"
|
|
[ -z "$path" ] && path="admin/$app_name"
|
|
|
|
local emoji=$(get_service_emoji "$app_name")
|
|
|
|
json_add_object
|
|
json_add_string "id" "luci-$app_name"
|
|
json_add_string "name" "$title"
|
|
json_add_string "type" "dashboard"
|
|
json_add_string "url" "/cgi-bin/luci/$path"
|
|
json_add_string "emoji" "$emoji"
|
|
json_add_string "category" "web"
|
|
json_close_object
|
|
count=$((count + 1))
|
|
done
|
|
|
|
echo "$count"
|
|
}
|
|
|
|
# Get metrics summary (optimized - skip slow cscli)
|
|
get_metrics_summary() {
|
|
json_add_object "metrics"
|
|
|
|
# System metrics from /proc (fast)
|
|
local uptime=$(cut -d' ' -f1 /proc/uptime | cut -d'.' -f1)
|
|
local load=$(cut -d' ' -f1 /proc/loadavg)
|
|
|
|
# Memory from /proc/meminfo in one read
|
|
eval $(awk '/^MemTotal:/{t=$2} /^MemAvailable:/{a=$2} END{printf "mem_total=%d mem_avail=%d", t, a}' /proc/meminfo)
|
|
local mem_pct=$(( (mem_total - mem_avail) * 100 / mem_total ))
|
|
|
|
json_add_int "uptime" "$uptime"
|
|
json_add_string "load" "$load"
|
|
json_add_int "mem_pct" "$mem_pct"
|
|
|
|
# Service counts from cached data or fast check
|
|
local running=$(pgrep -c . 2>/dev/null || echo 0)
|
|
local total=$(ls -1 /etc/init.d/ 2>/dev/null | wc -l)
|
|
|
|
json_add_int "services_running" "$running"
|
|
json_add_int "services_total" "$total"
|
|
|
|
# CrowdSec metrics from cache file (fast)
|
|
if [ -f /tmp/secubox/crowdsec-overview.json ]; then
|
|
local alerts=$(jsonfilter -i /tmp/secubox/crowdsec-overview.json -e '@.alerts_24h' 2>/dev/null || echo 0)
|
|
local bans=$(jsonfilter -i /tmp/secubox/crowdsec-overview.json -e '@.active_decisions' 2>/dev/null || echo 0)
|
|
json_add_int "crowdsec_alerts" "${alerts:-0}"
|
|
json_add_int "crowdsec_bans" "${bans:-0}"
|
|
else
|
|
json_add_int "crowdsec_alerts" 0
|
|
json_add_int "crowdsec_bans" 0
|
|
fi
|
|
|
|
json_close_object
|
|
}
|
|
|
|
case "$1" in
|
|
list)
|
|
json_init
|
|
json_add_object "getServices"
|
|
json_close_object
|
|
json_add_object "getPublished"
|
|
json_close_object
|
|
json_add_object "getMetrics"
|
|
json_close_object
|
|
json_add_object "getAll"
|
|
json_close_object
|
|
json_dump
|
|
;;
|
|
|
|
call)
|
|
case "$2" in
|
|
getServices)
|
|
json_init
|
|
json_add_boolean "success" 1
|
|
json_add_array "services"
|
|
get_init_services >/dev/null
|
|
json_close_array
|
|
json_dump
|
|
;;
|
|
|
|
getPublished)
|
|
json_init
|
|
json_add_boolean "success" 1
|
|
json_add_array "published"
|
|
get_haproxy_vhosts >/dev/null
|
|
get_tor_services >/dev/null
|
|
json_close_array
|
|
json_dump
|
|
;;
|
|
|
|
getMetrics)
|
|
json_init
|
|
json_add_boolean "success" 1
|
|
get_metrics_summary
|
|
json_dump
|
|
;;
|
|
|
|
getAll)
|
|
json_init
|
|
json_add_boolean "success" 1
|
|
|
|
# All services
|
|
json_add_array "services"
|
|
get_init_services >/dev/null
|
|
json_close_array
|
|
|
|
# Published URLs (vhosts, onions)
|
|
json_add_array "published"
|
|
get_haproxy_vhosts >/dev/null
|
|
get_tor_services >/dev/null
|
|
json_close_array
|
|
|
|
# Proxy instances
|
|
json_add_array "proxies"
|
|
get_mitmproxy_instances >/dev/null
|
|
json_close_array
|
|
|
|
# LuCI dashboards
|
|
json_add_array "dashboards"
|
|
get_luci_apps >/dev/null
|
|
json_close_array
|
|
|
|
# Metrics
|
|
get_metrics_summary
|
|
|
|
json_dump
|
|
;;
|
|
|
|
*)
|
|
json_init
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Unknown method"
|
|
json_dump
|
|
;;
|
|
esac
|
|
;;
|
|
esac
|