fix: reload/stop jail 404 + access list simulator

Task 1 — fix Stop/Reload Jail returning 404
  Root cause: reload_jail and reload_all sent an empty config stream
  (["reload", name, [], []]).  In fail2ban's reload protocol the end-of-
  reload phase deletes every jail still in reload_state — i.e. every jail
  that received no configuration commands.  An empty stream means *all*
  affected jails are silently removed from the daemon's runtime, causing
  everything touching those jails afterwards (including stop) to receive
  UnknownJailException → HTTP 404.

  Fixes:
  - reload_jail: send ["start", name] in the config stream; startJail()
    removes the jail from reload_state so the end phase commits instead of
    deletes, and un-idles the jail.
  - reload_all: fetch current jail list first, build a ["start", name]
    entry for every active jail, then send reload --all with that stream.
  - stop_jail: made idempotent — if the jail is already gone (not-found
    error) the operation silently succeeds (200 OK) rather than returning
    404, matching the user expectation that stop = ensure-stopped.
  - Router: removed dead JailNotFoundError handler from stop endpoint.

  391 tests pass (2 new), ruff clean, mypy clean (pre-existing
  config.py error unchanged).

Task 2 — access list simulator
  - Docker/simulate_accesses.sh: writes fake HTTP-scan log lines in
    custom format (bangui-access: http scan from <IP> ...) to
    Docker/logs/access.log so the bangui-access jail detects them.
  - fail2ban/filter.d/bangui-access.conf: failregex matching the above.
  - fail2ban/jail.d/bangui-access.conf: polling jail on access.log,
    same settings as bangui-sim (maxretry=3, bantime=60s).
  - .gitignore: whitelist new bangui-access.conf files.
  - Docker/fail2ban-dev-config/README.md: added "Testing the Access
    List Feature" section with step-by-step instructions and updated
    Configuration Reference + Troubleshooting.
This commit is contained in:
2026-03-06 19:49:31 +01:00
parent 73c1300d9f
commit 08b8f3872a
10 changed files with 239 additions and 965 deletions

2
.gitignore vendored
View File

@@ -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

View File

