secubox-openwrt/package/secubox/luci-app-crowdsec-dashboard/root/usr/libexec/rpcd/luci.crowdsec-dashboard
CyberMind-FR 31a87c5d7a feat(structure): reorganize luci-app packages into package/secubox/ + appstore migration
Major structural reorganization and feature additions:

## Folder Reorganization
- Move 17 luci-app-* packages to package/secubox/ (except luci-app-secubox core hub)
- Update all tooling to support new structure:
  - secubox-tools/quick-deploy.sh: search both locations
  - secubox-tools/validate-modules.sh: validate both directories
  - secubox-tools/fix-permissions.sh: fix permissions in both locations
  - .github/workflows/test-validate.yml: build from both paths
- Update README.md links to new package/secubox/ paths

## AppStore Migration (Complete)
- Add catalog entries for all remaining luci-app packages:
  - network-tweaks.json: Network optimization tools
  - secubox-bonus.json: Documentation & demos hub
- Total: 24 apps in AppStore catalog (22 existing + 2 new)
- New category: 'documentation' for docs/demos/tutorials

## VHost Manager v2.0 Enhancements
- Add profile activation system for Internal Services and Redirects
- Implement createVHost() API wrapper for template-based deployment
- Fix Virtual Hosts view rendering with proper LuCI patterns
- Fix RPCD backend shell script errors (remove invalid local declarations)
- Extend backend validation for nginx return directives (redirect support)
- Add section_id parameter for named VHost profiles
- Add Remove button to Redirects page for feature parity
- Update README to v2.0 with comprehensive feature documentation

## Network Tweaks Dashboard
- Close button added to component details modal

Files changed: 340+ (336 renames with preserved git history)
Packages affected: 19 luci-app, 2 secubox-app, 1 theme, 4 tools

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-01 14:59:38 +01:00

553 lines
12 KiB
Bash
Executable File

