Usage:
Run the script periodically using a cron job (Linux) or Task Scheduler (Windows) to handle blocking and removal of expired blocks.
Ensure paths to logs and other configurations are updated to reflect your actual setup.
<?php // Cloudflare API settings $apiKey = "your_cloudflare_api_key"; // Global API Key $authEmail = "your_cloudflare_email"; // Cloudflare account email $zoneID = "your_zone_id"; // Zone ID $logPath = "C:pathtoaccess.log"; // Path to Apache access log $blockLogPath = "C:pathtoblock.log"; // Path to log block actions $blockRemoveLogPath = "C:pathtoblock_remove.log"; // Path to log block removal actions $threshold = 10; // Number of 404s to trigger a block $timeWindowMinutes = 10; // Time window in minutes for analysis $blockDurationHours = 1; // Duration to block IP in hours // Current timestamp minus time window (in seconds) $timeWindow = time() - ($timeWindowMinutes * 60); // Function to log messages to a file function logMessage($message, $logFilePath) { $timestamp = date('Y-m-d H:i:s'); file_put_contents($logFilePath, "[$timestamp] $message" . PHP_EOL, FILE_APPEND); } // Function to check if an IP is already blocked function isIPBlocked($ip, $apiKey, $authEmail, $zoneID) { $url = "https://api.cloudflare.com/client/v4/zones/$zoneID/firewall/access_rules/rules?configuration[target]=ip&configuration[value]=$ip"; $headers = [ "Content-Type: application/json", "X-Auth-Key: $apiKey", "X-Auth-Email: $authEmail" ]; $options = [ 'http' => [ 'header' => implode("rn", $headers), 'method' => 'GET' ] ]; $context = stream_context_create($options); $result = file_get_contents($url, false, $context); if ($result === FALSE) { $error = "Failed to check if IP $ip is blocked"; logMessage($error, $logFilePath); echo "$errorn"; return false; } $response = json_decode($result, true); if (isset($response['result']) && count($response['result']) > 0) { return true; } return false; } // Function to send block request to Cloudflare function blockIPInCloudflare($ip, $apiKey, $authEmail, $zoneID, $blockLogPath) { $url = "https://api.cloudflare.com/client/v4/zones/$zoneID/firewall/access_rules/rules"; $data = [ "mode" => "block", "configuration" => [ "target" => "ip", "value" => $ip ], "notes" => "Fail2ban block" ]; $headers = [ "Content-Type: application/json", "X-Auth-Key: $apiKey", "X-Auth-Email: $authEmail" ]; $options = [ 'http' => [ 'header' => implode("rn", $headers), 'method' => 'POST', 'content' => json_encode($data) ] ]; $context = stream_context_create($options); $result = file_get_contents($url, false, $context); if ($result === FALSE) { $error = "Failed to block IP $ip"; logMessage($error, $blockLogPath); echo "$errorn"; return null; } $response = json_decode($result, true); if (isset($response['result']['id'])) { $success = "Successfully blocked IP $ip"; logMessage($success, $blockLogPath); // Save block rule ID and expiration time file_put_contents("C:pathtoblocked_ips.log", "$ip|{$response['result']['id']}|".(time() + ($blockDurationHours * 3600)).PHP_EOL, FILE_APPEND); return $response['result']['id']; } else { $error = "Failed to retrieve rule ID for IP $ip"; logMessage($error, $blockLogPath); echo "$errorn"; return null; } } // Function to remove block request from Cloudflare function removeIPFromCloudflare($blockRuleID, $apiKey, $authEmail, $zoneID, $blockRemoveLogPath) { $url = "https://api.cloudflare.com/client/v4/zones/$zoneID/firewall/access_rules/rules/$blockRuleID"; $headers = [ "Content-Type: application/json", "X-Auth-Key: $apiKey", "X-Auth-Email: $authEmail" ]; $options = [ 'http' => [ 'header' => implode("rn", $headers), 'method' => 'DELETE' ] ]; $context = stream_context_create($options); $result = file_get_contents($url, false, $context); if ($result === FALSE) { $error = "Failed to remove block rule $blockRuleID"; logMessage($error, $blockRemoveLogPath); echo "$errorn"; } else { $success = "Successfully removed block rule $blockRuleID"; logMessage($success, $blockRemoveLogPath); echo "$successn"; } } // Function to process access log and block IPs if necessary function processAccessLog($logPath, $apiKey, $authEmail, $zoneID, $blockLogPath) { global $timeWindow, $threshold; $ipCounts = []; if (($handle = fopen($logPath, "r")) !== FALSE) { while (($line = fgets($handle)) !== FALSE) { preg_match('/[(.*?)] "(.*?)" (d{3})/', $line, $matches); if (count($matches) > 0) { $logTimestamp = strtotime($matches[1]); $statusCode = $matches[3]; if ($statusCode == '404' && $logTimestamp > $timeWindow) { $ip = preg_split('/s+/', $line)[0]; if (!isset($ipCounts[$ip])) { $ipCounts[$ip] = 1; } else { $ipCounts[$ip]++; } } } } fclose($handle); } foreach ($ipCounts as $ip => $count) { if ($count >= $threshold) { if (!isIPBlocked($ip, $apiKey, $authEmail, $zoneID)) { blockIPInCloudflare($ip, $apiKey, $authEmail, $zoneID, $blockLogPath); } else { logMessage("IP $ip is already blocked.", $blockLogPath); } } } } // Function to remove expired block rules function removeExpiredBlocks($blockLogPath, $apiKey, $authEmail, $zoneID, $blockRemoveLogPath) { $currentTime = time(); if (file_exists($blockLogPath)) { $lines = file($blockLogPath); foreach ($lines as $line) { list($ip, $blockRuleID, $expireTime) = explode('|', trim($line)); if ($currentTime >= $expireTime) { removeIPFromCloudflare($blockRuleID, $apiKey, $authEmail, $zoneID, $blockRemoveLogPath); // Remove or comment out the line from the log file file_put_contents($blockLogPath, str_replace($line, '', file_get_contents($blockLogPath))); } } } } // Execute the functions processAccessLog($logPath, $apiKey, $authEmail, $zoneID, $blockLogPath); removeExpiredBlocks("C:pathtoblocked_ips.log", $apiKey, $authEmail, $zoneID, $blockRemoveLogPath); ?>