feature/ignore-self-toggle #1
@@ -6,6 +6,7 @@ files directly on the filesystem (``jail.d/``, ``filter.d/``, ``action.d/``).
|
|||||||
Endpoints:
|
Endpoints:
|
||||||
* ``GET /api/config/jail-files`` — list all jail config files
|
* ``GET /api/config/jail-files`` — list all jail config files
|
||||||
* ``GET /api/config/jail-files/{filename}`` — get one jail config file (with content)
|
* ``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
|
* ``PUT /api/config/jail-files/{filename}/enabled`` — enable/disable a jail config
|
||||||
* ``GET /api/config/filters`` — list all filter files
|
* ``GET /api/config/filters`` — list all filter files
|
||||||
* ``GET /api/config/filters/{name}`` — get one filter file (with content)
|
* ``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
|
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(
|
@router.put(
|
||||||
"/jail-files/{filename}/enabled",
|
"/jail-files/{filename}/enabled",
|
||||||
status_code=status.HTTP_204_NO_CONTENT,
|
status_code=status.HTTP_204_NO_CONTENT,
|
||||||
|
|||||||
@@ -418,6 +418,49 @@ async def create_jail_config_file(
|
|||||||
return await asyncio.get_event_loop().run_in_executor(None, _do)
|
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
|
# Internal helpers — generic conf file listing / reading / writing
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user