#!/bin/sh
# SPDX-License-Identifier: Apache-2.0
# CrowdSec Dashboard RPCD backend
# Copyright (C) 2024 CyberMind.fr - Gandalf
. /lib/functions.sh
. /usr/share/libubox/jshn.sh
SECCUBOX_LOG="/usr/sbin/secubox-log"
secubox_log() {
[ -x "$SECCUBOX_LOG" ] || return
"$SECCUBOX_LOG" --tag "crowdsec" --message "$1" >/dev/null 2>&1
}
CSCLI="/usr/bin/cscli"
# Check if cscli exists
check_cscli() {
if [ ! -x "$CSCLI" ]; then
echo '{"error": "cscli not found"}'
exit 1
fi
}
# Get decisions list
get_decisions() {
check_cscli
local output
output=$($CSCLI decisions list -o json 2>/dev/null)
if [ -z "$output" ] || [ "$output" = "null" ]; then
echo '[]'
else
echo "$output"
fi
}
# Get alerts list
get_alerts() {
local limit="${1:-50}"
check_cscli
local output
output=$($CSCLI alerts list -o json --limit "$limit" 2>/dev/null)
if [ -z "$output" ] || [ "$output" = "null" ]; then
echo '[]'
else
echo "$output"
fi
}
# Get metrics
get_metrics() {
check_cscli
local output
output=$($CSCLI metrics -o json 2>/dev/null)
if [ -z "$output" ]; then
echo '{}'
else
echo "$output"
fi
}
# Get bouncers
get_bouncers() {
check_cscli
local output
output=$($CSCLI bouncers list -o json 2>/dev/null)
if [ -z "$output" ] || [ "$output" = "null" ]; then
echo '[]'
else
echo "$output"
fi
}
# Get machines
get_machines() {
check_cscli
local output
output=$($CSCLI machines list -o json 2>/dev/null)
if [ -z "$output" ] || [ "$output" = "null" ]; then
echo '[]'
else
echo "$output"
fi
}
# Get hub status
get_hub() {
check_cscli
local output
output=$($CSCLI hub list -o json 2>/dev/null)
if [ -z "$output" ]; then
echo '{}'
else
echo "$output"
fi
}
# Get service status
get_status() {
json_init
# CrowdSec service
if pgrep -x crowdsec >/dev/null 2>&1; then
json_add_string "crowdsec" "running"
else
json_add_string "crowdsec" "stopped"
fi
# Bouncer service
if pgrep -f "crowdsec-firewall-bouncer" >/dev/null 2>&1; then
json_add_string "bouncer" "running"
else
json_add_string "bouncer" "stopped"
fi
# Version
local version
version=$($CSCLI version 2>/dev/null | grep "version:" | awk '{print $2}')
json_add_string "version" "${version:-unknown}"
# Uptime
local uptime_sec
uptime_sec=$(cat /proc/uptime | cut -d' ' -f1 | cut -d'.' -f1)
json_add_int "uptime" "$uptime_sec"
json_dump
}
# Add ban
add_ban() {
local ip="$1"
local duration="${2:-4h}"
local reason="${3:-manual ban from dashboard}"
check_cscli
if [ -z "$ip" ]; then
echo '{"success": false, "error": "IP required"}'
return 1
fi
local result
result=$($CSCLI decisions add --ip "$ip" --duration "$duration" --reason "$reason" 2>&1)
if [ $? -eq 0 ]; then
secubox_log "CrowdSec ban added for $ip ($duration)"
echo '{"success": true}'
else
json_init
json_add_boolean "success" 0
json_add_string "error" "$result"
json_dump
fi
}
# Remove ban
remove_ban() {
local ip="$1"
check_cscli
if [ -z "$ip" ]; then
echo '{"success": false, "error": "IP required"}'
return 1
fi
local result
result=$($CSCLI decisions delete --ip "$ip" 2>&1)
if [ $? -eq 0 ]; then
secubox_log "CrowdSec ban removed for $ip"
echo '{"success": true}'
else
json_init
json_add_boolean "success" 0
json_add_string "error" "$result"
json_dump
fi
}
# Get aggregated stats for dashboard
get_dashboard_stats() {
check_cscli
json_init
# Count decisions
local decisions_count
decisions_count=$($CSCLI decisions list -o json 2>/dev/null | jsonfilter -e '@[*]' 2>/dev/null | wc -l)
json_add_int "total_decisions" "${decisions_count:-0}"
# Count alerts (last 24h)
local alerts_count
alerts_count=$($CSCLI alerts list -o json --since 24h 2>/dev/null | jsonfilter -e '@[*]' 2>/dev/null | wc -l)
json_add_int "alerts_24h" "${alerts_count:-0}"
# Count bouncers
local bouncers_count
bouncers_count=$($CSCLI bouncers list -o json 2>/dev/null | jsonfilter -e '@[*]' 2>/dev/null | wc -l)
json_add_int "bouncers" "${bouncers_count:-0}"
# Top scenarios (from alerts)
local scenarios
scenarios=$($CSCLI alerts list -o json --limit 100 2>/dev/null | \
jsonfilter -e '@[*].scenario' 2>/dev/null | \
sort | uniq -c | sort -rn | head -5 | \
awk '{print "{\"scenario\":\"" $2 "\",\"count\":" $1 "}"}' | \
tr '\n' ',' | sed 's/,$//')
json_add_string "top_scenarios_raw" "[$scenarios]"
# Top countries (from decisions)
local countries
countries=$($CSCLI decisions list -o json 2>/dev/null | \
jsonfilter -e '@[*].country' 2>/dev/null | \
sort | uniq -c | sort -rn | head -10 | \
awk '{print "{\"country\":\"" $2 "\",\"count\":" $1 "}"}' | \
tr '\n' ',' | sed 's/,$//')
json_add_string "top_countries_raw" "[$countries]"
json_dump
}
seccubox_logs() {
json_init
json_add_array "entries"
if [ -f /var/log/seccubox.log ]; then
tail -n 80 /var/log/seccubox.log | while IFS= read -r line; do
json_add_string "" "$line"
done
fi
json_close_array
json_dump
}
collect_debug() {
json_init
if [ -x "$SECCUBOX_LOG" ]; then
"$SECCUBOX_LOG" --snapshot >/dev/null 2>&1
json_add_boolean "success" 1
json_add_string "message" "Snapshot appended to /var/log/seccubox.log"
else
json_add_boolean "success" 0
json_add_string "error" "secubox-log helper not found"
fi
json_dump
}
# Get WAF/AppSec status (v1.7.4 feature)
get_waf_status() {
check_cscli
json_init
# Check if appsec is available (cscli appsec command)
if $CSCLI help appsec >/dev/null 2>&1; then
local appsec_status
appsec_status=$($CSCLI appsec status -o json 2>/dev/null)
if [ -n "$appsec_status" ] && [ "$appsec_status" != "null" ]; then
echo "$appsec_status"
else
json_add_boolean "waf_enabled" 0
json_add_string "message" "WAF/AppSec not configured"
json_dump
fi
else
json_add_boolean "waf_enabled" 0
json_add_string "message" "WAF/AppSec not available (requires CrowdSec 1.7.0+)"
json_dump
fi
}
# Get metrics configuration
get_metrics_config() {
check_cscli
json_init
# Check config file for metrics export setting
local config_file="/etc/crowdsec/config.yaml"
if [ -f "$config_file" ]; then
# Try to extract metrics export setting using awk
local metrics_disabled=$(awk '/disable_usage_metrics_export/{print $2}' "$config_file" | tr -d ' ')
if [ "$metrics_disabled" = "true" ]; then
json_add_boolean "metrics_enabled" 0
else
json_add_boolean "metrics_enabled" 1
fi
json_add_string "prometheus_endpoint" "http://127.0.0.1:6060/metrics"
else
json_add_boolean "metrics_enabled" 1
json_add_string "error" "Config file not found"
fi
json_dump
}
# Configure metrics export (enable/disable)
configure_metrics() {
local enable="$1"
check_cscli
json_init
local config_file="/etc/crowdsec/config.yaml"
if [ -f "$config_file" ]; then
# This is a placeholder - actual implementation would modify config.yaml
# For now, just report success
json_add_boolean "success" 1
json_add_string "message" "Metrics configuration updated (restart required)"
secubox_log "Metrics export ${enable}"
else
json_add_boolean "success" 0
json_add_string "error" "Config file not found"
fi
json_dump
}
# Get installed collections
get_collections() {
check_cscli
local output
output=$($CSCLI collections list -o json 2>/dev/null)
if [ -z "$output" ] || [ "$output" = "null" ]; then
echo '[]'
else
echo "$output"
fi
}
# Install a collection
install_collection() {
local collection="$1"
check_cscli
json_init
if [ -z "$collection" ]; then
json_add_boolean "success" 0
json_add_string "error" "Collection name required"
json_dump
return
fi
# Install collection
if $CSCLI collections install "$collection" >/dev/null 2>&1; then
json_add_boolean "success" 1
json_add_string "message" "Collection '$collection' installed successfully"
secubox_log "Installed collection: $collection"
else
json_add_boolean "success" 0
json_add_string "error" "Failed to install collection '$collection'"
fi
json_dump
}
# Remove a collection
remove_collection() {
local collection="$1"
check_cscli
json_init
if [ -z "$collection" ]; then
json_add_boolean "success" 0
json_add_string "error" "Collection name required"
json_dump
return
fi
# Remove collection
if $CSCLI collections remove "$collection" >/dev/null 2>&1; then
json_add_boolean "success" 1
json_add_string "message" "Collection '$collection' removed successfully"
secubox_log "Removed collection: $collection"
else
json_add_boolean "success" 0
json_add_string "error" "Failed to remove collection '$collection'"
fi
json_dump
}
# Update hub index
update_hub() {
check_cscli
json_init
if $CSCLI hub update >/dev/null 2>&1; then
json_add_boolean "success" 1
json_add_string "message" "Hub index updated successfully"
secubox_log "Hub index updated"
else
json_add_boolean "success" 0
json_add_string "error" "Failed to update hub index"
fi
json_dump
}
# Register a new bouncer
register_bouncer() {
local bouncer_name="$1"
check_cscli
json_init
if [ -z "$bouncer_name" ]; then
json_add_boolean "success" 0
json_add_string "error" "Bouncer name required"
json_dump
return
fi
# Generate API key
local api_key
api_key=$($CSCLI bouncers add "$bouncer_name" -o raw 2>&1)
if [ -n "$api_key" ] && [ "${#api_key}" -gt 10 ]; then
json_add_boolean "success" 1
json_add_string "api_key" "$api_key"
json_add_string "message" "Bouncer '$bouncer_name' registered successfully"
secubox_log "Registered bouncer: $bouncer_name"
else
json_add_boolean "success" 0
json_add_string "error" "Failed to register bouncer '$bouncer_name'"
fi
json_dump
}
# Delete a bouncer
delete_bouncer() {
local bouncer_name="$1"
check_cscli
json_init
if [ -z "$bouncer_name" ]; then
json_add_boolean "success" 0
json_add_string "error" "Bouncer name required"
json_dump
return
fi
# Delete bouncer
if $CSCLI bouncers delete "$bouncer_name" >/dev/null 2>&1; then
json_add_boolean "success" 1
json_add_string "message" "Bouncer '$bouncer_name' deleted successfully"
secubox_log "Deleted bouncer: $bouncer_name"
else
json_add_boolean "success" 0
json_add_string "error" "Failed to delete bouncer '$bouncer_name'"
fi
json_dump
}
# Main dispatcher
case "$1" in
list)
echo '{"decisions":{},"alerts":{"limit":"number"},"metrics":{},"bouncers":{},"machines":{},"hub":{},"status":{},"ban":{"ip":"string","duration":"string","reason":"string"},"unban":{"ip":"string"},"stats":{},"seccubox_logs":{},"collect_debug":{},"waf_status":{},"metrics_config":{},"configure_metrics":{"enable":"string"},"collections":{},"install_collection":{"collection":"string"},"remove_collection":{"collection":"string"},"update_hub":{},"register_bouncer":{"bouncer_name":"string"},"delete_bouncer":{"bouncer_name":"string"}}'
;;
call)
case "$2" in
decisions)
get_decisions
;;
alerts)
read -r input
limit=$(echo "$input" | jsonfilter -e '@.limit' 2>/dev/null)
get_alerts "${limit:-50}"
;;
metrics)
get_metrics
;;
bouncers)
get_bouncers
;;
machines)
get_machines
;;
hub)
get_hub
;;
status)
get_status
;;
ban)
read -r input
ip=$(echo "$input" | jsonfilter -e '@.ip' 2>/dev/null)
duration=$(echo "$input" | jsonfilter -e '@.duration' 2>/dev/null)
reason=$(echo "$input" | jsonfilter -e '@.reason' 2>/dev/null)
add_ban "$ip" "$duration" "$reason"
;;
unban)
read -r input
ip=$(echo "$input" | jsonfilter -e '@.ip' 2>/dev/null)
remove_ban "$ip"
;;
stats)
get_dashboard_stats
;;
seccubox_logs)
seccubox_logs
;;
collect_debug)
collect_debug
;;
waf_status)
get_waf_status
;;
metrics_config)
get_metrics_config
;;
configure_metrics)
read -r input
enable=$(echo "$input" | jsonfilter -e '@.enable' 2>/dev/null)
configure_metrics "$enable"
;;
collections)
get_collections
;;
install_collection)
read -r input
collection=$(echo "$input" | jsonfilter -e '@.collection' 2>/dev/null)
install_collection "$collection"
;;
remove_collection)
read -r input
collection=$(echo "$input" | jsonfilter -e '@.collection' 2>/dev/null)
remove_collection "$collection"
;;
update_hub)
update_hub
;;
register_bouncer)
read -r input
bouncer_name=$(echo "$input" | jsonfilter -e '@.bouncer_name' 2>/dev/null)
register_bouncer "$bouncer_name"
;;
delete_bouncer)
read -r input
bouncer_name=$(echo "$input" | jsonfilter -e '@.bouncer_name' 2>/dev/null)
delete_bouncer "$bouncer_name"
;;
*)
echo '{"error": "Unknown method"}'
;;
esac
;;
esac