banip: update 1.8.1-3

* add better input validation to the f_content and f_search functions,
   to compensate for the very limited Wildcard ACL mechanisms in LuCI, see
   https://github.com/openwrt/luci/issues/8435 for reference
* LuCI: add a proper poll mechanism to mitigate Reporting timeouts
  on "Search" and "Refresh", even with big  Sets
* LuCI: Refine some ACLs
* LuCI: more fixes & optimizations
* readme update

Signed-off-by: Dirk Brenken <dev@brenken.org>
(cherry picked from commit ef91c84fe6)
This commit is contained in:
Dirk Brenken
2026-03-22 16:14:10 +01:00
parent 6fe86fc9d3
commit c27b1d95cf
4 changed files with 124 additions and 36 deletions

View File

@@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=banip
PKG_VERSION:=1.8.1
PKG_RELEASE:=1
PKG_RELEASE:=3
PKG_LICENSE:=GPL-3.0-or-later
PKG_MAINTAINER:=Dirk Brenken <dev@brenken.org>

View File

@@ -132,7 +132,7 @@ Available commands:
enable Enable service autostart
disable Disable service autostart
enabled Check if service is started on boot
report [text|json|mail] Print banIP related Set statistics
report [text|json|mail|gen] Print banIP related Set statistics
search [<IPv4 address>|<IPv6 address>] Check if an element exists in a banIP Set
content [<Set name>] [true|false] Listing of all or only elements with hits of a given banIP Set
running Check if service is running
@@ -438,7 +438,7 @@ To make this work, banIP uses the following external components:
**CGI interface to receive remote logging events**
banIP ships a basic cgi interface in '/www/cgi-bin/banip' to receive remote logging events (disabled by default). The cgi interface evaluates logging events via GET or POST request (see examples below). To enable the cgi interface set the following options:
* set 'ban_remotelog' to '1' to enbale the cgi interface
* set 'ban_remotelog' to '1' to enable the cgi interface
* set 'ban_remotetoken' to a secret transfer token, allowed token characters consist of '[A-Za-z]', '[0-9]', '.' and ':'
Examples to transfer remote logging events from an internal server to banIP via cgi interface:

View File

