Sven Schwedas wrote in
<[email protected]>:
|
|> 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)