TASK-014: Add log path validation to prevent arbitrary file access

Restrict monitored log paths to a configurable allowlist of safe directories
to prevent authenticated users from instructing fail2ban to monitor arbitrary
files on the system, which could leak contents via fail2ban logging.

Changes:
- Add 'allowed_log_dirs' setting to Settings (defaults to /var/log, /config/log)
- Add @field_validator to AddLogPathRequest to validate log paths at request time
- Validator resolves paths to canonical form and checks against allowed prefixes
- Use Path.is_relative_to() to prevent prefix bypass attacks like /var/log_evil
- Add comprehensive tests for valid/invalid paths and symlink handling
- Update Features.md and Backend-Development.md with security documentation

Security improvements:
- Blocks access to sensitive files (/etc/shadow, /etc/passwd, etc.)
- Resolves symlinks before validation to prevent escape routes
- Uses proper path comparison instead of string prefix matching
- Configurable via BANGUI_ALLOWED_LOG_DIRS environment variable

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-04-26 13:49:04 +02:00
parent 2331567bd7
commit 308cf680a7
5 changed files with 223 additions and 4 deletions

View File

@@ -142,6 +142,14 @@ class Settings(BaseSettings):
"Used for listing, viewing, and editing configuration files through the web UI."
),
)
allowed_log_dirs: list[str] = Field(
default_factory=lambda: ["/var/log", "/config/log"],
description=(
"List of allowed directory prefixes for jail log paths. "
"Any log path added must resolve to a path within one of these directories. "
"Use absolute paths. Symlinks are resolved before validation."
),
)
fail2ban_start_command: str = Field(
default="fail2ban-client start",
description=(
@@ -193,4 +201,4 @@ def get_settings() -> Settings:
A validated :class:`Settings` object. Raises :class:`pydantic.ValidationError`
if required keys are absent or values fail validation.
"""
return Settings() # type: ignore[call-arg] # pydantic-settings populates required fields from env vars
return Settings() # pydantic-settings populates required fields from env vars