fix(multi): HAProxy duplicate server, Streamlit headless, dashboard optimization

Fixes:
- HAProxy: Prevent duplicate server names when both inline and separate
  server UCI sections exist for same backend
- Streamlit: Force --server.headless=true in start script (required for server)
- Dashboard: Optimize get_dashboard_data RPC call (6.56s → 0.09s) by using
  fast catalog counting instead of slow appstore list command
- Exposure: Add themed dashboard with SecuBox styling
- ACL: Add missing RPCD permissions for various LuCI apps

Version bumps:
- luci-app-exposure: 1.0.0-r3
- secubox-core: 0.10.0-r5
- secubox-app-haproxy: 1.0.0-r18
- secubox-app-streamlit: 1.0.0-r2
- Portal: v0.15.51

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
CyberMind-FR 2026-01-26 11:04:02 +01:00
parent e79a643134
commit 26daa57a4b
15 changed files with 2335 additions and 363 deletions

View File

@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-exposure
PKG_VERSION:=1.0.0
PKG_RELEASE:=2
PKG_RELEASE:=3
PKG_MAINTAINER:=SecuBox Team <contact@secubox.dev>
PKG_LICENSE:=MIT

View File

@ -137,25 +137,26 @@ case "$1" in
json_close_array
json_close_object
# HAProxy SSL backends - use temp file to avoid subshell
HAPROXY_CONFIG="/srv/lxc/haproxy/rootfs/etc/haproxy/haproxy.cfg"
ssl_count=0
[ -f "$HAPROXY_CONFIG" ] && ssl_count=$(grep -c "^backend.*_backend$" "$HAPROXY_CONFIG" 2>/dev/null || echo 0)
# HAProxy SSL backends - read from UCI config
TMP_SSL="/tmp/exposure_ssl_$$"
if [ -f "$HAPROXY_CONFIG" ]; then
grep -E "^backend .+_backend$" "$HAPROXY_CONFIG" 2>/dev/null | while read line; do
backend=$(echo "$line" | awk '{print $2}' | sed 's/_backend$//')
domain=$(grep "acl host_${backend} " "$HAPROXY_CONFIG" 2>/dev/null | awk '{print $NF}')
echo "$backend ${domain:-N/A}"
done > "$TMP_SSL"
fi
ssl_count=0
# Get vhosts from UCI (enabled ones with domains)
for vhost in $(uci show haproxy 2>/dev/null | grep "=vhost$" | cut -d'.' -f2 | cut -d'=' -f1); do
domain=$(uci -q get "haproxy.${vhost}.domain")
backend=$(uci -q get "haproxy.${vhost}.backend")
enabled=$(uci -q get "haproxy.${vhost}.enabled")
[ "$enabled" != "1" ] && continue
[ -z "$domain" ] && continue
echo "${backend:-$vhost}|${domain}" >> "$TMP_SSL"
ssl_count=$((ssl_count + 1))
done
json_add_object "ssl"
json_add_int "count" "$ssl_count"
json_add_array "backends"
if [ -f "$TMP_SSL" ]; then
while read backend domain; do
while IFS='|' read backend domain; do
[ -z "$backend" ] && continue
json_add_object ""
json_add_string "service" "$backend"
@ -203,24 +204,31 @@ case "$1" in
;;
ssl_list)
HAPROXY_CONFIG="/srv/lxc/haproxy/rootfs/etc/haproxy/haproxy.cfg"
TMP_SSLLIST="/tmp/exposure_ssllist_$$"
> "$TMP_SSLLIST"
# Extract backend info to temp file to avoid subshell issues
if [ -f "$HAPROXY_CONFIG" ]; then
grep -E "^backend .+_backend$" "$HAPROXY_CONFIG" 2>/dev/null | while read line; do
backend=$(echo "$line" | awk '{print $2}')
service=$(echo "$backend" | sed 's/_backend$//')
domain=$(grep "acl host_${service} " "$HAPROXY_CONFIG" 2>/dev/null | awk '{print $NF}')
server=$(grep -A5 "backend $backend" "$HAPROXY_CONFIG" 2>/dev/null | grep "server " | awk '{print $3}')
echo "$service|${domain:-N/A}|${server:-N/A}"
done > "$TMP_SSLLIST"
fi
# Read from HAProxy UCI config (vhosts with their backends)
for vhost in $(uci show haproxy 2>/dev/null | grep "=vhost$" | cut -d'.' -f2 | cut -d'=' -f1); do
domain=$(uci -q get "haproxy.${vhost}.domain")
backend=$(uci -q get "haproxy.${vhost}.backend")
enabled=$(uci -q get "haproxy.${vhost}.enabled")
[ "$enabled" != "1" ] && continue
[ -z "$domain" ] && continue
# Get server address from backend config
server=""
if [ -n "$backend" ]; then
server=$(uci -q get "haproxy.${backend}.server" 2>/dev/null | head -1 | awk '{print $2}')
fi
echo "${backend:-$vhost}|${domain}|${server:-N/A}" >> "$TMP_SSLLIST"
done
json_init
json_add_array "backends"
if [ -f "$TMP_SSLLIST" ]; then
if [ -s "$TMP_SSLLIST" ]; then
while IFS='|' read service domain server; do
[ -z "$service" ] && continue
json_add_object ""
@ -229,8 +237,8 @@ case "$1" in
json_add_string "backend" "$server"
json_close_object
done < "$TMP_SSLLIST"
rm -f "$TMP_SSLLIST"
fi
rm -f "$TMP_SSLLIST"
json_close_array
json_dump

