Automatically Expiring CloudFlare IP Blocks by Age

JeffTechnical Articles & Notes

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