# banIP - ban incoming and/or outgoing ip adresses via ipsets
## Description
-IP address blocking is commonly used to protect against brute force attacks, prevent disruptive or unautherized address(es) from access or it can be used to restrict access to or from a particular geographic area — for example.
+IP address blocking is commonly used to protect against brute force attacks, prevent disruptive or unauthorized address(es) from access or it can be used to restrict access to or from a particular geographic area — for example.
## 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 four different download utilities: uclient-fetch, wget, curl, aria2c
+* automatically selects one of the following download utilities: aria2c, curl, uclient-fetch, wget
* Really fast downloads & list processing as they are handled in parallel as background jobs in a configurable 'Download Queue'
* 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 'dropbear' or 'sshd' to local blacklist (see 'ban_autoblacklist' option)
+* auto-add unsuccessful LuCI and ssh login attempts via 'dropbear' or 'sshd' to local blacklist (see 'ban_autoblacklist' option)
* auto-add the uplink subnet to local whitelist (see 'ban_autowhitelist' option)
+* provides a small background log monitor to ban unsuccessful login attempts in real-time
* per source configuration of SRC (incoming) and DST (outgoing)
* integrated IPSet-Lookup
* integrated RIPE-Lookup
## Prerequisites
* [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
+* download utility: 'uclient-fetch' with one of the 'libustream-*' ssl libraries, 'wget', 'aria2c' or 'curl' is required
## Installation & Usage
* install 'banip' (_opkg install banip_)
* the following options apply to the 'global' config section:
* ban\_enabled => main switch to enable/disable banIP service (bool/default: '0', disabled)
* ban\_automatic => determine the L2/L3 WAN network device automatically (bool/default: '1', enabled)
- * ban\_fetchutil => name of the used download utility: 'uclient-fetch', 'wget', 'curl', 'aria2c', 'wget-nossl'. 'busybox' (default: 'uclient-fetch')
- * ban\_iface => space separated list of WAN network interface(s)/device(s) used by banIP (default: automatically set by banIP ('ban_automatic'))
+ * ban\_iface => space separated list of WAN network interface(s)/device(s) used by banIP (default: not set, automatically detected)
+ * ban\_realtime => a small log/banIP background monitor to block SSH/LuCI brute force attacks in realtime (bool/default: 'false', disabled)
* the following options apply to the 'extra' config section:
* ban\_debug => enable/disable banIP debug output (bool/default: '0', disabled)
* ban\_triggerdelay => additional trigger delay in seconds before banIP processing begins (int/default: '2')
* 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\_starttype => select the used start type during boot, 'start', 'refresh' or 'reload' (default: 'start')
* ban\_maxqueue => size of the download queue to handle downloads & IPSet processing in parallel (int/default: '4')
+ * ban\_fetchutil => name of the used download utility: 'uclient-fetch', 'wget', 'curl', 'aria2c' (default: not set, automatically detected)
* 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)
/etc/init.d/banip status
::: banIP runtime information
+ status : enabled
- + version : 0.2.0
- + fetch_info : /bin/uclient-fetch (libustream-ssl)
- + ipset_info : 11 IPSets with overall 118359 IPs/Prefixes
+ + version : 0.3.0
+ + util_info : /usr/bin/aria2c, true
+ + ipset_info : 10 IPSets with overall 106729 IPs/Prefixes
+ backup_dir : /tmp
- + last_run : 09.09.2019 16:49:40
- + system : UBNT-ERX, OpenWrt SNAPSHOT r10962-c19b9f9a26
+ + last_run : 03.10.2019 19:15:25
+ + system : UBNT-ERX, OpenWrt SNAPSHOT r11102-ced4c0e635
</code></pre>
**cronjob for a regular IPSet blocklist update (/etc/crontabs/root):**
#
LC_ALL=C
PATH="/usr/sbin:/usr/bin:/sbin:/bin"
-ban_ver="0.2.1"
+ban_ver="0.3.0"
+ban_basever=""
ban_enabled=0
ban_automatic="1"
ban_sources=""
ban_maxqueue=4
ban_autoblacklist=1
ban_autowhitelist=1
-ban_fetchutil="uclient-fetch"
+ban_realtime="false"
+ban_fetchutil=""
ban_ip="$(command -v ip)"
ban_ipt="$(command -v iptables)"
ban_ipt_save="$(command -v iptables-save)"
ban_action="${1:-"start"}"
ban_pidfile="/var/run/banip.pid"
ban_rtfile="/tmp/ban_runtime.json"
+ban_logservice="/etc/banip/banip.service"
ban_sshdaemon="dropbear"
ban_setcnt=0
ban_cnt=0
config_load banip
config_foreach parse_config source
+ # version check
+ #
+ if [ -z "${ban_basever}" ] || [ "${ban_ver%.*}" != "${ban_basever}" ]
+ then
+ f_log "info" "your banIP config seems to be too old, please update your config with the '--force-maintainer' opkg option"
+ exit 0
+ fi
+
# create temp directory & files
#
f_temp
#
if [ "${ban_enabled}" -eq 0 ]
then
+ f_bgserv "stop"
f_jsnup disabled
f_ipset destroy
f_rmbackup
#
f_envcheck()
{
- local ssl_lib tmp
+ local util utils packages tmp cnt=0
# check backup directory
#
# check fetch utility
#
- case "${ban_fetchutil}" in
- "uclient-fetch")
- if [ -f "/lib/libustream-ssl.so" ]
+ if [ -z "${ban_fetchutil}" ]
+ then
+ utils="aria2c curl wget uclient-fetch"
+ packages="$(opkg list-installed 2>/dev/null)"
+ for util in ${utils}
+ do
+ if { [ "${util}" = "uclient-fetch" ] && [ -n "$(printf "%s\\n" "${packages}" | grep "^libustream-")" ]; } || \
+ { [ "${util}" = "wget" ] && [ -n "$(printf "%s\\n" "${packages}" | grep "^wget -")" ]; } || \
+ { [ "${util}" != "uclient-fetch" ] && [ "${util}" != "wget" ]; }
then
- ban_fetchparm="${ban_fetchparm:-"--timeout=20 --no-check-certificate -O"}"
- ssl_lib="libustream-ssl"
+ ban_fetchutil="$(command -v "${util}")"
+ if [ -x "${ban_fetchutil}" ]
+ then
+ break
+ fi
fi
- ;;
- "wget")
- ban_fetchparm="${ban_fetchparm:-"--no-cache --no-cookies --max-redirect=0 --timeout=20 --no-check-certificate -O"}"
- ssl_lib="built-in"
+ unset ban_fetchutil util
+ done
+ else
+ util="${ban_fetchutil}"
+ ban_fetchutil="$(command -v "${util}")"
+ if [ ! -x "${ban_fetchutil}" ]
+ then
+ unset ban_fetchutil util
+ fi
+ fi
+ case "${util}" in
+ "aria2c")
+ ban_fetchparm="${ban_fetchparm:-"--timeout=20 --allow-overwrite=true --auto-file-renaming=false --check-certificate=true --dir=" " -o"}"
;;
"curl")
- ban_fetchparm="${ban_fetchparm:-"--connect-timeout 20 --insecure -o"}"
- ssl_lib="built-in"
+ ban_fetchparm="${ban_fetchparm:-"--connect-timeout 20 -o"}"
;;
- "aria2c")
- ban_fetchparm="${ban_fetchparm:-"--timeout=20 --allow-overwrite=true --auto-file-renaming=false --check-certificate=false -o"}"
- ssl_lib="built-in"
+ "uclient-fetch")
+ ban_fetchparm="${ban_fetchparm:-"--timeout=20 -O"}"
+ ;;
+ "wget")
+ ban_fetchparm="${ban_fetchparm:-"--no-cache --no-cookies --max-redirect=0 --timeout=20 -O"}"
;;
esac
- ban_fetchutil="$(command -v "${ban_fetchutil}")"
- ban_fetchinfo="${ban_fetchutil:-"-"} (${ssl_lib:-"-"})"
-
- if [ ! -x "${ban_fetchutil}" ] || [ -z "${ban_fetchutil}" ] || [ -z "${ban_fetchparm}" ]
+ if [ -z "${ban_fetchutil}" ] || [ -z "${ban_fetchparm}" ]
then
- f_log "err" "download utility not found, please install 'uclient-fetch' with the 'libustream-mbedtls' ssl library or the full 'wget' package"
+ 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'"
fi
# get wan device and wan subnets
#
if [ "${ban_automatic}" = "1" ]
then
- network_find_wan ban_iface
- if [ -z "${ban_iface}" ]
- then
- network_find_wan6 ban_iface
- fi
+ while [ "${cnt}" -le 30 ]
+ do
+ network_find_wan ban_iface
+ if [ -z "${ban_iface}" ]
+ then
+ network_find_wan6 ban_iface
+ fi
+ if [ -z "${ban_iface}" ]
+ then
+ network_flush_cache
+ cnt=$((cnt+1))
+ sleep 1
+ else
+ break
+ fi
+ done
fi
for iface in ${ban_iface}
if [ -z "${ban_iface}" ] || [ -z "${ban_dev}" ]
then
f_log "err" "wan interface(s)/device(s) (${ban_iface:-"-"}/${ban_dev:-"-"}) not found, please please check your configuration"
+ else
+ ban_dev_all="$(${ban_ip} link show | awk 'BEGIN{FS="[@: ]"}/^[0-9:]/{if(($3!="lo")&&($3!="br-lan")){print $3}}')"
+ f_jsnup "running"
+ f_log "info" "start banIP processing (${ban_action})"
fi
- ban_dev_all="$(${ban_ip} link show | awk 'BEGIN{FS="[@: ]"}/^[0-9:]/{if(($3!="lo")&&($3!="br-lan")){print $3}}')"
- uci_set banip global ban_iface "${ban_iface}"
- uci_commit banip
-
- f_jsnup "running"
- f_log "info" "start banIP processing (${ban_action})"
}
# create temporary files and directories
if [ -n "${log_msg}" ] && { [ "${class}" != "debug" ] || [ "${ban_debug}" -eq 1 ]; }
then
- logger -p "${class}" -t "banIP-[${ban_ver}]" "${log_msg}"
+ logger -p "${class}" -t "banIP-${ban_ver}[${$}]" "${log_msg}"
if [ "${class}" = "err" ]
then
f_jsnup error
f_ipset destroy
f_rmbackup
f_rmtemp
- logger -p "${class}" -t "banIP-[${ban_ver}]" "Please also check 'https://github.com/openwrt/packages/blob/master/net/banip/files/README.md'"
+ logger -p "${class}" -t "banIP-${ban_ver}[${$}]" "Please also check 'https://github.com/openwrt/packages/blob/master/net/banip/files/README.md'"
exit 1
fi
fi
}
+# start log service to trace failed ssh/luci logins
+#
+f_bgserv()
+{
+ local bg_pid status="${1}"
+
+ 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}')"
+ if [ -z "${bg_pid}" ] && [ "${status}" = "start" ] \
+ && [ -x "${ban_logservice}" ] && [ "${ban_realtime}" = "true" ]
+ then
+ ( "${ban_logservice}" "${ban_ver}" &)
+ elif [ -n "${bg_pid}" ] && [ "${status}" = "stop" ]
+ then
+ kill -HUP "${bg_pid}" 2>/dev/null
+ fi
+ f_log "debug" "f_bgserv ::: status: ${status:-"-"}, bg_pid: ${bg_pid:-"-"}, ban_realtime: ${ban_realtime:-"-"}, log_service: ${ban_logservice:-"-"}"
+}
+
# main function for banIP processing
#
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 src_rc
- local wan_input wan_forward lan_input lan_forward target_src target_dst log_content
+ 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
+ local wan_input wan_forward lan_input lan_forward target_src target_dst ssh_log luci_log
- log_content="$(logread -e "${ban_sshdaemon}")"
+ ssh_log="$(logread -e "${ban_sshdaemon}" | grep -o "${ban_sshdaemon}.*" | sed 's/:[0-9]*$//g')"
+ luci_log="$(logread -e "luci: failed login" | grep -o "luci:.*")"
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:-"-"}, 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
+ 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}"
# main loop
#
+ f_ipset initial
for src_name in ${ban_sources}
do
unset src_on
"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)}')"
+ pid_list="$(printf "%s\\n" "${ssh_log}" | 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)}')"
+ 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}')"
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)}')"
+ src_addon="$(printf "%s\\n" "${ssh_log}" | grep -E "[0-9]+ \[preauth\]$" | awk 'match($0,/([0-9]{1,3}\.){3}[0-9]{1,3}$/){ORS=" ";print substr($0,RSTART,RLENGTH)}')"
fi
+ 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)}')"
;;
"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)}')"
+ pid_list="$(printf "%s\\n" "${ssh_log}" | 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)}')"
+ 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}')"
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)}')"
+ src_addon="$(printf "%s\\n" "${ssh_log}" | grep -E "[0-9]+ \[preauth\]$" | awk 'match($0,/(([0-9A-f]{0,4}::?){1,7}[0-9A-f]{0,4}$)/){ORS=" ";print substr($0,RSTART,RLENGTH)}')"
fi
+ 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)}')"
;;
esac
for ip in ${src_addon}
if { [ "${src_name//_*/}" = "blacklist" ] && [ "${ban_autoblacklist}" -eq 1 ]; } || \
{ [ "${src_name//_*/}" = "whitelist" ] && [ "${ban_autowhitelist}" -eq 1 ]; }
then
- printf "%s\\n" "${ip}" >> "${src_url}"
+ src_ts="# auto-added $(date "+%d.%m.%Y %H:%M:%S")"
+ printf "%s %s\\n" "${ip}" "${src_ts}" >> "${src_url}"
fi
fi
done
ban_cnt="$((ban_cnt+cnt))"
done
f_log "info" "${ban_setcnt} IPSets with overall ${ban_cnt} IPs/Prefixes loaded successfully (${ban_sysver})"
+ f_bgserv "start"
f_jsnup
f_rmtemp
}
{
local rundate status="${1:-"enabled"}"
- rundate="$(/bin/date "+%d.%m.%Y %H:%M:%S")"
+ rundate="$(date "+%d.%m.%Y %H:%M:%S")"
ban_cntinfo="${ban_setcnt} IPSets with overall ${ban_cnt} IPs/Prefixes"
> "${ban_rtfile}"
json_add_object "data"
json_add_string "status" "${status}"
json_add_string "version" "${ban_ver}"
- json_add_string "fetch_info" "${ban_fetchinfo:-"-"}"
+ json_add_string "util_info" "${ban_fetchutil:-"-"}, ${ban_realtime:-"-"}"
json_add_string "ipset_info" "${ban_cntinfo:-"-"}"
json_add_string "backup_dir" "${ban_backupdir}"
json_add_string "last_run" "${rundate:-"-"}"
f_envload
case "${ban_action}" in
"stop")
+ f_bgserv "stop"
f_jsnup stopped
f_ipset destroy
f_rmbackup
f_rmtemp
;;
"start"|"restart"|"reload"|"refresh")
+ f_bgserv "stop"
f_envcheck
f_main
;;