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

@@ -1,32 +1,3 @@
## TASK-019 — `session_secret` has no minimum-length enforcement
**Severity:** Medium
### Where found
`backend/app/config.py``session_secret: str = Field(..., description="...")`. No `min_length` constraint.
### Why this is needed
`session_secret` is the HMAC key used to sign all session tokens. A secret shorter than 32 characters (256 bits) significantly weakens HMAC-SHA256. The app currently accepts any non-empty string, including a single character.
### Goal
Enforce a minimum secret length of 32 characters at startup.
### What to do
1. Add `min_length=32` to the `session_secret` Field definition.
2. Update the error message to explain: `"session_secret must be at least 32 characters. Generate one with: python -c \"import secrets; print(secrets.token_hex(32))\""`.
### Possible traps and issues
- This is a breaking change for any existing deployment where the secret is shorter than 32 characters. Include a migration note in the changelog.
- The debug compose file uses `dev-secret-do-not-use-in-production` (42 chars) — this already passes the 32-char check, so dev environments are unaffected.
### Docs changes needed
- `Backend-Development.md` — document the `session_secret` constraint.
### Doc references
- [Backend-Development.md](Backend-Development.md) — configuration reference
---
## TASK-020 — `log_target` accepts arbitrary paths — root file write via fail2ban (CRITICAL)
**Severity:** Critical