2 # banIP main service script - ban incoming and outgoing ip addresses/subnets via sets in nftables
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_genstatus
"processing"
24 f_mkdir
"${ban_backupdir}"
25 f_mkfile
"${ban_blocklist}"
26 f_mkfile
"${ban_allowlist}"
30 if [ "${ban_action}" != "reload" ]; then
31 if [ -x "${ban_fw4cmd}" ]; then
33 while [ "${cnt}" -lt "10" ] && ! /etc
/init.d
/firewall status |
grep -q "^active"; do
37 if ! /etc
/init.d
/firewall status |
grep -q "^active"; then
38 f_log
"err" "nft based firewall/fw4 not functional"
41 f_log
"err" "nft based firewall/fw4 not found"
47 if [ "${ban_action}" != "reload" ] ||
! "${ban_nftcmd}" -t list
set inet banIP allowlistvMAC
>/dev
/null
2>&1; then
48 if f_nftinit
"${ban_tmpfile}".init.nft
; then
49 f_log
"info" "nft namespace initialized"
51 f_log
"err" "nft namespace can't be initialized"
57 f_log
"info" "start banIP download processes"
58 if [ "${ban_allowlistonly}" = "1" ]; then
62 if ! json_load_file
"${ban_feedfile}" >/dev
/null
2>&1; then
63 f_log
"err" "banIP feed file can't be loaded"
65 [ "${ban_deduplicate}" = "1" ] && printf "\n" >"${ban_tmpfile}.deduplicate"
69 for feed
in allowlist
${ban_feed} blocklist
; do
72 if [ "${feed}" = "allowlist" ] ||
[ "${feed}" = "blocklist" ]; then
73 for proto
in MAC
4 6; do
74 [ "${feed}" = "blocklist" ] && wait
75 (f_down
"${feed}" "${proto}") &
76 [ "${feed}" = "blocklist" ] || { [ "${feed}" = "allowlist" ] && [ "${proto}" = "MAC" ]; } && wait
77 hold="$
((cnt
% ban_cores
))"
78 [ "${hold}" = "0" ] && wait
85 # read external feed information
87 if ! json_select "${feed}" >/dev/null 2>&1; then
90 json_objects="url_4 rule_4 url_6 rule_6 flag
"
91 for object in ${json_objects}; do
92 eval json_get_var feed_"${object}" '${object}' >/dev/null 2>&1
95 # handle IPv4/IPv6 feeds with the same/single download URL
97 if [ "${feed_url_4}" = "${feed_url_6}" ]; then
98 if [ "${ban_protov4}" = "1" ] && [ -n "${feed_url_4}" ] && [ -n "${feed_rule_4}" ]; then
99 (f_down "${feed}" "4" "${feed_url_4}" "${feed_rule_4}" "${feed_flag}") &
103 if [ "${ban_protov6}" = "1" ] && [ -n "${feed_url_6}" ] && [ -n "${feed_rule_6}" ]; then
104 (f_down "${feed}" "6" "${feed_url_6}" "${feed_rule_6}" "${feed_flag}") &
105 hold="$
((cnt
% ban_cores
))"
106 [ "${hold}" = "0" ] && wait
111 # handle IPv4/IPv6 feeds with separated download URLs
113 if [ "${ban_protov4}" = "1" ] && [ -n "${feed_url_4}" ] && [ -n "${feed_rule_4}" ]; then
114 (f_down "${feed}" "4" "${feed_url_4}" "${feed_rule_4}" "${feed_flag}") &
115 hold="$
((cnt
% ban_cores
))"
116 [ "${hold}" = "0" ] && wait
119 if [ "${ban_protov6}" = "1" ] && [ -n "${feed_url_6}" ] && [ -n "${feed_rule_6}" ]; then
120 (f_down "${feed}" "6" "${feed_url_6}" "${feed_rule_6}" "${feed_flag}") &
121 hold="$
((cnt
% ban_cores
))"
122 [ "${hold}" = "0" ] && wait
128 # start domain lookup
130 f_log "info
" "start detached banIP domain lookup
"
131 (f_lookup "allowlist
") &
132 hold="$
((cnt
% ban_cores
))"
133 [ "${hold}" = "0" ] && wait
134 (f_lookup "blocklist
") &
139 f_rmdir "${ban_tmpdir}"
141 f_log "info
" "finished banIP download processes
"
146 if [ -x "${ban_logreadcmd}" ] && [ -n "${ban_logterm%%??}" ]; then
147 f_log "info
" "start detached banIP log service
"
149 nft_expiry="$
(printf "%s" "${ban_nftexpiry}" |
grep -oE "([0-9]+[h|m|s]$)")"
150 [ -n "${nft_expiry}" ] && nft_expiry="timeout
${nft_expiry}"
152 # read log continuously with given logterms
154 "${ban_logreadcmd}" -fe "${ban_logterm%%??}" 2>/dev/null |
155 while read -r line; do
159 ip="$
(printf "%s" "${line}" | "${ban_awkcmd}" 'BEGIN{RS="(([0-9]{1,3}\\.){3}[0-9]{1,3})+"}{if(!seen
[RT
]++)printf "%s ",RT
}')"
160 ip="$(f_trim "${ip}")"
162 [ -n "${ip}" ] && proto="v4"
163 if [ -z "${proto}" ]; then
166 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}')"
167 ip="$
(f_trim
"${ip}")"
169 [ -n "${ip}" ] && proto="v6
"
171 if [ -n "${proto}" ] && ! "${ban_nftcmd}" get element inet banIP blocklist"${proto}" "{ ${ip} }" >/dev/null 2>&1; then
172 f_log "info
" "suspicious IP
${proto} found
'${ip}'"
173 log_raw="$
("${ban_logreadcmd}" -l "${ban_loglimit}" 2>/dev
/null
)"
174 log_count="$
(printf "%s\n" "${log_raw}" |
grep -c "found '${ip}'")"
175 if [ "${log_count}" -ge "${ban_logcount}" ]; then
176 if "${ban_nftcmd}" add element inet banIP "blocklist${proto}" "{ ${ip} ${nft_expiry} }" >/dev/null 2>&1; then
177 f_log "info
" "added IP
${proto} '${ip}' (expiry: ${nft_expiry:-"-"}) to blocklist${proto} set"
178 if [ "${ban_autoblocklist}" = "1" ] && ! grep -q "^${ip}" "${ban_blocklist}"; then
179 printf "%-42s%s
\n" "${ip}" "# added on $(date "+%Y-%m-%d %H:%M:%S")" >>"${ban_blocklist}"
180 f_log
"info" "added IP${proto} '${ip}' to local blocklist"
187 # start no-op service loop
190 f_log
"info" "start detached no-op banIP service (logterms are missing)"