"""Utilities for ensuring required fail2ban jail configuration files exist. BanGUI requires two custom jails — ``manual-Jail`` and ``blocklist-import`` — to be present in the fail2ban ``jail.d`` directory. This module provides :func:`ensure_jail_configs` which checks each of the four files (``*.conf`` template + ``*.local`` override) and creates any that are missing with the correct default content. """ from __future__ import annotations from typing import TYPE_CHECKING import structlog if TYPE_CHECKING: from pathlib import Path log: structlog.stdlib.BoundLogger = structlog.get_logger() # --------------------------------------------------------------------------- # Default file contents # --------------------------------------------------------------------------- _MANUAL_JAIL_CONF = """\ [manual-Jail] enabled = false filter = manual-Jail logpath = /remotelogs/bangui/auth.log backend = polling maxretry = 3 findtime = 120 bantime = 60 ignoreip = 127.0.0.0/8 ::1 172.16.0.0/12 """ _MANUAL_JAIL_LOCAL = """\ [manual-Jail] enabled = true """ _BLOCKLIST_IMPORT_CONF = """\ [blocklist-import] enabled = false filter = logpath = /dev/null backend = auto maxretry = 1 findtime = 1d bantime = 86400 ignoreip = 127.0.0.0/8 ::1 172.16.0.0/12 """ _BLOCKLIST_IMPORT_LOCAL = """\ [blocklist-import] enabled = true """ # --------------------------------------------------------------------------- # File registry: (filename, default_content) # --------------------------------------------------------------------------- _JAIL_FILES: list[tuple[str, str]] = [ ("manual-Jail.conf", _MANUAL_JAIL_CONF), ("manual-Jail.local", _MANUAL_JAIL_LOCAL), ("blocklist-import.conf", _BLOCKLIST_IMPORT_CONF), ("blocklist-import.local", _BLOCKLIST_IMPORT_LOCAL), ] def ensure_jail_configs(jail_d_path: Path) -> None: """Ensure the required fail2ban jail configuration files exist. Checks for ``manual-Jail.conf``, ``manual-Jail.local``, ``blocklist-import.conf``, and ``blocklist-import.local`` inside *jail_d_path*. Any file that is missing is created with its default content. Existing files are **never** overwritten. Args: jail_d_path: Path to the fail2ban ``jail.d`` directory. Will be created (including all parents) if it does not already exist. """ jail_d_path.mkdir(parents=True, exist_ok=True) for filename, default_content in _JAIL_FILES: file_path = jail_d_path / filename if file_path.exists(): log.debug("jail_config_already_exists", path=str(file_path)) else: file_path.write_text(default_content, encoding="utf-8") log.info("jail_config_created", path=str(file_path))