fix: CAPI registration with stale credentials cleanup

- repair_lapi() now removes stale online_api_credentials.yaml and retries
- New repair_capi() function for dedicated CAPI repair
- console_enroll() handles CAPI credential cleanup before retry
- Added repairCapi API method in frontend
- Bump luci-app-crowdsec-dashboard to 0.7.0-r20
- Add openwrt-luci-bf.yaml scenario for LuCI brute force detection
- Add secubox-auth-acquis.yaml acquisition config

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
CyberMind-FR 2026-01-13 11:16:58 +01:00
parent 19f903c0c9
commit c2ea22bcab
8 changed files with 186 additions and 30 deletions

View File

@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-crowdsec-dashboard
PKG_VERSION:=0.7.0
PKG_RELEASE:=19
PKG_RELEASE:=20
PKG_ARCH:=all
PKG_LICENSE:=Apache-2.0

View File

@ -197,6 +197,12 @@ var callRepairLapi = rpc.declare({
expect: { }
});
var callRepairCapi = rpc.declare({
object: 'luci.crowdsec-dashboard',
method: 'repair_capi',
expect: { }
});
var callResetWizard = rpc.declare({
object: 'luci.crowdsec-dashboard',
method: 'reset_wizard',
@ -403,6 +409,7 @@ return baseclass.extend({
checkWizardNeeded: callCheckWizardNeeded,
getWizardState: callWizardState,
repairLapi: callRepairLapi,
repairCapi: callRepairCapi,
resetWizard: callResetWizard,
// Console Methods

View File

@ -1120,18 +1120,41 @@ repair_lapi() {
# Step 13: Register with CAPI (required for console enrollment)
if [ "$lapi_ok" = "1" ] && [ -x "$CSCLI" ]; then
local capi_creds="/etc/crowdsec/online_api_credentials.yaml"
local capi_registered=0
# First attempt: try to register with existing credentials
if run_with_timeout 15 "$CSCLI" capi register >/dev/null 2>&1; then
capi_registered=1
steps_done="${steps_done}CAPI registered; "
else
# CAPI registration may fail if already registered, which is OK
# Check if CAPI is already working
local capi_status=""
capi_status=$(run_with_timeout 5 "$CSCLI" capi status 2>&1)
if echo "$capi_status" | grep -qi "registered\|online"; then
if echo "$capi_status" | grep -qi "You can successfully interact with Central API"; then
capi_registered=1
steps_done="${steps_done}CAPI OK; "
else
errors="${errors}CAPI not registered; "
fi
fi
# If CAPI still not working, try with fresh credentials
if [ "$capi_registered" = "0" ] && [ -f "$capi_creds" ]; then
secubox_log "CAPI registration failed, removing stale credentials and retrying..."
rm -f "$capi_creds"
steps_done="${steps_done}CAPI creds reset; "
# Retry registration with clean slate
if run_with_timeout 15 "$CSCLI" capi register >/dev/null 2>&1; then
capi_registered=1
steps_done="${steps_done}CAPI re-registered; "
else
local capi_err=""
capi_err=$(run_with_timeout 10 "$CSCLI" capi register 2>&1)
errors="${errors}CAPI: ${capi_err}; "
fi
elif [ "$capi_registered" = "0" ]; then
errors="${errors}CAPI not registered; "
fi
fi
if [ "$lapi_ok" = "1" ]; then
@ -1150,6 +1173,84 @@ repair_lapi() {
json_dump
}
# Repair CAPI - fix Central API registration issues
repair_capi() {
json_init
local steps_done=""
local errors=""
local capi_creds="/etc/crowdsec/online_api_credentials.yaml"
secubox_log "Starting CAPI repair..."
# Check if CrowdSec and LAPI are running
if ! pgrep crowdsec >/dev/null 2>&1; then
json_add_boolean "success" 0
json_add_string "error" "CrowdSec is not running. Start CrowdSec first."
json_dump
return
fi
if ! run_with_timeout 5 "$CSCLI" lapi status >/dev/null 2>&1; then
json_add_boolean "success" 0
json_add_string "error" "LAPI is not available. Run LAPI repair first."
json_dump
return
fi
# Check current CAPI status
local capi_status=""
capi_status=$(run_with_timeout 5 "$CSCLI" capi status 2>&1)
if echo "$capi_status" | grep -qi "You can successfully interact with Central API"; then
steps_done="${steps_done}CAPI already working; "
json_add_boolean "success" 1
json_add_string "message" "CAPI is already connected"
json_add_string "steps" "$steps_done"
json_dump
return
fi
# Step 1: Backup and remove existing credentials
if [ -f "$capi_creds" ]; then
cp "$capi_creds" "${capi_creds}.bak" 2>/dev/null
rm -f "$capi_creds"
steps_done="${steps_done}Removed stale credentials; "
fi
# Step 2: Register with CAPI
local reg_output=""
reg_output=$(run_with_timeout 20 "$CSCLI" capi register 2>&1)
if [ $? -eq 0 ]; then
steps_done="${steps_done}CAPI registered; "
else
# Check if error is about already registered
if echo "$reg_output" | grep -qi "already registered"; then
steps_done="${steps_done}Already registered; "
else
errors="${errors}Registration failed: ${reg_output}; "
fi
fi
# Step 3: Verify CAPI connection
sleep 2
capi_status=$(run_with_timeout 5 "$CSCLI" capi status 2>&1)
if echo "$capi_status" | grep -qi "You can successfully interact with Central API"; then
steps_done="${steps_done}CAPI connected; "
json_add_boolean "success" 1
json_add_string "message" "CAPI repaired successfully"
json_add_string "steps" "$steps_done"
secubox_log "CAPI repair successful: $steps_done"
else
json_add_boolean "success" 0
json_add_string "error" "CAPI repair incomplete"
json_add_string "steps" "$steps_done"
json_add_string "errors" "$errors"
json_add_string "capi_output" "$capi_status"
secubox_log "CAPI repair failed: $errors"
fi
json_dump
}
# Reset wizard - clean up for fresh start
reset_wizard() {
json_init
@ -1261,23 +1362,38 @@ console_enroll() {
# Step 0: Ensure CAPI is registered (prerequisite for console enrollment)
local capi_ok=0
local capi_status=""
local capi_creds="/etc/crowdsec/online_api_credentials.yaml"
capi_status=$(run_with_timeout 5 "$CSCLI" capi status 2>&1)
if echo "$capi_status" | grep -qi "registered\|online"; then
if echo "$capi_status" | grep -qi "You can successfully interact with Central API"; then
capi_ok=1
secubox_log "CAPI already connected"
else
# Try to register with CAPI
secubox_log "CAPI not registered, attempting registration..."
secubox_log "CAPI not connected, attempting registration..."
if run_with_timeout 15 "$CSCLI" capi register >/dev/null 2>&1; then
capi_ok=1
secubox_log "CAPI registration successful"
else
json_add_boolean "success" 0
json_add_string "error" "CAPI registration failed. Please run LAPI repair first."
json_dump
return
# If registration fails and credentials exist, try with fresh credentials
if [ -f "$capi_creds" ]; then
secubox_log "CAPI registration failed, removing stale credentials..."
rm -f "$capi_creds"
if run_with_timeout 15 "$CSCLI" capi register >/dev/null 2>&1; then
capi_ok=1
secubox_log "CAPI re-registration successful"
fi
fi
fi
fi
if [ "$capi_ok" = "0" ]; then
json_add_boolean "success" 0
json_add_string "error" "CAPI registration failed. Please run LAPI repair first."
json_dump
return
fi
# Build enroll command
local enroll_cmd="run_cscli console enroll $key"
if [ -n "$name" ]; then
@ -1954,7 +2070,7 @@ remove_hub_item() {
# 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":{},"acquisition_metrics":{},"health_check":{},"capi_metrics":{},"hub_available":{},"install_hub_item":{"item_type":"string","item_name":"string"},"remove_hub_item":{"item_type":"string","item_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":{},"check_wizard_needed":{},"wizard_state":{},"repair_lapi":{},"repair_capi":{},"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":{},"acquisition_metrics":{},"health_check":{},"capi_metrics":{},"hub_available":{},"install_hub_item":{"item_type":"string","item_name":"string"},"remove_hub_item":{"item_type":"string","item_name":"string"}}'
;;
call)
case "$2" in
@ -2068,6 +2184,9 @@ case "$1" in
repair_lapi)
repair_lapi
;;
repair_capi)
repair_capi
;;
reset_wizard)
reset_wizard
;;

View File

@ -36,6 +36,10 @@ define Package/secubox-auth-logger/install
$(INSTALL_BIN) ./files/secubox-auth-logger.init $(1)/etc/init.d/secubox-auth-logger
$(INSTALL_DIR) $(1)/etc/crowdsec/parsers/s01-parse
$(INSTALL_DATA) ./files/openwrt-luci-auth.yaml $(1)/etc/crowdsec/parsers/s01-parse/
$(INSTALL_DIR) $(1)/etc/crowdsec/scenarios
$(INSTALL_DATA) ./files/openwrt-luci-bf.yaml $(1)/etc/crowdsec/scenarios/
$(INSTALL_DIR) $(1)/etc/crowdsec/acquis.d
$(INSTALL_DATA) ./files/secubox-auth-acquis.yaml $(1)/etc/crowdsec/acquis.d/
$(INSTALL_DIR) $(1)/etc/uci-defaults
$(INSTALL_BIN) ./files/99-secubox-auth-logger $(1)/etc/uci-defaults/
endef

View File

@ -1,33 +1,24 @@
#!/bin/sh
# SecuBox Auth Logger - Post-install configuration
# Enables verbose logging for Dropbear and uhttpd
# Enables verbose logging for uhttpd and configures CrowdSec
# Note: Dropbear 2024.86 does NOT support -v flag
# Auth monitoring relies on parsing existing syslog messages
# The auth-monitor.sh script watches logread for auth failures
# Enable uhttpd syslog
# Enable uhttpd syslog for LuCI login monitoring
if [ -f /etc/config/uhttpd ]; then
uci set uhttpd.main.syslog='1'
uci commit uhttpd
/etc/init.d/uhttpd restart 2>/dev/null
fi
# Create auth failures log file
touch /var/log/auth-failures.log
chmod 644 /var/log/auth-failures.log
# Create auth log file for secubox-auth-logger
touch /var/log/secubox-auth.log
chmod 644 /var/log/secubox-auth.log
# Add acquisition for CrowdSec if installed
if [ -d /etc/crowdsec/acquis.d ]; then
cat > /etc/crowdsec/acquis.d/secubox-auth.yaml << 'EOF'
# SecuBox Auth Failure Acquisition
# Reads from /var/log/messages for secubox-auth tagged messages
filenames:
- /var/log/messages
labels:
type: syslog
EOF
# Restart CrowdSec to pick up new acquisition
# Restart CrowdSec to pick up new acquisition/parser/scenario
if [ -x /etc/init.d/crowdsec ]; then
/etc/init.d/crowdsec restart 2>/dev/null
fi

View File

@ -1,10 +1,15 @@
#!/bin/sh
# SecuBox Auth Monitor - Lightweight auth failure detection
# Monitors SSH and LuCI login failures, logs to syslog for CrowdSec
# Monitors SSH and LuCI login failures for CrowdSec
# Copyright (C) 2024 CyberMind.fr
LOG_FILE="/var/log/secubox-auth.log"
LOG_TAG="secubox-auth"
# Ensure log file exists
touch "$LOG_FILE"
chmod 644 "$LOG_FILE"
# Track recent IPs to avoid duplicate logging
TRACK_FILE="/tmp/auth-monitor-track"
touch "$TRACK_FILE"
@ -27,7 +32,12 @@ log_failure() {
fi
echo "${key}:${now}" >> "$TRACK_FILE"
# Log to syslog - CrowdSec will parse this
# Log to dedicated file in syslog format for CrowdSec
local ts=$(date "+%b %d %H:%M:%S")
local hostname=$(cat /proc/sys/kernel/hostname 2>/dev/null || echo "OpenWrt")
echo "$ts $hostname $LOG_TAG[$$]: authentication failure for $user from $ip via $service" >> "$LOG_FILE"
# Also log to syslog for local visibility
logger -t "$LOG_TAG" -p auth.warning "authentication failure for $user from $ip via $service"
}

View File

@ -0,0 +1,18 @@
# CrowdSec Scenario for SecuBox LuCI Brute Force Detection
# Triggers when multiple authentication failures are detected from the same IP
# Works with secubox/openwrt-luci-auth parser
type: leaky
name: secubox/openwrt-luci-bf
description: "Detect LuCI/OpenWrt web interface brute force attempts"
filter: "evt.Meta.log_type == 'auth_failure'"
leakspeed: "10s"
capacity: 5
groupby: evt.Meta.source_ip
blackhole: 1m
reprocess: true
labels:
service: http
remediation: true
type: bruteforce
confidence: 3

View File

@ -0,0 +1,7 @@
# SecuBox Auth Logger Acquisition
# Reads authentication failure logs from secubox-auth-logger
# Used by secubox/openwrt-luci-auth parser and secubox/openwrt-luci-bf scenario
filenames:
- /var/log/secubox-auth.log
labels:
type: syslog