feat: Add local crowdsec-firewall-bouncer with nftables integration
New package: crowdsec-firewall-bouncer (v0.0.34) - Based on official OpenWrt package from openwrt/packages - Full nftables integration with IPv4/IPv6 support - Timeout-based sets for automatic ban expiration - Input and forward chain filtering - Interface-based filtering - procd service management with ujail support - UCI configuration Init script features: - Creates nftables tables: crowdsec (IPv4), crowdsec6 (IPv6) - Creates timeout-enabled sets for blocklists - Generates YAML config from UCI settings - Automatic cleanup on service stop Updated secubox-app-crowdsec-bouncer to v0.0.32 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
4b1e0f3405
commit
a0d3faed9e
83
package/secubox/crowdsec-firewall-bouncer/Makefile
Normal file
83
package/secubox/crowdsec-firewall-bouncer/Makefile
Normal file
@ -0,0 +1,83 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# Copyright (C) 2021-2022 Gerald Kerma <gandalf@gk2.net>
|
||||
# Copyright (C) 2024-2025 CyberMind.fr (SecuBox adaptation)
|
||||
#
|
||||
# CrowdSec Firewall Bouncer - nftables integration
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=crowdsec-firewall-bouncer
|
||||
PKG_VERSION:=0.0.34
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/crowdsecurity/cs-firewall-bouncer/tar.gz/v$(PKG_VERSION)?
|
||||
PKG_HASH:=5c58f5cb9a8afc94520f62a39be290e8eea4c1a5bbacc5fea78ccfad9c8da232
|
||||
|
||||
PKG_BUILD_DIR:=$(BUILD_DIR)/cs-firewall-bouncer-$(PKG_VERSION)
|
||||
|
||||
PKG_LICENSE:=MIT
|
||||
PKG_LICENSE_FILES:=LICENSE
|
||||
PKG_MAINTAINER:=CyberMind <contact@cybermind.fr>
|
||||
|
||||
PKG_BUILD_DEPENDS:=golang/host
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
PKG_BUILD_FLAGS:=no-mips16
|
||||
|
||||
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.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
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
TITLE:=CrowdSec Firewall Bouncer
|
||||
URL:=https://github.com/crowdsecurity/cs-firewall-bouncer
|
||||
endef
|
||||
|
||||
define Package/crowdsec-firewall-bouncer
|
||||
$(call Package/crowdsec-firewall-bouncer/Default)
|
||||
DEPENDS:=$(GO_ARCH_DEPENDS) +nftables
|
||||
endef
|
||||
|
||||
define Package/crowdsec-firewall-bouncer/description
|
||||
CrowdSec Firewall Bouncer for OpenWrt/SecuBox.
|
||||
|
||||
Fetches decisions from CrowdSec Local API and enforces them
|
||||
using nftables. Supports both IPv4 and IPv6 blocking with
|
||||
timeout-based set entries for automatic expiration.
|
||||
|
||||
Features:
|
||||
- Native nftables integration
|
||||
- IPv4 and IPv6 support
|
||||
- Input and forward chain filtering
|
||||
- Interface-based filtering
|
||||
- Automatic cleanup on stop
|
||||
- procd service management
|
||||
endef
|
||||
|
||||
define Package/crowdsec-firewall-bouncer/conffiles
|
||||
/etc/config/crowdsec
|
||||
endef
|
||||
|
||||
define Package/crowdsec-firewall-bouncer/install
|
||||
$(call GoPackage/Package/Install/Bin,$(1))
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/config
|
||||
$(INSTALL_CONF) ./files/crowdsec.config $(1)/etc/config/crowdsec
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/init.d
|
||||
$(INSTALL_BIN) ./files/crowdsec-firewall-bouncer.initd $(1)/etc/init.d/crowdsec-firewall-bouncer
|
||||
endef
|
||||
|
||||
$(eval $(call GoBinPackage,crowdsec-firewall-bouncer))
|
||||
$(eval $(call BuildPackage,crowdsec-firewall-bouncer))
|
||||
@ -0,0 +1,250 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
# SPDX-License-Identifier: MIT
|
||||
# CrowdSec Firewall Bouncer - nftables integration for SecuBox OpenWrt
|
||||
# Copyright (C) 2021-2022 Gerald Kerma
|
||||
# Copyright (C) 2024-2025 CyberMind.fr (SecuBox adaptation)
|
||||
|
||||
USE_PROCD=1
|
||||
|
||||
START=99
|
||||
|
||||
NAME=crowdsec-firewall-bouncer
|
||||
PROG=/usr/bin/cs-firewall-bouncer
|
||||
VARCONFIGDIR=/var/etc/crowdsec/bouncers
|
||||
VARCONFIG=/var/etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml
|
||||
|
||||
CONFIGURATION=crowdsec
|
||||
|
||||
TABLE="crowdsec"
|
||||
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
|
||||
}
|
||||
|
||||
init_yaml() {
|
||||
local section="$1"
|
||||
|
||||
local set_only
|
||||
local hook_priority
|
||||
local update_frequency
|
||||
local log_level
|
||||
local api_url
|
||||
local api_key
|
||||
local ipv6
|
||||
local deny_action
|
||||
local deny_log
|
||||
local log_prefix
|
||||
local log_max_size
|
||||
local log_max_backups
|
||||
local log_max_age
|
||||
local ipv4
|
||||
local chain_name
|
||||
local chain6_name
|
||||
local retry_initial_connect
|
||||
|
||||
config_get hook_priority $section priority "4"
|
||||
config_get update_frequency $section update_frequency '10s'
|
||||
config_get log_level $section log_level 'info'
|
||||
config_get api_url $section api_url "http://127.0.0.1:8080"
|
||||
config_get api_key $section api_key "API_KEY"
|
||||
config_get_bool ipv6 $section ipv6 '1'
|
||||
config_get deny_action $section deny_action "drop"
|
||||
config_get_bool deny_log $section deny_log '0'
|
||||
config_get log_prefix $section log_prefix "crowdsec: "
|
||||
config_get log_max_size $section log_max_size '100'
|
||||
config_get log_max_backups $section log_max_backups '3'
|
||||
config_get log_max_age $section log_max_age '30'
|
||||
config_get_bool ipv4 $section ipv4 '1'
|
||||
config_get chain_name $section chain_name "crowdsec-chain"
|
||||
config_get chain6_name $section chain6_name "crowdsec6-chain"
|
||||
config_get_bool retry_initial_connect $section retry_initial_connect '1'
|
||||
|
||||
# Create tmp dir & permissions if needed
|
||||
if [ ! -d "${VARCONFIGDIR}" ]; then
|
||||
mkdir -m 0755 -p "${VARCONFIGDIR}"
|
||||
fi
|
||||
|
||||
cat > $VARCONFIG <<-EOM
|
||||
mode: nftables
|
||||
pid_dir: /var/run/
|
||||
update_frequency: $update_frequency
|
||||
daemonize: true
|
||||
log_mode: file
|
||||
log_dir: /var/log/
|
||||
log_level: $log_level
|
||||
log_compression: true
|
||||
log_max_size: $log_max_size
|
||||
log_max_backups: $log_max_backups
|
||||
log_max_age: $log_max_age
|
||||
api_url: $api_url
|
||||
api_key: $api_key
|
||||
retry_initial_connect: bool($retry_initial_connect)
|
||||
insecure_skip_verify: true
|
||||
disable_ipv6: boolnot($ipv6)
|
||||
deny_action: $deny_action
|
||||
deny_log: bool($deny_log)
|
||||
supported_decisions_type:
|
||||
- ban
|
||||
deny_log_prefix: "$log_prefix"
|
||||
blacklists_ipv4: crowdsec-blacklists
|
||||
blacklists_ipv6: crowdsec6-blacklists
|
||||
ipset_type: nethash
|
||||
iptables_chains:
|
||||
- INPUT
|
||||
nftables:
|
||||
ipv4:
|
||||
enabled: bool($ipv4)
|
||||
set-only: false
|
||||
table: $TABLE
|
||||
chain: $chain_name
|
||||
priority: $hook_priority
|
||||
ipv6:
|
||||
enabled: bool($ipv6)
|
||||
set-only: false
|
||||
table: $TABLE6
|
||||
chain: $chain6_name
|
||||
priority: $hook_priority
|
||||
nftables_hooks:
|
||||
- input
|
||||
- forward
|
||||
pf:
|
||||
anchor_name: ""
|
||||
prometheus:
|
||||
enabled: false
|
||||
listen_addr: 127.0.0.1
|
||||
listen_port: 60601
|
||||
EOM
|
||||
|
||||
# Replace bool placeholders with actual values
|
||||
sed -i "s/bool(1)/true/g" $VARCONFIG
|
||||
sed -i "s/bool(0)/false/g" $VARCONFIG
|
||||
sed -i "s/boolnot(1)/false/g" $VARCONFIG
|
||||
sed -i "s/boolnot(0)/true/g" $VARCONFIG
|
||||
sed -i "s,^\(\s*api_url\s*:\s*\).*\$,\1$api_url," $VARCONFIG
|
||||
sed -i "s,^\(\s*api_key\s*:\s*\).*\$,\1$api_key," $VARCONFIG
|
||||
}
|
||||
|
||||
init_nftables() {
|
||||
local section="$1"
|
||||
|
||||
local hook_priority
|
||||
local deny_action
|
||||
local deny_log
|
||||
local log_prefix
|
||||
local ipv4
|
||||
local ipv6
|
||||
local filter_input
|
||||
local filter_forward
|
||||
local chain_name
|
||||
local chain6_name
|
||||
local interface
|
||||
local log_term=""
|
||||
|
||||
config_get hook_priority $section priority "4"
|
||||
config_get deny_action $section deny_action "drop"
|
||||
config_get_bool deny_log $section deny_log '0'
|
||||
config_get log_prefix $section log_prefix "crowdsec: "
|
||||
config_get_bool ipv4 $section ipv4 '1'
|
||||
config_get_bool ipv6 $section ipv6 '1'
|
||||
config_get_bool filter_input $section filter_input '1'
|
||||
config_get_bool filter_forward $section filter_forward '1'
|
||||
config_get chain_name $section chain_name "crowdsec-chain"
|
||||
config_get chain6_name $section chain6_name "crowdsec6-chain"
|
||||
config_get interface $section interface 'eth1'
|
||||
|
||||
if [ "$deny_log" -eq "1" ]; then
|
||||
log_term="log prefix \"${log_prefix}\""
|
||||
fi
|
||||
|
||||
# Handle multiple interfaces (space-separated to comma-separated)
|
||||
interface="${interface// /, }"
|
||||
|
||||
# Clean up existing tables (kernel 3.18+ supports delete without flush)
|
||||
nft delete table ip crowdsec 2>/dev/null
|
||||
nft delete table ip6 crowdsec6 2>/dev/null
|
||||
|
||||
# Setup IPv4 nftables
|
||||
if [ "$ipv4" -eq "1" ]; then
|
||||
nft add table ip crowdsec
|
||||
nft add set ip crowdsec crowdsec-blacklists '{ type ipv4_addr; flags timeout; }'
|
||||
|
||||
if [ "$filter_input" -eq "1" ]; then
|
||||
nft add chain ip "$TABLE" $chain_name-input "{ type filter hook input priority $hook_priority; policy accept; }"
|
||||
nft add rule ip "$TABLE" $chain_name-input ct state established,related accept
|
||||
nft add rule ip "$TABLE" $chain_name-input iifname != \{ $interface \} accept
|
||||
fi
|
||||
|
||||
if [ "$filter_forward" -eq "1" ]; then
|
||||
nft add chain ip "$TABLE" $chain_name-forward "{ type filter hook forward priority $hook_priority; policy accept; }"
|
||||
nft add rule ip "$TABLE" $chain_name-forward ct state established,related accept
|
||||
nft add rule ip "$TABLE" $chain_name-forward iifname != \{ $interface \} accept
|
||||
fi
|
||||
fi
|
||||
|
||||
# Setup IPv6 nftables
|
||||
if [ "$ipv6" -eq "1" ]; then
|
||||
nft add table ip6 crowdsec6
|
||||
nft add set ip6 crowdsec6 crowdsec6-blacklists '{ type ipv6_addr; flags timeout; }'
|
||||
|
||||
if [ "$filter_input" -eq "1" ]; then
|
||||
nft add chain ip6 "$TABLE6" $chain6_name-input "{ type filter hook input priority $hook_priority; policy accept; }"
|
||||
nft add rule ip6 "$TABLE6" $chain6_name-input ct state established,related accept
|
||||
nft add rule ip6 "$TABLE6" $chain6_name-input iifname != \{ $interface \} accept
|
||||
fi
|
||||
|
||||
if [ "$filter_forward" -eq "1" ]; then
|
||||
nft add chain ip6 "$TABLE6" $chain6_name-forward "{ type filter hook forward priority $hook_priority; policy accept; }"
|
||||
nft add rule ip6 "$TABLE6" $chain6_name-forward ct state established,related accept
|
||||
nft add rule ip6 "$TABLE6" $chain6_name-forward iifname != \{ $interface \} accept
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
run_bouncer() {
|
||||
local section="$1"
|
||||
|
||||
local enabled
|
||||
config_get_bool enabled $section enabled 0
|
||||
|
||||
if [ "$enabled" -eq "1" ]; then
|
||||
init_yaml "$section"
|
||||
init_nftables "$section"
|
||||
|
||||
procd_open_instance
|
||||
procd_set_param command "$PROG" -c "$VARCONFIG"
|
||||
procd_set_param stdout 1
|
||||
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
|
||||
|
||||
procd_close_instance
|
||||
fi
|
||||
}
|
||||
|
||||
start_service() {
|
||||
config_load "${CONFIGURATION}"
|
||||
config_foreach run_bouncer bouncer
|
||||
}
|
||||
|
||||
service_stopped() {
|
||||
# Clean up config file
|
||||
rm -f $VARCONFIG
|
||||
|
||||
# Remove nftables tables
|
||||
nft delete table ip crowdsec 2>/dev/null
|
||||
nft delete table ip6 crowdsec6 2>/dev/null
|
||||
}
|
||||
|
||||
reload_service() {
|
||||
stop
|
||||
start
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
# CrowdSec Firewall Bouncer Configuration
|
||||
# SecuBox OpenWrt Edition
|
||||
#
|
||||
# This bouncer fetches decisions from CrowdSec LAPI and enforces them
|
||||
# using nftables with automatic expiration via timeout sets.
|
||||
|
||||
config bouncer
|
||||
option enabled '0'
|
||||
option ipv4 '1'
|
||||
option ipv6 '1'
|
||||
option api_url 'http://127.0.0.1:8080/'
|
||||
option api_key ''
|
||||
option update_frequency '10s'
|
||||
option priority '4'
|
||||
option deny_action 'drop'
|
||||
option deny_log '1'
|
||||
option log_prefix 'CrowdSec: '
|
||||
option log_level 'info'
|
||||
option log_max_size '100'
|
||||
option log_max_backups '3'
|
||||
option log_max_age '30'
|
||||
option filter_input '1'
|
||||
option filter_forward '1'
|
||||
option chain_name 'crowdsec-chain'
|
||||
option chain6_name 'crowdsec6-chain'
|
||||
option retry_initial_connect '1'
|
||||
list interface 'br-lan'
|
||||
list interface 'eth1'
|
||||
@ -1,7 +1,7 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=secubox-app-crowdsec-bouncer
|
||||
PKG_VERSION:=0.0.31
|
||||
PKG_VERSION:=0.0.32
|
||||
PKG_RELEASE:=1
|
||||
PKG_ARCH:=all
|
||||
PKG_MAINTAINER:=CyberMind Studio <contact@cybermind.fr>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user