banip: update 0.2.0
authorDirk Brenken <redacted>
Mon, 9 Sep 2019 15:12:52 +0000 (17:12 +0200)
committerDirk Brenken <redacted>
Mon, 9 Sep 2019 19:11:10 +0000 (21:11 +0200)
* remove 'http-only' mode, all sources are now fetched from https sites
* the backup mode is now mandatory ('/tmp' is the default backup
  directory), always create and re-use backups if available.
  To force a re-download take the 'reload' action.
* support 'sshd' in addition to 'dropbear' for logfile parsing
  to detect break-in events
* always update the black-/whitelist with logfile parsing results
  in 'refresh' mode (no new downloads)
* rework the return code handling
* tweak procd trigger
* various small fixes
* (s)hellsheck cosmetics

Signed-off-by: Dirk Brenken <redacted>
net/banip/Makefile
net/banip/files/README.md
net/banip/files/banip.conf
net/banip/files/banip.hotplug
net/banip/files/banip.init
net/banip/files/banip.sh

index 1ad420738f066810d966b5679055c0da0784e092..591f45b80726512db55ca4ca33f0b9ce3f2dbe01 100644 (file)
@@ -6,7 +6,7 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=banip
-PKG_VERSION:=0.1.5
+PKG_VERSION:=0.2.0
 PKG_RELEASE:=1
 PKG_LICENSE:=GPL-3.0+
 PKG_MAINTAINER:=Dirk Brenken <dev@brenken.org>
index dfa6c7d7e7c2c16316ada9da48d4340263d71173..3147349de16b39a089e6fd20f9d35e761a6ba7a9 100644 (file)
@@ -6,39 +6,36 @@ IP address blocking is commonly used to protect against brute force attacks, pre
 ## Main Features
 * support many IP blocklist sources (free for private usage, for commercial use please check their individual licenses):
 * zero-conf like automatic installation & setup, usually no manual changes needed
-* supports six different download utilities: uclient-fetch, wget, curl, aria2c, wget-nossl, busybox-wget
+* supports four different download utilities: uclient-fetch, wget, curl, aria2c
 * Really fast downloads & list processing as they are handled in parallel as background jobs in a configurable 'Download Queue'
-* provides 'http only' mode without installed ssl library for all non-SSL blocklist sources
 * full IPv4 and IPv6 support
 * ipsets (one per source) are used to ban a large number of IP addresses
 * supports blocking by ASN numbers
 * supports blocking by iso country codes
 * supports local white & blacklist (IPv4, IPv6 & CIDR notation), located by default in /etc/banip/banip.whitelist and /etc/banip/banip.blacklist
-* auto-add unsuccessful ssh login attempts to local blacklist (see 'ban_autoblacklist' option)
+* auto-add unsuccessful ssh login attempts to 'dropbear' or 'sshd' to local blacklist (see 'ban_autoblacklist' option)
 * auto-add the uplink subnet to local whitelist (see 'ban_autowhitelist' option)
 * per source configuration of SRC (incoming) and DST (outgoing)
 * integrated IPSet-Lookup
 * integrated RIPE-Lookup
 * blocklist source parsing by fast & flexible regex rulesets
 * minimal status & error logging to syslog, enable debug logging to receive more output
-* procd based init system support (start/stop/restart/reload/status)
+* procd based init system support (start/stop/restart/reload/refresh/status)
 * procd network interface trigger support
-* automatic blocklist backup & restore, they will be used in case of download errors or during startup in backup mode
-* 'backup mode' to re-use blocklist backups during startup, get fresh lists via reload or restart action
+* automatic blocklist backup & restore, they will be used in case of download errors or during startup
 * output comprehensive runtime information via LuCI or via 'status' init command
 * strong LuCI support
 * optional: add new banIP sources on your own
 
 ## Prerequisites
