7803376bf50c6e0d91d07870b5e5af7cd3611f3d
[feed/packages.git] / net / banip / files / banip-service.sh
1 #!/bin/sh
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.
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_genstatus "processing"
19 f_tmp
20 f_fetch
21 f_getif
22 f_getdev
23 f_getsub
24 f_mkdir "${ban_backupdir}"
25 f_mkfile "${ban_blocklist}"
26 f_mkfile "${ban_allowlist}"
27
28 # firewall check
29 #
30 if [ "${ban_action}" != "reload" ]; then
31 if [ -x "${ban_fw4cmd}" ]; then
32 cnt=0
33 while [ "${cnt}" -lt "10" ] && ! /etc/init.d/firewall status | grep -q "^active"; do
34 cnt="$((cnt + 1))"
35 sleep 1
36 done
37 if ! /etc/init.d/firewall status | grep -q "^active"; then
38 f_log "err" "nft based firewall/fw4 not functional"
39 fi
40 else
41 f_log "err" "nft based firewall/fw4 not found"
42 fi
43 fi
44
45 # init nft namespace
46 #
47 if [ "${ban_action}" != "reload" ] || ! "${ban_nftcmd}" -t list table inet banIP >/dev/null 2>&1; then
48 if f_nftinit "${ban_tmpfile}".init.nft; then
49 f_log "info" "nft namespace initialized"
50 else
51 f_log "err" "nft namespace can't be initialized"
52 fi
53 fi
54
55 # handle downloads
56 #
57 f_log "info" "start banIP download processes"
58 if [ "${ban_allowlistonly}" = "1" ]; then
59 ban_feed=""
60 else
61 json_init
62 if ! json_load_file "${ban_feedfile}" >/dev/null 2>&1; then
63 f_log "err" "banIP feed file can't be loaded"
64 fi
65 [ "${ban_deduplicate}" = "1" ] && printf "\n" >"${ban_tmpfile}.deduplicate"
66 fi
67
68 cnt="1"
69 for feed in allowlist ${ban_feed} blocklist; do
70 # local feeds
71 #
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
79 cnt="$((cnt + 1))"
80 done
81 wait
82 continue
83 fi
84
85 # read external feed information
86 #
87 if ! json_select "${feed}" >/dev/null 2>&1; then
88 continue
89 fi
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
93 done
94 json_select ..
95 # handle IPv4/IPv6 feeds with the same/single download URL
96 #
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}") &
100 feed_url_6="local"
101 wait
102 fi
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
107 cnt="$((cnt + 1))"
108 fi
109 continue
110 fi
111 # handle IPv4/IPv6 feeds with separated download URLs
112 #
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
117 cnt="$((cnt + 1))"
118 fi
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
123 cnt="$((cnt + 1))"
124 fi
125 done
126 wait
127
128 # start domain lookup
129 #
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") &
135
136 # tidy up
137 #
138 f_rmset
139 f_rmdir "${ban_tmpdir}"
140 f_genstatus "active"
141 f_log "info" "finished banIP download processes"
142 rm -rf "${ban_lock}"
143
144 # start log service
145 #
146 if [ -x "${ban_logreadcmd}" ] && [ -n "${ban_logterm%%??}" ]; then
147 f_log "info" "start detached banIP log service"
148
149 nft_expiry="$(printf "%s" "${ban_nftexpiry}" | grep -oE "([0-9]+[h|m|s]$)")"
150 [ -n "${nft_expiry}" ] && nft_expiry="timeout ${nft_expiry}"
151
152 # read log continuously with given logterms
153 #
154 "${ban_logreadcmd}" -fe "${ban_logterm%%??}" 2>/dev/null |
155 while read -r line; do
156 proto=""
157 # IPv4 log parsing
158 #
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}")"
161 ip="${ip##* }"
162 [ -n "${ip}" ] && proto="v4"
163 if [ -z "${proto}" ]; then
164 # IPv6 log parsing
165 #
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}")"
168 ip="${ip##* }"
169 [ -n "${ip}" ] && proto="v6"
170 fi
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}' (${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"
181 fi
182 fi
183 fi
184 fi
185 done
186
187 # start no-op service loop
188 #
189 else
190 f_log "info" "start detached no-op banIP service (logterms are missing)"
191 while :; do
192 sleep 1
193 done
194 fi