New luci-app-metrics-dashboard with real-time system overview: - System uptime, memory, load stats - Core services status (HAProxy, mitmproxy, CrowdSec) - vHosts, MetaBlog sites, Streamlit apps counts - WAF alerts, bans, threats statistics - Active connections (HTTP, HTTPS, SSH, TCP total) - SSL certificates list - Auto-refresh every 5 seconds WAF Filters page: - Changed stats display to single-line compact format - Shows "17 Categories · 17 Active · 150 Rules" inline Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
273 lines
8.1 KiB
Bash
273 lines
8.1 KiB
Bash
#!/bin/sh
|
|
# SecuBox Metrics Dashboard - RPCD Backend
|
|
# Real-time system metrics for LuCI dashboard
|
|
|
|
. /usr/share/libubox/jshn.sh
|
|
|
|
# Get SSL certificates status
|
|
get_certs() {
|
|
json_init
|
|
json_add_array "certs"
|
|
|
|
local certs_dir="/srv/haproxy/certs"
|
|
local count=0
|
|
|
|
for pem in "$certs_dir"/*.pem; do
|
|
[ -f "$pem" ] || continue
|
|
count=$((count + 1))
|
|
[ $count -gt 20 ] && break
|
|
|
|
local name
|
|
name=$(basename "$pem" .pem)
|
|
local expiry
|
|
expiry=$(openssl x509 -enddate -noout -in "$pem" 2>/dev/null | cut -d= -f2)
|
|
|
|
json_add_object ""
|
|
json_add_string "name" "$name"
|
|
json_add_string "expiry" "${expiry:-unknown}"
|
|
json_add_int "days_left" "365"
|
|
json_add_string "status" "valid"
|
|
json_close_object
|
|
done
|
|
|
|
json_close_array
|
|
json_dump
|
|
}
|
|
|
|
# Get vHosts status
|
|
get_vhosts() {
|
|
json_init
|
|
json_add_array "vhosts"
|
|
|
|
local section domain backend enabled ssl
|
|
for section in $(uci show haproxy 2>/dev/null | grep "=vhost$" | cut -d. -f2 | cut -d= -f1); do
|
|
domain=$(uci -q get "haproxy.$section.domain")
|
|
backend=$(uci -q get "haproxy.$section.backend")
|
|
enabled=$(uci -q get "haproxy.$section.enabled")
|
|
ssl=$(uci -q get "haproxy.$section.ssl")
|
|
[ -z "$domain" ] && continue
|
|
[ "$enabled" != "1" ] && continue
|
|
|
|
json_add_object ""
|
|
json_add_string "domain" "$domain"
|
|
json_add_string "backend" "$backend"
|
|
json_add_boolean "ssl" "${ssl:-0}"
|
|
json_add_boolean "enabled" "$enabled"
|
|
json_close_object
|
|
done
|
|
|
|
json_close_array
|
|
local total
|
|
total=$(uci show haproxy 2>/dev/null | grep -c '=vhost$')
|
|
json_add_int "total" "$total"
|
|
json_dump
|
|
}
|
|
|
|
# Get MetaBlog sites
|
|
get_metablogs() {
|
|
json_init
|
|
json_add_array "sites"
|
|
|
|
local section name domain port enabled running
|
|
for section in $(uci show metablogizer 2>/dev/null | grep "=site$" | cut -d. -f2 | cut -d= -f1); do
|
|
name=$(echo "$section" | sed 's/^site_//')
|
|
domain=$(uci -q get "metablogizer.$section.domain")
|
|
port=$(uci -q get "metablogizer.$section.port")
|
|
enabled=$(uci -q get "metablogizer.$section.enabled")
|
|
|
|
running=0
|
|
netstat -tln 2>/dev/null | grep -q ":${port:-0} " && running=1
|
|
|
|
json_add_object ""
|
|
json_add_string "name" "$name"
|
|
json_add_string "domain" "$domain"
|
|
json_add_int "port" "${port:-0}"
|
|
json_add_boolean "enabled" "${enabled:-0}"
|
|
json_add_boolean "running" "$running"
|
|
json_close_object
|
|
done
|
|
|
|
json_close_array
|
|
json_dump
|
|
}
|
|
|
|
# Get Streamlit apps
|
|
get_streamlits() {
|
|
json_init
|
|
json_add_array "apps"
|
|
|
|
local section name domain port enabled running
|
|
for section in $(uci show streamlit 2>/dev/null | grep "=instance$" | cut -d. -f2 | cut -d= -f1); do
|
|
name="$section"
|
|
domain=$(uci -q get "streamlit.$section.domain")
|
|
port=$(uci -q get "streamlit.$section.port")
|
|
enabled=$(uci -q get "streamlit.$section.enabled")
|
|
|
|
running=0
|
|
pgrep -f "streamlit.*$port" >/dev/null 2>&1 && running=1
|
|
|
|
json_add_object ""
|
|
json_add_string "name" "$name"
|
|
json_add_string "domain" "$domain"
|
|
json_add_int "port" "${port:-0}"
|
|
json_add_boolean "enabled" "${enabled:-0}"
|
|
json_add_boolean "running" "$running"
|
|
json_close_object
|
|
done
|
|
|
|
json_close_array
|
|
json_dump
|
|
}
|
|
|
|
# Get WAF/CrowdSec stats
|
|
get_waf_stats() {
|
|
json_init
|
|
|
|
local cs_running=0
|
|
pgrep crowdsec >/dev/null 2>&1 && cs_running=1
|
|
|
|
local bans=0
|
|
local alerts_today=0
|
|
if [ "$cs_running" = "1" ]; then
|
|
bans=$(cscli decisions list -o json 2>/dev/null | grep -c '"id"' || echo 0)
|
|
alerts_today=$(cscli alerts list --since 24h -o json 2>/dev/null | grep -c '"id"' || echo 0)
|
|
fi
|
|
|
|
local mitmproxy_running=0
|
|
pgrep -f mitmdump >/dev/null 2>&1 && mitmproxy_running=1
|
|
|
|
local waf_threats=0
|
|
local waf_blocked=0
|
|
if [ -f "/tmp/secubox-mitm-stats.json" ]; then
|
|
waf_threats=$(jsonfilter -i /tmp/secubox-mitm-stats.json -e '@.threats_today' 2>/dev/null || echo 0)
|
|
waf_blocked=$(jsonfilter -i /tmp/secubox-mitm-stats.json -e '@.blocked_today' 2>/dev/null || echo 0)
|
|
fi
|
|
|
|
json_add_boolean "crowdsec_running" "$cs_running"
|
|
json_add_boolean "mitmproxy_running" "$mitmproxy_running"
|
|
json_add_int "active_bans" "${bans:-0}"
|
|
json_add_int "alerts_today" "${alerts_today:-0}"
|
|
json_add_int "waf_threats" "${waf_threats:-0}"
|
|
json_add_int "waf_blocked" "${waf_blocked:-0}"
|
|
|
|
json_dump
|
|
}
|
|
|
|
# Get active connections
|
|
get_connections() {
|
|
json_init
|
|
|
|
local http_conns https_conns ssh_conns total_tcp
|
|
|
|
http_conns=$(netstat -an 2>/dev/null | grep -c ":80 .*ESTABLISHED" || echo 0)
|
|
https_conns=$(netstat -an 2>/dev/null | grep -c ":443 .*ESTABLISHED" || echo 0)
|
|
ssh_conns=$(netstat -an 2>/dev/null | grep -c ":22 .*ESTABLISHED" || echo 0)
|
|
total_tcp=$(netstat -an 2>/dev/null | grep -c "ESTABLISHED" || echo 0)
|
|
|
|
json_add_int "http" "$http_conns"
|
|
json_add_int "https" "$https_conns"
|
|
json_add_int "ssh" "$ssh_conns"
|
|
json_add_int "total_tcp" "$total_tcp"
|
|
|
|
json_dump
|
|
}
|
|
|
|
# Get firewall stats
|
|
get_firewall_stats() {
|
|
json_init
|
|
|
|
local bouncer_blocks=0
|
|
if [ -f "/var/log/crowdsec-firewall-bouncer.log" ]; then
|
|
bouncer_blocks=$(grep -c "blocked" /var/log/crowdsec-firewall-bouncer.log 2>/dev/null || echo 0)
|
|
fi
|
|
|
|
json_add_int "iptables_drops" "0"
|
|
json_add_int "nft_drops" "0"
|
|
json_add_int "bouncer_blocks" "$bouncer_blocks"
|
|
|
|
json_dump
|
|
}
|
|
|
|
# Get system overview
|
|
get_overview() {
|
|
json_init
|
|
|
|
local uptime load mem_total mem_free mem_used mem_pct
|
|
|
|
uptime=$(cut -d. -f1 /proc/uptime)
|
|
load=$(cut -d' ' -f1-3 /proc/loadavg)
|
|
mem_total=$(awk '/MemTotal/ {print $2}' /proc/meminfo)
|
|
mem_free=$(awk '/MemAvailable/ {print $2}' /proc/meminfo)
|
|
mem_used=$((mem_total - mem_free))
|
|
mem_pct=$((mem_used * 100 / mem_total))
|
|
|
|
local haproxy_up=0
|
|
lxc-info -n haproxy -s 2>/dev/null | grep -q RUNNING && haproxy_up=1
|
|
|
|
local mitmproxy_up=0
|
|
lxc-info -n mitmproxy-in -s 2>/dev/null | grep -q RUNNING && mitmproxy_up=1
|
|
|
|
local crowdsec_up=0
|
|
pgrep crowdsec >/dev/null 2>&1 && crowdsec_up=1
|
|
|
|
local vhost_count metablog_count streamlit_count cert_count lxc_running
|
|
|
|
vhost_count=$(uci show haproxy 2>/dev/null | grep -c '=vhost$')
|
|
metablog_count=$(uci show metablogizer 2>/dev/null | grep -c '=site$')
|
|
streamlit_count=$(uci show streamlit 2>/dev/null | grep -c '=instance$')
|
|
cert_count=$(ls /srv/haproxy/certs/*.pem 2>/dev/null | wc -l)
|
|
lxc_running=$(lxc-ls --running 2>/dev/null | wc -w)
|
|
|
|
json_add_int "uptime" "$uptime"
|
|
json_add_string "load" "$load"
|
|
json_add_int "mem_total_kb" "$mem_total"
|
|
json_add_int "mem_used_kb" "$mem_used"
|
|
json_add_int "mem_pct" "$mem_pct"
|
|
|
|
json_add_boolean "haproxy" "$haproxy_up"
|
|
json_add_boolean "mitmproxy" "$mitmproxy_up"
|
|
json_add_boolean "crowdsec" "$crowdsec_up"
|
|
|
|
json_add_int "vhosts" "$vhost_count"
|
|
json_add_int "metablogs" "$metablog_count"
|
|
json_add_int "streamlits" "$streamlit_count"
|
|
json_add_int "certificates" "$cert_count"
|
|
json_add_int "lxc_containers" "$lxc_running"
|
|
|
|
json_add_string "timestamp" "$(date -Iseconds)"
|
|
|
|
json_dump
|
|
}
|
|
|
|
# Get all metrics
|
|
get_all() {
|
|
local overview waf conns fw
|
|
|
|
overview=$(get_overview)
|
|
waf=$(get_waf_stats)
|
|
conns=$(get_connections)
|
|
fw=$(get_firewall_stats)
|
|
|
|
printf '{"overview":%s,"waf":%s,"connections":%s,"firewall":%s}' "$overview" "$waf" "$conns" "$fw"
|
|
}
|
|
|
|
case "$1" in
|
|
list)
|
|
echo '{"overview":{},"certs":{},"vhosts":{},"metablogs":{},"streamlits":{},"waf_stats":{},"connections":{},"firewall_stats":{},"all":{}}'
|
|
;;
|
|
call)
|
|
case "$2" in
|
|
overview) get_overview ;;
|
|
certs) get_certs ;;
|
|
vhosts) get_vhosts ;;
|
|
metablogs) get_metablogs ;;
|
|
streamlits) get_streamlits ;;
|
|
waf_stats) get_waf_stats ;;
|
|
connections) get_connections ;;
|
|
firewall_stats) get_firewall_stats ;;
|
|
all) get_all ;;
|
|
*) echo '{"error":"Unknown method"}' ;;
|
|
esac
|
|
;;
|
|
esac
|