From 4cef51e15f002d54f49ccaec8bfda19556ac0fd2 Mon Sep 17 00:00:00 2001 From: "Gan, Jimmy" Date: Sun, 1 Mar 2026 10:48:02 +0800 Subject: [PATCH 1/4] fix: dev dashboard shares prod socket proxy network --- dashboard/docker-compose.dev.yml | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/dashboard/docker-compose.dev.yml b/dashboard/docker-compose.dev.yml index ca79ea3..4bac144 100644 --- a/dashboard/docker-compose.dev.yml +++ b/dashboard/docker-compose.dev.yml @@ -35,29 +35,8 @@ services: extra_hosts: - "host.docker.internal:host-gateway" networks: - - dashboard_internal + - nas-dashboard_internal - gitea_gitea - depends_on: - - docker-socket-proxy - logging: - driver: json-file - options: - max-size: "10m" - max-file: "3" - - docker-socket-proxy: - image: tecnativa/docker-socket-proxy - container_name: docker-socket-proxy-dev - restart: unless-stopped - environment: - CONTAINERS: 1 - IMAGES: 1 - INFO: 1 - POST: 1 - volumes: - - /var/run/docker.sock:/var/run/docker.sock:ro - networks: - - dashboard_internal logging: driver: json-file options: @@ -65,6 +44,7 @@ services: max-file: "3" networks: - dashboard_internal: + nas-dashboard_internal: + external: true gitea_gitea: external: true From 26f458aceecafaaa19ac2aea01c78687dab9ac9c Mon Sep 17 00:00:00 2001 From: "Gan, Jimmy" Date: Sun, 1 Mar 2026 11:06:48 +0800 Subject: [PATCH 2/4] fix: parse Authelia logfmt format in security logs --- dashboard/backend/routers/security.py | 38 +++++++++++++++++++++------ 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/dashboard/backend/routers/security.py b/dashboard/backend/routers/security.py index fe7476b..80b6257 100644 --- a/dashboard/backend/routers/security.py +++ b/dashboard/backend/routers/security.py @@ -26,25 +26,47 @@ _INTERNAL_PATHS = re.compile( ) +_LOGFMT_RE = re.compile(r'(\w+)="([^"]*)"|\b(\w+)=(\S+)') + + +def _parse_logfmt(line: str) -> dict: + """Parse a logfmt line into a dict.""" + result = {} + for m in _LOGFMT_RE.finditer(line): + if m.group(1): + result[m.group(1)] = m.group(2) + elif m.group(3): + result[m.group(3)] = m.group(4) + return result + + def _parse_authelia_logs(lines: str, limit: int) -> list[dict]: - """Extract failed auth attempts from Authelia JSON logs.""" + """Extract failed auth attempts from Authelia logs (logfmt or JSON).""" entries = [] for line in lines.strip().splitlines(): - # Authelia outputs JSON log lines - try: - obj = json.loads(line.strip()) - except (json.JSONDecodeError, ValueError): + line = line.strip() + if not line: continue + # Try JSON first, fall back to logfmt + try: + obj = json.loads(line) + except (json.JSONDecodeError, ValueError): + obj = _parse_logfmt(line) + if not obj: + continue + level = obj.get("level", "") msg = obj.get("msg", "") - # Failed auth: level=error or warning with auth-related messages is_failed = ( level in ("error", "warning") and any(kw in msg.lower() for kw in ("unsuccessful", "banned", "failed", "invalid", "denied")) ) - if not is_failed: + # Also catch "not authorized" for anonymous access attempts + is_unauthorized = "is not authorized" in msg.lower() + + if not is_failed and not is_unauthorized: continue ts_raw = obj.get("time", "") @@ -54,7 +76,7 @@ def _parse_authelia_logs(lines: str, limit: int) -> list[dict]: ts = ts_raw[:19] if ts_raw else "" entries.append({ - "type": "auth_failure", + "type": "auth_failure" if is_failed else "unauthorized", "timestamp": ts, "message": msg, "username": obj.get("username", obj.get("user", "")), From 53481f16f80bac15a9ddd244eb09f490df610d8d Mon Sep 17 00:00:00 2001 From: "Gan, Jimmy" Date: Sun, 1 Mar 2026 11:43:02 +0800 Subject: [PATCH 3/4] fix: reduce log tail defaults and add Docker client timeout to prevent security page hanging --- dashboard/backend/routers/security.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dashboard/backend/routers/security.py b/dashboard/backend/routers/security.py index 80b6257..4d235ed 100644 --- a/dashboard/backend/routers/security.py +++ b/dashboard/backend/routers/security.py @@ -7,7 +7,7 @@ from fastapi import APIRouter, Query from config import DOCKER_HOST router = APIRouter() -client = docker.DockerClient(base_url=DOCKER_HOST) +client = docker.DockerClient(base_url=DOCKER_HOST, timeout=10) _tz_cst = timezone(timedelta(hours=8)) @@ -132,8 +132,8 @@ def _parse_caddy_logs(lines: str, limit: int) -> list[dict]: @router.get("/logs") def security_logs( - tail: int = Query(500, le=5000), - limit: int = Query(200, le=1000), + tail: int = Query(200, le=2000), + limit: int = Query(100, le=500), ): """Fetch and parse security-relevant logs from Authelia and Caddy.""" results = [] @@ -164,7 +164,7 @@ def security_logs( @router.get("/stats") -def security_stats(tail: int = Query(2000, le=10000)): +def security_stats(tail: int = Query(500, le=2000)): """Aggregated security stats: failed logins, top IPs, hourly timeline.""" auth_entries = [] caddy_entries = [] From 533672c8aeb59232a423bc37460545aa0321f0a1 Mon Sep 17 00:00:00 2001 From: "Gan, Jimmy" Date: Sun, 1 Mar 2026 12:54:00 +0800 Subject: [PATCH 4/4] fix: disable Caddy log fetching - causes Docker API timeouts --- dashboard/backend/routers/security.py | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/dashboard/backend/routers/security.py b/dashboard/backend/routers/security.py index 4d235ed..5ed3014 100644 --- a/dashboard/backend/routers/security.py +++ b/dashboard/backend/routers/security.py @@ -148,15 +148,8 @@ def security_logs( except Exception as e: results.append({"type": "error", "message": f"Authelia log error: {e}", "timestamp": "", "ip": ""}) - # Caddy logs - try: - caddy = client.containers.get("caddy") - raw = caddy.logs(tail=tail, timestamps=False).decode(errors="replace") - results.extend(_parse_caddy_logs(raw, limit)) - except docker.errors.NotFound: - pass - except Exception as e: - results.append({"type": "error", "message": f"Caddy log error: {e}", "timestamp": "", "ip": ""}) + # Caddy logs disabled — Caddy outputs massive logs that timeout the Docker API + # TODO: re-enable when Caddy is configured with structured access logging to a file # Sort by timestamp descending results.sort(key=lambda x: x.get("timestamp", ""), reverse=True) @@ -176,12 +169,7 @@ def security_stats(tail: int = Query(500, le=2000)): except Exception: pass - try: - caddy = client.containers.get("caddy") - raw = caddy.logs(tail=tail, timestamps=False).decode(errors="replace") - caddy_entries = _parse_caddy_logs(raw, 1000) - except Exception: - pass + # Caddy logs disabled — too large, causes Docker API timeouts all_entries = auth_entries + caddy_entries