JFIFXX    $.' ",#(7),01444'9=82<.342  2!!22222222222222222222222222222222222222222222222222"4 ,PG"Z_4˷kjزZ,F+_z,© zh6٨icfu#ډb_N?wQ5-~I8TK<5oIv-k_U_~bMdӜUHh?]EwQk{_}qFW7HTՑYF?_'ϔ_Ջt=||I 6έ"D/[k9Y8ds|\Ҿp6Ҵ].6znopM[mei$[soᘨ˸ nɜG-ĨUycP3.DBli;hjx7Z^NhN3u{:jx힞#M&jL P@_ P&o89@Sz6t7#Oߋ s}YfTlmrZ)'Nk۞pw\Tȯ?8`Oi{wﭹW[r Q4F׊3m&L=h3z~#\l :F,j@ ʱwQT8"kJO6֚l}R>ډK]y&p}b;N1mr$|7>e@BTM*-iHgD) Em|ؘbҗaҾt4oG*oCNrPQ@z,|?W[0:n,jWiEW$~/hp\?{(0+Y8rΟ+>S-SVN;}s?. w9˟<Mq4Wv'{)01mBVW[8/< %wT^5b)iM pgN&ݝVO~qu9 !J27$O-! :%H ـyΠM=t{!S oK8txA& j0 vF Y|y ~6@c1vOpIg4lODL Rcj_uX63?nkWyf;^*B @~a`Eu+6L.ü>}y}_O6͐:YrGXkGl^w~㒶syIu! W XN7BVO!X2wvGRfT#t/?%8^WaTGcLMI(J1~8?aT ]ASE(*E} 2#I/׍qz^t̔bYz4xt){ OH+(EA&NXTo"XC')}Jzp ~5}^+6wcQ|LpdH}(.|kc4^"Z?ȕ a<L!039C EuCFEwç ;n?*oB8bʝ'#RqfM}7]s2tcS{\icTx;\7KPʇ Z O-~c>"?PEO8@8GQgaՎ󁶠䧘_%#r>1zaebqcPѵn#L =׀t L7`VA{C:ge@w1 Xp3c3ġpM"'-@n4fGB3DJ8[JoߐgK)ƛ$ 83+ 6ʻ SkI*KZlT _`?KQKdB`s}>`*>,*@JdoF*弝O}ks]yߘc1GV<=776qPTtXԀ!9*44Tހ3XΛex46YD  BdemDa\_l,G/֌7Y](xTt^%GE4}bTڹ;Y)BQu>J/J ⮶.XԄjݳ+Ed r5_D1 o Bx΢#<W8R6@gM. drD>(otU@x=~v2 ӣdoBd3eO6㣷ݜ66YQz`S{\P~z m5{J/L1xO\ZFu>ck#&:`$ai>2ΔloF[hlEܺΠk:)` $[69kOw\|8}ބ:񶐕IA1/=2[,!.}gN#ub ~݊}34qdELc$"[qU硬g^%B zrpJru%v\h1Yne`ǥ:gpQM~^Xi `S:V29.PV?Bk AEvw%_9CQwKekPؠ\;Io d{ ߞoc1eP\ `E=@KIRYK2NPlLɀ)&eB+ь( JTx_?EZ }@ 6U뙢طzdWIn` D噥[uV"G&Ú2g}&m?ċ"Om# {ON"SXNeysQ@FnVgdX~nj]J58up~.`r\O,ư0oS _Ml4kv\JSdxSW<AeIX$Iw:Sy›R9Q[,5;@]%u@ *rolbI  +%m:͇ZVủθau,RW33 dJeTYE.Mϧ-oj3+yy^cVO9NV\nd1 !͕_)av;թMlWR1)ElP;yوÏu 3k5Pr6<⒲l!˞*u־n!l:UNW %Chx8vL'X@*)̮ˍ D-M+JUkvK+x8cY?Ԡ~3mo|u@[XeYC\Kpx8oCC&N~3-H MXsu<`~"WL$8ξ3a)|:@m\^`@ҷ)5p+6p%i)P Mngc#0AruzRL+xSS?ʮ}()#tmˇ!0}}y$6Lt;$ʳ{^6{v6ķܰgVcnn ~zx«,2u?cE+ȘH؎%Za)X>uWTzNyosFQƤ$*&LLXL)1" LeOɟ9=:tZcŽY?ӭVwv~,Yrۗ|yGaFC.+ v1fήJ]STBn5sW}y$~z'c 8  ,! pVNSNNqy8z˱A4*'2n<s^ǧ˭PJޮɏUGLJ*#i}K%,)[z21z ?Nin1?TIR#m-1lA`fT5+ܐcq՝ʐ,3f2Uեmab#ŠdQy>\)SLYw#.ʑf ,"+w~N'cO3FN<)j&,- љ֊_zSTǦw>?nU仆Ve0$CdrP m׈eXmVu L.bֹ [Դaզ*\y8Է:Ez\0KqC b̘cөQ=0YsNS.3.Oo:#v7[#߫ 5܎LEr49nCOWlG^0k%;YߝZǓ:S#|}y,/kLd TA(AI$+I3;Y*Z}|ӧOdv..#:nf>>ȶITX 8y"dR|)0=n46ⲑ+ra ~]R̲c?6(q;5% |uj~z8R=XIV=|{vGj\gcqz؋%Mߍ1y#@f^^>N#x#۹6Y~?dfPO{P4Vu1E1J *|%JN`eWuzk M6q t[ gGvWIGu_ft5j"Y:Tɐ*; e54q$C2d} _SL#mYpO.C;cHi#֩%+) ӍƲVSYźg |tj38r|V1#;.SQA[S#`n+$$I P\[@s(EDzP])8G#0B[ىXIIq<9~[Z멜Z⊔IWU&A>P~#dp]9 "cP Md?٥Ifتuk/F9c*9Ǎ:ØFzn*@|Iށ9N3{'['ͬҲ4#}!V Fu,,mTIkv C7vB6kT91*l '~ƞFlU'M ][ΩũJ_{iIn$L jOdxkza۪#EClx˘oVɞljr)/,߬hL#^Lф,íMƁe̩NBLiLq}(q6IçJ$WE$:=#(KBzђ xlx?>Պ+>W,Ly!_DŌlQ![ SJ1ƐY}b,+Loxɓ)=yoh@꥟/Iѭ=Py9 ۍYӘe+pJnϱ?V\SO%(t =?MR[Șd/ nlB7j !;ӥ/[-A>dNsLj ,ɪv=1c.SQO3UƀܽE̻9GϷD7(}Ävӌ\y_0[w <΍>a_[0+LF.޺f>oNTq;y\bՃyjH<|q-eɏ_?_9+PHp$[uxK wMwNی'$Y2=qKBP~Yul:[<F12O5=d]Ysw:ϮEj,_QXz`H1,#II dwrP˂@ZJVy$\y{}^~[:NߌUOdؾe${p>G3cĖlʌ ת[`ϱ-WdgIig2 }s ؤ(%#sS@~3XnRG~\jc3vӍLM[JBTs3}jNʖW;7ç?=XF=-=qߚ#='c7ڑWI(O+=:uxqe2zi+kuGR0&eniT^J~\jyp'dtGsO39* b#Ɋ p[BwsT>d4ۧsnvnU_~,vƜJ1s QIz)(lv8MU=;56Gs#KMP=LvyGd}VwWBF'à ?MHUg2 !p7Qjڴ=ju JnA suMeƆҔ!)'8Ϣٔޝ(Vpצ֖d=ICJǠ{qkԭ߸i@Ku|p=..*+xz[Aqġ#s2aƊRR)*HRsi~a &fMP-KL@ZXy'x{}Zm+:)) IJ-iu ܒH'L(7yGӜq j 6ߌg1go,kرtY?W,pefOQS!K۟cҒA|սj>=⬒˧L[ ߿2JaB~Ru:Q] 0H~]7ƼI(}cq 'ήETq?fabӥvr )o-Q_'ᴎoK;Vo%~OK *bf:-ťIR`B5!RB@ï u ̯e\_U_ gES3QTaxU<~c?*#]MW,[8Oax]1bC|踤Plw5V%){t<d50iXSUm:Z┵i"1^B-PhJ&)O*DcWvM)}Pܗ-q\mmζZ-l@}aE6F@&Sg@ݚM ȹ 4#p\HdYDoH"\..RBHz_/5˘6KhJRPmƶim3,#ccoqa)*PtRmk7xDE\Y閣_X<~)c[[BP6YqS0%_;Àv~| VS؇ 'O0F0\U-d@7SJ*z3nyPOm~P3|Yʉr#CSN@ ƮRN)r"C:: #qbY. 6[2K2uǦHYRQMV G$Q+.>nNHq^ qmMVD+-#*U̒ p욳u:IBmPV@Or[b= 1UE_NmyKbNOU}the`|6֮P>\2PVIDiPO;9rmAHGWS]J*_G+kP2KaZH'KxWMZ%OYDRc+o?qGhmdSoh\D|:WUAQc yTq~^H/#pCZTI1ӏT4"ČZ}`w#*,ʹ 0i課Om*da^gJ݅{le9uF#Tֲ̲ٞC"qߍ ոޑo#XZTp@ o8(jdxw],f`~|,s^f1t|m򸄭/ctr5s79Q4H1꠲BB@l9@C+wpxu£Yc9?`@#omHs2)=2.ljg9$YS%*LRY7Z,*=䷘$armoϰUW.|rufIGwtZwo~5 YյhO+=8fF)W7L9lM̘·Y֘YLf큹pRF99.A "wz=E\Z'a 2Ǚ#;'}G*l^"q+2FQ hjkŦ${ޮ-T٭cf|3#~RJt$b(R(rdx >U b&9,>%E\ Άe$'q't*אެb-|dSBOO$R+H)܎K1m`;J2Y~9Og8=vqD`K[F)k[1m޼cn]skz$@)!I x՝"v9=ZA=`Ɠi :E)`7vI}dYI_ o:obo 3Q&D&2= Ά;>hy.*ⅥSӬ+q&j|UƧ}J0WW< ۋS)jQRjƯrN)Gű4Ѷ(S)Ǣ8iW52No˓ ۍ%5brOnL;n\G=^UdI8$&h'+(cȁ߫klS^cƗjԌEꭔgFȒ@}O*;evWVYJ\]X'5ղkFb 6Ro՜mi Ni>J?lPmU}>_Z&KKqrIDՉ~q3fL:Se>E-G{L6pe,8QIhaXaUA'ʂs+טIjP-y8ۈZ?J$WP Rs]|l(ԓsƊio(S0Y 8T97.WiLc~dxcE|2!XKƘਫ਼$((6~|d9u+qd^389Y6L.I?iIq9)O/뚅OXXVZF[یgQLK1RҖr@v#XlFНyS87kF!AsM^rkpjPDyS$Nqnxҍ!Uf!ehi2m`YI9r6 TFC}/y^Η5d'9A-J>{_l+`A['յϛ#w:݅%X}&PStQ"-\縵/$ƗhXb*yBS;Wջ_mcvt?2}1;qSdd~u:2k52R~z+|HE!)Ǟl7`0<,2*Hl-x^'_TVgZA'j ^2ΪN7t?w x1fIzC-ȖK^q;-WDvT78Z hK(P:Q- 8nZ܃e貾<1YT<,"6{/ ?͟|1:#gW>$dJdB=jf[%rE^il:BxSּ1հ,=*7 fcG#q eh?27,!7x6nLC4x},GeǝtC.vS F43zz\;QYC,6~;RYS/6|25vTimlv& nRh^ejRLGf? ۉҬܦƩ|Ȱ>3!viʯ>vオX3e_1zKȗ\qHS,EW[㺨uch⍸O}a>q6n6N6qN ! 1AQaq0@"2BRb#Pr3C`Scst$4D%Td ?Na3mCwxAmqmm$4n淿t'C"wzU=D\R+wp+YT&պ@ƃ3ޯ?AﶂaŘ@-Q=9Dռѻ@MVP܅G5fY6# ?0UQ,IX(6ڵ[DIMNލc&υj\XR|,4 jThAe^db#$]wOӪ1y%LYm뭛CUƃߜ}Cy1XνmF8jI]HۺиE@Ii;r8ӭVFՇ| &?3|xBMuSGe=Ӕ#BE5GY!z_eqр/W>|-Ci߇t1ޯќdR3ug=0 5[?#͏qcfH{ ?u=??ǯ}ZzhmΔBFTWPxs}G93 )gGR<>r h$'nchPBjJҧH -N1N?~}-q!=_2hcMlvY%UE@|vM2.Y[|y"EïKZF,ɯ?,q?vM 80jx";9vk+ ֧ ȺU?%vcVmA6Qg^MA}3nl QRNl8kkn'(M7m9وq%ޟ*h$Zk"$9: ?U8Sl,,|ɒxH(ѷGn/Q4PG%Ա8N! &7;eKM749R/%lc>x;>C:th?aKXbheᜋ^$Iհ hr7%F$EFdt5+(M6tÜUU|zW=aTsTgdqPQb'm1{|YXNb P~F^F:k6"j! Ir`1&-$Bevk:y#ywI0x=D4tUPZHڠ底taP6b>xaQ# WeFŮNjpJ* mQN*I-*ȩFg3 5Vʊɮa5FO@{NX?H]31Ri_uѕ 0 F~:60p͈SqX#a5>`o&+<2D: ڝ$nP*)N|yEjF5ټeihyZ >kbHavh-#!Po=@k̆IEN@}Ll?jO߭ʞQ|A07xwt!xfI2?Z<ץTcUj]陎Ltl }5ϓ$,Omˊ;@OjEj(ا,LXLOЦ90O .anA7j4 W_ٓzWjcBy՗+EM)dNg6y1_xp$Lv:9"zpʙ$^JԼ*ϭo=xLj6Ju82AH3$ٕ@=Vv]'qEz;I˼)=ɯx /W(Vp$ mu񶤑OqˎTr㠚xsrGCbypG1ߠw e8$⿄/M{*}W]˷.CK\ުx/$WPwr |i&}{X >$-l?-zglΆ(FhvS*b߲ڡn,|)mrH[a3ר[13o_U3TC$(=)0kgP u^=4 WYCҸ:vQרXàtkm,t*^,}D* "(I9R>``[~Q]#afi6l86:,ssN6j"A4IuQ6E,GnHzSHOuk5$I4ؤQ9@CwpBGv[]uOv0I4\yQѸ~>Z8Taqޣ;za/SI:ܫ_|>=Z8:SUIJ"IY8%b8H:QO6;7ISJҌAά3>cE+&jf$eC+z;V rʺmyeaQf&6ND.:NTvm<- uǝ\MvZYNNT-A>jr!SnO 13Ns%3D@`ܟ 1^c< aɽ̲Xë#w|ycW=9I*H8p^(4՗karOcWtO\ƍR8'KIQ?5>[}yUײ -h=% qThG2)"ו3]!kB*pFDlA,eEiHfPs5H:Փ~H0DتDIhF3c2E9H5zԑʚiX=:mxghd(v׊9iSOd@0ڽ:p5h-t&Xqӕ,ie|7A2O%PEhtjY1wЃ!  ࢽMy7\a@ţJ 4ȻF@o̒?4wx)]P~u57X 9^ܩU;Iꭆ 5 eK27({|Y׎ V\"Z1 Z}(Ǝ"1S_vE30>p; ΝD%xW?W?vo^Vidr[/&>~`9Why;R ;;ɮT?r$g1KACcKl:'3 cﳯ*"t8~l)m+U,z`(>yJ?h>]vЍG*{`;y]IT ;cNUfo¾h/$|NS1S"HVT4uhǜ]v;5͠x'C\SBplh}N ABx%ޭl/Twʽ]D=Kžr㻠l4SO?=k M: cCa#ha)ѐxcsgPiG{+xQI= zԫ+ 8"kñj=|c yCF/*9жh{ ?4o kmQNx;Y4膚aw?6>e]Qr:g,i"ԩA*M7qB?ӕFhV25r[7 Y }LR}*sg+xr2U=*'WSZDW]WǞ<叓{$9Ou4y90-1'*D`c^o?(9uݐ'PI& fJݮ:wSjfP1F:X H9dԯ˝[_54 }*;@ܨ ðynT?ןd#4rGͨH1|-#MrS3G3).᧏3vz֑r$G"`j 1tx0<ƆWh6y6,œGagAyb)hDß_mü gG;evݝnQ C-*oyaMI><]obD":GA-\%LT8c)+y76oQ#*{(F⽕y=rW\p۩cA^e6KʐcVf5$'->ՉN"F"UQ@fGb~#&M=8טJNu9D[̤so~ G9TtW^g5y$bY'سǴ=U-2 #MCt(i lj@Q 5̣i*OsxKf}\M{EV{υƇ);HIfeLȣr2>WIȂ6ik 5YOxȺ>Yf5'|H+98pjn.OyjY~iw'l;s2Y:'lgꥴ)o#'SaaKZ m}`169n"xI *+ }FP"l45'ZgE8?[X7(.Q-*ތL@̲v.5[=t\+CNܛ,gSQnH}*FG16&:t4ُ"Ạ$b |#rsaT ]ӽDP7ո0y)e$ٕvIh'QEAm*HRI=: 4牢) %_iNݧl] NtGHL ɱg<1V,J~ٹ"KQ 9HS9?@kr;we݁]I!{ @G["`J:n]{cAEVʆ#U96j#Ym\qe4hB7Cdv\MNgmAyQL4uLjj9#44tl^}LnR!t±]rh6ٍ>yҏNfU  Fm@8}/ujb9he:AyծwGpΧh5l}3p468)Udc;Us/֔YX1O2uqs`hwgr~{ RmhN؎*q 42*th>#E#HvOq}6e\,Wk#Xb>p}դ3T5†6[@Py*n|'f֧>lư΂̺SU'*qp_SM 'c6m ySʨ;MrƋmKxo,GmPAG:iw9}M(^V$ǒѽ9| aJSQarB;}ٻ֢2%Uc#gNaݕ'v[OY'3L3;,p]@S{lsX'cjwk'a.}}& dP*bK=ɍ!;3ngΊUߴmt'*{,=SzfD Ako~Gaoq_mi}#mPXhύmxǍ΂巿zfQc|kc?WY$_Lvl߶c`?ljݲˏ!V6UЂ(A4y)HpZ_x>eR$/`^'3qˏ-&Q=?CFVR DfV9{8gnh(P"6[D< E~0<@`G6Hгcc cK.5DdB`?XQ2ٿyqo&+1^ DW0ꊩG#QnL3c/x 11[yxპCWCcUĨ80me4.{muI=f0QRls9f9~fǨa"@8ȁQ#cicG$Gr/$W(WV"m7[mAmboD j۳ l^kh׽ # iXnveTka^Y4BNĕ0 !01@Q"2AaPq3BR?@4QT3,㺠W[=JKϞ2r^7vc:9 EߴwS#dIxu:Hp9E! V 2;73|F9Y*ʬFDu&y؟^EAA(ɩ^GV:ݜDy`Jr29ܾ㝉[E;FzxYGUeYC v-txIsםĘqEb+P\ :>iC';k|zرny]#ǿbQw(r|ӹs[D2v-%@;8<a[\o[ϧwI!*0krs)[J9^ʜp1) "/_>o<1AEy^C`x1'ܣnps`lfQ):lb>MejH^?kl3(z:1ŠK&?Q~{ٺhy/[V|6}KbXmn[-75q94dmc^h X5G-}دBޟ |rtMV+]c?-#ڛ^ǂ}LkrOu>-Dry D?:ޞUǜ7V?瓮"#rչģVR;n/_ ؉vݶe5db9/O009G5nWJpA*r9>1.[tsFnQ V 77R]ɫ8_0<՜IFu(v4Fk3E)N:yڮeP`1}$WSJSQNjٺ޵#lј(5=5lǏmoWv-1v,Wmn߀$x_DȬ0¤#QR[Vkzmw"9ZG7'[=Qj8R?zf\a=OU*oBA|G254 p.w7  &ξxGHp B%$gtЏ򤵍zHNuЯ-'40;_3 !01"@AQa2Pq#3BR?ʩcaen^8F<7;EA{EÖ1U/#d1an.1ě0ʾRh|RAo3m3 % 28Q yφHTo7lW>#i`qca m,B-j݋'mR1Ήt>Vps0IbIC.1Rea]H64B>o]($Bma!=?B KǾ+Ծ"nK*+[T#{EJSQs5:U\wĐf3܆&)IԆwE TlrTf6Q|Rh:[K zc֧GC%\_a84HcObiؖV7H )*ģK~Xhչ04?0 E<}3#u? |gS6ꊤ|I#Hڛ աwX97Ŀ%SLy6č|Fa 8b$sקhb9RAu7˨pČ_\*w묦F 4D~f|("mNKiS>$d7SlA/²SL|6N}S˯g]6; #. 403WebShell
403Webshell
Server IP : 45.32.152.128  /  Your IP : 216.73.216.91
Web Server : nginx/1.24.0
System : Linux stage-vultr 5.4.0-216-generic #236-Ubuntu SMP Fri Apr 11 19:53:21 UTC 2025 x86_64
User : forge ( 1000)
PHP Version : 8.2.14
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : ON  |  Pkexec : ON
Directory :  /etc/fail2ban/action.d/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /etc/fail2ban/action.d/badips.py
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*-
# vi: set ft=python sts=4 ts=4 sw=4 noet :

