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]
|
||||
# Tryb debug - wyświetla szczegółowe informacje
|
||||
debug = false
|
||||
|
||||
# Ścieżka do pliku z logami LogMon
|
||||
log_file = /var/log/logmon.log
|
||||
|
||||
# Plik PID demona
|
||||
pid_file = /var/run/logmon.pid
|
||||
|
||||
# Backend firewall: csf, nftables, iptables, ufw
|
||||
backend = csf
|
||||
|
||||
|
||||
# ============================================
|
||||
# Konfiguracja backendów firewall
|
||||
# ============================================
|
||||
|
||||
[backend_csf]
|
||||
# Ścieżka do wykonywania CSF
|
||||
csf_path = /usr/sbin/csf
|
||||
# Dodatkowe opcje CSF
|
||||
|
||||
[backend_nftables]
|
||||
table_name = filter
|
||||
# Nazwa tabeli i chain dla nftables
|
||||
table_name = inet
|
||||
chain_name = logmon_block
|
||||
|
||||
[backend_iptables]
|
||||
# Nazwa chain dla iptables
|
||||
chain_name = LOGMON_BLOCK
|
||||
|
||||
[backend_ufw]
|
||||
# UFW nie wymaga dodatkowych parametrów
|
||||
|
||||
|
||||
# ============================================
|
||||
# Moduł Postfix - SMTP Server
|
||||
# ============================================
|
||||
|
||||
[module_postfix]
|
||||
# Włącz/wyłącz moduł
|
||||
enabled = true
|
||||
|
||||
# Ścieżka do logu Postfix
|
||||
log_file = /var/log/mail.log
|
||||
# Alternatywnie dla systemd:
|
||||
|
||||
# Alternatywnie dla systemd journald:
|
||||
# use_journald = true
|
||||
# journald_unit = postfix.service
|
||||
|
||||
# Parametry detekcji
|
||||
# Maksymalna liczba niepowodzeń przed banem
|
||||
max_failures = 5
|
||||
|
||||
# Okno czasowe w sekundach (domyślnie 60s = 1 minuta)
|
||||
time_window = 60
|
||||
|
||||
# Czas bana w sekundach (domyślnie 86400s = 24 godziny)
|
||||
ban_duration = 86400
|
||||
|
||||
# Wzorce do wykrywania
|
||||
patterns = auth_failed,sasl_failed
|
||||
# Lista wzorców do wykrywania (oddzielone przecinkami)
|
||||
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
|
||||
score = 1
|
||||
|
||||
[pattern_sasl_failed]
|
||||
[pattern_postfix_sasl_failed]
|
||||
# Wykrywa: "SASL LOGIN authentication failed" i podobne
|
||||
regex = SASL [A-Z\-\d]+ authentication failed
|
||||
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 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:
|
||||
"""Główny demon LogMon"""
|
||||
@@ -112,6 +114,14 @@ class LogMonDaemon:
|
||||
self.logger.info("Loaded Postfix module")
|
||||
except Exception as 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
|
||||
# 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 .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