Harden fail2ban integration and mark task complete

This commit is contained in:
2026-04-06 20:20:14 +02:00
parent 594f55d157
commit 3ccfc20c64
5 changed files with 20 additions and 76 deletions

View File

@@ -91,12 +91,9 @@ _RETRYABLE_ERRNOS: frozenset[int] = frozenset(
_RETRY_MAX_ATTEMPTS: int = 3
_RETRY_INITIAL_BACKOFF: float = 0.15 # seconds; doubles on each attempt
# Maximum number of concurrent in-flight socket commands. Operations that
# exceed this cap wait until a slot is available.
# Maximum number of concurrent in-flight socket commands per client.
# Operations that exceed this cap wait until a slot is available.
_COMMAND_SEMAPHORE_CONCURRENCY: int = 10
# The semaphore is created lazily on the first send() call so it binds to the
# event loop that is actually running (important for test isolation).
_command_semaphore: asyncio.Semaphore | None = None
class Fail2BanConnectionError(Exception):
@@ -266,6 +263,9 @@ class Fail2BanClient:
"""
self.socket_path: str = socket_path
self.timeout: float = timeout
self._command_semaphore: asyncio.Semaphore = asyncio.Semaphore(
_COMMAND_SEMAPHORE_CONCURRENCY
)
async def send(self, command: Fail2BanCommand) -> object:
"""Send a command to fail2ban and return the response.
@@ -290,18 +290,14 @@ class Fail2BanClient:
connection is unexpectedly closed.
Fail2BanProtocolError: If the response cannot be decoded.
"""
global _command_semaphore
if _command_semaphore is None:
_command_semaphore = asyncio.Semaphore(_COMMAND_SEMAPHORE_CONCURRENCY)
if _command_semaphore.locked():
if self._command_semaphore.locked():
log.debug(
"fail2ban_command_waiting_semaphore",
command=command,
concurrency_limit=_COMMAND_SEMAPHORE_CONCURRENCY,
)
async with _command_semaphore:
async with self._command_semaphore:
log.debug("fail2ban_sending_command", command=command)
loop: asyncio.AbstractEventLoop = asyncio.get_event_loop()
try: