#!/usr/bin/python3 """Fail2ban action: publish each ban/unban event to the local MQTT broker.""" import configparser import datetime import json import socket import sys import uuid from pathlib import Path from typing import Any import paho.mqtt.publish as publish CONFIG_FILE = Path('/etc/fail2ban/mqtt.conf') LOG_FILE = '/var/log/fail2ban.log' # Timeout global sur toutes les connexions socket (évite de bloquer fail2ban) socket.setdefaulttimeout(5) def load_mqtt_config() -> dict[str, Any]: parser = configparser.ConfigParser() if not parser.read(CONFIG_FILE): raise FileNotFoundError(f'{CONFIG_FILE} introuvable — voir mqtt.conf.example') section = parser['mqtt'] return { 'host': section.get('host', '127.0.0.1'), 'port': section.getint('port', 1883), 'username': section['username'], 'password': section['password'], } def log(message: str) -> None: try: with open(LOG_FILE, 'a') as f: f.write('{} [MQTT PUBLISHER] {}\n'.format(datetime.datetime.now(), message)) except OSError as e: print(f'fail2ban mqtt log error: {e}') def publish_message(base_topic: str, mqtt_params: dict[str, Any], topic: str, **payload: Any) -> None: try: message = json.dumps(payload) publish.single(base_topic + topic, message, **mqtt_params) except Exception as e: log(f'publish_message error: {e}') def main() -> None: try: config = load_mqtt_config() except Exception as e: log(f'config error: {e}') return node_id = uuid.getnode() node_type = 'fail2ban' base_topic = f'{node_type}/{node_id}' mqtt_params: dict[str, Any] = dict( qos=0, retain=False, hostname=config['host'], port=config['port'], keepalive=60, auth=dict(username=config['username'], password=config['password']), client_id=f'{node_type}_{node_id}', ) try: payload = json.loads(sys.argv[1]) publish_message(base_topic, mqtt_params, '/jail', node=node_id, **payload) except Exception as e: log(f'main error: {e}') if __name__ == '__main__': main()