-* [OpenWrt](https://openwrt.org), tested with the stable release series (18.06) and with the latest snapshot
+* [OpenWrt](https://openwrt.org), tested with the stable release series (19.07) and with the latest snapshot
 * a download utility:
-    * to support all blocklist sources a full version (with ssl support) of 'wget', 'uclient-fetch' with one of the 'libustream-*' ssl libraries, 'aria2c' or 'curl' is required
-    * for limited devices with real memory constraints, banIP provides also a 'http only' option and supports wget-nossl and uclient-fetch (without libustream-ssl) as well
+    * to support all blocklist sources a full version with ssl support of 'wget', 'uclient-fetch' with one of the 'libustream-*' ssl libraries, 'aria2c' or 'curl' is required
 
 ## Installation & Usage
 * install 'banip' (_opkg install banip_)
 * at minimum configure the needed IP blocklist sources, the download utility and enable the banIP service in _/etc/config/banip_
-* control the banip service manually with _/etc/init.d/banip_ start/stop/restart/reload/status or use the LuCI frontend
+* control the banip service manually with _/etc/init.d/banip_ start/stop/restart/reload/refresh/status or use the LuCI frontend
 
 ## LuCI banIP companion package
 * it's recommended to use the provided LuCI frontend to control all aspects of banIP
@@ -57,10 +54,10 @@ IP address blocking is commonly used to protect against brute force attacks, pre
     * ban\_debug => enable/disable banIP debug output (bool/default: '0', disabled)
     * ban\_nice => set the nice level of the banIP process and all sub-processes (int/default: '0', standard priority)
     * ban\_triggerdelay => additional trigger delay in seconds before banIP processing begins (int/default: '2')
-    * ban\_backup => create compressed blocklist backups, they will be used in case of download errors or during startup in 'backup mode' (bool/default: '0', disabled)
-    * ban\_backupdir => target directory for adblock backups (default: not set)
-    * ban\_backupboot => do not automatically update blocklists during startup, use their backups instead (bool/default: '0', disabled)
-    * ban\_maxqueue => size of the download queue to handle downloads & IPSet processing in parallel (int/default: '8')
+    * ban\_backupdir => target directory for banIP backups (default: '/tmp')
+    * ban\_sshdaemon => select the SSH daemon for logfile parsing, 'dropbear' or 'sshd' (default: 'dropbear')
+    * ban\_starttype => select the used start type during boot, 'start' or 'reload' (default: 'start')
+    * ban\_maxqueue => size of the download queue to handle downloads & IPSet processing in parallel (int/default: '4')
     * ban\_fetchparm => special config options for the download utility (default: not set)
     * ban\_autoblacklist => store auto-addons temporary in ipset and permanently in local blacklist as well (bool/default: '1', enabled)
     * ban\_autowhitelist => store auto-addons temporary in ipset and permanently in local whitelist as well (bool/default: '1', enabled)
@@ -72,14 +69,15 @@ IP address blocking is commonly used to protect against brute force attacks, pre
 /etc/init.d/banip status
 ::: banIP runtime information
   + status     : enabled
-  + version    : 0.1.0
+  + version    : 0.2.0
   + fetch_info : /bin/uclient-fetch (libustream-ssl)
-  + ipset_info : 1 IPSets with overall 516 IPs/Prefixes (backup mode)
-  + last_run   : 05.01.2019 14:48:18
-  + system     : TP-LINK RE450, OpenWrt SNAPSHOT r8910+72-25d8aa7d02
+  + ipset_info : 11 IPSets with overall 118359 IPs/Prefixes
+  + backup_dir : /tmp
+  + last_run   : 09.09.2019 16:49:40
+  + system     : UBNT-ERX, OpenWrt SNAPSHOT r10962-c19b9f9a26
 </code></pre>
   
-**cronjob for a regular block list update (/etc/crontabs/root):**
+**cronjob for a regular IPSet blocklist update (/etc/crontabs/root):**
 
 <pre><code>
 0 06 * * *    /etc/init.d/banip reload
index 08053a2fe6887f0092cabc49ebf144bf4d1b77a1..f0565c91a712a0e93be0cd023a32ec73f0cca343 100644 (file)
@@ -7,7 +7,6 @@ config banip 'global'
 
 config banip 'extra'
        option ban_debug '0'
-       option ban_backup '0'
        option ban_maxqueue '4'
 
 config source 'whitelist'
@@ -83,7 +82,7 @@ config source 'myip'
        option ban_src_on_6 '0'
 
 config source 'yoyo'
-       option ban_src 'http://pgl.yoyo.org/adservers/iplist.php?ipformat=plain&showintro=0&mimetype=plaintext'
+       option ban_src 'https://pgl.yoyo.org/adservers/iplist.php?ipformat=plain&showintro=0&mimetype=plaintext'
        option ban_src_desc 'IP blocklist provided by Peter Lowe (IPv4)'
        option ban_src_rset '/^(([0-9]{1,3}\.){3}[0-9]{1,3})([[:space:]]|$)/{print \"add yoyo \"\$1}'
        option ban_src_settype 'ip'
@@ -115,7 +114,7 @@ config source 'feodo'
        option ban_src_on '0'
 
 config source 'dshield'
-       option ban_src 'http://feeds.dshield.org/block.txt'
+       option ban_src 'https://feeds.dshield.org/block.txt'
        option ban_src_desc 'Dshield recommended IP blocklist. Contains top 20 attacking class C subnets (IPv4)'
        option ban_src_rset '/^(([0-9]{1,3}\.){3}[0-9]{1,3})([[:space:]]|$)/{print \"add dshield \"\$1 \"/\"\$3}'
        option ban_src_settype 'net'
@@ -131,7 +130,7 @@ config source 'proxy'
        option ban_src_on '0'
 
 config source 'iblocklist'
-       option ban_src 'http://list.iblocklist.com/?list=dgxtneitpuvgqqcpfulq&fileformat=cidr&archiveformat=gz'
+       option ban_src 'https://list.iblocklist.com/?list=dgxtneitpuvgqqcpfulq&fileformat=cidr&archiveformat=gz'
        option ban_src_desc 'Contains advertising trackers and a short list of bad/intrusive porn sites (IPv4)'
        option ban_src_rset '/^(([0-9]{1,3}\.){3}[0-9]{1,3}(\/[0-9]{1,2})?)([[:space:]]|$)/{print \"add iblocklist \"\$1}'
        option ban_src_settype 'net'
index 56e5b2a9932990ed5e7a8a7fda6b589d0775ecbf..763c07baf593b6974ef8433c0ec4982e1addd3f9 100644 (file)
@@ -1,12 +1,11 @@
 #!/bin/sh
 #
-
 ban_pidfile="/var/run/banip.pid"
-ban_enabled="$(/etc/init.d/banip enabled; printf "%u" ${?})"
+ban_enabled="$(/etc/init.d/banip enabled; printf "%u" "${?}")"
 
 if [ "${ban_enabled}" = "1" ] || [ ! -f "${ban_pidfile}" ] || [ -s "${ban_pidfile}" ] || [ "${ACTION}" != "add" ]
 then
        exit 0
+else
+       /etc/init.d/banip refresh
 fi
-
-/etc/init.d/banip refresh
index 235bbfd9c0a0b58974b9aa24972c48fb619b83c7..787a4edaa77672676874c5beb2e407c414e4e9c9 100755 (executable)
@@ -20,17 +20,17 @@ boot()
 
 start_service()
 {
-       if [ $("${ban_init}" enabled; printf "%u" ${?}) -eq 0 ]
+       if [ "$("${ban_init}" enabled; printf "%u" ${?})" -eq 0 ]
        then
                if [ "${ban_boot}" = "1" ]
                then
                        return 0
                fi
-               local nice="$(uci_get banip extra ban_nice)"
+               local nice="$(uci_get banip extra ban_nice "0")"
                procd_open_instance "banip"
                procd_set_param command "${ban_script}" "${@}"
                procd_set_param pidfile "${ban_pidfile}"
-               procd_set_param nice ${nice:-0}
+               procd_set_param nice "${nice}"
                procd_set_param stdout 1
                procd_set_param stderr 1
                procd_close_instance
@@ -39,6 +39,7 @@ start_service()
 
 refresh()
 {
+       [ -s "${ban_pidfile}" ] && return 1
        rc_procd start_service refresh
 }
 
@@ -54,34 +55,40 @@ stop_service()
 
 status()
 {
-       local key keylist value rtfile="$(uci_get banip global ban_rtfile)"
+       local key keylist value 
+       local rtfile="$(uci_get banip global ban_rtfile "/tmp/ban_runtime.json")"
 
-       rtfile="${rtfile:-"/tmp/ban_runtime.json"}"
        json_load_file "${rtfile}" >/dev/null 2>&1
        json_select data >/dev/null 2>&1
-       if [ ${?} -eq 0 ]
+       if [ "${?}" -eq 0 ]
        then
-               printf "%s\n" "::: banIP runtime information"
+               printf "%s\\n" "::: banIP runtime information"
                json_get_keys keylist
                for key in ${keylist}
                do
                        json_get_var value "${key}"
-                       printf "  + %-10s : %s\n" "${key}" "${value}"
+                       printf "  + %-10s : %s\\n" "${key}" "${value}"
                done
        else
-               printf "%s\n" "::: no banIP runtime information available"
+               printf "%s\\n" "::: no banIP runtime information available"
        fi
 }
 
 service_triggers()
 {
        local ban_iface="$(uci_get banip global ban_iface)"
-       local delay="$(uci_get banip extra ban_triggerdelay)"
+       local delay="$(uci_get banip extra ban_triggerdelay "2")"
+       local type="$(uci_get banip extra ban_starttype "start")"
 
-       PROCD_RELOAD_DELAY=$((${delay:-2} * 1000))
-       for iface in ${ban_iface:-"wan"}
-       do
-               procd_add_interface_trigger "interface.*.up" "${iface}" "${ban_init}" start
-       done
+       PROCD_RELOAD_DELAY=$((${delay}*1000))
+       if [ -n "${ban_iface}" ]
+       then
+               for iface in ${ban_iface}
+               do
+                       procd_add_interface_trigger "interface.*.up" "${iface}" "${ban_init}" "${type}"
+               done
+       else
+               procd_add_raw_trigger "interface.*.up" ${PROCD_RELOAD_DELAY} "${ban_init}" "${type}"
+       fi
        procd_add_reload_trigger "banip"
 }
index c546e872445e7324fb68f26ff9ef3874672e9f29..4cfac5455b4199674aad976db76153ea1f902e6d 100755 (executable)
@@ -6,19 +6,19 @@
 # You should have received a copy of the GNU General Public License
 # along with this program. If not, see <http://www.gnu.org/licenses/>.
 
+# (s)hellcheck exceptions
+# shellcheck disable=1091 disable=2039 disable=2143 disable=2181 disable=2188
+
 # set initial defaults
 #
 LC_ALL=C
 PATH="/usr/sbin:/usr/bin:/sbin:/bin"
-ban_ver="0.1.5"
-ban_sysver="unknown"
+ban_ver="0.2.0"
 ban_enabled=0
 ban_automatic="1"
 ban_sources=""
 ban_iface=""
 ban_debug=0
-ban_backup=0
-ban_backupboot=0
 ban_backupdir="/mnt"
 ban_maxqueue=4
 ban_autoblacklist=1
@@ -36,25 +36,18 @@ ban_chain="banIP"
 ban_action="${1:-"start"}"
 ban_pidfile="/var/run/banip.pid"
 ban_rtfile="/tmp/ban_runtime.json"
+ban_sshdaemon="dropbear"
 ban_setcnt=0
 ban_cnt=0
-ban_rc=0
 
 # load environment
 #
 f_envload()
 {
-       local sys_call sys_desc sys_model
-
        # get system information
        #
-       sys_call="$(ubus -S call system board 2>/dev/null)"
-       if [ -n "${sys_call}" ]
-       then
-               sys_desc="$(printf '%s' "${sys_call}" | jsonfilter -e '@.release.description')"
-               sys_model="$(printf '%s' "${sys_call}" | jsonfilter -e '@.model')"
-               ban_sysver="${sys_model}, ${sys_desc}"
-       fi
+       ban_sysver="$(ubus -S call system board 2>/dev/null | jsonfilter -e '@.model' -e '@.release.description' | \
+               awk 'BEGIN{ORS=", "}{print $0}' | awk '{print substr($0,1,length($0)-2)}')"
 
        # parse 'global' and 'extra' section by callback
        #
@@ -124,33 +117,32 @@ f_envcheck()
 {
        local ssl_lib tmp
 
+       # check backup directory
+       #
+       if [ ! -d "${ban_backupdir}" ]
+       then
+               f_log "err" "the backup directory '${ban_backupdir}' does not exist/is not mounted yet, please create the directory or raise the 'ban_triggerdelay' to defer the banIP start"
+       fi
+
        # check fetch utility
        #
        case "${ban_fetchutil}" in
-               uclient-fetch)
+               "uclient-fetch")
                        if [ -f "/lib/libustream-ssl.so" ]
                        then
                                ban_fetchparm="${ban_fetchparm:-"--timeout=20 --no-check-certificate -O"}"
                                ssl_lib="libustream-ssl"
-                       else
-                               ban_fetchparm="${ban_fetchparm:-"--timeout=20 -O"}"
                        fi
                ;;
-               wget)
+               "wget")
                        ban_fetchparm="${ban_fetchparm:-"--no-cache --no-cookies --max-redirect=0 --timeout=20 --no-check-certificate -O"}"
                        ssl_lib="built-in"
                ;;
-               wget-nossl)
-                       ban_fetchparm="${ban_fetchparm:-"--no-cache --no-cookies --max-redirect=0 --timeout=20 -O"}"
-               ;;
-               busybox)
-                       ban_fetchparm="${ban_fetchparm:-"-O"}"
-               ;;
-               curl)
+               "curl")
                        ban_fetchparm="${ban_fetchparm:-"--connect-timeout 20 --insecure -o"}"
                        ssl_lib="built-in"
                ;;
