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.

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