This commit is contained in:
root
2025-10-28 21:41:58 +01:00
parent 7b41672d05
commit ea55e6f95c
17 changed files with 336 additions and 18 deletions
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+157 -8
View File
@@ -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
+13 -3
View File
@@ -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):
+3 -1
View File
@@ -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']
Binary file not shown.
Binary file not shown.
Binary file not shown.
+162
View 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
View File
-5
View File
@@ -1,5 +0,0 @@
"""
LogMon Utils - Narzędzia pomocnicze
"""
__all__ = []