dovecot
This commit is contained in:
		
							
								
								
									
										0
									
								
								__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										
											BIN
										
									
								
								backends/__pycache__/__init__.cpython-312.pyc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								backends/__pycache__/__init__.cpython-312.pyc
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								backends/__pycache__/base.cpython-312.pyc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								backends/__pycache__/base.cpython-312.pyc
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								backends/__pycache__/csf.cpython-312.pyc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								backends/__pycache__/csf.cpython-312.pyc
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								backends/__pycache__/iptables.cpython-312.pyc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								backends/__pycache__/iptables.cpython-312.pyc
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								backends/__pycache__/nftables.cpython-312.pyc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								backends/__pycache__/nftables.cpython-312.pyc
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								backends/__pycache__/ufw.cpython-312.pyc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								backends/__pycache__/ufw.cpython-312.pyc
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										165
									
								
								config.ini
									
									
									
									
									
								
							
							
						
						
									
										165
									
								
								config.ini
									
									
									
									
									
								
							| @@ -1,42 +1,191 @@ | |||||||
|  | # ============================================ | ||||||
|  | # LogMon Configuration File | ||||||
|  | # ============================================ | ||||||
|  |  | ||||||
| [general] | [general] | ||||||
|  | # Tryb debug - wyświetla szczegółowe informacje | ||||||
| debug = false | debug = false | ||||||
|  |  | ||||||
|  | # Ścieżka do pliku z logami LogMon | ||||||
| log_file = /var/log/logmon.log | log_file = /var/log/logmon.log | ||||||
|  |  | ||||||
|  | # Plik PID demona | ||||||
| pid_file = /var/run/logmon.pid | pid_file = /var/run/logmon.pid | ||||||
|  |  | ||||||
|  | # Backend firewall: csf, nftables, iptables, ufw | ||||||
| backend = csf | backend = csf | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # ============================================ | ||||||
|  | # Konfiguracja backendów firewall | ||||||
|  | # ============================================ | ||||||
|  |  | ||||||
| [backend_csf] | [backend_csf] | ||||||
|  | # Ścieżka do wykonywania CSF | ||||||
| csf_path = /usr/sbin/csf | csf_path = /usr/sbin/csf | ||||||
| # Dodatkowe opcje CSF |  | ||||||
|  |  | ||||||
| [backend_nftables] | [backend_nftables] | ||||||
| table_name = filter | # Nazwa tabeli i chain dla nftables | ||||||
|  | table_name = inet | ||||||
| chain_name = logmon_block | chain_name = logmon_block | ||||||
|  |  | ||||||
| [backend_iptables] | [backend_iptables] | ||||||
|  | # Nazwa chain dla iptables | ||||||
| chain_name = LOGMON_BLOCK | chain_name = LOGMON_BLOCK | ||||||
|  |  | ||||||
| [backend_ufw] | [backend_ufw] | ||||||
| # UFW nie wymaga dodatkowych parametrów | # UFW nie wymaga dodatkowych parametrów | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # ============================================ | ||||||
|  | # Moduł Postfix - SMTP Server | ||||||
|  | # ============================================ | ||||||
|  |  | ||||||
| [module_postfix] | [module_postfix] | ||||||
|  | # Włącz/wyłącz moduł | ||||||
| enabled = true | enabled = true | ||||||
|  |  | ||||||
|  | # Ścieżka do logu Postfix | ||||||
| log_file = /var/log/mail.log | log_file = /var/log/mail.log | ||||||
| # Alternatywnie dla systemd: |  | ||||||
|  | # Alternatywnie dla systemd journald: | ||||||
| # use_journald = true | # use_journald = true | ||||||
| # journald_unit = postfix.service | # journald_unit = postfix.service | ||||||
|  |  | ||||||
| # Parametry detekcji | # Maksymalna liczba niepowodzeń przed banem | ||||||
| max_failures = 5 | max_failures = 5 | ||||||
|  |  | ||||||
|  | # Okno czasowe w sekundach (domyślnie 60s = 1 minuta) | ||||||
| time_window = 60 | time_window = 60 | ||||||
|  |  | ||||||
|  | # Czas bana w sekundach (domyślnie 86400s = 24 godziny) | ||||||
| ban_duration = 86400 | ban_duration = 86400 | ||||||
|  |  | ||||||
| # Wzorce do wykrywania | # Lista wzorców do wykrywania (oddzielone przecinkami) | ||||||
| patterns = auth_failed,sasl_failed | patterns = postfix_auth_failed,postfix_sasl_failed | ||||||
|  |  | ||||||
| [pattern_auth_failed] |  | ||||||
|  | # ============================================ | ||||||
|  | # Moduł Dovecot - IMAP/POP3 Server | ||||||
|  | # ============================================ | ||||||
|  |  | ||||||
|  | [module_dovecot] | ||||||
|  | # Włącz/wyłącz moduł | ||||||
|  | enabled = true | ||||||
|  |  | ||||||
|  | # Ścieżka do logu Dovecot | ||||||
|  | log_file = /var/log/dovecot-info.log | ||||||
|  |  | ||||||
|  | # Maksymalna liczba niepowodzeń przed banem | ||||||
|  | max_failures = 5 | ||||||
|  |  | ||||||
|  | # Okno czasowe w sekundach (domyślnie 120s = 2 minuty) | ||||||
|  | time_window = 120 | ||||||
|  |  | ||||||
|  | # Czas bana w sekundach (domyślnie 86400s = 24 godziny) | ||||||
|  | ban_duration = 86400 | ||||||
|  |  | ||||||
|  | # Ignoruj błędy SSL/TLS (często są to skanery, nie ataki brute-force) | ||||||
|  | ignore_ssl_errors = true | ||||||
|  |  | ||||||
|  | # Ignoruj połączenia z localhost (127.0.0.1) | ||||||
|  | ignore_localhost = true | ||||||
|  |  | ||||||
|  | # Lista wzorców do wykrywania | ||||||
|  | patterns = dovecot_auth_failed,dovecot_auth_failed_multi | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # ============================================ | ||||||
|  | # Wzorce dla Postfix | ||||||
|  | # ============================================ | ||||||
|  |  | ||||||
|  | [pattern_postfix_auth_failed] | ||||||
|  | # Wykrywa: "authentication failed" | ||||||
| regex = authentication failed | regex = authentication failed | ||||||
| score = 1 | score = 1 | ||||||
|  |  | ||||||
| [pattern_sasl_failed] | [pattern_postfix_sasl_failed] | ||||||
|  | # Wykrywa: "SASL LOGIN authentication failed" i podobne | ||||||
| regex = SASL [A-Z\-\d]+ authentication failed | regex = SASL [A-Z\-\d]+ authentication failed | ||||||
| score = 2 | score = 2 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # ============================================ | ||||||
|  | # Wzorce dla Dovecot | ||||||
|  | # ============================================ | ||||||
|  |  | ||||||
|  | [pattern_dovecot_auth_failed] | ||||||
|  | # Wykrywa: "auth failed, 1 attempts" | ||||||
|  | regex = auth failed, 1 attempts | ||||||
|  | score = 2 | ||||||
|  |  | ||||||
|  | [pattern_dovecot_auth_failed_multi] | ||||||
|  | # Wykrywa: "auth failed, 2 attempts" lub więcej (2-9+) | ||||||
|  | regex = auth failed, [2-9]+ attempts | ||||||
|  | score = 5 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # ============================================ | ||||||
|  | # Dodatkowe moduły (przygotowane do rozbudowy) | ||||||
|  | # ============================================ | ||||||
|  |  | ||||||
|  | # [module_ssh] | ||||||
|  | # enabled = false | ||||||
|  | # log_file = /var/log/auth.log | ||||||
|  | # max_failures = 5 | ||||||
|  | # time_window = 300 | ||||||
|  | # ban_duration = 3600 | ||||||
|  | # patterns = ssh_failed_password,ssh_invalid_user | ||||||
|  |  | ||||||
|  | # [pattern_ssh_failed_password] | ||||||
|  | # regex = Failed password for .+ from | ||||||
|  | # score = 2 | ||||||
|  |  | ||||||
|  | # [pattern_ssh_invalid_user] | ||||||
|  | # regex = Invalid user .+ from | ||||||
|  | # score = 3 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # [module_nginx] | ||||||
|  | # enabled = false | ||||||
|  | # log_file = /var/log/nginx/error.log | ||||||
|  | # max_failures = 10 | ||||||
|  | # time_window = 60 | ||||||
|  | # ban_duration = 3600 | ||||||
|  | # patterns = nginx_404_flood,nginx_403_scan | ||||||
|  |  | ||||||
|  | # [pattern_nginx_404_flood] | ||||||
|  | # regex = \[error\].*GET .* HTTP/ | ||||||
|  | # score = 1 | ||||||
|  |  | ||||||
|  | # [pattern_nginx_403_scan] | ||||||
|  | # regex = 403.*GET | ||||||
|  | # score = 2 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # ============================================ | ||||||
|  | # Whitelist IP (przygotowane do implementacji) | ||||||
|  | # ============================================ | ||||||
|  |  | ||||||
|  | # [whitelist] | ||||||
|  | # # Lista IP które nigdy nie będą banowane (oddzielone przecinkami) | ||||||
|  | # ips = 127.0.0.1,192.168.1.0/24,10.0.0.0/8 | ||||||
|  | #  | ||||||
|  | # # Lub z pliku: | ||||||
|  | # # file = /etc/logmon/whitelist.txt | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # ============================================ | ||||||
|  | # Zaawansowane opcje | ||||||
|  | # ============================================ | ||||||
|  |  | ||||||
|  | # [advanced] | ||||||
|  | # # Maksymalna liczba jednocześnie śledzonych IP | ||||||
|  | # max_tracked_ips = 10000 | ||||||
|  | #  | ||||||
|  | # # Jak często sprawdzać wygasłe bany (w sekundach) | ||||||
|  | # check_expired_interval = 10 | ||||||
|  | #  | ||||||
|  | # # Persystencja - zapisz stan banów do pliku | ||||||
|  | # persist_state = true | ||||||
|  | # persist_file = /var/lib/logmon/state.json | ||||||
|   | |||||||
							
								
								
									
										16
									
								
								logmon.py
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								logmon.py
									
									
									
									
									
								
							| @@ -16,10 +16,12 @@ from collections import defaultdict, deque | |||||||
