"""Diffuse chaque BanEvent (création et mise à jour géoloc) aux clients WebSocket via le channel layer, et déclenche la géolocalisation asynchrone à la création. Journalise aussi les verrouillages django-axes (protection /admin/, cf. AXES_* dans config/settings/base.py) pour que fail2ban (generic/fail2ban/jail.d/emitter-admin-auth.conf) puisse réagir.""" import logging from typing import Any from asgiref.sync import async_to_sync from axes.signals import user_locked_out from channels.layers import get_channel_layer from django.db.models.signals import post_save from django.dispatch import receiver from django.http import HttpRequest from .consumers import GROUP_NAME from .models import BanEvent, NodeRegistry from .serializers import event_payload from .tasks import geolocate_ip fail2ban_axes_logger = logging.getLogger('fail2ban.axes') @receiver(post_save, sender=BanEvent) def broadcast_ban_event(sender: type[BanEvent], instance: BanEvent, created: bool, **kwargs: Any) -> None: channel_layer = get_channel_layer() if channel_layer is not None: # Se déclenche deux fois par event (création, puis mise à jour # géoloc par geolocate_ip ci-dessous) : lookup unique indexé, sans # conséquence à tourner deux fois (résultat identique). node_alias = NodeRegistry.objects.filter(node_id=instance.node).values_list('alias', flat=True).first() async_to_sync(channel_layer.group_send)( GROUP_NAME, {'type': 'ban.event', 'payload': event_payload(instance, node_alias)}, ) if created: geolocate_ip.delay(instance.pk) @receiver(user_locked_out) def log_axes_lockout(sender: Any, request: HttpRequest, username: str | None, ip_address: str, **kwargs: Any) -> None: """Un seul point de vérité pour le seuil : axes décide (AXES_FAILURE_LIMIT) qu'il y a un problème, cette ligne fait réagir fail2ban (ban réseau + propagation SYNC_BAN aux autres noeuds, comme toute autre jail — cf. jail.local [DEFAULT] action). maxretry=1 côté jail : chaque ligne ici EST déjà la décision de verrouillage, pas la peine de recompter. Format de message imposé par filter.d/app-auth.conf (générique, réutilisable par toute appli du VPS — pas seulement ce projet) : "Verrouillage après échecs répétés depuis (...)", = "émetteur" ici, un seul mot (\\S+ côté filtre).""" fail2ban_axes_logger.warning( 'Verrouillage émetteur après échecs répétés depuis %s (utilisateur tenté : %s)', ip_address, username or '?', )