In my last post I talked about how to automatically add IP blocks to CloudFlare from your own server. I also talked about the problem that could lead to, which is potentially 1000s of IP blocks mounting up over time (leading to firewall performance issues, and hitting your CloudFlare IP block limit).
I mulled over the best solution to this for some time. Wondering if hooking into the ConfigServer Firewall ‘UNBLOCK REPORT’ notification was the best way to proceed – I decided it was not; for a number of reasons.
I reasoned that adding IPs to CloudFlare immediately they were known to be a threat was imperative, and that’s how that’s done (see previous post(s)), but their removal is far less pressing. So long as they are culled relatively regularly, no problem.
So I wrote a PHP script to be run as a root cron job, to query ALL IP blocks on CloudFlare, look at their creation date (‘created_on’ in CloudFlare API parameters) and if before some cut off, delete that block.
Here’s the PHP script:
1 <?php
2 // Read in all existing CloudFlare IP blocks then delete
3 // all which are older than some specified value
4
5 $authemail = "your_cloudflare@email_address.com";
6 $authkey = "your_cloudflare_auth_key";
7 $page = 1;
8 $ids = array(); // ids to block
9 $cutoff = time()-(3600*24*28); // 28 days
10
11 $CARRYON = true; $START = time();
12 while($CARRYON)
13 {
14 $ch = curl_init("https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules?mode=block&configuration_target=ip&page=$page&per_page=10&order=created_on&direction=asc&match=all");
15 curl_setopt($ch, CURLOPT_HTTPHEADER, array(
16 'X-Auth-Email: '.$authemail,
17 'X-Auth-Key: '.$authkey,
18 'Content-Type: application/json'
19 ));
20 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
21 $response = curl_exec($ch);
22 curl_close($ch);
23
24 $r = json_decode($response, true);
25
26 $result = $r['result'];
27
28 // Scan for results which were created BEFORE $cutoff
29 foreach ($result as $block)
30 {
31 // Only remove 'block' type rules
32 // And not if 'donotexpire' is in the notes
33 // for the rule
34 if (($block['mode'] == 'block') and (!preg_match("/donotexpire/is",$block['notes'])))
35 {
36 $blocktime = strtotime($block['created_on']);
37 if ($blocktime <= $cutoff)
38 {
39 $ids[] = $block['id'];
40 }
41 }
42 }
43
44 $info = $r['result_info'];
45
46 // Result info tells us how many pages in total there are
47 $page++;
48 if ($info['total_count'] < ($page*10))
49 {
50 $CARRYON = false;
51 }
52
53 // Max run time of 30 seconds
54 if ((time() - $START) > 30)
55 {
56 $CARRYON = false;
57 }
58 }
59
60 $log = '';
61
62 foreach ($ids as $id)
63 {
64 // Delete this rule
65 $ch = curl_init("https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules/$id");
66 curl_setopt($ch, CURLOPT_HTTPHEADER, array(
67 'X-Auth-Email: '.$authemail,
68 'X-Auth-Key: '.$authkey,
69 'Content-Type: application/json'
70 ));
71 curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
72 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
73 $response = curl_exec($ch);
74 curl_close($ch);
75
76 $log .= $response . "\n";
77 }
78
79 if (1) //(sizeof($ids)>0)
80 {
81 mail($authemail, "info@aetherweb.co.uk", "CF UNBLOCK REPORT " . date('r'), $log);
82 }
I chose to set a longevity of 28 days for all IP blocks. I also gave myself the option to put a note in the CloudFlare rule to make it persist. You can see in the code above ‘donotexpire’. If that string is found in the notes of any rule on CloudFlare, it will not be expired/deleted.
All you need to do then is queue up the script to be run periodically and it will do so, expire old IP blocks, and email you a very rough report if any blocks are implemented.
Here’s the crontab line for this that I’m using:
# Periodically cull IPs from CloudFlare 00 04 * * * root php -f /mypath/mypath/cf_expire_ips.php
The script can be saved anywhere you like on your server. Perhaps set up a new folder for the purpose. Call it something appropriate and give it the extension “.php”. Make sure to set the permissions such that the root user can execute it – ‘700’ should suffice.
/myscripts/expire_cf_ips.php
To schedule this script to be run periodically you need to edit the file:
/etc/crontab
And add the line as described above which will cause the script to be executed at 04:00 every day.
I recommend adding this line in the top of the /etc/crontab file so that error reporting is sent to your choice of email address:
MAILTO=you@yourdomain.com