-               aria2c)
+               "aria2c")
                        ban_fetchparm="${ban_fetchparm:-"--timeout=20 --allow-overwrite=true --auto-file-renaming=false --check-certificate=false -o"}"
                        ssl_lib="built-in"
                ;;
@@ -160,7 +152,7 @@ f_envcheck()
 
        if [ ! -x "${ban_fetchutil}" ] || [ -z "${ban_fetchutil}" ] || [ -z "${ban_fetchparm}" ]
        then
-               f_log "err" "download utility not found, please install 'uclient-fetch' with 'libustream-mbedtls' or the full 'wget' package"
+               f_log "err" "download utility not found, please install 'uclient-fetch' with the 'libustream-mbedtls' ssl library or the full 'wget' package"
        fi
 
        # get wan device and wan subnets
@@ -215,16 +207,19 @@ f_envcheck()
 #
 f_temp()
 {
-       if [ -z "${ban_tmpdir}" ]
+       if [ -d "/tmp" ] && [ -z "${ban_tmpdir}" ]
        then
                ban_tmpdir="$(mktemp -p /tmp -d)"
                ban_tmpload="$(mktemp -p "${ban_tmpdir}" -tu)"
                ban_tmpfile="$(mktemp -p "${ban_tmpdir}" -tu)"
+       elif [ ! -d "/tmp" ]
+       then
+               f_log "err" "the temp directory '/tmp' does not exist/is not mounted yet, please create the directory or raise the 'ban_triggerdelay' to defer the banIP start"
        fi
 
        if [ ! -s "${ban_pidfile}" ]
        then
-               printf '%s' "${$}" > "${ban_pidfile}"
+               printf "%s" "${$}" > "${ban_pidfile}"
        fi
 }
 
