Merge pull request #6 from gkerma/release/v0.15.0

Release/v0.15.0
This commit is contained in:
CyberMind 2026-01-15 15:41:45 +01:00 committed by GitHub
commit 76b3ff4f93
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 391 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))

View File

@ -2060,6 +2060,136 @@ run_firmware_build() {
return 0
}
# Build native packages using full OpenWrt toolchain (without firmware)
run_toolchain_build() {
local device="${1:-espressobin-v7}"
local packages=("${@:2}")
print_header "Building Native Packages (Toolchain)"
# Parse device profile for architecture
parse_device_profile "$device" || return 1
# Check dependencies
check_dependencies
# Setup OpenWrt environment
download_openwrt_source || return 1
setup_openwrt_feeds || return 1
copy_secubox_to_openwrt || return 1
# Generate minimal config for package building
print_header "Generating Package Build Configuration"
cd "$OPENWRT_DIR"
# Start with minimal config
cat > .config << EOF
# Target configuration for $FW_TARGET/$FW_SUBTARGET
CONFIG_TARGET_${FW_TARGET}=y
CONFIG_TARGET_${FW_TARGET}_${FW_SUBTARGET}=y
CONFIG_TARGET_MULTI_PROFILE=y
CONFIG_TARGET_ALL_PROFILES=y
# Build packages only (no firmware)
CONFIG_ALL_NONSHARED=n
CONFIG_ALL_KMODS=n
CONFIG_ALL=n
# Enable SecuBox native packages
CONFIG_PACKAGE_secubox-app-cs-firewall-bouncer=m
CONFIG_PACKAGE_secubox-app-crowdsec=m
CONFIG_PACKAGE_secubox-app-ndpid=m
CONFIG_PACKAGE_secubox-app-netifyd=m
CONFIG_PACKAGE_secubox-app-nodogsplash=m
# Required dependencies
CONFIG_PACKAGE_nftables=y
CONFIG_PACKAGE_golang=y
# Disable GDB to speed up build
CONFIG_GDB=n
EOF
# Expand config
make defconfig
# Download sources
print_info "Downloading package sources..."
make download -j$(nproc) V=s 2>&1 | grep -v "^make\[" || true
# Build prerequisite targets first
# This builds tools, toolchain, and target preparation in one step
print_header "Building Prerequisites (Tools + Toolchain)"
print_info "This may take 1-2 hours on first run..."
# Build tools, toolchain, and target preparation (needed for packages)
if ! make tools/install toolchain/install target/compile V=s -j$(nproc) 2>&1 | tee build-prereqs.log; then
print_warning "Prerequisites build had issues, continuing..."
fi
print_success "Prerequisites built"
# Build Go compiler for host (needed for Go packages)
print_header "Building Go Compiler (for Go packages)"
if ! make package/feeds/packages/lang/golang/host/compile V=s -j$(nproc) 2>&1 | tee build-golang.log; then
print_warning "Go compiler build had issues, some packages may fail"
fi
print_success "Go compiler ready"
# Build specific packages or all native SecuBox packages
print_header "Compiling Native Packages"
local native_packages=(
"secubox-app-cs-firewall-bouncer"
"secubox-app-crowdsec"
"secubox-app-ndpid"
"secubox-app-netifyd"
"secubox-app-nodogsplash"
)
if [[ ${#packages[@]} -gt 0 ]]; then
native_packages=("${packages[@]}")
fi
local built=()
local failed=()
for pkg in "${native_packages[@]}"; do
print_info "Building: $pkg"
if make package/$pkg/compile V=s -j$(nproc) 2>&1 | tee -a build-$pkg.log; then
built+=("$pkg")
print_success "Built: $pkg"
else
failed+=("$pkg")
print_error "Failed: $pkg"
fi
done
# Collect built packages
print_header "Collecting Built Packages"
local output_dir="$BUILD_DIR/toolchain-$ARCH"
mkdir -p "$output_dir"
# Find and copy IPK packages
find "$OPENWRT_DIR/bin/packages" -name "*.ipk" -exec cp {} "$output_dir/" \; 2>/dev/null || true
find "$OPENWRT_DIR/bin/targets" -name "*.ipk" -exec cp {} "$output_dir/" \; 2>/dev/null || true
local pkg_count=$(find "$output_dir" -name "*.ipk" 2>/dev/null | wc -l)
cd - > /dev/null
# Summary
print_header "Toolchain Build Summary"
print_success "Built: ${#built[@]} packages: ${built[*]}"
if [[ ${#failed[@]} -gt 0 ]]; then
print_error "Failed: ${#failed[@]} packages: ${failed[*]}"
fi
print_success "Total IPK packages: $pkg_count"
print_success "Location: $output_dir/"
return 0
}
# Show usage
show_usage() {
cat << EOF
@ -2074,6 +2204,7 @@ COMMANDS:
build Build all packages for x86_64
build <package> Build single package
build --arch <arch> Build for specific architecture
build-toolchain <device> Build native packages (Go/C++) using full toolchain
build-firmware <device> Build full firmware image for device
debug-firmware <device> Debug firmware build (check config without building)
full Run validation then build
@ -2221,6 +2352,12 @@ main() {
run_firmware_build "$device"
;;
build-toolchain)
local device="${1:-espressobin-v7}"
shift || true
run_toolchain_build "$device" "$@"
;;
debug-firmware)
local device="$1"
if [[ -z "$device" ]]; then