feat: Add Firewall Bouncer management API to CrowdSec dashboard

Add comprehensive backend support for managing the CrowdSec Firewall Bouncer
through the dashboard with full control and monitoring capabilities.

RPC Backend Enhancements (luci.crowdsec-dashboard):
- get_firewall_bouncer_status: Detailed status (running, enabled, UCI config, nftables)
- control_firewall_bouncer: Service control (start/stop/restart/enable/disable)
- get_firewall_bouncer_config: Read UCI configuration
- update_firewall_bouncer_config: Modify UCI settings
- get_nftables_stats: nftables statistics (blocked IPs, rules count)

API Methods Added (api.js):
- getFirewallBouncerStatus(): Get bouncer status and health
- controlFirewallBouncer(action): Control service lifecycle
- getFirewallBouncerConfig(): Read configuration
- updateFirewallBouncerConfig(key, value): Update settings
- getNftablesStats(): Get firewall statistics

Features:
- Real-time service status monitoring
- nftables table detection (IPv4/IPv6)
- Blocked IP counting
- UCI configuration management
- Service lifecycle control
- Comprehensive error handling

Status Information Provided:
- Service running state
- Init script enabled state
- UCI configuration status
- nftables tables active (crowdsec, crowdsec6)
- Blocked IPv4/IPv6 count
- Rules count per table

Configuration Options Supported:
- enabled, ipv4, ipv6 (boolean)
- api_url, update_frequency, deny_action, log_level (string)
- deny_log, filter_input, filter_forward (boolean)
- interfaces list

Next: Frontend UI enhancements for bouncer management panel

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
CyberMind-FR 2026-01-06 20:19:11 +01:00
parent 9b59b55c9e
commit 24383006aa
2 changed files with 372 additions and 1 deletions

View File

@ -145,6 +145,39 @@ var callDeleteBouncer = rpc.declare({
expect: { success: false }
});
// Firewall Bouncer Management
var callFirewallBouncerStatus = rpc.declare({
object: 'luci.crowdsec-dashboard',
method: 'firewall_bouncer_status',
expect: { }
});
var callControlFirewallBouncer = rpc.declare({
object: 'luci.crowdsec-dashboard',
method: 'control_firewall_bouncer',
params: ['action'],
expect: { success: false }
});
var callFirewallBouncerConfig = rpc.declare({
object: 'luci.crowdsec-dashboard',
method: 'firewall_bouncer_config',
expect: { }
});
var callUpdateFirewallBouncerConfig = rpc.declare({
object: 'luci.crowdsec-dashboard',
method: 'update_firewall_bouncer_config',
params: ['key', 'value'],
expect: { success: false }
});
var callNftablesStats = rpc.declare({
object: 'luci.crowdsec-dashboard',
method: 'nftables_stats',
expect: { }
});
function formatDuration(seconds) {
if (!seconds) return 'N/A';
if (seconds < 60) return seconds + 's';
@ -188,6 +221,13 @@ return baseclass.extend({
registerBouncer: callRegisterBouncer,
deleteBouncer: callDeleteBouncer,
// Firewall Bouncer Management
getFirewallBouncerStatus: callFirewallBouncerStatus,
controlFirewallBouncer: callControlFirewallBouncer,
getFirewallBouncerConfig: callFirewallBouncerConfig,
updateFirewallBouncerConfig: callUpdateFirewallBouncerConfig,
getNftablesStats: callNftablesStats,
formatDuration: formatDuration,
formatDate: formatDate,

View File

@ -462,10 +462,321 @@ delete_bouncer() {
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.$key=$value"
;;
api_url|update_frequency|deny_action|log_level)
# 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.$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
}
# 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"}}'
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":{}}'
;;
call)
case "$2" in
@ -550,6 +861,26 @@ case "$1" in
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
;;
*)
echo '{"error": "Unknown method"}'
;;