@@ -819,17 +819,20 @@ f_nftinit() {
printf "%s\n" "delete table inet banIP"
fi
printf "%s\n" "add table inet banIP"
# base chains
#
printf "%s\n" "add chain inet banIP pre-routing { type filter hook prerouting priority -175; policy accept; }"
printf "%s\n" "add chain inet banIP wan-input { type filter hook input priority ${ban_nftpriority}; policy accept; }"
printf "%s\n" "add chain inet banIP wan-forward { type filter hook forward priority ${ban_nftpriority}; policy accept; }"
printf "%s\n" "add chain inet banIP lan-forward { type filter hook forward priority ${ban_nftpriority}; policy accept; }"
# regular chains
#
printf "%s\n" "add chain inet banIP _inbound"
printf "%s\n" "add chain inet banIP _outbound"
printf "%s\n" "add chain inet banIP _reject"
# named counter
#
printf "%s\n" "add counter inet banIP cnt_icmpflood"
@@ -847,33 +850,43 @@ f_nftinit() {
# default pre-routing rules
#
printf "%s\n" "add rule inet banIP pre-routing iifname != { ${wan_dev} } counter accept"
# ct state invalid
#
if [ "${ban_logprerouting}" = "1" ]; then
printf "%s\n" "add rule inet banIP pre-routing ct state invalid ${log_ct}"
fi
printf "%s\n" "add rule inet banIP pre-routing ct state invalid counter name cnt_ctinvalid drop"
# ICMP Flood
#
if [ "${ban_icmplimit}" -gt "0" ]; then
if [ "${ban_logprerouting}" = "1" ]; then
printf "%s\n" "add rule inet banIP pre-routing meta nfproto . meta l4proto { ipv4 . icmp , ipv6 . icmpv6 } limit rate over ${ban_icmplimit}/second ${log_icmp}"
fi
printf "%s\n" "add rule inet banIP pre-routing meta nfproto . meta l4proto { ipv4 . icmp , ipv6 . icmpv6 } limit rate over ${ban_icmplimit}/second counter name cnt_icmpflood drop"
fi
# UDP Flood
#
if [ "${ban_udplimit}" -gt "0" ]; then
if [ "${ban_logprerouting}" = "1" ]; then
printf "%s\n" "add rule inet banIP pre-routing meta l4proto udp ct state new limit rate over ${ban_udplimit}/second ${log_udp}"
fi
printf "%s\n" "add rule inet banIP pre-routing meta l4proto udp ct state new limit rate over ${ban_udplimit}/second counter name cnt_udpflood drop"
fi
# SYN Flood
#
if [ "${ban_synlimit}" -gt "0" ]; then
if [ "${ban_logprerouting}" = "1" ]; then
printf "%s\n" "add rule inet banIP pre-routing tcp flags & (fin|syn|rst|ack) == syn limit rate over ${ban_synlimit}/second ${log_syn}"
fi
printf "%s\n" "add rule inet banIP pre-routing tcp flags & (fin|syn|rst|ack) == syn limit rate over ${ban_synlimit}/second counter name cnt_synflood drop"
fi
# TCP Invalid
#
if [ "${ban_logprerouting}" = "1" ]; then
printf "%s\n" "add rule inet banIP pre-routing tcp flags & (fin|syn) == (fin|syn) ${log_tcp}"
printf "%s\n" "add rule inet banIP pre-routing tcp flags & (syn|rst) == (syn|rst) ${log_tcp}"
@@ -1264,6 +1277,7 @@ f_down() {
# handle external feeds
#
elif [ "${restore_rc}" != "0" ] && [ "${feed_url}" != "local" ]; then
# handle country downloads
#
if [ "${feed%%.*}" = "country" ]; then
@@ -1288,6 +1302,7 @@ f_down() {
feed_rc="4"
fi
fi
# handle asn downloads
#
elif [ "${feed%%.*}" = "asn" ]; then
@@ -1312,6 +1327,7 @@ f_down() {
feed_rc="4"
fi
fi
# handle compressed downloads
#
elif [ "${feed_comp}" = "gz" ]; then
@@ -1350,6 +1366,7 @@ f_down() {
# final file & Set preparation for regular downloads
#
if [ "${feed_rc}" = "0" ] && [ ! -s "${tmp_nft}" ]; then
# deduplicate Sets
#
if [ "${ban_deduplicate}" = "1" ] && [ "${feed_url}" != "local" ] && [ -z "${feed_complete}" ]; then
@@ -1361,6 +1378,7 @@ f_down() {
feed_rc="${?}"
fi
: >"${tmp_raw}" >"${tmp_load}"
# split Sets
#
if [ "${feed_rc}" = "0" ]; then
@@ -1375,6 +1393,7 @@ f_down() {
feed_rc="${?}"
fi
fi
# build nft file
#
if [ "${feed_rc}" = "0" ] && [ -s "${tmp_file}.1" ]; then
@@ -1422,6 +1441,7 @@ f_down() {
fi
: >"${tmp_flush}" >"${tmp_file}.1"
fi
# load generated nft file in banIP table
#
if [ "${feed_rc}" = "0" ]; then
@@ -1434,10 +1454,12 @@ f_down() {
: >"${tmp_split}"
fi
if [ "${cnt_dl:-"0"}" -gt "0" ] || [ "${feed%%.*}" = "allowlist" ] || [ "${feed%%.*}" = "blocklist" ]; then
# load initial file to nftset
#
f_nftload "${tmp_nft}" "can't load initial file to nfset '${feed}'"
feed_rc="${?}"
# load additional split files
#
if [ "${feed_rc}" = "0" ]; then
@@ -1445,6 +1467,7 @@ f_down() {
if [ -s "${split_file}" ]; then
"${ban_sedcmd}" -i "1 i #!${ban_nftcmd} -f\nadd element inet banIP ${feed} { " "${split_file}"
printf "%s\n" "}" >>"${split_file}"
# load split file to nftset
#
f_nftload "${split_file}" "can't load split file '${split_file##*.}' to nfset '${feed}'"
@@ -1522,7 +1545,7 @@ f_rmset() {
continue
fi
;;
asn)
"asn")
asn="${feed%.*}"
asn="${asn#*.}"
if [ "${ban_asnsplit}" = "1" ] && printf "%s" "${ban_feed}" | "${ban_grepcmd}" -q "${feed%%.*}" &&
@@ -2082,57 +2105,92 @@ f_report() {
[ -n "${ban_mailreceiver}" ] && [ -x "${ban_mailcmd}" ] && f_mail
: >"${report_txt}"
;;
*)
"gen")
printf "%s\n" "$(date "+%s")" >"/var/run/banIP.report"
;;
*)
: >"${report_txt}"
;;
esac
}
# Set search
#
f_search() {
local item table_sets ip proto cnt result="/var/run/banIP.search" input="${1}"
local item table_sets ip proto cnt tmp_result result res input="${1}"
if [ -n "${input}" ]; then
ip="$(printf "%s" "${input}" | "${ban_awkcmd}" 'BEGIN{RS="(([1-9][0-9]{0,2}\\.){1}([0-9]{1,3}\\.){2}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?[[:space:]]*$)"}{printf "%s",RT}')"
[ -n "${ip}" ] && proto="v4"
if [ -z "${proto}" ]; then
ip="$(printf "%s" "${input}" | "${ban_awkcmd}" 'BEGIN{RS="(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\\/(1?[0-2][0-8]|[0-9][0-9]))?)([[:space:]].*|$)"}{printf "%s",RT}')"
[ -n "${ip}" ] && proto="v6"
fi
fi
# prepare result file
#
tmp_result="/var/run/banIP.search.tmp"
result="/var/run/banIP.search"
# validate input
#
case "${input}" in
''|*[!0-9A-Fa-f:/.]*)
printf "%s\n%s\n%s\n" ":::" "::: no valid search input" ":::"
printf "%s\n%s\n%s\n" ":::" "::: no valid search input" ":::" >"${result}"
return
;;
esac
# determine protocol via awk
#
res="$(printf "%s" "${input}" | "${ban_awkcmd}" '
{
if (match($0,/([1-9][0-9]{0,2}\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\/([12]?[0-9]|3[012]))?[[:space:]]*$/)) {
printf "v4 %s",substr($0,RSTART,RLENGTH)
} else if (match($0,/(([0-9A-Fa-f]{0,4}:){1,7}[0-9A-Fa-f]{0,4}:?(\/(1?[0-2][0-8]|[0-9][0-9]))?)/)) {
printf "v6 %s",substr($0,RSTART,RLENGTH)
}
}')"
proto="${res%% *}"
ip="${res#* }"
[ "${proto}" != "v4" ] && [ "${proto}" != "v6" ] && proto="" && ip=""
# get relevant Sets
#
if [ -n "${proto}" ]; then
table_sets="$("${ban_nftcmd}" -tj list table inet banIP 2>>"${ban_errorlog}" | "${ban_jsoncmd}" -qe "@.nftables[@.set.type=\"ip${proto}_addr\"].set.name")"
table_sets="$("${ban_nftcmd}" -tj list table inet banIP 2>>"${ban_errorlog}" | \
"${ban_jsoncmd}" -qe "@.nftables[@.set.type=\"ip${proto}_addr\"].set.name")"
else
printf "%s\n%s\n%s\n" ":::" "::: no valid search input" ":::"
printf "%s\n%s\n%s\n" ":::" "::: no valid search input" ":::" >"${result}"
return
fi
# initial output
#
{
printf "%s\n%s\n%s\n" ":::" "::: banIP Search" ":::"
printf " %s\n" "Looking for IP '${ip}' on $(date "+%Y-%m-%d %H:%M:%S")"
printf " %s\n" "---"
} >"${tmp_result}"
# search for IP in Sets
#
cnt="1"
: >"${result}"
for item in ${table_sets}; do
case "${item}" in
*[!a-zA-Z0-9_.]*)
continue
;;
esac
(
if "${ban_nftcmd}" get element inet banIP "${item}" "{ ${ip} }" >/dev/null 2>&1; then
printf "%s " "${item}" >>"${result}"
printf " %s\n" "IP found in Set '${item}'" >>"${tmp_result}"
fi
) &
[ "${cnt}" -gt "${ban_cores}" ] && wait -n
cnt="$((cnt + 1))"
done
wait
if [ -s "${result}" ]; then
printf "%s\n%s\n%s\n" ":::" "::: banIP Search" ":::"
printf " %s\n" "Looking for IP '${ip}' on $(date "+%Y-%m-%d %H:%M:%S")"
printf " %s\n" "---"
for item in $("${ban_catcmd}" "${result}"); do
printf " %s\n" "IP found in Set '${item}'"
done
: >"${result}"
else
printf "%s\n%s\n%s\n" ":::" "::: banIP Search" ":::"
printf " %s\n" "Looking for IP '${ip}' on $(date "+%Y-%m-%d %H:%M:%S")"
printf " %s\n" "---"
printf " %s\n" "IP not found"
# output result
#
if ! "${ban_grepcmd}" -qm1 "found" "${tmp_result}"; then
printf " %s\n" "IP not found" >>"${tmp_result}"
fi
"${ban_mvcmd}" -f "${tmp_result}" "${result}"
"${ban_catcmd}" "${result}"
}
# Set content
@@ -2140,12 +2198,38 @@ f_search() {
f_content() {
local set_raw set_elements input="${1}" filter="${2}"
if [ -z "${input}" ]; then
printf "%s\n%s\n%s\n" ":::" "::: no valid Set input" ":::"
# validate input
#
case "${input}" in
""|*[!a-zA-Z0-9_.]*)
printf "%s\n%s\n%s\n" ":::" "::: no valid Set input" ":::"
return
;;
esac
case "${filter}" in
""|"false")
filter="false"
;;
"true")
filter="true"
;;
*)
printf "%s\n%s\n%s\n" ":::" "::: no valid filter input" ":::"
return
;;
esac
# check if Set exists
#
if ! "${ban_nftcmd}" -t list set inet banIP "${input}" >/dev/null 2>&1; then
printf "%s\n%s\n%s\n" ":::" "::: Set '${input}' not found" ":::"
return
fi
set_raw="$("${ban_nftcmd}" -j list set inet banIP "${input}" 2>>"${ban_errorlog}")"
# get Set content
#
set_raw="$("${ban_nftcmd}" -j list set inet banIP "${input}" 2>>"${ban_errorlog}")"
if [ "$(uci_get banip global ban_nftcount)" = "1" ]; then
if [ "${filter}" = "true" ]; then
set_elements="$(printf "%s" "${set_raw}" | "${ban_jsoncmd}" -qe '@.nftables[*].set.elem[*][@.counter.packets>0].*' |
@@ -2157,6 +2241,9 @@ f_content() {
else
set_elements="$(printf "%s" "${set_raw}" | "${ban_jsoncmd}" -qe '@.nftables[*].set.elem[*]')"
fi
# output result
#
printf "%s\n%s\n%s\n" ":::" "::: banIP Set Content" ":::"
printf " %s\n" "List elements of the Set '${input}' on $(date "+%Y-%m-%d %H:%M:%S")"
printf " %s\n" "---"
@@ -2348,6 +2435,7 @@ ban_zcatcmd="$(f_cmd zcat)"
ban_gzipcmd="$(f_cmd gzip)"
ban_sortcmd="$(f_cmd sort)"
ban_wccmd="$(f_cmd wc)"
ban_mvcmd="$(f_cmd mv)"
f_system
if [ "${ban_action}" != "stop" ]; then

View File

@@ -9,7 +9,7 @@
START=95
USE_PROCD=1
extra_command "report" "[text|json|mail] Print banIP related Set statistics"
extra_command "report" "[text|json|mail|gen] Print banIP related Set statistics"
extra_command "search" "[<IPv4 address>|<IPv6 address>] Check if an element exists in a banIP Set"
extra_command "content" "[<Set name>] [true|false] Listing of all or only elements with hits of a given banIP Set"