@@ -259,7 +254,7 @@ f_iptrule()
        then
                if [ -x "${ban_ipt6}" ]
                then
-                       rc="$("${ban_ipt6}" "${timeout}" -C ${rule} 2>/dev/null; printf '%u' ${?})"
+                       rc="$("${ban_ipt6}" "${timeout}" -C ${rule} 2>/dev/null; printf "%u" ${?})"
 
                        if { [ "${rc}" -ne 0 ] && { [ "${action}" = "-A" ] || [ "${action}" = "-I" ]; } } || \
                                { [ "${rc}" -eq 0 ] && [ "${action}" = "-D" ]; }
@@ -270,7 +265,7 @@ f_iptrule()
        else
                if [ -x "${ban_ipt}" ]
                then
-                       rc="$("${ban_ipt}" "${timeout}" -C ${rule} 2>/dev/null; printf '%u' ${?})"
+                       rc="$("${ban_ipt}" "${timeout}" -C ${rule} 2>/dev/null; printf "%u" ${?})"
 
                        if { [ "${rc}" -ne 0 ] && { [ "${action}" = "-A" ] || [ "${action}" = "-I" ]; } } || \
                                { [ "${rc}" -eq 0 ] && [ "${action}" = "-D" ]; }
@@ -339,7 +334,7 @@ f_iptadd()
 #
 f_ipset()
 {
-       local rc cnt cnt_ip cnt_cidr size source action ruleset ruleset_6 rule timeout="-w 5" mode="${1}"
+       local out_rc cnt cnt_ip cnt_cidr size source action ruleset ruleset_6 rule timeout="-w 5" mode="${1}" in_rc="${src_rc:-0}"
 
        if [ "${src_name%_6*}" = "whitelist" ]
        then
@@ -349,32 +344,33 @@ f_ipset()
        fi
 
        case "${mode}" in
-               backup)
-                       ban_rc=4
-                       if [ -d "${ban_backupdir}" ]
-                       then
-                               gzip -cf "${tmp_load}" 2>/dev/null > "${ban_backupdir}/banIP.${src_name}.gz"
-                               ban_rc=${?}
-                       fi
-                       f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, rc: ${ban_rc}"
+               "backup")
+                       gzip -cf "${tmp_load}" 2>/dev/null > "${ban_backupdir}/banIP.${src_name}.gz"
+                       out_rc="${?:-"${in_rc}"}"
+                       f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, out_rc: ${out_rc}"
+                       return "${out_rc}"
                ;;
-               restore)
-                       ban_rc=4
-                       if [ -d "${ban_backupdir}" ] && [ -f "${ban_backupdir}/banIP.${src_name}.gz" ]
+               "restore")
+                       if [ -f "${ban_backupdir}/banIP.${src_name}.gz" ]
                        then
-                               gunzip -cf "${ban_backupdir}/banIP.${src_name}.gz" 2>/dev/null > "${tmp_load}"
-                               ban_rc=${?}
+                               zcat "${ban_backupdir}/banIP.${src_name}.gz" 2>/dev/null > "${tmp_load}"
+                               out_rc="${?}"
                        fi
-                       f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, rc: ${ban_rc}"
+                       out_rc="${out_rc:-"${in_rc}"}"
+                       f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, out_rc: ${out_rc}"
+                       return "${out_rc}"
                ;;
-               remove)
-                       if [ -d "${ban_backupdir}" ] && [ -f "${ban_backupdir}/banIP.${src_name}.gz" ]
+               "remove")
+                       if [ -f "${ban_backupdir}/banIP.${src_name}.gz" ]
                        then
                                rm -f "${ban_backupdir}/banIP.${src_name}.gz"
+                               out_rc="${?}"
                        fi
-                       f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}"
+                       out_rc="${out_rc:-"${in_rc}"}"
+                       f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, out_rc: ${out_rc}"
+                       return "${out_rc}"
                ;;
-               initial)
+               "initial")
                        if [ -x "${ban_ipt}" ] && [ -z "$("${ban_ipt}" "${timeout}" -nL "${ban_chain}" 2>/dev/null)" ]
                        then
                                "${ban_ipt}" "${timeout}" -N "${ban_chain}"
@@ -387,7 +383,6 @@ f_ipset()
                                        f_iptrule "-D" "${rule} -j ${ban_chain}"
                                done
                        fi
-
                        if [ -x "${ban_ipt6}" ] && [ -z "$("${ban_ipt6}" "${timeout}" -nL "${ban_chain}" 2>/dev/null)" ]
                        then
                                "${ban_ipt6}" "${timeout}" -N "${ban_chain}"
@@ -402,48 +397,51 @@ f_ipset()
                        fi
                        f_log "debug" "f_ipset ::: name: -, mode: ${mode:-"-"}, chain: ${ban_chain:-"-"}, ruleset: ${ruleset:-"-"}, ruleset_6: ${ruleset_6:-"-"}"
                ;;
