I like iptables very much for IP blocking!
I use iptables as the key to my firewalling against a variety of threats. In fact, I can use it for anything that can be identified in a system log with a pattern match - and it works very well for me. I'm doing it on two Spry Webmin 400 Fedora 4 servers. It keeps traffic, bandwidth, resource use and log size very manageable.
The other key item in this scenario is pop-before-smtp (popbsmtp.sourceforge.net), a very nice, small, perl application normally used to validate users to my smtp server after they successfully validate to the pop (or imap) server - i.e. in order to send mail, they need to succeed in logging in to collect it. It comes, out of the box, with a config file full of sample patterns to match nearly any mail server software (my favorite is postfix) and also another file showing how it can be used for blocking IPs instead. I adapted it for use on my Spry servers, so if you're not using Fedora Core 4 it may need some changes to adapt to your own versions of logging and iptables.
Basically, I installed the pop-before-smtp daemon according to the instructions, then configure a config file and startup script for each application (e.g. ssh login attacks.) Sounds tough perhaps but it is really very simple! I call my first config file /etc/ip-blocking-conf.pl and the uncommented lines are:
Code:
use strict;
use vars qw(
$pat $out_pat $write $flock $debug $reprocess $grace $logto %file_tail
@mynets %db $dbfile $dbvalue
$mynet_func $tie_func $flock_func $add_func $del_func $sync_func
$tail_init_func $tail_getline_func $log_func
$PID_pat $IP_pat $OK_pat $FAIL_pat $OUT_pat $cmdformat
);
$cmdformat = 'iptables %s INPUT -s %s -j REJECT';
$dbfile = '/var/lib/ip-blocking'; #### PERSONALIZED! ####
$flock = 0;
# A 3-minute IP-blocking period before the IP address is expired.
$grace = 3*60;
$file_tail{'name'} = '/var/log/messages'; #### PERSONALIZED! ####
# This is for catching login errors on SSHD only. #### PERSONALIZED! ####
$pat = '^(... .. ..:..:..).+Failed password.+from\s+(\d+\.\d+\.\d+\.\d+)';
$mynet_func = \&mynet_ipblock;
$add_func = \&add_ipblock;
$del_func = \&del_ipblock;
sub mynet_ipblock ## Don't block my own IP or one of my clients' ranges
{
'127.0.0.0/8 12.207.218.33 64.55.222.0/24'; #### PERSONALIZED! ####
}
sub add_ipblock
{
my($ip) = @_;
$db{$ip} = $dbvalue;
system(sprintf($cmdformat, '-A', $ip));
}
sub del_ipblock
{
my($ip) = @_;
system(sprintf($cmdformat, '-D', $ip));
delete $db{$ip};
}
1; ## THIS LINE MUST REMAIN LAST IN THE FILE! ##
I marked the few lines that are unique for this instance. Note I have excluded (or whitelisted) a couple of addresses or ranges, and that I set the timeout to three minutes instead of the default 30. This is because result of passing pattern test is that the offending IP is rejected upon initial attempt to connect to the server in any way. As far as they can tell, the machine is offline or crashed. For robots, it is a great deterrent. For clients, three minutes is a minor but necessary inconvenience. For very persistent hackers, it will limit their number of guess attempts to a very few every three or four minutes. I went out looking for this system at a time when the guesses logged were hundreds per second! Life is better today!
OK, the next (last) item in this system is the startup script. I'd like this to run every time the machine boots up, so I put it in the init.d folder: /etc/init.d/pop-before-ip-blocking and the uncommented lines are (though only the first five are personalized for this daemon)
Code:
progname=pop-before-ip-blocking #### PERSONALIZED! ####
pgm=/usr/sbin/pop-before-smtp
pid=/var/adm/$progname.pid
conf=/etc/ip-blocking-conf.pl #### PERSONALIZED! ####
test -d /var/run && pid=/var/run/$progname.pid
if test -f $conf; then
conf=--config=$conf
fi
die(){ echo "$progname: $*">&2; exit 1; }
case "$1" in
start)
echo -n "Starting $progname: "
$pgm $conf $dbfile $watchlog $logto --daemon=$pid
if test $? -eq 0; then
echo done
else
echo failed
fi
;;
stop)
echo -n "Stopping $progname: "
p=`cat $pid 2>/dev/null`; test -n "$p" && (
kill $p || exit 0; sleep 1
kill -9 $p 2>/dev/null || exit 0; sleep 1
kill -0 $p && die "$pid won't die"
)
if test $? -eq 0; then
rm -f $pid
echo done
else
echo failed
fi
;;
restart)
$0 stop
$0 start
;;
status)
p=`cat $pid 2>/dev/null`
test -n "$p" || die "no pidfile for $pgm"
kill -0 $p || die "$pgm[$p] is no longer running"
ps -fp $p
;;
*)
die "Usage: `basename $0` {start|stop|restart|status}"
;;
esac
That's it! Start it manually by giving the shell command
Code:
/etc/init.d/pop-before-ip-blocking start
or restart, or stop. Upon start, you are now protected from the common dictionary sshd attacks, as logged in /var/log/messages.
Additional building blocks to my firewall follow (only the unique parts shown though):
/etc/ip-blocking-pop-conf.pl
Code:
$dbfile = '/var/lib/ip-pop-blocking';
$file_tail{'name'} = '/var/log/maillog';
$pat = '^(... .. ..:..:..).+pop3d: LOGIN FAILED, user=.+\[.*(\d+\.\d+\.\d+\.\d+)\]';
/etc/init.d/pop-before-pop-blocking
Code:
progname=pop-before-pop-blocking
conf=/etc/ip-blocking-pop-conf.pl
(Like I said, simple. Pop guessers are now banned for three minutes too.)
/etc/ip-blocking-secure-conf.pl
Code:
$dbfile = '/var/lib/ip-blocking-secure';
$file_tail{'name'} = '/var/log/secure';
$pat = '^(... .. ..:..:..).+Invalid user.+from\s+(\d+\.\d+\.\d+\.\d+)';
/etc/init.d/pop-before-secure-blocking
Code:
progname=pop-before-secure-blocking
conf=/etc/ip-blocking-secure-conf.pl
/etc/ip-blocking-smtp-conf.pl
Code:
$dbfile = '/var/lib/ip-smtp-blocking';
$file_tail{'name'} = '/var/log/maillog';
$pat = '^(... .. ..:..:..).+NOQUEUE: reject: RCPT from.+\[(\d+\.\d+\.\d+\.\d+)\]';
/etc/init.d/pop-before-smtp-blocking
Code:
progname=pop-before-smtp-blocking
conf=/etc/ip-blocking-smtp-conf.pl
This last is a favorite! It matches the pattern my mail system logs when a spammer is identified by one of various methods. I've constructed an elaborate inhouse mail and quarantine system using postfix, amavisd-new, spamassassin, razor, dccm, clamd, and openvispadmin (to give a graphical control panel to the mysql based user and quarantining system.) This last item keeps the dictionary mail attacks at bay, lightening server load immensely.
Anyhow, this is the essence of it. Very simple, small, lightweight and effective. Anything you can match a log pattern for can be blocked by adding a new config/startup pair, thinking up a new name for each. I name mine such that they alpha-list together, they can be anything you like. Then just identify the log and the pattern, and point a startup script at it and you're ready to go.
I added a couple of maintenance items as well. In /etc/bashrc I added the alias
Code:
alias blocked='iptables -L -n'
so I can simply type 'blocked' in my shell window to generate a list of currently blocked IP's, usually between a dozen and four dozen. Once I found a very long list, I guess the unblock part of the script had stalled out. I restarted my daemons and restored sanity (it erases the blocked list and starts fresh) and then wrote the same commands to an alias in /etc/bashrc:
Code:
alias unblock='/etc/init.d/pop-before-smtp-blocking restart;
/etc/init.d/pop-before-pop-blocking restart;
/etc/init.d/pop-before-ip-blocking restart;
/etc/init.d/pop-before-secure-blocking restart'
so the 'unblock' command will restart all my daemons.
So! That's about the size of it. I began this project because, thankfully, when I originally provisioned this server the supplied firewall wasn't working well or something. I think my own (above) does more of what I need. It has been working fine for over a year now. I began writing about this because my server half-crashed yesterday (apache jammed up) simply due to Internet traffic - mxlogic.com showed spam traffic at 88.8%! This is incredible: given that legit traffic is likely constant, the difference between 90% spam and 80% spam is a DOUBLING of the total traffic on the 'net! My hope is that my system will be adopted by enough Spry customers (there's a big bunch of us) to positively impact this statistic globally. It should also save tons of CO2.