| from datetime import datetime, timedelta | from datetime import datetime, timedelta | ||||||
| from pathlib import Path | from pathlib import Path | ||||||
|  |  | ||||||
| # Importy z lokalnych modułów |  | ||||||
| from modules import PostfixModule |  | ||||||
| from backends import CSFBackend, NFTablesBackend, IPTablesBackend, UFWBackend |  | ||||||
|  |  | ||||||
|  | sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) | ||||||
|  |  | ||||||
|  | # Importy z lokalnych modułów | ||||||
|  | from modules import PostfixModule, DovecotModule | ||||||
|  | from backends import CSFBackend, NFTablesBackend, IPTablesBackend, UFWBackend | ||||||
|  |  | ||||||
| class LogMonDaemon: | class LogMonDaemon: | ||||||
|     """Główny demon LogMon""" |     """Główny demon LogMon""" | ||||||
| @@ -112,6 +114,14 @@ class LogMonDaemon: | |||||||
|                 self.logger.info("Loaded Postfix module") |                 self.logger.info("Loaded Postfix module") | ||||||
|             except Exception as e: |             except Exception as e: | ||||||
|                 self.logger.error(f"Error loading Postfix module: {e}") |                 self.logger.error(f"Error loading Postfix module: {e}") | ||||||
|  |       # Dovecot module | ||||||
|  |           if self.config.getboolean('module_dovecot', 'enabled', fallback=False): | ||||||
|  |             try: | ||||||
|  |                 module = DovecotModule(self.config, self) | ||||||
|  |                 modules.append(module) | ||||||
|  |                 self.logger.info("Loaded Dovecot module") | ||||||
|  |             except Exception as e: | ||||||
|  |                 self.logger.error(f"Error loading Dovecot module: {e}")         | ||||||
|          |          | ||||||
|         # Tutaj można dodać więcej modułów w przyszłości |         # Tutaj można dodać więcej modułów w przyszłości | ||||||
|         # if self.config.getboolean('module_ssh', 'enabled', fallback=False): |         # if self.config.getboolean('module_ssh', 'enabled', fallback=False): | ||||||
|   | |||||||
| @@ -4,5 +4,7 @@ LogMon Modules - Moduły monitorowania różnych aplikacji | |||||||
| 
 | 
 | ||||||