@@ -67,6 +67,50 @@ Chains steps 13 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 <COUNT> <SOURCE_IP> <LOG_FILE>
# 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 <IP> "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 13 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 <IP>
# bangui-access (access log):
YYYY-MM-DD HH:MM:SS bangui-access: http scan from <IP> "<METHOD> <path> HTTP/1.1" <STATUS>
```
### iptables / permission errors

View File

@@ -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 <IP> "<METHOD> <path> HTTP/1.1" <STATUS>
# ──────────────────────────────────────────────────────────────
[Definition]
failregex = ^.* bangui-access: http scan from <HOST> ".*" [45]\d\d\s*$
ignoreregex =

View File

@@ -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

View File

@@ -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 <IP> "<METHOD> <path> HTTP/1.1" <STATUS>
#
# 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"

View File

@@ -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(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 INFO: 10.89.0.3:55942 - "GET /api/dashboard/status HTTP/1.1" 200 OK
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>) 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(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 INFO: 10.89.0.3:54778 - "GET /api/dashboard/status HTTP/1.1" 200 OK
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 INFO: 10.89.0.3:54780 - "GET /api/dashboard/status HTTP/1.1" 200 OK
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 INFO: 10.89.0.3:54790 - "GET /api/dashboard/status HTTP/1.1" 200 OK
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 INFO: 10.89.0.3:54804 - "GET /api/dashboard/status HTTP/1.1" 200 OK
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>) 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 <function connect.<locals>.connector at 0x7abad4a2aac0>
9110898d07c7 returning exception unable to open database file
9110898d07c7 executing <function Connection.stop.<locals>.close_and_stop at 0x7abad4a29580>
9110898d07c7 operation <function Connection.stop.<locals>.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(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 INFO: 10.89.0.3:59946 - "GET /api/dashboard/status HTTP/1.1" 200 OK
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>) 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(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 INFO: 10.89.0.3:44266 - "GET /api/dashboard/status HTTP/1.1" 200 OK
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>) 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(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 INFO: 10.89.0.3:53218 - "GET /api/dashboard/status HTTP/1.1" 200 OK
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 INFO: 10.89.0.3:53230 - "GET /api/dashboard/status HTTP/1.1" 200 OK
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6495740>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6495740>) 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 <function connect.<locals>.connector at 0x7abad4a08540>
9110898d07c7 returning exception unable to open database file
9110898d07c7 executing <function Connection.stop.<locals>.close_and_stop at 0x7abad4b3ede0>
9110898d07c7 operation <function Connection.stop.<locals>.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(<built-in method execute of sqlite3.Connection object at 0x7abad
2. ✅ DONE — fix access list 500 error
**Root cause:** Same compose volume issue as task 1 — the backend could not open the fail2ban SQLite database.
**Fix:** Resolved by the same bind-mount change from task 1 (`./fail2ban-dev-config:/config:ro`).
~~2. issues with access list~~
client.ts:55 GET http://127.0.0.1:5173/api/dashboard/accesses?range=24h&page=1&page_size=100 500 (Internal Server Error)
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad49b7540>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad49b7540>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad49b7540>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad49b7540>) completed
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad49b7540>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad49b7540>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad49b7540>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad49b7540>) completed
9110898d07c7 INFO: 10.89.0.3:51584 - "GET /api/dashboard/status HTTP/1.1" 200 OK
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad49b7540>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad49b7540>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad49b7540>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad49b7540>) completed
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad49b7540>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad49b7540>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad49b7540>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad49b7540>) completed
9110898d07c7 INFO: 10.89.0.3:51594 - "GET /api/dashboard/status HTTP/1.1" 200 OK
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad49b7540>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad49b7540>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad49b7540>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad49b7540>) completed
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad49b7540>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad49b7540>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad49b7540>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad49b7540>) 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 <function connect.<locals>.connector at 0x7abad4a08720>
9110898d07c7 returning exception unable to open database file
9110898d07c7 executing <function Connection.stop.<locals>.close_and_stop at 0x7abad4a087c0>
9110898d07c7 operation <function Connection.stop.<locals>.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(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad49b7540>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad49b7540>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad49b7540>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad49b7540>) completed
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6494ec0>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6494ec0>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6494ec0>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6494ec0>) completed
9110898d07c7 INFO: 10.89.0.3:51600 - "GET /api/dashboard/status HTTP/1.1" 200 OK
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6494ec0>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6494ec0>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6494ec0>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6494ec0>) completed
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6494ec0>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6494ec0>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6494ec0>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6494ec0>) 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(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad49b7440>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad49b7440>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad49b7440>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad49b7440>) completed
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad49b7440>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad49b7440>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad49b7440>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad49b7440>) completed
9110898d07c7 INFO: 10.89.0.3:43234 - "GET /api/dashboard/status HTTP/1.1" 200 OK
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad49b7440>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad49b7440>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad49b7440>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad49b7440>) completed
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad49b7440>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad49b7440>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad49b7440>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad49b7440>) completed
9110898d07c7 INFO: 10.89.0.3:43236 - "GET /api/dashboard/status HTTP/1.1" 200 OK
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad49b7440>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad49b7440>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad49b7440>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad49b7440>) completed
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad49b7440>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad49b7440>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad49b7440>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad49b7440>) 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 <function connect.<locals>.connector at 0x7abad4a2aca0>
9110898d07c7 returning exception unable to open database file
9110898d07c7 executing <function Connection.stop.<locals>.close_and_stop at 0x7abad4a2af20>
9110898d07c7 operation <function Connection.stop.<locals>.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(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT value FROM settings WHERE key = ?', ('setup_completed',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad49b7440>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad49b7440>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad49b7440>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad49b7440>) completed
9110898d07c7 executing functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',))
9110898d07c7 operation functools.partial(<built-in method execute of sqlite3.Connection object at 0x7abad6521210>, 'SELECT id, token, created_at, expires_at FROM sessions WHERE token = ?', ('e829a2a69e384640c259f069c8574de6b8bb486257f4a7f005a05757260ce832',)) completed
9110898d07c7 executing functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6476ec0>)
9110898d07c7 operation functools.partial(<built-in method fetchone of sqlite3.Cursor object at 0x7abad6476ec0>) completed
9110898d07c7 executing functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6476ec0>)
9110898d07c7 operation functools.partial(<built-in method close of sqlite3.Cursor object at 0x7abad6476ec0>) 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 <function connect.<locals>.connector at 0x7abad4b3e660>
9110898d07c7 returning exception unable to open database file
9110898d07c7 executing <function Connection.stop.<locals>.close_and_stop at 0x7abad4b3dda0>
9110898d07c7 operation <function Connection.stop.<locals>.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

View File

@@ -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:

View File

@@ -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

View File

@@ -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
# ---------------------------------------------------------------------------

View File

@@ -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")