Sven Schwedas wrote in <1d9d281d-de64-8299-2bed-35fc9889f...@tao.at>: | |> What is the recommended way to combat this behavior? | |I'd personally lean towards fail2ban or comparable solutions to |aggregate Rejects with other suspicious behaviour on other ports and |react with system-wide IP bans. | |Fail2ban e.g. has examples for catching REJECTs in its wiki: |http://www.fail2ban.org/wiki/index.php/Postfix |Plus built-in modules to handle Postfix SASL login failures and others. |I expect the competition is doing the same.
I use firewall rules to assign traffic control dependent on the hit count if fwcore_has_i smtp || fwcore_has_i smtps || fwcore_has_i submission; then change_chain i__smtp if [ -n "${FWCORE_SMTPX_NOLIMIT_PEERS}" ]; then for i in ${FWCORE_SMTPX_NOLIMIT_PEERS}; do Whitelisting some which contribute most to my traffic. Whitelisted also in graylist. (I hate i need two different lists.) if ipaddr_split a "${i}"; then if fwcore_has_i smtp; then [ -z "${port}" -o "${port}" = smtp ] && add_rule -p tcp --src ${addr}${mask} \ --dport ${p_smtp} -m limit --limit 60/m -j f_m0_2 fi if fwcore_has_i smtps; then [ -z "${port}" -o "${port}" = smtps ] && add_rule -p tcp --src ${addr}${mask} \ --dport ${p_smtps} -m limit --limit 60/m -j f_m0_2 fi #if fwcore_has_i submission; then # [ -z "${port}" -o "${port}" = submission ] && # add_rule -p tcp --src ${addr}${mask} \ # --dport ${p_smtps} -m limit --limit 60/m -j f_m0_2 #fi fi done fi #-m recent --name alien --set # Alienization now handled by cron-parse-mail.awk # -m recent --name alien --set add_rule -m recent --name smtp --set \ -m recent --name smtp ! --rcheck --seconds 600 --reap --hitcount 20 \ -j f_m2 add_rule -m recent --name smtp --rcheck --seconds 120 --hitcount 16 \ -j f_m5 add_rule -m recent --name smtp ! --rcheck --hitcount 32 -j f_m3 add_rule -j f_m5 fi f_m2 is second best (VPN and ssh are best), f_m5 is the slowest that is possible (1 percent of "max"). Ie conn/marks are set, traffic control is applied. (This is a private server with only a high 3-digit number of messages a day, the remains is noise.) There are "alien"s which seem strange, and after appearing like this multiple times they become "super_alien"s which are blocked for quite some hours. I found it impossible, really, to do this automatically from within the firewall for SMTP (and HTTP), because the firewall just does not know enough to make a decision. Therefore i had to bite the bullet and finally wrote a primitive log parser: #!/usr/bin/awk -f #@ Parse postfix log output, as via #@ exec /root/bin/cron-parse-mail.awk < /var/log/mail # DEBUG: 1=logger(1), >1=SANDBOX BEGIN{ DEBUG = 0; sl = ""; xl = "" } function doit(line){ if((i = match(line, "\\[[^]]+\\]$")) != 0){ j = substr(line, i + 1) j = substr(j, 1, length(j) - 1) if(!drops[j]){ i = maydrops[j] if(!i) i = 0 else if(i >= 2){ drops[j] = 1 if((i = match(line, " [^ ]+$")) != 0) j = substr(line, i + 1) if(sl) sl = sl " " sl = sl j return } maydrops[j] = ++i } } } # To avoid that "unknown" that tries evil is not blocked because we think # it is only a local DNS error, treat logins special /too many errors after AUTH from.+\[/ {doit($0); next;} # ] /SSL_accept error from.+\[/ { # ] line = $0 if((i = match(line, ": -?[[:alnum:]]+$")) != 0) line = substr(line, 1, i - 1) doit(line) next } /too many errors.+from unknown\[/{ if((i = match($0, "\\[[^]]+\\]$")) != 0){ j = substr($0, i + 1) j = substr(j, 1, length(j) - 1) if(unign[j]) next if(!drops[j]){ i = maydrops[j] if(!i) i = 0 else if(i >= 2){ # Could be local resolver error, try this first This only because my ISP gives me bind and powerdns via two different IPs after bind alone started producing errors after i have finally turned on dnssec in my dnsmasq cache. Ie bind failed a lot for FreeBSD (maybe truncation i have no idea), and the other fails for other things (i am no longer looking), anyhow if all else fails we check Google DNS. if(DEBUG > 1) es = 1 else es = system("{ command -v host && \ host " j " 8.8.8.8 || \ nslookup " j " 8.8.8.8; } >/dev/null 2>&1") if(es == 0){ unign[j] = 1 if(xl) xl = xl " " xl = xl j }else drops[j] = 1 next } maydrops[j] = ++i } } next } # vim ] # nawk cannot escape newlines /too many errors after (EHLO|END-OF-MESSAGE|HELO|STARTTLS|UNKNOWN) from.+\[/{ Note i removed RCPT| because of gray listing, and RCPT just does not occur here for any other reason. (I have super low error counts.) doit($0) } # vim ] END{ dropno = 0 ipl = "" for(ip in drops){ if(!drops[ip]) continue ++dropno ipl = ipl " " ip } if(dropno > 0){ if(DEBUG > 1) print "/root/bin/net-qos.sh add alien_super " ipl else system("/root/bin/net-qos.sh add alien_super " ipl) if(DEBUG > 0){ if(sl) sl = "; Named: " sl if(xl) xl = "; local DNS error: " xl if(DEBUG > 1) print "logger -t /root/bin/cron-parse-mail.awk '" dropno \ " aliens" sl xl "'" else system("logger -t /root/bin/cron-parse-mail.awk '" dropno \ " aliens" sl xl "'") } } } # s-sh-mode It is primitive as it does not sit on the thing with inotify or similar mechanism, and then only parses new stuff, but always reads the entire file. But the logs get rotated when the file reaches 200 KiB so awk processes these small files fast (real 0m 0.03s even on that vserver), and no bad effect there is. It runs several times an hour. Works for me. --steffen | |Der Kragenbaer, The moon bear, |der holt sich munter he cheerfully and one by one |einen nach dem anderen runter wa.ks himself off |(By Robert Gernhardt)