diff --git a/.gitignore b/.gitignore index ea32dfb..503a0f9 100644 --- a/.gitignore +++ b/.gitignore @@ -100,8 +100,10 @@ Docker/fail2ban-dev-config/** !Docker/fail2ban-dev-config/fail2ban/ !Docker/fail2ban-dev-config/fail2ban/filter.d/ !Docker/fail2ban-dev-config/fail2ban/filter.d/bangui-sim.conf +!Docker/fail2ban-dev-config/fail2ban/filter.d/bangui-access.conf !Docker/fail2ban-dev-config/fail2ban/jail.d/ !Docker/fail2ban-dev-config/fail2ban/jail.d/bangui-sim.conf +!Docker/fail2ban-dev-config/fail2ban/jail.d/bangui-access.conf # ── Misc ────────────────────────────────────── *.log diff --git a/Docker/fail2ban-dev-config/README.md b/Docker/fail2ban-dev-config/README.md index e23e31f..e451112 100644 --- a/Docker/fail2ban-dev-config/README.md +++ b/Docker/fail2ban-dev-config/README.md @@ -67,6 +67,50 @@ Chains steps 1–3 automatically with appropriate sleep intervals. --- +## Testing the Access List Feature + +The **Access List** tab in BanGUI displays each individual matched log line +stored in fail2ban's database. A second jail — `bangui-access` — monitors +`Docker/logs/access.log` for simulated HTTP bot-scan entries. + +### 1 — Run the access-scan simulation + +```bash +bash Docker/simulate_accesses.sh +``` + +Default: writes **5** HTTP-scan lines for IP `203.0.113.7` to +`Docker/logs/access.log`. +Optional overrides: + +```bash +bash Docker/simulate_accesses.sh +# e.g. bash Docker/simulate_accesses.sh 6 198.51.100.5 +``` + +Log line format: + +``` +YYYY-MM-DD HH:MM:SS bangui-access: http scan from "GET /.env HTTP/1.1" 404 +``` + +### 2 — Verify the IP was banned + +```bash +bash Docker/check_ban_status.sh +``` + +The `bangui-access` jail should appear alongside `bangui-sim`, showing the +banned IP and matched line count. + +### 3 — Unban and re-test + +```bash +bash Docker/check_ban_status.sh --unban 203.0.113.7 +``` + +--- + ## Configuration Reference | File | Purpose | @@ -74,6 +118,9 @@ Chains steps 1–3 automatically with appropriate sleep intervals. | `fail2ban/filter.d/bangui-sim.conf` | Defines the `failregex` that matches simulation log lines | | `fail2ban/jail.d/bangui-sim.conf` | Jail settings: `maxretry=3`, `bantime=60s`, `findtime=120s` | | `Docker/logs/auth.log` | Log file written by the simulation script (host path) | +| `fail2ban/filter.d/bangui-access.conf` | Defines the `failregex` that matches access-scan log lines | +| `fail2ban/jail.d/bangui-access.conf` | Access jail settings: `maxretry=3`, `bantime=60s`, `findtime=120s` | +| `Docker/logs/access.log` | Log file written by `simulate_accesses.sh` (host path) | Inside the container the log file is mounted at `/remotelogs/bangui/auth.log` (see `fail2ban/paths-lsio.conf` — `remote_logs_path = /remotelogs`). @@ -109,13 +156,20 @@ Test the regex manually: ```bash docker exec bangui-fail2ban-dev \ fail2ban-regex /remotelogs/bangui/auth.log bangui-sim + +docker exec bangui-fail2ban-dev \ + fail2ban-regex /remotelogs/bangui/access.log bangui-access ``` The output should show matched lines. If nothing matches, check that the log -lines produced by `simulate_failed_logins.sh` match this pattern exactly: +lines match the corresponding `failregex` pattern: ``` +# bangui-sim (auth log): YYYY-MM-DD HH:MM:SS bangui-auth: authentication failure from + +# bangui-access (access log): +YYYY-MM-DD HH:MM:SS bangui-access: http scan from " HTTP/1.1" ``` ### iptables / permission errors diff --git a/Docker/fail2ban-dev-config/fail2ban/filter.d/bangui-access.conf b/Docker/fail2ban-dev-config/fail2ban/filter.d/bangui-access.conf new file mode 100644 index 0000000..28abe3c --- /dev/null +++ b/Docker/fail2ban-dev-config/fail2ban/filter.d/bangui-access.conf @@ -0,0 +1,13 @@ +# ────────────────────────────────────────────────────────────── +# BanGUI — Simulated HTTP access scan failure filter +# +# Matches lines written by Docker/simulate_accesses.sh. +# Format: +# YYYY-MM-DD HH:MM:SS bangui-access: http scan from " HTTP/1.1" +# ────────────────────────────────────────────────────────────── + +[Definition] + +failregex = ^.* bangui-access: http scan from ".*" [45]\d\d\s*$ + +ignoreregex = diff --git a/Docker/fail2ban-dev-config/fail2ban/jail.d/bangui-access.conf b/Docker/fail2ban-dev-config/fail2ban/jail.d/bangui-access.conf new file mode 100644 index 0000000..bc86665 --- /dev/null +++ b/Docker/fail2ban-dev-config/fail2ban/jail.d/bangui-access.conf @@ -0,0 +1,20 @@ +# ────────────────────────────────────────────────────────────── +# BanGUI — Simulated HTTP access scan jail +# +# Watches Docker/logs/access.log (mounted at /remotelogs/bangui) +# for lines produced by Docker/simulate_accesses.sh. +# ────────────────────────────────────────────────────────────── + +[bangui-access] + +enabled = true +filter = bangui-access +logpath = /remotelogs/bangui/access.log +backend = polling +maxretry = 3 +findtime = 120 +bantime = 60 +banaction = iptables-allports + +# Never ban localhost, the Docker bridge network, or the host machine. +ignoreip = 127.0.0.0/8 ::1 172.16.0.0/12 diff --git a/Docker/simulate_accesses.sh b/Docker/simulate_accesses.sh new file mode 100644 index 0000000..bb68614 --- /dev/null +++ b/Docker/simulate_accesses.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash +# ────────────────────────────────────────────────────────────── +# simulate_accesses.sh +# +# Writes synthetic HTTP-scan log lines to a file that matches +# the bangui-access fail2ban filter. Use this to populate the +# Access List tab in BanGUI without a real web server. +# +# Usage: +# bash Docker/simulate_accesses.sh [COUNT] [SOURCE_IP] [LOG_FILE] +# +# Defaults: +# COUNT : 5 +# SOURCE_IP: 203.0.113.7 +# LOG_FILE : Docker/logs/access.log (relative to repo root) +# +# Log line format (must match bangui-access failregex exactly): +# YYYY-MM-DD HH:MM:SS bangui-access: http scan from " HTTP/1.1" +# +# fail2ban bans the IP after maxretry (default 3) matching lines. +# ────────────────────────────────────────────────────────────── + +set -euo pipefail + +# ── Defaults ────────────────────────────────────────────────── +readonly DEFAULT_COUNT=5 +readonly DEFAULT_IP="203.0.113.7" + +# Resolve script location so defaults work regardless of cwd. +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +readonly DEFAULT_LOG_FILE="${SCRIPT_DIR}/logs/access.log" + +# ── Arguments ───────────────────────────────────────────────── +COUNT="${1:-${DEFAULT_COUNT}}" +SOURCE_IP="${2:-${DEFAULT_IP}}" +LOG_FILE="${3:-${DEFAULT_LOG_FILE}}" + +# ── Validate COUNT is a positive integer ────────────────────── +if ! [[ "${COUNT}" =~ ^[1-9][0-9]*$ ]]; then + echo "ERROR: COUNT must be a positive integer, got: '${COUNT}'" >&2 + exit 1 +fi + +# ── Common bot-scan paths ───────────────────────────────────── +PATHS=( + "GET /.env HTTP/1.1" + "GET /wp-login.php HTTP/1.1" + "GET /admin HTTP/1.1" + "POST /wp-login.php HTTP/1.1" + "GET /.git/config HTTP/1.1" + "GET /phpinfo.php HTTP/1.1" + "GET /wp-config.php HTTP/1.1" + "GET /phpmyadmin HTTP/1.1" +) +readonly PATHS + +NUM_PATHS="${#PATHS[@]}" + +# ── Ensure log directory exists ─────────────────────────────── +LOG_DIR="$(dirname "${LOG_FILE}")" +mkdir -p "${LOG_DIR}" + +# ── Write scan lines ────────────────────────────────────────── +echo "Writing ${COUNT} HTTP-scan line(s) for ${SOURCE_IP} to ${LOG_FILE} ..." + +for ((i = 1; i <= COUNT; i++)); do + TIMESTAMP="$(date '+%Y-%m-%d %H:%M:%S')" + # Cycle through the scan paths so each run looks varied. + PATH_ENTRY="${PATHS[$(( (i - 1) % NUM_PATHS ))]}" + printf '%s bangui-access: http scan from %s "%s" 404\n' \ + "${TIMESTAMP}" "${SOURCE_IP}" "${PATH_ENTRY}" >> "${LOG_FILE}" + sleep 0.5 +done + +# ── Summary ─────────────────────────────────────────────────── +echo "Done." +echo " Lines written : ${COUNT}" +echo " Source IP : ${SOURCE_IP}" +echo " Log file : ${LOG_FILE}" +echo "" +echo " fail2ban bans after maxretry=3 matching lines." +echo " Check ban status with: bash Docker/check_ban_status.sh" diff --git a/Docs/Tasks.md b/Docs/Tasks.md index 237ce75..f2e4f78 100644 --- a/Docs/Tasks.md +++ b/Docs/Tasks.md @@ -3,951 +3,12 @@ This document breaks the entire BanGUI project into development stages, ordered so that each stage builds on the previous one. Every task is described in prose with enough detail for a developer to begin work. References point to the relevant documentation. --- -1. ✅ DONE — fix ban list 500 error - **Root cause:** `compose.debug.yml` mounted `fail2ban-dev-config` as a named Docker volume - for the backend container, while fail2ban used a bind-mount of the same local directory. - The backend's `/config` was therefore an empty volume and could not open the fail2ban - SQLite database that fail2ban reported via `get dbfile`. - **Fix:** Changed the backend volume entry from `fail2ban-dev-config:/config:ro` to - `./fail2ban-dev-config:/config:ro` (bind-mount), and removed the now-unused named volume. - - ~~1. issue with ban list~~ -client.ts:55 GET http://127.0.0.1:5173/api/dashboard/bans?range=24h&page=1&page_size=100 500 (Internal Server Error) -request @ client.ts:55 -get @ client.ts:88 -fetchBans @ dashboard.ts:43 -(anonymous) @ useBans.ts:68 -(anonymous) @ useBans.ts:90 -commitHookEffectListMount @ react-dom.development.js:23189 -commitPassiveMountOnFiber @ react-dom.development.js:24965 -commitPassiveMountEffects_complete @ react-dom.development.js:24930 -commitPassiveMountEffects_begin @ react-dom.development.js:24917 -commitPassiveMountEffects @ react-dom.development.js:24905 -flushPassiveEffectsImpl @ react-dom.development.js:27078 -flushPassiveEffects @ react-dom.development.js:27023 -(anonymous) @ react-dom.development.js:26808 -workLoop @ scheduler.development.js:266 -flushWork @ scheduler.development.js:239 -performWorkUntilDeadline @ scheduler.development.js:533 -client.ts:55 GET http://127.0.0.1:5173/api/dashboard/bans?range=24h&page=1&page_size=100 500 (Internal Server Error) +1. fix Stop Jail command -9110898d07c7 INFO: 10.89.0.3:55926 - "GET /api/dashboard/bans?range=24h&page=1&page_size=100 HTTP/1.1" 500 Internal Server Error -9110898d07c7 ERROR: Exception in ASGI application -9110898d07c7 Traceback (most recent call last): -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/uvicorn/protocols/http/httptools_impl.py", line 416, in run_asgi -9110898d07c7 result = await app( # type: ignore[func-returns-value] -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/uvicorn/middleware/proxy_headers.py", line 60, in __call__ -9110898d07c7 return await self.app(scope, receive, send) -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/fastapi/applications.py", line 1160, in __call__ -9110898d07c7 await super().__call__(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/applications.py", line 107, in __call__ -9110898d07c7 await self.middleware_stack(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/errors.py", line 186, in __call__ -9110898d07c7 raise exc -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/errors.py", line 164, in __call__ -9110898d07c7 await self.app(scope, receive, _send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 191, in __call__ -9110898d07c7 with recv_stream, send_stream, collapse_excgroups(): -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/usr/local/lib/python3.12/contextlib.py", line 158, in __exit__ -9110898d07c7 self.gen.throw(value) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/_utils.py", line 87, in collapse_excgroups -9110898d07c7 raise exc -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 193, in __call__ -9110898d07c7 response = await self.dispatch_func(request, call_next) -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/app/app/main.py", line 294, in dispatch -9110898d07c7 return await call_next(request) -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 168, in call_next -9110898d07c7 raise app_exc from app_exc.__cause__ or app_exc.__context__ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 144, in coro -9110898d07c7 await self.app(scope, receive_or_disconnect, send_no_error) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/cors.py", line 87, in __call__ -9110898d07c7 await self.app(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/exceptions.py", line 63, in __call__ -9110898d07c7 await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app -9110898d07c7 raise exc -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app -9110898d07c7 await app(scope, receive, sender) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/fastapi/middleware/asyncexitstack.py", line 18, in __call__ -9110898d07c7 await self.app(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/routing.py", line 716, in __call__ -9110898d07c7 await self.middleware_stack(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/routing.py", line 736, in app -9110898d07c7 await route.handle(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/routing.py", line 290, in handle -9110898d07c7 await self.app(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/fastapi/routing.py", line 129, in app -9110898d07c7 await wrap_app_handling_exceptions(app, request)(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app -9110898d07c7 raise exc -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app -9110898d07c7 await app(scope, receive, sender) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/fastapi/routing.py", line 115, in app -9110898d07c7 response = await f(request) -9110898d07c7 ^^^^^^^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/fastapi/routing.py", line 630, in app -9110898d07c7 raw_response = await run_endpoint_function( -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/fastapi/routing.py", line 323, in run_endpoint_function -9110898d07c7 return await dependant.call(**values) -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/app/app/routers/dashboard.py", line 107, in get_dashboard_bans -9110898d07c7 return await ban_service.list_bans( -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/app/app/services/ban_service.py", line 181, in list_bans -9110898d07c7 async with aiosqlite.connect(f"file:{db_path}?mode=ro", uri=True) as f2b_db: -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py", line 181, in __aenter__ -9110898d07c7 return await self -9110898d07c7 ^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py", line 168, in _connect -9110898d07c7 self._connection = await future -9110898d07c7 ^^^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py", line 63, in _connection_worker_thread -9110898d07c7 result = function() -9110898d07c7 ^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py", line 466, in connector -9110898d07c7 return sqlite3.connect(loc, **kwargs) -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 sqlite3.OperationalError: unable to open database file -9110898d07c7 executing functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) -9110898d07c7 operation functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) -9110898d07c7 operation functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 INFO: 10.89.0.3:55942 - "GET /api/dashboard/status HTTP/1.1" 200 OK -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) -9110898d07c7 operation functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) -9110898d07c7 operation functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 INFO: 10.89.0.3:55946 - "GET /api/dashboard/status HTTP/1.1" 200 OK -9110898d07c7 Looking for jobs to run -9110898d07c7 Next wakeup is due at 2026-03-06 17:47:54.843643+00:00 (in 29.999878 seconds) -9110898d07c7 Running job "_run_probe (trigger: interval[0:00:30], next run at: 2026-03-06 17:47:54 UTC)" (scheduled at 2026-03-06 17:47:24.843643+00:00) -9110898d07c7 {"command": ["ping"], "event": "fail2ban_sending_command", "timestamp": "2026-03-06T17:47:24.843895Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"command": ["ping"], "event": "fail2ban_received_response", "timestamp": "2026-03-06T17:47:24.844510Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"command": ["version"], "event": "fail2ban_sending_command", "timestamp": "2026-03-06T17:47:24.844635Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"command": ["version"], "event": "fail2ban_received_response", "timestamp": "2026-03-06T17:47:24.844940Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"command": ["status"], "event": "fail2ban_sending_command", "timestamp": "2026-03-06T17:47:24.844983Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"command": ["status"], "event": "fail2ban_received_response", "timestamp": "2026-03-06T17:47:24.845150Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"command": ["status", "bangui-sim"], "event": "fail2ban_sending_command", "timestamp": "2026-03-06T17:47:24.845188Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"command": ["status", "bangui-sim"], "event": "fail2ban_received_response", "timestamp": "2026-03-06T17:47:24.845396Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"version": "1.1.0", "active_jails": 1, "total_bans": 0, "total_failures": 0, "event": "fail2ban_probe_ok", "timestamp": "2026-03-06T17:47:24.845437Z", "logger": "app.services.health_service", "level": "debug"} -9110898d07c7 {"online": true, "version": "1.1.0", "active_jails": 1, "event": "health_check_complete", "timestamp": "2026-03-06T17:47:24.845498Z", "logger": "app.tasks.health_check", "level": "debug"} -9110898d07c7 Job "_run_probe (trigger: interval[0:00:30], next run at: 2026-03-06 17:47:54 UTC)" executed successfully -9110898d07c7 INFO: 127.0.0.1:58940 - "GET /api/health HTTP/1.1" 200 OK -9110898d07c7 INFO: 127.0.0.1:41956 - "GET /api/health HTTP/1.1" 200 OK -9110898d07c7 executing functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) -9110898d07c7 operation functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) -9110898d07c7 operation functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 INFO: 10.89.0.3:54778 - "GET /api/dashboard/status HTTP/1.1" 200 OK -9110898d07c7 executing functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) -9110898d07c7 operation functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) -9110898d07c7 operation functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 INFO: 10.89.0.3:54780 - "GET /api/dashboard/status HTTP/1.1" 200 OK -9110898d07c7 executing functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) -9110898d07c7 operation functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) -9110898d07c7 operation functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 INFO: 10.89.0.3:54790 - "GET /api/dashboard/status HTTP/1.1" 200 OK -9110898d07c7 executing functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) -9110898d07c7 operation functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) -9110898d07c7 operation functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 INFO: 10.89.0.3:54804 - "GET /api/dashboard/status HTTP/1.1" 200 OK -9110898d07c7 executing functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) -9110898d07c7 operation functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) -9110898d07c7 operation functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 {"command": ["get", "dbfile"], "event": "fail2ban_sending_command", "timestamp": "2026-03-06T17:47:51.158844Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"command": ["get", "dbfile"], "event": "fail2ban_received_response", "timestamp": "2026-03-06T17:47:51.159260Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"db_path": "/config/fail2ban/fail2ban.sqlite3", "since": 1772732871, "range": "24h", "event": "ban_service_list_accesses", "timestamp": "2026-03-06T17:47:51.159342Z", "logger": "app.services.ban_service", "level": "info"} -9110898d07c7 executing .connector at 0x7abad4a2aac0> -9110898d07c7 returning exception unable to open database file -9110898d07c7 executing .close_and_stop at 0x7abad4a29580> -9110898d07c7 operation .close_and_stop at 0x7abad4a29580> completed -9110898d07c7 {"path": "/api/dashboard/accesses", "method": "GET", "event": "unhandled_exception", "timestamp": "2026-03-06T17:47:51.159773Z", "logger": "app.main", "level": "error", "exception": "Traceback (most recent call last):\n File \"/opt/venv/lib/python3.12/site-packages/starlette/middleware/errors.py\", line 164, in __call__\n await self.app(scope, receive, _send)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py\", line 191, in __call__\n with recv_stream, send_stream, collapse_excgroups():\n ^^^^^^^^^^^^^^^^^^^^\n File \"/usr/local/lib/python3.12/contextlib.py\", line 158, in __exit__\n self.gen.throw(value)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/_utils.py\", line 87, in collapse_excgroups\n raise exc\n File \"/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py\", line 193, in __call__\n response = await self.dispatch_func(request, call_next)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/app/app/main.py\", line 294, in dispatch\n return await call_next(request)\n ^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py\", line 168, in call_next\n raise app_exc from app_exc.__cause__ or app_exc.__context__\n File \"/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py\", line 144, in coro\n await self.app(scope, receive_or_disconnect, send_no_error)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/middleware/cors.py\", line 87, in __call__\n await self.app(scope, receive, send)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/middleware/exceptions.py\", line 63, in __call__\n await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py\", line 53, in wrapped_app\n raise exc\n File \"/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py\", line 42, in wrapped_app\n await app(scope, receive, sender)\n File \"/opt/venv/lib/python3.12/site-packages/fastapi/middleware/asyncexitstack.py\", line 18, in __call__\n await self.app(scope, receive, send)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/routing.py\", line 716, in __call__\n await self.middleware_stack(scope, receive, send)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/routing.py\", line 736, in app\n await route.handle(scope, receive, send)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/routing.py\", line 290, in handle\n await self.app(scope, receive, send)\n File \"/opt/venv/lib/python3.12/site-packages/fastapi/routing.py\", line 129, in app\n await wrap_app_handling_exceptions(app, request)(scope, receive, send)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py\", line 53, in wrapped_app\n raise exc\n File \"/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py\", line 42, in wrapped_app\n await app(scope, receive, sender)\n File \"/opt/venv/lib/python3.12/site-packages/fastapi/routing.py\", line 115, in app\n response = await f(request)\n ^^^^^^^^^^^^^^^^\n File \"/opt/venv/lib/python3.12/site-packages/fastapi/routing.py\", line 630, in app\n raw_response = await run_endpoint_function(\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/opt/venv/lib/python3.12/site-packages/fastapi/routing.py\", line 323, in run_endpoint_function\n return await dependant.call(**values)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/app/app/routers/dashboard.py\", line 152, in get_dashboard_accesses\n return await ban_service.list_accesses(\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/app/app/services/ban_service.py\", line 280, in list_accesses\n async with aiosqlite.connect(f\"file:{db_path}?mode=ro\", uri=True) as f2b_db:\n File \"/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py\", line 181, in __aenter__\n return await self\n ^^^^^^^^^^\n File \"/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py\", line 168, in _connect\n self._connection = await future\n ^^^^^^^^^^^^\n File \"/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py\", line 63, in _connection_worker_thread\n result = function()\n ^^^^^^^^^^\n File \"/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py\", line 466, in connector\n return sqlite3.connect(loc, **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nsqlite3.OperationalError: unable to open database file"} -9110898d07c7 INFO: 10.89.0.3:54816 - "GET /api/dashboard/accesses?range=24h&page=1&page_size=100 HTTP/1.1" 500 Internal Server Error -9110898d07c7 ERROR: Exception in ASGI application -9110898d07c7 Traceback (most recent call last): -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/uvicorn/protocols/http/httptools_impl.py", line 416, in run_asgi -9110898d07c7 result = await app( # type: ignore[func-returns-value] -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/uvicorn/middleware/proxy_headers.py", line 60, in __call__ -9110898d07c7 return await self.app(scope, receive, send) -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/fastapi/applications.py", line 1160, in __call__ -9110898d07c7 await super().__call__(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/applications.py", line 107, in __call__ -9110898d07c7 await self.middleware_stack(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/errors.py", line 186, in __call__ -9110898d07c7 raise exc -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/errors.py", line 164, in __call__ -9110898d07c7 await self.app(scope, receive, _send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 191, in __call__ -9110898d07c7 with recv_stream, send_stream, collapse_excgroups(): -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/usr/local/lib/python3.12/contextlib.py", line 158, in __exit__ -9110898d07c7 self.gen.throw(value) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/_utils.py", line 87, in collapse_excgroups -9110898d07c7 raise exc -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 193, in __call__ -9110898d07c7 response = await self.dispatch_func(request, call_next) -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/app/app/main.py", line 294, in dispatch -9110898d07c7 return await call_next(request) -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 168, in call_next -9110898d07c7 raise app_exc from app_exc.__cause__ or app_exc.__context__ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 144, in coro -9110898d07c7 await self.app(scope, receive_or_disconnect, send_no_error) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/cors.py", line 87, in __call__ -9110898d07c7 await self.app(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/exceptions.py", line 63, in __call__ -9110898d07c7 await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app -9110898d07c7 raise exc -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app -9110898d07c7 await app(scope, receive, sender) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/fastapi/middleware/asyncexitstack.py", line 18, in __call__ -9110898d07c7 await self.app(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/routing.py", line 716, in __call__ -9110898d07c7 await self.middleware_stack(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/routing.py", line 736, in app -9110898d07c7 await route.handle(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/routing.py", line 290, in handle -9110898d07c7 await self.app(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/fastapi/routing.py", line 129, in app -9110898d07c7 await wrap_app_handling_exceptions(app, request)(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app -9110898d07c7 raise exc -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app -9110898d07c7 await app(scope, receive, sender) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/fastapi/routing.py", line 115, in app -9110898d07c7 response = await f(request) -9110898d07c7 ^^^^^^^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/fastapi/routing.py", line 630, in app -9110898d07c7 raw_response = await run_endpoint_function( -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/fastapi/routing.py", line 323, in run_endpoint_function -9110898d07c7 return await dependant.call(**values) -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/app/app/routers/dashboard.py", line 152, in get_dashboard_accesses -9110898d07c7 return await ban_service.list_accesses( -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/app/app/services/ban_service.py", line 280, in list_accesses -9110898d07c7 async with aiosqlite.connect(f"file:{db_path}?mode=ro", uri=True) as f2b_db: -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py", line 181, in __aenter__ -9110898d07c7 return await self -9110898d07c7 ^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py", line 168, in _connect -9110898d07c7 self._connection = await future -9110898d07c7 ^^^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py", line 63, in _connection_worker_thread -9110898d07c7 result = function() -9110898d07c7 ^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py", line 466, in connector -9110898d07c7 return sqlite3.connect(loc, **kwargs) -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 sqlite3.OperationalError: unable to open database file -9110898d07c7 Looking for jobs to run -9110898d07c7 Next wakeup is due at 2026-03-06 17:48:24.843643+00:00 (in 29.999538 seconds) -9110898d07c7 Running job "_run_probe (trigger: interval[0:00:30], next run at: 2026-03-06 17:48:24 UTC)" (scheduled at 2026-03-06 17:47:54.843643+00:00) -9110898d07c7 {"command": ["ping"], "event": "fail2ban_sending_command", "timestamp": "2026-03-06T17:47:54.844275Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"command": ["ping"], "event": "fail2ban_received_response", "timestamp": "2026-03-06T17:47:54.844782Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"command": ["version"], "event": "fail2ban_sending_command", "timestamp": "2026-03-06T17:47:54.844845Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"command": ["version"], "event": "fail2ban_received_response", "timestamp": "2026-03-06T17:47:54.845117Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"command": ["status"], "event": "fail2ban_sending_command", "timestamp": "2026-03-06T17:47:54.845171Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"command": ["status"], "event": "fail2ban_received_response", "timestamp": "2026-03-06T17:47:54.845424Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"command": ["status", "bangui-sim"], "event": "fail2ban_sending_command", "timestamp": "2026-03-06T17:47:54.845486Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"command": ["status", "bangui-sim"], "event": "fail2ban_received_response", "timestamp": "2026-03-06T17:47:54.845745Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"version": "1.1.0", "active_jails": 1, "total_bans": 0, "total_failures": 0, "event": "fail2ban_probe_ok", "timestamp": "2026-03-06T17:47:54.845805Z", "logger": "app.services.health_service", "level": "debug"} -9110898d07c7 {"online": true, "version": "1.1.0", "active_jails": 1, "event": "health_check_complete", "timestamp": "2026-03-06T17:47:54.845871Z", "logger": "app.tasks.health_check", "level": "debug"} -9110898d07c7 Job "_run_probe (trigger: interval[0:00:30], next run at: 2026-03-06 17:48:24 UTC)" executed successfully -9110898d07c7 INFO: 127.0.0.1:52658 - "GET /api/health HTTP/1.1" 200 OK -9110898d07c7 INFO: 127.0.0.1:42840 - "GET /api/health HTTP/1.1" 200 OK -9110898d07c7 executing functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) -9110898d07c7 operation functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) -9110898d07c7 operation functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 INFO: 10.89.0.3:59946 - "GET /api/dashboard/status HTTP/1.1" 200 OK -9110898d07c7 executing functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) -9110898d07c7 operation functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) -9110898d07c7 operation functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 INFO: 10.89.0.3:59952 - "GET /api/dashboard/status HTTP/1.1" 200 OK -9110898d07c7 Looking for jobs to run -9110898d07c7 Next wakeup is due at 2026-03-06 17:48:54.843643+00:00 (in 29.998906 seconds) -9110898d07c7 Running job "_run_probe (trigger: interval[0:00:30], next run at: 2026-03-06 17:48:54 UTC)" (scheduled at 2026-03-06 17:48:24.843643+00:00) -9110898d07c7 {"command": ["ping"], "event": "fail2ban_sending_command", "timestamp": "2026-03-06T17:48:24.844922Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"command": ["ping"], "event": "fail2ban_received_response", "timestamp": "2026-03-06T17:48:24.845403Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"command": ["version"], "event": "fail2ban_sending_command", "timestamp": "2026-03-06T17:48:24.845477Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"command": ["version"], "event": "fail2ban_received_response", "timestamp": "2026-03-06T17:48:24.845741Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"command": ["status"], "event": "fail2ban_sending_command", "timestamp": "2026-03-06T17:48:24.845783Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"command": ["status"], "event": "fail2ban_received_response", "timestamp": "2026-03-06T17:48:24.846021Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"command": ["status", "bangui-sim"], "event": "fail2ban_sending_command", "timestamp": "2026-03-06T17:48:24.846060Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"command": ["status", "bangui-sim"], "event": "fail2ban_received_response", "timestamp": "2026-03-06T17:48:24.846284Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"version": "1.1.0", "active_jails": 1, "total_bans": 0, "total_failures": 0, "event": "fail2ban_probe_ok", "timestamp": "2026-03-06T17:48:24.846340Z", "logger": "app.services.health_service", "level": "debug"} -9110898d07c7 {"online": true, "version": "1.1.0", "active_jails": 1, "event": "health_check_complete", "timestamp": "2026-03-06T17:48:24.846394Z", "logger": "app.tasks.health_check", "level": "debug"} -9110898d07c7 Job "_run_probe (trigger: interval[0:00:30], next run at: 2026-03-06 17:48:54 UTC)" executed successfully -9110898d07c7 INFO: 127.0.0.1:39434 - "GET /api/health HTTP/1.1" 200 OK -9110898d07c7 INFO: 127.0.0.1:47306 - "GET /api/health HTTP/1.1" 200 OK -9110898d07c7 executing functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) -9110898d07c7 operation functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) -9110898d07c7 operation functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 INFO: 10.89.0.3:44266 - "GET /api/dashboard/status HTTP/1.1" 200 OK -9110898d07c7 executing functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) -9110898d07c7 operation functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) -9110898d07c7 operation functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 INFO: 10.89.0.3:44278 - "GET /api/dashboard/status HTTP/1.1" 200 OK -9110898d07c7 Looking for jobs to run -9110898d07c7 Next wakeup is due at 2026-03-06 17:49:24.843643+00:00 (in 29.999723 seconds) -9110898d07c7 Running job "_run_probe (trigger: interval[0:00:30], next run at: 2026-03-06 17:49:24 UTC)" (scheduled at 2026-03-06 17:48:54.843643+00:00) -9110898d07c7 {"command": ["ping"], "event": "fail2ban_sending_command", "timestamp": "2026-03-06T17:48:54.844056Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"command": ["ping"], "event": "fail2ban_received_response", "timestamp": "2026-03-06T17:48:54.844512Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"command": ["version"], "event": "fail2ban_sending_command", "timestamp": "2026-03-06T17:48:54.844602Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"command": ["version"], "event": "fail2ban_received_response", "timestamp": "2026-03-06T17:48:54.844871Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"command": ["status"], "event": "fail2ban_sending_command", "timestamp": "2026-03-06T17:48:54.844910Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"command": ["status"], "event": "fail2ban_received_response", "timestamp": "2026-03-06T17:48:54.845133Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"command": ["status", "bangui-sim"], "event": "fail2ban_sending_command", "timestamp": "2026-03-06T17:48:54.845172Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"command": ["status", "bangui-sim"], "event": "fail2ban_received_response", "timestamp": "2026-03-06T17:48:54.845364Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"version": "1.1.0", "active_jails": 1, "total_bans": 0, "total_failures": 0, "event": "fail2ban_probe_ok", "timestamp": "2026-03-06T17:48:54.845412Z", "logger": "app.services.health_service", "level": "debug"} -9110898d07c7 {"online": true, "version": "1.1.0", "active_jails": 1, "event": "health_check_complete", "timestamp": "2026-03-06T17:48:54.845463Z", "logger": "app.tasks.health_check", "level": "debug"} -9110898d07c7 Job "_run_probe (trigger: interval[0:00:30], next run at: 2026-03-06 17:49:24 UTC)" executed successfully -9110898d07c7 INFO: 127.0.0.1:57926 - "GET /api/health HTTP/1.1" 200 OK +17403c53ff2c INFO: 10.89.0.7:35600 - "POST /api/jails/bangui-sim/reload HTTP/1.1" 404 Not Found +17403c53ff2c INFO: 10.89.0.7:35614 - "POST /api/jails/bangui-sim/stop HTTP/1.1" 404 Not Found +2. access list simulator - -9110898d07c7 executing functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) -9110898d07c7 operation functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) -9110898d07c7 operation functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 INFO: 10.89.0.3:53218 - "GET /api/dashboard/status HTTP/1.1" 200 OK -9110898d07c7 executing functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) -9110898d07c7 operation functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) -9110898d07c7 operation functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 INFO: 10.89.0.3:53230 - "GET /api/dashboard/status HTTP/1.1" 200 OK -9110898d07c7 executing functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) -9110898d07c7 operation functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) -9110898d07c7 operation functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 {"command": ["get", "dbfile"], "event": "fail2ban_sending_command", "timestamp": "2026-03-06T17:49:14.550932Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"command": ["get", "dbfile"], "event": "fail2ban_received_response", "timestamp": "2026-03-06T17:49:14.551431Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"db_path": "/config/fail2ban/fail2ban.sqlite3", "since": 1772732954, "range": "24h", "event": "ban_service_list_bans", "timestamp": "2026-03-06T17:49:14.551521Z", "logger": "app.services.ban_service", "level": "info"} -9110898d07c7 executing .connector at 0x7abad4a08540> -9110898d07c7 returning exception unable to open database file -9110898d07c7 executing .close_and_stop at 0x7abad4b3ede0> -9110898d07c7 operation .close_and_stop at 0x7abad4b3ede0> completed -9110898d07c7 {"path": "/api/dashboard/bans", "method": "GET", "event": "unhandled_exception", "timestamp": "2026-03-06T17:49:14.552411Z", "logger": "app.main", "level": "error", "exception": "Traceback (most recent call last):\n File \"/opt/venv/lib/python3.12/site-packages/starlette/middleware/errors.py\", line 164, in __call__\n await self.app(scope, receive, _send)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py\", line 191, in __call__\n with recv_stream, send_stream, collapse_excgroups():\n ^^^^^^^^^^^^^^^^^^^^\n File \"/usr/local/lib/python3.12/contextlib.py\", line 158, in __exit__\n self.gen.throw(value)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/_utils.py\", line 87, in collapse_excgroups\n raise exc\n File \"/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py\", line 193, in __call__\n response = await self.dispatch_func(request, call_next)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/app/app/main.py\", line 294, in dispatch\n return await call_next(request)\n ^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py\", line 168, in call_next\n raise app_exc from app_exc.__cause__ or app_exc.__context__\n File \"/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py\", line 144, in coro\n await self.app(scope, receive_or_disconnect, send_no_error)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/middleware/cors.py\", line 87, in __call__\n await self.app(scope, receive, send)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/middleware/exceptions.py\", line 63, in __call__\n await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py\", line 53, in wrapped_app\n raise exc\n File \"/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py\", line 42, in wrapped_app\n await app(scope, receive, sender)\n File \"/opt/venv/lib/python3.12/site-packages/fastapi/middleware/asyncexitstack.py\", line 18, in __call__\n await self.app(scope, receive, send)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/routing.py\", line 716, in __call__\n await self.middleware_stack(scope, receive, send)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/routing.py\", line 736, in app\n await route.handle(scope, receive, send)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/routing.py\", line 290, in handle\n await self.app(scope, receive, send)\n File \"/opt/venv/lib/python3.12/site-packages/fastapi/routing.py\", line 129, in app\n await wrap_app_handling_exceptions(app, request)(scope, receive, send)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py\", line 53, in wrapped_app\n raise exc\n File \"/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py\", line 42, in wrapped_app\n await app(scope, receive, sender)\n File \"/opt/venv/lib/python3.12/site-packages/fastapi/routing.py\", line 115, in app\n response = await f(request)\n ^^^^^^^^^^^^^^^^\n File \"/opt/venv/lib/python3.12/site-packages/fastapi/routing.py\", line 630, in app\n raw_response = await run_endpoint_function(\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/opt/venv/lib/python3.12/site-packages/fastapi/routing.py\", line 323, in run_endpoint_function\n return await dependant.call(**values)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/app/app/routers/dashboard.py\", line 107, in get_dashboard_bans\n return await ban_service.list_bans(\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/app/app/services/ban_service.py\", line 181, in list_bans\n async with aiosqlite.connect(f\"file:{db_path}?mode=ro\", uri=True) as f2b_db:\n File \"/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py\", line 181, in __aenter__\n return await self\n ^^^^^^^^^^\n File \"/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py\", line 168, in _connect\n self._connection = await future\n ^^^^^^^^^^^^\n File \"/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py\", line 63, in _connection_worker_thread\n result = function()\n ^^^^^^^^^^\n File \"/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py\", line 466, in connector\n return sqlite3.connect(loc, **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nsqlite3.OperationalError: unable to open database file"} -9110898d07c7 INFO: 10.89.0.3:53234 - "GET /api/dashboard/bans?range=24h&page=1&page_size=100 HTTP/1.1" 500 Internal Server Error -9110898d07c7 ERROR: Exception in ASGI application -9110898d07c7 Traceback (most recent call last): -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/uvicorn/protocols/http/httptools_impl.py", line 416, in run_asgi -9110898d07c7 result = await app( # type: ignore[func-returns-value] -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/uvicorn/middleware/proxy_headers.py", line 60, in __call__ -9110898d07c7 return await self.app(scope, receive, send) -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/fastapi/applications.py", line 1160, in __call__ -9110898d07c7 await super().__call__(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/applications.py", line 107, in __call__ -9110898d07c7 await self.middleware_stack(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/errors.py", line 186, in __call__ -9110898d07c7 raise exc -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/errors.py", line 164, in __call__ -9110898d07c7 await self.app(scope, receive, _send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 191, in __call__ -9110898d07c7 with recv_stream, send_stream, collapse_excgroups(): -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/usr/local/lib/python3.12/contextlib.py", line 158, in __exit__ -9110898d07c7 self.gen.throw(value) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/_utils.py", line 87, in collapse_excgroups -9110898d07c7 raise exc -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 193, in __call__ -9110898d07c7 response = await self.dispatch_func(request, call_next) -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/app/app/main.py", line 294, in dispatch -9110898d07c7 return await call_next(request) -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 168, in call_next -9110898d07c7 raise app_exc from app_exc.__cause__ or app_exc.__context__ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 144, in coro -9110898d07c7 await self.app(scope, receive_or_disconnect, send_no_error) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/cors.py", line 87, in __call__ -9110898d07c7 await self.app(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/exceptions.py", line 63, in __call__ -9110898d07c7 await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app -9110898d07c7 raise exc -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app -9110898d07c7 await app(scope, receive, sender) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/fastapi/middleware/asyncexitstack.py", line 18, in __call__ -9110898d07c7 await self.app(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/routing.py", line 716, in __call__ -9110898d07c7 await self.middleware_stack(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/routing.py", line 736, in app -9110898d07c7 await route.handle(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/routing.py", line 290, in handle -9110898d07c7 await self.app(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/fastapi/routing.py", line 129, in app -9110898d07c7 await wrap_app_handling_exceptions(app, request)(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app -9110898d07c7 raise exc -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app -9110898d07c7 await app(scope, receive, sender) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/fastapi/routing.py", line 115, in app -9110898d07c7 response = await f(request) -9110898d07c7 ^^^^^^^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/fastapi/routing.py", line 630, in app -9110898d07c7 raw_response = await run_endpoint_function( -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/fastapi/routing.py", line 323, in run_endpoint_function -9110898d07c7 return await dependant.call(**values) -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/app/app/routers/dashboard.py", line 107, in get_dashboard_bans -9110898d07c7 return await ban_service.list_bans( -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/app/app/services/ban_service.py", line 181, in list_bans -9110898d07c7 async with aiosqlite.connect(f"file:{db_path}?mode=ro", uri=True) as f2b_db: -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py", line 181, in __aenter__ -9110898d07c7 return await self -9110898d07c7 ^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py", line 168, in _connect -9110898d07c7 self._connection = await future -9110898d07c7 ^^^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py", line 63, in _connection_worker_thread -9110898d07c7 result = function() -9110898d07c7 ^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py", line 466, in connector -9110898d07c7 return sqlite3.connect(loc, **kwargs) -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 sqlite3.OperationalError: unable to open database file -9110898d07c7 executing functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) -9110898d07c7 operation functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) -9110898d07c7 operation functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 INFO: 10.89.0.3:51584 - "GET /api/dashboard/status HTTP/1.1" 200 OK -9110898d07c7 executing functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) -9110898d07c7 operation functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) -9110898d07c7 operation functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 INFO: 10.89.0.3:51594 - "GET /api/dashboard/status HTTP/1.1" 200 OK -9110898d07c7 executing functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) -9110898d07c7 operation functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) -9110898d07c7 operation functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 {"command": ["get", "dbfile"], "event": "fail2ban_sending_command", "timestamp": "2026-03-06T17:50:15.290244Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"command": ["get", "dbfile"], "event": "fail2ban_received_response", "timestamp": "2026-03-06T17:50:15.290773Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"db_path": "/config/fail2ban/fail2ban.sqlite3", "since": 1772733015, "range": "24h", "event": "ban_service_list_accesses", "timestamp": "2026-03-06T17:50:15.290865Z", "logger": "app.services.ban_service", "level": "info"} -9110898d07c7 executing .connector at 0x7abad4a08720> -9110898d07c7 returning exception unable to open database file -9110898d07c7 executing .close_and_stop at 0x7abad4a087c0> -9110898d07c7 operation .close_and_stop at 0x7abad4a087c0> completed -9110898d07c7 {"path": "/api/dashboard/accesses", "method": "GET", "event": "unhandled_exception", "timestamp": "2026-03-06T17:50:15.291399Z", "logger": "app.main", "level": "error", "exception": "Traceback (most recent call last):\n File \"/opt/venv/lib/python3.12/site-packages/starlette/middleware/errors.py\", line 164, in __call__\n await self.app(scope, receive, _send)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py\", line 191, in __call__\n with recv_stream, send_stream, collapse_excgroups():\n ^^^^^^^^^^^^^^^^^^^^\n File \"/usr/local/lib/python3.12/contextlib.py\", line 158, in __exit__\n self.gen.throw(value)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/_utils.py\", line 87, in collapse_excgroups\n raise exc\n File \"/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py\", line 193, in __call__\n response = await self.dispatch_func(request, call_next)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/app/app/main.py\", line 294, in dispatch\n return await call_next(request)\n ^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py\", line 168, in call_next\n raise app_exc from app_exc.__cause__ or app_exc.__context__\n File \"/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py\", line 144, in coro\n await self.app(scope, receive_or_disconnect, send_no_error)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/middleware/cors.py\", line 87, in __call__\n await self.app(scope, receive, send)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/middleware/exceptions.py\", line 63, in __call__\n await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py\", line 53, in wrapped_app\n raise exc\n File \"/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py\", line 42, in wrapped_app\n await app(scope, receive, sender)\n File \"/opt/venv/lib/python3.12/site-packages/fastapi/middleware/asyncexitstack.py\", line 18, in __call__\n await self.app(scope, receive, send)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/routing.py\", line 716, in __call__\n await self.middleware_stack(scope, receive, send)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/routing.py\", line 736, in app\n await route.handle(scope, receive, send)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/routing.py\", line 290, in handle\n await self.app(scope, receive, send)\n File \"/opt/venv/lib/python3.12/site-packages/fastapi/routing.py\", line 129, in app\n await wrap_app_handling_exceptions(app, request)(scope, receive, send)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py\", line 53, in wrapped_app\n raise exc\n File \"/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py\", line 42, in wrapped_app\n await app(scope, receive, sender)\n File \"/opt/venv/lib/python3.12/site-packages/fastapi/routing.py\", line 115, in app\n response = await f(request)\n ^^^^^^^^^^^^^^^^\n File \"/opt/venv/lib/python3.12/site-packages/fastapi/routing.py\", line 630, in app\n raw_response = await run_endpoint_function(\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/opt/venv/lib/python3.12/site-packages/fastapi/routing.py\", line 323, in run_endpoint_function\n return await dependant.call(**values)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/app/app/routers/dashboard.py\", line 152, in get_dashboard_accesses\n return await ban_service.list_accesses(\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/app/app/services/ban_service.py\", line 280, in list_accesses\n async with aiosqlite.connect(f\"file:{db_path}?mode=ro\", uri=True) as f2b_db:\n File \"/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py\", line 181, in __aenter__\n return await self\n ^^^^^^^^^^\n File \"/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py\", line 168, in _connect\n self._connection = await future\n ^^^^^^^^^^^^\n File \"/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py\", line 63, in _connection_worker_thread\n result = function()\n ^^^^^^^^^^\n File \"/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py\", line 466, in connector\n return sqlite3.connect(loc, **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nsqlite3.OperationalError: unable to open database file"} -9110898d07c7 INFO: 10.89.0.3:51598 - "GET /api/dashboard/accesses?range=24h&page=1&page_size=100 HTTP/1.1" 500 Internal Server Error -9110898d07c7 ERROR: Exception in ASGI application -9110898d07c7 Traceback (most recent call last): -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/uvicorn/protocols/http/httptools_impl.py", line 416, in run_asgi -9110898d07c7 result = await app( # type: ignore[func-returns-value] -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/uvicorn/middleware/proxy_headers.py", line 60, in __call__ -9110898d07c7 return await self.app(scope, receive, send) -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/fastapi/applications.py", line 1160, in __call__ -9110898d07c7 await super().__call__(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/applications.py", line 107, in __call__ -9110898d07c7 await self.middleware_stack(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/errors.py", line 186, in __call__ -9110898d07c7 raise exc -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/errors.py", line 164, in __call__ -9110898d07c7 await self.app(scope, receive, _send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 191, in __call__ -9110898d07c7 with recv_stream, send_stream, collapse_excgroups(): -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/usr/local/lib/python3.12/contextlib.py", line 158, in __exit__ -9110898d07c7 self.gen.throw(value) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/_utils.py", line 87, in collapse_excgroups -9110898d07c7 raise exc -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 193, in __call__ -9110898d07c7 response = await self.dispatch_func(request, call_next) -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/app/app/main.py", line 294, in dispatch -9110898d07c7 return await call_next(request) -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 168, in call_next -9110898d07c7 raise app_exc from app_exc.__cause__ or app_exc.__context__ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 144, in coro -9110898d07c7 await self.app(scope, receive_or_disconnect, send_no_error) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/cors.py", line 87, in __call__ -9110898d07c7 await self.app(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/exceptions.py", line 63, in __call__ -9110898d07c7 await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app -9110898d07c7 raise exc -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app -9110898d07c7 await app(scope, receive, sender) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/fastapi/middleware/asyncexitstack.py", line 18, in __call__ -9110898d07c7 await self.app(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/routing.py", line 716, in __call__ -9110898d07c7 await self.middleware_stack(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/routing.py", line 736, in app -9110898d07c7 await route.handle(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/routing.py", line 290, in handle -9110898d07c7 await self.app(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/fastapi/routing.py", line 129, in app -9110898d07c7 await wrap_app_handling_exceptions(app, request)(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app -9110898d07c7 raise exc -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app -9110898d07c7 await app(scope, receive, sender) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/fastapi/routing.py", line 115, in app -9110898d07c7 response = await f(request) -9110898d07c7 ^^^^^^^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/fastapi/routing.py", line 630, in app -9110898d07c7 raw_response = await run_endpoint_function( -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/fastapi/routing.py", line 323, in run_endpoint_function -9110898d07c7 return await dependant.call(**values) -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/app/app/routers/dashboard.py", line 152, in get_dashboard_accesses -9110898d07c7 return await ban_service.list_accesses( -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/app/app/services/ban_service.py", line 280, in list_accesses -9110898d07c7 async with aiosqlite.connect(f"file:{db_path}?mode=ro", uri=True) as f2b_db: -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py", line 181, in __aenter__ -9110898d07c7 return await self -9110898d07c7 ^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py", line 168, in _connect -9110898d07c7 self._connection = await future -9110898d07c7 ^^^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py", line 63, in _connection_worker_thread -9110898d07c7 result = function() -9110898d07c7 ^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py", line 466, in connector -9110898d07c7 return sqlite3.connect(loc, **kwargs) -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 sqlite3.OperationalError: unable to open database file -9110898d07c7 executing functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) -9110898d07c7 operation functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) -9110898d07c7 operation functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 INFO: 10.89.0.3:51600 - "GET /api/dashboard/status HTTP/1.1" 200 OK -9110898d07c7 executing functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) -9110898d07c7 operation functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) -9110898d07c7 operation functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 INFO: 10.89.0.3:51606 - "GET /api/dashboard/status HTTP/1.1" 200 OK -9110898d07c7 INFO: 127.0.0.1:60528 - "GET /api/health HTTP/1.1" 200 OK - -3. ✅ DONE — World Map page - **Root cause:** `/api/dashboard/bans/by-country` returned 500 due to the same database mount issue (task 1/2). - **Fix:** Compose bind-mount fix (task 1) resolves the backend error. The complete World Map feature is implemented: `MapPage.tsx`, `WorldMap.tsx` (SVG via react-simple-maps), `useMapData` hook, `api/map.ts`, `types/map.ts`, and the backend `bans_by_country` endpoint with geo enrichment. - -~~3. World map~~ - - -9110898d07c7 executing functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) -9110898d07c7 operation functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) -9110898d07c7 operation functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 INFO: 10.89.0.3:43234 - "GET /api/dashboard/status HTTP/1.1" 200 OK -9110898d07c7 executing functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) -9110898d07c7 operation functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) -9110898d07c7 operation functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 INFO: 10.89.0.3:43236 - "GET /api/dashboard/status HTTP/1.1" 200 OK -9110898d07c7 executing functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) -9110898d07c7 operation functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) -9110898d07c7 operation functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 {"command": ["get", "dbfile"], "event": "fail2ban_sending_command", "timestamp": "2026-03-06T17:50:59.369697Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"command": ["get", "dbfile"], "event": "fail2ban_received_response", "timestamp": "2026-03-06T17:50:59.370154Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"db_path": "/config/fail2ban/fail2ban.sqlite3", "since": 1772733059, "range": "24h", "event": "ban_service_bans_by_country", "timestamp": "2026-03-06T17:50:59.370213Z", "logger": "app.services.ban_service", "level": "info"} -9110898d07c7 executing .connector at 0x7abad4a2aca0> -9110898d07c7 returning exception unable to open database file -9110898d07c7 executing .close_and_stop at 0x7abad4a2af20> -9110898d07c7 operation .close_and_stop at 0x7abad4a2af20> completed -9110898d07c7 {"path": "/api/dashboard/bans/by-country", "method": "GET", "event": "unhandled_exception", "timestamp": "2026-03-06T17:50:59.370651Z", "logger": "app.main", "level": "error", "exception": "Traceback (most recent call last):\n File \"/opt/venv/lib/python3.12/site-packages/starlette/middleware/errors.py\", line 164, in __call__\n await self.app(scope, receive, _send)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py\", line 191, in __call__\n with recv_stream, send_stream, collapse_excgroups():\n ^^^^^^^^^^^^^^^^^^^^\n File \"/usr/local/lib/python3.12/contextlib.py\", line 158, in __exit__\n self.gen.throw(value)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/_utils.py\", line 87, in collapse_excgroups\n raise exc\n File \"/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py\", line 193, in __call__\n response = await self.dispatch_func(request, call_next)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/app/app/main.py\", line 294, in dispatch\n return await call_next(request)\n ^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py\", line 168, in call_next\n raise app_exc from app_exc.__cause__ or app_exc.__context__\n File \"/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py\", line 144, in coro\n await self.app(scope, receive_or_disconnect, send_no_error)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/middleware/cors.py\", line 87, in __call__\n await self.app(scope, receive, send)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/middleware/exceptions.py\", line 63, in __call__\n await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py\", line 53, in wrapped_app\n raise exc\n File \"/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py\", line 42, in wrapped_app\n await app(scope, receive, sender)\n File \"/opt/venv/lib/python3.12/site-packages/fastapi/middleware/asyncexitstack.py\", line 18, in __call__\n await self.app(scope, receive, send)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/routing.py\", line 716, in __call__\n await self.middleware_stack(scope, receive, send)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/routing.py\", line 736, in app\n await route.handle(scope, receive, send)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/routing.py\", line 290, in handle\n await self.app(scope, receive, send)\n File \"/opt/venv/lib/python3.12/site-packages/fastapi/routing.py\", line 129, in app\n await wrap_app_handling_exceptions(app, request)(scope, receive, send)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py\", line 53, in wrapped_app\n raise exc\n File \"/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py\", line 42, in wrapped_app\n await app(scope, receive, sender)\n File \"/opt/venv/lib/python3.12/site-packages/fastapi/routing.py\", line 115, in app\n response = await f(request)\n ^^^^^^^^^^^^^^^^\n File \"/opt/venv/lib/python3.12/site-packages/fastapi/routing.py\", line 630, in app\n raw_response = await run_endpoint_function(\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/opt/venv/lib/python3.12/site-packages/fastapi/routing.py\", line 323, in run_endpoint_function\n return await dependant.call(**values)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/app/app/routers/dashboard.py\", line 192, in get_bans_by_country\n return await ban_service.bans_by_country(\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/app/app/services/ban_service.py\", line 366, in bans_by_country\n async with aiosqlite.connect(f\"file:{db_path}?mode=ro\", uri=True) as f2b_db:\n File \"/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py\", line 181, in __aenter__\n return await self\n ^^^^^^^^^^\n File \"/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py\", line 168, in _connect\n self._connection = await future\n ^^^^^^^^^^^^\n File \"/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py\", line 63, in _connection_worker_thread\n result = function()\n ^^^^^^^^^^\n File \"/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py\", line 466, in connector\n return sqlite3.connect(loc, **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nsqlite3.OperationalError: unable to open database file"} -9110898d07c7 INFO: 10.89.0.3:43246 - "GET /api/dashboard/bans/by-country?range=24h HTTP/1.1" 500 Internal Server Error -9110898d07c7 ERROR: Exception in ASGI application -9110898d07c7 Traceback (most recent call last): -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/uvicorn/protocols/http/httptools_impl.py", line 416, in run_asgi -9110898d07c7 result = await app( # type: ignore[func-returns-value] -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/uvicorn/middleware/proxy_headers.py", line 60, in __call__ -9110898d07c7 return await self.app(scope, receive, send) -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/fastapi/applications.py", line 1160, in __call__ -9110898d07c7 await super().__call__(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/applications.py", line 107, in __call__ -9110898d07c7 await self.middleware_stack(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/errors.py", line 186, in __call__ -9110898d07c7 raise exc -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/errors.py", line 164, in __call__ -9110898d07c7 await self.app(scope, receive, _send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 191, in __call__ -9110898d07c7 with recv_stream, send_stream, collapse_excgroups(): -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/usr/local/lib/python3.12/contextlib.py", line 158, in __exit__ -9110898d07c7 self.gen.throw(value) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/_utils.py", line 87, in collapse_excgroups -9110898d07c7 raise exc -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 193, in __call__ -9110898d07c7 response = await self.dispatch_func(request, call_next) -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/app/app/main.py", line 294, in dispatch -9110898d07c7 return await call_next(request) -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 168, in call_next -9110898d07c7 raise app_exc from app_exc.__cause__ or app_exc.__context__ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 144, in coro -9110898d07c7 await self.app(scope, receive_or_disconnect, send_no_error) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/cors.py", line 87, in __call__ -9110898d07c7 await self.app(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/exceptions.py", line 63, in __call__ -9110898d07c7 await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app -9110898d07c7 raise exc -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app -9110898d07c7 await app(scope, receive, sender) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/fastapi/middleware/asyncexitstack.py", line 18, in __call__ -9110898d07c7 await self.app(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/routing.py", line 716, in __call__ -9110898d07c7 await self.middleware_stack(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/routing.py", line 736, in app -9110898d07c7 await route.handle(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/routing.py", line 290, in handle -9110898d07c7 await self.app(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/fastapi/routing.py", line 129, in app -9110898d07c7 await wrap_app_handling_exceptions(app, request)(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app -9110898d07c7 raise exc -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app -9110898d07c7 await app(scope, receive, sender) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/fastapi/routing.py", line 115, in app -9110898d07c7 response = await f(request) -9110898d07c7 ^^^^^^^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/fastapi/routing.py", line 630, in app -9110898d07c7 raw_response = await run_endpoint_function( -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/fastapi/routing.py", line 323, in run_endpoint_function -9110898d07c7 return await dependant.call(**values) -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/app/app/routers/dashboard.py", line 192, in get_bans_by_country -9110898d07c7 return await ban_service.bans_by_country( -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/app/app/services/ban_service.py", line 366, in bans_by_country -9110898d07c7 async with aiosqlite.connect(f"file:{db_path}?mode=ro", uri=True) as f2b_db: -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py", line 181, in __aenter__ -9110898d07c7 return await self -9110898d07c7 ^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py", line 168, in _connect -9110898d07c7 self._connection = await future -9110898d07c7 ^^^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py", line 63, in _connection_worker_thread -9110898d07c7 result = function() -9110898d07c7 ^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py", line 466, in connector -9110898d07c7 return sqlite3.connect(loc, **kwargs) -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 sqlite3.OperationalError: unable to open database file -9110898d07c7 executing functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) -9110898d07c7 operation functools.partial(, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) -9110898d07c7 operation functools.partial(, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 executing functools.partial() -9110898d07c7 operation functools.partial() completed -9110898d07c7 {"command": ["get", "dbfile"], "event": "fail2ban_sending_command", "timestamp": "2026-03-06T17:50:59.377467Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"command": ["get", "dbfile"], "event": "fail2ban_received_response", "timestamp": "2026-03-06T17:50:59.378109Z", "logger": "app.utils.fail2ban_client", "level": "debug"} -9110898d07c7 {"db_path": "/config/fail2ban/fail2ban.sqlite3", "since": 1772733059, "range": "24h", "event": "ban_service_bans_by_country", "timestamp": "2026-03-06T17:50:59.378383Z", "logger": "app.services.ban_service", "level": "info"} -9110898d07c7 executing .connector at 0x7abad4b3e660> -9110898d07c7 returning exception unable to open database file -9110898d07c7 executing .close_and_stop at 0x7abad4b3dda0> -9110898d07c7 operation .close_and_stop at 0x7abad4b3dda0> completed -9110898d07c7 {"path": "/api/dashboard/bans/by-country", "method": "GET", "event": "unhandled_exception", "timestamp": "2026-03-06T17:50:59.380270Z", "logger": "app.main", "level": "error", "exception": "Traceback (most recent call last):\n File \"/opt/venv/lib/python3.12/site-packages/starlette/middleware/errors.py\", line 164, in __call__\n await self.app(scope, receive, _send)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py\", line 191, in __call__\n with recv_stream, send_stream, collapse_excgroups():\n ^^^^^^^^^^^^^^^^^^^^\n File \"/usr/local/lib/python3.12/contextlib.py\", line 158, in __exit__\n self.gen.throw(value)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/_utils.py\", line 87, in collapse_excgroups\n raise exc\n File \"/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py\", line 193, in __call__\n response = await self.dispatch_func(request, call_next)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/app/app/main.py\", line 294, in dispatch\n return await call_next(request)\n ^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py\", line 168, in call_next\n raise app_exc from app_exc.__cause__ or app_exc.__context__\n File \"/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py\", line 144, in coro\n await self.app(scope, receive_or_disconnect, send_no_error)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/middleware/cors.py\", line 87, in __call__\n await self.app(scope, receive, send)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/middleware/exceptions.py\", line 63, in __call__\n await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py\", line 53, in wrapped_app\n raise exc\n File \"/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py\", line 42, in wrapped_app\n await app(scope, receive, sender)\n File \"/opt/venv/lib/python3.12/site-packages/fastapi/middleware/asyncexitstack.py\", line 18, in __call__\n await self.app(scope, receive, send)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/routing.py\", line 716, in __call__\n await self.middleware_stack(scope, receive, send)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/routing.py\", line 736, in app\n await route.handle(scope, receive, send)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/routing.py\", line 290, in handle\n await self.app(scope, receive, send)\n File \"/opt/venv/lib/python3.12/site-packages/fastapi/routing.py\", line 129, in app\n await wrap_app_handling_exceptions(app, request)(scope, receive, send)\n File \"/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py\", line 53, in wrapped_app\n raise exc\n File \"/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py\", line 42, in wrapped_app\n await app(scope, receive, sender)\n File \"/opt/venv/lib/python3.12/site-packages/fastapi/routing.py\", line 115, in app\n response = await f(request)\n ^^^^^^^^^^^^^^^^\n File \"/opt/venv/lib/python3.12/site-packages/fastapi/routing.py\", line 630, in app\n raw_response = await run_endpoint_function(\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/opt/venv/lib/python3.12/site-packages/fastapi/routing.py\", line 323, in run_endpoint_function\n return await dependant.call(**values)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/app/app/routers/dashboard.py\", line 192, in get_bans_by_country\n return await ban_service.bans_by_country(\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/app/app/services/ban_service.py\", line 366, in bans_by_country\n async with aiosqlite.connect(f\"file:{db_path}?mode=ro\", uri=True) as f2b_db:\n File \"/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py\", line 181, in __aenter__\n return await self\n ^^^^^^^^^^\n File \"/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py\", line 168, in _connect\n self._connection = await future\n ^^^^^^^^^^^^\n File \"/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py\", line 63, in _connection_worker_thread\n result = function()\n ^^^^^^^^^^\n File \"/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py\", line 466, in connector\n return sqlite3.connect(loc, **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nsqlite3.OperationalError: unable to open database file"} -9110898d07c7 INFO: 10.89.0.3:43258 - "GET /api/dashboard/bans/by-country?range=24h HTTP/1.1" 500 Internal Server Error -9110898d07c7 ERROR: Exception in ASGI application -9110898d07c7 Traceback (most recent call last): -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/uvicorn/protocols/http/httptools_impl.py", line 416, in run_asgi -9110898d07c7 result = await app( # type: ignore[func-returns-value] -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/uvicorn/middleware/proxy_headers.py", line 60, in __call__ -9110898d07c7 return await self.app(scope, receive, send) -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/fastapi/applications.py", line 1160, in __call__ -9110898d07c7 await super().__call__(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/applications.py", line 107, in __call__ -9110898d07c7 await self.middleware_stack(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/errors.py", line 186, in __call__ -9110898d07c7 raise exc -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/errors.py", line 164, in __call__ -9110898d07c7 await self.app(scope, receive, _send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 191, in __call__ -9110898d07c7 with recv_stream, send_stream, collapse_excgroups(): -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/usr/local/lib/python3.12/contextlib.py", line 158, in __exit__ -9110898d07c7 self.gen.throw(value) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/_utils.py", line 87, in collapse_excgroups -9110898d07c7 raise exc -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 193, in __call__ -9110898d07c7 response = await self.dispatch_func(request, call_next) -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/app/app/main.py", line 294, in dispatch -9110898d07c7 return await call_next(request) -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 168, in call_next -9110898d07c7 raise app_exc from app_exc.__cause__ or app_exc.__context__ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 144, in coro -9110898d07c7 await self.app(scope, receive_or_disconnect, send_no_error) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/cors.py", line 87, in __call__ -9110898d07c7 await self.app(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/middleware/exceptions.py", line 63, in __call__ -9110898d07c7 await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app -9110898d07c7 raise exc -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app -9110898d07c7 await app(scope, receive, sender) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/fastapi/middleware/asyncexitstack.py", line 18, in __call__ -9110898d07c7 await self.app(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/routing.py", line 716, in __call__ -9110898d07c7 await self.middleware_stack(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/routing.py", line 736, in app -9110898d07c7 await route.handle(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/routing.py", line 290, in handle -9110898d07c7 await self.app(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/fastapi/routing.py", line 129, in app -9110898d07c7 await wrap_app_handling_exceptions(app, request)(scope, receive, send) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app -9110898d07c7 raise exc -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app -9110898d07c7 await app(scope, receive, sender) -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/fastapi/routing.py", line 115, in app -9110898d07c7 response = await f(request) -9110898d07c7 ^^^^^^^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/fastapi/routing.py", line 630, in app -9110898d07c7 raw_response = await run_endpoint_function( -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/fastapi/routing.py", line 323, in run_endpoint_function -9110898d07c7 return await dependant.call(**values) -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/app/app/routers/dashboard.py", line 192, in get_bans_by_country -9110898d07c7 return await ban_service.bans_by_country( -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 File "/app/app/services/ban_service.py", line 366, in bans_by_country -9110898d07c7 async with aiosqlite.connect(f"file:{db_path}?mode=ro", uri=True) as f2b_db: -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py", line 181, in __aenter__ -9110898d07c7 return await self -9110898d07c7 ^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py", line 168, in _connect -9110898d07c7 self._connection = await future -9110898d07c7 ^^^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py", line 63, in _connection_worker_thread -9110898d07c7 result = function() -9110898d07c7 ^^^^^^^^^^ -9110898d07c7 File "/opt/venv/lib/python3.12/site-packages/aiosqlite/core.py", line 466, in connector -9110898d07c7 return sqlite3.connect(loc, **kwargs) -9110898d07c7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -9110898d07c7 sqlite3.OperationalError: unable to open database file -9110898d07c7 INFO: 127.0.0.1:41282 - "GET /api/health HTTP/1.1" 200 OK - +make a script that put a access log entry, so i can check the access list feature. +check and modify /home/lukas/Volume/repo/BanGUI/Docker/fail2ban-dev-config/README.md \ No newline at end of file diff --git a/backend/app/routers/jails.py b/backend/app/routers/jails.py index 09db8c7..d6ded0f 100644 --- a/backend/app/routers/jails.py +++ b/backend/app/routers/jails.py @@ -248,7 +248,8 @@ async def stop_jail( """Stop a running fail2ban jail. The jail will no longer monitor logs or issue new bans. Existing bans - may or may not be removed depending on fail2ban configuration. + may or may not be removed depending on fail2ban configuration. If the + jail is already stopped the request succeeds silently (idempotent). Args: request: Incoming request (used to access ``app.state``). @@ -259,7 +260,6 @@ async def stop_jail( :class:`~app.models.jail.JailCommandResponse` confirming the stop. Raises: - HTTPException: 404 when the jail does not exist. HTTPException: 409 when fail2ban reports the operation failed. HTTPException: 502 when fail2ban is unreachable. """ @@ -267,8 +267,6 @@ async def stop_jail( try: await jail_service.stop_jail(socket_path, name) return JailCommandResponse(message=f"Jail {name!r} stopped.", jail=name) - except JailNotFoundError: - raise _not_found(name) from None except JailOperationError as exc: raise _conflict(str(exc)) from exc except Fail2BanConnectionError as exc: diff --git a/backend/app/services/jail_service.py b/backend/app/services/jail_service.py index 8990215..5416c4f 100644 --- a/backend/app/services/jail_service.py +++ b/backend/app/services/jail_service.py @@ -431,12 +431,14 @@ async def start_jail(socket_path: str, name: str) -> None: async def stop_jail(socket_path: str, name: str) -> None: """Stop a running fail2ban jail. + If the jail is not currently active (already stopped), this is a no-op + and no error is raised — the operation is idempotent. + Args: socket_path: Path to the fail2ban Unix domain socket. name: Jail name to stop. Raises: - JailNotFoundError: If *name* is not a known jail. JailOperationError: If fail2ban reports the operation failed. ~app.utils.fail2ban_client.Fail2BanConnectionError: If the socket cannot be reached. @@ -447,7 +449,9 @@ async def stop_jail(socket_path: str, name: str) -> None: log.info("jail_stopped", jail=name) except ValueError as exc: if _is_not_found_error(exc): - raise JailNotFoundError(name) from exc + # Jail is already stopped or was never running — treat as a no-op. + log.info("jail_stop_noop", jail=name) + return raise JailOperationError(str(exc)) from exc @@ -482,6 +486,13 @@ async def set_idle(socket_path: str, name: str, *, on: bool) -> None: async def reload_jail(socket_path: str, name: str) -> None: """Reload a single fail2ban jail to pick up configuration changes. + The reload protocol requires a non-empty configuration stream. Without + one, fail2ban's end-of-reload phase removes every jail that received no + configuration commands — permanently deleting the jail from the running + daemon. Sending ``['start', name]`` as the minimal stream is sufficient: + ``startJail`` removes the jail from ``reload_state``, which causes the + end phase to *commit* the reload instead of deleting the jail. + Args: socket_path: Path to the fail2ban Unix domain socket. name: Jail name to reload. @@ -494,7 +505,7 @@ async def reload_jail(socket_path: str, name: str) -> None: """ client = Fail2BanClient(socket_path=socket_path, timeout=_SOCKET_TIMEOUT) try: - _ok(await client.send(["reload", name, [], []])) + _ok(await client.send(["reload", name, [], [["start", name]]])) log.info("jail_reloaded", jail=name) except ValueError as exc: if _is_not_found_error(exc): @@ -505,6 +516,11 @@ async def reload_jail(socket_path: str, name: str) -> None: async def reload_all(socket_path: str) -> None: """Reload all fail2ban jails at once. + Fetches the current jail list first so that a ``['start', name]`` entry + can be included in the config stream for every active jail. Without a + non-empty stream the end-of-reload phase deletes every jail that received + no configuration commands. + Args: socket_path: Path to the fail2ban Unix domain socket. @@ -515,7 +531,13 @@ async def reload_all(socket_path: str) -> None: """ client = Fail2BanClient(socket_path=socket_path, timeout=_SOCKET_TIMEOUT) try: - _ok(await client.send(["reload", "--all", [], []])) + # Resolve jail names so we can build the minimal config stream. + status_raw = _ok(await client.send(["status"])) + status_dict = _to_dict(status_raw) + jail_list_raw: str = str(status_dict.get("Jail list", "")) + jail_names = [n.strip() for n in jail_list_raw.split(",") if n.strip()] + stream: list[list[str]] = [["start", n] for n in jail_names] + _ok(await client.send(["reload", "--all", [], stream])) log.info("all_jails_reloaded") except ValueError as exc: raise JailOperationError(str(exc)) from exc diff --git a/backend/tests/test_routers/test_jails.py b/backend/tests/test_routers/test_jails.py index 6c3ebdc..2841334 100644 --- a/backend/tests/test_routers/test_jails.py +++ b/backend/tests/test_routers/test_jails.py @@ -257,17 +257,19 @@ class TestStopJail: assert resp.status_code == 200 - async def test_404_for_unknown_jail(self, jails_client: AsyncClient) -> None: - """POST /api/jails/ghost/stop returns 404.""" - from app.services.jail_service import JailNotFoundError + async def test_200_for_already_stopped_jail(self, jails_client: AsyncClient) -> None: + """POST /api/jails/sshd/stop returns 200 even when the jail is already stopped. + stop_jail is idempotent — service returns None rather than raising + JailNotFoundError when the jail is not present in fail2ban's runtime. + """ with patch( "app.routers.jails.jail_service.stop_jail", - AsyncMock(side_effect=JailNotFoundError("ghost")), + AsyncMock(return_value=None), ): - resp = await jails_client.post("/api/jails/ghost/stop") + resp = await jails_client.post("/api/jails/sshd/stop") - assert resp.status_code == 404 + assert resp.status_code == 200 # --------------------------------------------------------------------------- diff --git a/backend/tests/test_services/test_jail_service.py b/backend/tests/test_services/test_jail_service.py index 6f3f902..2d23a54 100644 --- a/backend/tests/test_services/test_jail_service.py +++ b/backend/tests/test_services/test_jail_service.py @@ -283,13 +283,28 @@ class TestJailControls: await jail_service.set_idle(_SOCKET, "sshd", on=False) # should not raise async def test_reload_jail_success(self) -> None: - """reload_jail sends the reload command without error.""" - with _patch_client({"reload|sshd|[]|[]": (0, "OK")}): + """reload_jail sends a reload command with a minimal start-stream.""" + with _patch_client({"reload|sshd|[]|[['start', 'sshd']]": (0, "OK")}): await jail_service.reload_jail(_SOCKET, "sshd") # should not raise async def test_reload_all_success(self) -> None: - """reload_all sends the reload --all command without error.""" - with _patch_client({"reload|--all|[]|[]": (0, "OK")}): + """reload_all fetches jail names then sends reload --all with a start-stream.""" + with _patch_client( + { + "status": _make_global_status("sshd, nginx"), + "reload|--all|[]|[['start', 'sshd'], ['start', 'nginx']]": (0, "OK"), + } + ): + await jail_service.reload_all(_SOCKET) # should not raise + + async def test_reload_all_no_jails_still_sends_reload(self) -> None: + """reload_all works with an empty jail list (sends an empty stream).""" + with _patch_client( + { + "status": (0, [("Number of jail", 0), ("Jail list", "")]), + "reload|--all|[]|[]": (0, "OK"), + } + ): await jail_service.reload_all(_SOCKET) # should not raise async def test_start_not_found_raises(self) -> None: @@ -297,8 +312,13 @@ class TestJailControls: with _patch_client({"start|ghost": (1, Exception("Unknown jail: 'ghost'"))}), pytest.raises(JailNotFoundError): await jail_service.start_jail(_SOCKET, "ghost") + async def test_stop_jail_already_stopped_is_noop(self) -> None: + """stop_jail silently succeeds when the jail is not found (idempotent).""" + with _patch_client({"stop|sshd": (1, Exception("UnknownJailException('sshd')"))}): + await jail_service.stop_jail(_SOCKET, "sshd") # should not raise + async def test_stop_operation_error_raises(self) -> None: - """stop_jail raises JailOperationError on fail2ban error code.""" + """stop_jail raises JailOperationError on a non-not-found fail2ban error.""" with _patch_client({"stop|sshd": (1, Exception("cannot stop"))}), pytest.raises(JailOperationError): await jail_service.stop_jail(_SOCKET, "sshd")