Merge pull request #18400 from nmav/tmp-tang-self-contained
[feed/packages.git] / net / banip / files / banip-service.sh
1 #!/bin/sh
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.
5
6 # (s)hellcheck exceptions
7 # shellcheck disable=all
8
9 ban_action="${1}"
10 ban_starttime="$(date "+%s")"
11 ban_funlib="/usr/lib/banip-functions.sh"
12 [ -z "$(command -v "f_system")" ] && . "${ban_funlib}"
13
14 # load config and set banIP environment
15 #
16 f_conf
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"
20 f_tmp
21 f_fetch
22 f_getif
23 f_getdev
24 f_getuplink
25 f_mkdir "${ban_backupdir}"
26 f_mkfile "${ban_blocklist}"
27 f_mkfile "${ban_allowlist}"
28
29 # firewall check
30 #
31 if [ "${ban_action}" != "reload" ]; then
32 if [ -x "${ban_fw4cmd}" ]; then
33 cnt="0"
34 while [ "${cnt}" -lt "30" ] && ! /etc/init.d/firewall status >/dev/null 2>&1; do
35 cnt="$((cnt + 1))"
36 sleep 1
37 done
38 if ! /etc/init.d/firewall status >/dev/null 2>&1; then
39 f_log "err" "error in nft based firewall/fw4"
40 fi
41 else
42 f_log "err" "no nft based firewall/fw4"
43 fi
44 fi
45
46 # init nft namespace
47 #
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"
51 else
52 f_log "err" "can't initialize nft namespace"
53 fi
54 fi
55
56 # handle downloads
57 #
58 f_log "info" "start banIP download processes"
59 if [ "${ban_allowlistonly}" = "1" ]; then
60 ban_feed=""
61 else
62 f_getfeed
63 [ "${ban_deduplicate}" = "1" ] && printf "\n" >"${ban_tmpfile}.deduplicate"
64 fi
65
66 cnt="1"
67 for feed in allowlist ${ban_feed} blocklist; do
68 # local feeds
69 #
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
77 cnt="$((cnt + 1))"
78 done
79 wait
80 continue
81 fi
82
83 # external feeds
84 #
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}"
88 uci_commit "banip"
89 continue
90 fi
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
94 done
95 json_select ..
96
97 # skip incomplete feeds
98 #
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}'"
103 continue
104 fi
105
106 # handle IPv4/IPv6 feeds with the same/single download URL
107 #
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}") &
111 feed_url_6="local"
112 wait
113 fi
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
118 cnt="$((cnt + 1))"
119 fi
120 continue
121 fi
122 # handle IPv4/IPv6 feeds with separated download URLs
123 #
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
128 cnt="$((cnt + 1))"
129 fi
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
134 cnt="$((cnt + 1))"
135 fi
136 done
137 wait
138 f_rmset
139 f_rmdir "${ban_tmpdir}"
140 f_genstatus "active"
141
142 # start domain lookup
143 #
144 f_log "info" "start banIP domain lookup"
145 cnt="1"
146 for list in allowlist blocklist; do
147 (f_lookup "${list}") &
148 hold="$((cnt % ban_cores))"
149 [ "${hold}" = "0" ] && wait
150 cnt="$((cnt + 1))"
151 done
152 wait
153
154 # end processing
155 #
156 if [ "${ban_mailnotification}" = "1" ] && [ -n "${ban_mailreceiver}" ] && [ -x "${ban_mailcmd}" ]; then
157 (
158 sleep ${ban_triggerdelay}
159 f_mail
160 ) &
161 fi
162 json_cleanup
163 rm -rf "${ban_lock}"
164
165 # start detached log service
166 #
167 if [ -x "${ban_logreadcmd}" ] && [ -n "${ban_logterm%%??}" ] && [ "${ban_loglimit}" != "0" ]; then
168 f_log "info" "start detached banIP log service"
169
170 nft_expiry="$(printf "%s" "${ban_nftexpiry}" | grep -oE "([0-9]+[h|m|s]$)")"
171 [ -n "${nft_expiry}" ] && nft_expiry="timeout ${nft_expiry}"
172
173 # read log continuously with given logterms
174 #
175 "${ban_logreadcmd}" -fe "${ban_logterm%%??}" 2>/dev/null |
176 while read -r line; do
177 proto=""
178 # IPv4 log parsing
179 #
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}")"
182 ip="${ip##* }"
183 [ -n "${ip}" ] && proto="v4"
184 if [ -z "${proto}" ]; then
185 # IPv6 log parsing
186 #
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}")"
189 ip="${ip##* }"
190 [ -n "${ip}" ] && proto="v6"
191 fi
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"
202 fi
203 fi
204 fi
205 fi
206 done
207
208 # start detached no-op service loop
209 #
210 else
211 f_log "info" "start detached no-op banIP service"
212 while :; do
213 sleep 1
214 done
215 fi