-               create)
-                       cnt="$(wc -l 2>/dev/null < "${tmp_file}")"
-                       cnt_cidr="$(grep -cF "/" "${tmp_file}")"
-                       cnt_ip="$((cnt-cnt_cidr))"
-                       size="$((cnt/4))"
-
-                       if [ "${cnt}" -gt 0 ]
+               "create")
+                       if [ -x "${ban_ipset}" ] && [ -z "$("${ban_ipset}" -q -n list "${src_name}")" ]
+                       then
+                               "${ban_ipset}" -q create "${src_name}" hash:"${src_settype}" hashsize "${size}" maxelem 262144 family "${src_setipv}" counters
+                       else
+                               "${ban_ipset}" -q flush "${src_name}"
+                       fi
+                       if [ -s "${tmp_file}" ]
                        then
-                               if [ -x "${ban_ipset}" ] && [ -z "$("${ban_ipset}" -q -n list "${src_name}")" ]
-                               then
-                                       "${ban_ipset}" -q create "${src_name}" hash:"${src_settype}" hashsize "${size}" maxelem 262144 family "${src_setipv}" counters
-                               else
-                                       "${ban_ipset}" -q flush "${src_name}"
-                               fi
                                "${ban_ipset}" -! restore < "${tmp_file}"
-                               printf "%s\n" "1" > "${tmp_set}"
-                               printf "%s\n" "${cnt}" > "${tmp_cnt}"
+                               out_rc="${?}"
+                               "${ban_ipset}" -q save "${src_name}" > "${tmp_file}"
+                               cnt="$(($(wc -l 2>/dev/null < "${tmp_file}")-1))"
+                               cnt_cidr="$(grep -cF "/" "${tmp_file}")"
+                               cnt_ip="$((cnt-cnt_cidr))"
+                               size="$((cnt/4))"
+                               printf "%s\\n" "1" > "${tmp_set}"
+                               printf "%s\\n" "${cnt}" > "${tmp_cnt}"
+                               f_iptadd
                        fi
-                       f_iptadd
                        end_ts="$(date +%s)"
-                       f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, settype: ${src_settype:-"-"}, setipv: ${src_setipv:-"-"}, ruletype: ${src_ruletype:-"-"}, count(sum/ip/cidr): ${cnt:-0}/${cnt_ip:-0}/${cnt_cidr:-0}, time: $((end_ts-start_ts))"
+                       out_rc="${out_rc:-"${in_rc}"}"
+                       f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, settype: ${src_settype:-"-"}, setipv: ${src_setipv:-"-"}, ruletype: ${src_ruletype:-"-"}, count(sum/ip/cidr): ${cnt:-0}/${cnt_ip:-0}/${cnt_cidr:-0}, time: $((end_ts-start_ts)), out_rc: ${out_rc}"
                ;;
-               refresh)
-                       ban_rc=4
+               "refresh")
                        if [ -x "${ban_ipset}" ] && [ -n "$("${ban_ipset}" -q -n list "${src_name}")" ]
                        then
                                "${ban_ipset}" -q save "${src_name}" > "${tmp_file}"
+                               out_rc="${?}"
                                if [ -s "${tmp_file}" ]
                                then
-                                       ban_rc=0
                                        cnt="$(($(wc -l 2>/dev/null < "${tmp_file}")-1))"
                                        cnt_cidr="$(grep -cF "/" "${tmp_file}")"
                                        cnt_ip="$((cnt-cnt_cidr))"
-                                       printf "%s\n" "1" > "${tmp_set}"
-                                       printf "%s\n" "${cnt}" > "${tmp_cnt}"
+                                       printf "%s\\n" "1" > "${tmp_set}"
+                                       printf "%s\\n" "${cnt}" > "${tmp_cnt}"
                                fi
                                f_iptadd
                        fi
                        end_ts="$(date +%s)"
-                       f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, count: ${cnt:-0}/${cnt_ip:-0}/${cnt_cidr:-0}, time: $((end_ts-start_ts)), rc: ${ban_rc}"
+                       out_rc="${out_rc:-"${in_rc}"}"
+                       f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, count: ${cnt:-0}/${cnt_ip:-0}/${cnt_cidr:-0}, time: $((end_ts-start_ts)), out_rc: ${out_rc}"
+                       return "${out_rc}"
                ;;
-               flush)
+               "flush")
                        f_iptadd "remove"
 
                        if [ -x "${ban_ipset}" ] && [ -n "$("${ban_ipset}" -q -n list "${src_name}")" ]
@@ -453,7 +451,7 @@ f_ipset()
                        fi
                        f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}"
                ;;
-               destroy)
+               "destroy")
                        if [ -x "${ban_ipt}" ] && [ -x "${ban_ipt_save}" ] && [ -x "${ban_ipt_restore}" ] && \
                                [ -n "$("${ban_ipt}" "${timeout}" -nL "${ban_chain}" 2>/dev/null)" ]
                        then
@@ -461,7 +459,6 @@ f_ipset()
                                "${ban_ipt}" "${timeout}" -F "${ban_chain}"
                                "${ban_ipt}" "${timeout}" -X "${ban_chain}"
                        fi
-
                        if [ -x "${ban_ipt6}" ] && [ -x "${ban_ipt6_save}" ] && [ -x "${ban_ipt6_restore}" ] && \
                                [ -n "$("${ban_ipt6}" "${timeout}" -nL "${ban_chain}" 2>/dev/null)" ]
                        then
@@ -469,7 +466,6 @@ f_ipset()
                                "${ban_ipt6}" "${timeout}" -F "${ban_chain}"
                                "${ban_ipt6}" "${timeout}" -X "${ban_chain}"
                        fi
-
                        for source in ${ban_sources}
                        do
                                if [ -x "${ban_ipset}" ] && [ -n "$("${ban_ipset}" -q -n list "${source}")" ]