View File

@ -15,7 +15,8 @@
"list_acls",
"list_redirects",
"get_settings",
"get_logs"
"get_logs",
"list_exposed_services"
]
},
"uci": ["haproxy"]

View File

@ -179,7 +179,7 @@ return view.extend({
E('div', { 'class': 'sb-portal-brand' }, [
E('div', { 'class': 'sb-portal-logo' }, 'S'),
E('span', { 'class': 'sb-portal-title' }, 'SecuBox'),
E('span', { 'class': 'sb-portal-version' }, 'v0.15.48')
E('span', { 'class': 'sb-portal-version' }, 'v0.15.51')
]),
// Navigation
E('nav', { 'class': 'sb-portal-nav' },

View File

@ -9,7 +9,8 @@
"get_threat_history",
"get_stats_by_type",
"get_stats_by_host",
"get_blocked_ips"
"get_blocked_ips",
"get_security_stats"
],
"luci.crowdsec-dashboard": [
"decisions",

View File

@ -32,7 +32,10 @@
"listSnapshots",
"get_appstore_apps",
"get_appstore_app",
"get_public_ips"
"get_public_ips",
"get_network_health",
"get_vital_services",
"get_full_health_report"
],
"uci": [
"get",

View File

@ -302,7 +302,13 @@ return view.extend({
var reader = new FileReader();
reader.onload = function(e) {
var content = btoa(e.target.result);
// Convert ArrayBuffer to base64 (handles UTF-8 correctly)
var bytes = new Uint8Array(e.target.result);
var binary = '';
for (var i = 0; i < bytes.byteLength; i++) {
binary += String.fromCharCode(bytes[i]);
}
var content = btoa(binary);
api.uploadApp(name, content).then(function(result) {
if (result && result.success) {
@ -317,7 +323,7 @@ return view.extend({
ui.addNotification(null, E('p', {}, _('Upload failed: ') + err.message), 'error');
});
};
reader.readAsText(file);
reader.readAsArrayBuffer(file);
},
handleActivate: function(name) {

View File

@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=secubox-app-haproxy
PKG_VERSION:=1.0.0
PKG_RELEASE:=16
PKG_RELEASE:=18
PKG_MAINTAINER:=CyberMind <contact@cybermind.fr>
PKG_LICENSE:=MIT

View File

@ -246,17 +246,14 @@ echo "Config: $CONFIG_FILE"
ls -la /opt/haproxy/
ls -la /opt/haproxy/certs/ 2>/dev/null || echo "No certs dir"
# Fix certificate key naming for HAProxy compatibility
# HAProxy expects .crt.key when it finds a .crt file in the directory
# Clean up legacy certificate files - only .pem files should exist
# HAProxy loads all files from certs directory, and extra files cause errors
if [ -d "/opt/haproxy/certs" ]; then
for crt in /opt/haproxy/certs/*.crt; do
[ -f "$crt" ] || continue
base="${crt%.crt}"
# If .key exists but .crt.key doesn't, rename it
if [ -f "${base}.key" ] && [ ! -f "${crt}.key" ]; then
echo "[haproxy] Renaming ${base}.key -> ${crt}.key"
mv "${base}.key" "${crt}.key"
fi
for pem in /opt/haproxy/certs/*.pem; do
[ -f "$pem" ] || continue
base="${pem%.pem}"
# Remove any associated .crt, .key, .fullchain.pem, .crt.key files
rm -f "${base}.crt" "${base}.key" "${base}.crt.key" "${base}.fullchain.pem" 2>/dev/null
done
fi
@ -517,10 +514,26 @@ _generate_backend() {
[ -n "$health_check" ] && echo " option $health_check"
# Add servers defined in backend section (handles both single and list)
local server_line
config_get server_line "$section" server ""
[ -n "$server_line" ] && echo " server $server_line"
# Check if there are separate server sections for this backend
local has_server_sections=0
_check_server_sections() {
local srv_section="$1"
local srv_backend
config_get srv_backend "$srv_section" backend
config_get srv_enabled "$srv_section" enabled "0"
if [ "$srv_backend" = "$name" ] && [ "$srv_enabled" = "1" ]; then
has_server_sections=1
fi
}
config_foreach _check_server_sections server
# Add inline server ONLY if no separate server sections exist
# This prevents duplicate server names
if [ "$has_server_sections" = "0" ]; then
local server_line
config_get server_line "$section" server ""
[ -n "$server_line" ] && echo " server $server_line"
fi
# Add servers from separate server UCI sections
config_foreach _add_server_to_backend server "$name"
@ -783,12 +796,9 @@ cmd_cert_add() {
cat "$CERTS_PATH/$domain.fullchain.pem" "$CERTS_PATH/$domain.key" > "$CERTS_PATH/$domain.pem"
chmod 600 "$CERTS_PATH/$domain.pem"
# HAProxy expects key files named <cert>.key when loading .crt files from directory
# Rename the key file to match the .crt file naming convention
if [ -f "$CERTS_PATH/$domain.crt" ] && [ -f "$CERTS_PATH/$domain.key" ]; then
mv "$CERTS_PATH/$domain.key" "$CERTS_PATH/$domain.crt.key"
chmod 600 "$CERTS_PATH/$domain.crt.key"
fi
# Clean up intermediate files - HAProxy only needs the .pem file
# Keeping these causes issues when HAProxy loads certs from directory
rm -f "$CERTS_PATH/$domain.crt" "$CERTS_PATH/$domain.key" "$CERTS_PATH/$domain.fullchain.pem" "$CERTS_PATH/$domain.crt.key" 2>/dev/null
fi
# Restart HAProxy if it was running

View File

@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=secubox-app-streamlit
PKG_VERSION:=1.0.0
PKG_RELEASE:=1
PKG_RELEASE:=2
PKG_ARCH:=all
PKG_MAINTAINER:=CyberMind Studio <contact@cybermind.fr>

View File

@ -187,7 +187,7 @@ cd /srv/apps
exec streamlit run "$APP_PATH" \
--server.address="${STREAMLIT_HOST:-0.0.0.0}" \
--server.port="${STREAMLIT_PORT:-8501}" \
--server.headless="${STREAMLIT_HEADLESS:-true}" \
--server.headless=true \
--browser.gatherUsageStats="${STREAMLIT_STATS:-false}" \
--theme.base="${STREAMLIT_THEME_BASE:-dark}" \
--theme.primaryColor="${STREAMLIT_THEME_PRIMARY:-#0ff}"

View File

@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=secubox-core
PKG_VERSION:=0.10.0
PKG_RELEASE:=4
PKG_RELEASE:=5
PKG_ARCH:=all
PKG_LICENSE:=GPL-2.0
PKG_MAINTAINER:=SecuBox Team

View File

@ -71,6 +71,15 @@ case "$1" in
json_add_object "getHealth"
json_close_object
json_add_object "get_network_health"
json_close_object
json_add_object "get_vital_services"
json_close_object
json_add_object "get_full_health_report"
json_close_object
json_add_object "getLogs"
json_add_string "service" "string"
json_add_int "lines" "integer"
@ -330,18 +339,385 @@ case "$1" in
/usr/sbin/secubox-core health
;;
get_network_health)
# Network health monitoring - detects CRC errors, link flapping
DMESG_LINES=500
FLAP_THRESHOLD=5
CRC_THRESHOLD=10
json_init
json_add_string "timestamp" "$(date -Iseconds)"
json_add_object "interfaces"
overall="healthy"
critical_count=0
warning_count=0
for iface_path in /sys/class/net/eth* /sys/class/net/wan* /sys/class/net/lan*; do
[ -d "$iface_path" ] || continue
[ -d "$iface_path/device" ] || continue
iface=$(basename "$iface_path")
current_state=$(cat "$iface_path/operstate" 2>/dev/null || echo "unknown")
crc_count=$(dmesg | tail -n $DMESG_LINES | grep -c "$iface.*crc error" 2>/dev/null)
crc_count=${crc_count:-0}
link_up=$(dmesg | tail -n $DMESG_LINES | grep -c "$iface: Link is Up" 2>/dev/null)
link_up=${link_up:-0}
link_down=$(dmesg | tail -n $DMESG_LINES | grep -c "$iface: Link is Down" 2>/dev/null)
link_down=${link_down:-0}
link_changes=$((link_up + link_down))
status="ok"
issues=""
if [ "$crc_count" -ge "$CRC_THRESHOLD" ]; then
status="critical"
issues="CRC errors ($crc_count)"
critical_count=$((critical_count + 1))
fi
if [ "$link_changes" -ge "$FLAP_THRESHOLD" ]; then
[ "$status" = "ok" ] && status="warning"
[ -n "$issues" ] && issues="$issues; "
issues="${issues}Link flapping ($link_changes changes)"
warning_count=$((warning_count + 1))
fi
rx_errors=$(cat "$iface_path/statistics/rx_errors" 2>/dev/null || echo 0)
tx_errors=$(cat "$iface_path/statistics/tx_errors" 2>/dev/null || echo 0)
json_add_object "$iface"
json_add_string "status" "$status"
json_add_string "state" "$current_state"
json_add_int "crc_errors" "$crc_count"
json_add_int "link_changes" "$link_changes"
json_add_int "rx_errors" "$rx_errors"
json_add_int "tx_errors" "$tx_errors"
json_add_string "issues" "$issues"
json_close_object
done
json_close_object
if [ "$critical_count" -gt 0 ]; then
overall="critical"
elif [ "$warning_count" -gt 0 ]; then
overall="warning"
fi
json_add_string "overall" "$overall"
json_add_int "critical_interfaces" "$critical_count"
json_add_int "warning_interfaces" "$warning_count"
if [ "$overall" != "healthy" ]; then
json_add_array "recommendations"
[ "$critical_count" -gt 0 ] && json_add_string "" "Check/replace Ethernet cables"
[ "$critical_count" -gt 0 ] && json_add_string "" "Try different port on switch/modem"
[ "$warning_count" -gt 0 ] && json_add_string "" "Monitor link stability"
json_close_array
fi
json_dump
;;
get_vital_services)
# Vital services monitoring for web hosting and remote management
json_init
json_add_string "timestamp" "$(date -Iseconds)"
# Helper function to check service
check_service() {
local name="$1"
local category="$2"
local check_type="$3"
local check_value="$4"
local description="$5"
local critical="$6"
local status="unknown"
local details=""
case "$check_type" in
process)
if pgrep -f "$check_value" >/dev/null 2>&1; then
status="running"
else
status="stopped"
fi
;;
port)
if netstat -tln 2>/dev/null | grep -q ":${check_value} "; then
status="running"
details="Port $check_value listening"
else
status="stopped"
details="Port $check_value not listening"
fi
;;
init)
if [ -f "/etc/init.d/$check_value" ]; then
if /etc/init.d/$check_value enabled 2>/dev/null; then
if /etc/init.d/$check_value running 2>/dev/null; then
status="running"
else
status="stopped"
fi
else
status="disabled"
fi
else
status="not_installed"
fi
;;
lxc)
if lxc-info -n "$check_value" -s 2>/dev/null | grep -q "RUNNING"; then
status="running"
elif lxc-info -n "$check_value" 2>/dev/null | grep -q "State"; then
status="stopped"
else
status="not_installed"
fi
;;
file)
if [ -f "$check_value" ]; then
status="present"
else
status="missing"
fi
;;
esac
json_add_object ""
json_add_string "name" "$name"
json_add_string "category" "$category"
json_add_string "status" "$status"
json_add_string "description" "$description"
json_add_boolean "critical" "${critical:-0}"
[ -n "$details" ] && json_add_string "details" "$details"
json_close_object
}
# Core Infrastructure Services
json_add_array "core"
check_service "SSH" "remote" "port" "22" "Remote shell access" 1
check_service "HTTPS Admin" "remote" "port" "8444" "LuCI admin interface" 1
check_service "DNS" "network" "port" "53" "Domain name resolution" 1
check_service "DHCP" "network" "process" "dnsmasq" "IP address assignment" 1
check_service "Firewall" "security" "process" "fw4" "Network firewall" 1
json_close_array
# Security Services
json_add_array "security"
check_service "CrowdSec" "security" "process" "crowdsec" "Intrusion prevention" 1
check_service "CrowdSec Bouncer" "security" "process" "crowdsec-firewall-bouncer" "Firewall bouncer" 1
check_service "Tor" "privacy" "init" "tor" "Anonymous routing" 0
json_close_array
# Web Publishing Services
json_add_array "publishers"
check_service "HAProxy" "proxy" "lxc" "haproxy" "Load balancer & reverse proxy" 1
check_service "HexoJS" "cms" "lxc" "hexojs" "Static blog generator" 0
check_service "Gitea" "devops" "lxc" "gitea" "Git repository hosting" 0
check_service "Streamlit" "app" "lxc" "streamlit" "Python web apps" 0
json_close_array
# Media & App Services
json_add_array "apps"
check_service "Lyrion" "media" "lxc" "lyrion" "Music streaming server" 0
check_service "MagicMirror" "display" "lxc" "magicmirror2" "Smart mirror display" 0
check_service "PicoBrew" "app" "lxc" "picobrew" "Brewing automation" 0
json_close_array
# Monitoring Services
json_add_array "monitoring"
check_service "Netifyd" "monitoring" "process" "netifyd" "Network intelligence" 0
check_service "Syslog-ng" "logging" "process" "syslog-ng" "System logging" 1
json_close_array
# Calculate summary
json_add_object "summary"
total=0
running=0
stopped=0
critical_down=0
for svc in /etc/init.d/*; do
[ -x "$svc" ] || continue
total=$((total + 1))
done
# Count running LXC containers
lxc_running=$(lxc-ls --running 2>/dev/null | wc -w)
lxc_total=$(lxc-ls 2>/dev/null | wc -w)
json_add_int "init_services" "$total"
json_add_int "lxc_running" "$lxc_running"
json_add_int "lxc_total" "$lxc_total"
json_close_object
json_dump
;;
get_full_health_report)
# Combined health report: network + services + system
json_init
json_add_string "timestamp" "$(date -Iseconds)"
json_add_string "hostname" "$(uci get system.@system[0].hostname 2>/dev/null || hostname)"
# System info
json_add_object "system"
json_add_int "uptime" "$(cut -d. -f1 /proc/uptime)"
json_add_string "load" "$(cut -d' ' -f1-3 /proc/loadavg)"
mem_total=$(awk '/MemTotal/ {print $2}' /proc/meminfo)
mem_avail=$(awk '/MemAvailable/ {print $2}' /proc/meminfo)
mem_avail=${mem_avail:-0}
mem_used=$((mem_total - mem_avail))
mem_pct=$((mem_used * 100 / mem_total))
json_add_int "memory_percent" "$mem_pct"
disk_pct=$(df / | tail -1 | awk '{print $5}' | tr -d '%')
json_add_int "disk_percent" "${disk_pct:-0}"
json_close_object
# Network Health Summary
json_add_object "network"
net_overall="healthy"
net_issues=0
for iface_path in /sys/class/net/eth* /sys/class/net/wan*; do
[ -d "$iface_path" ] || continue
[ -d "$iface_path/device" ] || continue
iface=$(basename "$iface_path")
crc=$(dmesg | tail -n 500 | grep -c "$iface.*crc error" 2>/dev/null)
crc=${crc:-0}
flap=$(dmesg | tail -n 500 | grep -c "$iface: Link is" 2>/dev/null)
flap=${flap:-0}
if [ "$crc" -ge 10 ] || [ "$flap" -ge 10 ]; then
net_overall="critical"
net_issues=$((net_issues + 1))
json_add_object "$iface"
json_add_string "status" "critical"
json_add_int "crc_errors" "$crc"
json_add_int "link_changes" "$flap"
json_close_object
fi
done
json_add_string "overall" "$net_overall"
json_add_int "issues" "$net_issues"
json_close_object
# Critical Services Status
json_add_object "services"
svc_ok=0
svc_down=0
# Check critical services
for svc in sshd dropbear dnsmasq haproxy crowdsec; do
if pgrep -x "$svc" >/dev/null 2>&1 || pgrep -f "$svc" >/dev/null 2>&1; then
svc_ok=$((svc_ok + 1))
else
# Check if it's supposed to be running
if [ -f "/etc/init.d/$svc" ] && /etc/init.d/$svc enabled 2>/dev/null; then
svc_down=$((svc_down + 1))
fi
fi
done
# Check LXC containers
lxc_expected=$(lxc-ls 2>/dev/null | wc -w)
lxc_running=$(lxc-ls --running 2>/dev/null | wc -w)
json_add_int "services_ok" "$svc_ok"
json_add_int "services_down" "$svc_down"
json_add_int "containers_running" "$lxc_running"
json_add_int "containers_total" "$lxc_expected"
if [ "$svc_down" -gt 0 ]; then
json_add_string "overall" "warning"
else
json_add_string "overall" "healthy"
fi
json_close_object
# Overall health score
health_score=100
[ "$net_overall" = "critical" ] && health_score=$((health_score - 30))
[ "$svc_down" -gt 0 ] && health_score=$((health_score - (svc_down * 10)))
[ "$mem_pct" -gt 90 ] && health_score=$((health_score - 10))
[ "${disk_pct:-0}" -gt 90 ] && health_score=$((health_score - 10))
json_add_int "health_score" "$health_score"
if [ "$health_score" -ge 80 ]; then
json_add_string "overall_status" "healthy"
elif [ "$health_score" -ge 50 ]; then
json_add_string "overall_status" "warning"
else
json_add_string "overall_status" "critical"
fi
# Alerts
json_add_array "alerts"
[ "$net_overall" = "critical" ] && {
json_add_object ""
json_add_string "level" "critical"
json_add_string "message" "Network interface issues detected - check cables"
json_close_object
}
[ "$svc_down" -gt 0 ] && {
json_add_object ""
json_add_string "level" "warning"
json_add_string "message" "$svc_down critical service(s) not running"
json_close_object
}
[ "$mem_pct" -gt 90 ] && {
json_add_object ""
json_add_string "level" "warning"
json_add_string "message" "High memory usage: ${mem_pct}%"
json_close_object
}
json_close_array
json_dump
;;
get_dashboard_data)
# Return dashboard summary data
# Return dashboard summary data (OPTIMIZED - no slow appstore call)
json_init
# Get module stats
modules_output=$(/usr/sbin/secubox-appstore list --json 2>/dev/null || echo '{"modules":[]}')
total_modules=$(echo "$modules_output" | jsonfilter -e '@.modules[*]' | wc -l)
running_modules=$(echo "$modules_output" | jsonfilter -e '@.modules[@.state="running"]' | wc -l 2>/dev/null || echo 0)
# Fast module counting: count installed secubox packages
# This avoids the slow secubox-appstore list --json call
total_modules=0
running_modules=0
# Count from catalog (fast - just count JSON entries)
CATALOG_FILE="/usr/share/secubox/catalog.json"
if [ -f "$CATALOG_FILE" ]; then
total_modules=$(jsonfilter -i "$CATALOG_FILE" -e '@.plugins[*].id' 2>/dev/null | wc -l)
fi
[ -z "$total_modules" ] || [ "$total_modules" -eq 0 ] && total_modules=0
# Count running LXC containers (fast)
lxc_running=$(lxc-ls --running 2>/dev/null | wc -w)
lxc_running=${lxc_running:-0}
# Count running init services that are SecuBox-related (fast)
svc_running=0
for svc in crowdsec tor haproxy netifyd syslog-ng; do
if pgrep -f "$svc" >/dev/null 2>&1; then
svc_running=$((svc_running + 1))
fi
done
running_modules=$((lxc_running + svc_running))
# Get system info
uptime_seconds=$(cat /proc/uptime | cut -d' ' -f1 | cut -d'.' -f1)
load_avg=$(cat /proc/loadavg | cut -d' ' -f1-3)
uptime_seconds=$(cut -d' ' -f1 /proc/uptime | cut -d'.' -f1)
load_avg=$(cut -d' ' -f1-3 /proc/loadavg)
# Build response
json_add_object "status"
@ -353,6 +729,8 @@ case "$1" in
json_add_object "counts"
json_add_int "total" "$total_modules"
json_add_int "running" "$running_modules"
json_add_int "lxc_running" "$lxc_running"
json_add_int "services_running" "$svc_running"
json_close_object
json_dump