Files
logmon/modules/postfix.py
Mateusz Gruszczyński 7b41672d05 upload
2025-10-28 21:27:10 +01:00

121 lines
4.0 KiB
Python

"""
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