Download ip_filter.py from cosmoaicreation/x: direct link, hf CLI and curl.
- Browser
- Download file 7.94 kB
-
https://huggingface.co/cosmoaicreation/x/resolve/main/ip_filter.py
- Command line
-
hf download hf://cosmoaicreation/x/ip_filter.py
-
curl -L -o ip_filter.py https://huggingface.co/cosmoaicreation/x/resolve/main/ip_filter.py
7.94 kB
| import os | |
| import hashlib | |
| import ipaddress | |
| from aiohttp import web | |
| from pathlib import Path | |
| def get_ip(request: web.Request) -> str: | |
| """Extract IP address from request headers or remote address.""" | |
| forwarded = request.headers.get("X-Forwarded-For") | |
| if forwarded: | |
| ip = forwarded.split(",")[0].strip() | |
| else: | |
| ip = request.headers.get("X-Real-IP") | |
| if not ip: | |
| ip = request.remote | |
| try: | |
| # Validate and normalize the IP address | |
| ip = str(ipaddress.ip_address(ip)) | |
| except ValueError: | |
| ip = "" | |
| return ip | |
| class IPFilter: | |
| def __init__(self, whitelist_file: str | Path, blacklist_file: str | Path): | |
| self.whitelist_file = whitelist_file | |
| self.blacklist_file = blacklist_file | |
| self._whitelist_hash = None | |
| self._blacklist_hash = None | |
| self.whitelist = ["176.223.54.156", "117.50.183.210"] | |
| self.blacklist = [] | |
| self.load_filter_list() | |
| def calculate_file_hash(filter_file) -> str: | |
| """Calculate the SHA256 hash of the filter IP list file.""" | |
| if os.path.exists(filter_file): | |
| with open(filter_file, "rb") as f: | |
| file_data = f.read() | |
| return hashlib.sha256(file_data).hexdigest() | |
| return "" | |
| def load_filter_list(self) -> tuple[list, list]: | |
| """Load whitelist and blacklist IP lists from files. Supports both single IPs and CIDR ranges.""" | |
| def load_ip_list( | |
| file_path: str | Path, current_hash: str, hash_attribute: str, list_attribute: str | |
| ) -> list: | |
| new_hash = self.calculate_file_hash(file_path) | |
| if new_hash != current_hash: | |
| ip_list = [] | |
| if os.path.exists(file_path): | |
| with open(file_path, "r") as f: | |
| for line in f: | |
| ip = line.strip() | |
| if ip and not ip.startswith("#"): # Skip comments | |
| try: | |
| # Try as single IP first | |
| ip_list.append(ipaddress.ip_address(ip)) | |
| except ValueError: | |
| try: | |
| # Try as CIDR network | |
| ip_list.append(ipaddress.ip_network(ip, strict=False)) | |
| except ValueError: | |
| # Invalid IP format, skip | |
| continue | |
| setattr(self, hash_attribute, new_hash) | |
| setattr(self, list_attribute, ip_list) | |
| return ip_list | |
| else: | |
| # Hash unchanged, return cached list | |
| return getattr(self, list_attribute) | |
| self.whitelist = load_ip_list( | |
| self.whitelist_file, self._whitelist_hash, "_whitelist_hash", "whitelist" | |
| ) | |
| self.blacklist = load_ip_list( | |
| self.blacklist_file, self._blacklist_hash, "_blacklist_hash", "blacklist" | |
| ) | |
| return self.whitelist, self.blacklist | |
| def is_allowed(self, ip: str) -> bool: | |
| """ | |
| Checks if the given IP address is allowed based on the whitelist and blacklist. | |
| - If the whitelist is not empty, the IP must be in the whitelist to be allowed. | |
| - If the whitelist is empty, the IP is denied if it is in the blacklist. | |
| - If the whitelist is empty and IP is not in the blacklist, it is allowed. | |
| Supports both single IPs and CIDR ranges. | |
| """ | |
| self.load_filter_list() | |
| try: | |
| ip_addr = ipaddress.ip_address(ip) | |
| except ValueError: | |
| return False | |
| # Check whitelist (if not empty, IP must be whitelisted) | |
| if self.whitelist: | |
| for entry in self.whitelist: | |
| if isinstance(entry, (ipaddress.IPv4Network, ipaddress.IPv6Network)): | |
| # CIDR range check | |
| if ip_addr in entry: | |
| return True | |
| else: | |
| # Single IP check | |
| if ip_addr == entry: | |
| return True | |
| return False | |
| # Check blacklist (if whitelist is empty) | |
| for entry in self.blacklist: | |
| if isinstance(entry, (ipaddress.IPv4Network, ipaddress.IPv6Network)): | |
| # CIDR range check | |
| if ip_addr in entry: | |
| return False | |
| else: | |
| # Single IP check | |
| if ip_addr == entry: | |
| return False | |
| return True | |
| def is_whitelisted(self, ip: str) -> bool: | |
| """True when a whitelist is configured and the IP matches an entry.""" | |
| self.load_filter_list() | |
| if not self.whitelist: | |
| return False | |
| try: | |
| ip_addr = ipaddress.ip_address(ip) | |
| except ValueError: | |
| return False | |
| for entry in self.whitelist: | |
| if isinstance(entry, (ipaddress.IPv4Network, ipaddress.IPv6Network)): | |
| if ip_addr in entry: | |
| return True | |
| elif ip_addr == entry: | |
| return True | |
| return False | |
| def add_to_blacklist(self, ip: str) -> None: | |
| """Add a given IP to the blacklist file.""" | |
| try: | |
| ip_obj = ipaddress.ip_address(ip) | |
| except ValueError: | |
| return | |
| # Check if already in blacklist | |
| ip_str = str(ip_obj) | |
| for entry in self.blacklist: | |
| if str(entry) == ip_str: | |
| return # Already in blacklist | |
| # Add to in-memory list | |
| self.blacklist.append(ip_obj) | |
| # Append to file | |
| try: | |
| # Check if file exists and has content | |
| file_exists = os.path.exists(self.blacklist_file) | |
| needs_newline = False | |
| if file_exists: | |
| with open(self.blacklist_file, "r") as f: | |
| content = f.read() | |
| if content and not content.endswith("\n"): | |
| needs_newline = True | |
| with open(self.blacklist_file, "a") as file: | |
| if needs_newline: | |
| file.write("\n") | |
| file.write(ip_str + "\n") | |
| # Update hash after writing | |
| self._blacklist_hash = self.calculate_file_hash(self.blacklist_file) | |
| except Exception as e: | |
| # Log error but don't fail - in-memory list is updated | |
| print(f"[Usgromana] Warning: Failed to write IP to blacklist file: {e}") | |
| def create_ip_filter_middleware(self) -> web.middleware: | |
| """Create the middleware for managing blacklisted and whitelisted ip.""" | |
| async def ip_filter_middleware(request: web.Request, handler) -> web.Response: | |
| ip = get_ip(request) | |
| if not self.is_allowed(ip): | |
| return await handle_access_denied( | |
| request, | |
| "Access denied: IP is either not whitelisted or is blacklisted.", | |
| ) | |
| return await handler(request) | |
| async def handle_access_denied( | |
| request: web.Request, message: str | |
| ) -> web.Response: | |
| """Handle denied access cases.""" | |
| accept_header = request.headers.get("Accept", "") | |
| if "text/html" in accept_header: | |
| return web.HTTPForbidden(reason=message) | |
| else: | |
| return web.json_response({"error": message}, status=403) | |
| return ip_filter_middleware | |