近年来IPv6的推广已得到极大支持,家宽基本已经实现IPv4和IPv6双栈,而上述修改版bearDropper仅支持IPv4地址的识别,在有IPv6地址访问失败的记录下,大概率出现malformat错误: authpriv.notice bearDropper[xxxxx]: processLogLine(,171xxxxxxx) malformed line (authpriv.info dropbear[xxxxx]: Exit before auth from <2a06:4880:d000::e8:34201>: Exited normally)
getLogIP () { local logLine="$1" local ebaPID=$(echo "$logLine" | sed -n 's/^.*authpriv.info \(dropbear\[[0-9]*\]:\) Exit before auth.*/\1/p') [ -n "$ebaPID" ] && logLine=$($cmdLogreadEba | grep -F "${ebaPID} Child connection from ") echo "$logLine" | sed -n 's/^.*[^0-9]\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*$/\1/p' } # Args: $1=log line
里面关于ebaPID的2行代码是根据dropbear的进程ID来获取对应的Child connection from xxx.xxx.xxx.xxx和Exit before auth from <xxx.xxx.xxx.xxx>2行日志,如果是一个登录失败,这2行日志是成对出现的。而最后一行代码中的sed语句则是提取传入变量$logLine中的IPv4地址的正则表达式。 要使其识别IPv6地址很简单,只需要把对应的表达式换成识别IPv6的即可,在网上查询一下很快便找到了一个相关讨论:
IPv6 addresses: zero compressed IPv6 addresses (section 2.2 of rfc5952) link-local IPv6 addresses with zone index (section 11 of rfc4007) IPv4-Embedded IPv6 Address (section 2 of rfc6052) IPv4-mapped IPv6 addresses (section 2.1 of rfc2765) IPv4-translated addresses (section 2.1 of rfc2765) IPv6 Regular Expression:
# determine if IP is IPv4 or IPv6, Args: $1=IP getIPtype () { local ip="$1" local ipv4_regex="^([0-9]{1,3}\.){3}[0-9]{1,3}$" local ipv6_regex="(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))" if [[ $ip =~ $ipv4_regex ]]; then echo "ipv4" elif [[ $ip =~ $ipv6_regex ]]; then echo "ipv6" else echo "invalid" fi }
# Args: $1=IP banIPv4 () { local ip="$1" if ! nft list set inet fw4 ${firewallChain}_v4 >/dev/null 2>/dev/null ; then logLine 1 "Creating nft set ${firewallChain}_v4" nft create set inet fw4 ${firewallChain}_v4 { type ipv4_addr \; flags interval \; auto-merge \; } fi
for x in $firewallHookChains ; do chain="${x%:*}" ; position="${x#*:}" if [ $position -ge 0 ] && ! nft -a list chain inet fw4 $chain 2>/dev/null | grep -qE "\\t+ip saddr @${firewallChain}_v4 $firewallTarget" ; then logLine 1 "Inserting IPv4 hook into nft chain $chain" if [ $position = 0 ] ; then _hl=$(nft -a list chain inet fw4 $chain | sed -En 's/\t*ip saddr @${firewallChain}_v4 $firewallTarget # handle //p' | tail -n1) if [ -z "$_hl" ] ; then nft add rule inet fw4 $chain ip saddr @${firewallChain}_v4 $firewallTarget else nft insert rule inet fw4 $chain handle $_hl ip saddr @${firewallChain}_v4 $firewallTarget ; fi else nft insert rule inet fw4 $chain index $((position-1)) ip saddr @${firewallChain}_v4 $firewallTarget fi ; fi done if [ -z "$(nft list set inet fw4 ${firewallChain}_v4 | grep ${ip})" ] ; then logLine 1 "Inserting IP $ip into nft set ${firewallChain}_v4" nft add element inet fw4 ${firewallChain}_v4 {$ip} else logLine 2 "banIPv4() IP $ip already in nft set ${firewallChain}_v4" fi }
banIPv6 () { local ip="$1" if ! nft list set inet fw4 ${firewallChain}_v6 >/dev/null 2>/dev/null ; then logLine 1 "Creating nft set ${firewallChain}_v6" nft create set inet fw4 ${firewallChain}_v6 { type ipv6_addr \; flags interval \; auto-merge \; } fi for x in $firewallHookChains ; do chain="${x%:*}" ; position="${x#*:}" if [ $position -ge 0 ] && ! nft -a list chain inet fw4 $chain 2>/dev/null | grep -qE "\\t+ip6 saddr @${firewallChain}_v6 $firewallTarget" ; then logLine 1 "Inserting IPv6 hook into nft chain $chain" if [ $position = 0 ] ; then _hl=$(nft -a list chain inet fw4 $chain | sed -En 's/\t*ip6 saddr @${firewallChain}_v6 $firewallTarget # handle //p' | tail -n1) if [ -z "$_hl" ] ; then nft add rule inet fw4 $chain ip6 saddr @${firewallChain}_v6 $firewallTarget else nft insert rule inet fw4 $chain handle $_hl ip6 saddr @${firewallChain}_v6 $firewallTarget ; fi else nft insert rule inet fw4 $chain index $((position-1)) ip6 saddr @${firewallChain}_v6 $firewallTarget fi ; fi done if [ -z "$(nft list set inet fw4 ${firewallChain}_v6 | grep ${ip})" ] ; then logLine 1 "Inserting IP $ip into nft set ${firewallChain}_v6" nft add element inet fw4 ${firewallChain}_v6 {$ip} else logLine 2 "banIPv6() IP $ip already in nft set ${firewallChain}_v6" fi }
banIP () { local ip="$1" local type=$(getIPtype "$ip") case $type in ipv4) banIPv4 "$ip" ;; ipv6) banIPv6 "$ip" ;; *) logLine 1 "Error: banIP() invalid IP address ($ip)" ;; esac }
函数unBanIP
下面是对主要函数unBanIP 的分析和修改:
1 2 3 4 5 6 7 8 9 10 11
# Args: $1=IP unBanIP () { local ip="$1" _hl=$(nft -a list chain inet fw4 $firewallChain 2>/dev/null | sed -n "/ saddr $ip /{s|\t*ip saddr $ip .*$firewallTarget # handle ||p;q}") if [ "$_hl" ] ; then logLine 1 "Removing ban rule for IP $ip from nft" nft delete rule inet fw4 bearDropper handle $_hl else logLine 3 "unBanIP() Ban rule for $ip not present in nft" fi }
# Args: $1=IP unBanIPv4 () { local ip="$1" if ! nft delete element inet fw4 ${firewallChain}_v4 {$ip} >/dev/null 2>/dev/null ; then logLine 3 "unBanIP() $ip not present in nft set ${firewallChain}_v4" else logLine 1 "Removing IP $ip from nft set" fi }
unBanIPv6 () { local ip="$1" if ! nft delete element inet fw4 ${firewallChain}_v6 {$ip} >/dev/null 2>/dev/null ; then logLine 3 "unBanIP() $ip not present in nft" else logLine 1 "Removing IP $ip from nft set ${firewallChain}_v6" fi }
函数wipeFirewall
对主要函数wipeFirewall 的分析和修改:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
wipeFirewall () { local x chain position for x in $firewallHookChains ; do chain="${x%:*}" ; position="${x#*:}" if [ $position -ge 0 ] ; then _hl=$(nft -a list chain inet fw4 $chain 2>/dev/null | sed -nE "s/\\t+jump $firewallChain # handle //p") if [ "$_hl" ] ; then logLine 1 "Removing hook from nft chain $chain" nft delete rule inet fw4 $chain handle $_hl fi ; fi done if nft list chain inet fw4 $firewallChain >/dev/null 2>/dev/null ; then logLine 1 "Flushing and removing nft chain $firewallChain" nft flush chain inet fw4 $firewallChain 2>/dev/null nft delete chain inet fw4 $firewallChain 2>/dev/null fi }
这个函数主要是在程序升级,退出,或者手动执行wipe的情况下,对已有链和IP drop规则的清楚,改起来也是针对相应的chain和rule进行修改,改为我们的set和delete elment即可,我们需要注意原来的每个chain对应一个$firewallChain,而我们修改后是有v4和v6两个set的,需要分别获取对应的handle,然后delete rule by handle,最后flush set,delete set即可。修改后的函数:
wipeFirewall () { local x chain position for x in $firewallHookChains ; do chain="${x%:*}" ; position="${x#*:}" if [ $position -ge 0 ] ; then _hl4=$(nft -a list chain inet fw4 $chain 2>/dev/null | sed -nE "s/\\t+ip saddr @${firewallChain}_v4 $firewallTarget # handle //p") _hl6=$(nft -a list chain inet fw4 $chain 2>/dev/null | sed -nE "s/\\t+ip6 saddr @${firewallChain}_v6 $firewallTarget # handle //p") if [ "$_hl4" ] ; then logLine 1 "Removing IPv4 hook from nft chain $chain" nft delete rule inet fw4 $chain handle $_hl4 fi if [ "$_hl6" ] ; then logLine 1 "Removing IPv6 hook from nft chain $chain" nft delete rule inet fw4 $chain handle $_hl6 fi fi done if nft list set inet fw4 ${firewallChain}_v4 >/dev/null 2>/dev/null ; then logLine 1 "Flushing and removing nft set ${firewallChain}_v4" nft flush set inet fw4 ${firewallChain}_v4 2>/dev/null nft delete set inet fw4 ${firewallChain}_v4 2>/dev/null fi if nft list set inet fw4 ${firewallChain}_v6 >/dev/null 2>/dev/null ; then logLine 1 "Flushing and removing nft setß ${firewallChain}_v6" nft flush set inet fw4 ${firewallChain}_v6 2>/dev/null nft delete set inet fw4 ${firewallChain}_v6 2>/dev/null fi }
# Saves environment bddb entries to file, Arg: $1 = file to save in bddbSave () { local saveFile="$1.$2" fileType="$2" if [ "$fileType" = bddb ] ; then set | grep -E '^bddb_[a-f,A-F,0-9_]*=' | sed s/\'//g > "$saveFile" elif [ "$fileType" = bddbz ] ; then set | grep -E '^bddb_[a-f,A-F,0-9_]*=' | sed s/\'//g | gzip -c > "$saveFile" fi bddbStateChange=0 }
# Returns all IPs (not CIDR) present in records bddbGetAllIPs () { local ipRaw record set | grep -E '^bddb_[0-9_]*=' | tr \' \ | while read record ; do ipRaw=`echo $record | cut -f1 -d= | sed 's/^bddb_//'` if [ `echo $ipRaw | tr _ \ | wc -w` -eq 4 ] ; then echo $ipRaw | tr _ . fi done }