Advertisement
dualarrow

CrowdSec php Bouncer

Nov 21st, 2024 (edited)
483
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 6.10 KB | Source Code | 0 0
  1. #!/usr/bin/php
  2. <?php
  3. // Configuration
  4. define('CROWDSEC_API_URL', 'http://127.0.0.1:8080/v1/decisions'); // Adjust CrowdSec API URL
  5. define('CROWDSEC_API_KEY', 'api-key-here'); // Replace with your CrowdSec API key
  6. define('FIREWALLD_IPSETS_DIR', '/etc/firewalld/ipsets/');
  7. define('IPSET_NAME_IPV4', 'crowdsec-banned-ipv4');
  8. define('IPSET_NAME_IPV6', 'crowdsec-banned-ipv6');
  9. define('LOG_FILE', '/var/log/php-bouncer.log');
  10. define('SLEEP_INTERVAL', 60); // Set the interval for the service in seconds
  11.  
  12. // Ensure this script is run from the command line
  13. //if (php_sapi_name() !== 'cli') {
  14. //    die("This script can only be run from the command line.\n");
  15. //}
  16.  
  17. // Function to fetch decisions from CrowdSec
  18. function fetchDecisions() {
  19.     $ch = curl_init();
  20.     curl_setopt($ch, CURLOPT_URL, CROWDSEC_API_URL);
  21.     curl_setopt($ch, CURLOPT_HTTPHEADER, [
  22.         'X-Api-Key: ' . CROWDSEC_API_KEY,
  23.         'Accept: application/json'
  24.     ]);
  25.  
  26.     $userAgent = "Mozilla/5.0 (compatible; MyCustomUserAgent/1.0)";
  27.     curl_setopt($ch, CURLOPT_USERAGENT, $userAgent);
  28.  
  29.     curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  30.  
  31.     $response = curl_exec($ch);
  32.     if (curl_errno($ch)) {
  33.         echo "Curl error: " . curl_error($ch);
  34.         return [];
  35.     }
  36.  
  37.     curl_close($ch);
  38.     return json_decode($response, true);
  39. }
  40.  
  41. // Function to separate IPv4 and IPv6 addresses
  42. function separateIpAddresses($decisions) {
  43.     $ipv4Addresses = [];
  44.     $ipv6Addresses = [];
  45.  
  46.     foreach ($decisions as $decision) {
  47.         $ip = $decision['value'];
  48.         if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
  49.             $ipv4Addresses[] = $ip;
  50.         } elseif (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
  51.             $ipv6Addresses[] = $ip;
  52.         }
  53.     }
  54.  
  55.     return [$ipv4Addresses, $ipv6Addresses];
  56. }
  57.  
  58. // Function to load existing ipset entries from an XML file
  59. function loadIpset($ipsetName) {
  60.     $ipsetFile = FIREWALLD_IPSETS_DIR . $ipsetName . '.xml';
  61.     $ipAddresses = [];
  62.  
  63.     if (file_exists($ipsetFile)) {
  64.         $xml = simplexml_load_file($ipsetFile);
  65.         foreach ($xml->entry as $entry) {
  66.             $ipAddresses[] = (string) $entry;
  67.         }
  68.     }
  69.  
  70.     return $ipAddresses;
  71. }
  72.  
  73. // Function to update firewalld ipset file only if changes are needed
  74. function updateIpsetIfNeeded($ipAddresses, $ipsetName, $type, $iptype) {
  75.     $existingIps = loadIpset($ipsetName);
  76.     sort($ipAddresses);
  77.     sort($existingIps);
  78.  
  79.     // Check if there are any changes
  80.     if ($ipAddresses === $existingIps) {
  81.         echo "No changes needed for $ipsetName.\n";
  82.         return false;
  83.     }
  84.  
  85.     // Create new XML structure
  86.     $xml = new SimpleXMLElement('<ipset></ipset>');
  87.     $xml->addAttribute('type', $type); // 'hash:ip' for IPv4, 'hash:net' for IPv6
  88.  
  89.     $opt = $xml->addChild('option');
  90.     $opt->addAttribute('name', 'family');
  91.     $opt->addAttribute('value', $iptype);
  92.  
  93.     $opt = $xml->addChild('option');
  94.     $opt->addAttribute('name', 'hashsize');
  95.     $opt->addAttribute('value', '4096');
  96.  
  97.     $opt = $xml->addChild('option');
  98.     $opt->addAttribute('name', 'maxelem');
  99.     $opt->addAttribute('value', '1000000');
  100.  
  101.     foreach ($ipAddresses as $ip) {
  102.         $entry = $xml->addChild('entry');
  103.         $entry[0] = $ip;
  104.     }
  105.  
  106.     // Save XML to ipset file
  107.     $ipsetFile = FIREWALLD_IPSETS_DIR . $ipsetName . '.xml';
  108.     $xml->asXML($ipsetFile);
  109.     echo "Updated $ipsetName with new entries.\n";
  110.     return true;
  111. }
  112.  
  113. // Function to reload firewalld to apply changes
  114. function reloadFirewalld() {
  115.     exec('firewall-cmd --reload', $output, $status);
  116.     if ($status !== 0) {
  117.         echo "Error reloading firewalld: " . implode("\n", $output) . "\n";
  118.     }
  119. }
  120.  
  121. function Run()
  122. {
  123.     // Main script logic
  124.     $decisions = fetchDecisions();
  125.     if (!empty($decisions)) {
  126.         list($ipv4Addresses, $ipv6Addresses) = separateIpAddresses($decisions);
  127.  
  128.         // Track if any changes were made
  129.         $ipv4Updated = !empty($ipv4Addresses) ? updateIpsetIfNeeded($ipv4Addresses, IPSET_NAME_IPV4, 'hash:ip', 'inet') : false;
  130.         $ipv6Updated = !empty($ipv6Addresses) ? updateIpsetIfNeeded($ipv6Addresses, IPSET_NAME_IPV6, 'hash:net', 'inet6') : false;
  131. //$ipv4Updated = true;
  132. //$ipv6Updated = true;
  133.         // Reload firewalld only if there were changes
  134.         if ($ipv4Updated || $ipv6Updated) {
  135.             reloadFirewalld();
  136.             logMessage("firewalld reloaded with new CrowdSec decisions for IPv4 and IPv6.");
  137.         } else {
  138.             //logMessage("No changes detected. firewalld reload not required.");
  139.         }
  140.     }
  141.     else {
  142.         logMessage("No decisions to update.");
  143.     }
  144. //    logMessage("Waiting for next iteration");
  145. //break;
  146. }
  147.  
  148.  
  149. // Flag to keep the service running
  150. $running = true;
  151.  
  152. // Function to handle signals
  153. function signalHandler($signal) {
  154.     global $running;
  155.     switch ($signal) {
  156.         case SIGTERM:
  157.         case SIGINT:
  158.             logMessage("Received termination signal. Stopping...");
  159.             $running = false;
  160.             break;
  161.     }
  162. }
  163.  
  164. // Attach signal handlers
  165. pcntl_signal(SIGTERM, "signalHandler");
  166. pcntl_signal(SIGINT, "signalHandler");
  167.  
  168. // Function to log messages to a file
  169. function logMessage($message) {
  170.     file_put_contents(LOG_FILE, date('Y-m-d H:i:s') . " - $message\n", FILE_APPEND);
  171. }
  172.  
  173. // The main service function that runs the program logic
  174. function service($isService = false) {
  175.     global $running;
  176.  
  177.     logMessage("Service started.");
  178.  
  179.     do {
  180.         run();
  181.         // Check for signals to allow graceful shutdown
  182.         pcntl_signal_dispatch();
  183.  
  184.         // If running as a service, sleep for the specified interval before repeating
  185.         if ($isService && $running) {
  186.             sleep(SLEEP_INTERVAL);
  187.         }
  188.     } while ($isService && $running);
  189.  
  190.     logMessage("Service stopped.");
  191. }
  192.  
  193. function isRunningAsService() {
  194.     global $argv;
  195.     return in_array('--service', $argv);
  196. }
  197.  
  198. //chdir('/root');
  199. // Check if running from the command line or as a systemd service
  200. if (isRunningAsService()) {
  201.     service(true);
  202. } else {
  203.     service(false);
  204. }
  205. ?>
  206.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement