TASK-020: Fix log_target security vulnerability (defense in depth)

**Issue:**
- log_target accepted arbitrary paths, allowing authenticated users to write
  files as root via fail2ban (e.g., /etc/cron.d/bangui-pwned)
- fail2ban runs as root and opens files specified in log_target

**Solution:**
1. **Model layer validation:** Already existed in GlobalConfigUpdate, prevents
   invalid paths before reaching service
2. **Service layer validation:** Added defensive check in update_global_config()
   that validates log_target even if model validation is bypassed
3. **New validation helper:** Added validate_log_target() utility that accepts
   special values (STDOUT, STDERR, SYSLOG) or paths within allowed directories

**Changes:**
- app/utils/path_utils.py: Added validate_log_target() helper
- app/services/config_service.py: Added service-layer validation before
  sending command to fail2ban
- backend/tests: Fixed session_secret length issues in fixtures (min 32 chars)
- backend/tests: Added tests for valid special log targets
- Docs/Backend-Development.md: Documented log_target security requirements

**Test Coverage:**
- Model validation rejects /etc/passwd (existing test)
- Model validation accepts STDOUT, STDERR, SYSLOG special values
- Model validation accepts paths in allowed directories
- Service layer validation tested with special values

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-04-26 14:23:56 +02:00
parent d9022b9d06
commit d476e9d611
7 changed files with 84 additions and 33 deletions

View File

@@ -734,6 +734,27 @@ BANGUI_ALLOWED_LOG_DIRS="/var/log,/config/log" # Default
BANGUI_ALLOWED_LOG_DIRS="/var/log,/config/log,/home/app/logs" # Custom directory
```
### Log Target Validation (fail2ban)
The `log_target` field on the global config endpoint (`PUT /api/config/global`) is critical for security because fail2ban runs as root. Users can only set log targets to:
1. **Special values:** `STDOUT`, `STDERR`, `SYSLOG` (case-insensitive)
2. **File paths:** Must resolve to one of the configured allowed directories (same allowlist as log paths)
**Why This Matters:**
- fail2ban creates/opens files with root privileges. Without validation, an attacker could write to arbitrary system paths (e.g., `/etc/cron.d/malicious_script`).
- Validation occurs at **both** the Pydantic model layer (`GlobalConfigUpdate.validate_log_target()`) **and** the service layer (`update_global_config()`) for defense in depth.
- This prevents both HTTP and non-HTTP attack vectors.
**Implementation:**
```python
# Model layer: Automatic validation via @field_validator
update = GlobalConfigUpdate(log_target="/etc/passwd") # Raises ValidationError → HTTP 422
# Service layer: Defense in depth
await config_service.update_global_config(socket_path, update) # Validates again before sending to fail2ban
```
### Login Rate Limiting
The login endpoint (`POST /api/auth/login`) is protected against brute-force attacks using an in-memory rate limiter.