From d01ca3512e4e06e3be00bb1307fafcb43738e7b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Gruszczy=C5=84ski?= Date: Mon, 3 Nov 2025 10:27:52 +0100 Subject: [PATCH] new options --- log_parser.py | 210 ++++++++++++++++---------------------------- templates.tar.gz | Bin 10423 -> 0 bytes templates/logs.html | 3 +- 3 files changed, 79 insertions(+), 134 deletions(-) delete mode 100644 templates.tar.gz diff --git a/log_parser.py b/log_parser.py index 984ac37..330d489 100644 --- a/log_parser.py +++ b/log_parser.py @@ -1,11 +1,13 @@ import re -from collections import defaultdict -from datetime import datetime def parse_log_file(log_file_path): - + """ + Parse HAProxy syslog format and identify security threats. + Format: <134>Nov 3 09:18:35 haproxy[18]: IP:PORT [DATE:TIME] FRONTEND BACKEND STATUS BYTES ... + """ parsed_entries = [] + # Security threat patterns xss_patterns = [ r'<\s*script\s*', r'javascript:', @@ -16,8 +18,7 @@ def parse_log_file(log_file_path): r'<\s*input\s*[^>]*\s*value\s*=?', r'<\s*form\s*action\s*=?', r'<\s*svg\s*on\w+\s*=?', - r'script', - r'alert', + r'alert\s*\(', r'onerror', r'onload', ] @@ -32,7 +33,6 @@ def parse_log_file(log_file_path): r'1\s*=\s*1', r'@@\w+', r'`1', - r'\|\|\s*chr\(', ] webshells_patterns = [ @@ -43,147 +43,93 @@ def parse_log_file(log_file_path): r'exec\s*\(', r'popen\s*\(', r'proc_open\s*\(', - r'pcntl_exec\s*\(', - r'\.php\?cmd=', - r'\.php\?id=', - r'backdoor|webshell|phpspy|c99|kacak|b374k|wsos|madspot|r57|c100|r57shell', + r'backdoor|webshell|phpspy|c99|kacak|b374k|wsos', ] + # Compile patterns xss_pattern = re.compile('|'.join(xss_patterns), re.IGNORECASE) sql_pattern = re.compile('|'.join(sql_patterns), re.IGNORECASE) webshell_pattern = re.compile('|'.join(webshells_patterns), re.IGNORECASE) try: - with open(log_file_path, 'r') as log_file: + with open(log_file_path, 'r', encoding='utf-8', errors='ignore') as log_file: log_lines = log_file.readlines() for line in log_lines: if not line.strip(): continue - - match = re.search( - r'(\w+\s+\d+\s\d+:\d+:\d+).*\s(\d+\.\d+\.\d+\.\d+).*"?\s*(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)\s+([^"\s]+)"?\s+(\d{3})', - line - ) - - if not match: + + try: + # Extract syslog header + syslog_match = re.search( + r'<\d+>(\w+\s+\d+\s+\d+:\d+:\d+).*haproxy\[\d+\]:\s+', + line + ) + + if not syslog_match: + continue + + timestamp = syslog_match.group(1) + + # Extract IP:PORT + ip_match = re.search(r'(\d+\.\d+\.\d+\.\d+):(\d+)', line) + if not ip_match: + continue + + ip_address = ip_match.group(1) + + # Extract date/time in brackets + datetime_match = re.search(r'\[(\d{2}/\w+/\d{4}:\d{2}:\d{2}:\d{2})', line) + if datetime_match: + timestamp = datetime_match.group(1) + + # Extract frontend and backend + fe_be_match = re.search(r'\]\s+(\S+)\s+(\S+)\s+(\d+/\d+/\d+/\d+/\d+)\s+(\d{3})', line) + if not fe_be_match: + continue + + frontend = fe_be_match.group(1) + backend = fe_be_match.group(2) + status_code = fe_be_match.group(4) + + # Extract HTTP method and URL + http_match = re.search(r'"(\w+)\s+([^\s]+)\s+HTTP', line) + if not http_match: + continue + + http_method = http_match.group(1) + requested_url = http_match.group(2) + + # Detect threats + xss_alert = bool(xss_pattern.search(line)) + sql_alert = bool(sql_pattern.search(line)) + webshell_alert = bool(webshell_pattern.search(line)) + put_method = http_method == 'PUT' + illegal_resource = status_code == '403' + + parsed_entries.append({ + 'timestamp': timestamp, + 'ip_address': ip_address, + 'http_method': http_method, + 'requested_url': requested_url, + 'status_code': status_code, + 'frontend': frontend, + 'backend': backend, + 'xss_alert': xss_alert, + 'sql_alert': sql_alert, + 'put_method': put_method, + 'illegal_resource': illegal_resource, + 'webshell_alert': webshell_alert, + }) + except Exception as e: + print(f"Error parsing line: {e}") continue - - timestamp = match.group(1) - ip_address = match.group(2) - http_method = match.group(3) - requested_url = match.group(4) - status_code = int(match.group(5)) - - threats = [] - threat_level = 'info' - - if xss_pattern.search(line): - threats.append('XSS Attack') - threat_level = 'danger' - - if sql_pattern.search(line): - threats.append('SQL Injection') - threat_level = 'danger' - - if webshell_pattern.search(line): - threats.append('Webshell') - threat_level = 'danger' - - if http_method == 'PUT': - threats.append('Remote Upload') - threat_level = 'warning' - - if 'admin' in requested_url.lower() or 'config' in requested_url.lower(): - if status_code == 403: - threats.append('Unauthorized Access') - threat_level = 'warning' - - status_category = 'info' - if 200 <= status_code < 300: - status_category = 'success' - elif 300 <= status_code < 400: - status_category = 'secondary' - elif 400 <= status_code < 500: - status_category = 'warning' - elif status_code >= 500: - status_category = 'danger' - - parsed_entries.append({ - 'timestamp': timestamp, - 'ip_address': ip_address, - 'http_method': http_method, - 'requested_url': requested_url, - 'status_code': status_code, - 'status_category': status_category, - 'threats': threats if threats else ['None'], - 'threat_level': threat_level if threats else 'info', - 'is_threat': bool(threats), - }) except FileNotFoundError: - return [{'error': f'Log file not found: {log_file_path}'}] + print(f"Log file not found: {log_file_path}") + return [] except Exception as e: - return [{'error': f'Error parsing log: {str(e)}'}] + print(f"Error reading log file: {e}") + return [] return parsed_entries - - -def get_log_statistics(parsed_entries): - - stats = { - 'total_requests': len(parsed_entries), - 'threat_count': sum(1 for e in parsed_entries if e.get('is_threat')), - 'status_codes': defaultdict(int), - 'http_methods': defaultdict(int), - 'top_ips': defaultdict(int), - 'threat_types': defaultdict(int), - } - - for entry in parsed_entries: - if 'error' in entry: - continue - - stats['status_codes'][entry['status_code']] += 1 - stats['http_methods'][entry['http_method']] += 1 - stats['top_ips'][entry['ip_address']] += 1 - - for threat in entry.get('threats', []): - if threat != 'None': - stats['threat_types'][threat] += 1 - - stats['top_ips'] = sorted( - stats['top_ips'].items(), - key=lambda x: x[1], - reverse=True - )[:5] - - stats['status_codes'] = dict(stats['status_codes']) - stats['http_methods'] = dict(stats['http_methods']) - stats['threat_types'] = dict(stats['threat_types']) - - return stats - - -def filter_logs(parsed_entries, filters=None): - if not filters: - return parsed_entries - - filtered = parsed_entries - - if 'status_code' in filters and filters['status_code']: - filtered = [e for e in filtered if e.get('status_code') == int(filters['status_code'])] - - if 'threat_level' in filters and filters['threat_level']: - filtered = [e for e in filtered if e.get('threat_level') == filters['threat_level']] - - if 'http_method' in filters and filters['http_method']: - filtered = [e for e in filtered if e.get('http_method') == filters['http_method']] - - if 'ip_address' in filters and filters['ip_address']: - filtered = [e for e in filtered if e.get('ip_address') == filters['ip_address']] - - if 'has_threat' in filters and filters['has_threat']: - filtered = [e for e in filtered if e.get('is_threat')] - - return filtered diff --git a/templates.tar.gz b/templates.tar.gz deleted file mode 100644 index 5e73581ac33a9056fa6bb1974863a6154407696d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10423 zcmV;oC`i{IiwFQxYX)fm1MPkLciXm-aDMhV`X5mBrnYmVx8x*_t-Z06e93Nln~mIU zdwP1j5D7_0D1s#*KR#do?|TLSNst67>R~6jjnY#~d<+IN1I%D%Fc4%OcvukLdT?i- zcDwy#FhCF5?aq@y+x#1UP-oC-clrnY?x2U-oo=T)cz^~E?%}iG0*4RU?fJC)xn=it zKjOb%$w$QeCoUDunV5T9Z=>CAAN2c0{O=6f-EPGHL9a7F?N0AWzx@EUAKb%d@z>`6 z(RVL@Iz9XC{VN2lAMPB%ALLKeUrsc2!>J!+}z>KTvvpU^BMljHhKh=3oqT8`^CKXR9N^dfBfM6~>1-WoF| zxCn9Zk3qB7Y`0v>Ma$tlxzU_czv*yZMs(xWgwF^Ob+f7Ly?omrdDHRO!ku_H zBu$6STlgcsYI$_bTNCDs2EHVm%}MK^*>1Ka4%qYJ)>UH&9mz(bUkJ4xq0rMx zBQ#+l5;KB;CnQ9VTJdxG%1wL$K4a{BnucuQyA6kVEIdTtPrC5$dFrtMyDs&ohp5{Q zuF|($$^#EyAEJpzuB>l0JTei7sMF59i#IZ{I-Sc4sI4(e&7qWvCGjWLULNK#WNqckRFB(#G(v1poB^oYqScnB> z{vq<2PYT%4X$Mypov++`zKZ;E@xC^&|lI+ z8{_jr?&9zhnlqOqlyAbdhP-BCY{9qF?VNO9nONDdjS$I$Z>qu6iJGxA(H-;#eX^{H z)kHRfkOi-pa}j3SlO_uz3&m+E0W-tRD!twD_?h!$TP-#xm^UW4G4A8_*HNwHrE|zAJnEwF9mmo0w~MfTK+Yl;l+!WwsJM=%k(f_| zc+PjDi}DqW#zY7r-(6r2;UFM5#J&^HMBE9P=Z$fw44FeDX4HS4xs|3E`hL=Zf9k2j z!^R~Uf1;w1JTGR8`M8)QPvyrXKqJI1UGPJ6pgSU8ATCmnG_JQhw>vw~%OLQs5f6w% zC)7d8kU^I-;!AB8^MrCVVa|dpjc4K7p}f5r?-E;NGLfjC&agiv5#!`t#C(NG)DyN4 z9`%VHd+^o=GG`YAxnzPDp3qP0$Kb8oot;^)2_po?$K+-wLTDP2>*sLW=%4wd?wyG~ z`i{=Qq{Y6-zWF`J7v$WT5l7F;J2rKphkYcxY66K2l%tk5`i@%4*ghIF_gb6YKoJjf zeq7gDsg9_d{Ln@!)&^;7#^$817<^o}Ph7FBiO|uk7d^BcJZ^gwhWP7HE=64Q13JOZC*r%7mq7w-0ALA%qV6Zf;OW{#+0)nlr{llLsCZd%K6b`@gsU zK)1JtPy6k@Y{%9F5;&Z~itc>uVM$!wS7iwJ@vZMFtzpOAS-|KyicI5RZ& z8eknq0!sa0A&|HZ$Z=f`mNC1k+W|G?vr5cioc+jLGLk^!B^e$Qui#~QuMTs0{_U4w zRl&_HZX^MfIfBLa!IT z1A`L@h6I~VJ(3u|@|j8U0NjN>Ko|D*4KvtbJ{JgP4RnlL<}BvK7ftwW=yT>?Hzm#9 zQ7)Pa+TLzFlkM(5PZkNS78>CTf)<>FyBq}}nUIix78DG5%)#DBun zMQx?ZMUtn*c5Yo$?RtueitRXrv zRuo2OC`T}TyNLSvUa20*`$nfyXijrz>JHHf+RMCoe2jKQxFEYG<5IjbXoX$ZLZ+l! zFsBiiPmo8sAU+AD{LBba!_VkIdJvfMF(u-aN8szp^;_Lu@?_L#`YK>1C$e|C-Mw8$ zs_)&$83EmYzLSW((rhFgCXuDb!O^%!#Ynxm+C$S zn_RKa_>q>9ElpTBM{^=(%ssBZ|7moVRguO#3C}jbA_y`M8IzfuuVzY3N+jsCK1|dl zXzzH^%7KXOx2X5o4x5PRiL$P}b(uC%$S(PDy*mK!oyU*soo>56ytx5kn$W4_28iA5 z?NKXgHq&!8rRIo{My)YsLa@2^In}F?%2XnJF`gUhdMtc27JlOrhd%YEipPm*7WLM= zBBzVvx}4{JP!q+PjRql|7%Lj!1p`e4h9(th7H%{I}cf zc9QsSyDj6tz23L@?`Mer)^Y)0xB^b8T1g1Fw#685%@zb!-X3+Wc8?L@+P#K=RcpFU zbfQKLr4 zHvP8Ujapk40IC&6ZEH*7KT9!;BDb}Qp<62|$}zuMS8tQA$+z$MVh-ycBMz0^tQZ4b z(!`Pwam^AVu2l*Wn@?Hk5~EWq2^80sMv9lVvm%{xXw^#N#nIhuin2|9ieGWt$DB!^hDB1r3%0%7&2dg79}ifs!#?B%#Y=$;q2W*vr7CmI{B>qWD)$2f%98 zB4D+3LSVHuVqmrH2f++9z`qy~1Q&FwI1X0ZC=gcLED}~*KNMEmAQo1;lVDgr`jxWF zR*rwwwi*DdZ8-u4gAUvlFc@PeeLOOk`00}wtFOGc(&?I=NDBS-)-k9CQ-OPUE*ky% z@E77aY%W7lzp+J_5lA(#n&=G)$u37P&}ha&fkq0gLp{!nwu5+-6YSQ9E01fi)kEe_ z!I3o4!|U8(3+*M>H{)CUs1*#WH{S@=jBY~$oH}zH!P@EEww zvj||vvoww!Z+9y#JIu8Z-^14=d}3)=-Plu^9zq2B2z>Kn>_J|SyDn$!sQE-(vhY*m zl7_@Bz_DM1V_)GYoM68ahQ?&1hDJt3<<&tpWKpqKfwz!hY$}z=KgngRNLo2HqEwig zRLt+SYW`2eKLLPTc&o3CUUSgwG`UCT`A~fA-h%$7{;2Il2>`V! z8346qDFC%9IRLd)k^s!Pv2q$fZR0$E+O`t`6u{WZTss$_wn;KTtx7gPZS{13TCQ`; zG6HJbNeQTJm=ge2MmzxEM`Y=%=tJ#W_~CQ;|Eceit2+q*l>7gmJbBVNi2eWF1MvU% zJN<9||9km-@%z6_qyDe32kfu20W9$!8>^<=&abL_XbiwH++-OPgIyi21@OJ>CR6I# z$XK^f?%OuA;%#PCV+WSdL8Zt+kwbqcgTR-wvFwZ5N>)Oqczl&Pzc+xt(q^#=KK`v! za0`8uF2vuZyAkmEBNpnYiIbP`?|D^8Oz&K&)ULmnk4czoIW5R*wrdGs1R)d5VV;>4 zZ4{B4-$^?vaqxUB{FrJb+qB9V zQnqDFxwqaZTkMjGnbiL>Y$9z)%n?Q~f5_B31xj0PW=ty^@cN7H@w;v7i%CY2h$Tt%-ma|CSJTX<5o=!5l z3!thMHZqi82UMEHJX$9VoQ;gd8@7(ddf%JG4h)2s%BzB+{S0%$Ii8YaN50%%1`!Fl z;Rv>V=tn0D&%2Jd0wjBSA_(z*g!~o?z4-@wA;Io7B3G0PwO1y2+1wqH?9E7RzA*MS zth*26<|c0PHnZ_#ybuBcT~e{AABiY1EL%T#fPMAk zjJhuIkLwVEsSke;bWZs5qZT}n-S?r87%tuBVcQrOFb=rM;2oGPd7UdayoK!2G}o+K z2f@B?%-wiAWu#r|$9PF45FAvV@Lw4D4Xy|xEC3+=EHXk%lumj(moxLCB8CEd14RY%oGQk2Xsk1w+O&N+kTlL{P zhFx?blaXNG>5uZVMSwW*0O1l5Nq|Zg8EvE~&jOk6qBl=VFHOkC)UOXez<2ZkejK&b zW2NtU1asjq->(nf!B<$@`$TGFUI_qai_jtU;Yj|i^vY!z)`!1*_)zU(c8d3Rdr z;Sl>So7aaQKG^2m)VvnMtTvPJ8R(-1_Jpw-N|U5mrxs{H=`dnEQgwEX7>A+L8N&s0 z8l6*duH9(7K70qi(FgteF(OU5{;Z@M%VxPuGge&2*gto5ZIAl!?R#{Bee_aa*rPt; z#9_W0VbvM%{Y~*oH)$8+(SN@~Z~c!pDYoboxqPL@CQH-3%BTTu{W==m1CM?kjkd;_E6&sWNG`0%1>?mH zX%AGaN^vMVCiI%7xHV>E!J2%r-dmAHb{HFSE6;(-%zH`v zYxD~l^BM8H`{3JU)YjHmW*#;ovjUE6#dgAuwo0QP+qRt~ z%=Pxsfa&z&^qurCL_VXnj3?NIvpwTU_UOgwyG^YSN&3LU4w*5}CE;{FgRDvREY=zakCXhTN$4SY5`g^H{64m>pp5wBp?Foz#^=`tWy~c09y%xjzt%E zVOO=_GSCY!hAdP4Y`yw`ld%;Rj-|5-z?+-+>;iOGgHwB(j1ijiDs5|+YQWXT>KOZa zl%BwdMI+*KD(HoBs;xCj6N+zdtR^tkaDvgY4f)H!&EQ`)bUlw<66MZ+p}wQH?_sJ7 z3Fn0GsYTASZnJanwApKRdZ_cP7yamOy!0{KUqUe@4!}-@R13Rv>Tirewt37#I+s0lT0Ap`cv+;}A;Jqdf>jp*e;c`W#W;!5m7`p@qMY z_4MI3-!GyAW8gMXSb(dS3B|iJhEBln-x>{44OzO_TE;2Jgo-`%XRLZir3z;Rv*HA# zMvoqe8Rh7bdR{CP2@_f}Yb5>!-yS_GnrrT4#WP{yB@W&5Nvd@3W^0}lOmo$fzp>)F z#{yvOZQc+@)ZSzbb^=?L%H|o}(DHN`R9Y6nmLrqPtX-O6V2^%Tgrsl7s3$M3m0DB1 z#6e_Ak4Sg{^UHf?L?&dKO}Yy*8Tah$J!Is1Vy1Tho7pqVru>M$$5LI9J=rrSQrmQuf|P$$;Z{Z5X9S00Ot4m}R!FaW zh}NV@9aTQqlU$Iw*v$8?_v5+;dGxJm5t)>XmAEGL^a{)>px?PJxMOzj=MV3YG7_)Z zB7|dIKwjn4t6r7+X)UJXdJ4jV&T=xr7&trK{t$q;+V)34H{WKrbPRM?8U)?XI|_gg zFbK9H|1w%5sPpL>jR@yvxMm4kSL6?5t$(^c=c*AMV98u#zT;8nQ^X*Jmqu$Thcdvb zCh*7sAoYSKdYT$rnMu;9Z8Dc+AOWdi-+*G=dlGV)f9nu*b{!OC>s1W9TU)Y=eLDK> zF!qTOIClX4qS31l|MTj@+n4spc>xUBctZd`U|R$HIFrnwK{1?kXuXtIGI34q=G5D4 z?ndZg6{=F4lLc?|J0t809fi$OSvTNRl=F}T-nHQxPW66U>e40QhKdp(sD-QMOL5le zTADA0Z&@1s+Bo$n@kQh9`)Y_vVU02UANaLpR8IHkgzuCfDRa7F=68gX{bB$Pu9J0%(L|Q{Nnn})H&Z45k z;3A#@B|8bTG{)sTE8!iGO@XY!M%72jP5%2I8F5uNJ4I~Hu>0;PM1wqiiZa0~PzS)R zKpPrJ%6w)TfZr@nh2li=rAg(|t1N-8Knv-;`vRyj$5)Wj*{KhI#8;5exd9PkSS!#$ zzXPOLi2}B>k!+J?*)Eus_;5m1{EkP2zI-YxDjuVbv5h~j zo0q3crh1D3y(M_H0r)mBFV6z|OYAk&E=YpWOFS!Lr5dXI>I!6Bs-~M)E5bE}S zZJnZldYlFo9)|fuJr3V0yr2i3dPo1ry_TaH^?ee)Is5S)ytT8PDao9U{&u6tbi0Nc zFHlkm$StYyG?mI`@S?eE3uFS}xLsM77m`SU+omqR!@et7zg@e6>p&Y)3X7~VG_{j@0?OKP!o}Wh)1qWDS_-P2#!5}r;Z>V9 zSop&83LrE+;!nj44N)hHO*Dxs{P!}A-Kssx`B&t=;JrGWlQxGsY%{21iv^PVfe`aa z#;j=8vyg4nY_()UBWz=VLWpo|`3ysC2)SJ!_C2>|6sk-s_pfv+>7ZbuT?_4pXot-1 ze%Y@4h@&|pM(flFzhgL=o;RggX49n*!!jJ{Wrj^j6wGeg2xmjk!jX`3f|c|RD(ET% zEZooZttow4P3_V`$7pv9*;~75)ZvpxJpUdz!*;DAn8!}`VIAZwja0$S)~CYpaaTy# z2$YPZ%o)>Xs@!!X(H!kvv&F9G$wG-Rq9~*@g0MhU1P)^sl6aUe<87*h0qjWpPkez# zayhY|pZF|cQ|rKy?$64iDxbCsf8QLs;y60Yk=>>MY@xFZ zAzCf;Hl{cPt5WlrE>0d7Wv|d$CJwY7-EA?L^(mi*cCWNg2MX2}!q`{8J`s{A#GMcT z{%_WbQRf*~tN*_j*`+f79TJ(1M;$f~m=7hkj1BZ#IuKyRs!{(4SP+o#BX2n@B>(6( z+s!Acbkt(*w$#m48Uef1^_y|gDfj`H#}``t=AhY47pBU?ujX8lMvAO1l&L_2Cj!m| zMiX)wZ9t4+{4F-?_M5VX%wC}>!ihMebHWy)_!$Lqw<#?(FnOfKwku7|tW@4UMuT>{ zt?K>1iI7ht-HQ-LdpN1R3#DC>(!2X9Rg@OfJmKtzGf1+jNVA?q^r(%MQ#=AyRFg7) zXO-DN4P*26TBA7+=JmRh3)nUpe4?2k%hgV?>3ElG74S~Dex!u`WCS(>iNfC?Lqn31h$G%$L+g0rQJyT5K zkMiYVj>8!_OUYOj<1oFf)CCxhtE^&+grSNr-M%hd4zs&D(C$*PtqfFo+7r=DQ%$YK z(RZn(t4#po-YlK9%x-3WuMOrqOF6E6bTu@HO)Pp+Y zY@$jCs7i&Y)j`yqjGVk=yo$K#x=?vJd&#)2i&$2XI4Q)qn8@kR*;4Jli!{5)rUF>*OxeKSX=D3R;Z#j39dJ`$`8f~w`UGw!4?#38y z$lVe+D|1)=F0olhe|mAvr&6PJu)5W1!mebt7F6QW7FdA^Hn7@YF;T2YY_vS72r=)I z-;%p^&cgNtQ`OFNwoWrW3&T^+;Z*?wDV(1l_Y%7~p&gNb@oAVfK#N{gB z?v%*}vnu&@p{9zg+N08hwykV!HI+)CSf1ZV&DsEN#NlcH?}En}R@vt3wO6`oc?*3@ z3f`=;@=cYKic9n_TLmYtFr4&ZCCSZ|2rMsa+rCZ6P(%n!R$^dQd zQ-M#hQ-dcb!&FPFMg^I9oLBIGB`33FGnnGnb0m;i+LiL^a@06#sbfcvT2N1LzC1`bIU04fV?7<7O`mjN?`jC=#>s)n zXM{w%S5;Hld-=9Ma@ZiVa3>xPRrM$Q5nr`DI_51|01aOfNMvmtG~3O#!~uI=+Ybd9MlvH@nE! z3E1t&NdMRD?U#a`^>Rm(PeXET<(EEJ-0X!Mtx(=i*7s{YLNAisT6r%?t~L3gc%th1 z$x}CN%a>++K5?c|`;peg5^mvK#PD9PxAZLZQq&G-?4pWf^-lWz{)rWd%u(>f|QE+wVO_ou`AxsPpU@NN{@)i?D4lzUrR#W4#pVXp@2^{nIB+5%+u% zddaA-&{3#E7+W3cf9hoC;m(m>T4>Fx_dn|~Z`KX#WRJR^F{llt%ZS;f!YmIm>qXBR z)nmI|vl1GNliZ1rE1u8%1_#ohJZ1ibPT>?R7A8ovX1%CH3AXn~1CE8iK77OG#PT?C zp=?V2@mDStC^Bmm7M8bdZ^Cw|=pc|E^bT&ztFVHzy*?t&A_O0ZT>5g$n=n9NE2U$9 zAMQGEH~%VF!H2EZ&5bN407qlMK^aXRcvS4|9`5e{-u?sK-X1>fKkdtAY%QSJ+J~l$ zR?I`irRIzF_9OjM9gdFXUIUDb$dWECF`WivmYGf;j2^E6%rerMm(5br*|*`?bOaaT z@&-}$n=0vsv1YdgKB5!ZE$M@D)nGU%;CC{QW)7uS&@$)JC#H7`jbPI}98a^$Q4o>| z31x*!!H~xs?2QBq@swmxpgh74>IbA-)NW~>X)^*~LTz#)-U>2LjcftJ`ts7@o=N*td(i2`Fqe$Zz$xqag1B%HdahU6^#k{fPNmSC=Frp~lBtsEH|4JI za6wXu$|+uD>-*%$lynPb1(MHbgj7Z;f<_0@vRRs-mwGa4G<|h>uqR=X5eG1|3kmh$ zO6GpEz<95?z_`8`ExH~?CnB2~Ud>nwj{5B$#5T3V_Z^#}SH1`-;l^~IDz|a-P@2_h zMNNJAg%S?uyQg%IF)zhdL9FpImD{5g)YJAu3;MaG_Csr_&0rYS5Qc4)Fip;b!?=hr zy17v}|Gq0vvWL67Fzp_-f>L0~2`pA%G&slVb|q-C#&>);a2_1!3Vq&h89aR!g<;av z<0kY`oy3LG+tx->FBUhGZ#Ozl@e~U(W#P58VC2n17}mNeIDk-XUi~90Ax3ZF8vSWk zNFBLY;KrCY)R0M@Fz(=R3ZAy_uQ)%|cW-Wx$5_y$zDusq?QMMsNWTCy&=C)?A2p~9 zFIlFhKHNKneB-D_337?R)Z7$WN}*WL^{I`82xNO$R>Lod28lLG)!_-6iwP4= z$^rxR1I2jS3l`lxM8tRL#Ks-F0Kh4n)dc`DChtc8_@7>}007c=EdXT*+<^d?l)YC0 zc>nX6p2Aj;gJ2<&dF^fmAy^27Ua>eBUASwhSdPsd$VQ6V_bME3J&#PWhdvN>C_H+h ziYF`;5UO505A};*f|wL_=HF6gEDZQMBqz$*z9&6XL2iB_;}KwlUI>AmPye3sk|X=Q zNlOr8qZcu!%q51U+i&-B2xJ;o4Z=c3w`f-2tz6&68a_=GpBXQp@|~py&E{zd-#+*B zN%B7uAKgeP6YqrA|Box%aUPCI6Lo}zZ>lMzp-C|)l!S2*9 z3}bnxAV;#@9E4JewNSjI}~nR#$Xe9tB}QzG*DP?{I=H^1Xl%$K1Gfu{J&wD zo(n9PLO`=Y20)6p&&pYQV6t{;ifl0s-C`WNFEnRXybjl3D15 zJU=yA{Mw*Y&c13tZ5L->Mc&`@v4gt?GIA1Rp^f6ZV&xFC2m@xP(Oej3zI8jjA zGf}2tvK|VXrcp~~^97_{QwuHior{nL5XiK5OF9`!C?X6Sv;e>`ucMh8Y3E3f$kPsw zT4J^aICTh@2520fSP5Jz?yOAw^e|r+#$W>&a%EK?o?HvU(OVzw{dls!A(WpMVofkn zhb(HS4AfDpNH*ZTLa92EKBr=^I?WaaG-TOYt+f>;v_!t+hs+cOWa>ClwcOW!RlD;oZ56@u4Tpvfn9R~N`X`e#)HWu*z|;dIm*MCe z;+r~z9gNo3%I_NcZ53KS$kmcaU+8DUV+hz3@EcOy(#gQboDVI8hBw hfKxEl&u>X+-#*_y-#*_y4?gqf{{SS)$sYg)0RWVQLL~qI diff --git a/templates/logs.html b/templates/logs.html index 546f4ae..8c1b1f2 100644 --- a/templates/logs.html +++ b/templates/logs.html @@ -9,12 +9,11 @@ {% block content %}
-
+
HAProxy Access Logs
- {% if logs %}