@@ -508,13 +504,13 @@ f_log()
 f_main()
 {
        local pid pid_list start_ts end_ts ip tmp_raw tmp_cnt tmp_load tmp_file mem_total mem_free cnt=1
-       local src_name src_on src_url src_rset src_setipv src_settype src_ruletype src_cat src_log src_addon
+       local src_name src_on src_url src_rset src_setipv src_settype src_ruletype src_cat src_log src_addon src_rc
        local wan_input wan_forward lan_input lan_forward target_src target_dst log_content
 
-       log_content="$(logread -e "dropbear")"
+       log_content="$(logread -e "${ban_sshdaemon}")"
        mem_total="$(awk '/^MemTotal/ {print int($2/1000)}' "/proc/meminfo" 2>/dev/null)"
        mem_free="$(awk '/^MemFree/ {print int($2/1000)}' "/proc/meminfo" 2>/dev/null)"
-       f_log "debug" "f_main  ::: fetch_util: ${ban_fetchinfo:-"-"}, fetch_parm: ${ban_fetchparm:-"-"}, interface(s): ${ban_iface:-"-"}, device(s): ${ban_dev:-"-"}, all_devices: ${ban_dev_all:-"-"}, backup: ${ban_backup:-"-"}, backup_boot: ${ban_backupboot:-"-"}, backup_dir: ${ban_backupdir:-"-"}, mem_total: ${mem_total:-0}, mem_free: ${mem_free:-0}, max_queue: ${ban_maxqueue}"
+       f_log "debug" "f_main  ::: fetch_util: ${ban_fetchinfo:-"-"}, 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}"
 
        f_ipset initial
 
@@ -527,9 +523,9 @@ f_main()
                then
                        if [ -x "${ban_ipt6}" ]
                        then
-                               src_on="$(eval printf '%s' \"\${ban_src_on_6_${src_name%_6*}\}\")"
-                               src_url="$(eval printf '%s' \"\${ban_src_6_${src_name%_6*}\}\")"
-                               src_rset="$(eval printf '%s' \"\${ban_src_rset_6_${src_name%_6*}\}\")"
+                               src_on="$(eval printf "%s" \"\$\{ban_src_on_6_${src_name%_6*}\}\")"
+                               src_url="$(eval printf "%s" \"\$\{ban_src_6_${src_name%_6*}\}\")"
+                               src_rset="$(eval printf "%s" \"\$\{ban_src_rset_6_${src_name%_6*}\}\")"
                                src_setipv="inet6"
                                wan_input="${ban_wan_input_chain_6:-"input_wan_rule"}"
                                wan_forward="${ban_wan_forward_chain_6:-"forwarding_wan_rule"}"
@@ -541,9 +537,9 @@ f_main()
                else
                        if [ -x "${ban_ipt}" ]
                        then
-                               src_on="$(eval printf '%s' \"\${ban_src_on_${src_name}\}\")"
-                               src_url="$(eval printf '%s' \"\${ban_src_${src_name}\}\")"
-                               src_rset="$(eval printf '%s' \"\${ban_src_rset_${src_name}\}\")"
+                               src_on="$(eval printf "%s" \"\$\{ban_src_on_${src_name}\}\")"
+                               src_url="$(eval printf "%s" \"\$\{ban_src_${src_name}\}\")"
+                               src_rset="$(eval printf "%s" \"\$\{ban_src_rset_${src_name}\}\")"
                                src_setipv="inet"
                                wan_input="${ban_wan_input_chain:-"input_wan_rule"}"
                                wan_forward="${ban_wan_forward_chain:-"forwarding_wan_rule"}"
@@ -553,10 +549,11 @@ f_main()
                                target_dst="${ban_target_dst:-"REJECT"}"
                        fi
                fi
-               src_settype="$(eval printf '%s' \"\${ban_src_settype_${src_name%_6*}\}\")"
-               src_ruletype="$(eval printf '%s' \"\${ban_src_ruletype_${src_name%_6*}\}\")"
-               src_cat="$(eval printf '%s' \"\${ban_src_cat_${src_name%_6*}\}\")"
+               src_settype="$(eval printf "%s" \"\$\{ban_src_settype_${src_name%_6*}\}\")"
+               src_ruletype="$(eval printf "%s" \"\$\{ban_src_ruletype_${src_name%_6*}\}\")"
+               src_cat="$(eval printf "%s" \"\$\{ban_src_cat_${src_name%_6*}\}\")"
                src_addon=""
+               src_rc=4
                tmp_load="${ban_tmpload}.${src_name}"
                tmp_file="${ban_tmpfile}.${src_name}"
                tmp_raw="${tmp_load}.raw"
@@ -573,11 +570,11 @@ f_main()
                        f_ipset flush
                        f_ipset remove
                        continue
-               elif [ "${ban_action}" = "refresh" ]
+               elif [ "${ban_action}" = "refresh" ] && [ ! -f "${src_url}" ]
                then
                        start_ts="$(date +%s)"
                        f_ipset refresh
-                       if [ ${ban_rc} -eq 0 ]
+                       if [ "${?}" -eq 0 ]
                        then
                                continue
                        fi
@@ -587,48 +584,60 @@ f_main()
                #
                (
                        start_ts="$(date +%s)"
-                       if [ ! -f "${src_url}" ] && [ ${ban_backup} -eq 1 ] && [ ${ban_backupboot} -eq 1 ] && [ "${ban_action}" = "start" ]
+                       if [ ! -f "${src_url}" ] && [ "${ban_action}" = "start" ]
                        then
                                f_ipset restore
                        fi
-
-                       if [ ${ban_rc} -ne 0 ] || [ ! -s "${tmp_load}" ]
+                       src_rc="${?}"
+                       if [ "${src_rc}" -ne 0 ] || [ ! -s "${tmp_load}" ]
                        then
                                if [ -f "${src_url}" ]
                                then
                                        src_log="$(cat "${src_url}" 2>/dev/null > "${tmp_load}")"
-                                       ban_rc=${?}
+                                       src_rc="${?}"
                                        case "${src_name}" in
-                                               whitelist)
+                                               "whitelist")
                                                        src_addon="${ban_subnets}"
                                                ;;
-                                               whitelist_6)
+                                               "whitelist_6")
                                                        src_addon="${ban_subnets6}"
                                                ;;
