Reporting Akismet identified spam using fail2ban

I use fail2ban to protect my server. I additionally report the IP addresses  to AbuseIPDB 

But Forum spam is a big problem as anyone with a blog will know

I have Akismet configured, which pretty much stops it all from ever appearing, but that doesn’t stop them from repeatedly posting spam.

I looked at various plugins but couldn’t find one that did what I wanted – and I certainly wasn’t going to pay for one.

So I’ve built my own system. It has two parts

  1. Report spam from the blog to a file that can be read by fail2ban
  2. Fail2ban reads the file and firewalls the IP address and then reports it to AbuseIPDB

Report spam from the blog to a file that can be read by fail2ban

Create a file somewhere on your Blog where it can be accessed via a url. lets call it logspam.php. This code has been developed for a networked blog site so we have to do a bit of work to get the right table name. Also note that if this file isn’t in the root of your blog directory you’ll need to change the require_once value

<?php
require_once __DIR__ . '/wp-load.php';
$prefix = $wpdb->prefix;
$table = $prefix."comments";
$site_title = trim(htmlspecialchars_decode(get_bloginfo( 'name' )));$sql="select max(comment_id) maxid from $table where comment_approved='spam' and comment_karma=0";
$r = $wpdb->get_row($sql, ARRAY_A);
$maxid=$r['maxid'];
Print date("d/m/Y H:i:s")." Doing $site_title \n";
if (is_null($maxid)) {
print date("d/m/Y H:i:s")." No Spam to process \n";
}
else {
$sql="select distinct comment_author_ip from $table where comment_approved='spam' and comment_karma=0";
$r2 = $wpdb->get_results($sql, ARRAY_A);
openlog("Wordpress_spam", LOG_PID | LOG_PERROR | LOG_NDELAY, LOG_LOCAL0);
foreach($r2 as $row){
$x=$row['comment_author_ip'];
print date("d/m/Y H:i:s"). " reporting spam IP address $x \n";
syslog(LOG_WARNING, "WP_SPAM_IP $x ");
}
closelog();
$sql= "update $table set comment_karma=-1 where comment_approved='spam' and comment_karma=0 and comment_id <= $maxid";
$r3 = $wpdb->get_results($sql, ARRAY_A);
}
Print date("d/m/Y H:i:s")." Completed $site_title \n";
print "------------------------\n";
?>

So this code checks to see if there are spammer IP addresses to report, gets a distinct list of them and reports them into the server syslog file with a line that looks like this

Jul 2 19:04:01 nyman WordPress_spam[2104013]: WP_SPAM_IP 78.46.108.24

Once it’s reported them it uses the old comment_karma field to mark that it’s reported it so it doesn’t get picked up if the code is run again before you’ve deleted the spam from the blog.

Then set up a cron job to call the reporting php file – you can capture the output of the cron call if you like – it produces something like this

------------------------
02/07/2024 19:04:01 Doing Steve's Blog
02/07/2024 19:04:01 reporting spam IP address 103.76.117.236
02/07/2024 19:04:01 reporting spam IP address 94.103.188.103
02/07/2024 19:04:01 reporting spam IP address 78.46.108.24
02/07/2024 19:04:01 reporting spam IP address 23.81.64.213
02/07/2024 19:04:01 Completed Steve's Blog
------------------------

Fail2ban reads the file and firewalls the IP address and then reports it to AbuseIPDB

Create a new filter file called wordpress_spam.conf in the filter.d folder

[Definition]
failregex = ^ .*. WP_SPAM_IP <ADDR>.$

Then create your new entry in jail.local file – this should all look pretty normal apart from the abuseipdb bit. Details on how to integrate AbuseIPDB with fail2ban have been written up by AbuseIPDB

[blog-spam]
enabled = true
port = http,https
filter = wordpress_spam
logpath = /var/log/syslog
#bantime = 172800
maxretry = 1
action = %(action_mwl)s
%(action_abuseipdb)s[abuseipdb_apikey="yourAPI key goes in here", abuseipdb_category="10",matches_comment="Forum Spam"]

then reload fail2ban using the fail2ban-client utility

When the cron runs the wget it writes to the syslog file which then gets picked up by fail2ban and you get emails

and reports in AbuseIPDB