#!/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