VPOP3 will automatically block IP addresses which repeatedly try to log in with an incorrect username or password. However, to prevent blocking users who accidentally enter the wrong details for too long, this usually just blocks the IP address for a few minutes (the default is 30 minutes).
There is an online database at https://www.abuseipdb.com/ which is used by many organisations to report abusive IP addresses. This database has an API which is free for low usage (1000 or 3000 calls a day). The data from multiple users is amalgamated to give a confidence of abuse from a particular IP address. That means that if just one or two services report problems, it could be accidental failed logins or similar, so we can ignore it, but if many report problems it is more likely to be deliberate attacks, so we can take action.
This article is about using the abuseIPDB database to identify failed login attempts to VPOP3 and then blocking those IP addresses for longer periods. We will use some Lua scripting to report failed logins to abuseIPDB then utilise the ‘GeoIP’ facility in VPOP3 to block those IP addresses that are rated at ‘100%’ by abuseIPDB.
This script requires VPOP3 Enterprise v8.1 or later.
Sign up to abuseIPDB
The first step is to sign up for an abuseIPDB account. For most VPOP3 users, their free account will suffice. You can increase the free account’s 1000 API-calls-a-day limit to 3000 API calls a day if you have your own website and change the account to a ‘webmaster’ account.
To sign up for an abuseIPDB account, go to https://www.abuseipdb.com/pricing
Once you have the account, you can go to your account settings and sign up as a ‘webmaster’ user if you wish – just follow the instructions (it involves uploading a small file to a special location on your web server)
You will now need to generate an API key so that our script can report and check data on IP addresses. To do this, in your abuseIPDB account, go to the ‘API’ tab, and click on ‘Create Key’. The key is a long alphanumeric string.
Create new database tables in VPOP3
We want to create a new database table in VPOP3 so that we can track when we have blocked IP addresses, so that they can be automatically be removed after a certain amount of time.
To do this, on your VPOP3 server, go to a command prompt and navigate to the VPOP3 directory, then type ‘psql’
In the psql prompt, type:
create table geoipv4_autoblocks (addr inet, dateadded timestamp with time zone default now());
create table abuseipdb (addr inet, dateadded timestamp with time zone default now());
geoipv4_autoblocks contains the IP address and when it was added to the block list. This will be used to automatically remove blocks after a specified time.
This creates two database tables for the script to use.
abuseipdb contains IP addresses and when they were reported to abuseIPDB, so that VPOP3 will only report addresses a maximum of once per day.
Create Lua Script
In the VPOP3 settings, go to Settings -> Scripts and choose the ‘Scheduler.lua’ script from the drop-down box. For this article we will assume that there is nothing in that script. If you already have functions in this script, then you may need to modify those by adding the script here, rather than replacing or adding it.
The Scheduler.lua script can contain functions which VPOP3 will periodically run in the background. We will use this to have VPOP3 check the failed logins log database table every five minutes and act accordingly.
In this script, copy and paste the function as below (again, if you already have a ‘Sched5Min’ function, you will need to modify it accordingly):
function Sched5Min() -- Script options abuseipdbApikey = "<your API key>"; notificationEmail = "<your email address>"; triggerCount = 3; purgeDays = 365; -- delete IP addresses from the abuseipdb table after 1 day VPOP3.PostgresQuery("delete from abuseipdb where dateadded < now() - interval '1 day';"); -- delete IP addresses from the blocklist after 'purgeDays' days -- use a local variable for the date to purge before rather than a PostgreSQL formula -- to avoid race condition purgeDate = os.date("%Y-%m-%d %H:%M:%S", os.time() - purgeDays * 24 * 60 * 60); VPOP3.PostgresQuery(string.format([[with x as (select addr from geoipv4_autoblocks where dateadded < '%s') delete from geoipv4 using x where addr=addrfrom and result='block';]], purgeDate)); VPOP3.PostgresQuery(string.format("delete from geoipv4_autoblocks where dateadded < '%s';", purgeDate)); -- Find IP addresses which have had more than triggerCount failed logins over the past day -- and have not yet been reported today x = VPOP3.PostgresQuery(string.format([[with x as (select ipaddress, username, count(*) cnt, min(datetime) as datetime from loginaudit where not result and datetime>now()-interval '1 day' group by ipaddress,username) select count(*) usercount, sum(cnt) failcount, ipaddress, min(datetime) as datetime from x where ipaddress not in (select addr from abuseipdb) group by ipaddress having count(*)>=%d limit 10;]], triggerCount)); -- Process database query results if x.Rows and x.Rows > 0 then for ind = 1,x.Rows do -- report the IP address to abuseipdb and get the address status res = VPOP3Net.PostHTTP("https://api.abuseipdb.com/api/v2/report", {key=abuseipdbApikey, ip=x[ind]["ipaddress"], categories="18", comment= string.format("Email Auth Brute force attack %d/%d in last day", x[ind]["failcount"], x[ind]["usercount"])}); print('Post Result ', res.ErrorCode, res.HTTPResult); if res.ErrorCode == 0 and type(res.Body[1]) == "string" then -- If the IP address status has a rating of 100% then do stuff if string.find(res.Body[1], "\"abuseConfidenceScore\":100") then -- Notify the administrator if an email address is specified if notificationEmail then VPOP3.SendMessage(notificationEmail, notificationEmail, string.format("Login Attack - %s", x[ind]["ipaddress"]), string.format([[Failed logins from %s https://www.abuseipdb.com/check/%s result = %s"]], x[ind]["ipaddress"], x[ind]["ipaddress"], res.Body[1])); end -- Add the IP address to the GeoIP table VPOP3.PostgresQuery(string.format("insert into geoipv4 (addrfrom, addrto, result) values('%s','%s', 'block');", x[ind]["ipaddress"], x[ind]["ipaddress"])); -- Add the IP address to the geoipv4_autoblocks table VPOP3.PostgresQuery(string.format("insert into geoipv4_autoblocks (addr) values('%s');", x[ind]["ipaddress"])); end -- if the HTTP Post to abuseIPDB succeeded, then remember that we have reported the IP address, -- so we don't do it again for another day VPOP3.PostgresQuery(string.format("insert into abuseipdb (addr) values('%s');", x[ind]["ipaddress"])); end end end end
You can change the first few lines of this function to meet your requirements. You MUST set the abuseIPDB API key and your email address as appropriate.
If you don’t want to receive notification emails, change the ‘notificationEmail’ line to:notificationEmail = nil;
The ‘triggerCount’ value is the number of different failed usernames attempted over the past day which will trigger notification. The ‘purgeDays’ value is how long IP addresses will be blocked for if blocked by this script.
Block IP addresses
The Lua script above will create ‘GeoIP’ entries in VPOP3 with the identifier of ‘block’ if it sees repeated failed login attempts and the IP address has a 100% rating on abuseIPDB.
To use these to actually block IP addresses from trying to log into VPOP3, we need to go to the appropriate Services in the VPOP3 settings, go to the ‘IP Access Restrictions’ tab and add a new restriction to ‘block’ addresses using GeoIP lookup where the search result is ‘block’
You can add this IP Access Restriction to multiple services as you wish – eg the POP3, SMTP and IMAP4 services