-                                               blacklist)
-                                                       pid_list="$(printf "%s\n" "${log_content}" | grep -F "Exit before auth" | awk 'match($0,/(\[[0-9]+\])/){ORS=" ";print substr($0,RSTART,RLENGTH)}')"
-                                                       for pid in ${pid_list}
-                                                       do
-                                                               src_addon="${src_addon} $(printf "%s\n" "${log_content}" | grep -F "${pid}" | awk 'match($0,/([0-9]{1,3}\.){3}[0-9]{1,3}/){ORS=" ";print substr($0,RSTART,RLENGTH)}')"
-                                                       done
+                                               "blacklist")
+                                                       if [ "${ban_sshdaemon}" = "dropbear" ]
+                                                       then
+                                                               pid_list="$(printf "%s\\n" "${log_content}" | grep -F "Exit before auth" | awk 'match($0,/(\[[0-9]+\])/){ORS=" ";print substr($0,RSTART,RLENGTH)}')"
+                                                               for pid in ${pid_list}
+                                                               do
+                                                                       src_addon="${src_addon} $(printf "%s\\n" "${log_content}" | grep -F "${pid}" | awk 'match($0,/([0-9]{1,3}\.){3}[0-9]{1,3}/){ORS=" ";print substr($0,RSTART,RLENGTH)}')"
+                                                               done
+                                                       elif [ "${ban_sshdaemon}" = "sshd" ]
+                                                       then
+                                                               src_addon="$(printf "%s\\n" "${log_content}" | grep -E "[0-9]+ \[preauth\]$" | awk 'match($0,/([0-9]{1,3}\.){3}[0-9]{1,3}/){ORS=" ";print substr($0,RSTART,RLENGTH)}')"
+                                                       fi
                                                ;;
-                                               blacklist_6)
-                                                       pid_list="$(printf "%s\n" "${log_content}" | grep -F "Exit before auth" | awk 'match($0,/(\[[0-9]+\])/){ORS=" ";print substr($0,RSTART,RLENGTH)}')"
-                                                       for pid in ${pid_list}
-                                                       do
-                                                               src_addon="${src_addon} $(printf "%s\n" "${log_content}" | grep -F "${pid}" | awk 'match($0,/([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}/){ORS=" ";print substr($0,RSTART,RLENGTH)}')"
-                                                       done
+                                               "blacklist_6")
+                                                       if [ "${ban_sshdaemon}" = "dropbear" ]
+                                                       then
+                                                               pid_list="$(printf "%s\\n" "${log_content}" | grep -F "Exit before auth" | awk 'match($0,/(\[[0-9]+\])/){ORS=" ";print substr($0,RSTART,RLENGTH)}')"
+                                                               for pid in ${pid_list}
+                                                               do
+                                                                       src_addon="${src_addon} $(printf "%s\\n" "${log_content}" | grep -F "${pid}" | awk 'match($0,/([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}/){ORS=" ";print substr($0,RSTART,RLENGTH)}')"
+                                                               done
+                                                       elif [ "${ban_sshdaemon}" = "sshd" ]
+                                                       then
+                                                               src_addon="$(printf "%s\\n" "${log_content}" | grep -E "[0-9]+ \[preauth\]$" | awk 'match($0,/([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}/){ORS=" ";print substr($0,RSTART,RLENGTH)}')"
+                                                       fi
                                                ;;
                                        esac
                                        for ip in ${src_addon}
                                        do
                                                if [ -z "$(grep -F "${ip}" "${src_url}")" ]
                                                then
-                                                       printf '%s\n' "${ip}" >> "${tmp_load}"
+                                                       printf "%s\\n" "${ip}" >> "${tmp_load}"
                                                        if { [ "${src_name//_*/}" = "blacklist" ] && [ "${ban_autoblacklist}" -eq 1 ]; } || \
                                                                { [ "${src_name//_*/}" = "whitelist" ] && [ "${ban_autowhitelist}" -eq 1 ]; }
                                                        then
-                                                               printf '%s\n' "${ip}" >> "${src_url}"
+                                                               printf "%s\\n" "${ip}" >> "${src_url}"
                                                        fi
                                                fi
                                        done
@@ -639,18 +648,18 @@ f_main()
                                                for as in ${src_cat}
                                                do
                                                        src_log="$("${ban_fetchutil}" ${ban_fetchparm} "${tmp_raw}" "${src_url}AS${as}" 2>&1)"
-                                                       ban_rc=${?}
-                                                       if [ ${ban_rc} -eq 0 ]
+                                                       src_rc="${?}"
+                                                       if [ "${src_rc}" -eq 0 ]
                                                        then
                                                                jsonfilter -i "${tmp_raw}" -e '@.data.prefixes.*.prefix' 2>/dev/null >> "${tmp_load}"
                                                        else
                                                                break
                                                        fi
                                                done
-                                               if [ ${ban_rc} -eq 0 ] && [ ${ban_backup} -eq 1 ]
+                                               if [ "${src_rc}" -eq 0 ]
                                                then
                                                        f_ipset backup
-                                               elif [ ${ban_backup} -eq 1 ]
+                                               elif [ "${ban_action}" != "start" ]
                                                then
                                                        f_ipset restore
                                                fi
@@ -658,8 +667,8 @@ f_main()
                                                for co in ${src_cat}
                                                do
                                                        src_log="$("${ban_fetchutil}" ${ban_fetchparm} "${tmp_raw}" "${src_url}${co}&v4_format=prefix" 2>&1)"
-                                                       ban_rc=${?}
-                                                       if [ ${ban_rc} -eq 0 ]
+                                                       src_rc="${?}"
+                                                       if [ "${src_rc}" -eq 0 ]
                                                        then
                                                                if [ "${src_name##*_}" = "6" ]
                                                                then
@@ -671,53 +680,62 @@ f_main()
                                                                break
                                                        fi
                                                done
-                                               if [ ${ban_rc} -eq 0 ] && [ ${ban_backup} -eq 1 ]
+                                               if [ "${src_rc}" -eq 0 ]
                                                then
                                                        f_ipset backup
