From cb574851cfa9b3988cb6f1a90600e1baf6c50b75 Mon Sep 17 00:00:00 2001 From: Maciej Pienczyn Date: Wed, 14 Jan 2026 22:11:53 +0100 Subject: [PATCH] feat: Add GeoIP blocking for high-risk countries (RU, CN, KP, IR, BY, SY, VE, CU) - Update security_service.py with BLOCKED_COUNTRIES list - Add check_geoip() middleware in app.py - Log blocked attempts with security alerts - Uses MaxMind GeoLite2-Country database Co-Authored-By: Claude Opus 4.5 --- app.py | 32 ++++++++++++++++++++++++++++++++ security_service.py | 12 +++++++----- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/app.py b/app.py index 95140dc..b9f0dde 100644 --- a/app.py +++ b/app.py @@ -453,6 +453,38 @@ def get_or_create_analytics_session(): db.close() +@app.before_request +def check_geoip(): + """Block requests from high-risk countries (RU, CN, KP, IR, BY, SY, VE, CU).""" + # Skip static files and health checks + if request.path.startswith('/static') or request.path == '/health': + return + + if not is_ip_allowed(): + ip = request.headers.get('X-Forwarded-For', request.remote_addr) + if ip: + ip = ip.split(',')[0].strip() + from security_service import get_country_code + country = get_country_code(ip) + logger.warning(f"GEOIP_BLOCKED ip={ip} country={country} path={request.path}") + + # Create alert for blocked access + try: + db = SessionLocal() + from security_service import create_security_alert + create_security_alert( + db, 'geo_blocked', 'low', + ip_address=ip, + details={'country': country, 'path': request.path, 'user_agent': request.user_agent.string[:200]} + ) + db.commit() + db.close() + except Exception as e: + logger.error(f"Failed to create geo block alert: {e}") + + abort(403) + + @app.before_request def track_page_view(): """Track page views (excluding static files and API calls)""" diff --git a/security_service.py b/security_service.py index 5f6cca7..c106e51 100644 --- a/security_service.py +++ b/security_service.py @@ -5,7 +5,7 @@ Norda Biznes - Security Service Security utilities for NordaBiz platform: - Audit logging (admin action tracking) - Security alerting (email notifications) -- GeoIP blocking (Poland only) +- GeoIP blocking (block high-risk countries: RU, CN, KP, IR, BY, SY, VE, CU) - 2FA (TOTP) helpers Author: Norda Biznes Development Team @@ -180,8 +180,9 @@ def _send_alert_email(alert): # GeoIP configuration GEOIP_ENABLED = os.getenv('GEOIP_ENABLED', 'false').lower() == 'true' -GEOIP_DB_PATH = os.getenv('GEOIP_DB_PATH', '/var/lib/GeoIP/GeoLite2-Country.mmdb') -ALLOWED_COUNTRIES = {'PL'} # Only Poland allowed +GEOIP_DB_PATH = os.getenv('GEOIP_DB_PATH', '/var/www/nordabiznes/geoip/GeoLite2-Country.mmdb') +# Block high-risk countries (Russia, China, North Korea, Iran, etc.) +BLOCKED_COUNTRIES = {'RU', 'CN', 'KP', 'IR', 'BY', 'SY', 'VE', 'CU'} GEOIP_WHITELIST = set(os.getenv('GEOIP_WHITELIST', '').split(',')) - {''} # Whitelisted IPs # GeoIP reader (lazy loaded) @@ -233,7 +234,7 @@ def get_country_code(ip_address: str) -> str: def is_ip_allowed(ip_address: str = None) -> bool: """ - Check if an IP address is allowed (from Poland or whitelisted). + Check if an IP address is allowed (not from blocked high-risk countries). Args: ip_address: IP to check (defaults to current request IP) @@ -264,7 +265,8 @@ def is_ip_allowed(ip_address: str = None) -> bool: if country is None: return True - return country in ALLOWED_COUNTRIES + # Block high-risk countries + return country not in BLOCKED_COUNTRIES def geoip_check():