2 # banIP main service script - ban incoming and outgoing IPs via named nftables Sets
3 # Copyright (c) 2018-2023 Dirk Brenken (dev@brenken.org)
4 # This is free software, licensed under the GNU General Public License v3.
6 # (s)hellcheck exceptions
7 # shellcheck disable=all
10 ban_starttime
="$(date "+%s
")"
11 ban_funlib
="/usr/lib/banip-functions.sh"
12 [ -z "$(command -v "f_system
")" ] && .
"${ban_funlib}"
14 # load config and set banIP environment
17 f_log
"info" "start banIP processing (${ban_action})"
18 f_log
"debug" "f_system ::: system: ${ban_sysver:-"n/a"}, version: ${ban_ver:-"n/a"}, memory: ${ban_memory:-"0"}, cpu_cores: ${ban_cores}"
19 f_genstatus
"processing"
25 f_mkdir
"${ban_backupdir}"
26 f_mkfile
"${ban_blocklist}"
27 f_mkfile
"${ban_allowlist}"
31 if [ "${ban_action}" != "reload" ]; then
32 if [ -x "${ban_fw4cmd}" ]; then
34 while [ "${cnt}" -lt "30" ] && ! /etc
/init.d
/firewall status
>/dev
/null
2>&1; do
38 if ! /etc
/init.d
/firewall status
>/dev
/null
2>&1; then
39 f_log
"err" "error in nft based firewall/fw4"
42 f_log
"err" "no nft based firewall/fw4"
48 if [ "${ban_action}" != "reload" ] ||
! "${ban_nftcmd}" -t list
set inet banIP allowlistvMAC
>/dev
/null
2>&1; then
49 if f_nftinit
"${ban_tmpfile}".init.nft
; then
50 f_log
"info" "initialize nft namespace"
52 f_log
"err" "can't initialize nft namespace"
58 f_log
"info" "start banIP download processes"
59 if [ "${ban_allowlistonly}" = "1" ]; then
63 [ "${ban_deduplicate}" = "1" ] && printf "\n" >"${ban_tmpfile}.deduplicate"
67 for feed
in allowlist
${ban_feed} blocklist
; do
70 if [ "${feed}" = "allowlist" ] ||
[ "${feed}" = "blocklist" ]; then
71 for proto
in MAC
4 6; do
72 [ "${feed}" = "blocklist" ] && wait
73 (f_down
"${feed}" "${proto}") &
74 [ "${feed}" = "blocklist" ] || { [ "${feed}" = "allowlist" ] && [ "${proto}" = "MAC" ]; } && wait
75 hold="$
((cnt
% ban_cores
))"
76 [ "${hold}" = "0" ] && wait
85 if ! json_select "${feed}" >/dev/null 2>&1; then
86 f_log "info
" "remove unknown feed
'${feed}'"
87 uci_remove_list banip global ban_feed "${feed}"
91 json_objects="url_4 rule_4 url_6 rule_6 flag
"
92 for object in ${json_objects}; do
93 eval json_get_var feed_"${object}" '${object}' >/dev/null 2>&1
97 # skip incomplete feeds
99 if { { [ -n "${feed_url_4}" ] && [ -z "${feed_rule_4}" ]; } || { [ -z "${feed_url_4}" ] && [ -n "${feed_rule_4}" ]; }; } ||
100 { { [ -n "${feed_url_6}" ] && [ -z "${feed_rule_6}" ]; } || { [ -z "${feed_url_6}" ] && [ -n "${feed_rule_6}" ]; }; } ||
101 { [ -z "${feed_url_4}" ] && [ -z "${feed_rule_4}" ] && [ -z "${feed_url_6}" ] && [ -z "${feed_rule_6}" ]; }; then
102 f_log
"info" "skip incomplete feed '${feed}'"
106 # handle IPv4/IPv6 feeds with the same/single download URL
108 if [ "${feed_url_4}" = "${feed_url_6}" ]; then
109 if [ "${ban_protov4}" = "1" ] && [ -n "${feed_url_4}" ] && [ -n "${feed_rule_4}" ]; then
110 (f_down
"${feed}" "4" "${feed_url_4}" "${feed_rule_4}" "${feed_flag}") &
114 if [ "${ban_protov6}" = "1" ] && [ -n "${feed_url_6}" ] && [ -n "${feed_rule_6}" ]; then
115 (f_down
"${feed}" "6" "${feed_url_6}" "${feed_rule_6}" "${feed_flag}") &
116 hold
="$((cnt % ban_cores))"
117 [ "${hold}" = "0" ] && wait
122 # handle IPv4/IPv6 feeds with separated download URLs
124 if [ "${ban_protov4}" = "1" ] && [ -n "${feed_url_4}" ] && [ -n "${feed_rule_4}" ]; then
125 (f_down
"${feed}" "4" "${feed_url_4}" "${feed_rule_4}" "${feed_flag}") &
126 hold
="$((cnt % ban_cores))"
127 [ "${hold}" = "0" ] && wait
130 if [ "${ban_protov6}" = "1" ] && [ -n "${feed_url_6}" ] && [ -n "${feed_rule_6}" ]; then
131 (f_down
"${feed}" "6" "${feed_url_6}" "${feed_rule_6}" "${feed_flag}") &
132 hold
="$((cnt % ban_cores))"
133 [ "${hold}" = "0" ] && wait
139 f_rmdir
"${ban_tmpdir}"
142 # start domain lookup
144 f_log
"info" "start banIP domain lookup"
146 for list
in allowlist blocklist
; do
147 (f_lookup
"${list}") &
148 hold
="$((cnt % ban_cores))"
149 [ "${hold}" = "0" ] && wait
156 if [ "${ban_mailnotification}" = "1" ] && [ -n "${ban_mailreceiver}" ] && [ -x "${ban_mailcmd}" ]; then
158 sleep ${ban_triggerdelay}
165 # start detached log service
167 if [ -x "${ban_logreadcmd}" ] && [ -n "${ban_logterm%%??}" ] && [ "${ban_loglimit}" != "0" ]; then
168 f_log
"info" "start detached banIP log service"
170 nft_expiry
="$(printf "%s
" "${ban_nftexpiry}" | grep -oE "([0-9]+[h|m|s
]$
)")"
171 [ -n "${nft_expiry}" ] && nft_expiry
="timeout ${nft_expiry}"
173 # read log continuously with given logterms
175 "${ban_logreadcmd}" -fe "${ban_logterm%%??}" 2>/dev
/null |
176 while read -r line
; do
180 ip
="$(printf "%s
" "${line}" | "${ban_awkcmd}" 'BEGIN{RS="(([0-9]{1,3}\\.
){3}[0-9]{1,3})+"}{if(!seen[RT]++)printf "%s
",RT}')"
181 ip
="$(f_trim "${ip}")"
183 [ -n "${ip}" ] && proto
="v4"
184 if [ -z "${proto}" ]; then
187 ip
="$(printf "%s
" "${line}" | "${ban_awkcmd}" 'BEGIN{RS="([A-Fa-f0-9]{1,4}::?
){3,7}[A-Fa-f0-9
]{1,4}"}{if(!seen[RT]++)printf "%s
",RT}')"
188 ip
="$(f_trim "${ip}")"
190 [ -n "${ip}" ] && proto
="v6"
192 if [ -n "${proto}" ] && ! "${ban_nftcmd}" get element inet banIP blocklist"${proto}" "{ ${ip} }" >/dev
/null
2>&1; then
193 f_log
"info" "suspicious IP${proto} '${ip}'"
194 log_raw
="$("${ban_logreadcmd}" -l "${ban_loglimit}" 2>/dev/null)"
195 log_count
="$(printf "%s
\n" "${log_raw}" | grep -c "suspicious IP${proto} '${ip}'")"
196 if [ "${log_count}" -ge "${ban_logcount}" ]; then
197 if "${ban_nftcmd}" add element inet banIP "blocklist${proto}" "{ ${ip} ${nft_expiry} }" >/dev/null 2>&1; then
198 f_log "info" "add IP${proto} '${ip}' (expiry: ${nft_expiry:-"-"}) to blocklist${proto} set"
199 if [ "${ban_autoblocklist}" = "1" ] && ! grep -q "^${ip}" "${ban_blocklist}"; then
200 printf "%-42s%s\n" "${ip}" "# added on $(date "+%Y-%m-%d %H:%M:%S")" >>"${ban_blocklist}"
201 f_log "info" "add IP${proto} '${ip}' to local blocklist"
208 # start detached no-op service loop
211 f_log "info" "start detached no-op banIP service"