From 40f1051d1f2885be0a4705550fc0ad3ad7480614 Mon Sep 17 00:00:00 2001 From: Lorenzo Venerandi Date: Mon, 9 Mar 2026 17:54:35 +0100 Subject: [PATCH 1/5] feat: add access logging middleware and disable default uvicorn access log --- src/app.py | 28 ++++++++++++++++++++++++++-- src/logger.py | 4 ++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/app.py b/src/app.py index 7d313bb..6364705 100644 --- a/src/app.py +++ b/src/app.py @@ -16,7 +16,7 @@ from config import get_config from tracker import AccessTracker, set_tracker from database import initialize_database from tasks_master import get_tasksmaster -from logger import initialize_logging, get_app_logger +from logger import initialize_logging, get_app_logger, get_access_logger from generators import random_server_header @@ -121,11 +121,35 @@ def create_app() -> FastAPI: application.add_middleware(DeceptionMiddleware) - # Banned IP check middleware (outermost — runs first on request) + # Banned IP check middleware from middleware.ban_check import BanCheckMiddleware application.add_middleware(BanCheckMiddleware) + # Access log middleware (outermost — logs every request with real client IP) + @application.middleware("http") + async def access_log_middleware(request: Request, call_next): + response: Response = await call_next(request) + from dependencies import get_client_ip + + client_ip = get_client_ip(request) + path = request.url.path + method = request.method + status = response.status_code + access_logger = get_access_logger() + + user_agent = request.headers.get("User-Agent", "") + tracker = request.app.state.tracker + suspicious = tracker.is_suspicious_user_agent(user_agent) + + if suspicious: + access_logger.warning( + f"[SUSPICIOUS] [{method}] {client_ip} - {path} - {status} - {user_agent[:50]}" + ) + else: + access_logger.info(f"[{method}] {client_ip} - {path} - {status}") + return response + # Mount static files for the dashboard config = get_config() secret = config.dashboard_secret_path.lstrip("/") diff --git a/src/logger.py b/src/logger.py index d556684..d65ce50 100644 --- a/src/logger.py +++ b/src/logger.py @@ -112,6 +112,10 @@ class LoggerManager: credential_file_handler.setFormatter(credential_format) self._credential_logger.addHandler(credential_file_handler) + # Disable uvicorn's default access log to avoid duplicate entries + # with the wrong (proxy) IP. Our custom access logger handles this. + logging.getLogger("uvicorn.access").setLevel(logging.WARNING) + self._initialized = True @property From 4442bcc4067cd9215d90f0754be1a910931170df Mon Sep 17 00:00:00 2001 From: Lorenzo Venerandi Date: Mon, 9 Mar 2026 17:54:47 +0100 Subject: [PATCH 2/5] feat: enhance logging for authentication events --- src/config.py | 3 +++ src/routes/api.py | 2 ++ 2 files changed, 5 insertions(+) diff --git a/src/config.py b/src/config.py index f43b390..be0cf93 100644 --- a/src/config.py +++ b/src/config.py @@ -258,6 +258,9 @@ def override_config_from_env(config: Config = None): try: field_type = config.__dataclass_fields__[field].type env_value = os.environ[env_var] + # If password is overridden, it's no longer auto-generated + if field == "dashboard_password": + config.dashboard_password_generated = False if field_type == int: setattr(config, field, int(env_value)) elif field_type == float: diff --git a/src/routes/api.py b/src/routes/api.py index 661b3cc..08c9eeb 100644 --- a/src/routes/api.py +++ b/src/routes/api.py @@ -73,6 +73,7 @@ async def authenticate(request: Request, body: AuthRequest): if hmac.compare_digest(body.fingerprint, expected): # Success — clear failed attempts _auth_attempts.pop(ip, None) + get_app_logger().info(f"[AUTH] Successful login from {ip}") token = secrets.token_hex(32) _auth_tokens.add(token) response = JSONResponse(content={"authenticated": True}) @@ -85,6 +86,7 @@ async def authenticate(request: Request, body: AuthRequest): return response # Failed attempt — track and possibly lock out + get_app_logger().warning(f"[AUTH] Failed login attempt from {ip}") if not record: record = {"attempts": 0, "locked_until": 0, "lockouts": 0} _auth_attempts[ip] = record From 68375f6a32103aa811ee9d733775096a2febd1ae Mon Sep 17 00:00:00 2001 From: Lorenzo Venerandi Date: Mon, 9 Mar 2026 17:54:51 +0100 Subject: [PATCH 3/5] feat: refine logging for honeypot trap requests --- src/routes/honeypot.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/routes/honeypot.py b/src/routes/honeypot.py index e4b384c..cd844d5 100644 --- a/src/routes/honeypot.py +++ b/src/routes/honeypot.py @@ -394,13 +394,6 @@ async def trap_page(request: Request, path: str): is_suspicious = tracker.is_suspicious_user_agent(user_agent) - if is_suspicious: - access_logger.warning( - f"[SUSPICIOUS] {client_ip} - {user_agent[:50]} - {full_path}" - ) - else: - access_logger.info(f"[REQUEST] {client_ip} - {full_path}") - # Record access unless the router dependency already handled it # (attack pattern or honeypot path → already recorded by _track_honeypot_request) if not tracker.detect_attack_type(full_path) and not tracker.is_honeypot_path( From 6218a196382f02623e9b444ff46a776ca361d1f6 Mon Sep 17 00:00:00 2001 From: Lorenzo Venerandi Date: Mon, 9 Mar 2026 17:59:00 +0100 Subject: [PATCH 4/5] feat: improve access logging for banned IPs and handle connection resets --- src/app.py | 10 +++++++++- src/middleware/ban_check.py | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/app.py b/src/app.py index 6364705..2884162 100644 --- a/src/app.py +++ b/src/app.py @@ -129,9 +129,17 @@ def create_app() -> FastAPI: # Access log middleware (outermost — logs every request with real client IP) @application.middleware("http") async def access_log_middleware(request: Request, call_next): - response: Response = await call_next(request) from dependencies import get_client_ip + try: + response: Response = await call_next(request) + except ConnectionResetError: + client_ip = get_client_ip(request) + path = request.url.path + method = request.method + get_access_logger().info(f"[BANNED] [{method}] {client_ip} - {path}") + raise + client_ip = get_client_ip(request) path = request.url.path method = request.method diff --git a/src/middleware/ban_check.py b/src/middleware/ban_check.py index a3be689..c4b2e80 100644 --- a/src/middleware/ban_check.py +++ b/src/middleware/ban_check.py @@ -2,6 +2,7 @@ """ Middleware for checking if client IP is banned. +Resets the connection for banned IPs instead of sending a response. """ from starlette.middleware.base import BaseHTTPMiddleware @@ -11,6 +12,13 @@ from starlette.responses import Response from dependencies import get_client_ip +class ConnectionResetResponse(Response): + """Response that abruptly closes the connection without sending data.""" + + async def __call__(self, scope, receive, send): + raise ConnectionResetError() + + class BanCheckMiddleware(BaseHTTPMiddleware): async def dispatch(self, request: Request, call_next): # Skip ban check for dashboard routes @@ -23,7 +31,7 @@ class BanCheckMiddleware(BaseHTTPMiddleware): tracker = request.app.state.tracker if tracker.is_banned_ip(client_ip): - return Response(status_code=500) + return ConnectionResetResponse() response = await call_next(request) return response From 1d3b01f61fe4cd3b48cea105876cc5243c85ac27 Mon Sep 17 00:00:00 2001 From: Lorenzo Venerandi Date: Mon, 9 Mar 2026 17:59:20 +0100 Subject: [PATCH 5/5] feat: update chart version to 1.1.7 --- helm/Chart.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helm/Chart.yaml b/helm/Chart.yaml index ec5889f..b66537f 100644 --- a/helm/Chart.yaml +++ b/helm/Chart.yaml @@ -2,8 +2,8 @@ apiVersion: v2 name: krawl-chart description: A Helm chart for Krawl honeypot server type: application -version: 1.1.6 -appVersion: 1.1.6 +version: 1.1.7 +appVersion: 1.1.7 keywords: - honeypot - security