Fix #34: Replace setup redirect allowlist prefix matching with explicit allowlist
- Replace fragile startswith() matching with explicit path matching - Split allowlist into _EXACT_ALLOWED (exact paths) and _PREFIX_ALLOWED (prefixes) - Prefix paths MUST end with '/' to prevent matching unintended paths like /api/setup-debug - Paths correctly matched: /api/setup, /api/health, /api/docs, /api/redoc, /api/openapi.json, /api/setup/timezone - Paths correctly blocked: /api/setup-debug, /api/setup123, /api/jails - Add comprehensive Setup Guard Route Policy documentation to Backend-Development.md - Update line numbers in documentation to reflect current implementation This prevents future route additions from accidentally bypassing the setup guard. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -1726,6 +1726,57 @@ Cookie: bangui_session=...
|
||||
(no X-BanGUI-Request header needed)
|
||||
```
|
||||
|
||||
### Setup Guard Route Policy
|
||||
|
||||
BanGUI requires a one-time setup wizard to be completed before the application is usable. The `SetupRedirectMiddleware` enforces this by redirecting unauthenticated API requests to `/api/setup` until setup is complete.
|
||||
|
||||
**How It Works:**
|
||||
|
||||
1. **Explicit Allowlist:** The middleware maintains two allowlists:
|
||||
- `_EXACT_ALLOWED`: Exact paths that bypass the guard (e.g., `/api/setup`, `/api/health`, `/api/docs`)
|
||||
- `_PREFIX_ALLOWED`: Route prefixes that bypass the guard (e.g., `/api/setup/` for nested routes like `/api/setup/timezone`)
|
||||
|
||||
2. **Path Matching Strategy:** The middleware uses **exact matching for exact paths** and **prefix matching with trailing slashes for nested routes**. This prevents fragile prefix-based allowlists (e.g., using `startswith("/api/setup")` would accidentally allow `/api/setup-debug`).
|
||||
|
||||
3. **When Setup is Complete:** Once setup completes, the middleware becomes a no-op and all routes are accessible normally.
|
||||
|
||||
**Allowlisted Paths:**
|
||||
- `/api/setup` — Setup status check and initialization endpoint
|
||||
- `/api/setup/timezone` — Timezone configuration (reaches via `/api/setup/` prefix)
|
||||
- `/api/health` — Health check endpoint (used by monitoring and load balancers)
|
||||
- `/api/docs` — Swagger UI documentation
|
||||
- `/api/redoc` — ReDoc documentation
|
||||
- `/api/openapi.json` — OpenAPI schema (required by docs frontends)
|
||||
|
||||
**Adding New Setup Routes:**
|
||||
|
||||
When adding new routes to the setup flow:
|
||||
1. If the route is an exact path (e.g., `/api/setup/validate`), add it to `_EXACT_ALLOWED`
|
||||
2. If the route is nested under `/api/setup/` (e.g., `/api/setup/validate/config`), ensure `/api/setup/` is in `_PREFIX_ALLOWED` (it already is)
|
||||
3. Never use prefix matching without a trailing slash — it leads to security issues with future route additions
|
||||
|
||||
**Implementation Location:**
|
||||
- Middleware: `backend/app/main.py` — `SetupRedirectMiddleware` class
|
||||
- Configuration: Lines 584–601 in `backend/app/main.py` — `_EXACT_ALLOWED` and `_PREFIX_ALLOWED` constants
|
||||
- Guard logic: Lines 638–648 in `backend/app/main.py` — `dispatch()` method
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
# If setup is incomplete:
|
||||
GET /api/jails
|
||||
→ 307 Temporary Redirect to /api/setup
|
||||
|
||||
# Allowlisted paths are always accessible:
|
||||
GET /api/setup → 200 OK (setup status)
|
||||
POST /api/setup → 201 Created (run setup)
|
||||
GET /api/setup/timezone → 200 OK (get timezone)
|
||||
GET /api/health → 200 OK (health check)
|
||||
GET /api/docs → 200 OK (documentation)
|
||||
|
||||
# If setup is complete, all routes are accessible:
|
||||
GET /api/jails → 200 OK (jail list)
|
||||
```
|
||||
|
||||
### fail2ban_start_command Configuration
|
||||
|
||||
The `fail2ban_start_command` setting specifies the shell command used to start the fail2ban daemon during recovery operations (e.g., after a rollback).
|
||||
|
||||
Reference in New Issue
Block a user