I’ve not bothered with POP before SMTP authentication before but since v1.38 the script can take an optional config file that allows it to watch different log files in an easily customised fashion, which is great as I was wondering what the most efficient way of blocking automated ssh attempts was. I didn’t want to run a one minute cron job to parse the authlog file as it was clunky and most of the automated attacks I have seen are over in under two minutes or so. I did consider sticking a pipe in as the destination for the syslog data, but that would mean that if my code died then logging would be seriously compromised.

The best solution is to watch the file for modifications and then parse only the newest data, storing the matches in a database so that I don’t get multiple entries, which is pretty much what pop-before-smtp does, and it ships with an example non-POP3 config file ip-blocking-conf.pl which just needs some tweaks to fit in nicely with ipfilter on NetBSD. In my case, I’m not too bothered about removing the block: if someone tries non-existant usernames via ssh then they can vanish forever – a luxury of a home system that not many people visit 🙂 To that end, I not only want to implement the ban but also record the ban so that the blocking continues over a reboot: this is obviously dangerous and not recommended so I’ll show both the temporary and permanent versions.

File location (for stock NetBSD 3.0):

$dbfile = '/var/db/ip-blocking';
$file_tail{'name'} = '/var/log/authlog';

For testing, dump all activity to the terminal:

$debug = 1;
$logto = '-';

IPFilter differs from ipchains, so change the add and remove subroutines to pass the correct flags and change the order of the arguments:

sub add_ipblock
    my($ip) = @_;
    $db{$ip} = $dbvalue;
    system(sprintf($cmdformat, $ip, '-A'));
sub del_ipblock
    my($ip) = @_;
    system(sprintf($cmdformat, $ip, '-A -r'));
    delete $db{$ip};

Now change the command to be executed when a match is found, first for a temporary solution:

$cmdformat = 'echo "block in log quick from %s to any" | /sbin/ipf %s -f -';                       

A more permanent record:

$cmdformat = 'echo "block in log quick from %s to any" | /usr/bin/tee -a /etc/ipf.conf | /sbin/ipf %s -f -';                       

Note that when blocking permanently, it doesn’t make sense to have the rule removed after a timeout, so simply comment out the content of the del_ipblock like so:

sub del_ipblock
#    my($ip) = @_;
#    system(sprintf($cmdformat, $ip, '-A -r'));
#    delete $db{$ip};

To start the system off, simply run the script with the correct privilege level to access the database directory and log file:

sudo ./pop-before-smtp --config=./ipf-blocking.pl

I also have a problem with WordPress spam attacks, and have solved it for the most part by deleting the wp-trackback.php file from my site, but that doesn’t stop bots, obviously. What I also want to do is consign these machines to the bitbucket, as they are simply annoying and are often open relays, which will mean SMTP traffic is also likely to originate there. The drawback is that whilst the pattern match is configurable via the $pat variable, the main pop-before-smtp makes a fairly valid assumption that all log lines start with a syslog-style timestamp, which Apache doesn’t do. Without that extra match, the software would have a hard time just tracking new additions, so rather than change the source I decided to log some extra data from Apache, and then strip it out when I do my monthly log rotations. Note that I also log the virtual host in my Apache access_log file, so this strings might have an extra %v in it compared to normal:

LogFormat "%{%b %e %T}t %v %h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-Agent}i"" syslogcvh
CustomLog /var/log/httpd/access_log syslogcvh

This simply duplicates data (in a lossy fashion), and can be trimmed via a regexp or simple cut -b 17- command during normal log rotation/processing. The changes to the ip-blocking-conf.pl are pretty much as before, but if you want to run both at once it’s advisable to change the DB used to store the matched IP’s:

$dbfile = '/var/db/wp-ip-blocking';
$file_tail{'name'} = '/var/log/httpd/access_log';
$pat = '^(... .. ..:..:..)\s+(?:virtual.hostname.1|virtual.hostname.2)\s+(\d+\.\d+\.\d+\.\d+).+(POST )';

Note that the $pat above is very aggressive (and not WordPress specific): I have it set so that virtual.hostname.1 is the catch-all which is hit when a numeric IP is used to access my site, and virtual.hostanme.2 no-longer runs WordPress so any POST is invalid. Do NOT apply this to a live site, or no-one will be able to post a comment ! Extending the regexp to trap POST requests for WordPress files that arrive from an invalid Referer is left as an exercise for the reader[1]

One final warning, you must, must, MUST set your local subnet correctly in the mynet_ipblock or you will come unstuck. I have also added in an extra item or two for external systems that I trust so that I always have a route back in again should the worst happen:

sub mynet_ipblock
    # You'll probably want to edit this (it specifies IP ranges to ignore).

Note that running two such scanners does raise the chance that one IP will get blocked twice (most likely when the program is first started and old lines are being processed), in which case an error like 1:ioctl(add/insert rule): File exists may well be shown. This is a non-fatal error, and for tidiness the duplicate IP should be removed from /etc/ipf.conf if permanent logging has been chosen. If the block is temporary then the message can be safely ignored.

[1] Heh, memories of Dave Smallwood lectures still linger, although I think I need ‘trivial‘ in there somewhere…