upload
This commit is contained in:
		
							
								
								
									
										54
									
								
								modules/base.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								modules/base.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | ||||
| """ | ||||
| Bazowa klasa dla modułów monitorowania logów | ||||
| """ | ||||
|  | ||||
| import logging | ||||
| import time | ||||
| import threading | ||||
|  | ||||
|  | ||||
| class LogModule: | ||||
|     """Bazowa klasa dla modułów monitorowania""" | ||||
|      | ||||
|     def __init__(self, config, daemon): | ||||
|         """ | ||||
|         Args: | ||||
|             config: ConfigParser object z konfiguracją | ||||
|             daemon: Referencja do głównego demona | ||||
|         """ | ||||
|         self.config = config | ||||
|         self.daemon = daemon | ||||
|         self.logger = logging.getLogger(self.__class__.__name__) | ||||
|         self.running = False | ||||
|         self.thread = None | ||||
|          | ||||
|     def start(self): | ||||
|         """Uruchamia moduł w osobnym wątku""" | ||||
|         if self.running: | ||||
|             self.logger.warning("Module already running") | ||||
|             return | ||||
|              | ||||
|         self.running = True | ||||
|         self.thread = threading.Thread(target=self._run, daemon=True) | ||||
|         self.thread.start() | ||||
|         self.logger.info(f"{self.__class__.__name__} started") | ||||
|          | ||||
|     def stop(self): | ||||
|         """Zatrzymuje moduł""" | ||||
|         self.running = False | ||||
|         if self.thread and self.thread.is_alive(): | ||||
|             self.thread.join(timeout=5) | ||||
|         self.logger.info(f"{self.__class__.__name__} stopped") | ||||
|          | ||||
|     def _run(self): | ||||
|         """Główna pętla modułu - do nadpisania w klasach potomnych""" | ||||
|         raise NotImplementedError("Subclasses must implement _run()") | ||||
|          | ||||
|     def process_line(self, line): | ||||
|         """ | ||||
|         Przetwarza pojedynczą linię logu | ||||
|          | ||||
|         Args: | ||||
|             line: Linia tekstu z logu | ||||
|         """ | ||||
|         raise NotImplementedError("Subclasses must implement process_line()") | ||||
							
								
								
									
										8
									
								
								modules/init.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								modules/init.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| """ | ||||
| LogMon Modules - Moduły monitorowania różnych aplikacji | ||||
| """ | ||||
|  | ||||
| from .base import LogModule | ||||
| from .postfix import PostfixModule | ||||
|  | ||||
| __all__ = ['LogModule', 'PostfixModule'] | ||||
							
								
								
									
										120
									
								
								modules/postfix.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								modules/postfix.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,120 @@ | ||||
| """ | ||||
| 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 | ||||
		Reference in New Issue
	
	Block a user
	 Mateusz Gruszczyński
					Mateusz Gruszczyński