refactor(packages): Rename and reorganize SecuBox packages

- Rename crowdsec-firewall-bouncer to secubox-app-cs-firewall-bouncer
- Rename secubox-auth-logger to secubox-app-auth-logger
- Delete secubox-crowdsec-setup (merged into other packages)
- Fix circular dependencies in luci-app-secubox-crowdsec
- Fix dependency chain in secubox-app-crowdsec-bouncer
- Add consolidated get_overview API to crowdsec-dashboard
- Improve crowdsec-dashboard overview performance

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
CyberMind-FR 2026-01-15 10:42:52 +01:00
parent fd7caeb8c3
commit e62919eec7
23 changed files with 254 additions and 1165 deletions

View File

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

View File

@ -526,6 +526,7 @@ return view.extend({
]),
E('div', { 'class': 'cs-charts-row' }, [
this.renderFirewallHealth(),
this.renderFirewallBlocks()
]),
@ -871,6 +872,78 @@ return view.extend({
]);
},
// Firewall Health Status Card
renderFirewallHealth: function() {
var stats = this.nftablesStats || {};
var health = stats.firewall_health || {};
var status = health.status || 'unknown';
var issues = health.issues || '';
var bouncerRunning = health.bouncer_running;
var uciEnabled = health.uci_enabled;
var apiKeyConfigured = health.api_key_configured;
var inputHooked = health.input_chain_hooked;
var forwardHooked = health.forward_chain_hooked;
var setsHaveTimeout = health.sets_have_timeout;
var decisionsSynced = health.decisions_synced;
var cscliDecisions = health.cscli_decisions_count || 0;
var nftElements = health.nft_elements_count || 0;
var statusColor = status === 'ok' ? '#00d4aa' : (status === 'warning' ? '#ffa500' : '#ff4757');
var statusIcon = status === 'ok' ? '✅' : (status === 'warning' ? '⚠️' : '❌');
var statusText = status === 'ok' ? 'Healthy' : (status === 'warning' ? 'Warning' : 'Error');
var checkItems = [
{ label: 'Bouncer Process', ok: bouncerRunning, detail: bouncerRunning ? 'Running' : 'Not running' },
{ label: 'UCI Enabled', ok: uciEnabled, detail: uciEnabled ? 'Enabled' : 'Disabled' },
{ label: 'API Key', ok: apiKeyConfigured, detail: apiKeyConfigured ? 'Configured' : 'Missing or default' },
{ label: 'Input Chain', ok: inputHooked, detail: inputHooked ? 'Hooked' : 'Not hooked' },
{ label: 'Forward Chain', ok: forwardHooked, detail: forwardHooked ? 'Hooked' : 'Not hooked' },
{ label: 'Set Timeout', ok: setsHaveTimeout, detail: setsHaveTimeout ? 'Enabled' : 'Disabled' },
{ label: 'Decisions Sync', ok: decisionsSynced, detail: decisionsSynced ? (nftElements + ' synced') : 'Out of sync' }
];
var checkRows = checkItems.map(function(item) {
return E('div', { 'style': 'display: flex; align-items: center; justify-content: space-between; padding: 0.4em 0; border-bottom: 1px solid rgba(255,255,255,0.05);' }, [
E('div', { 'style': 'display: flex; align-items: center; gap: 0.5em;' }, [
E('span', { 'style': 'font-size: 1em;' }, item.ok ? '✅' : '❌'),
E('span', { 'style': 'font-size: 0.85em;' }, item.label)
]),
E('span', { 'style': 'font-size: 0.75em; color: ' + (item.ok ? '#00d4aa' : '#ff4757') + ';' }, item.detail)
]);
});
return E('div', { 'class': 'cs-card', 'style': 'flex: 1;' }, [
E('div', { 'class': 'cs-card-header' }, [
E('div', { 'class': 'cs-card-title' }, [
_('Firewall Health'),
E('span', {
'style': 'margin-left: 0.75em; font-size: 0.8em; padding: 0.2em 0.6em; background: ' + statusColor + '; border-radius: 12px;'
}, statusIcon + ' ' + statusText)
])
]),
E('div', { 'class': 'cs-card-body' }, [
// Status summary
issues ? E('div', { 'style': 'background: rgba(255,71,87,0.1); border: 1px solid rgba(255,71,87,0.3); border-radius: 8px; padding: 0.75em; margin-bottom: 1em;' }, [
E('div', { 'style': 'font-size: 0.85em; color: #ff4757;' }, issues)
]) : E('span'),
// Sync stats
E('div', { 'style': 'display: flex; gap: 1em; margin-bottom: 1em;' }, [
E('div', { 'style': 'flex: 1; text-align: center; padding: 0.5em; background: rgba(102,126,234,0.1); border-radius: 8px;' }, [
E('div', { 'style': 'font-size: 1.25em; font-weight: 700; color: #667eea;' }, String(cscliDecisions)),
E('div', { 'style': 'font-size: 0.7em; color: #888;' }, 'Decisions')
]),
E('div', { 'style': 'flex: 1; text-align: center; padding: 0.5em; background: rgba(0,212,170,0.1); border-radius: 8px;' }, [
E('div', { 'style': 'font-size: 1.25em; font-weight: 700; color: #00d4aa;' }, String(nftElements)),
E('div', { 'style': 'font-size: 0.7em; color: #888;' }, 'In Firewall')
])
]),
// Check items
E('div', {}, checkRows)
])
]);
},
// Firewall Blocks - Shows IPs blocked in nftables
renderFirewallBlocks: function() {
var self = this;

View File

@ -947,6 +947,86 @@ get_nftables_stats() {
json_add_int "ipv6_cscli_count" "$ipv6_cscli"
json_add_int "ipv6_total_count" "$((ipv6_capi + ipv6_cscli))"
# Firewall Health Check
json_add_object "firewall_health"
# Check bouncer process
local bouncer_running=0
if pgrep cs-firewall-bouncer >/dev/null 2>&1; then
bouncer_running=1
fi
json_add_boolean "bouncer_running" "$bouncer_running"
# Check UCI config
local uci_enabled=0
local uci_api_key=""
if uci -q get crowdsec.bouncer.enabled >/dev/null 2>&1; then
[ "$(uci -q get crowdsec.bouncer.enabled)" = "1" ] && uci_enabled=1
fi
uci_api_key=$(uci -q get crowdsec.bouncer.api_key 2>/dev/null)
json_add_boolean "uci_enabled" "$uci_enabled"
json_add_boolean "api_key_configured" "$([ -n "$uci_api_key" ] && [ "$uci_api_key" != "API_KEY" ] && echo 1 || echo 0)"
# Check chains are hooked (input/forward)
local input_hooked=0
local forward_hooked=0
if [ "$ipv4_exists" = "1" ]; then
nft list table ip crowdsec 2>/dev/null | grep -q "hook input" && input_hooked=1
nft list table ip crowdsec 2>/dev/null | grep -q "hook forward" && forward_hooked=1
fi
json_add_boolean "input_chain_hooked" "$input_hooked"
json_add_boolean "forward_chain_hooked" "$forward_hooked"
# Check if sets have timeout flag (required for auto-expiry)
local sets_have_timeout=0
if [ "$ipv4_exists" = "1" ]; then
nft list set ip crowdsec crowdsec-blacklists 2>/dev/null | grep -q "flags timeout" && sets_have_timeout=1
fi
json_add_boolean "sets_have_timeout" "$sets_have_timeout"
# Check decisions sync (compare cscli decisions count vs nftables)
local cscli_decisions=0
local nft_elements=0
local sync_ok=0
if command -v cscli >/dev/null 2>&1; then
cscli_decisions=$(cscli decisions list -o json 2>/dev/null | jsonfilter -e '@[*]' 2>/dev/null | wc -l || echo "0")
fi
nft_elements=$((ipv4_capi + ipv4_cscli + ipv4_other + ipv6_capi + ipv6_cscli))
# Sync is OK if nft has at least some elements when decisions exist
[ "$cscli_decisions" -gt 0 ] && [ "$nft_elements" -gt 0 ] && sync_ok=1
[ "$cscli_decisions" -eq 0 ] && [ "$nft_elements" -eq 0 ] && sync_ok=1
json_add_int "cscli_decisions_count" "$cscli_decisions"
json_add_int "nft_elements_count" "$nft_elements"
json_add_boolean "decisions_synced" "$sync_ok"
# Overall health status
local health_status="ok"
local health_issues=""
if [ "$bouncer_running" != "1" ]; then
health_status="error"
health_issues="Bouncer not running; "
fi
if [ "$uci_enabled" != "1" ]; then
health_status="warning"
health_issues="${health_issues}Bouncer not enabled in UCI; "
fi
if [ "$ipv4_exists" != "1" ]; then
health_status="error"
health_issues="${health_issues}IPv4 table missing; "
fi
if [ "$input_hooked" != "1" ] && [ "$forward_hooked" != "1" ]; then
health_status="error"
health_issues="${health_issues}No chains hooked; "
fi
if [ "$sync_ok" != "1" ]; then
health_status="warning"
health_issues="${health_issues}Decisions not synced to firewall; "
fi
json_add_string "status" "$health_status"
json_add_string "issues" "$health_issues"
json_close_object
json_dump
}

View File

@ -11,7 +11,7 @@ PKG_VERSION:=1.0.0
PKG_RELEASE:=1
LUCI_TITLE:=LuCI SecuBox CrowdSec Dashboard
LUCI_DEPENDS:=+luci-base +crowdsec +crowdsec-firewall-bouncer
LUCI_DEPENDS:=+luci-base +crowdsec +secubox-app-cs-firewall-bouncer
LUCI_PKGARCH:=all
PKG_MAINTAINER:=Gerald Kerma <gandalf@gk2.net>

View File

@ -3,7 +3,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=secubox-auth-logger
PKG_NAME:=secubox-app-auth-logger
PKG_VERSION:=1.2.2
PKG_RELEASE:=1
PKG_ARCH:=all
@ -12,15 +12,16 @@ PKG_MAINTAINER:=CyberMind <contact@cybermind.fr>
include $(INCLUDE_DIR)/package.mk
define Package/secubox-auth-logger
define Package/secubox-app-auth-logger
SECTION:=secubox
CATEGORY:=SecuBox
TITLE:=Authentication Failure Logger for CrowdSec
DEPENDS:=+rpcd +uhttpd +libubox-lua
PKGARCH:=all
PROVIDES:=secubox-auth-logger
endef
define Package/secubox-auth-logger/description
define Package/secubox-app-auth-logger/description
Logs authentication failures from LuCI/rpcd and Dropbear SSH
for CrowdSec detection. Includes:
- SSH failure monitoring (OpenSSH/Dropbear)
@ -32,14 +33,14 @@ endef
define Build/Compile
endef
define Package/secubox-auth-logger/install
define Package/secubox-app-auth-logger/install
# Auth monitor script
$(INSTALL_DIR) $(1)/usr/lib/secubox
$(INSTALL_BIN) ./files/auth-monitor.sh $(1)/usr/lib/secubox/
# Init script
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_BIN) ./files/secubox-auth-logger.init $(1)/etc/init.d/secubox-auth-logger
$(INSTALL_BIN) ./files/secubox-app-auth-logger.init $(1)/etc/init.d/secubox-app-auth-logger
# RPCD plugin for auth logging via ubus
$(INSTALL_DIR) $(1)/usr/libexec/rpcd
@ -61,10 +62,6 @@ define Package/secubox-auth-logger/install
$(INSTALL_DIR) $(1)/etc/crowdsec/parsers/s01-parse
$(INSTALL_DATA) ./files/openwrt-luci-auth.yaml $(1)/etc/crowdsec/parsers/s01-parse/
# CrowdSec whitelist for private IPs (RFC1918)
$(INSTALL_DIR) $(1)/etc/crowdsec/parsers/s02-enrich
$(INSTALL_DATA) ./files/secubox-private-ip-whitelist.yaml $(1)/etc/crowdsec/parsers/s02-enrich/
# CrowdSec scenario
$(INSTALL_DIR) $(1)/etc/crowdsec/scenarios
$(INSTALL_DATA) ./files/openwrt-luci-bf.yaml $(1)/etc/crowdsec/scenarios/
@ -75,28 +72,28 @@ define Package/secubox-auth-logger/install
# UCI defaults for first boot setup
$(INSTALL_DIR) $(1)/etc/uci-defaults
$(INSTALL_BIN) ./files/99-secubox-auth-logger $(1)/etc/uci-defaults/
$(INSTALL_BIN) ./files/99-secubox-app-auth-logger $(1)/etc/uci-defaults/
endef
define Package/secubox-auth-logger/postinst
define Package/secubox-app-auth-logger/postinst
#!/bin/sh
[ -n "$${IPKG_INSTROOT}" ] || {
# Restart rpcd to load new plugin
/etc/init.d/rpcd restart 2>/dev/null
# Enable and start auth monitor
/etc/init.d/secubox-auth-logger enable
/etc/init.d/secubox-auth-logger start
/etc/init.d/secubox-app-auth-logger enable
/etc/init.d/secubox-app-auth-logger start
# Run uci-defaults to inject JS hook
/etc/uci-defaults/99-secubox-auth-logger 2>/dev/null || true
/etc/uci-defaults/99-secubox-app-auth-logger 2>/dev/null || true
echo "SecuBox Auth Logger installed - LuCI login failures now logged for CrowdSec"
}
exit 0
endef
define Package/secubox-auth-logger/postrm
define Package/secubox-app-auth-logger/postrm
#!/bin/sh
[ -n "$${IPKG_INSTROOT}" ] || {
# Restore dispatcher from backup
@ -127,4 +124,4 @@ define Package/secubox-auth-logger/postrm
exit 0
endef
$(eval $(call BuildPackage,secubox-auth-logger))
$(eval $(call BuildPackage,secubox-app-auth-logger))

View File

@ -15,7 +15,7 @@ define Package/secubox-app-crowdsec-bouncer
PKGARCH:=all
SUBMENU:=SecuBox Apps
TITLE:=SecuBox CrowdSec Firewall Bouncer wrapper
DEPENDS:=+uci +libuci +crowdsec-firewall-bouncer +crowdsec +nftables
DEPENDS:=+uci +libuci +secubox-app-cs-firewall-bouncer +crowdsec +nftables
endef
define Package/secubox-app-crowdsec-bouncer/description

View File

@ -3,18 +3,20 @@
# Copyright (C) 2021-2022 Gerald Kerma <gandalf@gk2.net>
# Copyright (C) 2024-2025 CyberMind.fr (SecuBox adaptation)
#
# CrowdSec Firewall Bouncer - nftables integration
# SecuBox CrowdSec Firewall Bouncer - nftables integration
#
include $(TOPDIR)/rules.mk
PKG_NAME:=crowdsec-firewall-bouncer
PKG_VERSION:=0.0.34
PKG_NAME:=secubox-app-cs-firewall-bouncer
PKG_VERSION:=0.0.31
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
# Source from upstream CrowdSec
# Note: v0.0.31 is the last version compatible with Go 1.23 (OpenWrt 24.10 SDK)
PKG_SOURCE:=crowdsec-firewall-bouncer-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/crowdsecurity/cs-firewall-bouncer/tar.gz/v$(PKG_VERSION)?
PKG_HASH:=5c58f5cb9a8afc94520f62a39be290e8eea4c1a5bbacc5fea78ccfad9c8da232
PKG_HASH:=c34963f0680ae296ae974d8f6444a2d1e2dd7617e7b05d4ad85c320529eec5f5
PKG_BUILD_DIR:=$(BUILD_DIR)/cs-firewall-bouncer-$(PKG_VERSION)
@ -30,27 +32,30 @@ GO_PKG:=github.com/crowdsecurity/cs-firewall-bouncer
# Build version information
GO_PKG_LDFLAGS_X:= \
github.com/crowdsecurity/go-cs-lib/version.Tag=v$(PKG_VERSION)-openwrt \
github.com/crowdsecurity/go-cs-lib/version.Tag=v$(PKG_VERSION)-secubox \
github.com/crowdsecurity/go-cs-lib/version.Timestamp=$(SOURCE_DATE_EPOCH) \
github.com/crowdsecurity/go-cs-lib/version.GoVersion=$(shell $(GO_STAGING_DIR)/bin/go version | cut -d" " -f3)
include $(INCLUDE_DIR)/package.mk
include $(TOPDIR)/feeds/packages/lang/golang/golang-package.mk
define Package/crowdsec-firewall-bouncer/Default
define Package/secubox-app-cs-firewall-bouncer/Default
SECTION:=net
CATEGORY:=Network
TITLE:=CrowdSec Firewall Bouncer
SUBMENU:=SecuBox
TITLE:=SecuBox CrowdSec Firewall Bouncer
URL:=https://github.com/crowdsecurity/cs-firewall-bouncer
endef
define Package/crowdsec-firewall-bouncer
$(call Package/crowdsec-firewall-bouncer/Default)
define Package/secubox-app-cs-firewall-bouncer
$(call Package/secubox-app-cs-firewall-bouncer/Default)
DEPENDS:=$(GO_ARCH_DEPENDS) +nftables
PROVIDES:=crowdsec-firewall-bouncer
CONFLICTS:=crowdsec-firewall-bouncer
endef
define Package/crowdsec-firewall-bouncer/description
CrowdSec Firewall Bouncer for OpenWrt/SecuBox.
define Package/secubox-app-cs-firewall-bouncer/description
SecuBox CrowdSec Firewall Bouncer for OpenWrt.
Fetches decisions from CrowdSec Local API and enforces them
using nftables. Supports both IPv4 and IPv6 blocking with
@ -61,15 +66,15 @@ define Package/crowdsec-firewall-bouncer/description
- IPv4 and IPv6 support
- Input and forward chain filtering
- Interface-based filtering
- Automatic cleanup on stop
- Automatic restart on firewall reload
- procd service management
endef
define Package/crowdsec-firewall-bouncer/conffiles
define Package/secubox-app-cs-firewall-bouncer/conffiles
/etc/config/crowdsec
endef
define Package/crowdsec-firewall-bouncer/install
define Package/secubox-app-cs-firewall-bouncer/install
$(call GoPackage/Package/Install/Bin,$(1))
$(INSTALL_DIR) $(1)/etc/config
@ -77,7 +82,11 @@ define Package/crowdsec-firewall-bouncer/install
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_BIN) ./files/crowdsec-firewall-bouncer.initd $(1)/etc/init.d/crowdsec-firewall-bouncer
# Hotplug script to restart bouncer when firewall reloads
$(INSTALL_DIR) $(1)/etc/hotplug.d/iface
$(INSTALL_DATA) ./files/hotplug.d/99-crowdsec-bouncer $(1)/etc/hotplug.d/iface/99-crowdsec-bouncer
endef
$(eval $(call GoBinPackage,crowdsec-firewall-bouncer))
$(eval $(call BuildPackage,crowdsec-firewall-bouncer))
$(eval $(call GoBinPackage,secubox-app-cs-firewall-bouncer))
$(eval $(call BuildPackage,secubox-app-cs-firewall-bouncer))

