#!/usr/bin/env python3 import os import re import time import subprocess import threading from datetime import datetime try: from croniter import croniter except ImportError: print("Bitte 'croniter' via pip installieren!") exit(1) LOG_FILE = '/var/log/mail/mail.log' WHITELIST_DURATION_SEC = 24 * 60 * 60 # 24 Stunden CRON_SCHEDULE = "0 * * * *" # Jede Stunde active_ips = {} # Regex für Dovecot IMAP/POP3 erfolgreiche Logins LOGIN_REGEX = re.compile(r"dovecot: (?:imap|pop3)-login: Login: user=<[^>]+>.*rip=([0-9]{1,3}(?:\.[0-9]{1,3}){3}),") # Private Netze (Docker/Local) ignorieren IGNORE_REGEX = re.compile(r"^(172\.|10\.|192\.168\.|127\.)") def run_command(cmd): try: subprocess.run(cmd, shell=True, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) except Exception as e: print(f"Fehler bei: {cmd} - {e}") def cleanup_job(): """Cron-Thread für das stündliche Aufräumen abgelaufener IPs.""" iter = croniter(CRON_SCHEDULE, datetime.now()) while True: next_run = iter.get_next(datetime) sleep_seconds = (next_run - datetime.now()).total_seconds() if sleep_seconds > 0: time.sleep(sleep_seconds) print(f"[{datetime.now()}] Starte stündlichen Whitelist-Cleanup...") now = time.time() expired_ips = [ip for ip, timestamp in active_ips.items() if now - timestamp > WHITELIST_DURATION_SEC] for ip in expired_ips: print(f"[{datetime.now()}] Whitelist für {ip} abgelaufen. Entferne...") run_command(f"fail2ban-client set dovecot delignoreip {ip}") run_command(f"fail2ban-client set postfix delignoreip {ip}") del active_ips[ip] def follow_log(): """Verwendet System 'tail -F', da dies Log-Rotation automatisch handhabt.""" print(f"[{datetime.now()}] Dynamic Whitelist Monitor gestartet...") while not os.path.exists(LOG_FILE): time.sleep(2) process = subprocess.Popen(['tail', '-F', LOG_FILE], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, text=True) for line in process.stdout: match = LOGIN_REGEX.search(line) if match: ip = match.group(1) if IGNORE_REGEX.match(ip): continue now = time.time() # Neue IP in die Fail2ban Whitelist eintragen if ip not in active_ips: print(f"[{datetime.now()}] Neuer erfolgreicher Login von {ip}. Setze auf Whitelist...") run_command(f"fail2ban-client set dovecot addignoreip {ip}") run_command(f"fail2ban-client set postfix addignoreip {ip}") # Timestamp (Last Seen) aktualisieren active_ips[ip] = now if __name__ == '__main__': # Warte kurz, bis Fail2ban nach einem Container-Start hochgefahren ist time.sleep(15) # Cron-Cleanup im Hintergrund starten threading.Thread(target=cleanup_job, daemon=True).start() # Log-Überwachung in der Endlosschleife starten follow_log()