#!/usr/bin/env python3 from routeros_api import RouterOsApiPool import requests import ipaddress import datetime import sys # ======================= # CONFIGURATION # ======================= MIKROTIK_IP = "10.10.10.1" USER = "fail2ban" PASSWORD = "password" ADDRESS_LIST = "firehol_l1" TIMEOUT = "7d" FIREHOL_URL = "https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/firehol_level1.netset" # Whitelist (ALWAYS CIDR) WHITELIST = { "100.64.0.0/10" # Apartment CGNAT } # Safety cap MAX_NEW_ENTRIES = 5000 # ======================= # FUNCTIONS # ======================= def valid_net(net): try: n = ipaddress.ip_network(net, strict=False) if n.version != 4: return False if n.prefixlen < 10: return False if ( n.is_private or n.is_loopback or n.is_multicast or n.is_reserved or n.is_link_local ): return False return True except Exception: return False def is_whitelisted(net): try: n = ipaddress.ip_network(net, strict=False) for w in WHITELIST: wnet = ipaddress.ip_network(w, strict=False) if n.subnet_of(wnet) or n == wnet: return True return False except Exception: return False # ======================= # MAIN # ======================= print(f"[{datetime.datetime.now()}] FireHOL diff sync start") # Fetch FireHOL try: response = requests.get(FIREHOL_URL, timeout=30) response.raise_for_status() firehol_raw = response.text.splitlines() except Exception as e: print(f"ERROR: Failed to fetch FireHOL list: {e}") sys.exit(1) # Parse & filter FireHOL firehol = { net.strip() for net in firehol_raw if valid_net(net.strip()) and not is_whitelisted(net.strip()) } print(f"Parsed FireHOL entries after filtering: {len(firehol)}") # Connect to MikroTik API (RouterOS v7) try: api_pool = RouterOsApiPool( MIKROTIK_IP, username=USER, password=PASSWORD, plaintext_login=True ) api = api_pool.get_api() except Exception as e: print(f"ERROR: Failed to connect to MikroTik API: {e}") sys.exit(1) address_list = api.get_resource("/ip/firewall/address-list") # Fetch existing MikroTik entries current = { entry["address"] for entry in address_list.get(list=ADDRESS_LIST) } print(f"Current MikroTik entries: {len(current)}") # Diff calculation to_add = firehol - current print(f"New entries to add: {len(to_add)}") # Safety check if len(to_add) > MAX_NEW_ENTRIES: print("ERROR: Safety stop — too many new entries") api_pool.disconnect() sys.exit(1) # Add missing entries for net in sorted(to_add): address_list.add( list=ADDRESS_LIST, address=net, timeout=TIMEOUT, comment="FireHOL L1" ) api_pool.disconnect() print("FireHOL diff sync complete")