You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1028 lines
30 KiB

  1. #!/bin/sh
  2. # banIP - ban incoming and outgoing ip adresses/subnets via ipset
  3. # written by Dirk Brenken (dev@brenken.org)
  4. # This is free software, licensed under the GNU General Public License v3.
  5. # You should have received a copy of the GNU General Public License
  6. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  7. # (s)hellcheck exceptions
  8. # shellcheck disable=1091,2039,2086,2140,2143,2181,2188
  9. # set initial defaults
  10. #
  11. LC_ALL=C
  12. PATH="/usr/sbin:/usr/bin:/sbin:/bin"
  13. ban_ver="0.3.12"
  14. ban_basever=""
  15. ban_enabled=0
  16. ban_automatic="1"
  17. ban_sources=""
  18. ban_iface=""
  19. ban_debug=0
  20. ban_backupdir="/mnt"
  21. ban_maxqueue=4
  22. ban_autoblacklist=1
  23. ban_autowhitelist=1
  24. ban_realtime="false"
  25. ban_fetchutil=""
  26. ban_ipt="$(command -v iptables)"
  27. ban_ipt_save="$(command -v iptables-save)"
  28. ban_ipt_restore="$(command -v iptables-restore)"
  29. ban_ipt6="$(command -v ip6tables)"
  30. ban_ipt6_save="$(command -v ip6tables-save)"
  31. ban_ipt6_restore="$(command -v ip6tables-restore)"
  32. ban_ipset="$(command -v ipset)"
  33. ban_logger="$(command -v logger)"
  34. ban_chain="banIP"
  35. ban_action="${1:-"start"}"
  36. ban_pidfile="/var/run/banip.pid"
  37. ban_rtfile="/tmp/ban_runtime.json"
  38. ban_logservice="/etc/banip/banip.service"
  39. ban_sshdaemon=""
  40. ban_setcnt=0
  41. ban_cnt=0
  42. ban_log_src=0
  43. ban_log_dst=0
  44. # load environment
  45. #
  46. f_envload()
  47. {
  48. # get system information
  49. #
  50. ban_sysver="$(ubus -S call system board 2>/dev/null | jsonfilter -e '@.model' -e '@.release.description' | \
  51. awk 'BEGIN{ORS=", "}{print $0}' | awk '{print substr($0,1,length($0)-2)}')"
  52. # parse 'global' and 'extra' section by callback
  53. #
  54. config_cb()
  55. {
  56. local type="${1}"
  57. if [ "${type}" = "banip" ]
  58. then
  59. option_cb()
  60. {
  61. local option="${1}"
  62. local value="${2}"
  63. eval "${option}=\"${value}\""
  64. }
  65. else
  66. reset_cb
  67. fi
  68. }
  69. # parse 'source' typed sections
  70. #
  71. parse_config()
  72. {
  73. local value opt section="${1}" options="ban_src ban_src_6 ban_src_rset ban_src_rset_6 ban_src_settype ban_src_ruletype ban_src_on ban_src_on_6 ban_src_cat"
  74. for opt in ${options}
  75. do
  76. config_get value "${section}" "${opt}"
  77. if [ -n "${value}" ]
  78. then
  79. eval "${opt}_${section}=\"${value}\""
  80. if [ "${opt}" = "ban_src" ]
  81. then
  82. eval "ban_sources=\"${ban_sources} ${section}\""
  83. elif [ "${opt}" = "ban_src_6" ]
  84. then
  85. eval "ban_sources=\"${ban_sources} ${section}_6\""
  86. fi
  87. fi
  88. done
  89. }
  90. # load config
  91. #
  92. config_load banip
  93. config_foreach parse_config source
  94. # setup logging
  95. #
  96. ban_log_chain_src="${ban_log_chain_src:-"${ban_chain}_log_src"}"
  97. if [ "${ban_log_src}" -eq 1 ]
  98. then
  99. log_target_src="${ban_target_src:-"DROP"}"
  100. ban_target_src="${ban_log_chain_src}"
  101. log_target_src_6="${ban_target_src_6:-"DROP"}"
  102. ban_target_src_6="${ban_log_chain_src}"
  103. fi
  104. ban_log_chain_dst="${ban_log_chain_dst:-"${ban_chain}_log_dst"}"
  105. if [ "${ban_log_dst}" -eq 1 ]
  106. then
  107. log_target_dst="${ban_target_dst:-"REJECT"}"
  108. ban_target_dst="${ban_log_chain_dst}"
  109. log_target_dst_6="${ban_target_dst_6:-"REJECT"}"
  110. ban_target_dst_6="${ban_log_chain_dst}"
  111. fi
  112. # version check
  113. #
  114. if [ -z "${ban_basever}" ] || [ "${ban_ver%.*}" != "${ban_basever}" ]
  115. then
  116. f_log "info" "your banIP config seems to be too old, please update your config with the '--force-maintainer' opkg option"
  117. f_rmtemp
  118. exit 0
  119. fi
  120. # create temp directory & files
  121. #
  122. f_temp
  123. # check status
  124. #
  125. if [ "${ban_enabled}" -eq 0 ]
  126. then
  127. f_bgserv "stop"
  128. f_jsnup disabled
  129. f_ipset destroy
  130. f_rmbackup
  131. f_rmtemp
  132. f_log "info" "banIP is currently disabled, please set ban_enabled to '1' to use this service"
  133. exit 0
  134. fi
  135. }
  136. # check environment
  137. #
  138. f_envcheck()
  139. {
  140. local util utils packages iface tmp cnt=0 cnt_max=0
  141. f_jsnup "running"
  142. f_log "info" "start banIP processing (${ban_action})"
  143. # check backup directory
  144. #
  145. if [ ! -d "${ban_backupdir}" ]
  146. then
  147. f_log "err" "the backup directory '${ban_backupdir}' does not exist or has not been mounted yet, please create the directory or raise the 'ban_triggerdelay' to defer the banIP start"
  148. fi
  149. # get wan devices and wan subnets
  150. #
  151. if [ "${ban_automatic}" = "1" ]
  152. then
  153. while [ "${cnt}" -le 30 ]
  154. do
  155. network_find_wan iface
  156. if [ -n "${iface}" ] && [ -z "$(printf "%s\\n" "${ban_iface}" | grep -F "${iface}")" ]
  157. then
  158. ban_iface="${ban_iface} ${iface}"
  159. if [ "${cnt_max}" -eq 0 ]
  160. then
  161. cnt_max=$((cnt+5))
  162. fi
  163. fi
  164. network_find_wan6 iface
  165. if [ -n "${iface}" ] && [ -z "$(printf "%s\\n" "${ban_iface}" | grep -F "${iface}")" ]
  166. then
  167. ban_iface="${ban_iface} ${iface}"
  168. if [ "${cnt_max}" -eq 0 ]
  169. then
  170. cnt_max=$((cnt+5))
  171. fi
  172. fi
  173. if [ -z "${ban_iface}" ] || [ "${cnt}" -le "${cnt_max}" ]
  174. then
  175. network_flush_cache
  176. cnt=$((cnt+1))
  177. sleep 1
  178. else
  179. break
  180. fi
  181. done
  182. fi
  183. for iface in ${ban_iface}
  184. do
  185. network_get_device tmp "${iface}"
  186. if [ -n "${tmp}" ] && [ -z "$(printf "%s\\n" "${ban_dev}" | grep -F "${tmp}")" ]
  187. then
  188. ban_dev="${ban_dev} ${tmp}"
  189. else
  190. network_get_physdev tmp "${iface}"
  191. if [ -n "${tmp}" ] && [ -z "$(printf "%s\\n" "${ban_dev}" | grep -F "${tmp}")" ]
  192. then
  193. ban_dev="${ban_dev} ${tmp}"
  194. fi
  195. fi
  196. network_get_subnets tmp "${iface}"
  197. if [ -n "${tmp}" ] && [ -z "$(printf "%s\\n" "${ban_subnets}" | grep -F "${tmp}")" ]
  198. then
  199. ban_subnets="${ban_subnets} ${tmp}"
  200. fi
  201. network_get_subnets6 tmp "${iface}"
  202. if [ -n "${tmp}" ] && [ -z "$(printf "%s\\n" "${ban_subnets6}" | grep -F "${tmp}")" ]
  203. then
  204. ban_subnets6="${ban_subnets6} ${tmp}"
  205. fi
  206. done
  207. ban_dev_all="$(ip link show 2>/dev/null | awk 'BEGIN{FS="[@: ]"}/^[0-9:]/{if($3!="lo"){print $3}}')"
  208. if [ -z "${ban_iface}" ] || [ -z "${ban_dev}" ] || [ -z "${ban_dev_all}" ]
  209. then
  210. f_log "err" "wan interface(s)/device(s) (${ban_iface:-"-"}/${ban_dev:-"-"}) not found, please please check your configuration"
  211. fi
  212. # check fetch utility
  213. #
  214. if [ -z "${ban_fetchutil}" ]
  215. then
  216. cnt_max=$((cnt+5))
  217. while [ -z "${packages}" ]
  218. do
  219. packages="$(opkg list-installed 2>/dev/null)"
  220. if [ "${cnt}" -gt "${cnt_max}" ]
  221. then
  222. break
  223. fi
  224. cnt=$((cnt+1))
  225. sleep 1
  226. done
  227. if [ -n "${packages}" ]
  228. then
  229. utils="aria2c curl wget uclient-fetch"
  230. for util in ${utils}
  231. do
  232. if { [ "${util}" = "uclient-fetch" ] && [ -n "$(printf "%s\\n" "${packages}" | grep "^libustream-")" ]; } || \
  233. { [ "${util}" = "wget" ] && [ -n "$(printf "%s\\n" "${packages}" | grep "^wget -")" ]; } || \
  234. { [ "${util}" != "uclient-fetch" ] && [ "${util}" != "wget" ]; }
  235. then
  236. ban_fetchutil="$(command -v "${util}")"
  237. if [ -x "${ban_fetchutil}" ]
  238. then
  239. break
  240. fi
  241. fi
  242. unset ban_fetchutil util
  243. done
  244. fi
  245. else
  246. util="${ban_fetchutil}"
  247. ban_fetchutil="$(command -v "${util}")"
  248. if [ ! -x "${ban_fetchutil}" ]
  249. then
  250. unset ban_fetchutil util
  251. fi
  252. fi
  253. case "${util}" in
  254. "aria2c")
  255. ban_fetchparm="${ban_fetchparm:-"--timeout=20 --allow-overwrite=true --auto-file-renaming=false --check-certificate=true --dir=/ -o"}"
  256. ;;
  257. "curl")
  258. ban_fetchparm="${ban_fetchparm:-"--connect-timeout 20 -o"}"
  259. ;;
  260. "uclient-fetch")
  261. ban_fetchparm="${ban_fetchparm:-"--timeout=20 -O"}"
  262. ;;
  263. "wget")
  264. ban_fetchparm="${ban_fetchparm:-"--no-cache --no-cookies --max-redirect=0 --timeout=20 -O"}"
  265. ;;
  266. esac
  267. if [ -z "${ban_fetchutil}" ] || [ -z "${ban_fetchparm}" ]
  268. then
  269. f_log "err" "download utility with SSL support not found, please install 'uclient-fetch' with a 'libustream-*' variant or another download utility like 'wget', 'curl' or 'aria2'"
  270. fi
  271. # check ssh daemon
  272. #
  273. if [ -z "${ban_sshdaemon}" ]
  274. then
  275. utils="sshd dropbear"
  276. for util in ${utils}
  277. do
  278. if [ -x "$(command -v "${util}")" ]
  279. then
  280. if [ "$("/etc/init.d/${util}" enabled; printf "%u" ${?})" -eq 0 ]
  281. then
  282. ban_sshdaemon="${util}"
  283. break
  284. fi
  285. fi
  286. done
  287. fi
  288. if [ -z "${ban_sshdaemon}" ]
  289. then
  290. f_log "err" "ssh daemon not found, please install 'dropbear' or 'sshd'"
  291. fi
  292. }
  293. # create temporary files and directories
  294. #
  295. f_temp()
  296. {
  297. if [ -d "/tmp" ] && [ -z "${ban_tmpdir}" ]
  298. then
  299. ban_tmpdir="$(mktemp -p /tmp -d)"
  300. ban_tmpfile="$(mktemp -p "${ban_tmpdir}" -tu)"
  301. elif [ ! -d "/tmp" ]
  302. then
  303. f_log "err" "the temp directory '/tmp' does not exist or has not been mounted yet, please create the directory or raise the 'ban_triggerdelay' to defer the banIP start"
  304. fi
  305. if [ ! -f "${ban_pidfile}" ] || [ ! -s "${ban_pidfile}" ]
  306. then
  307. printf "%s" "${$}" > "${ban_pidfile}"
  308. fi
  309. }
  310. # remove temporary files and directories
  311. #
  312. f_rmtemp()
  313. {
  314. if [ -d "${ban_tmpdir}" ]
  315. then
  316. rm -rf "${ban_tmpdir}"
  317. fi
  318. > "${ban_pidfile}"
  319. }
  320. # remove backup files
  321. #
  322. f_rmbackup()
  323. {
  324. if [ -d "${ban_backupdir}" ]
  325. then
  326. rm -f "${ban_backupdir}"/banIP.*.gz
  327. fi
  328. }
  329. # iptables rules engine
  330. #
  331. f_iptrule()
  332. {
  333. local rc timeout="-w 5" action="${1}" rule="${2}"
  334. if [ "${src_name##*_}" = "6" ]
  335. then
  336. if [ -x "${ban_ipt6}" ]
  337. then
  338. rc="$("${ban_ipt6}" "${timeout}" -C ${rule} 2>/dev/null; printf "%u" ${?})"
  339. if { [ "${rc}" -ne 0 ] && { [ "${action}" = "-A" ] || [ "${action}" = "-I" ]; } } || \
  340. { [ "${rc}" -eq 0 ] && [ "${action}" = "-D" ]; }
  341. then
  342. "${ban_ipt6}" "${timeout}" "${action}" ${rule} 2>/dev/null
  343. fi
  344. fi
  345. else
  346. if [ -x "${ban_ipt}" ]
  347. then
  348. rc="$("${ban_ipt}" "${timeout}" -C ${rule} 2>/dev/null; printf "%u" ${?})"
  349. if { [ "${rc}" -ne 0 ] && { [ "${action}" = "-A" ] || [ "${action}" = "-I" ]; } } || \
  350. { [ "${rc}" -eq 0 ] && [ "${action}" = "-D" ]; }
  351. then
  352. "${ban_ipt}" "${timeout}" "${action}" ${rule} 2>/dev/null
  353. fi
  354. fi
  355. fi
  356. if [ "${?}" -ne 0 ]
  357. then
  358. > "${tmp_err}"
  359. f_log "info" "can't create iptables rule: action: '${action:-"-"}', rule: '${rule:-"-"}'"
  360. fi
  361. }
  362. # remove/add iptables rules
  363. #
  364. f_iptadd()
  365. {
  366. local rm="${1}" dev
  367. for dev in ${ban_dev_all}
  368. do
  369. f_iptrule "-D" "${ban_chain} -i ${dev} -m conntrack --ctstate NEW -m set --match-set ${src_name} src -j ${target_src}"
  370. f_iptrule "-D" "${ban_chain} -o ${dev} -m conntrack --ctstate NEW -m set --match-set ${src_name} dst -j ${target_dst}"
  371. done
  372. if [ -z "${rm}" ] && [ "${cnt}" -gt 0 ]
  373. then
  374. if [ "${src_ruletype}" != "dst" ]
  375. then
  376. f_iptrule "-I" "${wan_input} -j ${ban_chain}"
  377. f_iptrule "-I" "${wan_forward} -j ${ban_chain}"
  378. if [ "${src_name##*_}" != "6" ]
  379. then
  380. # special IPv4 rules
  381. f_iptrule "-A" "${ban_chain} -p udp --dport 67:68 --sport 67:68 -j RETURN"
  382. fi
  383. for dev in ${ban_dev}
  384. do
  385. f_iptrule "${action:-"-A"}" "${ban_chain} -i ${dev} -m conntrack --ctstate NEW -m set --match-set ${src_name} src -j ${target_src}"
  386. done
  387. fi
  388. if [ "${src_ruletype}" != "src" ]
  389. then
  390. f_iptrule "-I" "${lan_input} -j ${ban_chain}"
  391. f_iptrule "-I" "${lan_forward} -j ${ban_chain}"
  392. if [ "${src_name##*_}" != "6" ]
  393. then
  394. # special IPv4 rules
  395. f_iptrule "-A" "${ban_chain} -p udp --dport 67:68 --sport 67:68 -j RETURN"
  396. fi
  397. for dev in ${ban_dev}
  398. do
  399. f_iptrule "${action:-"-A"}" "${ban_chain} -o ${dev} -m conntrack --ctstate NEW -m set --match-set ${src_name} dst -j ${target_dst}"
  400. done
  401. fi
  402. else
  403. if [ -x "${ban_ipset}" ] && [ -n "$("${ban_ipset}" -q -n list "${src_name}")" ]
  404. then
  405. "${ban_ipset}" -q destroy "${src_name}"
  406. fi
  407. fi
  408. }
  409. # ipset/iptables actions
  410. #
  411. f_ipset()
  412. {
  413. local out_rc source action ruleset rule cnt=0 cnt_ip=0 cnt_cidr=0 timeout="-w 5" mode="${1}" in_rc="${src_rc:-0}"
  414. if [ "${src_name%_6*}" = "whitelist" ]
  415. then
  416. target_src="RETURN"
  417. target_dst="RETURN"
  418. action="-I"
  419. fi
  420. case "${mode}" in
  421. "backup")
  422. gzip -cf "${tmp_load}" 2>/dev/null > "${ban_backupdir}/banIP.${src_name}.gz"
  423. out_rc="${?:-"${in_rc}"}"
  424. f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, out_rc: ${out_rc}"
  425. return "${out_rc}"
  426. ;;
  427. "restore")
  428. if [ -f "${ban_backupdir}/banIP.${src_name}.gz" ]
  429. then
  430. zcat "${ban_backupdir}/banIP.${src_name}.gz" 2>/dev/null > "${tmp_load}"
  431. out_rc="${?}"
  432. fi
  433. out_rc="${out_rc:-"${in_rc}"}"
  434. f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, out_rc: ${out_rc}"
  435. return "${out_rc}"
  436. ;;
  437. "remove")
  438. if [ -f "${ban_backupdir}/banIP.${src_name}.gz" ]
  439. then
  440. rm -f "${ban_backupdir}/banIP.${src_name}.gz"
  441. out_rc="${?}"
  442. fi
  443. out_rc="${out_rc:-"${in_rc}"}"
  444. f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, out_rc: ${out_rc}"
  445. return "${out_rc}"
  446. ;;
  447. "initial")
  448. local ipt log_src_target log_src_opts log_src_prefix log_dst_target log_dst_opts log_dst_prefix
  449. for src_name in "ruleset" "ruleset_6"
  450. do
  451. if [ "${src_name##*_}" = "6" ]
  452. then
  453. ipt="${ban_ipt6}"
  454. ruleset="${ban_wan_input_chain_6:-"input_wan_rule"} ${ban_wan_forward_chain_6:-"forwarding_wan_rule"} ${ban_lan_input_chain_6:-"input_lan_rule"} ${ban_lan_forward_chain_6:-"forwarding_lan_rule"}"
  455. log_src_target="${log_target_src_6}"
  456. log_src_opts="${ban_log_src_opts_6:-"-m limit --limit 10/sec"}"
  457. log_src_prefix="${ban_log_src_prefix_6:-"${log_target_src_6}(src banIP) "}"
  458. log_dst_target="${log_target_dst_6}"
  459. log_dst_opts="${ban_log_dst_opts_6:-"-m limit --limit 10/sec"}"
  460. log_dst_prefix="${ban_log_dst_prefix_6:-"${log_target_dst_6}(dst banIP) "}"
  461. else
  462. ipt="${ban_ipt}"
  463. ruleset="${ban_wan_input_chain:-"input_wan_rule"} ${ban_wan_forward_chain:-"forwarding_wan_rule"} ${ban_lan_input_chain:-"input_lan_rule"} ${ban_lan_forward_chain:-"forwarding_lan_rule"}"
  464. log_src_target="${log_target_src}"
  465. log_src_opts="${ban_log_src_opts:-"-m limit --limit 10/sec"}"
  466. log_src_prefix="${ban_log_src_prefix:-"${log_target_src}(src banIP) "}"
  467. log_dst_target="${log_target_dst}"
  468. log_dst_opts="${ban_log_dst_opts:-"-m limit --limit 10/sec"}"
  469. log_dst_prefix="${ban_log_dst_prefix:-"${log_target_dst}(dst banIP) "}"
  470. fi
  471. if [ -x "${ipt}" ]
  472. then
  473. if [ -z "$("${ipt}" "${timeout}" -nL "${ban_chain}" 2>/dev/null)" ]
  474. then
  475. "${ipt}" "${timeout}" -N "${ban_chain}" 2>/dev/null
  476. out_rc="${?}"
  477. else
  478. out_rc=0
  479. for rule in ${ruleset}
  480. do
  481. f_iptrule "-D" "${rule} -j ${ban_chain}"
  482. done
  483. fi
  484. f_log "debug" "f_ipset ::: name: -, mode: ${mode:-"-"}, chain: ${ban_chain:-"-"}, $src_name: ${ruleset:-"-"}, out_rc: ${out_rc}"
  485. if [ "${ban_log_src}" -eq 1 ] && [ "${out_rc}" -eq 0 ]
  486. then
  487. if [ -z "$("${ipt}" "${timeout}" -nL "${ban_log_chain_src}" 2>/dev/null)" ]
  488. then
  489. "${ipt}" "${timeout}" -N "${ban_log_chain_src}" 2>/dev/null
  490. out_rc="${?}"
  491. if [ "${out_rc}" -eq 0 ]
  492. then
  493. "${ipt}" "${timeout}" -A "${ban_log_chain_src}" -j LOG ${log_src_opts} --log-prefix "${log_src_prefix}" && \
  494. "${ipt}" "${timeout}" -A "${ban_log_chain_src}" -j "${log_src_target}"
  495. out_rc="${?}"
  496. fi
  497. fi
  498. f_log "debug" "f_ipset ::: name: -, mode: ${mode:-"-"}, chain: ${ban_log_chain_src:-"-"}, out_rc: ${out_rc}"
  499. fi
  500. if [ "${ban_log_dst}" -eq 1 ] && [ "${out_rc}" -eq 0 ]
  501. then
  502. if [ -z "$("${ipt}" "${timeout}" -nL "${ban_log_chain_dst}" 2>/dev/null)" ]
  503. then
  504. "${ipt}" "${timeout}" -N "${ban_log_chain_dst}" 2>/dev/null
  505. out_rc="${?}"
  506. if [ "${out_rc}" -eq 0 ]
  507. then
  508. "${ipt}" "${timeout}" -A "${ban_log_chain_dst}" -j LOG ${log_dst_opts} --log-prefix "${log_dst_prefix}" && \
  509. "${ipt}" "${timeout}" -A "${ban_log_chain_dst}" -j "${log_dst_target}"
  510. out_rc="${?}"
  511. fi
  512. fi
  513. f_log "debug" "f_ipset ::: name: -, mode: ${mode:-"-"}, chain: ${ban_log_chain_dst:-"-"}, out_rc: ${out_rc}"
  514. fi
  515. fi
  516. done
  517. out_rc="${out_rc:-"${in_rc}"}"
  518. f_log "debug" "f_ipset ::: name: -, mode: ${mode:-"-"}, out_rc: ${out_rc}"
  519. return "${out_rc}"
  520. ;;
  521. "create")
  522. if [ -x "${ban_ipset}" ]
  523. then
  524. if [ -s "${tmp_file}" ] && [ -z "$("${ban_ipset}" -q -n list "${src_name}")" ]
  525. then
  526. "${ban_ipset}" -q create "${src_name}" hash:"${src_settype}" hashsize 64 maxelem 262144 family "${src_setipv}" counters
  527. out_rc="${?}"
  528. else
  529. "${ban_ipset}" -q flush "${src_name}"
  530. out_rc="${?}"
  531. fi
  532. if [ -s "${tmp_file}" ] && [ "${out_rc}" -eq 0 ]
  533. then
  534. "${ban_ipset}" -q -! restore < "${tmp_file}"
  535. out_rc="${?}"
  536. if [ "${out_rc}" -eq 0 ]
  537. then
  538. "${ban_ipset}" -q save "${src_name}" > "${tmp_file}"
  539. cnt="$(($(wc -l 2>/dev/null < "${tmp_file}")-1))"
  540. cnt_cidr="$(grep -cF "/" "${tmp_file}")"
  541. cnt_ip="$((cnt-cnt_cidr))"
  542. printf "%s\\n" "${cnt}" > "${tmp_cnt}"
  543. fi
  544. fi
  545. f_iptadd
  546. fi
  547. end_ts="$(date +%s)"
  548. out_rc="${out_rc:-"${in_rc}"}"
  549. f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, settype: ${src_settype:-"-"}, setipv: ${src_setipv:-"-"}, ruletype: ${src_ruletype:-"-"}, count(sum/ip/cidr): ${cnt}/${cnt_ip}/${cnt_cidr}, time: $((end_ts-start_ts)), out_rc: ${out_rc}"
  550. return "${out_rc}"
  551. ;;
  552. "refresh")
  553. if [ -x "${ban_ipset}" ] && [ -n "$("${ban_ipset}" -q -n list "${src_name}")" ]
  554. then
  555. "${ban_ipset}" -q save "${src_name}" > "${tmp_file}"
  556. out_rc="${?}"
  557. if [ -s "${tmp_file}" ] && [ "${out_rc}" -eq 0 ]
  558. then
  559. cnt="$(($(wc -l 2>/dev/null < "${tmp_file}")-1))"
  560. cnt_cidr="$(grep -cF "/" "${tmp_file}")"
  561. cnt_ip="$((cnt-cnt_cidr))"
  562. printf "%s\\n" "${cnt}" > "${tmp_cnt}"
  563. fi
  564. f_iptadd
  565. fi
  566. end_ts="$(date +%s)"
  567. out_rc="${out_rc:-"${in_rc}"}"
  568. f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, count: ${cnt}/${cnt_ip}/${cnt_cidr}, time: $((end_ts-start_ts)), out_rc: ${out_rc}"
  569. return "${out_rc}"
  570. ;;
  571. "flush")
  572. f_iptadd "remove"
  573. if [ -x "${ban_ipset}" ] && [ -n "$("${ban_ipset}" -q -n list "${src_name}")" ]
  574. then
  575. "${ban_ipset}" -q flush "${src_name}"
  576. "${ban_ipset}" -q destroy "${src_name}"
  577. fi
  578. f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}"
  579. ;;
  580. "destroy")
  581. for chain in ${ban_log_chain_src} ${ban_log_chain_dst} ${ban_chain}
  582. do
  583. if [ -x "${ban_ipt}" ] && [ -x "${ban_ipt_save}" ] && [ -x "${ban_ipt_restore}" ] && \
  584. [ -n "$("${ban_ipt}" "${timeout}" -nL "${chain}" 2>/dev/null)" ]
  585. then
  586. "${ban_ipt_save}" | grep -v -- "-j ${chain}" | "${ban_ipt_restore}"
  587. "${ban_ipt}" "${timeout}" -F "${chain}" 2>/dev/null
  588. "${ban_ipt}" "${timeout}" -X "${chain}" 2>/dev/null
  589. fi
  590. if [ -x "${ban_ipt6}" ] && [ -x "${ban_ipt6_save}" ] && [ -x "${ban_ipt6_restore}" ] && \
  591. [ -n "$("${ban_ipt6}" "${timeout}" -nL "${chain}" 2>/dev/null)" ]
  592. then
  593. "${ban_ipt6_save}" | grep -v -- "-j ${chain}" | "${ban_ipt6_restore}"
  594. "${ban_ipt6}" "${timeout}" -F "${chain}" 2>/dev/null
  595. "${ban_ipt6}" "${timeout}" -X "${chain}" 2>/dev/null
  596. fi
  597. done
  598. for source in ${ban_sources}
  599. do
  600. if [ -x "${ban_ipset}" ] && [ -n "$("${ban_ipset}" -q -n list "${source}")" ]
  601. then
  602. "${ban_ipset}" -q destroy "${source}"
  603. fi
  604. done
  605. f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}"
  606. ;;
  607. esac
  608. }
  609. # write to syslog
  610. #
  611. f_log()
  612. {
  613. local class="${1}" log_msg="${2}"
  614. if [ -n "${log_msg}" ] && { [ "${class}" != "debug" ] || [ "${ban_debug}" -eq 1 ]; }
  615. then
  616. if [ -x "${ban_logger}" ]
  617. then
  618. "${ban_logger}" -p "${class}" -t "banIP-${ban_ver}[${$}]" "${log_msg}"
  619. else
  620. printf "%s %s %s\\n" "${class}" "banIP-${ban_ver}[${$}]" "${log_msg}"
  621. fi
  622. if [ "${class}" = "err" ]
  623. then
  624. f_jsnup error
  625. f_ipset destroy
  626. f_rmbackup
  627. f_rmtemp
  628. exit 1
  629. fi
  630. fi
  631. }
  632. # start log service to trace failed ssh/luci logins
  633. #
  634. f_bgserv()
  635. {
  636. local bg_pid status="${1}"
  637. bg_pid="$(pgrep -f "^/bin/sh ${ban_logservice}.*|^logread -f -e ${ban_sshdaemon}\|luci: failed login|^grep -qE Exit before auth|luci: failed login|[0-9]+ \[preauth\]$" | awk '{ORS=" "; print $1}')"
  638. if [ -z "${bg_pid}" ] && [ "${status}" = "start" ] \
  639. && [ -x "${ban_logservice}" ] && [ "${ban_realtime}" = "true" ]
  640. then
  641. ( "${ban_logservice}" "${ban_ver}" "${ban_sshdaemon}" & )
  642. elif [ -n "${bg_pid}" ] && [ "${status}" = "stop" ]
  643. then
  644. kill -HUP "${bg_pid}" 2>/dev/null
  645. fi
  646. f_log "debug" "f_bgserv ::: status: ${status:-"-"}, bg_pid: ${bg_pid:-"-"}, ban_realtime: ${ban_realtime:-"-"}, log_service: ${ban_logservice:-"-"}"
  647. }
  648. # main function for banIP processing
  649. #
  650. f_main()
  651. {
  652. local pid pid_list start_ts end_ts ip tmp_raw tmp_cnt tmp_load tmp_file mem_total mem_free cnt=1
  653. local src_name src_on src_url src_rset src_setipv src_settype src_ruletype src_cat src_log src_addon src_ts src_rc
  654. local wan_input wan_forward lan_input lan_forward target_src target_dst ssh_log luci_log
  655. if [ "${ban_sshdaemon}" = "dropbear" ]
  656. then
  657. ssh_log="$(logread -e "${ban_sshdaemon}" | grep -o "${ban_sshdaemon}.*" | sed 's/:[0-9]*$//g')"
  658. elif [ "${ban_sshdaemon}" = "sshd" ]
  659. then
  660. ssh_log="$(logread -e "${ban_sshdaemon}" | grep -o "${ban_sshdaemon}.*" | sed 's/ port.*$//g')"
  661. fi
  662. luci_log="$(logread -e "luci: failed login" | grep -o "luci:.*")"
  663. mem_total="$(awk '/^MemTotal/ {print int($2/1000)}' "/proc/meminfo" 2>/dev/null)"
  664. mem_free="$(awk '/^MemFree/ {print int($2/1000)}' "/proc/meminfo" 2>/dev/null)"
  665. f_log "debug" "f_main ::: fetch_util: ${ban_fetchutil:-"-"}, fetch_parm: ${ban_fetchparm:-"-"}, ssh_daemon: ${ban_sshdaemon}, interface(s): ${ban_iface:-"-"}, device(s): ${ban_dev:-"-"}, all_devices: ${ban_dev_all:-"-"}, backup_dir: ${ban_backupdir:-"-"}, mem_total: ${mem_total:-0}, mem_free: ${mem_free:-0}, max_queue: ${ban_maxqueue}"
  666. # chain creation
  667. #
  668. f_ipset initial
  669. if [ "${?}" -ne 0 ]
  670. then
  671. f_log "err" "banIP processing failed, fatal error during iptables chain creation (${ban_sysver})"
  672. fi
  673. # main loop
  674. #
  675. for src_name in ${ban_sources}
  676. do
  677. unset src_on
  678. if [ "${src_name##*_}" = "6" ]
  679. then
  680. if [ -x "${ban_ipt6}" ]
  681. then
  682. src_on="$(eval printf "%s" \"\$\{ban_src_on_6_${src_name%_6*}\}\")"
  683. src_url="$(eval printf "%s" \"\$\{ban_src_6_${src_name%_6*}\}\")"
  684. src_rset="$(eval printf "%s" \"\$\{ban_src_rset_6_${src_name%_6*}\}\")"
  685. src_setipv="inet6"
  686. wan_input="${ban_wan_input_chain_6:-"input_wan_rule"}"
  687. wan_forward="${ban_wan_forward_chain_6:-"forwarding_wan_rule"}"
  688. lan_input="${ban_lan_input_chain_6:-"input_lan_rule"}"
  689. lan_forward="${ban_lan_forward_chain_6:-"forwarding_lan_rule"}"
  690. target_src="${ban_target_src_6:-"DROP"}"
  691. target_dst="${ban_target_dst_6:-"REJECT"}"
  692. fi
  693. else
  694. if [ -x "${ban_ipt}" ]
  695. then
  696. src_on="$(eval printf "%s" \"\$\{ban_src_on_${src_name}\}\")"
  697. src_url="$(eval printf "%s" \"\$\{ban_src_${src_name}\}\")"
  698. src_rset="$(eval printf "%s" \"\$\{ban_src_rset_${src_name}\}\")"
  699. src_setipv="inet"
  700. wan_input="${ban_wan_input_chain:-"input_wan_rule"}"
  701. wan_forward="${ban_wan_forward_chain:-"forwarding_wan_rule"}"
  702. lan_input="${ban_lan_input_chain:-"input_lan_rule"}"
  703. lan_forward="${ban_lan_forward_chain:-"forwarding_lan_rule"}"
  704. target_src="${ban_target_src:-"DROP"}"
  705. target_dst="${ban_target_dst:-"REJECT"}"
  706. fi
  707. fi
  708. src_settype="$(eval printf "%s" \"\$\{ban_src_settype_${src_name%_6*}\}\")"
  709. src_ruletype="$(eval printf "%s" \"\$\{ban_src_ruletype_${src_name%_6*}\}\")"
  710. src_cat="$(eval printf "%s" \"\$\{ban_src_cat_${src_name%_6*}\}\")"
  711. src_addon=""
  712. src_rc=4
  713. tmp_load="${ban_tmpfile}.${src_name}.load"
  714. tmp_file="${ban_tmpfile}.${src_name}.file"
  715. tmp_raw="${tmp_file}.raw"
  716. tmp_cnt="${tmp_file}.cnt"
  717. tmp_err="${tmp_file}.err"
  718. # basic pre-checks
  719. #
  720. f_log "debug" "f_main ::: name: ${src_name}, src_on: ${src_on:-"-"}"
  721. if [ -z "${src_on}" ] || [ "${src_on}" != "1" ] || [ -z "${src_url}" ] || \
  722. [ -z "${src_rset}" ] || [ -z "${src_settype}" ] || [ -z "${src_ruletype}" ]
  723. then
  724. f_ipset flush
  725. f_ipset remove
  726. continue
  727. elif [ "${ban_action}" = "refresh" ] && [ ! -f "${src_url}" ]
  728. then
  729. start_ts="$(date +%s)"
  730. f_ipset refresh
  731. if [ "${?}" -eq 0 ]
  732. then
  733. continue
  734. fi
  735. fi
  736. # download queue processing
  737. #
  738. (
  739. start_ts="$(date +%s)"
  740. if [ "${ban_action}" = "start" ] && [ ! -f "${src_url}" ]
  741. then
  742. f_ipset restore
  743. fi
  744. src_rc="${?}"
  745. if [ "${src_rc}" -ne 0 ] || [ ! -s "${tmp_load}" ]
  746. then
  747. if [ -f "${src_url}" ]
  748. then
  749. src_log="$(cat "${src_url}" 2>/dev/null > "${tmp_load}")"
  750. src_rc="${?}"
  751. case "${src_name}" in
  752. "whitelist")
  753. src_addon="${ban_subnets}"
  754. ;;
  755. "whitelist_6")
  756. src_addon="${ban_subnets6}"
  757. ;;
  758. "blacklist")
  759. if [ "${ban_sshdaemon}" = "dropbear" ]
  760. then
  761. pid_list="$(printf "%s\\n" "${ssh_log}" | grep -F "Exit before auth" | awk 'match($0,/(\[[0-9]+\])/){ORS=" ";print substr($0,RSTART,RLENGTH)}')"
  762. for pid in ${pid_list}
  763. do
  764. src_addon="${src_addon} $(printf "%s\\n" "${ssh_log}" | grep -F "${pid}" | awk 'match($0,/([0-9]{1,3}\.){3}[0-9]{1,3}$/){ORS=" ";print substr($0,RSTART,RLENGTH);exit}')"
  765. done
  766. elif [ "${ban_sshdaemon}" = "sshd" ]
  767. then
  768. src_addon="$(printf "%s\\n" "${ssh_log}" | grep -F "error: maximum authentication attempts exceeded" | awk 'match($0,/([0-9]{1,3}\.){3}[0-9]{1,3}$/){ORS=" ";print substr($0,RSTART,RLENGTH)}')"
  769. fi
  770. src_addon="${src_addon} $(printf "%s\\n" "${luci_log}" | awk 'match($0,/([0-9]{1,3}\.){3}[0-9]{1,3}$/){ORS=" ";print substr($0,RSTART,RLENGTH)}')"
  771. ;;
  772. "blacklist_6")
  773. if [ "${ban_sshdaemon}" = "dropbear" ]
  774. then
  775. pid_list="$(printf "%s\\n" "${ssh_log}" | grep -F "Exit before auth" | awk 'match($0,/(\[[0-9]+\])/){ORS=" ";print substr($0,RSTART,RLENGTH)}')"
  776. for pid in ${pid_list}
  777. do
  778. src_addon="${src_addon} $(printf "%s\\n" "${ssh_log}" | grep -F "${pid}" | awk 'match($0,/(([0-9A-f]{0,4}::?){1,7}[0-9A-f]{0,4}$)/){ORS=" ";print substr($0,RSTART,RLENGTH);exit}')"
  779. done
  780. elif [ "${ban_sshdaemon}" = "sshd" ]
  781. then
  782. src_addon="$(printf "%s\\n" "${ssh_log}" | grep -F "error: maximum authentication attempts exceeded" | awk 'match($0,/(([0-9A-f]{0,4}::?){1,7}[0-9A-f]{0,4}$)/){ORS=" ";print substr($0,RSTART,RLENGTH)}')"
  783. fi
  784. src_addon="${src_addon} $(printf "%s\\n" "${luci_log}" | awk 'match($0,/(([0-9A-f]{0,4}::?){1,7}[0-9A-f]{0,4}$)/){ORS=" ";print substr($0,RSTART,RLENGTH)}')"
  785. ;;
  786. esac
  787. for ip in ${src_addon}
  788. do
  789. if [ -z "$(grep -F "${ip}" "${src_url}")" ]
  790. then
  791. printf "%s\\n" "${ip}" >> "${tmp_load}"
  792. if { [ "${src_name//_*/}" = "blacklist" ] && [ "${ban_autoblacklist}" -eq 1 ]; } || \
  793. { [ "${src_name//_*/}" = "whitelist" ] && [ "${ban_autowhitelist}" -eq 1 ]; }
  794. then
  795. src_ts="# auto-added $(date "+%d.%m.%Y %H:%M:%S")"
  796. printf "%s %s\\n" "${ip}" "${src_ts}" >> "${src_url}"
  797. fi
  798. fi
  799. done
  800. elif [ -n "${src_cat}" ]
  801. then
  802. if [ "${src_cat//[0-9]/}" != "${src_cat}" ]
  803. then
  804. for as in ${src_cat}
  805. do
  806. src_log="$("${ban_fetchutil}" ${ban_fetchparm} "${tmp_raw}" "${src_url}AS${as}" 2>&1)"
  807. src_rc="${?}"
  808. if [ "${src_rc}" -eq 0 ]
  809. then
  810. jsonfilter -i "${tmp_raw}" -e '@.data.prefixes.*.prefix' 2>/dev/null >> "${tmp_load}"
  811. else
  812. break
  813. fi
  814. done
  815. if [ "${src_rc}" -eq 0 ]
  816. then
  817. f_ipset backup
  818. elif [ "${ban_action}" != "start" ]
  819. then
  820. f_ipset restore
  821. fi
  822. else
  823. for co in ${src_cat}
  824. do
  825. src_log="$("${ban_fetchutil}" ${ban_fetchparm} "${tmp_raw}" "${src_url}${co}&v4_format=prefix" 2>&1)"
  826. src_rc="${?}"
  827. if [ "${src_rc}" -eq 0 ]
  828. then
  829. if [ "${src_name##*_}" = "6" ]
  830. then
  831. jsonfilter -i "${tmp_raw}" -e '@.data.resources.ipv6.*' 2>/dev/null >> "${tmp_load}"
  832. else
  833. jsonfilter -i "${tmp_raw}" -e '@.data.resources.ipv4.*' 2>/dev/null >> "${tmp_load}"
  834. fi
  835. else
  836. break
  837. fi
  838. done
  839. if [ "${src_rc}" -eq 0 ]
  840. then
  841. f_ipset backup
  842. elif [ "${ban_action}" != "start" ]
  843. then
  844. f_ipset restore
  845. fi
  846. fi
  847. else
  848. src_log="$("${ban_fetchutil}" ${ban_fetchparm} "${tmp_raw}" "${src_url}" 2>&1)"
  849. src_rc="${?}"
  850. if [ "${src_rc}" -eq 0 ]
  851. then
  852. zcat "${tmp_raw}" 2>/dev/null > "${tmp_load}"
  853. src_rc="${?}"
  854. if [ "${src_rc}" -ne 0 ]
  855. then
  856. mv -f "${tmp_raw}" "${tmp_load}"
  857. src_rc="${?}"
  858. fi
  859. if [ "${src_rc}" -eq 0 ]
  860. then
  861. f_ipset backup
  862. src_rc="${?}"
  863. fi
  864. elif [ "${ban_action}" != "start" ]
  865. then
  866. f_ipset restore
  867. src_rc="${?}"
  868. fi
  869. fi
  870. fi
  871. if [ "${src_rc}" -eq 0 ]
  872. then
  873. awk "${src_rset}" "${tmp_load}" 2>/dev/null > "${tmp_file}"
  874. src_rc="${?}"
  875. if [ "${src_rc}" -eq 0 ]
  876. then
  877. f_ipset create
  878. src_rc="${?}"
  879. elif [ "${ban_action}" != "refresh" ]
  880. then
  881. f_ipset refresh
  882. src_rc="${?}"
  883. fi
  884. else
  885. src_log="$(printf "%s" "${src_log}" | awk '{ORS=" ";print $0}')"
  886. if [ "${ban_action}" != "refresh" ]
  887. then
  888. f_ipset refresh
  889. src_rc="${?}"
  890. fi
  891. f_log "debug" "f_main ::: name: ${src_name}, url: ${src_url}, rc: ${src_rc}, log: ${src_log:-"-"}"
  892. fi
  893. )&
  894. hold="$((cnt%ban_maxqueue))"
  895. if [ "${hold}" -eq 0 ]
  896. then
  897. wait
  898. fi
  899. cnt="$((cnt+1))"
  900. done
  901. wait
  902. if [ -z "$(ls "${ban_tmpfile}".*.err 2>/dev/null)" ]
  903. then
  904. for cnt_file in "${ban_tmpfile}".*.cnt
  905. do
  906. if [ -f "$cnt_file" ]
  907. then
  908. read -r cnt < "$cnt_file"
  909. ban_cnt="$((ban_cnt+cnt))"
  910. ban_setcnt="$((ban_setcnt+1))"
  911. fi
  912. done
  913. f_log "info" "${ban_setcnt} IPSets with overall ${ban_cnt} IPs/Prefixes loaded successfully (${ban_sysver})"
  914. f_bgserv "start"
  915. f_jsnup
  916. f_rmtemp
  917. else
  918. f_log "err" "banIP processing failed, fatal iptables error(s) during subshell processing (${ban_sysver})"
  919. fi
  920. }
  921. # update runtime information
  922. #
  923. f_jsnup()
  924. {
  925. local rundate status="${1:-"enabled"}"
  926. rundate="$(date "+%d.%m.%Y %H:%M:%S")"
  927. ban_cntinfo="${ban_setcnt} IPSets with overall ${ban_cnt} IPs/Prefixes"
  928. > "${ban_rtfile}"
  929. json_load_file "${ban_rtfile}" >/dev/null 2>&1
  930. json_init
  931. json_add_object "data"
  932. json_add_string "status" "${status}"
  933. json_add_string "version" "${ban_ver}"
  934. json_add_string "util_info" "${ban_fetchutil:-"-"}, ${ban_realtime:-"-"}"
  935. json_add_string "ipset_info" "${ban_cntinfo:-"-"}"
  936. json_add_string "backup_dir" "${ban_backupdir}"
  937. json_add_string "last_run" "${rundate:-"-"}"
  938. json_add_string "system" "${ban_sysver}"
  939. json_close_object
  940. json_dump > "${ban_rtfile}"
  941. f_log "debug" "f_jsnup ::: status: ${status}, setcnt: ${ban_setcnt}, cnt: ${ban_cnt}"
  942. }
  943. # source required system libraries
  944. #
  945. if [ -r "/lib/functions.sh" ] && [ -r "/lib/functions/network.sh" ] && [ -r "/usr/share/libubox/jshn.sh" ]
  946. then
  947. . "/lib/functions.sh"
  948. . "/lib/functions/network.sh"
  949. . "/usr/share/libubox/jshn.sh"
  950. else
  951. f_log "err" "system libraries not found"
  952. fi
  953. # handle different banIP actions
  954. #
  955. f_envload
  956. case "${ban_action}" in
  957. "stop")
  958. f_bgserv "stop"
  959. f_jsnup stopped
  960. f_ipset destroy
  961. f_rmbackup
  962. f_rmtemp
  963. ;;
  964. "start"|"restart"|"reload"|"refresh")
  965. f_bgserv "stop"
  966. f_envcheck
  967. f_main
  968. ;;
  969. esac