""" Moduł monitorujący Postfix SMTP server """ import re import time from .base import LogModule class PostfixModule(LogModule): """Moduł monitorujący Postfix""" def __init__(self, config, daemon): super().__init__(config, daemon) # Kompiluj wzorce regex dla wydajności self.patterns = self._load_patterns() # Regex do wyciągania IP z logów Postfix # Obsługuje zarówno unknown[IP] jak i hostname[IP] self.ip_pattern = re.compile( r'(?:unknown|[\w\-\.]+)\[(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\]' ) # Ścieżka do pliku logu self.log_file = config.get('module_postfix', 'log_file', fallback='/var/log/mail.log') self.logger.info(f"Loaded {len(self.patterns)} patterns for Postfix") def _load_patterns(self): """Ładuje wzorce z konfiguracji""" patterns = [] pattern_names = self.config.get('module_postfix', 'patterns', fallback='').split(',') for name in pattern_names: name = name.strip() if not name: continue section = f'pattern_{name}' if section not in self.config: self.logger.warning(f"Pattern section '{section}' not found in config") continue try: regex = self.config.get(section, 'regex') score = self.config.getint(section, 'score', fallback=1) patterns.append({ 'name': name, 'regex': re.compile(regex, re.IGNORECASE), 'score': score }) self.logger.debug(f"Loaded pattern '{name}': {regex} (score: {score})") except Exception as e: self.logger.error(f"Error loading pattern '{name}': {e}") return patterns def _run(self): """Główna pętla - tail -f na pliku logu""" self.logger.info(f"Tailing log file: {self.log_file}") try: with open(self.log_file, 'r') as f: # Przejdź na koniec pliku f.seek(0, 2) while self.running: line = f.readline() if line: self.process_line(line.strip()) else: # Brak nowych linii, czekaj chwilę time.sleep(0.1) except FileNotFoundError: self.logger.error(f"Log file not found: {self.log_file}") except PermissionError: self.logger.error(f"Permission denied reading: {self.log_file}") except Exception as e: self.logger.error(f"Error tailing log: {e}") def process_line(self, line): """ Przetwarza linię z logu Postfix Przykłady linii: - postfix/smtpd[1234]: warning: unknown[1.2.3.4]: SASL LOGIN authentication failed - postfix/smtpd[1234]: connect from unknown[1.2.3.4] """ # Wyciągnij IP ip_match = self.ip_pattern.search(line) if not ip_match: return ip = ip_match.group(1) # Pomiń lokalne IP if ip.startswith('127.') or ip.startswith('192.168.') or ip.startswith('10.'): return # Sprawdź wzorce for pattern in self.patterns: if pattern['regex'].search(line): self.logger.debug( f"Pattern '{pattern['name']}' matched for IP {ip}" ) self.logger.debug(f"Line: {line}") # Zgłoś niepowodzenie do demona self.daemon.track_failure(ip, pattern['score']) # Tylko pierwszy pasujący wzorzec break