Portal (luci-app-secubox-portal): - Fix service status showing 0/9 by checking if init scripts exist - Only count installed services in status display - Use pgrep fallback when init script status fails nDPId Dashboard (luci-app-ndpid): - Add default /etc/config/ndpid configuration - Add /etc/init.d/ndpid-compat init script - Enable compat service in postinst for app detection - Fix Makefile to install init script and config CrowdSec Dashboard: - Add CLAUDE.md with OpenWrt-specific guidelines (pgrep without -x) - CSS fixes for hiding LuCI left menu in all views - LAPI repair improvements with retry logic New Packages: - secubox-app-crowdsec: OpenWrt-native CrowdSec package - secubox-app-netifyd: Netifyd DPI integration - luci-app-secubox: Core SecuBox hub - luci-theme-secubox: Custom theme Removed: - luci-app-secubox-crowdsec (replaced by crowdsec-dashboard) - secubox-crowdsec-setup (functionality moved to dashboard) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1596 lines
43 KiB
Bash
Executable File
1596 lines
43 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"
|
|
CSCLI_TIMEOUT=10
|
|
|
|
# Check if timeout command exists
|
|
HAS_TIMEOUT=""
|
|
command -v timeout >/dev/null 2>&1 && HAS_TIMEOUT=1
|
|
|
|
# Run cscli with optional timeout to prevent hangs
|
|
run_cscli() {
|
|
if [ -n "$HAS_TIMEOUT" ]; then
|
|
timeout "$CSCLI_TIMEOUT" "$CSCLI" "$@" 2>/dev/null
|
|
else
|
|
"$CSCLI" "$@" 2>/dev/null
|
|
fi
|
|
}
|
|
|
|
# Run command with optional timeout
|
|
run_with_timeout() {
|
|
local secs="$1"
|
|
shift
|
|
if [ -n "$HAS_TIMEOUT" ]; then
|
|
timeout "$secs" "$@"
|
|
else
|
|
"$@"
|
|
fi
|
|
}
|
|
|
|
# Check if cscli exists and crowdsec is running
|
|
check_cscli() {
|
|
if [ ! -x "$CSCLI" ]; then
|
|
echo '{"error": "cscli not found"}'
|
|
exit 1
|
|
fi
|
|
|
|
# Check if crowdsec service is running
|
|
if ! pgrep crowdsec >/dev/null 2>&1; then
|
|
echo '{"error": "crowdsec service not running"}'
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Get decisions list
|
|
get_decisions() {
|
|
check_cscli
|
|
local output
|
|
output=$(run_cscli decisions list -o json)
|
|
if [ -z "$output" ] || [ "$output" = "null" ]; then
|
|
echo '{"alerts":[]}'
|
|
else
|
|
echo "{\"alerts\":$output}"
|
|
fi
|
|
}
|
|
|
|
# Get alerts list
|
|
get_alerts() {
|
|
local limit="${1:-50}"
|
|
check_cscli
|
|
local output
|
|
output=$(run_cscli alerts list -o json --limit "$limit" 2>/dev/null)
|
|
if [ -z "$output" ] || [ "$output" = "null" ]; then
|
|
echo '{"alerts":[]}'
|
|
else
|
|
echo "{\"alerts\":$output}"
|
|
fi
|
|
}
|
|
|
|
# Get metrics
|
|
get_metrics() {
|
|
check_cscli
|
|
local output
|
|
output=$(run_cscli metrics -o json 2>/dev/null)
|
|
if [ -z "$output" ]; then
|
|
echo '{}'
|
|
else
|
|
# Fix malformed JSON from cscli (remove empty string keys)
|
|
# CrowdSec sometimes outputs "": {...} which is technically valid but causes issues
|
|
echo "$output" | sed 's/"": {/"unknown": {/g'
|
|
fi
|
|
}
|
|
|
|
# Get bouncers
|
|
get_bouncers() {
|
|
check_cscli
|
|
local output
|
|
output=$(run_cscli bouncers list -o json 2>/dev/null)
|
|
if [ -z "$output" ] || [ "$output" = "null" ]; then
|
|
echo '{"bouncers":[]}'
|
|
else
|
|
echo "{\"bouncers\":$output}"
|
|
fi
|
|
}
|
|
|
|
# Get machines
|
|
get_machines() {
|
|
check_cscli
|
|
local output
|
|
output=$(run_cscli machines list -o json 2>/dev/null)
|
|
if [ -z "$output" ] || [ "$output" = "null" ]; then
|
|
echo '{"machines":[]}'
|
|
else
|
|
echo "{\"machines\":$output}"
|
|
fi
|
|
}
|
|
|
|
# Get hub status
|
|
get_hub() {
|
|
check_cscli
|
|
local output
|
|
output=$(run_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 - check multiple ways
|
|
local cs_running=0
|
|
if pgrep crowdsec >/dev/null 2>&1; then
|
|
cs_running=1
|
|
elif pgrep -f "/usr/bin/crowdsec" >/dev/null 2>&1; then
|
|
cs_running=1
|
|
elif [ -f /var/run/crowdsec.pid ] && kill -0 "$(cat /var/run/crowdsec.pid 2>/dev/null)" 2>/dev/null; then
|
|
cs_running=1
|
|
fi
|
|
|
|
if [ "$cs_running" = "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=$(run_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"
|
|
|
|
# LAPI status (check if Local API is accessible)
|
|
local lapi_status="unavailable"
|
|
local lapi_reason=""
|
|
|
|
# First check if cscli exists
|
|
if [ ! -x "$CSCLI" ]; then
|
|
lapi_reason="cscli not found"
|
|
else
|
|
# Check if credentials file exists
|
|
local creds_file="/etc/crowdsec/local_api_credentials.yaml"
|
|
if [ ! -f "$creds_file" ]; then
|
|
lapi_reason="credentials missing"
|
|
elif ! grep -q "password:" "$creds_file" 2>/dev/null; then
|
|
lapi_reason="credentials incomplete"
|
|
else
|
|
# Check if LAPI port is listening (8080 hex = 1F90)
|
|
local port_up=0
|
|
if grep -qi ":1F90 " /proc/net/tcp 2>/dev/null; then
|
|
port_up=1
|
|
fi
|
|
|
|
if [ "$port_up" = "0" ]; then
|
|
lapi_reason="port 8080 not listening"
|
|
else
|
|
# Try actual LAPI status check
|
|
if run_cscli lapi status >/dev/null 2>&1; then
|
|
lapi_status="available"
|
|
else
|
|
lapi_reason="lapi check failed"
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
json_add_string "lapi_status" "$lapi_status"
|
|
if [ -n "$lapi_reason" ]; then
|
|
json_add_string "lapi_reason" "$lapi_reason"
|
|
fi
|
|
|
|
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=$(run_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=$(run_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=$(run_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=$(run_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=$(run_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=$(run_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=$(run_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 run_cscli help appsec >/dev/null 2>&1; then
|
|
local appsec_status
|
|
appsec_status=$(run_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=$(run_cscli collections list -o json 2>/dev/null)
|
|
if [ -z "$output" ] || [ "$output" = "null" ]; then
|
|
echo '{"collections":[]}'
|
|
else
|
|
echo "{\"collections\":$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 run_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 run_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 run_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"
|
|
local force="${2:-0}"
|
|
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
|
|
|
|
# Check if bouncer already exists (robust pattern matching)
|
|
local exists=0
|
|
local bouncer_list
|
|
bouncer_list=$(run_cscli bouncers list -o json 2>/dev/null)
|
|
if echo "$bouncer_list" | grep -qE "\"name\"[[:space:]]*:[[:space:]]*\"$bouncer_name\""; then
|
|
exists=1
|
|
fi
|
|
|
|
local api_key
|
|
if [ "$exists" = "1" ]; then
|
|
secubox_log "Bouncer '$bouncer_name' already exists, deleting for re-registration..."
|
|
# Delete existing bouncer to get new API key
|
|
if ! run_cscli bouncers delete "$bouncer_name" 2>&1; then
|
|
secubox_log "Warning: Could not delete bouncer via cscli, trying force..."
|
|
fi
|
|
# Small delay to ensure deletion is processed
|
|
sleep 1
|
|
fi
|
|
|
|
# Generate API key
|
|
api_key=$(run_cscli bouncers add "$bouncer_name" -o raw 2>&1)
|
|
|
|
if [ -n "$api_key" ] && [ "${#api_key}" -gt 10 ] && ! echo "$api_key" | grep -qi "error\|unable\|failed"; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "api_key" "$api_key"
|
|
json_add_string "message" "Bouncer '$bouncer_name' registered successfully"
|
|
json_add_boolean "replaced" "$exists"
|
|
secubox_log "Registered bouncer: $bouncer_name (replaced: $exists)"
|
|
else
|
|
# If still failing, try more aggressive cleanup
|
|
if echo "$api_key" | grep -qi "already exists"; then
|
|
secubox_log "Bouncer still exists, attempting database-level cleanup..."
|
|
# Force delete from database
|
|
sqlite3 /srv/crowdsec/data/crowdsec.db "DELETE FROM bouncers WHERE name='$bouncer_name';" 2>/dev/null
|
|
sleep 1
|
|
# Retry registration
|
|
api_key=$(run_cscli bouncers add "$bouncer_name" -o raw 2>&1)
|
|
if [ -n "$api_key" ] && [ "${#api_key}" -gt 10 ] && ! echo "$api_key" | grep -qi "error\|unable\|failed"; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "api_key" "$api_key"
|
|
json_add_string "message" "Bouncer '$bouncer_name' registered after forced cleanup"
|
|
json_add_boolean "replaced" 1
|
|
secubox_log "Registered bouncer after forced cleanup: $bouncer_name"
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Failed to register bouncer '$bouncer_name' even after cleanup: $api_key"
|
|
fi
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Failed to register bouncer '$bouncer_name': $api_key"
|
|
fi
|
|
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 run_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
|
|
}
|
|
|
|
# Get firewall bouncer detailed status
|
|
get_firewall_bouncer_status() {
|
|
json_init
|
|
|
|
# Check if service is running
|
|
local running=0
|
|
if pgrep -f "cs-firewall-bouncer" >/dev/null 2>&1; then
|
|
running=1
|
|
fi
|
|
json_add_boolean "running" "$running"
|
|
|
|
# Check if service is enabled
|
|
local enabled=0
|
|
if [ -f "/etc/rc.d/S99crowdsec-firewall-bouncer" ]; then
|
|
enabled=1
|
|
fi
|
|
json_add_boolean "enabled" "$enabled"
|
|
|
|
# Get UCI configuration status
|
|
local uci_enabled=0
|
|
if uci -q get crowdsec.bouncer.enabled >/dev/null 2>&1; then
|
|
local val
|
|
val=$(uci -q get crowdsec.bouncer.enabled)
|
|
[ "$val" = "1" ] && uci_enabled=1
|
|
fi
|
|
json_add_boolean "configured" "$uci_enabled"
|
|
|
|
# Get nftables tables status
|
|
local nft_ipv4=0
|
|
local nft_ipv6=0
|
|
if command -v nft >/dev/null 2>&1; then
|
|
nft list tables 2>/dev/null | grep -q "crowdsec" && nft_ipv4=1
|
|
nft list tables 2>/dev/null | grep -q "crowdsec6" && nft_ipv6=1
|
|
fi
|
|
json_add_boolean "nftables_ipv4" "$nft_ipv4"
|
|
json_add_boolean "nftables_ipv6" "$nft_ipv6"
|
|
|
|
# Get blocked IPs count
|
|
local ipv4_count=0
|
|
local ipv6_count=0
|
|
if [ "$nft_ipv4" = "1" ]; then
|
|
ipv4_count=$(nft list set ip crowdsec crowdsec-blacklists 2>/dev/null | grep -c "elements = {" || echo "0")
|
|
fi
|
|
if [ "$nft_ipv6" = "1" ]; then
|
|
ipv6_count=$(nft list set ip6 crowdsec6 crowdsec6-blacklists 2>/dev/null | grep -c "elements = {" || echo "0")
|
|
fi
|
|
json_add_int "blocked_ipv4" "$ipv4_count"
|
|
json_add_int "blocked_ipv6" "$ipv6_count"
|
|
|
|
json_dump
|
|
}
|
|
|
|
# Control firewall bouncer service (start/stop/restart/enable/disable)
|
|
control_firewall_bouncer() {
|
|
local action="$1"
|
|
json_init
|
|
|
|
if [ -z "$action" ]; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Action required (start|stop|restart|enable|disable)"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
case "$action" in
|
|
start)
|
|
if /etc/init.d/crowdsec-firewall-bouncer start >/dev/null 2>&1; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Firewall bouncer started"
|
|
secubox_log "Firewall bouncer started"
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Failed to start firewall bouncer"
|
|
fi
|
|
;;
|
|
stop)
|
|
if /etc/init.d/crowdsec-firewall-bouncer stop >/dev/null 2>&1; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Firewall bouncer stopped"
|
|
secubox_log "Firewall bouncer stopped"
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Failed to stop firewall bouncer"
|
|
fi
|
|
;;
|
|
restart)
|
|
if /etc/init.d/crowdsec-firewall-bouncer restart >/dev/null 2>&1; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Firewall bouncer restarted"
|
|
secubox_log "Firewall bouncer restarted"
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Failed to restart firewall bouncer"
|
|
fi
|
|
;;
|
|
enable)
|
|
if /etc/init.d/crowdsec-firewall-bouncer enable >/dev/null 2>&1; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Firewall bouncer enabled"
|
|
secubox_log "Firewall bouncer enabled"
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Failed to enable firewall bouncer"
|
|
fi
|
|
;;
|
|
disable)
|
|
if /etc/init.d/crowdsec-firewall-bouncer disable >/dev/null 2>&1; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Firewall bouncer disabled"
|
|
secubox_log "Firewall bouncer disabled"
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Failed to disable firewall bouncer"
|
|
fi
|
|
;;
|
|
*)
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Unknown action: $action"
|
|
;;
|
|
esac
|
|
|
|
json_dump
|
|
}
|
|
|
|
# Get firewall bouncer UCI configuration
|
|
get_firewall_bouncer_config() {
|
|
json_init
|
|
|
|
# Check if bouncer section exists
|
|
if ! uci -q get crowdsec.bouncer >/dev/null 2>&1; then
|
|
json_add_boolean "configured" 0
|
|
json_add_string "message" "Bouncer not configured in UCI"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
json_add_boolean "configured" 1
|
|
|
|
# Get all configuration options
|
|
local val
|
|
val=$(uci -q get crowdsec.bouncer.enabled || echo "0")
|
|
json_add_string "enabled" "$val"
|
|
|
|
val=$(uci -q get crowdsec.bouncer.ipv4 || echo "1")
|
|
json_add_string "ipv4" "$val"
|
|
|
|
val=$(uci -q get crowdsec.bouncer.ipv6 || echo "1")
|
|
json_add_string "ipv6" "$val"
|
|
|
|
val=$(uci -q get crowdsec.bouncer.api_url || echo "http://127.0.0.1:8080/")
|
|
json_add_string "api_url" "$val"
|
|
|
|
val=$(uci -q get crowdsec.bouncer.update_frequency || echo "10s")
|
|
json_add_string "update_frequency" "$val"
|
|
|
|
val=$(uci -q get crowdsec.bouncer.deny_action || echo "drop")
|
|
json_add_string "deny_action" "$val"
|
|
|
|
val=$(uci -q get crowdsec.bouncer.deny_log || echo "0")
|
|
json_add_string "deny_log" "$val"
|
|
|
|
val=$(uci -q get crowdsec.bouncer.log_level || echo "info")
|
|
json_add_string "log_level" "$val"
|
|
|
|
val=$(uci -q get crowdsec.bouncer.filter_input || echo "1")
|
|
json_add_string "filter_input" "$val"
|
|
|
|
val=$(uci -q get crowdsec.bouncer.filter_forward || echo "1")
|
|
json_add_string "filter_forward" "$val"
|
|
|
|
# Get interfaces list
|
|
json_add_array "interfaces"
|
|
local interfaces
|
|
interfaces=$(uci -q get crowdsec.bouncer.interface 2>/dev/null || echo "")
|
|
if [ -n "$interfaces" ]; then
|
|
local iface
|
|
for iface in $interfaces; do
|
|
json_add_string "" "$iface"
|
|
done
|
|
fi
|
|
json_close_array
|
|
|
|
json_dump
|
|
}
|
|
|
|
# Update firewall bouncer UCI configuration
|
|
update_firewall_bouncer_config() {
|
|
local key="$1"
|
|
local value="$2"
|
|
json_init
|
|
|
|
if [ -z "$key" ]; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Configuration key required"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
# Validate and set configuration
|
|
case "$key" in
|
|
enabled|ipv4|ipv6|deny_log|filter_input|filter_forward)
|
|
# Boolean values
|
|
if [ "$value" != "0" ] && [ "$value" != "1" ]; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Invalid boolean value: $value"
|
|
json_dump
|
|
return
|
|
fi
|
|
uci set "crowdsec.@bouncer[0].$key=$value"
|
|
;;
|
|
api_url|update_frequency|deny_action|log_level|api_key)
|
|
# String values
|
|
if [ -z "$value" ]; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Value cannot be empty"
|
|
json_dump
|
|
return
|
|
fi
|
|
uci set "crowdsec.@bouncer[0].$key=$value"
|
|
;;
|
|
*)
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Unknown configuration key: $key"
|
|
json_dump
|
|
return
|
|
;;
|
|
esac
|
|
|
|
# Commit changes
|
|
if uci commit crowdsec 2>/dev/null; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Configuration updated: $key=$value"
|
|
secubox_log "Firewall bouncer config updated: $key=$value"
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Failed to commit UCI changes"
|
|
fi
|
|
|
|
json_dump
|
|
}
|
|
|
|
# Get nftables statistics
|
|
get_nftables_stats() {
|
|
json_init
|
|
|
|
if ! command -v nft >/dev/null 2>&1; then
|
|
json_add_boolean "available" 0
|
|
json_add_string "error" "nftables not available"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
json_add_boolean "available" 1
|
|
|
|
# Check IPv4 table
|
|
local ipv4_exists=0
|
|
if nft list table ip crowdsec >/dev/null 2>&1; then
|
|
ipv4_exists=1
|
|
fi
|
|
json_add_boolean "ipv4_table_exists" "$ipv4_exists"
|
|
|
|
# Check IPv6 table
|
|
local ipv6_exists=0
|
|
if nft list table ip6 crowdsec6 >/dev/null 2>&1; then
|
|
ipv6_exists=1
|
|
fi
|
|
json_add_boolean "ipv6_table_exists" "$ipv6_exists"
|
|
|
|
# Get blocked IPs from IPv4 set
|
|
json_add_array "ipv4_blocked_ips"
|
|
if [ "$ipv4_exists" = "1" ]; then
|
|
local ips
|
|
ips=$(nft list set ip crowdsec crowdsec-blacklists 2>/dev/null | sed -n '/elements = {/,/}/p' | grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' || echo "")
|
|
if [ -n "$ips" ]; then
|
|
local ip
|
|
for ip in $ips; do
|
|
json_add_string "" "$ip"
|
|
done
|
|
fi
|
|
fi
|
|
json_close_array
|
|
|
|
# Get blocked IPs from IPv6 set
|
|
json_add_array "ipv6_blocked_ips"
|
|
if [ "$ipv6_exists" = "1" ]; then
|
|
local ips
|
|
ips=$(nft list set ip6 crowdsec6 crowdsec6-blacklists 2>/dev/null | sed -n '/elements = {/,/}/p' | grep -oE '([0-9a-fA-F]{0,4}:){7}[0-9a-fA-F]{0,4}' || echo "")
|
|
if [ -n "$ips" ]; then
|
|
local ip
|
|
for ip in $ips; do
|
|
json_add_string "" "$ip"
|
|
done
|
|
fi
|
|
fi
|
|
json_close_array
|
|
|
|
# Get rules count
|
|
local ipv4_rules=0
|
|
local ipv6_rules=0
|
|
if [ "$ipv4_exists" = "1" ]; then
|
|
ipv4_rules=$(nft list table ip crowdsec 2>/dev/null | grep -c "rule" || echo "0")
|
|
fi
|
|
if [ "$ipv6_exists" = "1" ]; then
|
|
ipv6_rules=$(nft list table ip6 crowdsec6 2>/dev/null | grep -c "rule" || echo "0")
|
|
fi
|
|
json_add_int "ipv4_rules_count" "$ipv4_rules"
|
|
json_add_int "ipv6_rules_count" "$ipv6_rules"
|
|
|
|
json_dump
|
|
}
|
|
|
|
# Check if wizard should be shown (first-time setup detection)
|
|
check_wizard_needed() {
|
|
json_init
|
|
|
|
# Check if bouncer is configured
|
|
local bouncer_configured=0
|
|
if uci -q get crowdsec.bouncer.enabled >/dev/null 2>&1; then
|
|
bouncer_configured=1
|
|
fi
|
|
|
|
# Check if collections are installed
|
|
local collections_installed=0
|
|
if [ -x "$CSCLI" ]; then
|
|
if run_cscli collections list 2>/dev/null | grep -q "INSTALLED"; then
|
|
collections_installed=1
|
|
fi
|
|
fi
|
|
|
|
# Show wizard if not configured
|
|
local show_wizard=0
|
|
if [ "$bouncer_configured" = "0" ] || [ "$collections_installed" = "0" ]; then
|
|
show_wizard=1
|
|
fi
|
|
|
|
json_add_boolean "show_wizard" "$show_wizard"
|
|
json_add_boolean "bouncer_configured" "$bouncer_configured"
|
|
json_add_boolean "collections_installed" "$collections_installed"
|
|
|
|
json_dump
|
|
}
|
|
|
|
# Get wizard initial state
|
|
get_wizard_state() {
|
|
json_init
|
|
|
|
# Get collections count
|
|
local collections_count=0
|
|
if [ -x "$CSCLI" ]; then
|
|
collections_count=$(run_cscli collections list 2>/dev/null | grep -c "INSTALLED" || echo "0")
|
|
fi
|
|
|
|
json_add_int "collections_count" "$collections_count"
|
|
|
|
json_dump
|
|
}
|
|
|
|
# Repair LAPI - auto-fix common configuration issues
|
|
repair_lapi() {
|
|
json_init
|
|
local steps_done=""
|
|
local errors=""
|
|
local REPAIR_TIMEOUT=10
|
|
|
|
secubox_log "Starting LAPI repair..."
|
|
|
|
# Step 1: Stop CrowdSec completely
|
|
/etc/init.d/crowdsec stop >/dev/null 2>&1
|
|
sleep 1
|
|
killall crowdsec 2>/dev/null
|
|
sleep 1
|
|
steps_done="${steps_done}Stopped; "
|
|
|
|
# Step 2: Create required directories
|
|
mkdir -p /srv/crowdsec/data 2>/dev/null
|
|
mkdir -p /etc/crowdsec/hub 2>/dev/null
|
|
mkdir -p /etc/crowdsec/acquis.d 2>/dev/null
|
|
mkdir -p /var/etc/crowdsec 2>/dev/null
|
|
chmod 755 /srv/crowdsec/data 2>/dev/null
|
|
steps_done="${steps_done}Dirs; "
|
|
|
|
# Step 3: Fix config.yaml
|
|
local config_file="/etc/crowdsec/config.yaml"
|
|
local var_config="/var/etc/crowdsec/config.yaml"
|
|
if [ -f "$config_file" ]; then
|
|
sed -i 's|^\(\s*\)data_dir:.*|\1data_dir: /srv/crowdsec/data/|' "$config_file" 2>/dev/null
|
|
sed -i 's|^\(\s*\)db_path:.*|\1db_path: /srv/crowdsec/data/crowdsec.db|' "$config_file" 2>/dev/null
|
|
cp "$config_file" "$var_config" 2>/dev/null
|
|
steps_done="${steps_done}Config; "
|
|
else
|
|
errors="${errors}No config; "
|
|
fi
|
|
|
|
# Step 4: Remove corrupted credentials
|
|
local creds_file="/etc/crowdsec/local_api_credentials.yaml"
|
|
rm -f "$creds_file" 2>/dev/null
|
|
|
|
# Step 5: Clean up orphaned machines from database
|
|
local db_file="/srv/crowdsec/data/crowdsec.db"
|
|
if [ -f "$db_file" ]; then
|
|
if command -v sqlite3 >/dev/null 2>&1; then
|
|
# Remove all machines - we'll re-register
|
|
sqlite3 "$db_file" "DELETE FROM machines;" 2>/dev/null
|
|
steps_done="${steps_done}DB cleaned; "
|
|
else
|
|
# No sqlite3 - delete the database file, CrowdSec will recreate it
|
|
rm -f "$db_file" 2>/dev/null
|
|
steps_done="${steps_done}DB reset; "
|
|
fi
|
|
fi
|
|
|
|
# Step 6: Start CrowdSec in LAPI-only mode (no agent)
|
|
# This allows LAPI to start without needing valid credentials
|
|
local crowdsec_bin="/usr/bin/crowdsec"
|
|
if [ -x "$crowdsec_bin" ]; then
|
|
# Start LAPI-only mode in background
|
|
"$crowdsec_bin" -c "$var_config" -no-cs &
|
|
local cs_pid=$!
|
|
steps_done="${steps_done}LAPI-only started; "
|
|
|
|
# Wait for LAPI to be ready (max 8 seconds)
|
|
local retries=8
|
|
local lapi_ready=0
|
|
while [ $retries -gt 0 ]; do
|
|
if grep -qi ":1F90 " /proc/net/tcp 2>/dev/null; then
|
|
lapi_ready=1
|
|
break
|
|
fi
|
|
sleep 1
|
|
retries=$((retries - 1))
|
|
done
|
|
|
|
if [ "$lapi_ready" = "1" ]; then
|
|
steps_done="${steps_done}LAPI up; "
|
|
sleep 1
|
|
|
|
# Step 7: Register machine while LAPI is running
|
|
if [ -x "$CSCLI" ]; then
|
|
local reg_output=""
|
|
reg_output=$("$CSCLI" -c "$var_config" machines add localhost --auto --force -f "$creds_file" 2>&1)
|
|
if [ $? -eq 0 ]; then
|
|
steps_done="${steps_done}Registered; "
|
|
else
|
|
errors="${errors}Reg failed: ${reg_output}; "
|
|
fi
|
|
fi
|
|
else
|
|
errors="${errors}LAPI timeout; "
|
|
fi
|
|
|
|
# Step 8: Stop LAPI-only mode
|
|
kill $cs_pid 2>/dev/null
|
|
sleep 1
|
|
killall crowdsec 2>/dev/null
|
|
steps_done="${steps_done}LAPI stopped; "
|
|
else
|
|
errors="${errors}No crowdsec binary; "
|
|
fi
|
|
|
|
# Step 9: Verify credentials were created
|
|
if [ -s "$creds_file" ] && grep -q "password:" "$creds_file"; then
|
|
steps_done="${steps_done}Creds OK; "
|
|
else
|
|
errors="${errors}No creds; "
|
|
fi
|
|
|
|
# Step 10: Start CrowdSec normally via init
|
|
/etc/init.d/crowdsec start >/dev/null 2>&1
|
|
sleep 3
|
|
|
|
# Step 11: Verify everything is working
|
|
local lapi_ok=0
|
|
if pgrep crowdsec >/dev/null 2>&1; then
|
|
steps_done="${steps_done}Running; "
|
|
if [ -x "$CSCLI" ]; then
|
|
if run_with_timeout 5 "$CSCLI" lapi status >/dev/null 2>&1; then
|
|
lapi_ok=1
|
|
steps_done="${steps_done}LAPI OK"
|
|
else
|
|
errors="${errors}LAPI check failed; "
|
|
fi
|
|
fi
|
|
else
|
|
errors="${errors}Not running; "
|
|
# Get error from log
|
|
local log_err=""
|
|
log_err=$(tail -3 /var/log/crowdsec.log 2>/dev/null | grep -i "fatal\|error" | head -1)
|
|
[ -n "$log_err" ] && errors="${errors}${log_err}; "
|
|
fi
|
|
|
|
if [ "$lapi_ok" = "1" ]; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "LAPI repaired successfully"
|
|
json_add_string "steps" "$steps_done"
|
|
secubox_log "LAPI repair successful: $steps_done"
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "LAPI repair incomplete"
|
|
json_add_string "steps" "$steps_done"
|
|
json_add_string "errors" "$errors"
|
|
secubox_log "LAPI repair failed: $errors"
|
|
fi
|
|
|
|
json_dump
|
|
}
|
|
|
|
# Reset wizard - clean up for fresh start
|
|
reset_wizard() {
|
|
json_init
|
|
local steps_done=""
|
|
local errors=""
|
|
|
|
secubox_log "Starting CrowdSec wizard reset (recovery mode)..."
|
|
|
|
# Step 1: Stop services
|
|
/etc/init.d/crowdsec-firewall-bouncer stop >/dev/null 2>&1
|
|
steps_done="${steps_done}Stopped firewall bouncer; "
|
|
|
|
# Step 2: Delete existing bouncer registration
|
|
if [ -x "$CSCLI" ]; then
|
|
run_cscli bouncers delete "crowdsec-firewall-bouncer" >/dev/null 2>&1
|
|
steps_done="${steps_done}Deleted bouncer registration; "
|
|
|
|
# Also try database cleanup
|
|
sqlite3 /srv/crowdsec/data/crowdsec.db "DELETE FROM bouncers WHERE name='crowdsec-firewall-bouncer';" 2>/dev/null
|
|
fi
|
|
|
|
# Step 3: Clear UCI bouncer config
|
|
uci -q delete crowdsec.bouncer 2>/dev/null
|
|
uci commit crowdsec 2>/dev/null
|
|
steps_done="${steps_done}Cleared UCI config; "
|
|
|
|
# Step 4: Remove bouncer config file
|
|
rm -f /etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml 2>/dev/null
|
|
steps_done="${steps_done}Removed bouncer config file; "
|
|
|
|
# Step 5: Reset nftables rules
|
|
if command -v nft >/dev/null 2>&1; then
|
|
nft delete table ip crowdsec 2>/dev/null
|
|
nft delete table ip6 crowdsec6 2>/dev/null
|
|
steps_done="${steps_done}Cleared nftables rules; "
|
|
fi
|
|
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Wizard reset completed - ready for fresh setup"
|
|
json_add_string "steps" "$steps_done"
|
|
secubox_log "Wizard reset completed: $steps_done"
|
|
|
|
json_dump
|
|
}
|
|
|
|
# Console enrollment status
|
|
get_console_status() {
|
|
json_init
|
|
check_cscli
|
|
|
|
local enrolled=0
|
|
local name=""
|
|
local company=""
|
|
|
|
# Check if console is enrolled by checking if there's a console config
|
|
if [ -f "/etc/crowdsec/console.yaml" ]; then
|
|
# Check if the console is actually configured (has credentials)
|
|
if grep -q "share_manual_decisions\|share_tainted\|share_context" /etc/crowdsec/console.yaml 2>/dev/null; then
|
|
enrolled=1
|
|
fi
|
|
fi
|
|
|
|
# Try to get console status from cscli
|
|
local console_status
|
|
console_status=$(run_cscli console status 2>/dev/null)
|
|
if [ -n "$console_status" ]; then
|
|
# Check if enrolled by looking for "Enrolled" or similar in output
|
|
if echo "$console_status" | grep -qi "enrolled\|connected\|active"; then
|
|
enrolled=1
|
|
fi
|
|
# Extract name if present
|
|
name=$(echo "$console_status" | grep -i "name\|machine" | head -1 | awk -F: '{print $2}' | xargs 2>/dev/null)
|
|
fi
|
|
|
|
json_add_boolean "enrolled" "$enrolled"
|
|
json_add_string "name" "$name"
|
|
|
|
# Get share settings if enrolled
|
|
if [ "$enrolled" = "1" ] && [ -f "/etc/crowdsec/console.yaml" ]; then
|
|
local share_manual=$(grep "share_manual_decisions:" /etc/crowdsec/console.yaml 2>/dev/null | awk '{print $2}')
|
|
local share_tainted=$(grep "share_tainted:" /etc/crowdsec/console.yaml 2>/dev/null | awk '{print $2}')
|
|
local share_context=$(grep "share_context:" /etc/crowdsec/console.yaml 2>/dev/null | awk '{print $2}')
|
|
|
|
json_add_boolean "share_manual_decisions" "$([ \"$share_manual\" = \"true\" ] && echo 1 || echo 0)"
|
|
json_add_boolean "share_tainted" "$([ \"$share_tainted\" = \"true\" ] && echo 1 || echo 0)"
|
|
json_add_boolean "share_context" "$([ \"$share_context\" = \"true\" ] && echo 1 || echo 0)"
|
|
fi
|
|
|
|
json_dump
|
|
}
|
|
|
|
# Console enroll
|
|
console_enroll() {
|
|
local key="$1"
|
|
local name="$2"
|
|
|
|
json_init
|
|
check_cscli
|
|
|
|
if [ -z "$key" ]; then
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Enrollment key is required"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
secubox_log "Enrolling CrowdSec Console with key..."
|
|
|
|
# Build enroll command
|
|
local enroll_cmd="run_cscli console enroll $key"
|
|
if [ -n "$name" ]; then
|
|
enroll_cmd="$enroll_cmd --name \"$name\""
|
|
fi
|
|
|
|
# Run enrollment
|
|
local output
|
|
output=$(eval "$enroll_cmd" 2>&1)
|
|
local result=$?
|
|
|
|
if [ "$result" -eq 0 ]; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Successfully enrolled in CrowdSec Console"
|
|
json_add_string "output" "$output"
|
|
secubox_log "Console enrollment successful"
|
|
|
|
# Enable sharing options by default
|
|
run_cscli console enable share_manual_decisions >/dev/null 2>&1
|
|
run_cscli console enable share_tainted >/dev/null 2>&1
|
|
run_cscli console enable share_context >/dev/null 2>&1
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Enrollment failed"
|
|
json_add_string "output" "$output"
|
|
secubox_log "Console enrollment failed: $output"
|
|
fi
|
|
|
|
json_dump
|
|
}
|
|
|
|
# Console disable (unenroll)
|
|
console_disable() {
|
|
json_init
|
|
check_cscli
|
|
|
|
secubox_log "Disabling CrowdSec Console enrollment..."
|
|
|
|
# Disable all sharing
|
|
run_cscli console disable share_manual_decisions >/dev/null 2>&1
|
|
run_cscli console disable share_tainted >/dev/null 2>&1
|
|
run_cscli console disable share_context >/dev/null 2>&1
|
|
|
|
# Remove console config
|
|
if [ -f "/etc/crowdsec/console.yaml" ]; then
|
|
rm -f /etc/crowdsec/console.yaml
|
|
fi
|
|
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Console enrollment disabled"
|
|
secubox_log "Console enrollment disabled"
|
|
|
|
json_dump
|
|
}
|
|
|
|
# Configure log acquisition settings
|
|
configure_acquisition() {
|
|
local syslog_enabled="$1"
|
|
local firewall_enabled="$2"
|
|
local ssh_enabled="$3"
|
|
local http_enabled="$4"
|
|
local syslog_path="$5"
|
|
|
|
json_init
|
|
local steps_done=""
|
|
local errors=""
|
|
|
|
secubox_log "Configuring CrowdSec log acquisition..."
|
|
|
|
# Step 1: Ensure acquisition section exists in UCI
|
|
if ! uci -q get crowdsec.acquisition >/dev/null 2>&1; then
|
|
uci set crowdsec.acquisition='acquisition'
|
|
steps_done="${steps_done}Created acquisition section; "
|
|
fi
|
|
|
|
# Step 2: Set acquisition options
|
|
uci set crowdsec.acquisition.syslog_enabled="${syslog_enabled:-1}"
|
|
uci set crowdsec.acquisition.firewall_enabled="${firewall_enabled:-1}"
|
|
uci set crowdsec.acquisition.ssh_enabled="${ssh_enabled:-1}"
|
|
uci set crowdsec.acquisition.http_enabled="${http_enabled:-0}"
|
|
if [ -n "$syslog_path" ]; then
|
|
uci set crowdsec.acquisition.syslog_path="$syslog_path"
|
|
fi
|
|
uci commit crowdsec
|
|
steps_done="${steps_done}Updated UCI settings; "
|
|
|
|
# Step 3: Generate acquisition YAML files
|
|
# OpenWrt uses logread command instead of /var/log/messages by default
|
|
# All syslog entries (SSH, firewall, system) go through the same log stream
|
|
# We create ONE unified acquisition file to avoid multiple logread processes
|
|
local acquis_dir="/etc/crowdsec/acquis.d"
|
|
mkdir -p "$acquis_dir"
|
|
|
|
# Remove old separate acquisition files if they exist
|
|
rm -f "$acquis_dir/openwrt-syslog.yaml" 2>/dev/null
|
|
rm -f "$acquis_dir/openwrt-firewall.yaml" 2>/dev/null
|
|
rm -f "$acquis_dir/openwrt-dropbear.yaml" 2>/dev/null
|
|
|
|
# Create unified syslog acquisition if any syslog-based source is enabled
|
|
# SSH, firewall, and system logs all go through OpenWrt's logread
|
|
if [ "$syslog_enabled" = "1" ] || [ "$firewall_enabled" = "1" ] || [ "$ssh_enabled" = "1" ]; then
|
|
cat > "$acquis_dir/openwrt-unified.yaml" << 'YAML'
|
|
# OpenWrt Unified Syslog Acquisition
|
|
# Auto-generated by SecuBox CrowdSec Wizard
|
|
# Uses logread -f to stream all syslog entries
|
|
# Covers: system logs, SSH/Dropbear, firewall (iptables/nftables)
|
|
source: command
|
|
command: /sbin/logread -f
|
|
labels:
|
|
type: syslog
|
|
YAML
|
|
local enabled_sources=""
|
|
[ "$syslog_enabled" = "1" ] && enabled_sources="${enabled_sources}system "
|
|
[ "$ssh_enabled" = "1" ] && enabled_sources="${enabled_sources}SSH "
|
|
[ "$firewall_enabled" = "1" ] && enabled_sources="${enabled_sources}firewall "
|
|
steps_done="${steps_done}Created unified syslog acquisition (${enabled_sources}); "
|
|
else
|
|
rm -f "$acquis_dir/openwrt-unified.yaml"
|
|
steps_done="${steps_done}Disabled syslog acquisition; "
|
|
fi
|
|
|
|
# Enable/disable HTTP log acquisition (separate file-based source)
|
|
if [ "$http_enabled" = "1" ]; then
|
|
# Check if log files exist
|
|
if [ -f "/var/log/uhttpd.log" ]; then
|
|
cat > "$acquis_dir/openwrt-http.yaml" << 'YAML'
|
|
# OpenWrt uHTTPd Web Server Log Acquisition
|
|
# Auto-generated by SecuBox CrowdSec Wizard
|
|
filenames:
|
|
- /var/log/uhttpd.log
|
|
labels:
|
|
type: nginx
|
|
YAML
|
|
elif [ -f "/var/log/nginx/access.log" ]; then
|
|
cat > "$acquis_dir/openwrt-http.yaml" << 'YAML'
|
|
# OpenWrt nginx Web Server Log Acquisition
|
|
# Auto-generated by SecuBox CrowdSec Wizard
|
|
filenames:
|
|
- /var/log/nginx/access.log
|
|
labels:
|
|
type: nginx
|
|
YAML
|
|
else
|
|
# Fallback - try both locations
|
|
cat > "$acquis_dir/openwrt-http.yaml" << 'YAML'
|
|
# OpenWrt Web Server Log Acquisition
|
|
# Auto-generated by SecuBox CrowdSec Wizard
|
|
filenames:
|
|
- /var/log/uhttpd.log
|
|
- /var/log/nginx/access.log
|
|
labels:
|
|
type: nginx
|
|
YAML
|
|
fi
|
|
steps_done="${steps_done}Created HTTP acquisition; "
|
|
else
|
|
rm -f "$acquis_dir/openwrt-http.yaml"
|
|
rm -f "$acquis_dir/openwrt-uhttpd.yaml" 2>/dev/null
|
|
steps_done="${steps_done}Disabled HTTP acquisition; "
|
|
fi
|
|
|
|
# Step 4: Restart CrowdSec to apply acquisition changes
|
|
if /etc/init.d/crowdsec reload >/dev/null 2>&1; then
|
|
steps_done="${steps_done}Reloaded CrowdSec"
|
|
else
|
|
# Fallback to restart if reload fails
|
|
/etc/init.d/crowdsec restart >/dev/null 2>&1
|
|
steps_done="${steps_done}Restarted CrowdSec"
|
|
fi
|
|
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Acquisition configuration completed"
|
|
json_add_string "steps" "$steps_done"
|
|
secubox_log "Acquisition configuration completed: $steps_done"
|
|
|
|
json_dump
|
|
}
|
|
|
|
# Get current acquisition configuration
|
|
get_acquisition_config() {
|
|
json_init
|
|
|
|
# Get values from UCI
|
|
local syslog_enabled=$(uci -q get crowdsec.acquisition.syslog_enabled || echo "1")
|
|
local firewall_enabled=$(uci -q get crowdsec.acquisition.firewall_enabled || echo "1")
|
|
local ssh_enabled=$(uci -q get crowdsec.acquisition.ssh_enabled || echo "1")
|
|
local http_enabled=$(uci -q get crowdsec.acquisition.http_enabled || echo "0")
|
|
local syslog_path=$(uci -q get crowdsec.acquisition.syslog_path || echo "/var/log/messages")
|
|
|
|
json_add_string "syslog_enabled" "$syslog_enabled"
|
|
json_add_string "firewall_enabled" "$firewall_enabled"
|
|
json_add_string "ssh_enabled" "$ssh_enabled"
|
|
json_add_string "http_enabled" "$http_enabled"
|
|
json_add_string "syslog_path" "$syslog_path"
|
|
|
|
# Check which acquisition files exist
|
|
local acquis_dir="/etc/crowdsec/acquis.d"
|
|
local unified_exists=0
|
|
local http_exists=0
|
|
[ -f "$acquis_dir/openwrt-unified.yaml" ] && unified_exists=1
|
|
[ -f "$acquis_dir/openwrt-http.yaml" ] && http_exists=1
|
|
|
|
json_add_boolean "unified_file_exists" "$unified_exists"
|
|
json_add_boolean "http_file_exists" "$http_exists"
|
|
|
|
json_dump
|
|
}
|
|
|
|
# Service control (start/stop/restart/reload)
|
|
service_control() {
|
|
local action="$1"
|
|
json_init
|
|
|
|
case "$action" in
|
|
start|stop|restart|reload)
|
|
secubox_log "CrowdSec service $action requested"
|
|
local output
|
|
output=$(/etc/init.d/crowdsec "$action" 2>&1)
|
|
local result=$?
|
|
sleep 2
|
|
|
|
# Check if service is running after action
|
|
local running=0
|
|
if pgrep crowdsec >/dev/null 2>&1; then
|
|
running=1
|
|
fi
|
|
|
|
if [ "$result" -eq 0 ]; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "action" "$action"
|
|
json_add_boolean "running" "$running"
|
|
json_add_string "message" "Service $action completed"
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Service $action failed"
|
|
json_add_string "output" "$output"
|
|
fi
|
|
;;
|
|
*)
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Invalid action. Use: start, stop, restart, reload"
|
|
;;
|
|
esac
|
|
|
|
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"},"firewall_bouncer_status":{},"control_firewall_bouncer":{"action":"string"},"firewall_bouncer_config":{},"update_firewall_bouncer_config":{"key":"string","value":"string"},"nftables_stats":{},"check_wizard_needed":{},"wizard_state":{},"repair_lapi":{},"reset_wizard":{},"console_status":{},"console_enroll":{"key":"string","name":"string"},"console_disable":{},"service_control":{"action":"string"},"configure_acquisition":{"syslog_enabled":"string","firewall_enabled":"string","ssh_enabled":"string","http_enabled":"string","syslog_path":"string"},"acquisition_config":{}}'
|
|
;;
|
|
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"
|
|
;;
|
|
firewall_bouncer_status)
|
|
get_firewall_bouncer_status
|
|
;;
|
|
control_firewall_bouncer)
|
|
read -r input
|
|
action=$(echo "$input" | jsonfilter -e '@.action' 2>/dev/null)
|
|
control_firewall_bouncer "$action"
|
|
;;
|
|
firewall_bouncer_config)
|
|
get_firewall_bouncer_config
|
|
;;
|
|
update_firewall_bouncer_config)
|
|
read -r input
|
|
key=$(echo "$input" | jsonfilter -e '@.key' 2>/dev/null)
|
|
value=$(echo "$input" | jsonfilter -e '@.value' 2>/dev/null)
|
|
update_firewall_bouncer_config "$key" "$value"
|
|
;;
|
|
nftables_stats)
|
|
get_nftables_stats
|
|
;;
|
|
check_wizard_needed)
|
|
check_wizard_needed
|
|
;;
|
|
wizard_state)
|
|
get_wizard_state
|
|
;;
|
|
repair_lapi)
|
|
repair_lapi
|
|
;;
|
|
reset_wizard)
|
|
reset_wizard
|
|
;;
|
|
console_status)
|
|
get_console_status
|
|
;;
|
|
console_enroll)
|
|
read -r input
|
|
key=$(echo "$input" | jsonfilter -e '@.key' 2>/dev/null)
|
|
name=$(echo "$input" | jsonfilter -e '@.name' 2>/dev/null)
|
|
console_enroll "$key" "$name"
|
|
;;
|
|
console_disable)
|
|
console_disable
|
|
;;
|
|
service_control)
|
|
read -r input
|
|
action=$(echo "$input" | jsonfilter -e '@.action' 2>/dev/null)
|
|
service_control "$action"
|
|
;;
|
|
configure_acquisition)
|
|
read -r input
|
|
syslog_enabled=$(echo "$input" | jsonfilter -e '@.syslog_enabled' 2>/dev/null)
|
|
firewall_enabled=$(echo "$input" | jsonfilter -e '@.firewall_enabled' 2>/dev/null)
|
|
ssh_enabled=$(echo "$input" | jsonfilter -e '@.ssh_enabled' 2>/dev/null)
|
|
http_enabled=$(echo "$input" | jsonfilter -e '@.http_enabled' 2>/dev/null)
|
|
syslog_path=$(echo "$input" | jsonfilter -e '@.syslog_path' 2>/dev/null)
|
|
configure_acquisition "$syslog_enabled" "$firewall_enabled" "$ssh_enabled" "$http_enabled" "$syslog_path"
|
|
;;
|
|
acquisition_config)
|
|
get_acquisition_config
|
|
;;
|
|
*)
|
|
echo '{"error": "Unknown method"}'
|
|
;;
|
|
esac
|
|
;;
|
|
esac
|