From ea55e6f95cc43452b4f02a4851bc6bc6dcff154f Mon Sep 17 00:00:00 2001 From: root Date: Tue, 28 Oct 2025 21:41:58 +0100 Subject: [PATCH] dovecot --- __init__.py | 0 backends/{init.py => __init__.py} | 0 backends/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 482 bytes backends/__pycache__/base.cpython-312.pyc | Bin 0 -> 2382 bytes backends/__pycache__/csf.cpython-312.pyc | Bin 0 -> 4456 bytes backends/__pycache__/iptables.cpython-312.pyc | Bin 0 -> 5111 bytes backends/__pycache__/nftables.cpython-312.pyc | Bin 0 -> 5977 bytes backends/__pycache__/ufw.cpython-312.pyc | Bin 0 -> 3516 bytes config.ini | 165 +++++++++++++++++- logmon.py | 18 +- modules/{init.py => __init__.py} | 4 +- modules/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 337 bytes modules/__pycache__/base.cpython-312.pyc | Bin 0 -> 2842 bytes modules/__pycache__/postfix.cpython-312.pyc | Bin 0 -> 5512 bytes modules/dovecot.py | 162 +++++++++++++++++ utils/__init__.py | 0 utils/init.py | 5 - 17 files changed, 336 insertions(+), 18 deletions(-) create mode 100644 __init__.py rename backends/{init.py => __init__.py} (100%) create mode 100644 backends/__pycache__/__init__.cpython-312.pyc create mode 100644 backends/__pycache__/base.cpython-312.pyc create mode 100644 backends/__pycache__/csf.cpython-312.pyc create mode 100644 backends/__pycache__/iptables.cpython-312.pyc create mode 100644 backends/__pycache__/nftables.cpython-312.pyc create mode 100644 backends/__pycache__/ufw.cpython-312.pyc rename modules/{init.py => __init__.py} (58%) create mode 100644 modules/__pycache__/__init__.cpython-312.pyc create mode 100644 modules/__pycache__/base.cpython-312.pyc create mode 100644 modules/__pycache__/postfix.cpython-312.pyc create mode 100644 modules/dovecot.py create mode 100644 utils/__init__.py delete mode 100644 utils/init.py diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backends/init.py b/backends/__init__.py similarity index 100% rename from backends/init.py rename to backends/__init__.py diff --git a/backends/__pycache__/__init__.cpython-312.pyc b/backends/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..89adbce1865545dd719d90699da17c5718885dd6 GIT binary patch literal 482 zcmZ8dze@u#7)^4&(yOK7CS8t#UusSD|%d<|7O~3D5-?9EQEM${-}jd_pR9={s7Wurtj37}QTQ!hjuqON-Dq3SKgl`7@bH~TRO1_bIzJinRuF>ijq znYa8pHdaC~Zp~^oE+F)WoMgjDmB9^Ab`V8%L^Y~AT3yr83Zl7fM2*LK;#JrCdNm)s z1GCaH3B4Y{n+_2KQ-|ONX>Kx?K7G=|8#ZS>;y9(hWZYUaiB6=JP6qR!>>!5f8f>al zt)8R$HmVzP%X*#~APY1PvPcUc&3>_JM&q~B)XT|kfh_wxr8Ltc#JlG}%D;gNVh3$$ zTgZwHQuxvmk9G!5wgA6ds0|PfkG46yg3#miq>wwGS^HX54?crg38!O;x5T2F$;GDU zZrH6w{89Y7PPkwkdyO`0`Z&a!@oKu^UCax#!uzl@{75f&*yF)RBk;q zz8f8YD{3m}VWLQyg2@i@vx@`wGlTYlg!4}@Dz6yi_d zOQkp`2e0$6*Ial&g{LxwH%{jwa1+Sps{A4xaaWJA=P`oG14|G$GsolaD6?RQ&rqL? zQNMFguI-z(|Bw1FZ=!y;%Sn%h1YSsA-uX6zTb14VzB;W^&gMWQ8QJigQ7O*J!Rf_> zZ=VjmEiAy^Wi-W}UzPyA3Hpd~39k&_bY*(?80@u!^6I|1n!v7l5xB`A-_byREp|}c z;=cz~o%aaDX)jH1rk?Qk9(AEVLG$PfpLJ}e!jy7OyiU%nvfSH6RV}`ULRO!?`HMdA zZIQ2u?U07;*nU5}J`Bn+m-iuF6hT05FQbXc7?ZW?2$%n6L?z4WcreH9QV z{ooWWi+WAV;$`sTZ%H8=1ivc9v=rB*knJRXA6z0D3;B5!Ga$CnZ^rzgQOGZ8hZCdu zJKEvZQhrrCoGRr%(FRk+d{Rst1T7EC1hUd3)zS2?%ny=6KjhcJMWQ0EfOwhHH0^I) U*NlOQwDErn?`e~V2t-`jf9?}Pr2qf` literal 0 HcmV?d00001 diff --git a/backends/__pycache__/csf.cpython-312.pyc b/backends/__pycache__/csf.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4f9582fa3feea1b4f4d82570a3cb459a2bd82d00 GIT binary patch literal 4456 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 literal 0 HcmV?d00001 diff --git a/backends/__pycache__/nftables.cpython-312.pyc b/backends/__pycache__/nftables.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dc29622caf1308b19126a6a07578806d48bca4a3 GIT binary patch literal 5977 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 G7XA;yaVkdu literal 0 HcmV?d00001 diff --git a/backends/__pycache__/ufw.cpython-312.pyc b/backends/__pycache__/ufw.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..16874eeeeb7991858a461b6030e964292e36eb23 GIT binary patch literal 3516 zcmb7GO>7&-6`uVgx%?3+QL;%Z7T1#Eh%sW-t$z?FsVz!oW4INX+HPS14zc2_WYYZ0 z?5=4NR8Y7s-~t62_aX;HQ6Y!Ifpkbt<)J_$H3bUvLNzKRFI>Pteeq4TQXo))qVFw9 zS)}ARn!~qm{@%Qu`M!DcH=oZS|qXHE( zd{&qcIKq?f5h~p#)S(IULVeDJSmLA3@~iHcn!K*%s6sQUaykAJW$1D)na>t7sibOX zREejUR#Y>YsQU>{9Dl+AMmfvh>}bqS*4@?-$6(nQ0+=Nlfq2LSPq_(!^0&!^NCiNN zihvG`Y{FS`M4gr!0%-Uv``ZFbHl3nl{Bit|4GJ(zWMXXfrg>;;E4V4sJZ{N?)ZBm> zuEM3owt-Nkj_VNhMKVA*a)p$fH-#&t$VFXd1UfGkm|CP|RY{gh_HLy$-B4&=H$MNc zkSke&IaRcrH#DZF^0`Y-FwBp6t0Sov43lYzylE6n!*Uv_tOhp2P~%a-a_i<~f#s8$ zu3G{#bC#QFFaZvNYEj-2buBYxIhdxK8AHcOin7@|oG%!|nf!D%pBtX6AEG`C>mMzY ztWE>sN~kx~R7RalWl~0|{L-_%J-VaU3qJL;06!(`qDu}x?1{|2d-vV7&VBdfTA+U| zGO!j3f9VwWb=--syGi$f)!^WAaPU629E{ey(XZBdP7Xf^hBkE@w&l~Fy3+IZo%kbv&#GTp z_A7HEfA_z<7U-V!-1V#m4lM@`)dt_F#;V6_{byGKBaZ_IW;^b7%t>=MYmsk#*8SJ_ z{tVxQ{JeKRKHW}JG0_F`!E>#@45s-HuXOEhg%~r$0B5L=p^7$L>>(Y zOGAC59^ua(3GC$dYjx}TWi48!BB)uK;a{L;o=ViQ;M`KPP!V=jvun?4miMe?H>kNK z8c_3I)Z}?~bCkTG%PnQwx_sWfu#48IuOjZyWyg&8zF6{7`@2n-ep{E9b+f42^4d+8 zK>dtnXlx%GE!z*UL!Dt1pPAc5pA3mz-qxR0OUgC%hNjeY8ZRHLtJI#on@(MAI%Q!D z;{br=EUFBrFeDx81&B(vCK+Z}b_fBb$oc_vq}nzuy6hEEjlpedY&O1zGZp|)j~&2$ z(Bsft{C@nCSgq^SozX^G`=*L~t2$Y|RO^qe1V$geNRb1}fq~lLGa%lt5#L)?wR*AE zH?rc5?GWGLm_!z%y?{%S96Kp2b@auK2}{Q$uv^53*OUGB`5zdVNT(r{e#CtP+3}cg z7sUC-t7qmp!b>~Baa*BMljvjQs*jKvka|VDL3HkyF6wAlJ`fKt!kY<%fVsggTM5d3 z#x`5)-!yn?FOZUVCMelXP)=%pw+X>r?rV9HLAJ18WMv?AzNqF(QK6otAR(xRsavj` znVr;_M(b*TZd9x#8rn^RVKkeZ+Y-mljbGWyM9FJf@;as>TPU_0%tX#-47bw_yyq-Q zhj+74C$>#^o89${)5dN~%%pN!o#X8ZM^5PpSS+W}a`!ftZ?&F*IQ5?aAkBSp*y1yl1MJMvpU*p!sAX!Meda8$fsk;TXWjWCM`cxtZbC*?^Bb9rye; zc_4{23NmbVvN}}%UBwF@RO#Yj$H|rsO qm%R>Ed?xA&z`DS3+`oCA6E|Fh>-r}-^rhn*$Av$L{Dz literal 0 HcmV?d00001 diff --git a/config.ini b/config.ini index aef3d57..8b7b0d3 100644 --- a/config.ini +++ b/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 diff --git a/logmon.py b/logmon.py index 4775add..46302c4 100644 --- a/logmon.py +++ b/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,7 +114,15 @@ 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): # modules.append(SSHModule(self.config, self)) diff --git a/modules/init.py b/modules/__init__.py similarity index 58% rename from modules/init.py rename to modules/__init__.py index 14b94ec..8199461 100644 --- a/modules/init.py +++ b/modules/__init__.py @@ -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'] diff --git a/modules/__pycache__/__init__.cpython-312.pyc b/modules/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..faa41527e0e60dccc8a3cd8cddec0953b395c0e1 GIT binary patch literal 337 zcmX@j%ge<81dVy$G;WG}S=D(CXaPxsBwQ}E4CDa}bOR?r2rjy6>);(|@U_~Gmi8(p(@hcfV1NAWclGo2KDACW! zPY1e2KNsRg{rLDypwr^x^$IF~ao9ja?TUDS>Ol@FmH`qUm>C%vKQb{fvV7%WV3fJb Up!9%SYzF@YE{#U^B2Y*I0NJ@)hX4Qo literal 0 HcmV?d00001 diff --git a/modules/__pycache__/base.cpython-312.pyc b/modules/__pycache__/base.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1e88f8c2d81aa6a2a3d44def87822904f80aa1ed GIT binary patch literal 2842 zcmbVO&2Jk;6rWwM?M>`Dr64z{Q*~NU69a8jS`Z|mEzqQ(wpM{6AzxM-@5EWh-gRep ziEQN%l~!siP>*SZP&pE%Qjz!vkO(I(wi=YM?V%D}dMgPMQct`$Yj2#8N);pd?VC66 zV`koO-t(`!c6AdNrw1hS%@`qn;G{j27PD~zn0aE5JTW9gE=zeyeC50>zL9)HCTEEe zxk?OWNe-;?N=?p2y<;%zeqXQKGdi6v>yA#1vQ8_u;r(*u=O1T)vrO0Kuxyz+E!#y9 zc0WcJxkBJ5pi*nHaTJ(&!U*hvVElZ0Zq$KjN zRld~n2);u<2F8uI^$d&~dV?E}cwfR=H*Jmgc+nXSXDBS#)}&b+rXPs!MV&j0Q+uMs z3NEeFX(9EvUMMYJ+hS+v401J0KVg%MGpkU7Oxm<4vS_iQO$}VvL!0qjw#$zN(fn9o z?Z+X(Mbj$!T}9?XPKu2CU7A(^U^trQtD0u%6$V@k6=5;XJ!isCDzjY_<5M#znUIsrU$-P?xhw}tAnSSgQxDNPYad3sFGYu@4K#io!9_- zQV1ac6MQ}bFykNvz>pHEY#Y2EDrIvq*)MO(9sSg+35E46q3u5d5 zFC%g{e6F1k1MOt!IAlsaAsaGFEl$GI?R1J4Le-D~%{}2HWJ)iEY{+~=jOe#==b5uH z?;tFH#=XLnUV(xU6cRc^ZO5LlY85)Oe9fKq>ggcGR4*fHYP1ET+@-7xP|`WTPoSr+ zQU|6kGqMSeIN*44KR%;#0lTgMb^?Ky1Ok0U17|#iYEfb9E2cGRbG%@l0J11?yebEC zMfq+N_W`N*Y$8wCq0r$#=E$Q&&#lp$qif0Cx6a)-x0*cAOde>^w^x$ytR?q6+M7X~ zJxm{1O&@Ni4}WjmGZ)RDl$G?DfT5bamAH{uRr{K1--?=9YtMU|YVYkccXA84)&Aqn z{^O0zi61^}^u7C=dXi%Xb_VNHSbjQGlD-3U+dz3H+(Y<6_|`DwyOGW0 zL#(jQwKbq>;)QP;pt%!#F=N{X08H5euqXfwA)D7-UawWW5^EuB{4pLXEBSHPtT5Yi z1#s+Yl^MCL8X!kNLNy&tFPmR5Un$w9CE&#Of$b(fcm}2&=zxmm+SPih6CQT;6;%2h z2%w`?XaFQ5H%D%txHGaavYHucW`h z#fAVH+IhGlWo2&!md^%-U#HdO1vl*I9zB9x!P1SY>4=U#6LkH;6s_8>U9oC~sXG1C zn-C?RIke(AE;TFFGOIAlrTDf6vx}1dV$OCyYKxgM#<|V6UmVAMcHSFWzXmb~Q!z*W zOdV_-I&(iY)=dV(?8Jf%T0{(sR zLe-G@Zx04_HcZchkxpGU?YJJa-Yv%U!Dru5mD>dd{Sv(lX8FsozpN}uj<2B1!Bf(p z^7zS4?Rt6>W1aFLFmSL?&5^%Tq~}oM(E0nR3k~&xxHV|xqOSSfnilj`XwXSbyX@)Z zR=bXCnqe0-jpO4H)sADG775}5z%3HtDyW7EE+N5^=};h7$?wV=>q>Vtv)W z0!hiyacM)1M{`nOj3(JAe-%C)Yk{xeW8`U^3!j+#%{Wih(O|nL`4Lz^G&{JfClN`K d{*q-$*-(kp^ME8Cklp{pUYByxIzcL2{0lcpWPJbt literal 0 HcmV?d00001 diff --git a/modules/__pycache__/postfix.cpython-312.pyc b/modules/__pycache__/postfix.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6dd1fc335797793b1c4a1d4fb1ab9e506432d598 GIT binary patch literal 5512 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 literal 0 HcmV?d00001 diff --git a/modules/dovecot.py b/modules/dovecot.py new file mode 100644 index 0000000..f15cbfa --- /dev/null +++ b/modules/dovecot.py @@ -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=, 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 diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/utils/init.py b/utils/init.py deleted file mode 100644 index a6c55a4..0000000 --- a/utils/init.py +++ /dev/null @@ -1,5 +0,0 @@ -""" -LogMon Utils - Narzędzia pomocnicze -""" - -__all__ = []