View File

@ -7,6 +7,7 @@
USE_PROCD=1
START=99
STOP=10
NAME=crowdsec-firewall-bouncer
PROG=/usr/bin/cs-firewall-bouncer
@ -21,6 +22,8 @@ TABLE6="crowdsec6"
service_triggers() {
procd_add_reload_trigger crowdsec-firewall-bouncer
procd_add_config_trigger "config.change" "crowdsec" /etc/init.d/crowdsec-firewall-bouncer reload
# Restart bouncer when firewall reloads to re-apply nftables rules
procd_add_reload_trigger firewall
}
init_yaml() {
@ -235,19 +238,37 @@ run_bouncer() {
procd_set_param stderr 1
procd_set_param nice 10
# Use ujail if available for security isolation
if [ -x "/sbin/ujail" ]; then
procd_add_jail cs-bouncer log
procd_add_jail_mount $VARCONFIG
procd_add_jail_mount_rw /var/log/
procd_set_param no_new_privs 1
fi
# Note: ujail disabled - bouncer needs direct nftables access
# to add/remove IPs from sets which requires CAP_NET_ADMIN
# if [ -x "/sbin/ujail" ]; then
# procd_add_jail cs-bouncer log
# procd_add_jail_mount $VARCONFIG
# procd_add_jail_mount_rw /var/log/
# procd_set_param no_new_privs 1
# fi
procd_close_instance
fi
}
wait_for_firewall() {
# Wait for fw4/nftables to be ready (max 30 seconds)
local i=0
while [ $i -lt 30 ]; do
if nft list tables >/dev/null 2>&1; then
return 0
fi
sleep 1
i=$((i + 1))
done
logger -t crowdsec-bouncer "Warning: nftables not ready after 30s, starting anyway"
return 1
}
start_service() {
# Wait for firewall/nftables to be ready
wait_for_firewall
config_load "${CONFIGURATION}"
config_foreach run_bouncer bouncer
}

View File

@ -0,0 +1,31 @@
#!/bin/sh
# CrowdSec Firewall Bouncer - Interface/Firewall hotplug handler
# Ensures bouncer's nftables rules are applied after network/firewall changes
# Only act on interface up events for WAN
[ "$ACTION" = "ifup" ] || exit 0
[ "$INTERFACE" = "wan" ] || [ "$INTERFACE" = "wan6" ] || exit 0
# Check if bouncer is enabled
. /lib/functions.sh
config_load crowdsec
is_enabled() {
local section="$1"
local enabled
config_get_bool enabled "$section" enabled 0
[ "$enabled" -eq 1 ] && return 0
return 1
}
bouncer_enabled=0
config_foreach is_enabled bouncer && bouncer_enabled=1
[ "$bouncer_enabled" -eq 1 ] || exit 0
# Check if crowdsec tables exist - if not, bouncer needs restart
if ! nft list table ip crowdsec >/dev/null 2>&1; then
logger -t crowdsec-bouncer "WAN up but crowdsec nftables missing, restarting bouncer"
sleep 2
/etc/init.d/crowdsec-firewall-bouncer restart
fi

View File

@ -1,17 +0,0 @@
# CrowdSec Whitelist for Private IP Ranges
# Prevents blocking of internal network addresses (RFC1918)
# These IPs should never be banned as they are local network devices
name: secubox/private-ip-whitelist
description: "Whitelist private/internal IP ranges to prevent self-blocking"
whitelist:
reason: "Private IP addresses (RFC1918) - local network devices"
ip:
- "127.0.0.0/8" # Localhost
- "10.0.0.0/8" # Class A private
- "172.16.0.0/12" # Class B private
- "192.168.0.0/16" # Class C private
- "169.254.0.0/16" # Link-local
- "::1/128" # IPv6 localhost
- "fe80::/10" # IPv6 link-local
- "fc00::/7" # IPv6 unique local

View File

@ -1,53 +0,0 @@
# SPDX-License-Identifier: MIT
#
# SecuBox CrowdSec Setup Package
# Copyright (C) 2025 CyberMind.fr - Gandalf <gandalf@gk2.net>
#
include $(TOPDIR)/rules.mk
PKG_NAME:=secubox-crowdsec-setup
PKG_VERSION:=1.0.0
PKG_RELEASE:=1
PKG_MAINTAINER:=Gerald Kerma <gandalf@gk2.net>
PKG_LICENSE:=MIT
include $(INCLUDE_DIR)/package.mk
define Package/secubox-crowdsec-setup
SECTION:=secubox
CATEGORY:=SecuBox
SUBMENU:=Security
TITLE:=SecuBox CrowdSec Setup Utility
DEPENDS:=+crowdsec +crowdsec-firewall-bouncer +syslog-ng
PKGARCH:=all
endef
define Package/secubox-crowdsec-setup/description
Script d'installation automatisee de CrowdSec pour SecuBox.
Configure syslog-ng pour le forwarding des logs vers CrowdSec,
installe les collections de securite, et configure le bouncer
nftables pour fw4.
endef
define Build/Compile
endef
define Package/secubox-crowdsec-setup/install
$(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) ./files/usr/sbin/secubox-crowdsec-setup $(1)/usr/sbin/
$(INSTALL_DIR) $(1)/etc/secubox/backups/crowdsec
endef
define Package/secubox-crowdsec-setup/postinst
#!/bin/sh
[ -n "$${IPKG_INSTROOT}" ] || {
echo "SecuBox CrowdSec Setup installe."
echo "Executez 'secubox-crowdsec-setup' pour configurer CrowdSec."
}
exit 0
endef
$(eval $(call BuildPackage,secubox-crowdsec-setup))