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:
parent
9b59b55c9e
commit
24383006aa
@ -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,
|
||||
|
||||
|
||||
@ -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"}'
|
||||
;;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user