feature/ignore-self-toggle #1

Merged
lukas.pupkalipinski merged 97 commits from feature/ignore-self-toggle into main 2026-03-14 21:19:28 +01:00
2 changed files with 84 additions and 0 deletions
Showing only changes of commit cf2336c0bc - Show all commits

View File

@@ -6,6 +6,7 @@ files directly on the filesystem (``jail.d/``, ``filter.d/``, ``action.d/``).
Endpoints:
* ``GET /api/config/jail-files`` — list all jail config files
* ``GET /api/config/jail-files/{filename}`` — get one jail config file (with content)
* ``PUT /api/config/jail-files/{filename}`` — overwrite a jail config file
* ``PUT /api/config/jail-files/{filename}/enabled`` — enable/disable a jail config
* ``GET /api/config/filters`` — list all filter files
* ``GET /api/config/filters/{name}`` — get one filter file (with content)
@@ -169,6 +170,46 @@ async def get_jail_config_file(
raise _service_unavailable(str(exc)) from exc
@router.put(
"/jail-files/{filename}",
status_code=status.HTTP_204_NO_CONTENT,
summary="Overwrite a jail.d config file with new raw content",
)
async def write_jail_config_file(
request: Request,
_auth: AuthDep,
filename: _FilenamePath,
body: ConfFileUpdateRequest,
) -> None:
"""Overwrite the raw content of an existing jail.d config file.
The change is written directly to disk. You must reload fail2ban
(``POST /api/config/reload``) separately for the change to take effect.
Args:
request: Incoming request.
_auth: Validated session.
filename: Filename of the jail config file (e.g. ``sshd.conf``).
body: New raw file content.
Raises:
HTTPException: 400 if *filename* is unsafe or content is invalid.
HTTPException: 404 if the file does not exist.
HTTPException: 503 if the config directory is unavailable.
"""
config_dir: str = request.app.state.settings.fail2ban_config_dir
try:
await file_config_service.write_jail_config_file(config_dir, filename, body)
except ConfigFileNameError as exc:
raise _bad_request(str(exc)) from exc
except ConfigFileNotFoundError:
raise _not_found(filename) from None
except ConfigFileWriteError as exc:
raise _bad_request(str(exc)) from exc
except ConfigDirError as exc:
raise _service_unavailable(str(exc)) from exc
@router.put(
"/jail-files/{filename}/enabled",
status_code=status.HTTP_204_NO_CONTENT,

View File

@@ -418,6 +418,49 @@ async def create_jail_config_file(
return await asyncio.get_event_loop().run_in_executor(None, _do)
async def write_jail_config_file(
config_dir: str,
filename: str,
req: ConfFileUpdateRequest,
) -> None:
"""Overwrite an existing jail.d config file with new raw content.
Args:
config_dir: Path to the fail2ban configuration directory.
filename: Filename including extension (e.g. ``sshd.conf``).
req: :class:`~app.models.file_config.ConfFileUpdateRequest` with new
content.
Raises:
ConfigFileNotFoundError: If the file does not exist.
ConfigFileNameError: If *filename* is unsafe or has a bad extension.
ConfigFileWriteError: If the file cannot be written.
ConfigDirError: If *config_dir* does not exist.
"""
def _do() -> None:
jail_d = _resolve_subdir(config_dir, "jail.d").resolve()
if not jail_d.is_dir():
raise ConfigFileNotFoundError(filename)
path = (jail_d / filename).resolve()
_assert_within(jail_d, path)
if path.suffix not in _CONF_EXTENSIONS:
raise ConfigFileNameError(
f"Only .conf and .local files are supported, got {filename!r}."
)
if not path.is_file():
raise ConfigFileNotFoundError(filename)
try:
path.write_text(req.content, encoding="utf-8")
except OSError as exc:
raise ConfigFileWriteError(
f"Cannot write {filename!r}: {exc}"
) from exc
log.info("jail_config_file_written", filename=filename)
await asyncio.get_event_loop().run_in_executor(None, _do)
# ---------------------------------------------------------------------------
# Internal helpers — generic conf file listing / reading / writing
# ---------------------------------------------------------------------------