| from .base import LogModule | from .base import LogModule | ||||||
| from .postfix import PostfixModule | from .postfix import PostfixModule | ||||||
|  | from .dovecot import DovecotModule | ||||||
|  | 
 | ||||||
|  | __all__ = ['LogModule', 'PostfixModule', 'DovecotModule'] | ||||||
| 
 | 
 | ||||||
| __all__ = ['LogModule', 'PostfixModule'] |  | ||||||
							
								
								
									
										
											BIN
										
									
								
								modules/__pycache__/__init__.cpython-312.pyc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								modules/__pycache__/__init__.cpython-312.pyc
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								modules/__pycache__/base.cpython-312.pyc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								modules/__pycache__/base.cpython-312.pyc
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								modules/__pycache__/postfix.cpython-312.pyc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								modules/__pycache__/postfix.cpython-312.pyc
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										162
									
								
								modules/dovecot.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								modules/dovecot.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,162 @@ | |||||||
|  | """ | ||||||
|  | Moduł monitorujący Dovecot IMAP/POP3 server | ||||||
|  | """ | ||||||
|  |  | ||||||
|  | import re | ||||||
|  | import time | ||||||
|  | from .base import LogModule | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class DovecotModule(LogModule): | ||||||
|  |     """Moduł monitorujący Dovecot""" | ||||||
|  |      | ||||||
|  |     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 Dovecot | ||||||
|  |         # Obsługuje format: rip=IP, lip=IP | ||||||
|  |         self.ip_pattern = re.compile(r'rip=(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})') | ||||||
|  |          | ||||||
|  |         # Ścieżka do pliku logu | ||||||
|  |         self.log_file = config.get('module_dovecot', 'log_file',  | ||||||
|  |                                    fallback='/var/log/dovecot-info.log') | ||||||
|  |          | ||||||
|  |         # Czy ignorować błędy SSL/TLS (często są to skanery, nie ataki brute-force) | ||||||
|  |         self.ignore_ssl_errors = config.getboolean('module_dovecot', 'ignore_ssl_errors', | ||||||
|  |                                                    fallback=True) | ||||||
|  |          | ||||||
|  |         # Czy ignorować połączenia z localhost | ||||||
|  |         self.ignore_localhost = config.getboolean('module_dovecot', 'ignore_localhost', | ||||||
|  |                                                   fallback=True) | ||||||
|  |          | ||||||
|  |         self.logger.info(f"Loaded {len(self.patterns)} patterns for Dovecot") | ||||||
|  |         if self.ignore_ssl_errors: | ||||||
|  |             self.logger.info("SSL/TLS errors will be ignored") | ||||||
|  |          | ||||||
|  |     def _load_patterns(self): | ||||||
|  |         """Ładuje wzorce z konfiguracji""" | ||||||
|  |         patterns = [] | ||||||
|  |         pattern_names = self.config.get('module_dovecot', '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 _is_ssl_error(self, line): | ||||||
|  |         """Sprawdza czy linia zawiera błąd SSL/TLS""" | ||||||
|  |         ssl_keywords = [ | ||||||
|  |             'SSL_accept() failed', | ||||||
|  |             'TLS handshaking', | ||||||
|  |             'unsupported protocol', | ||||||
|  |             'version too low', | ||||||
|  |             'no shared cipher', | ||||||
|  |             'wrong version number', | ||||||
|  |             'internal error', | ||||||
|  |             'Connection reset by peer', | ||||||
|  |             'bad key share', | ||||||
|  |             'unknown protocol', | ||||||
|  |             'http request' | ||||||
|  |         ] | ||||||
|  |          | ||||||
|  |         line_lower = line.lower() | ||||||
|  |         return any(keyword.lower() in line_lower for keyword in ssl_keywords) | ||||||
|  |          | ||||||
|  |     def process_line(self, line): | ||||||
|  |         """ | ||||||
|  |         Przetwarza linię z logu Dovecot | ||||||
|  |          | ||||||
|  |         Przykłady linii: | ||||||
|  |         - imap-login: Info: Disconnected: Connection closed (auth failed, 1 attempts in 2 secs): user=<user@domain.pl>, method=PLAIN, rip=1.2.3.4, lip=5.6.7.8, TLS | ||||||
|  |         - imap-login: Info: Disconnected: Connection closed: SSL_accept() failed (no auth attempts): user=<>, rip=1.2.3.4 | ||||||
|  |         """ | ||||||
|  |          | ||||||
|  |         # Ignoruj błędy SSL/TLS jeśli włączone | ||||||
|  |         if self.ignore_ssl_errors and self._is_ssl_error(line): | ||||||
|  |             return | ||||||
|  |          | ||||||
|  |         # Wyciągnij IP | ||||||
|  |         ip_match = self.ip_pattern.search(line) | ||||||
|  |         if not ip_match: | ||||||
|  |             return | ||||||
|  |              | ||||||
|  |         ip = ip_match.group(1) | ||||||
|  |          | ||||||
|  |         # Pomiń localhost jeśli włączone | ||||||
|  |         if self.ignore_localhost and (ip.startswith('127.') or ip == '::1'): | ||||||
|  |             return | ||||||
|  |          | ||||||
|  |         # Pomiń lokalne IP | ||||||
|  |         if ip.startswith('192.168.') or ip.startswith('10.') or ip.startswith('172.'): | ||||||
|  |             # Sprawdź czy to nie jest 172.16-31.x.x (prywatne) | ||||||
|  |             if ip.startswith('172.'): | ||||||
|  |                 second_octet = int(ip.split('.')[1]) | ||||||
|  |                 if 16 <= second_octet <= 31: | ||||||
|  |                     return | ||||||
|  |             else: | ||||||
|  |                 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 | ||||||
							
								
								
									
										0
									
								
								utils/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								utils/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -1,5 +0,0 @@ | |||||||
| """ |  | ||||||
| LogMon Utils - Narzędzia pomocnicze |  | ||||||
| """ |  | ||||||
|  |  | ||||||
| __all__ = [] |  | ||||||
		Reference in New Issue
	
	Block a user
	 root
					root