# This file is part of Fail2Ban.
#
# Fail2Ban is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# Fail2Ban is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Fail2Ban; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

import sys
if sys.version_info < (2, 7): # pragma: no cover
	raise ImportError("badips.py action requires Python >= 2.7")
import json
import threading
import logging
if sys.version_info >= (3, ): # pragma: 2.x no cover
	from urllib.request import Request, urlopen
	from urllib.parse import urlencode
	from urllib.error import HTTPError
else: # pragma: 3.x no cover
	from urllib2 import Request, urlopen, HTTPError
	from urllib import urlencode

from fail2ban.server.actions import Actions, ActionBase, BanTicket
from fail2ban.helpers import splitwords, str2LogLevel



class BadIPsAction(ActionBase): # pragma: no cover - may be unavailable
	"""Fail2Ban action which reports bans to badips.com, and also
	blacklist bad IPs listed on badips.com by using another action's
	ban method.

	Parameters
	----------
	jail : Jail
		The jail which the action belongs to.
	name : str
		Name assigned to the action.
	category : str
		Valid badips.com category for reporting failures.
	score : int, optional
		Minimum score for bad IPs. Default 3.
	age : str, optional
		Age of last report for bad IPs, per badips.com syntax.
		Default "24h" (24 hours)
	banaction : str, optional
		Name of banaction to use for blacklisting bad IPs. If `None`,
		no blacklist of IPs will take place.
		Default `None`.
	bancategory : str, optional
		Name of category to use for blacklisting, which can differ
		from category used for reporting. e.g. may want to report
		"postfix", but want to use whole "mail" category for blacklist.
		Default `category`.
	bankey : str, optional
		Key issued by badips.com to retrieve personal list
		of blacklist IPs.
	updateperiod : int, optional
		Time in seconds between updating bad IPs blacklist.
		Default 900 (15 minutes)
	loglevel : int/str, optional
		Log level of the message when an IP is (un)banned.
		Default `DEBUG`.
		Can be also supplied as two-value list (comma- or space separated) to
		provide level of the summary message when a group of IPs is (un)banned.
		Example `DEBUG,INFO`.
	agent : str, optional
		User agent transmitted to server.
		Default `Fail2Ban/ver.`

	Raises
	------
	ValueError
		If invalid `category`, `score`, `banaction` or `updateperiod`.
	"""

	TIMEOUT = 10
	_badips = "https://www.badips.com"
	def _Request(self, url, **argv):
		return Request(url, headers={'User-Agent': self.agent}, **argv)

	def __init__(self, jail, name, category, score=3, age="24h",
		banaction=None, bancategory=None, bankey=None, updateperiod=900, 
		loglevel='DEBUG', agent="Fail2Ban", timeout=TIMEOUT):
		super(BadIPsAction, self).__init__(jail, name)

		self.timeout = timeout
		self.agent = agent
		self.category = category
		self.score = score
		self.age = age
		self.banaction = banaction
		self.bancategory = bancategory or category
		self.bankey = bankey
		loglevel = splitwords(loglevel)
		self.sumloglevel = str2LogLevel(loglevel[-1])
		self.loglevel = str2LogLevel(loglevel[0])
		self.updateperiod = updateperiod

		self._bannedips = set()
		# Used later for threading.Timer for updating badips
		self._timer = None

	@staticmethod
	def isAvailable(timeout=1):
		try:
			response = urlopen(Request("/".join([BadIPsAction._badips]),
					headers={'User-Agent': "Fail2Ban"}), timeout=timeout)
			return True, ''
		except Exception as e: # pragma: no cover
			return False, e

	def logError(self, response, what=''): # pragma: no cover - sporadical (502: Bad Gateway, etc)
		messages = {}
		try:
			messages = json.loads(response.read().decode('utf-8'))
		except:
			pass
		self._logSys.error(
			"%s. badips.com response: '%s'", what,
				messages.get('err', 'Unknown'))

	def getCategories(self, incParents=False):
		"""Get badips.com categories.

		Returns
		-------
		set
			Set of categories.

		Raises
		------
		HTTPError
			Any issues with badips.com request.
		ValueError
			If badips.com response didn't contain necessary information
		"""
		try:
			response = urlopen(
				self._Request("/".join([self._badips, "get", "categories"])), timeout=self.timeout)
		except HTTPError as response: # pragma: no cover
			self.logError(response, "Failed to fetch categories")
			raise
		else:
			response_json = json.loads(response.read().decode('utf-8'))
			if not 'categories' in response_json:
				err = "badips.com response lacked categories specification. Response was: %s" \
				  % (response_json,)
				self._logSys.error(err)
				raise ValueError(err)
			categories = response_json['categories']
			categories_names = set(
				value['Name'] for value in categories)
			if incParents:
				categories_names.update(set(
					value['Parent'] for value in categories
					if "Parent" in value))
			return categories_names

	def getList(self, category, score, age, key=None):
		"""Get badips.com list of bad IPs.

		Parameters
		----------
		category : str
			Valid badips.com category.
		score : int
			Minimum score for bad IPs.
		age : str
			Age of last report for bad IPs, per badips.com syntax.
		key : str, optional
			Key issued by badips.com to fetch IPs reported with the
			associated key.

		Returns
		-------
		set
			Set of bad IPs.

		Raises
		------
		HTTPError
			Any issues with badips.com request.
		"""
		try:
			url = "?".join([
				"/".join([self._badips, "get", "list", category, str(score)]),
				urlencode({'age': age})])
			if key:
				url = "&".join([url, urlencode({'key': key})])
			self._logSys.debug('badips.com: get list, url: %r', url)
			response = urlopen(self._Request(url), timeout=self.timeout)
		except HTTPError as response: # pragma: no cover
			self.logError(response, "Failed to fetch bad IP list")
			raise
		else:
			return set(response.read().decode('utf-8').split())

	@property
	def category(self):
		"""badips.com category for reporting IPs.
		"""
		return self._category

	@category.setter
	def category(self, category):
		if category not in self.getCategories():
			self._logSys.error("Category name '%s' not valid. "
				"see badips.com for list of valid categories",
				category)
			raise ValueError("Invalid category: %s" % category)
		self._category = category

	@property
	def bancategory(self):
		"""badips.com bancategory for fetching IPs.
		"""
		return self._bancategory

	@bancategory.setter
	def bancategory(self, bancategory):
		if bancategory != "any" and bancategory not in self.getCategories(incParents=True):
			self._logSys.error("Category name '%s' not valid. "
				"see badips.com for list of valid categories",
				bancategory)
			raise ValueError("Invalid bancategory: %s" % bancategory)
		self._bancategory = bancategory

	@property
	def score(self):
		"""badips.com minimum score for fetching IPs.
		"""
		return self._score

	@score.setter
	def score(self, score):
		score = int(score)
		if 0 <= score <= 5:
			self._score = score
		else:
			raise ValueError("Score must be 0-5")

	@property
	def banaction(self):
		"""Jail action to use for banning/unbanning.
		"""
		return self._banaction

	@banaction.setter
	def banaction(self, banaction):
		if banaction is not None and banaction not in self._jail.actions:
			self._logSys.error("Action name '%s' not in jail '%s'",
				banaction, self._jail.name)
			raise ValueError("Invalid banaction")
		self._banaction = banaction

	@property
	def updateperiod(self):
		"""Period in seconds between banned bad IPs will be updated.
		"""
		return self._updateperiod

	@updateperiod.setter
	def updateperiod(self, updateperiod):
		updateperiod = int(updateperiod)
		if updateperiod > 0:
			self._updateperiod = updateperiod
		else:
			raise ValueError("Update period must be integer greater than 0")

	def _banIPs(self, ips):
		for ip in ips:
			try:
				ai = Actions.ActionInfo(BanTicket(ip), self._jail)
				self._jail.actions[self.banaction].ban(ai)
			except Exception as e:
				self._logSys.error(
					"Error banning IP %s for jail '%s' with action '%s': %s",
					ip, self._jail.name, self.banaction, e,
					exc_info=self._logSys.getEffectiveLevel()<=logging.DEBUG)
			else:
				self._bannedips.add(ip)
				self._logSys.log(self.loglevel,
					"Banned IP %s for jail '%s' with action '%s'",
					ip, self._jail.name, self.banaction)

	def _unbanIPs(self, ips):
		for ip in ips:
			try:
				ai = Actions.ActionInfo(BanTicket(ip), self._jail)
				self._jail.actions[self.banaction].unban(ai)
			except Exception as e:
				self._logSys.error(
					"Error unbanning IP %s for jail '%s' with action '%s': %s",
					ip, self._jail.name, self.banaction, e,
					exc_info=self._logSys.getEffectiveLevel()<=logging.DEBUG)
			else:
				self._logSys.log(self.loglevel,
					"Unbanned IP %s for jail '%s' with action '%s'",
					ip, self._jail.name, self.banaction)
			finally:
				self._bannedips.remove(ip)

	def start(self):
		"""If `banaction` set, blacklists bad IPs.
		"""
		if self.banaction is not None:
			self.update()

	def update(self):
		"""If `banaction` set, updates blacklisted IPs.

		Queries badips.com for list of bad IPs, removing IPs from the
		blacklist if no longer present, and adds new bad IPs to the
		blacklist.
		"""
		if self.banaction is not None:
			if self._timer:
				self._timer.cancel()
				self._timer = None

			try:
				ips = self.getList(
					self.bancategory, self.score, self.age, self.bankey)
				# Remove old IPs no longer listed
				s = self._bannedips - ips
				m = len(s)
				self._unbanIPs(s)
				# Add new IPs which are now listed
				s = ips - self._bannedips
				p = len(s)
				self._banIPs(s)
				if m != 0 or p != 0:
					self._logSys.log(self.sumloglevel,
						"Updated IPs for jail '%s' (-%d/+%d)",
						self._jail.name, m, p)
				self._logSys.debug(
					"Next update for jail '%' in %i seconds",
					self._jail.name, self.updateperiod)
			finally:
				self._timer = threading.Timer(self.updateperiod, self.update)
				self._timer.start()

	def stop(self):
		"""If `banaction` set, clears blacklisted IPs.
		"""
		if self.banaction is not None:
			if self._timer:
				self._timer.cancel()
				self._timer = None
			self._unbanIPs(self._bannedips.copy())

	def ban(self, aInfo):
		"""Reports banned IP to badips.com.

		Parameters
		----------
		aInfo : dict
			Dictionary which includes information in relation to
			the ban.

		Raises
		------
		HTTPError
			Any issues with badips.com request.
		"""
		try:
			url = "/".join([self._badips, "add", self.category, str(aInfo['ip'])])
			self._logSys.debug('badips.com: ban, url: %r', url)
			response = urlopen(self._Request(url), timeout=self.timeout)
		except HTTPError as response: # pragma: no cover
			self.logError(response, "Failed to ban")
			raise
		else:
			messages = json.loads(response.read().decode('utf-8'))
			self._logSys.debug(
				"Response from badips.com report: '%s'",
				messages['suc'])

Action = BadIPsAction

Youez - 2016 - github.com/yon3zu
LinuXploit