-                                               elif [ ${ban_backup} -eq 1 ]
+                                               elif [ "${ban_action}" != "start" ]
                                                then
                                                        f_ipset restore
                                                fi
                                        fi
                                else
                                        src_log="$("${ban_fetchutil}" ${ban_fetchparm} "${tmp_raw}" "${src_url}" 2>&1)"
-                                       ban_rc=${?}
-                                       if [ ${ban_rc} -eq 0 ]
+                                       src_rc="${?}"
+                                       if [ "${src_rc}" -eq 0 ]
                                        then
                                                zcat "${tmp_raw}" 2>/dev/null > "${tmp_load}"
-                                               ban_rc=${?}
-                                               if [ ${ban_rc} -ne 0 ]
+                                               src_rc="${?}"
+                                               if [ "${src_rc}" -ne 0 ]
                                                then
                                                        mv -f "${tmp_raw}" "${tmp_load}"
-                                                       ban_rc=${?}
+                                                       src_rc="${?}"
                                                fi
-                                               if [ ${ban_rc} -eq 0 ] && [ ${ban_backup} -eq 1 ]
+                                               if [ "${src_rc}" -eq 0 ]
                                                then
                                                        f_ipset backup
+                                                       src_rc="${?}"
                                                fi
-                                       elif [ ${ban_backup} -eq 1 ]
+                                       elif [ "${ban_action}" != "start" ]
                                        then
                                                f_ipset restore
+                                               src_rc="${?}"
                                        fi
                                fi
                        fi
 
-                       if [ ${ban_rc} -eq 0 ]
+                       if [ "${src_rc}" -eq 0 ]
                        then
                                awk "${src_rset}" "${tmp_load}" 2>/dev/null > "${tmp_file}"
-                               ban_rc=${?}
-                               if [ ${ban_rc} -eq 0 ]
+                               src_rc="${?}"
+                               if [ "${src_rc}" -eq 0 ]
                                then
                                        f_ipset create
-                               else
+                                       src_rc="${?}"
+                               elif [ "${ban_action}" != "refresh" ]
+                               then
                                        f_ipset refresh
+                                       src_rc="${?}"
                                fi
                        else
-                               src_log="$(printf '%s' "${src_log}" | awk '{ORS=" ";print $0}')"
-                               f_log "debug" "f_main  ::: name: ${src_name}, url: ${src_url}, rc: ${ban_rc}, log: ${src_log:-"-"}"
-                               f_ipset refresh
+                               src_log="$(printf "%s" "${src_log}" | awk '{ORS=" ";print $0}')"
+                               if [ "${ban_action}" != "refresh" ]
+                               then
+                                       f_ipset refresh
+                                       src_rc="${?}"
+                               fi
+                               f_log "debug" "f_main  ::: name: ${src_name}, url: ${src_url}, rc: ${src_rc}, log: ${src_log:-"-"}"
                        fi
-               ) &
+               )&
                hold="$((cnt%ban_maxqueue))"
                if [ "${hold}" -eq 0 ]
                then
@@ -725,40 +743,30 @@ f_main()
                fi
                cnt="$((cnt+1))"
        done
-
        wait
-       if [ "${ban_rc}" -eq 0 ]
-       then
-               for cnt in $(cat "${ban_tmpfile}".*.setcnt 2>/dev/null)
-               do
-                       ban_setcnt="$((ban_setcnt+cnt))"
-               done
-               for cnt in $(cat "${ban_tmpfile}".*.cnt 2>/dev/null)
-               do
-                       ban_cnt="$((ban_cnt+cnt))"
-               done
-               f_log "info" "${ban_setcnt} IPSets with overall ${ban_cnt} IPs/Prefixes loaded successfully (${ban_sysver})"
-       fi
+
+       for cnt in $(cat "${ban_tmpfile}".*.setcnt 2>/dev/null)
+       do
+               ban_setcnt="$((ban_setcnt+cnt))"
+       done
+       for cnt in $(cat "${ban_tmpfile}".*.cnt 2>/dev/null)
+       do
+               ban_cnt="$((ban_cnt+cnt))"
+       done
+       f_log "info" "${ban_setcnt} IPSets with overall ${ban_cnt} IPs/Prefixes loaded successfully (${ban_sysver})"
        f_jsnup
        f_rmtemp
-       exit ${ban_rc}
 }
 
 # update runtime information
 #
 f_jsnup()
 {
-       local rundate mode status="${1:-"enabled"}"
+       local rundate status="${1:-"enabled"}"
 
        rundate="$(/bin/date "+%d.%m.%Y %H:%M:%S")"
-       mode="normal mode"
        ban_cntinfo="${ban_setcnt} IPSets with overall ${ban_cnt} IPs/Prefixes"
 
-       if [ "${ban_backupboot}" -eq 1 ]
-       then
-               mode="backup mode"
-       fi
-
        > "${ban_rtfile}"
        json_load_file "${ban_rtfile}" >/dev/null 2>&1
        json_init
@@ -766,12 +774,12 @@ f_jsnup()
        json_add_string "status" "${status}"
        json_add_string "version" "${ban_ver}"
        json_add_string "fetch_info" "${ban_fetchinfo:-"-"}"
-       json_add_string "ipset_info" "${ban_cntinfo:-"-"} (${mode})"
+       json_add_string "ipset_info" "${ban_cntinfo:-"-"}"
+       json_add_string "backup_dir" "${ban_backupdir}"
        json_add_string "last_run" "${rundate:-"-"}"
        json_add_string "system" "${ban_sysver}"
        json_close_object
        json_dump > "${ban_rtfile}"
-
        f_log "debug" "f_jsnup ::: status: ${status}, setcnt: ${ban_setcnt}, cnt: ${ban_cnt}"
 }
 
@@ -790,13 +798,13 @@ fi
 #
 f_envload
 case "${ban_action}" in
-       stop)
+       "stop")
                f_jsnup stopped
                f_ipset destroy
                f_rmbackup
                f_rmtemp
        ;;
-       start|restart|reload|refresh)
+       "start"|"restart"|"reload"|"refresh")
                f_envcheck
                f_main
        ;;
git clone https://git.99rst.org/PROJECT