diff --git a/DMS/Dockerfile b/DMS/Dockerfile index eaba22a..ef3bcc6 100644 --- a/DMS/Dockerfile +++ b/DMS/Dockerfile @@ -23,4 +23,9 @@ RUN chmod +x /scripts/sync.py COPY sieve-schedule /etc/sieve-schedule # 5. Supervisor Konfiguration kopieren -COPY sieve-supervisor.conf /etc/supervisor/conf.d/sieve-sync.conf \ No newline at end of file +COPY sieve-supervisor.conf /etc/supervisor/conf.d/sieve-sync.conf + +# 6. Dynamic Whitelist Script und Supervisor-Config kopieren +COPY dynamic_whitelist.py /scripts/dynamic_whitelist.py +RUN chmod +x /scripts/dynamic_whitelist.py +COPY whitelist-supervisor.conf /etc/supervisor/conf.d/dynamic-whitelist.conf \ No newline at end of file diff --git a/DMS/dynamic_whitelist.py b/DMS/dynamic_whitelist.py new file mode 100644 index 0000000..f984bf3 --- /dev/null +++ b/DMS/dynamic_whitelist.py @@ -0,0 +1,87 @@ +#!/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() \ No newline at end of file diff --git a/DMS/whitelist-supervisor.conf b/DMS/whitelist-supervisor.conf new file mode 100644 index 0000000..60472f5 --- /dev/null +++ b/DMS/whitelist-supervisor.conf @@ -0,0 +1,6 @@ +[program:dynamic-whitelist] +command=/usr/bin/python3 -u /scripts/dynamic_whitelist.py +autostart=true +autorestart=true +stderr_logfile=/var/log/supervisor/dynamic-whitelist.err.log +stdout_logfile=/var/log/supervisor/dynamic-whitelist.out.log \ No newline at end of file