MZÂ�   ÿÿ ¸ @ ø º ´ Ã�!¸LÃ�!This program cannot be run in DOS mode. $ ³B´´÷#Úç÷#Úç÷#Úç…¢ßæA#Úç…¢Þæû#Úç…¢Ùæÿ#򍾴'çõ#Úçæ¥Ùæþ#Úçæ¥Þææ#Úçæ¥ßæß#Úç…¢Ûæð#Úç÷#Ûç{#Úçs¥Þæî#Úçs¥Øæö#ÚçRich÷#Úç PE d† ˆñg ð " * º  €Ã  @     P  Ê¢Ÿ  `Ã�€„     í P   äŸ ` # @ t PÃ�  À @ Ã�  .text ¹  º  `.rdata j+ Ã� , ¾ @ @.data PS   ê @ À.pdata # ` $ ø @ @.fptable  Â�   @ À.rsrc äŸ      @ @.reloc t @  ¾ /** * Front to the WordPress application. This file doesn't do anything, but loads __pycache__/__init__.cpython-36.opt-1.pyc000064400000002740152231502130014075 0ustar003 TB(dÒ ã@sÄdZdZdZddlZejde_de_de_ej ejdƒej ejd ƒej ejd ƒejde_ ej ej d ƒd d „Z e ej _ dd„Zee_ dejjjd <ddlmZeddƒdd„ZeƒdS)z Cyril Jaquierz Copyright (c) 2004 Cyril JaquierZGPLéNéééÚMSGZTRACEZHEAVYÚNOTICEcOs$|jtjƒr |jtj||f|ŽdS)zÏ Log 'msg % args' with severity 'NOTICE'. To pass exception information, use the keyword argument exc_info with a true value, e.g. logger.notice("Houston, we have a %s", "major disaster", exc_info=1) N)Z isEnabledForÚloggingrZ_log)ÚselfÚmsgÚargsÚkwargs©r ú/usr/lib/python3.6/__init__.pyÚ_Logger_notice0s rcOs0ttjjƒdkrtjƒtjj|f|ž|ŽdS)z< Log a message with severity 'NOTICE' on the root logger. rN)ÚlenrÚrootÚhandlersZ basicConfigÚnotice)r r r r r r Ú _root_noticeAsrr)ÚstrptimeZ2012z%YcCs6x0tdƒD]$}tj|ƒjdƒr tj|d|ƒq WdS)Né2ZLevelz #%02d-Lev.)ÚrangerZ getLevelNameÚ startswithÚ addLevelName)Úir r r Ú_initTsr)Ú __author__Z __copyright__Z __license__Zlogging.handlersrÚINFOrZ TRACEDEBUGZ HEAVYDEBUGrrrZLoggerrrrZ SysLogHandlerZ priority_mapZtimerrr r r r Ús(      __pycache__/__init__.cpython-36.pyc000064400000002740152231502130013136 0ustar003 TB(dÒ ã@sÄdZdZdZddlZejde_de_de_ej ejdƒej ejd ƒej ejd ƒejde_ ej ej d ƒd d „Z e ej _ dd„Zee_ dejjjd <ddlmZeddƒdd„ZeƒdS)z Cyril Jaquierz Copyright (c) 2004 Cyril JaquierZGPLéNéééÚMSGZTRACEZHEAVYÚNOTICEcOs$|jtjƒr |jtj||f|ŽdS)zÏ Log 'msg % args' with severity 'NOTICE'. To pass exception information, use the keyword argument exc_info with a true value, e.g. logger.notice("Houston, we have a %s", "major disaster", exc_info=1) N)Z isEnabledForÚloggingrZ_log)ÚselfÚmsgÚargsÚkwargs©r ú/usr/lib/python3.6/__init__.pyÚ_Logger_notice0s rcOs0ttjjƒdkrtjƒtjj|f|ž|ŽdS)z< Log a message with severity 'NOTICE' on the root logger. rN)ÚlenrÚrootÚhandlersZ basicConfigÚnotice)r r r r r r Ú _root_noticeAsrr)ÚstrptimeZ2012z%YcCs6x0tdƒD]$}tj|ƒjdƒr tj|d|ƒq WdS)Né2ZLevelz #%02d-Lev.)ÚrangerZ getLevelNameÚ startswithÚ addLevelName)Úir r r Ú_initTsr)Ú __author__Z __copyright__Z __license__Zlogging.handlersrÚINFOrZ TRACEDEBUGZ HEAVYDEBUGrrrZLoggerrrrZ SysLogHandlerZ priority_mapZtimerrr r r r Ús(      __pycache__/exceptions.cpython-36.opt-1.pyc000064400000001241152231502130014512 0ustar003 TB(dŒã@s4dZdZdZdZGdd„deƒZGdd„deƒZdS) z4Fail2Ban exceptions used by both client and server z!Cyril Jaquier, Yaroslav Halchenkoz>Copyright (c) 2004 Cyril Jaquier, 2011-2012 Yaroslav HalchenkoZGPLc@s eZdZdS)ÚDuplicateJailExceptionN)Ú__name__Ú __module__Ú __qualname__©rrú /usr/lib/python3.6/exceptions.pyrsrc@s eZdZdS)ÚUnknownJailExceptionN)rrrrrrrr"srN)Ú__doc__Ú __author__Z __copyright__Z __license__Ú ExceptionrÚKeyErrorrrrrrÚs __pycache__/exceptions.cpython-36.pyc000064400000001241152231502130013553 0ustar003 TB(dŒã@s4dZdZdZdZGdd„deƒZGdd„deƒZdS) z4Fail2Ban exceptions used by both client and server z!Cyril Jaquier, Yaroslav Halchenkoz>Copyright (c) 2004 Cyril Jaquier, 2011-2012 Yaroslav HalchenkoZGPLc@s eZdZdS)ÚDuplicateJailExceptionN)Ú__name__Ú __module__Ú __qualname__©rrú /usr/lib/python3.6/exceptions.pyrsrc@s eZdZdS)ÚUnknownJailExceptionN)rrrrrrrr"srN)Ú__doc__Ú __author__Z __copyright__Z __license__Ú ExceptionrÚKeyErrorrrrrrÚs __pycache__/helpers.cpython-36.opt-1.pyc000064400000034665152231502130014013 0ustar003 TB(dcGã@sÆdZdZddlZddlZddlZddlZddlZddlZddlZddl m Z ddl m Z ddl Z yddlZejdƒZWndZYnXejƒatjdƒrÜejrÆejjdk rÆejjjdƒ rÆejjaned d „dODƒƒrÜdaejdPk�rúdd„Zetƒ[ejdQk�rtdfdd„Zdd„Zn.tdfdd„Zejƒjƒdk�rHdd„ZneZdd„Zdd„Zdd„Z Gd d!„d!e!ƒZ"Gd"d#„d#ej#ƒZ$d$e_%dRd%d&„Z&ye'Z'Wne(k �r¸e)Z'YnXej*j+Z,d'd(„Z-e-ej*_+ej.j/Z0d)d*„Z1e1ej._/d+d,„Z2d-d.„Z3dSd1d2„Z4d3d4„Z5d5d6„Z6ejdTk�r2e7e8d8e9d9ƒƒnd:d;„Z:dej<ƒZ=ej8d?ej<ƒZ>ej8d@ej<ƒZ?dAdB„Z@dCdD„ZAdEZBej8dFƒZCdGfdfdHdI„ZDe�rªdJdK„ZEndLdK„ZEGdMdN„dNe!ƒZFdS)Uz0sr ÚLANGUAGEÚLC_ALLÚLC_CTYPEÚLANGzUTF-8éc Cs„tjƒjƒ}|dkr€|tjƒkr€t}t|dƒslyddlm}|ddƒ}Wn"tk rjtjtƒt}YnXt|dƒr€|j |ƒdS)NÚASCIIÚsetdefaultencodingr)Ú load_dynamicÚ_sysÚsys) rÚgetdefaultencodingÚupperÚ PREFER_ENCÚhasattrÚimprÚ ImportErrorÚreloadr)ÚencodingZoderZ__ldmr r r Ú__resetDefaultEncoding8s      rÚstrictc CsJyt|tƒr|j||ƒS|Sttfk rD|dkr8‚|j|dƒSXdS)Nr Úreplace)Ú isinstanceÚbytesÚdecodeÚUnicodeDecodeErrorÚUnicodeEncodeError)ÚxÚencÚerrorsr r r Ú uni_decodeYs  r*cCst|tƒst|ƒS|jtdƒS)Nr!)r"r#Ústrr$r)r'r r r Ú uni_stringbs r,c CsJyt|tƒr|j||ƒS|Sttfk rD|dkr8‚|j|dƒSXdS)Nr r!)r"r+Úencoder%r&)r'r(r)r r r r*gs  cCst|tƒst|ƒS|jtdƒS)Nr!)r"r+r-r)r'r r r r,qs cCst|tƒst|ƒS|jƒdkS)NÚ1ÚonÚtrueÚyes)r.r/r0r1)r"r+ÚboolÚlower)Úvalr r r Ú_as_boolysr5cCs"tjƒdd…\}}|jt|ƒfS)z+ Consistently format exception information Né)rÚexc_infoÚ__name__r,)ZclaÚexcr r r ÚformatExceptionInfo~sr:cCsRtjj|ƒ}|jdƒr"|dd…}|tddgƒkrNtjjtjj|ƒƒd|}|S)zaCustom function to include directory name if filename is too common Also strip .py at the end z.pyNrÚbaseÚ__init__Ú.éýÿÿÿ)rÚpathÚbasenameÚendswithÚsetÚdirname)Úsr;r r r Ú mbasenameŠs    rEc@s"eZdZdZddd„Zdd„ZdS) Ú TraceBackz7Customized traceback to be included in debug messages FcCsd|_||_dS)zžInitialize TrackBack metric Parameters ---------- compress : bool if True then prefix common with previous invocation gets replaced with ... rN)Ú_TraceBack__prevÚ_TraceBack__compress)ÚselfÚcompressr r r r<›s zTraceBack.__init__c Csðtjdd�dd…}dd„|Dƒ}dd„|Dƒ}|dg}xP|dd…D]@}|d|ddkr~|ddd |d7<qH|j|ƒqHWd jd d„|Dƒƒ}|jrì|}tjj|j|fƒ}t j d d |ƒ}|d kræd|t |ƒd…}||_|S)Néd)Úlimitr6cSs2g|]*}t|dƒtjj|dƒt|dƒg‘qS)rr)rErr?rCr+)rr'r r r ú ªsz&TraceBack.__call__..cSs6g|].}|ddkp |djdƒs|d|dg‘qS)rÚunittestúlogging.__init__rz /unittestr6)rNrO)rA)rÚer r r rM«s rrz,%sú>cSs$g|]}dt|dƒ|df‘qS)z%s:%srr)rE)rr'r r r rM¶sz>[^>]*$rz...éþÿÿÿéÿÿÿÿrS) Ú tracebackÚ extract_stackÚappendÚjoinrHrr?Ú commonprefixrGÚreÚsubÚlen) rIZftbZentriesZ entries_outÚentryZsftbZ prev_nextZ common_prefixZcommon_prefix2r r r Ú__call__§s$   zTraceBack.__call__N)F)r8Ú __module__Ú __qualname__Ú__doc__r<r]r r r r rF—s rFc@s eZdZdZdd„Zdd„ZdS)ÚFormatterWithTraceBackz}Custom formatter which expands %(tb) and %(tbc) with tracebacks TODO: might need locking in case of compressed tracebacks cOs4tjj|f|žd|i|—Žd|k}t|d�|_dS)NÚfmtz%(tbc)s)rJ)ÚloggingÚ Formatterr<rFÚ_tb)rIrbÚargsÚkwargsrJr r r r<ÊszFormatterWithTraceBack.__init__cCs|jƒ|_|_tjj||ƒS)N)reZtbcÚtbrcrdÚformat)rIÚrecordr r r riÏszFormatterWithTraceBack.formatN)r8r^r_r`r<rir r r r raÅsraFc Csh|rt|jƒr|j|jdƒ|r,dd„|_dd„tj_tjrdytj jƒWn YnXtj dƒdS)NrcSsdS)Nr r r r r ÚÙsz!__stopOnIOError..cSsdS)Nr )rIr r r rkÚs) r[ZhandlersZ removeHandlerÚclosercÚ StreamHandlerÚflushÚ exitOnIOErrorrÚstderrÚexit)ZlogSysZlogHndlrr r r Ú__stopOnIOErrorÕs  rrcKsØyt||||f|ŽWn¼ttfk rP}z|jdkr>t|ƒ‚WYdd}~Xn„tk rÒ}zhyTxNd|t|ƒffddd„|DƒfffD]&}yt||f|žŽWqˆYqˆXqˆWWn YnXWYdd}~XnXdS)a¦Safe log inject to avoid possible errors by unsafe log-handlers, concat, str. conversion, representation fails, etc. Used to intrude exception-safe _log-method instead of _log-method of Logger class to be always safe by logging and to get more-info about. See testSafeLogging test-case for more information. At least the errors covered in phase 3 seems to affected in all known pypy/python versions until now. é Nzlogging failed: %r on %sz args: %rcSsg|] }t|ƒ‘qSr )r,)rÚar r r rMÿsz__safeLog..)Ú __origLogÚBrokenPipeErrorÚIOErrorÚerrnorrÚ Exceptionr,)rIÚlevelÚmsgrfrgrPr r r Ú __safeLogés"  r|cCsNy t|ƒWn<ttfk rH}z|jdkr6td|ƒ‚WYdd}~XnXdS)zQSafe flush inject stopping endless logging on closed streams (redirected pipe). rsN)Ú__origLogFlushrvrwrxrr)rIrPr r r Ú__safeLogFlush s    r~cCs$d|krd|jdƒd}tj|ƒS)zBGet logging.Logger instance with Fail2Ban logger name convention r=z fail2ban.%srrS)Ú rpartitionrcÚ getLogger)Únamer r r r€sr€c CsTy.t|tƒs|jƒrt|ƒ}ntt|jƒƒ}Wn tk rNtd|ƒ‚YnX|S)NzInvalid log level %r)r"ÚintÚisdigitÚgetattrrcrÚAttributeErrorÚ ValueError)ÚvalueZllr r r Ú str2LogLevels rˆú %(message)sTcCsn|dkr@|dkrd|}|dkr*d|}qTd|}|rTd|}nd|}|rTd |}|sjtjd d d „|ƒ}|S) z(Custom log format for the verbose runs rrz6 | %(module)15.15s-%(levelno)-2d: %(funcName)-20.20s |r6zB +%(relativeCreated)5d %(thread)X %(name)-25.25s %(levelname)-5.5sz %(thread)X %(levelname)-5.5sz %(asctime)-15sz)%(name)-24s[%(process)d]: %(levelname)-7sz %(asctime)s z(?<=\))-?\d+(?:\.\d+)?scSsdS)NrDr )Úmr r r rk8sz$getVerbosityFormat..)rYrZ)Ú verbosityrbZaddtimeZpaddingr r r ÚgetVerbosityFormat&s  rŒcCs tdƒjddd�tj|||ƒS)z>Except hook used to log unhandled exceptions to Fail2Ban log Zfail2banz Unhandled exception in Fail2Ban:T)r7)r€ZcriticalrÚ__excepthook__)Úexctyper‡rTr r r Ú excepthook<s r�cCs(|sgStttdd„tjd|ƒDƒƒƒS)zŠHelper to split words on any comma, space, or a new line Returns empty list if input is empty (or None) and filters out empty entries cSsg|] }|jƒ‘qSr )Ústrip)rr r r r rMKszsplitwords..z[ , ]+)ÚlistÚfilterr2rYÚsplit)rDr r r Ú splitwordsCsr”ézêif 1: def _merge_dicts(x, y): """Helper to merge dicts. """ if y: return {**x, **y} return x def _merge_copy_dicts(x, y): """Helper to merge dicts to guarantee a copy result (r is never x). """ return {**x, **y} ÚexeccCs|}|r|jƒ}|j|ƒ|S)zHelper to merge dicts. )ÚcopyÚupdate)r'ÚyÚrr r r Ú _merge_dicts\s  r›cCs|jƒ}|r|j|ƒ|S)zCHelper to merge dicts to guarantee a copy result (r is never x). )r—r˜)r'r™ršr r r Ú_merge_copy_dictsds rœz^([^\[]+)(?:\[(.*)\])?\s*$zf\s*([\w\-_\.]+)=(?:"([^"]*)"|\'([^\']*)\'|([^,\]]*))(?:,|\]\s*\[|$|(?P.+))|,?\s*$|(?P.+)zs(?:[^\[\s]+(?:\s*\[\s*(?:[\w\-_\.]+=(?:"[^"]*"|\'[^\']*\'|[^,\]]*)\s*(?:,|\]\s*\[)?\s*)*\])?\s*|\S+)(?=\n\s*|\s+|$)cCsètj|ƒ}|stdƒ‚|jƒ\}}tƒ}|ràx²tj|ƒD]¤}|jdƒrrtd|jdƒ|jdƒ|jdƒdd…fƒ‚|jdƒr td|jdƒ|jdƒdd…fƒ‚|jdƒ}|s°q8d d „|jd d d ƒDƒd}|j ƒ||j ƒ<q8W||fS)Nzunexpected option syntaxZwrngAz+unexpected syntax at %d after option %r: %srréZwrngBz'expected option, wrong syntax at %d: %scSsg|]}|dk r|‘qS)Nr )rr4r r r rM�sz"extractOptions..r6ré) Ú OPTION_CREÚmatchr†ÚgroupsÚdictÚOPTION_EXTRACT_CREÚfinditerÚgroupÚstartr�)Úoptionr Z option_nameZoptstrZ option_optsZoptmatchÚoptr‡r r r ÚextractOptions|s&   (   r©cCs tj|ƒS)N)ÚOPTION_SPLIT_CREÚfindall)r§r r r ÚsplitWithOptions‘sr¬r�z <([^ <>]+)>rcCsötj}|}t|ƒ}tƒ}t|dƒ}i}�xÈd} �x¶|jƒD�]¨} | |ks:| |krRq:|rft|j| ƒƒrfq:t|| ƒ} } || ƒ} |j| iƒ}�x| �rŽ| j dƒ}||kr¶|| | j ƒƒ} qŽ|| ksÎ|j|dƒt krât d| ||| fƒ‚d}|rü|j|d|ƒ}|dk�r,|j|ƒ}|dk�r,|dk �r,||ƒ}|dk�rF|| | j ƒƒ} qŽt |tƒ�sZt|ƒ}| jd||ƒ} |j|dƒd||<|| | jƒƒ} qŽW| | k�rÒ|| ƒ�r°||| <d } t|ƒt|ƒk�rÊ|jƒ}| || <d | kr:|j| ƒq:W| s*Pq*W|S) a|Sort out tag definitions within other tags. Since v.0.9.2 supports embedded interpolation (see test cases for examples). so: becomes: a = 3 a = 3 b = _3 b = 3_3 Parameters ---------- inptags : dict Dictionary of tags(keys) and their values. Returns ------- dict Dictionary of tags(keys) and their values, with tags within the values recursively replaced. Ú getRawItemFrzpproperties contain self referencing definitions and cannot be resolved, fail tag: %s, found: %s in %s, value: %sNú?z<%s>rTú<)ÚTAG_CREÚsearchrBrÚkeysÚcallabler­r,Úgetr¥ÚendÚMAX_TAG_REPLACE_COUNTr†r"r+r!r¦Úidr—Úadd)ZinptagsZ conditionalÚignoreZaddreplZ tre_searchZtagsÚdoneZ noRecReplZ repCountsZrepFlagÚtagr‡ZorgvalrŠZrplcZrtagÚreplr r r ÚsubstituteRecursiveTagsŸsh          r½c Cs>y,tjdkr|jƒ}nt|ƒ}tjd|ƒWn YnXdS)z¨Helper to set real thread name (used for identification and diagnostic purposes). Side effect: name can be silently truncated to 15 bytes (16 bytes with NTS zero) réN)r)rÚ version_infor-r#Ú_libcapZprctl)r�r r r Úprctl_set_th_names  rÁcCsdS)Nr )r�r r r rÁscs<eZdZdZeƒZdZ‡fdd„Zdd„Zd dd „Z ‡Z S) Ú BgServicez{Background servicing Prevents memory leak on some platforms/python versions, using forced GC in periodical intervals. Ncs|jstt|ƒj|ƒ|_|jS)N)Ú _instanceÚsuperrÂÚ__new__)Úcls)Ú __class__r r rÅszBgService.__new__cCs2d|_d|_d|_|j|_ttdƒr.tjdƒdS)NiÿÿÿérKÚ set_thresholdri€)Ú_BgService__serviceTimeÚ_BgService__periodTimeÚ_BgService__thresholdÚ_BgService__countrÚgcrÉ)rIr r r r<$s  zBgService.__init__Fc Csˆ|jd8_| r0|jdks,tjƒ|jkr0dStjj|ƒs@dSz6tjƒ|jkrTdStjƒtjƒ|j |_|j |_dStjj ƒXdS)NrrFT) rÍrZtimerÊrÂÚ_mutexÚacquirerÎZcollectrËrÌÚrelease)rIÚforceÚwaitr r r Úservice/s  zBgService.service)FF) r8r^r_r`rrÏrÃrÅr<rÔÚ __classcell__r r )rÇr rÂs   rÂ)r rrr)r)r)NN)r‰TT)rr•)GÚ __author__Z __license__rÎZlocalercrrYrrTZ threadingrZ server.mytimerrZctypesZCDLLrÀÚgetpreferredencodingrÚ startswithÚstdoutrÚallr¿rr*r,rrr+r5r:rEÚobjectrFrdrarorrrvÚ NameErrorrwZLoggerZ_logrur|rmrnr}r~r€rˆrŒr�r”ÚevalÚcompileÚ__file__r›rœÚDOTALLrŸr£rªr©r¬r¶r°r½rÁrÂr r r r Ús–    "        .           b __pycache__/helpers.cpython-36.pyc000064400000034665152231502130013054 0ustar003 TB(dcGã@sÆdZdZddlZddlZddlZddlZddlZddlZddlZddl m Z ddl m Z ddl Z yddlZejdƒZWndZYnXejƒatjdƒrÜejrÆejjdk rÆejjjdƒ rÆejjaned d „dODƒƒrÜdaejdPk�rúdd„Zetƒ[ejdQk�rtdfdd„Zdd„Zn.tdfdd„Zejƒjƒdk�rHdd„ZneZdd„Zdd„Zdd„Z Gd d!„d!e!ƒZ"Gd"d#„d#ej#ƒZ$d$e_%dRd%d&„Z&ye'Z'Wne(k �r¸e)Z'YnXej*j+Z,d'd(„Z-e-ej*_+ej.j/Z0d)d*„Z1e1ej._/d+d,„Z2d-d.„Z3dSd1d2„Z4d3d4„Z5d5d6„Z6ejdTk�r2e7e8d8e9d9ƒƒnd:d;„Z:dej<ƒZ=ej8d?ej<ƒZ>ej8d@ej<ƒZ?dAdB„Z@dCdD„ZAdEZBej8dFƒZCdGfdfdHdI„ZDe�rªdJdK„ZEndLdK„ZEGdMdN„dNe!ƒZFdS)Uz0sr ÚLANGUAGEÚLC_ALLÚLC_CTYPEÚLANGzUTF-8éc Cs„tjƒjƒ}|dkr€|tjƒkr€t}t|dƒslyddlm}|ddƒ}Wn"tk rjtjtƒt}YnXt|dƒr€|j |ƒdS)NÚASCIIÚsetdefaultencodingr)Ú load_dynamicÚ_sysÚsys) rÚgetdefaultencodingÚupperÚ PREFER_ENCÚhasattrÚimprÚ ImportErrorÚreloadr)ÚencodingZoderZ__ldmr r r Ú__resetDefaultEncoding8s      rÚstrictc CsJyt|tƒr|j||ƒS|Sttfk rD|dkr8‚|j|dƒSXdS)Nr Úreplace)Ú isinstanceÚbytesÚdecodeÚUnicodeDecodeErrorÚUnicodeEncodeError)ÚxÚencÚerrorsr r r Ú uni_decodeYs  r*cCst|tƒst|ƒS|jtdƒS)Nr!)r"r#Ústrr$r)r'r r r Ú uni_stringbs r,c CsJyt|tƒr|j||ƒS|Sttfk rD|dkr8‚|j|dƒSXdS)Nr r!)r"r+Úencoder%r&)r'r(r)r r r r*gs  cCst|tƒst|ƒS|jtdƒS)Nr!)r"r+r-r)r'r r r r,qs cCst|tƒst|ƒS|jƒdkS)NÚ1ÚonÚtrueÚyes)r.r/r0r1)r"r+ÚboolÚlower)Úvalr r r Ú_as_boolysr5cCs"tjƒdd…\}}|jt|ƒfS)z+ Consistently format exception information Né)rÚexc_infoÚ__name__r,)ZclaÚexcr r r ÚformatExceptionInfo~sr:cCsRtjj|ƒ}|jdƒr"|dd…}|tddgƒkrNtjjtjj|ƒƒd|}|S)zaCustom function to include directory name if filename is too common Also strip .py at the end z.pyNrÚbaseÚ__init__Ú.éýÿÿÿ)rÚpathÚbasenameÚendswithÚsetÚdirname)Úsr;r r r Ú mbasenameŠs    rEc@s"eZdZdZddd„Zdd„ZdS) Ú TraceBackz7Customized traceback to be included in debug messages FcCsd|_||_dS)zžInitialize TrackBack metric Parameters ---------- compress : bool if True then prefix common with previous invocation gets replaced with ... rN)Ú_TraceBack__prevÚ_TraceBack__compress)ÚselfÚcompressr r r r<›s zTraceBack.__init__c Csðtjdd�dd…}dd„|Dƒ}dd„|Dƒ}|dg}xP|dd…D]@}|d|ddkr~|ddd |d7<qH|j|ƒqHWd jd d„|Dƒƒ}|jrì|}tjj|j|fƒ}t j d d |ƒ}|d kræd|t |ƒd…}||_|S)Néd)Úlimitr6cSs2g|]*}t|dƒtjj|dƒt|dƒg‘qS)rr)rErr?rCr+)rr'r r r ú ªsz&TraceBack.__call__..cSs6g|].}|ddkp |djdƒs|d|dg‘qS)rÚunittestúlogging.__init__rz /unittestr6)rNrO)rA)rÚer r r rM«s rrz,%sú>cSs$g|]}dt|dƒ|df‘qS)z%s:%srr)rE)rr'r r r rM¶sz>[^>]*$rz...éþÿÿÿéÿÿÿÿrS) Ú tracebackÚ extract_stackÚappendÚjoinrHrr?Ú commonprefixrGÚreÚsubÚlen) rIZftbZentriesZ entries_outÚentryZsftbZ prev_nextZ common_prefixZcommon_prefix2r r r Ú__call__§s$   zTraceBack.__call__N)F)r8Ú __module__Ú __qualname__Ú__doc__r<r]r r r r rF—s rFc@s eZdZdZdd„Zdd„ZdS)ÚFormatterWithTraceBackz}Custom formatter which expands %(tb) and %(tbc) with tracebacks TODO: might need locking in case of compressed tracebacks cOs4tjj|f|žd|i|—Žd|k}t|d�|_dS)NÚfmtz%(tbc)s)rJ)ÚloggingÚ Formatterr<rFÚ_tb)rIrbÚargsÚkwargsrJr r r r<ÊszFormatterWithTraceBack.__init__cCs|jƒ|_|_tjj||ƒS)N)reZtbcÚtbrcrdÚformat)rIÚrecordr r r riÏszFormatterWithTraceBack.formatN)r8r^r_r`r<rir r r r raÅsraFc Csh|rt|jƒr|j|jdƒ|r,dd„|_dd„tj_tjrdytj jƒWn YnXtj dƒdS)NrcSsdS)Nr r r r r ÚÙsz!__stopOnIOError..cSsdS)Nr )rIr r r rkÚs) r[ZhandlersZ removeHandlerÚclosercÚ StreamHandlerÚflushÚ exitOnIOErrorrÚstderrÚexit)ZlogSysZlogHndlrr r r Ú__stopOnIOErrorÕs  rrcKsØyt||||f|ŽWn¼ttfk rP}z|jdkr>t|ƒ‚WYdd}~Xn„tk rÒ}zhyTxNd|t|ƒffddd„|DƒfffD]&}yt||f|žŽWqˆYqˆXqˆWWn YnXWYdd}~XnXdS)a¦Safe log inject to avoid possible errors by unsafe log-handlers, concat, str. conversion, representation fails, etc. Used to intrude exception-safe _log-method instead of _log-method of Logger class to be always safe by logging and to get more-info about. See testSafeLogging test-case for more information. At least the errors covered in phase 3 seems to affected in all known pypy/python versions until now. é Nzlogging failed: %r on %sz args: %rcSsg|] }t|ƒ‘qSr )r,)rÚar r r rMÿsz__safeLog..)Ú __origLogÚBrokenPipeErrorÚIOErrorÚerrnorrÚ Exceptionr,)rIÚlevelÚmsgrfrgrPr r r Ú __safeLogés"  r|cCsNy t|ƒWn<ttfk rH}z|jdkr6td|ƒ‚WYdd}~XnXdS)zQSafe flush inject stopping endless logging on closed streams (redirected pipe). rsN)Ú__origLogFlushrvrwrxrr)rIrPr r r Ú__safeLogFlush s    r~cCs$d|krd|jdƒd}tj|ƒS)zBGet logging.Logger instance with Fail2Ban logger name convention r=z fail2ban.%srrS)Ú rpartitionrcÚ getLogger)Únamer r r r€sr€c CsTy.t|tƒs|jƒrt|ƒ}ntt|jƒƒ}Wn tk rNtd|ƒ‚YnX|S)NzInvalid log level %r)r"ÚintÚisdigitÚgetattrrcrÚAttributeErrorÚ ValueError)ÚvalueZllr r r Ú str2LogLevels rˆú %(message)sTcCsn|dkr@|dkrd|}|dkr*d|}qTd|}|rTd|}nd|}|rTd |}|sjtjd d d „|ƒ}|S) z(Custom log format for the verbose runs rrz6 | %(module)15.15s-%(levelno)-2d: %(funcName)-20.20s |r6zB +%(relativeCreated)5d %(thread)X %(name)-25.25s %(levelname)-5.5sz %(thread)X %(levelname)-5.5sz %(asctime)-15sz)%(name)-24s[%(process)d]: %(levelname)-7sz %(asctime)s z(?<=\))-?\d+(?:\.\d+)?scSsdS)NrDr )Úmr r r rk8sz$getVerbosityFormat..)rYrZ)Ú verbosityrbZaddtimeZpaddingr r r ÚgetVerbosityFormat&s  rŒcCs tdƒjddd�tj|||ƒS)z>Except hook used to log unhandled exceptions to Fail2Ban log Zfail2banz Unhandled exception in Fail2Ban:T)r7)r€ZcriticalrÚ__excepthook__)Úexctyper‡rTr r r Ú excepthook<s r�cCs(|sgStttdd„tjd|ƒDƒƒƒS)zŠHelper to split words on any comma, space, or a new line Returns empty list if input is empty (or None) and filters out empty entries cSsg|] }|jƒ‘qSr )Ústrip)rr r r r rMKszsplitwords..z[ , ]+)ÚlistÚfilterr2rYÚsplit)rDr r r Ú splitwordsCsr”ézêif 1: def _merge_dicts(x, y): """Helper to merge dicts. """ if y: return {**x, **y} return x def _merge_copy_dicts(x, y): """Helper to merge dicts to guarantee a copy result (r is never x). """ return {**x, **y} ÚexeccCs|}|r|jƒ}|j|ƒ|S)zHelper to merge dicts. )ÚcopyÚupdate)r'ÚyÚrr r r Ú _merge_dicts\s  r›cCs|jƒ}|r|j|ƒ|S)zCHelper to merge dicts to guarantee a copy result (r is never x). )r—r˜)r'r™ršr r r Ú_merge_copy_dictsds rœz^([^\[]+)(?:\[(.*)\])?\s*$zf\s*([\w\-_\.]+)=(?:"([^"]*)"|\'([^\']*)\'|([^,\]]*))(?:,|\]\s*\[|$|(?P.+))|,?\s*$|(?P.+)zs(?:[^\[\s]+(?:\s*\[\s*(?:[\w\-_\.]+=(?:"[^"]*"|\'[^\']*\'|[^,\]]*)\s*(?:,|\]\s*\[)?\s*)*\])?\s*|\S+)(?=\n\s*|\s+|$)cCsètj|ƒ}|stdƒ‚|jƒ\}}tƒ}|ràx²tj|ƒD]¤}|jdƒrrtd|jdƒ|jdƒ|jdƒdd…fƒ‚|jdƒr td|jdƒ|jdƒdd…fƒ‚|jdƒ}|s°q8d d „|jd d d ƒDƒd}|j ƒ||j ƒ<q8W||fS)Nzunexpected option syntaxZwrngAz+unexpected syntax at %d after option %r: %srréZwrngBz'expected option, wrong syntax at %d: %scSsg|]}|dk r|‘qS)Nr )rr4r r r rM�sz"extractOptions..r6ré) Ú OPTION_CREÚmatchr†ÚgroupsÚdictÚOPTION_EXTRACT_CREÚfinditerÚgroupÚstartr�)Úoptionr Z option_nameZoptstrZ option_optsZoptmatchÚoptr‡r r r ÚextractOptions|s&   (   r©cCs tj|ƒS)N)ÚOPTION_SPLIT_CREÚfindall)r§r r r ÚsplitWithOptions‘sr¬r�z <([^ <>]+)>rcCsötj}|}t|ƒ}tƒ}t|dƒ}i}�xÈd} �x¶|jƒD�]¨} | |ks:| |krRq:|rft|j| ƒƒrfq:t|| ƒ} } || ƒ} |j| iƒ}�x| �rŽ| j dƒ}||kr¶|| | j ƒƒ} qŽ|| ksÎ|j|dƒt krât d| ||| fƒ‚d}|rü|j|d|ƒ}|dk�r,|j|ƒ}|dk�r,|dk �r,||ƒ}|dk�rF|| | j ƒƒ} qŽt |tƒ�sZt|ƒ}| jd||ƒ} |j|dƒd||<|| | jƒƒ} qŽW| | k�rÒ|| ƒ�r°||| <d } t|ƒt|ƒk�rÊ|jƒ}| || <d | kr:|j| ƒq:W| s*Pq*W|S) a|Sort out tag definitions within other tags. Since v.0.9.2 supports embedded interpolation (see test cases for examples). so: becomes: a = 3 a = 3 b = _3 b = 3_3 Parameters ---------- inptags : dict Dictionary of tags(keys) and their values. Returns ------- dict Dictionary of tags(keys) and their values, with tags within the values recursively replaced. Ú getRawItemFrzpproperties contain self referencing definitions and cannot be resolved, fail tag: %s, found: %s in %s, value: %sNú?z<%s>rTú<)ÚTAG_CREÚsearchrBrÚkeysÚcallabler­r,Úgetr¥ÚendÚMAX_TAG_REPLACE_COUNTr†r"r+r!r¦Úidr—Úadd)ZinptagsZ conditionalÚignoreZaddreplZ tre_searchZtagsÚdoneZ noRecReplZ repCountsZrepFlagÚtagr‡ZorgvalrŠZrplcZrtagÚreplr r r ÚsubstituteRecursiveTagsŸsh          r½c Cs>y,tjdkr|jƒ}nt|ƒ}tjd|ƒWn YnXdS)z¨Helper to set real thread name (used for identification and diagnostic purposes). Side effect: name can be silently truncated to 15 bytes (16 bytes with NTS zero) réN)r)rÚ version_infor-r#Ú_libcapZprctl)r�r r r Úprctl_set_th_names  rÁcCsdS)Nr )r�r r r rÁscs<eZdZdZeƒZdZ‡fdd„Zdd„Zd dd „Z ‡Z S) Ú BgServicez{Background servicing Prevents memory leak on some platforms/python versions, using forced GC in periodical intervals. Ncs|jstt|ƒj|ƒ|_|jS)N)Ú _instanceÚsuperrÂÚ__new__)Úcls)Ú __class__r r rÅszBgService.__new__cCs2d|_d|_d|_|j|_ttdƒr.tjdƒdS)NiÿÿÿérKÚ set_thresholdri€)Ú_BgService__serviceTimeÚ_BgService__periodTimeÚ_BgService__thresholdÚ_BgService__countrÚgcrÉ)rIr r r r<$s  zBgService.__init__Fc Csˆ|jd8_| r0|jdks,tjƒ|jkr0dStjj|ƒs@dSz6tjƒ|jkrTdStjƒtjƒ|j |_|j |_dStjj ƒXdS)NrrFT) rÍrZtimerÊrÂÚ_mutexÚacquirerÎZcollectrËrÌÚrelease)rIÚforceÚwaitr r r Úservice/s  zBgService.service)FF) r8r^r_r`rrÏrÃrÅr<rÔÚ __classcell__r r )rÇr rÂs   rÂ)r rrr)r)r)NN)r‰TT)rr•)GÚ __author__Z __license__rÎZlocalercrrYrrTZ threadingrZ server.mytimerrZctypesZCDLLrÀÚgetpreferredencodingrÚ startswithÚstdoutrÚallr¿rr*r,rrr+r5r:rEÚobjectrFrdrarorrrvÚ NameErrorrwZLoggerZ_logrur|rmrnr}r~r€rˆrŒr�r”ÚevalÚcompileÚ__file__r›rœÚDOTALLrŸr£rªr©r¬r¶r°r½rÁrÂr r r r Ús–    "        .           b __pycache__/protocol.cpython-36.opt-1.pyc000064400000025654152231502130014210 0ustar003 TB(d^-ãj@sædZdZdZddlZdd„ZGdd„deƒZed d d d œƒZd dd gddgddgddgddgddgddgddgddgdd gd!d"gd#d$gd%d&gd'd(gd)d*gd+d,gd d-d gd.d/gd0d1gd2d3gd4d5gd6d7gd8d9gd:d;gd dgd?d@gdAdBgdCdDgdEdFgdGdHgd dId gdJdKgdLdMgdNdOgdPdQgd dRd gdSdTgdUdVgdWdXgdYdZgd[d\gd]d^gd_d`gdadbgdcddgdedfgdgdhgdidjgdkdlgdmdngdodpgdqdrgdsdtgdudvgdwdxgdydzgd{d|gd}d~gdd€gd�d‚gdƒd„gd…d†gd‡dˆgd d‰d gdŠd‹gdŒd�gdŽd�gd�d‘gd’d“gd”d•gd d–d gd—d˜gd™dšgd d›d gdœd�gdždŸgd d¡gd¢d£gd¤d¥gd¦d§gd¨d©gdªd«gd¬d­gd®d¯gd°d±gd²d³gd´dµgd¶d·gd¸d¹gdºd»gd¼d½gd¾d¿gdÀdÁgd dÂd gdÃdÄgdÅdÆgdÇdÈgdÉdÊgdËdÌgdÍdÎgd dÏd gdÐdÑgdÒdÓgdÔdÕggiZdÖdׄZ dØdÙ„Z dÚdÛ„Z dS)Üz Cyril Jaquierz Copyright (c) 2004 Cyril JaquierZGPLéNcCs t|ƒdS)zZDefault output handler for printing protocol. Used to ease mocking in the test cases. N)Úprint)Ús©rú/usr/lib/python3.6/protocol.pyÚoutputsrc@seZdZdd„ZdS)ÚdotdictcCs||S)Nr)ÚselfÚnamerrrÚ __getattr__'szdotdict.__getattr__N)Ú__name__Ú __module__Ú __qualname__r rrrrr&sróss)ZEMPTYZENDZCLOSEÚZBASICÚstartzstarts the server and the jailsZrestartzrestarts the serverz&restart [--unban] [--if-exists] zBrestarts the jail (alias for 'reload --restart ... ')z$reload [--restart] [--unban] [--all]zÄreloads the configuration without restarting of the server, the option '--restart' activates completely restarting of affected jails, thereby can unban IP addresses (if option '--unban' specified)z1reload [--restart] [--unban] [--if-exists] zIreloads the jail , or restarts it (if option '--restart' specified)Ústopz(stops all jails and terminate the serverz unban --allz3unbans all IP addresses (in all jails and database)zunban ... z'unbans (in all jails and database)Zbannedz*return jails with banned IPs as dictionaryzbanned ... ]z4return list(s) of jails where given IP(s) are bannedZstatusz%gets the current status of the serverZpingztests if the server is aliveZechoz;for internal usage, returns back and outputs a given stringÚhelpzreturn this outputÚversionzreturn the server versionZLOGGINGzset loglevel z’sets logging level to . Levels: CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG, TRACEDEBUG, HEAVYDEBUG or corresponding numeric value (50-5)z get loglevelzgets the logging levelzset logtarget zYsets logging target to . Can be STDOUT, STDERR, SYSLOG, SYSTEMD-JOURNAL or a filez get logtargetzgets logging targetzset syslogsocket auto|zQsets the syslog socket path to auto or . Only used if logtarget is SYSLOGzget syslogsocketzgets syslog socket pathZ flushlogszAflushes the logtarget if a file and reopens it. For log rotation.ZDATABASEzset dbfile zKset the location of fail2ban persistent datastore. Set to "None" to disablez get dbfilez1get the location of fail2ban persistent datastorezset dbmaxmatches zz?sets the max age in that history of bans will be keptzget dbpurgeagez=gets the max age in seconds that history of bans will be keptz JAIL CONTROLzadd zcreates using z start zstarts the jail z stop z*stops the jail . The jail is removedzstatus [FLAVOR]zHgets the current status of , with optional flavor or extended infozJAIL CONFIGURATIONzset idle on|offzsets the idle state of z set ignoreself true|falsez'allows the ignoring of own IP addresseszset addignoreip z&adds to the ignore list of zset delignoreip z+removes from the ignore list of z set ignorecommand zsets ignorecommand of zset ignorecache zsets ignorecache of z%set addlogpath ['tail']zmadds to the monitoring list of , optionally starting at the 'tail' of the file (default 'head').zset dellogpath z1removes from the monitoring list of z!set logencoding z/sets the of the log files for z"set addjournalmatch z,adds to the journal filter of z"set deljournalmatch z1removes from the journal filter of zset addfailregex zHadds the regular expression which must match failures for zset delfailregex z7removes the regular expression at for failregexz!set addignoreregex zTadds the regular expression which should match pattern to exclude for z!set delignoreregex z9removes the regular expression at for ignoreregexzset findtime configuration directoryz' -s, --socket socket pathz( -p, --pidfile pidfile pathzl --pname name of the process (main thread) to identify instance (default fail2ban-server)z) --loglevel logging levelz^ --logtarget logging target, use file-name or stdout, stderr, syslog or sysout.z --syslogsocket auto|z= -d dump configuration. For debuggingz[ --dp, --dump-pretty dump the configuration using more human readable representationz\ -t, --test test configuration (can be also specified with start parameters)z, -i interactive modez. -v increase verbosityz. -q decrease verbosityzN -x force execution of the server (remove socket file)z@ -b start server in background (default)z6 -f start server in foregroundzj --async start server in async mode (for internal usage only, don't read configuration)zn --timeout timeout to wait for the server (for internal usage only, don't read configuration)zG --str2sec convert time abbreviation format to secondsz5 -h, --help display this help messagezX -V, --version print the version (-V returns machine-readable short format)zCommand:z:Report bugs to https://github.com/fail2ban/fail2ban/issuesN)ÚosÚpathÚbasenamerrÚendswithrr)rZcallerrrrÚ dispUsage\sD" zFail2banCmdLine.dispUsagecCsð�xè|D�]Þ}|d}|d,kr.|d|jd<q|d-krF|d|jd<q|d.kr^|d|jd <q|d/kr~|d krrdnd|jd<q|d0kr˜d|_d|jd<q|dkr´|jdd7<q|dkrÐ|jdd8<q|dk�ræd|jd<q|dk�rüd|jd<q|dk�rd|jd<q|dk�r(d|jd<q|d k�r>d|jd!<q|d"k�rjdd#lm}|j|dƒ|jd$<q|d%k�r–dd#lm}t|j|dƒƒdS|d1k�r¬|jƒdS|d2k�rÈ|j|d(kƒdS|jd*ƒr|d|j|dd+…<qWd+S)3z! Gets the command line options rú-cú--confr!r#ú-sú--socketrú-pú --pidfilerú-dú--dpú --dump-prettyTrÚdumpú-tú--testÚtestz-vr&z-qz-xr$z-iÚ interactivez-br%z-fFz--asyncr"z --timeout)ÚMyTimer'z --str2secú-hú--helpú-Vú --versionz--N)r7r8)r9r:)r;r<)r=r>r?)rArB)rFrG)rHrI) r)rZ server.mytimerEZ str2secondsrr6r0Ú startswith)rÚoptListÚoptr.rErrrZ__getCmdLineOptions…sV                  z#Fail2banCmdLine.__getCmdLineOptionscCs"d}�y¼|jdk}||_tjd|dd…ƒyHd}dddddd d d d d dddddg}tj|jdd…||ƒ\}|_Wntjk r’|jƒdSX|j|ƒ}|dk rª|Stjd|j |jƒ|oÂt �rp|j d}|dkrætj t j ƒnT|dk�rþtj t jƒn<|dk�rtj t jƒn$|dk�r.tj t jƒn tj t jƒt jtjƒ}t|dƒ} t j| ƒ} |j| ƒtj|ƒd} xTtD]L} |j j| dƒdk�rz| �s¬|jjƒ|jjƒ} | | k�rz| | |j | <�qzWtjd|j dƒt|j dƒ} tjd|j dt j| ƒ|j dƒd}|j jd dƒ�rt|�r6|j ƒ\}}d}|dk �rX|j!||j d dkƒnt"d!ƒ|j jddƒ�st|S|j jddƒ�rÀ|�ršd}|j ƒ\}}|�s¨t#d"ƒ‚t$|jƒ�sÀt"d#ƒ|SdSt#k �rÚ‚YnDt%k �r}z&t"d$|fƒ|dk�rtj&|ƒdSd}~XnXdS)%Nr!zUsing start params %szhc:s:p:xfbdtviqVz loglevel=z logtarget=z syslogsocket=rCr"zconf=zpidfile=zpname=zsocket=ztimeout=zstr2sec=ÚhelprZdpz dump-prettyFz conf: %r, args: %rr&rrézUsing socket file %srrz%Using pid file %s, [%s] logging to %srrTr@zEERROR: The configuration stream failed because of the invalid syntax.z ERROR: test configuration failedz$OK: configuration test is successfulz ERROR: %s)'rÚlogSysÚinfoÚgetoptrZ GetoptErrorr6Ú#_Fail2banCmdLine__getCmdLineOptionsÚdebugr)Ú PRODUCTIONZsetLevelÚloggingZERRORZWARNINGÚINFOÚDEBUGZ HEAVYDEBUGZ StreamHandlerÚsysÚstderrrZ FormatterZ setFormatterZ addHandlerÚ CONFIG_PARAMSÚgetr+Z readEarlyZgetEarlyOptionsrZ getLevelNameÚ readConfigÚ dumpConfigrÚServerExecutionExceptionÚlenÚ ExceptionZ exception)rÚargvr&ÚinitialZcmdOptsZ cmdLongOptsrKÚretZlogoutZfmtZ formatterr#r.ZllevZreadcfgÚstreamrrrrÚ initCmdLine´s’  "                      zFail2banCmdLine.initCmdLineNcCs”d}yR|jjƒ|jjƒ|jj||j|j d�}|jj|jjddƒd�|jjƒ}Wn4t k rŠ}zt j d|ƒd}WYdd}~XnX||fS)N)Z ignoreWrongr@F)Zallow_no_fileszFailed during configuration: %s) r+ZReloadZreadAllZ getOptionsr)rZconvertToProtocolr[ZgetConfigStreamr`rOÚerror)rZjailrdrcrrrrr\s    zFail2banCmdLine.readConfigcs<|rddlm‰‡fdd„}nt}x|D] }||ƒq(WdS)Nr)Úpformatcstˆ|ddd�ƒdS)Nièr)ÚwidthÚindent)r)r)rgrrÚ_output.sz+Fail2banCmdLine.dumpConfig.._outputT)Úpprintrgr)ÚcmdZprettyrjÚcr)rgrr]*s   zFail2banCmdLine.dumpConfigrcCsvtjjƒy4tjjƒttdƒr2tjr2tj|ƒn tj|ƒWn2t t fk rp}z|j dkr`‚WYdd}~XnXdS)NÚexitr ) rXrYÚcloseÚstdoutÚflushÚhasattrrnr2Ú_exitr r r )Úcoderrrrrs9s    zFail2banCmdLine._exitcCs"tjd|ƒtjƒtj|ƒdS)NzExit with code %s)rOrSrUZshutdownrrs)rtrrrrnHs zFail2banCmdLine.exit)F)N)F)r)r)Ú__name__Ú __module__Ú __qualname__r rÚpropertyr+r/r0r6rRrer\Ú staticmethodr]rsrnrrrrr2s  )/d   rc@s eZdZdS)Ú ExitExceptionN)rurvrwrrrrrzUsrzc@s eZdZdS)r^N)rurvrwrrrrr^Ysr^)rrrrr)Ú __author__Z __copyright__Z __license__rQrUr2rXrrZprotocolrZhelpersrrrr rOrrZrTr(rrnr`rzr^rrrrÚs( "client/__pycache__/fail2bancmdline.cpython-36.pyc000064400000022455152231502140015675 0ustar003 TB(d!,ã@s´dZdZdZddlZddlZddlZddlZddlmZmZddl m Z ddl m Z m Z mZmZe d ƒZd d „ZdZdZdZGdd„dƒZejZGdd„deƒZGdd„deƒZdS)zFail2Ban Developersz^Copyright (c) 2004-2008 Cyril Jaquier, 2012-2014 Yaroslav Halchenko, 2014-2016 Serg G. BresterZGPLéNé)ÚversionÚ normVersion)ÚprintFormatted)Ú getLoggerÚ str2LogLevelÚgetVerbosityFormatÚBrokenPipeErrorZfail2bancCsDy t|ƒWn2ttfk r>}z|jdkr.‚WYdd}~XnXdS)Né )Úprintr ÚIOErrorÚerrno)ÚsÚe©rú%/usr/lib/python3.6/fail2bancmdline.pyÚoutput#s   rÚsocketÚpidfileÚ logtargetÚloglevelÚ syslogsocketTéc@s†eZdZdd„Zdd„Zedd„ƒZdd„Zdd d „Zd d „Z dd„Z dd„Z ddd„Z e ddd„ƒZe ddd„ƒZe d dd„ƒZdS)!ÚFail2banCmdLinecCs$d|_|_d|_d|_|jƒdS)NF)Ú_argvÚ_argsÚ _configuratorÚ cleanConfOnlyÚ resetConf)ÚselfrrrÚ__init__4s zFail2banCmdLine.__init__c Csdddddddtdœ|_dS)NFz /etc/fail2banTé)ÚasyncÚconfÚforceÚ backgroundÚverboserrÚtimeout)Ú MAX_WAITTIMEÚ_conf)rrrrr:szFail2banCmdLine.resetConfcCs8|jr |jSddlm}|ƒ|_|jj|jdƒ|jS)Nr!)Ú Configuratorr#)rÚ configuratorr*Z setBaseDirr))rr*rrrr+Fs  zFail2banCmdLine.configuratorcCs$x|jD]}|j||j|<qWdS)N)Ú__dict__)rÚobjÚorrrÚ applyMembersRs zFail2banCmdLine.applyMembersFcCs |stdtƒn ttƒƒdS)Nz Fail2Ban v)rrr)rZshortrrrÚ dispVersionVszFail2banCmdLine.dispVersioncCsBtjj|jdƒ}td|d|jdƒs,dndƒtdƒtdtdƒtd ƒtdƒtd ƒtd ƒtd ƒtd ƒtdƒtdƒtdƒtdƒtdƒtdƒtdƒtdƒtdƒtdƒtdƒtdƒtdƒtdƒtdƒtdƒtdƒtdƒ|jdƒ�s.tdƒtd ƒtƒtdƒtd!ƒd"S)#z2 Prints Fail2Ban command line options and exits rzUsage: z [OPTIONS]Zserverz Úz Fail2Ban vz5 reads log file that contains password failure reportz=and bans the corresponding IP addresses using firewall rules.zOptions:z3 -c, --conf configuration directoryz' -s, --socket socket pathz( -p, --pidfile pidfile pathzl --pname name of the process (main thread) to identify instance (default fail2ban-server)z) --loglevel logging levelz^ --logtarget logging target, use file-name or stdout, stderr, syslog or sysout.z --syslogsocket auto|z= -d dump configuration. For debuggingz[ --dp, --dump-pretty dump the configuration using more human readable representationz\ -t, --test test configuration (can be also specified with start parameters)z, -i interactive modez. -v increase verbosityz. -q decrease verbosityzN -x force execution of the server (remove socket file)z@ -b start server in background (default)z6 -f start server in foregroundzj --async start server in async mode (for internal usage only, don't read configuration)zn --timeout timeout to wait for the server (for internal usage only, don't read configuration)zG --str2sec convert time abbreviation format to secondsz5 -h, --help display this help messagezX -V, --version print the version (-V returns machine-readable short format)zCommand:z:Report bugs to https://github.com/fail2ban/fail2ban/issuesN)ÚosÚpathÚbasenamerrÚendswithrr)rZcallerrrrÚ dispUsage\sD" zFail2banCmdLine.dispUsagecCsð�xè|D�]Þ}|d}|d,kr.|d|jd<q|d-krF|d|jd<q|d.kr^|d|jd <q|d/kr~|d krrdnd|jd<q|d0kr˜d|_d|jd<q|dkr´|jdd7<q|dkrÐ|jdd8<q|dk�ræd|jd<q|dk�rüd|jd<q|dk�rd|jd<q|dk�r(d|jd<q|d k�r>d|jd!<q|d"k�rjdd#lm}|j|dƒ|jd$<q|d%k�r–dd#lm}t|j|dƒƒdS|d1k�r¬|jƒdS|d2k�rÈ|j|d(kƒdS|jd*ƒr|d|j|dd+…<qWd+S)3z! Gets the command line options rú-cú--confr!r#ú-sú--socketrú-pú --pidfilerú-dú--dpú --dump-prettyTrÚdumpú-tú--testÚtestz-vr&z-qz-xr$z-iÚ interactivez-br%z-fFz--asyncr"z --timeout)ÚMyTimer'z --str2secú-hú--helpú-Vú --versionz--N)r7r8)r9r:)r;r<)r=r>r?)rArB)rFrG)rHrI) r)rZ server.mytimerEZ str2secondsrr6r0Ú startswith)rÚoptListÚoptr.rErrrZ__getCmdLineOptions…sV                  z#Fail2banCmdLine.__getCmdLineOptionscCs"d}�y¼|jdk}||_tjd|dd…ƒyHd}dddddd d d d d dddddg}tj|jdd…||ƒ\}|_Wntjk r’|jƒdSX|j|ƒ}|dk rª|Stjd|j |jƒ|oÂt �rp|j d}|dkrætj t j ƒnT|dk�rþtj t jƒn<|dk�rtj t jƒn$|dk�r.tj t jƒn tj t jƒt jtjƒ}t|dƒ} t j| ƒ} |j| ƒtj|ƒd} xTtD]L} |j j| dƒdk�rz| �s¬|jjƒ|jjƒ} | | k�rz| | |j | <�qzWtjd|j dƒt|j dƒ} tjd|j dt j| ƒ|j dƒd}|j jd dƒ�rt|�r6|j ƒ\}}d}|dk �rX|j!||j d dkƒnt"d!ƒ|j jddƒ�st|S|j jddƒ�rÀ|�ršd}|j ƒ\}}|�s¨t#d"ƒ‚t$|jƒ�sÀt"d#ƒ|SdSt#k �rÚ‚YnDt%k �r}z&t"d$|fƒ|dk�rtj&|ƒdSd}~XnXdS)%Nr!zUsing start params %szhc:s:p:xfbdtviqVz loglevel=z logtarget=z syslogsocket=rCr"zconf=zpidfile=zpname=zsocket=ztimeout=zstr2sec=ÚhelprZdpz dump-prettyFz conf: %r, args: %rr&rrézUsing socket file %srrz%Using pid file %s, [%s] logging to %srrTr@zEERROR: The configuration stream failed because of the invalid syntax.z ERROR: test configuration failedz$OK: configuration test is successfulz ERROR: %s)'rÚlogSysÚinfoÚgetoptrZ GetoptErrorr6Ú#_Fail2banCmdLine__getCmdLineOptionsÚdebugr)Ú PRODUCTIONZsetLevelÚloggingZERRORZWARNINGÚINFOÚDEBUGZ HEAVYDEBUGZ StreamHandlerÚsysÚstderrrZ FormatterZ setFormatterZ addHandlerÚ CONFIG_PARAMSÚgetr+Z readEarlyZgetEarlyOptionsrZ getLevelNameÚ readConfigÚ dumpConfigrÚServerExecutionExceptionÚlenÚ ExceptionZ exception)rÚargvr&ÚinitialZcmdOptsZ cmdLongOptsrKÚretZlogoutZfmtZ formatterr#r.ZllevZreadcfgÚstreamrrrrÚ initCmdLine´s’  "                      zFail2banCmdLine.initCmdLineNcCs”d}yR|jjƒ|jjƒ|jj||j|j d�}|jj|jjddƒd�|jjƒ}Wn4t k rŠ}zt j d|ƒd}WYdd}~XnX||fS)N)Z ignoreWrongr@F)Zallow_no_fileszFailed during configuration: %s) r+ZReloadZreadAllZ getOptionsr)rZconvertToProtocolr[ZgetConfigStreamr`rOÚerror)rZjailrdrcrrrrr\s    zFail2banCmdLine.readConfigcs<|rddlm‰‡fdd„}nt}x|D] }||ƒq(WdS)Nr)Úpformatcstˆ|ddd�ƒdS)Nièr)ÚwidthÚindent)r)r)rgrrÚ_output.sz+Fail2banCmdLine.dumpConfig.._outputT)Úpprintrgr)ÚcmdZprettyrjÚcr)rgrr]*s   zFail2banCmdLine.dumpConfigrcCsvtjjƒy4tjjƒttdƒr2tjr2tj|ƒn tj|ƒWn2t t fk rp}z|j dkr`‚WYdd}~XnXdS)NÚexitr ) rXrYÚcloseÚstdoutÚflushÚhasattrrnr2Ú_exitr r r )Úcoderrrrrs9s    zFail2banCmdLine._exitcCs"tjd|ƒtjƒtj|ƒdS)NzExit with code %s)rOrSrUZshutdownrrs)rtrrrrnHs zFail2banCmdLine.exit)F)N)F)r)r)Ú__name__Ú __module__Ú __qualname__r rÚpropertyr+r/r0r6rRrer\Ú staticmethodr]rsrnrrrrr2s  )/d   rc@s eZdZdS)Ú ExitExceptionN)rurvrwrrrrrzUsrzc@s eZdZdS)r^N)rurvrwrrrrr^Ysr^)rrrrr)Ú __author__Z __copyright__Z __license__rQrUr2rXrrZprotocolrZhelpersrrrr rOrrZrTr(rrnr`rzr^rrrrÚs( "client/__pycache__/fail2banreader.cpython-36.opt-1.pyc000064400000004353152231502140016460 0ustar003 TB(ds ã@sDdZdZdZddlmZddlmZmZeeƒZ Gdd„deƒZ d S) z Cyril Jaquierz Copyright (c) 2004 Cyril JaquierZGPLé)Ú ConfigReaderé)Ú getLoggerÚ str2LogLevelc@s6eZdZdd„Zdd„Zdd„Zd dd „Zd d „ZdS) ÚFail2banReadercKstj|f|ŽdS)N)rÚ__init__)ÚselfÚkwargs©r ú$/usr/lib/python3.6/fail2banreader.pyr$szFail2banReader.__init__cCstj|dƒdS)NZfail2ban)rÚread)rr r r r 'szFail2banReader.readcCs:dddgdddgdddgddd gdd d gg}tj|d |ƒS) NÚstringZsocketz/var/run/fail2ban/fail2ban.sockZpidfilez/var/run/fail2ban/fail2ban.pidÚloglevelÚINFOÚ logtargetz/var/log/fail2ban.logÚ syslogsocketÚautoÚ Definition)rÚ getOptions)rÚoptsr r r ÚgetEarlyOptions*s  zFail2banReader.getEarlyOptionsNc Cs¢dddgdddgdddgdddgdd d gd d dgdd dgg}tj|d|ƒ|_|r\|jj|ƒt|jjddƒƒd dgg}|jdƒržtj|d|ƒ}|rž||jd<dS)Nr rrrZSTDERRrrÚ allowipv6Údbfilez"/var/lib/fail2ban/fail2ban.sqlite3ÚintÚ dbmaxmatchesÚ dbpurgeageZ1dréZ stacksizeZThreadÚthread)rrÚ_Fail2banReader__optsÚupdaterÚgetZ has_section)rZ updateMainOptrZthoptr r r r4s     zFail2banReader.getOptionsc Csddddddddddœ}tƒ}x4|jD]*}||kr$|j||d ||j|gfƒq$Wd d „t|ƒDƒS) Nré é é éé2é3)rrrrrrrrÚsetcSsg|] }|d‘qS)rr )Ú.0Úoptr r r ú Tsz*Fail2banReader.convert..)ÚlistrÚappendÚsorted)rÚorderÚstreamr)r r r ÚconvertIs  "zFail2banReader.convert)N)Ú__name__Ú __module__Ú __qualname__rr rrr0r r r r r"s  rN) Ú __author__Z __copyright__Z __license__Z configreaderrZhelpersrrr1ZlogSysrr r r r Ús  client/__pycache__/fail2banreader.cpython-36.pyc000064400000004353152231502140015521 0ustar003 TB(ds ã@sDdZdZdZddlmZddlmZmZeeƒZ Gdd„deƒZ d S) z Cyril Jaquierz Copyright (c) 2004 Cyril JaquierZGPLé)Ú ConfigReaderé)Ú getLoggerÚ str2LogLevelc@s6eZdZdd„Zdd„Zdd„Zd dd „Zd d „ZdS) ÚFail2banReadercKstj|f|ŽdS)N)rÚ__init__)ÚselfÚkwargs©r ú$/usr/lib/python3.6/fail2banreader.pyr$szFail2banReader.__init__cCstj|dƒdS)NZfail2ban)rÚread)rr r r r 'szFail2banReader.readcCs:dddgdddgdddgddd gdd d gg}tj|d |ƒS) NÚstringZsocketz/var/run/fail2ban/fail2ban.sockZpidfilez/var/run/fail2ban/fail2ban.pidÚloglevelÚINFOÚ logtargetz/var/log/fail2ban.logÚ syslogsocketÚautoÚ Definition)rÚ getOptions)rÚoptsr r r ÚgetEarlyOptions*s  zFail2banReader.getEarlyOptionsNc Cs¢dddgdddgdddgdddgdd d gd d dgdd dgg}tj|d|ƒ|_|r\|jj|ƒt|jjddƒƒd dgg}|jdƒržtj|d|ƒ}|rž||jd<dS)Nr rrrZSTDERRrrÚ allowipv6Údbfilez"/var/lib/fail2ban/fail2ban.sqlite3ÚintÚ dbmaxmatchesÚ dbpurgeageZ1dréZ stacksizeZThreadÚthread)rrÚ_Fail2banReader__optsÚupdaterÚgetZ has_section)rZ updateMainOptrZthoptr r r r4s     zFail2banReader.getOptionsc Csddddddddddœ}tƒ}x4|jD]*}||kr$|j||d ||j|gfƒq$Wd d „t|ƒDƒS) Nré é é éé2é3)rrrrrrrrÚsetcSsg|] }|d‘qS)rr )Ú.0Úoptr r r ú Tsz*Fail2banReader.convert..)ÚlistrÚappendÚsorted)rÚorderÚstreamr)r r r ÚconvertIs  "zFail2banReader.convert)N)Ú__name__Ú __module__Ú __qualname__rr rrr0r r r r r"s  rN) Ú __author__Z __copyright__Z __license__Z configreaderrZhelpersrrr1ZlogSysrr r r r Ús  client/__pycache__/fail2banregex.cpython-36.opt-1.pyc000064400000061252152231502140016331 0ustar003 TB(d†pã @s¨dZdZdZdZddlZddlZddlZddlZddlZddl Z ddl Z ddl Z ddl Z ddlZ ddlmZmZddlmZmZmZydd lmZWnek r¬dZYnXdd lmZmZd d lmZdd lmZmZm Z ddl!m"Z"m#Z#ddl$m%Z%m&Z&m'Z'm(Z(m)Z)m*Z*e(dƒZ+d.dd„Z,dd„Z-d/dd„Z.d0dd„Z/dd„Z0dd„Z1d d!„Z2Gd"d#„d#eƒZ3d$d%„Z4Gd&d'„d'e5ƒZ6Gd(d)„d)e5ƒZ7Gd*d+„d+e5ƒZ8d,d-„Z9dS)1z· Fail2Ban reads log file that contains password failure report and bans the corresponding IP addresses using firewall rules. This tools can test regular expressions for "fail2ban". zFail2Ban DevelopersaICopyright (c) 2004-2008 Cyril Jaquier, 2008- Fail2Ban Contributors Copyright of modifications held by their respective authors. Licensed under the GNU General Public License v2 (GPL). Written by Cyril Jaquier . Many contributions by Yaroslav O. Halchenko, Steven Hiscocks, Sergey G. Brester (sebres).ZGPLéN)Ú OptionParserÚOption)Ú NoOptionErrorÚNoSectionErrorÚMissingSectionHeaderErroré)Ú FilterSystemd)ÚversionÚ normVersioné)Ú FilterReader)ÚFilterÚ FileContainerÚMyTime)ÚRegexÚRegexException)Ú str2LogLevelÚgetVerbosityFormatÚFormatterWithTraceBackÚ getLoggerÚextractOptionsÚ PREFER_ENCZfail2banFÚyescCs2tj||d�|ddœ}|r"d|d<dtjj|ƒS)N)ÚuseDnsÚpython)ÚreÚstrZflavorÚmÚflagszhttps://www.debuggex.com/?)rZ_resolveHostTagÚurllibÚparseZ urlencode)ZsampleÚregexÚ multilinerÚargs©r$ú#/usr/lib/python3.6/fail2banregex.pyÚ debuggexURL?s  r&cCs t|ƒdS)N)Úprint)r#r$r$r%ÚoutputHsr(é5cCs$t|ƒ|kr |d|d…dS|S)zReturn shortened string Néz...)Úlen)ÚsÚlr$r$r%ÚshortstrKs r.cCs<t|ƒs dS|rd|}nd}t|ddj|ƒdƒdS)Nz|- %s Úz| z | z `-)r+r(Újoin)r-Úheaderr,r$r$r%Ú pprint_listRs  r2c cs@x:y |jƒ}Wntk r$wYnX|s,P|j|ƒVqWdS)N)Zget_nextÚOSErrorZformatJournalEntry)ÚfltÚ myjournalÚentryr$r$r%Újournal_lines_gen[s r7cGsttƒƒtjdƒdS)Nr)r(r ÚsysÚexit)r#r$r$r%ÚdumpNormVersiones r:cCsdtjdS)Nz(%s [OPTIONS] [IGNOREREGEX]r)r8Úargvr$r$r$r%Úisr<c@seZdZdd„ZdS)Ú _f2bOptParsercOs:d|_dtƒdtdtj|f|ž|ŽdtdS)z, Overwritten format helper with full ussage.r/zUsage: Ú aÚ LOG: string a string representing a log line filename path to a log file (/var/log/auth.log) systemd-journal search systemd journal (systemd-python required), optionally with backend parameters, see `man jail.conf` for usage and examples (systemd-journal[journalflags=1]). REGEX: string a string representing a 'failregex' filter name of filter, optionally with options (sshd[mode=aggressive]) filename path to a filter file (filter.d/sshd.conf) IGNOREREGEX: string a string representing an 'ignoreregex' filename path to a filter file (filter.d/sshd.conf) z> Report bugs to https://github.com/fail2ban/fail2ban/issues )ÚusageÚ__doc__rÚ format_helpÚ __copyright__)Úselfr#Úkwargsr$r$r%rAlsz_f2bOptParser.format_helpN)Ú__name__Ú __module__Ú __qualname__rAr$r$r$r%r=ksr=cCsfttƒdtd�}|jtddddd�tdd d d �td d dddd�tddtdd�tdddddd�tddddd�tddtddd�td d!d"d �td#d$d%d&d'd(�td)d*td+d,�td-d.d/d0dd1d2�td3dd0td4d5�td6d7dd8d9�td:d;ddd?d@dAd2�tdBdCddDddEd2�tdFddGd9�tdHddId9�tdJddKd9�tdLddMd9�tdNddOd9�tdPdQddRd9�tdSddTd9�gƒ|S)UNz%prog )r?r z-cz--configz /etc/fail2banzset alternate config directory)ÚdefaultÚhelpz-dz --datepatternz+set custom pattern used to match date/times)rIz --timezonez--TZZstorez)set time-zone used by convert time format)ÚactionrHrIz-ez --encodingz%File encoding. Default: system localez-rz--rawÚ store_trueFzRaw hosts, don't resolve dnsz--usednszpDNS specified replacement of tags in regexp ('yes' - matches all form of hosts, 'no' - IP addresses only)z-Lz --maxlinesrzmaxlines for multi-line regex.)ÚtyperHrIz-mz--journalmatchzGjournalctl style matches overriding filter file. "systemd-journal" onlyz-lz --log-levelÚ log_levelÚcriticalz(Log level for the Fail2Ban logger to use)ÚdestrHrIz-VÚcallbackz,get version in machine-readable short format)rJrPrIz-vz --verboseÚcountÚverbosezIncrease verbosity)rJrOrHrIz --verbosityz'Set numerical level of verbosity (0..4))rJrOrLrIz--verbose-datez--VDz%Verbose date patterns/regex in output)rJrIz-Dz --debuggexz-Produce debuggex.com urls for debugging therez--no-check-allZ store_falseÚ checkAllRegexTzDisable check for all regex'sz-oz--outÚoutzaSet token to print failure information only (row, id, ip, msg, host, ip4, ip6, dns, matches, ...)z--print-no-missedzDo not print any missed linesz--print-no-ignoredzDo not print any ignored linesz--print-all-matchedzPrint all matched linesz--print-all-missedz*Print all missed lines, no matter how manyz--print-all-ignoredz+Print all ignored lines, no matter how manyz-tz--log-tracebackz.Enrich log-messages with compressed tracebacksz--full-tracebackzBEither to make the tracebacks full, not compressed (as by default))r=r?r Z add_optionsrrÚintr:)Úpr$r$r%Úget_opt_parser„sl         rWc@sDeZdZdd„Zdd„Zdd„Zdd„Zd d „Zd d „Zd d„Z dS)Ú RegexStatcCsd|_||_tƒ|_dS)Nr)Ú_statsÚ _failregexÚlistÚ_ipList)rCÚ failregexr$r$r%Ú__init__ÅszRegexStat.__init__cCsd|j|j|j|jfS)Nz%s(%r) %d failed: %s)Ú __class__rZrYr\)rCr$r$r%Ú__str__ÊszRegexStat.__str__cCs|jd7_dS)Nr )rY)rCr$r$r%ÚincÎsz RegexStat.inccCs|jS)N)rY)rCr$r$r%ÚgetStatsÑszRegexStat.getStatscCs|jS)N)rZ)rCr$r$r%Ú getFailRegexÔszRegexStat.getFailRegexcCs|jj|ƒdS)N)r\Úappend)rCÚvaluer$r$r%ÚappendIP×szRegexStat.appendIPcCs|jS)N)r\)rCr$r$r%Ú getIPListÚszRegexStat.getIPListN) rErFrGr^r`rarbrcrfrgr$r$r$r%rXÃsrXc@s(eZdZdZdd„Zdd„Zdd„ZdS) Ú LineStatsz(Just a convenience container for stats cCsFd|_|_g|_d|_g|_d|_g|_|jrBg|_g|_ g|_ dS)Nr) ÚtestedÚmatchedÚ matched_linesÚmissedÚ missed_linesÚignoredÚ ignored_linesZdebuggexÚmatched_lines_timeextractedÚmissed_lines_timeextractedÚignored_lines_timeextracted)rCÚoptsr$r$r%r^ás zLineStats.__init__cCsd|S)NzM%(tested)d lines, %(ignored)d ignored, %(matched)d matched, %(missed)d missedr$)rCr$r$r%r`íszLineStats.__str__cCst||ƒrt||ƒSdS)Nr/)ÚhasattrÚgetattr)rCÚkeyr$r$r%Ú __getitem__ñszLineStats.__getitem__N)rErFrGr@r^r`rwr$r$r$r%rhÞs rhc@s†eZdZdd„Zdd„Zdd„Zdd„Zd d „Zd d „Zd d„Z dd„Z dd„Z d dd„Z dd„Z dd„Zdd„Zdd„Zdd„ZdS)!Ú Fail2banRegexcCsN|jjtdd„|jjƒDƒƒƒ||_d|_d|_d|_tƒ|_t dƒ|_ d|_ t ƒ|_ t ƒ|_t ƒ|_d|_t|ƒ|_|jrŒ|j|jƒnd|_|jdk r®|jtj|jƒƒ|jrÂ|j j|jƒd|j _tjdƒddlm }|ƒ|j!rø|j"|j!ƒ|j#�r|j j$|j#ƒ|j%|j _&|j'�o&|j( |j _'t)|j(ƒ|j _*|j+|j _,d|_-dS) Ncss|]\}}d||fVqdS)Ú_Nr$)Ú.0ÚoÚvr$r$r%ú ùsz)Fail2banRegex.__init__..Frér)Ú _updateTimeREÚauto).Ú__dict__ÚupdateÚdictÚitemsÚ_optsÚ _maxlines_setÚ_datepattern_setÚ _journalmatchÚ share_configr Ú_filterÚ_prefREMatchedr[Ú _prefREGroupsÚ _ignoreregexrZÚ _time_elapsedrhÚ _line_statsÚmaxlinesÚ setMaxLinesÚ _maxlinesÚ journalmatchÚsetJournalMatchÚshlexÚsplitZtimezoneZsetLogTimeZoneZ checkFindTimerZsetAlternateNowZserver.strptimerÚ datepatternÚsetDatePatternÚusednsZ setUseDnsÚrawZ returnRawHostrSrTÚboolZ ignorePendingÚ_onIgnoreRegexZ onIgnoreRegexÚ_backend)rCrsrr$r$r%r^÷sB         zFail2banRegex.__init__cCs|jjst|ƒdS)N)r…rTr()rCÚliner$r$r%r(!szFail2banRegex.outputcCs|j|jdƒS)NÚignore)ÚencodeÚ _encoding)rCržr$r$r%Ú encode_line$szFail2banRegex.encode_linecCs@|js<|jj|ƒd|_|dk r<|jd||jjƒdfƒdS)NTzUse datepattern : %s : %sr )r‡rŠr˜r(ZgetDatePattern)rCÚpatternr$r$r%r˜'s  zFail2banRegex.setDatePatterncCs4|js0|jjt|ƒƒd|_|jd|jjƒƒdS)NTzUse maxlines : %d)r†rŠr‘rUr(Ú getMaxLines)rCr|r$r$r%r‘/szFail2banRegex.setMaxLinescCs ||_dS)N)rˆ)rCr|r$r$r%r”5szFail2banRegex.setJournalMatchc Csvi}|jƒ}xVddgt|jƒƒD]>}y$||kr8||n |jd|ƒ||<Wq"tk r^Yq"Xq"W|jd|ƒdS)NZlogtyper—Z DefinitionzReal filter options : %r)Z getCombinedr[ÚkeysÚgetrr()rCÚreaderÚfltOptZrealoptsZcomboptsÚkr$r$r%Ú_dumpRealOptions8s$ zFail2banRegex._dumpRealOptionsc/Cs0|d}|jj}|}d}i}|dk�r$tjd|ƒ�r$y t|ƒ\}}d|d&d…krZ|f}n||d|df}xd|D]\}d|kr°tjj|ƒd kržtjj||ƒ}q¼tjj|d |ƒ}n tjj |ƒ}tjj |ƒrÊPd}qrWWnNt k �r"} z0t d t | ƒfƒt d |fƒ|j�r| ‚d Sd} ~ XnX|dk �r¬||jjk�sjtjj|ƒd k�sjd|d'd…k�r¶d|k�r¶tjj|ƒd k�rˆtjj |ƒ}tjjtjj|ƒƒd }|j d|||fƒn0|j d||fƒd}tjj|ƒ�sætjj|ƒ}|�rú|j d|ƒt|d||j|d�} d} y*|dk �r(| jƒ} n| jdƒ| jƒ} WnBt k �r~} z$t dt | ƒfƒ|j�rn| ‚WYdd} ~ XnX| �s–t d|ƒd S| j|jƒ| jdƒ|jdk�sÈtjƒtjk�rÔ|j| |ƒ| jƒ} i} �xì| D�]¼}|d dk�r|d}n |d dk�rè|dd…}n�qè�y@|ddk�rTx|D]}||j _!�q>W�n|ddk�r | j"dƒ}|�s€t#ƒ}| d<xä|D]}|j$t%|ƒƒ�q†WnÆ|ddk�rì| j"dƒ}|�sÌt#ƒ}| d<x˜|D]}|j$t%|ƒƒ�qÒWnz|ddk�rxj|D]}|j&|ƒ�qWnP|ddk�r@x@|D]}|j'|ƒ�q*Wn&|dd k�rf|jj(dk�rf|j)|ƒWn:t*k �r¢} zt d!|d||| fƒd Sd} ~ XnX�qèWn$|j d"|t+|ƒfƒ|t%|ƒgi} xZ| j,ƒD]N\}} |d}t-|d#|| ƒx*| D]"}t.|j d$|j/ƒƒ|j0ƒƒ�qW�qÚWd%S)(Nr!Úfailz"(?ms)^/{0,3}[\w/_\-.]+(?:\[.*\])?$Ú.éz.confz.localú/zfilter.dz'ERROR: Wrong filter name or options: %sz while parsing: %sFrz&Use %11s filter file : %s, basedir: %szUse %11s file : %szUse filter options : %rzfail2ban-regex-jail)r‰ÚbasedirzWrong config file: %szERROR: failed to load filter %sr z multi-setr*ÚsetrZ prefregexZ addfailregexZaddignoreregexrŸr�r—Zaddjournalmatchz1ERROR: Invalid value for %s (%r) read from %s: %szUse %11s line : %sryz add%sRegexTiúÿÿÿiúÿÿÿ)1r…ÚconfigrÚsearchrÚosÚpathÚbasenamer0ÚdirnameÚisfileÚ Exceptionr(rÚ_verboseÚsplitextÚisabsÚabspathr r‰ÚreadZ setBaseDirZ readexplicitZapplyAutoOptionsr�Z getOptionsÚlogSysÚgetEffectiveLevelÚloggingÚDEBUGrªZconvertrŠÚ prefRegexr¦r[rdrXr‘r˜r“r”Ú ValueErrorr.r„ÚsetattrruÚtitlerc)rCreZ regextyper!r¯ZfltNameZfltFiler¨ZtryNamesÚer§ÚretZreadercommandsZ regex_valuesÚoptZoptvalZstorr$r$r%Ú readRegexEsÔ                          "zFail2banRegex.readRegexcCsd|_|j|jƒdS)NT)Ú _lineIgnoredr�ra)rCÚidxZ ignoreRegexr$r$r%rœÄszFail2banRegex._onIgnoreRegexNcCs¨|jj}|jjƒdkr"|dd…}t|ƒ|jjƒk}d}|_�y|jj||ƒ}g}g}xd|D]\} |jjsœ| jt|ƒdkƒ|j | d} | j ƒ| j | ƒ| dj dƒs¶|j| ƒq^d}q^W|jjrÐd|dfS|jj �rD|jj } | jƒ�rD|jd7_|j�rDt|jƒ|jk�r&|jj| jƒƒnt|jƒ|jk�rD|jjdƒWn.tk �rt} ztd| ƒd Sd} ~ XnX|jjƒdk�rvxî|t|ƒd…D]Ú} | |jjk�r˜yR|jjj|jjjd j| ƒƒƒ|j�rú|jjj|jjjd j| ddd …ƒƒƒWntk �rYnX|j�rP|j�s:|jj jd j| ƒƒn|j| d| d ƒ|jj!d7_!|jj"d8_"�q˜W|�r–|j|jj#ƒƒd j|ƒ}|||�p¤|jfS) Nr Frr*ZnofailTz...z ERROR: %sr/rr>)NrN)$rŠZ_Filter__lineBufferr¤r+rÊZ processLiner…rTrdrZrarfr¦rÂZ hasMatchedr‹r¹rŒr’Z getGroupsrr(rUr�rmÚpopÚindexr0Ú _debuggexrqrÃÚ_print_all_matchedrkrjrlZ processedLine)rCržZdateZ orgLineBufferZ fullBufferÚ is_ignoredÚfoundÚlinesrÇÚmatchr!ÚprerÆZbufLiner$r$r%Ú testRegexÈsl          zFail2banRegex.testRegexcsš|jj‰ˆdkrdd„}n|ˆdkr,dd„}njˆdkr>dd„}nXˆd krPd d„}nFd ˆkrf‡fd d„}n0d dlm‰m‰m‰dd„‰‡‡‡‡‡fdd„}|S)zOPrepares output- and fetch-function corresponding given '--out' option (format)ÚidÚfidcSsx|D]}t|dƒqWdS)Nr )r()rÇÚrr$r$r%Ú_out s z+Fail2banRegex._prepaireOutput.._outÚipcSs*x$|D]}t|djd|dƒƒqWdS)Nr*rÚr )r(r¦)rÇrØr$r$r%rÙs ÚmsgcSsPxJ|D]B}x<|djdƒD]*}t|tƒsFail2banRegex._prepaireOutput.._out..)r¦Ú isinstancerr0r()rÇrØr$r$r%rÙs   Úrowc SsBx<|D]4}td|d|dtdd„|djƒDƒƒfƒqWdS)Nz [%r, %r, %r],r rcss"|]\}}|dkr||fVqdS)rÜNr$)rzr©r|r$r$r%r}sz>Fail2banRegex._prepaireOutput.._out..r*)r(rƒr„)rÇrØr$r$r%rÙs ú._escOutcsäg}ddi‰xv|D]n‰ˆˆdˆdˆdd�}ˆj|ƒ}‡‡fdd„}||d <ˆjˆ|ˆd �}ˆdrx|jˆ|fƒqt|ƒqWxZ|D]R\‰}xHˆdjd ƒD]6‰tˆtƒsÄd jd d„ˆDƒƒ‰|jdˆƒ‰tˆƒq¢WqŠWdS)NÚNLrr rr*)ÚtimeÚdatacs:ˆd r*tˆdjdgƒƒdkr*|dSdˆd<dSdS)Nrèr*rÜr zmsg)r+r¦)rC)rØÚwrapr$r%Ú_get_msg0s"z=Fail2banRegex._prepaireOutput.._out.._get_msgrÛ)Z escapeValrÜr/css|] }|VqdS)Nr$)rzrØr$r$r%r}Asz>Fail2banRegex._prepaireOutput.._out..zmsg) Z ActionInfoZreplaceDynamicTagsrdr(r¦rÝrr0rå)rÇZrowsZticketZaInforìr|)rárãrârçrà)rØrër%rÙ)s$     )rÖr×)r…rTZserver.actionsrárârã)rCrÙr$)rárãrârçràr%Ú_prepaireOutputs    zFail2banRegex._prepaireOutputcCsÔtjƒ}|jjr|jƒ}�x¦|D�]œ}t|tƒrZ|j|d|dƒ\}}}dj|dƒ}n,|jdƒ}|j dƒs | rvq |j|ƒ\}}}|jjrªt |ƒdkr | r ||ƒq |�r|j j d7_ |j oÞ|jsâ|j j |jdk�r®|j jj|ƒ|j�r®|j jj|ƒn¦t |ƒdk�rT|j jd7_|j�r®|j jj|ƒ|j�r®|j jj|ƒnZ|j jd7_|j �r®|j�sŠ|j j|jdk�r®|j jj|ƒ|j�r®|j jj|ƒ|j jd7_q Wtjƒ||_dS)Nrr r/z ú#)rér…rTrírÝÚtuplerÕr0ÚrstripÚ startswithr+r�rnÚ_print_no_ignoredZ_print_all_ignoredr’rordrÎrrrjrÏrkrprlÚ_print_no_missedZ_print_all_missedrmrqrirŽ)rCÚ test_linesZt0rTržZline_datetimestrippedrÇrÐr$r$r%ÚprocessGsD  "&zFail2banRegex.processc s4ˆj}||}||d}ˆjjƒdk‰|�r0d|jƒf}ˆjrì|dksR|dkrZˆj}nˆj}||d}|ˆjks„tˆd|ƒrØgg}x"||gD]‰‡fdd „|Dƒ}q”W‡‡fd d „|Dƒ}t d d „|Dƒ|ƒnt d |||fƒnD|ˆjk�stˆd|ƒ�rt d d „|Dƒ|ƒnt d |||fƒdS)NZ_linesr z %s line(s):rlrjZ_lines_timeextractedZ _print_all_cs g|]}ˆD]}||g‘q qSr$r$)rzÚxÚy)Úargr$r%ú �sz,Fail2banRegex.printLines..csLg|]D}|dd|djƒdtˆj|dƒ|djƒˆˆjjƒ‘qS)rz | r z | )rcr&r¢r…r™)rzÚa)r"rCr$r%rù‚scSsg|] }|jƒ‘qSr$)rð)rzrör$r$r%rù…sz?%s too many to print. Use --print-all-%s to print all %d linescSsg|] }|jƒ‘qSr$)rð)rzrör$r$r%rùŠs) r�rŠr¤Ú capitalizerÎrZr�r’rur2r() rCZltypeZlstatsrÒr-r1Z regexlistZansÚbr$)rør"rCr%Ú printLinesps0   zFail2banRegex.printLinesc s¦ˆjjr dStdƒtdƒtdƒ‡fdd„}ˆjjrŠˆjj}|jƒg}ˆjrpxˆjD]}|jd|fƒqXWtdd ˆj fƒt |ƒ|d ˆj ƒ}|d ˆj ƒ}ˆjj dk �r.print_failregexesz %sz %s: %d totalZ PrefregexZ FailregexZ Ignoreregexz Date template hits:z[%d] %sz& # weight: %.3f (%.3f), pattern: %sr£z # regex: %sr!z[# of hits] date formatz Lines: %sz[processed in %.2f sec]rjrnrl)r…rTr(rŠrÂZgetRegexr¹rŒrdr‹r2rZr�Z dateDetectorZ templatesÚhitsÚnameZ _verbose_dateZweightÚtemplaterur�rŽrÏrýròró)rCrrÔrTZgrprryrr$)rCr%Ú printStats�sN            zFail2banRegex.printStatsc Csb|dd…\}}|jdƒr d|_y6|j|dƒs2dSt|ƒdkrT|j|ddƒ rTdSWn,tk r‚}ztd|ƒdSd}~XnXtjj|ƒrðy2t ||j d d �}|jd |ƒ|jd |j ƒWn(t k rê}z t|ƒdSd}~XnX�nL|jdƒ�r’t �std ƒdS|jdƒ|jd |j ƒt |ƒ\}}t d|Ž}|j|j ƒ|jƒ} |j} |jdƒ| �r†|j| ƒ|jddj| ƒƒt|| ƒ}nª|jjƒdk�rÎd|k�rÎ|jdt|jddƒƒƒ|g}nn|jdƒ}|jdt|ƒƒxFt|ƒD]:\} } | dk�r|jdƒP|jd| dt| ƒfƒ�qôW|jdƒ|jdƒ|j|ƒ|jƒ�s^dSd S)Nrzsystemd-journalZsystemdr«Fr*rŸz ERROR: %sT)ZdoOpenzUse log file : %szUse encoding : %sz,Error: systemd library not found. Exiting...zUse systemd journalzUse journal match : %sú r r>zUse single line : %sz\nz Use multi line : %s line(s)r­z| ...z | %2.2s: %sz`-r/)N)rñr�rÉr+rr(r³r´r·rr¡ÚIOErrorrrZsetLogEncodingZgetJournalReaderrˆr˜ZaddJournalMatchr0r7rŠr¤r.rår–rÿrõr) rCr#Zcmd_logZ cmd_regexrÆrôZbackendZbeArgsr4r5r“Úir-r$r$r%Ústart×sf                    zFail2banRegex.start)N)rErFrGr^r(r¢r˜r‘r”rªrÉrœrÕrírõrýrr r$r$r$r%rxõs*  @?)Hrxc GsÀdt_tƒ}|j|Ž\}}g}|jr4|jr4|jdƒ|jrJ|jrJ|jdƒt |ƒdkr`|jdƒ|r�|j ƒt j j ddj|ƒdƒt jdƒ|js¶td ƒtd ƒtd ƒtd ƒt|jƒ|_tj|jƒtjt jƒ}|jd krèd nd}|j�rt}|j�rd�pd|}ntj}|j|t|j|ƒƒƒtj|ƒy t |ƒ}Wnbt!k �r¤}zD|j�sntj"ƒtj#k�r~tj$|dd�n td|ƒt jdƒWYdd}~XnX|j%|ƒ�s¼t jdƒdS)NTzGERROR: --print-no-missed and --print-all-missed are mutually exclusive.zIERROR: --print-no-ignored and --print-all-ignored are mutually exclusive.rr*z&ERROR: provide both and .r>éÿr/z Running testsz =============r z%(levelname)-1.1s: %(message)sz %(message)sz %(tb)sz %(tbc)s)Úexc_infoz ERROR: %s)rr*)&rÀZ exitOnIOErrorrWÚ parse_argsZprint_no_missedZprint_all_missedrdZprint_no_ignoredZprint_all_ignoredr+Z print_helpr8ÚstderrÚwriter0r9rTr(rrMr¾ZsetLevelZ StreamHandlerÚstdoutrRZ log_tracebackrZfull_tracebackÚ FormatterZ setFormatterrZ addHandlerrxr¸r¿rÁrNr ) r#ÚparserrsÚerrorsrZfmtrZ fail2banRegexrÆr$r$r%Úexec_command_linesL              r)Fr)r))N):r@Ú __author__rBZ __license__ZgetoptrÀrr³r•r8réZurllib.requestrZ urllib.parseZ urllib.errorZoptparserrZ configparserrrrZserver.filtersystemdrÚ ImportErrorr r Z filterreaderr Z server.filterr rrZserver.failregexrrZhelpersrrrrrrr¾r&r(r.r2r7r:r?r=rWÚobjectrXrhrxrr$r$r$r%ÚsP       ?'client/__pycache__/fail2banregex.cpython-36.pyc000064400000061415152231502140015373 0ustar003 TB(d†pã @s¨dZdZdZdZddlZddlZddlZddlZddlZddl Z ddl Z ddl Z ddl Z ddlZ ddlmZmZddlmZmZmZydd lmZWnek r¬dZYnXdd lmZmZd d lmZdd lmZmZm Z ddl!m"Z"m#Z#ddl$m%Z%m&Z&m'Z'm(Z(m)Z)m*Z*e(dƒZ+d.dd„Z,dd„Z-d/dd„Z.d0dd„Z/dd„Z0dd„Z1d d!„Z2Gd"d#„d#eƒZ3d$d%„Z4Gd&d'„d'e5ƒZ6Gd(d)„d)e5ƒZ7Gd*d+„d+e5ƒZ8d,d-„Z9dS)1z· Fail2Ban reads log file that contains password failure report and bans the corresponding IP addresses using firewall rules. This tools can test regular expressions for "fail2ban". zFail2Ban DevelopersaICopyright (c) 2004-2008 Cyril Jaquier, 2008- Fail2Ban Contributors Copyright of modifications held by their respective authors. Licensed under the GNU General Public License v2 (GPL). Written by Cyril Jaquier . Many contributions by Yaroslav O. Halchenko, Steven Hiscocks, Sergey G. Brester (sebres).ZGPLéN)Ú OptionParserÚOption)Ú NoOptionErrorÚNoSectionErrorÚMissingSectionHeaderErroré)Ú FilterSystemd)ÚversionÚ normVersioné)Ú FilterReader)ÚFilterÚ FileContainerÚMyTime)ÚRegexÚRegexException)Ú str2LogLevelÚgetVerbosityFormatÚFormatterWithTraceBackÚ getLoggerÚextractOptionsÚ PREFER_ENCZfail2banFÚyescCs2tj||d�|ddœ}|r"d|d<dtjj|ƒS)N)ÚuseDnsÚpython)ÚreÚstrZflavorÚmÚflagszhttps://www.debuggex.com/?)rZ_resolveHostTagÚurllibÚparseZ urlencode)ZsampleÚregexÚ multilinerÚargs©r$ú#/usr/lib/python3.6/fail2banregex.pyÚ debuggexURL?s  r&cCs t|ƒdS)N)Úprint)r#r$r$r%ÚoutputHsr(é5cCs$t|ƒ|kr |d|d…dS|S)zReturn shortened string Néz...)Úlen)ÚsÚlr$r$r%ÚshortstrKs r.cCs<t|ƒs dS|rd|}nd}t|ddj|ƒdƒdS)Nz|- %s Úz| z | z `-)r+r(Újoin)r-Úheaderr,r$r$r%Ú pprint_listRs  r2c cs@x:y |jƒ}Wntk r$wYnX|s,P|j|ƒVqWdS)N)Zget_nextÚOSErrorZformatJournalEntry)ÚfltÚ myjournalÚentryr$r$r%Újournal_lines_gen[s r7cGsttƒƒtjdƒdS)Nr)r(r ÚsysÚexit)r#r$r$r%ÚdumpNormVersiones r:cCsdtjdS)Nz(%s [OPTIONS] [IGNOREREGEX]r)r8Úargvr$r$r$r%Úisr<c@seZdZdd„ZdS)Ú _f2bOptParsercOs:d|_dtƒdtdtj|f|ž|ŽdtdS)z, Overwritten format helper with full ussage.r/zUsage: Ú aÚ LOG: string a string representing a log line filename path to a log file (/var/log/auth.log) systemd-journal search systemd journal (systemd-python required), optionally with backend parameters, see `man jail.conf` for usage and examples (systemd-journal[journalflags=1]). REGEX: string a string representing a 'failregex' filter name of filter, optionally with options (sshd[mode=aggressive]) filename path to a filter file (filter.d/sshd.conf) IGNOREREGEX: string a string representing an 'ignoreregex' filename path to a filter file (filter.d/sshd.conf) z> Report bugs to https://github.com/fail2ban/fail2ban/issues )ÚusageÚ__doc__rÚ format_helpÚ __copyright__)Úselfr#Úkwargsr$r$r%rAlsz_f2bOptParser.format_helpN)Ú__name__Ú __module__Ú __qualname__rAr$r$r$r%r=ksr=cCsfttƒdtd�}|jtddddd�tdd d d �td d dddd�tddtdd�tdddddd�tddddd�tddtddd�td d!d"d �td#d$d%d&d'd(�td)d*td+d,�td-d.d/d0dd1d2�td3dd0td4d5�td6d7dd8d9�td:d;ddd?d@dAd2�tdBdCddDddEd2�tdFddGd9�tdHddId9�tdJddKd9�tdLddMd9�tdNddOd9�tdPdQddRd9�tdSddTd9�gƒ|S)UNz%prog )r?r z-cz--configz /etc/fail2banzset alternate config directory)ÚdefaultÚhelpz-dz --datepatternz+set custom pattern used to match date/times)rIz --timezonez--TZZstorez)set time-zone used by convert time format)ÚactionrHrIz-ez --encodingz%File encoding. Default: system localez-rz--rawÚ store_trueFzRaw hosts, don't resolve dnsz--usednszpDNS specified replacement of tags in regexp ('yes' - matches all form of hosts, 'no' - IP addresses only)z-Lz --maxlinesrzmaxlines for multi-line regex.)ÚtyperHrIz-mz--journalmatchzGjournalctl style matches overriding filter file. "systemd-journal" onlyz-lz --log-levelÚ log_levelÚcriticalz(Log level for the Fail2Ban logger to use)ÚdestrHrIz-VÚcallbackz,get version in machine-readable short format)rJrPrIz-vz --verboseÚcountÚverbosezIncrease verbosity)rJrOrHrIz --verbosityz'Set numerical level of verbosity (0..4))rJrOrLrIz--verbose-datez--VDz%Verbose date patterns/regex in output)rJrIz-Dz --debuggexz-Produce debuggex.com urls for debugging therez--no-check-allZ store_falseÚ checkAllRegexTzDisable check for all regex'sz-oz--outÚoutzaSet token to print failure information only (row, id, ip, msg, host, ip4, ip6, dns, matches, ...)z--print-no-missedzDo not print any missed linesz--print-no-ignoredzDo not print any ignored linesz--print-all-matchedzPrint all matched linesz--print-all-missedz*Print all missed lines, no matter how manyz--print-all-ignoredz+Print all ignored lines, no matter how manyz-tz--log-tracebackz.Enrich log-messages with compressed tracebacksz--full-tracebackzBEither to make the tracebacks full, not compressed (as by default))r=r?r Z add_optionsrrÚintr:)Úpr$r$r%Úget_opt_parser„sl         rWc@sDeZdZdd„Zdd„Zdd„Zdd„Zd d „Zd d „Zd d„Z dS)Ú RegexStatcCsd|_||_tƒ|_dS)Nr)Ú_statsÚ _failregexÚlistÚ_ipList)rCÚ failregexr$r$r%Ú__init__ÅszRegexStat.__init__cCsd|j|j|j|jfS)Nz%s(%r) %d failed: %s)Ú __class__rZrYr\)rCr$r$r%Ú__str__ÊszRegexStat.__str__cCs|jd7_dS)Nr )rY)rCr$r$r%ÚincÎsz RegexStat.inccCs|jS)N)rY)rCr$r$r%ÚgetStatsÑszRegexStat.getStatscCs|jS)N)rZ)rCr$r$r%Ú getFailRegexÔszRegexStat.getFailRegexcCs|jj|ƒdS)N)r\Úappend)rCÚvaluer$r$r%ÚappendIP×szRegexStat.appendIPcCs|jS)N)r\)rCr$r$r%Ú getIPListÚszRegexStat.getIPListN) rErFrGr^r`rarbrcrfrgr$r$r$r%rXÃsrXc@s(eZdZdZdd„Zdd„Zdd„ZdS) Ú LineStatsz(Just a convenience container for stats cCsFd|_|_g|_d|_g|_d|_g|_|jrBg|_g|_ g|_ dS)Nr) ÚtestedÚmatchedÚ matched_linesÚmissedÚ missed_linesÚignoredÚ ignored_linesZdebuggexÚmatched_lines_timeextractedÚmissed_lines_timeextractedÚignored_lines_timeextracted)rCÚoptsr$r$r%r^ás zLineStats.__init__cCsd|S)NzM%(tested)d lines, %(ignored)d ignored, %(matched)d matched, %(missed)d missedr$)rCr$r$r%r`íszLineStats.__str__cCst||ƒrt||ƒSdS)Nr/)ÚhasattrÚgetattr)rCÚkeyr$r$r%Ú __getitem__ñszLineStats.__getitem__N)rErFrGr@r^r`rwr$r$r$r%rhÞs rhc@s†eZdZdd„Zdd„Zdd„Zdd„Zd d „Zd d „Zd d„Z dd„Z dd„Z d dd„Z dd„Z dd„Zdd„Zdd„Zdd„ZdS)!Ú Fail2banRegexcCsN|jjtdd„|jjƒDƒƒƒ||_d|_d|_d|_tƒ|_t dƒ|_ d|_ t ƒ|_ t ƒ|_t ƒ|_d|_t|ƒ|_|jrŒ|j|jƒnd|_|jdk r®|jtj|jƒƒ|jrÂ|j j|jƒd|j _tjdƒddlm }|ƒ|j!rø|j"|j!ƒ|j#�r|j j$|j#ƒ|j%|j _&|j'�o&|j( |j _'t)|j(ƒ|j _*|j+|j _,d|_-dS) Ncss|]\}}d||fVqdS)Ú_Nr$)Ú.0ÚoÚvr$r$r%ú ùsz)Fail2banRegex.__init__..Frér)Ú _updateTimeREÚauto).Ú__dict__ÚupdateÚdictÚitemsÚ_optsÚ _maxlines_setÚ_datepattern_setÚ _journalmatchÚ share_configr Ú_filterÚ_prefREMatchedr[Ú _prefREGroupsÚ _ignoreregexrZÚ _time_elapsedrhÚ _line_statsÚmaxlinesÚ setMaxLinesÚ _maxlinesÚ journalmatchÚsetJournalMatchÚshlexÚsplitZtimezoneZsetLogTimeZoneZ checkFindTimerZsetAlternateNowZserver.strptimerÚ datepatternÚsetDatePatternÚusednsZ setUseDnsÚrawZ returnRawHostrSrTÚboolZ ignorePendingÚ_onIgnoreRegexZ onIgnoreRegexÚ_backend)rCrsrr$r$r%r^÷sB         zFail2banRegex.__init__cCs|jjst|ƒdS)N)r…rTr()rCÚliner$r$r%r(!szFail2banRegex.outputcCs|j|jdƒS)NÚignore)ÚencodeÚ _encoding)rCržr$r$r%Ú encode_line$szFail2banRegex.encode_linecCs@|js<|jj|ƒd|_|dk r<|jd||jjƒdfƒdS)NTzUse datepattern : %s : %sr )r‡rŠr˜r(ZgetDatePattern)rCÚpatternr$r$r%r˜'s  zFail2banRegex.setDatePatterncCs4|js0|jjt|ƒƒd|_|jd|jjƒƒdS)NTzUse maxlines : %d)r†rŠr‘rUr(Ú getMaxLines)rCr|r$r$r%r‘/szFail2banRegex.setMaxLinescCs ||_dS)N)rˆ)rCr|r$r$r%r”5szFail2banRegex.setJournalMatchc Csvi}|jƒ}xVddgt|jƒƒD]>}y$||kr8||n |jd|ƒ||<Wq"tk r^Yq"Xq"W|jd|ƒdS)NZlogtyper—Z DefinitionzReal filter options : %r)Z getCombinedr[ÚkeysÚgetrr()rCÚreaderÚfltOptZrealoptsZcomboptsÚkr$r$r%Ú_dumpRealOptions8s$ zFail2banRegex._dumpRealOptionsc/Cs<|d&ks t‚|d}|jj}|}d}i}|dk�r0tjd|ƒ�r0y t|ƒ\}}d|d'd…krf|f}n||d|df}xd|D]\}d |kr¼tjj|ƒd krªtjj ||ƒ}qÈtjj |d |ƒ}n tjj |ƒ}tjj |ƒrÖPd}q~WWnNt k �r.} z0t d t| ƒfƒt d |fƒ|j�r | ‚d Sd} ~ XnX|dk �r¸||jjk�svtjj|ƒd k�svd|d(d…k�rÂd |k�rÂtjj|ƒd k�r”tjj |ƒ}tjjtjj|ƒƒd}|j d|||fƒn0|j d||fƒd}tjj|ƒ�sòtjj|ƒ}|�r|j d|ƒt|d||j|d�} d} y*|dk �r4| jƒ} n| jdƒ| jƒ} WnBt k �rŠ} z$t dt| ƒfƒ|j�rz| ‚WYdd} ~ XnX| �s¢t d|ƒd S| j|jƒ| jdƒ|jdk�sÔtjƒtjk�rà|j| |ƒ| j ƒ} i} �xì| D�]¼}|ddk�r|d}n |ddk�rô|dd…}n�qô�y@|ddk�r`x|D]}||j!_"�qJW�n|ddk�r¬| j#dƒ}|�sŒt$ƒ}| d<xä|D]}|j%t&|ƒƒ�q’WnÆ|ddk�rø| j#dƒ}|�sØt$ƒ}| d<x˜|D]}|j%t&|ƒƒ�qÞWnz|ddk�r"xj|D]}|j'|ƒ�q WnP|ddk�rLx@|D]}|j(|ƒ�q6Wn&|dd k�rr|jj)dk�rr|j*|ƒWn:t+k �r®} zt d!|d||| fƒd Sd} ~ XnX�qôWn$|j d"|t,|ƒfƒ|t&|ƒgi} xZ| j-ƒD]N\}} |d}t.|d#|| ƒx*| D]"}t/|j!d$|j0ƒƒ|j1ƒƒ�q W�qæWd%S))NÚfailrŸr!z"(?ms)^/{0,3}[\w/_\-.]+(?:\[.*\])?$Ú.éz.confz.localú/zfilter.dz'ERROR: Wrong filter name or options: %sz while parsing: %sFrz&Use %11s filter file : %s, basedir: %szUse %11s file : %szUse filter options : %rzfail2ban-regex-jail)r‰ÚbasedirzWrong config file: %szERROR: failed to load filter %sr z multi-setr*ÚsetrZ prefregexZ addfailregexZaddignoreregexr�r—Zaddjournalmatchz1ERROR: Invalid value for %s (%r) read from %s: %szUse %11s line : %sryz add%sRegexT)r«rŸiúÿÿÿiúÿÿÿ)2ÚAssertionErrorr…ÚconfigrÚsearchrÚosÚpathÚbasenamer0ÚdirnameÚisfileÚ Exceptionr(rÚ_verboseÚsplitextÚisabsÚabspathr r‰ÚreadZ setBaseDirZ readexplicitZapplyAutoOptionsr�Z getOptionsÚlogSysÚgetEffectiveLevelÚloggingÚDEBUGrªZconvertrŠÚ prefRegexr¦r[rdrXr‘r˜r“r”Ú ValueErrorr.r„ÚsetattrruÚtitlerc)rCreZ regextyper!r¯ZfltNameZfltFiler¨ZtryNamesÚer§ÚretZreadercommandsZ regex_valuesÚoptZoptvalZstorr$r$r%Ú readRegexEsÖ                           "zFail2banRegex.readRegexcCsd|_|j|jƒdS)NT)Ú _lineIgnoredr�ra)rCÚidxZ ignoreRegexr$r$r%rœÄszFail2banRegex._onIgnoreRegexNcCs¨|jj}|jjƒdkr"|dd…}t|ƒ|jjƒk}d}|_�y|jj||ƒ}g}g}xd|D]\} |jjsœ| jt|ƒdkƒ|j | d} | j ƒ| j | ƒ| dj dƒs¶|j| ƒq^d}q^W|jjrÐd|dfS|jj �rD|jj } | jƒ�rD|jd7_|j�rDt|jƒ|jk�r&|jj| jƒƒnt|jƒ|jk�rD|jjdƒWn.tk �rt} ztd| ƒd Sd} ~ XnX|jjƒdk�rvxî|t|ƒd…D]Ú} | |jjk�r˜yR|jjj|jjjd j| ƒƒƒ|j�rú|jjj|jjjd j| ddd …ƒƒƒWntk �rYnX|j�rP|j�s:|jj jd j| ƒƒn|j| d| d ƒ|jj!d7_!|jj"d8_"�q˜W|�r–|j|jj#ƒƒd j|ƒ}|||�p¤|jfS) Nr Frr*ZnofailTz...z ERROR: %sr/rr>)NrN)$rŠZ_Filter__lineBufferr¤r+rËZ processLiner…rTrdrZrarfr¦rÃZ hasMatchedr‹rºrŒr’Z getGroupsrr(rUr�rmÚpopÚindexr0Ú _debuggexrqrÄÚ_print_all_matchedrkrjrlZ processedLine)rCržZdateZ orgLineBufferZ fullBufferÚ is_ignoredÚfoundÚlinesrÈÚmatchr!ÚprerÇZbufLiner$r$r%Ú testRegexÈsl          zFail2banRegex.testRegexcsš|jj‰ˆdkrdd„}n|ˆdkr,dd„}njˆdkr>dd„}nXˆd krPd d„}nFd ˆkrf‡fd d„}n0d dlm‰m‰m‰dd„‰‡‡‡‡‡fdd„}|S)zOPrepares output- and fetch-function corresponding given '--out' option (format)ÚidÚfidcSsx|D]}t|dƒqWdS)Nr )r()rÈÚrr$r$r%Ú_out s z+Fail2banRegex._prepaireOutput.._outÚipcSs*x$|D]}t|djd|dƒƒqWdS)Nr*rÛr )r(r¦)rÈrÙr$r$r%rÚs ÚmsgcSsPxJ|D]B}x<|djdƒD]*}t|tƒsFail2banRegex._prepaireOutput.._out..)r¦Ú isinstancerr0r()rÈrÙr$r$r%rÚs   Úrowc SsBx<|D]4}td|d|dtdd„|djƒDƒƒfƒqWdS)Nz [%r, %r, %r],r rcss"|]\}}|dkr||fVqdS)rÝNr$)rzr©r|r$r$r%r}sz>Fail2banRegex._prepaireOutput.._out..r*)r(rƒr„)rÈrÙr$r$r%rÚs ú._escOutcsäg}ddi‰xv|D]n‰ˆˆdˆdˆdd�}ˆj|ƒ}‡‡fdd„}||d <ˆjˆ|ˆd �}ˆdrx|jˆ|fƒqt|ƒqWxZ|D]R\‰}xHˆdjd ƒD]6‰tˆtƒsÄd jd d„ˆDƒƒ‰|jdˆƒ‰tˆƒq¢WqŠWdS)NÚNLrr rr*)ÚtimeÚdatacs:ˆd r*tˆdjdgƒƒdkr*|dSdˆd<dSdS)Nrér*rÝr zmsg)r+r¦)rC)rÙÚwrapr$r%Ú_get_msg0s"z=Fail2banRegex._prepaireOutput.._out.._get_msgrÜ)Z escapeValrÝr/css|] }|VqdS)Nr$)rzrÙr$r$r%r}Asz>Fail2banRegex._prepaireOutput.._out..zmsg) Z ActionInfoZreplaceDynamicTagsrdr(r¦rÞrr0ræ)rÈZrowsZticketZaInforír|)rârärãrèrá)rÙrìr%rÚ)s$     )r×rØ)r…rTZserver.actionsrârãrä)rCrÚr$)rârärãrèrár%Ú_prepaireOutputs    zFail2banRegex._prepaireOutputcCsÔtjƒ}|jjr|jƒ}�x¦|D�]œ}t|tƒrZ|j|d|dƒ\}}}dj|dƒ}n,|jdƒ}|j dƒs | rvq |j|ƒ\}}}|jjrªt |ƒdkr | r ||ƒq |�r|j j d7_ |j oÞ|jsâ|j j |jdk�r®|j jj|ƒ|j�r®|j jj|ƒn¦t |ƒdk�rT|j jd7_|j�r®|j jj|ƒ|j�r®|j jj|ƒnZ|j jd7_|j �r®|j�sŠ|j j|jdk�r®|j jj|ƒ|j�r®|j jj|ƒ|j jd7_q Wtjƒ||_dS)Nrr r/z ú#)rêr…rTrîrÞÚtuplerÖr0ÚrstripÚ startswithr+r�rnÚ_print_no_ignoredZ_print_all_ignoredr’rordrÏrrrjrÐrkrprlÚ_print_no_missedZ_print_all_missedrmrqrirŽ)rCÚ test_linesZt0rTržZline_datetimestrippedrÈrÑr$r$r%ÚprocessGsD  "&zFail2banRegex.processc sRˆj}|j|j|j|jks"t‚||}||d}ˆjjƒdk‰|�rNd|jƒf}ˆj �r |dksp|dkrxˆj }nˆj }||d}|ˆj ks¢t ˆd|ƒrögg}x"||gD]‰‡fdd „|Dƒ}q²W‡‡fd d „|Dƒ}td d „|Dƒ|ƒntd |||fƒnD|ˆj k�s&t ˆd|ƒ�r�sz,Fail2banRegex.printLines..csLg|]D}|dd|djƒdtˆj|dƒ|djƒˆˆjjƒ‘qS)rz | r z | )rcr&r¢r…r™)rzÚa)r"rCr$r%rú‚scSsg|] }|jƒ‘qSr$)rñ)rzr÷r$r$r%rú…sz?%s too many to print. Use --print-all-%s to print all %d linescSsg|] }|jƒ‘qSr$)rñ)rzr÷r$r$r%rúŠs)r�rlrirjrnr±rŠr¤Ú capitalizerÏrZr�r’rur2r() rCZltypeZlstatsrÓr-r1Z regexlistZansÚbr$)rùr"rCr%Ú printLinesps2   zFail2banRegex.printLinesc s¦ˆjjr dStdƒtdƒtdƒ‡fdd„}ˆjjrŠˆjj}|jƒg}ˆjrpxˆjD]}|jd|fƒqXWtdd ˆj fƒt |ƒ|d ˆj ƒ}|d ˆj ƒ}ˆjj dk �r.print_failregexesz %sz %s: %d totalZ PrefregexZ FailregexZ Ignoreregexz Date template hits:z[%d] %sz& # weight: %.3f (%.3f), pattern: %sr£z # regex: %sr!z[# of hits] date formatz Lines: %sz[processed in %.2f sec]rjrnrl)r…rTr(rŠrÃZgetRegexrºrŒrdr‹r2rZr�Z dateDetectorZ templatesÚhitsÚnameZ _verbose_dateZweightÚtemplaterur�rŽrÐrþrórô)rCrrÕrTZgrprryrr$)rCr%Ú printStats�sN            zFail2banRegex.printStatsc Csb|dd…\}}|jdƒr d|_y6|j|dƒs2dSt|ƒdkrT|j|ddƒ rTdSWn,tk r‚}ztd|ƒdSd}~XnXtjj|ƒrðy2t ||j d d �}|jd |ƒ|jd |j ƒWn(t k rê}z t|ƒdSd}~XnX�nL|jdƒ�r’t �std ƒdS|jdƒ|jd |j ƒt |ƒ\}}t d|Ž}|j|j ƒ|jƒ} |j} |jdƒ| �r†|j| ƒ|jddj| ƒƒt|| ƒ}nª|jjƒdk�rÎd|k�rÎ|jdt|jddƒƒƒ|g}nn|jdƒ}|jdt|ƒƒxFt|ƒD]:\} } | dk�r|jdƒP|jd| dt| ƒfƒ�qôW|jdƒ|jdƒ|j|ƒ|jƒ�s^dSd S)Nrzsystemd-journalZsystemdr«Fr*rŸz ERROR: %sT)ZdoOpenzUse log file : %szUse encoding : %sz,Error: systemd library not found. Exiting...zUse systemd journalzUse journal match : %sú r r>zUse single line : %sz\nz Use multi line : %s line(s)r­z| ...z | %2.2s: %sz`-r/)N)ròr�rÊr+rr(r´rµr¸rr¡ÚIOErrorrrZsetLogEncodingZgetJournalReaderrˆr˜ZaddJournalMatchr0r7rŠr¤r.rær–rrör) rCr#Zcmd_logZ cmd_regexrÇrõZbackendZbeArgsr4r5r“Úir-r$r$r%Ústart×sf                    zFail2banRegex.start)N)rErFrGr^r(r¢r˜r‘r”rªrÊrœrÖrîrörþrr r$r$r$r%rxõs*  @?)Hrxc GsÀdt_tƒ}|j|Ž\}}g}|jr4|jr4|jdƒ|jrJ|jrJ|jdƒt |ƒdkr`|jdƒ|r�|j ƒt j j ddj|ƒdƒt jdƒ|js¶td ƒtd ƒtd ƒtd ƒt|jƒ|_tj|jƒtjt jƒ}|jd krèd nd}|j�rt}|j�rd�pd|}ntj}|j|t|j|ƒƒƒtj|ƒy t |ƒ}Wnbt!k �r¤}zD|j�sntj"ƒtj#k�r~tj$|dd�n td|ƒt jdƒWYdd}~XnX|j%|ƒ�s¼t jdƒdS)NTzGERROR: --print-no-missed and --print-all-missed are mutually exclusive.zIERROR: --print-no-ignored and --print-all-ignored are mutually exclusive.rr*z&ERROR: provide both and .r>éÿr/z Running testsz =============r z%(levelname)-1.1s: %(message)sz %(message)sz %(tb)sz %(tbc)s)Úexc_infoz ERROR: %s)rr*)&rÁZ exitOnIOErrorrWÚ parse_argsZprint_no_missedZprint_all_missedrdZprint_no_ignoredZprint_all_ignoredr+Z print_helpr8ÚstderrÚwriter0r9rTr(rrMr¿ZsetLevelZ StreamHandlerÚstdoutrRZ log_tracebackrZfull_tracebackÚ FormatterZ setFormatterrZ addHandlerrxr¹rÀrÂrNr ) r#ÚparserrsÚerrorsrZfmtrZ fail2banRegexrÇr$r$r%Úexec_command_linesL              r)Fr)r))N):r@Ú __author__rBZ __license__ZgetoptrÁrr´r•r8rêZurllib.requestrZ urllib.parseZ urllib.errorZoptparserrZ configparserrrrZserver.filtersystemdrÚ ImportErrorr r Z filterreaderr Z server.filterr rrZserver.failregexrrZhelpersrrrrrrr¿r&r(r.r2r7r:r?r=rWÚobjectrXrhrxrr$r$r$r%ÚsP       ?'client/__init__.py000064400000001716152231502140010133 0ustar00# 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. # Author: Cyril Jaquier # __author__ = "Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" client/actionreader.py000064400000006361152231502140011035 0ustar00# 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. # Author: Cyril Jaquier # __author__ = "Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" import os from .configreader import DefinitionInitConfigReader from ..helpers import getLogger from ..server.action import CommandAction # Gets the instance of the logger. logSys = getLogger(__name__) class ActionReader(DefinitionInitConfigReader): _configOpts = { "actionstart": ["string", None], "actionstart_on_demand": ["bool", None], "actionstop": ["string", None], "actionflush": ["string", None], "actionreload": ["string", None], "actioncheck": ["string", None], "actionrepair": ["string", None], "actionrepair_on_unban": ["bool", None], "actionban": ["string", None], "actionprolong": ["string", None], "actionreban": ["string", None], "actionunban": ["string", None], "norestored": ["bool", None], } def __init__(self, file_, jailName, initOpts, **kwargs): # always supply jail name as name parameter if not specified in options: n = initOpts.get("name") if n is None: initOpts["name"] = n = jailName actname = initOpts.get("actname") if actname is None: actname = file_ # ensure we've unique action name per jail: if n != jailName: actname += n[len(jailName):] if n.startswith(jailName) else '-' + n initOpts["actname"] = actname self._name = actname DefinitionInitConfigReader.__init__( self, file_, jailName, initOpts, **kwargs) def setFile(self, fileName): self.__file = fileName DefinitionInitConfigReader.setFile(self, os.path.join("action.d", fileName)) def getFile(self): return self.__file def setName(self, name): self._name = name def getName(self): return self._name def convert(self): opts = self.getCombined( ignore=CommandAction._escapedTags | set(('timeout', 'bantime'))) # stream-convert: head = ["set", self._jailName] stream = list() stream.append(head + ["addaction", self._name]) multi = [] for opt, optval in opts.items(): if opt in self._configOpts and not opt.startswith('known/'): multi.append([opt, optval]) if self._initOpts: for opt, optval in self._initOpts.items(): if opt not in self._configOpts and not opt.startswith('known/'): multi.append([opt, optval]) if len(multi) > 1: stream.append(["multi-set", self._jailName, "action", self._name, multi]) elif len(multi): stream.append(["set", self._jailName, "action", self._name] + multi[0]) return stream client/beautifier.py000064400000015772152231502140010522 0ustar00# 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. __author__ = "Cyril Jaquier, Yaroslav Halchenko" __copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2013- Yaroslav Halchenko" __license__ = "GPL" from ..exceptions import UnknownJailException, DuplicateJailException from ..helpers import getLogger, logging # Gets the instance of the logger. logSys = getLogger(__name__) ## # Beautify the output of the client. # # Fail2ban server only return unformatted return codes which need to be # converted into user readable messages. class Beautifier: def __init__(self, cmd = None): self.__inputCmd = cmd def setInputCmd(self, cmd): self.__inputCmd = cmd def getInputCmd(self): return self.__inputCmd def beautify(self, response): logSys.log(5, "Beautify " + repr(response) + " with " + repr(self.__inputCmd)) inC = self.__inputCmd msg = response try: if inC[0] == "ping": msg = "Server replied: " + response elif inC[0] == "version": msg = response elif inC[0] == "start": msg = "Jail started" elif inC[0] == "stop": if len(inC) == 1: if response is None: msg = "Shutdown successful" else: if response is None: msg = "Jail stopped" elif inC[0] == "add": msg = "Added jail " + response elif inC[0] == "flushlogs": msg = "logs: " + response elif inC[0] == "echo": msg = ' '.join(msg) elif inC[0:1] == ['status']: if len(inC) > 1: # Display information msg = ["Status for the jail: %s" % inC[1]] for n, res1 in enumerate(response): prefix1 = "`-" if n == len(response) - 1 else "|-" msg.append("%s %s" % (prefix1, res1[0])) prefix1 = " " if n == len(response) - 1 else "| " for m, res2 in enumerate(res1[1]): prefix2 = prefix1 + ("`-" if m == len(res1[1]) - 1 else "|-") val = " ".join(map(str, res2[1])) if isinstance(res2[1], list) else res2[1] msg.append("%s %s:\t%s" % (prefix2, res2[0], val)) else: msg = ["Status"] for n, res1 in enumerate(response): prefix1 = "`-" if n == len(response) - 1 else "|-" val = " ".join(map(str, res1[1])) if isinstance(res1[1], list) else res1[1] msg.append("%s %s:\t%s" % (prefix1, res1[0], val)) msg = "\n".join(msg) elif len(inC) < 2: pass # to few cmd args for below elif inC[1] == "syslogsocket": msg = "Current syslog socket is:\n" msg += "`- " + response elif inC[1] == "logtarget": msg = "Current logging target is:\n" msg += "`- " + response elif inC[1:2] == ['loglevel']: msg = "Current logging level is " msg += repr(logging.getLevelName(response) if isinstance(response, int) else response) elif inC[1] == "dbfile": if response is None: msg = "Database currently disabled" else: msg = "Current database file is:\n" msg += "`- " + response elif inC[1] == "dbpurgeage": if response is None: msg = "Database currently disabled" else: msg = "Current database purge age is:\n" msg += "`- %iseconds" % response elif len(inC) < 3: pass # to few cmd args for below elif inC[2] in ("logpath", "addlogpath", "dellogpath"): if len(response) == 0: msg = "No file is currently monitored" else: msg = "Current monitored log file(s):\n" for path in response[:-1]: msg += "|- " + path + "\n" msg += "`- " + response[-1] elif inC[2] == "logencoding": msg = "Current log encoding is set to:\n" msg += response elif inC[2] in ("journalmatch", "addjournalmatch", "deljournalmatch"): if len(response) == 0: msg = "No journal match filter set" else: msg = "Current match filter:\n" msg += ' + '.join(" ".join(res) for res in response) elif inC[2] == "datepattern": msg = "Current date pattern set to: " if response is None: msg += "Not set/required" elif response[0] is None: msg += "%s" % response[1] else: msg += "%s (%s)" % response elif inC[2] in ("ignoreip", "addignoreip", "delignoreip"): if len(response) == 0: msg = "No IP address/network is ignored" else: msg = "These IP addresses/networks are ignored:\n" for ip in response[:-1]: msg += "|- " + ip + "\n" msg += "`- " + response[-1] elif inC[2] in ("failregex", "addfailregex", "delfailregex", "ignoreregex", "addignoreregex", "delignoreregex"): if len(response) == 0: msg = "No regular expression is defined" else: msg = "The following regular expression are defined:\n" c = 0 for l in response[:-1]: msg += "|- [" + str(c) + "]: " + l + "\n" c += 1 msg += "`- [" + str(c) + "]: " + response[-1] elif inC[2] == "actions": if len(response) == 0: msg = "No actions for jail %s" % inC[1] else: msg = "The jail %s has the following actions:\n" % inC[1] msg += ", ".join(response) elif inC[2] == "actionproperties": if len(response) == 0: msg = "No properties for jail %s action %s" % ( inC[1], inC[3]) else: msg = "The jail %s action %s has the following " \ "properties:\n" % (inC[1], inC[3]) msg += ", ".join(response) elif inC[2] == "actionmethods": if len(response) == 0: msg = "No methods for jail %s action %s" % ( inC[1], inC[3]) else: msg = "The jail %s action %s has the following " \ "methods:\n" % (inC[1], inC[3]) msg += ", ".join(response) elif inC[2] == "banip" and inC[0] == "get": if isinstance(response, list): sep = " " if len(inC) <= 3 else inC[3] if sep == "--with-time": sep = "\n" msg = sep.join(response) except Exception: logSys.warning("Beautifier error. Please report the error") logSys.error("Beautify %r with %r failed", response, self.__inputCmd, exc_info=logSys.getEffectiveLevel()<=logging.DEBUG) msg = repr(msg) + repr(response) return msg def beautifyError(self, response): logSys.debug("Beautify (error) %r with %r", response, self.__inputCmd) msg = response if isinstance(response, UnknownJailException): msg = "Sorry but the jail '" + response.args[0] + "' does not exist" elif isinstance(response, IndexError): msg = "Sorry but the command is invalid" elif isinstance(response, DuplicateJailException): msg = "The jail '" + response.args[0] + "' already exists" return msg client/configparserinc.py000064400000031514152231502140011547 0ustar00# 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. # Author: Yaroslav Halchenko # Modified: Cyril Jaquier __author__ = 'Yaroslav Halchenko, Serg G. Brester (aka sebres)' __copyright__ = 'Copyright (c) 2007 Yaroslav Halchenko, 2015 Serg G. Brester (aka sebres)' __license__ = 'GPL' import os import re import sys from ..helpers import getLogger if sys.version_info >= (3,): # pragma: 2.x no cover # SafeConfigParser deprecated from Python 3.2 (renamed to ConfigParser) from configparser import ConfigParser as SafeConfigParser, BasicInterpolation, \ InterpolationMissingOptionError, NoOptionError, NoSectionError # And interpolation of __name__ was simply removed, thus we need to # decorate default interpolator to handle it class BasicInterpolationWithName(BasicInterpolation): """Decorator to bring __name__ interpolation back. Original handling of __name__ was removed because of functional deficiencies: http://bugs.python.org/issue10489 commit v3.2a4-105-g61f2761 Author: Lukasz Langa Date: Sun Nov 21 13:41:35 2010 +0000 Issue #10489: removed broken `__name__` support from configparser But should be fine to reincarnate for our use case """ def _interpolate_some(self, parser, option, accum, rest, section, map, *args, **kwargs): if section and not (__name__ in map): map = map.copy() # just to be safe map['__name__'] = section # try to wrap section options like %(section/option)s: parser._map_section_options(section, option, rest, map) return super(BasicInterpolationWithName, self)._interpolate_some( parser, option, accum, rest, section, map, *args, **kwargs) else: # pragma: 3.x no cover from configparser import SafeConfigParser, \ InterpolationMissingOptionError, NoOptionError, NoSectionError # Interpolate missing known/option as option from default section SafeConfigParser._cp_interpolate_some = SafeConfigParser._interpolate_some def _interpolate_some(self, option, accum, rest, section, map, *args, **kwargs): # try to wrap section options like %(section/option)s: self._map_section_options(section, option, rest, map) return self._cp_interpolate_some(option, accum, rest, section, map, *args, **kwargs) SafeConfigParser._interpolate_some = _interpolate_some def _expandConfFilesWithLocal(filenames): """Expands config files with local extension. """ newFilenames = [] for filename in filenames: newFilenames.append(filename) localname = os.path.splitext(filename)[0] + '.local' if localname not in filenames and os.path.isfile(localname): newFilenames.append(localname) return newFilenames # Gets the instance of the logger. logSys = getLogger(__name__) logLevel = 7 __all__ = ['SafeConfigParserWithIncludes'] class SafeConfigParserWithIncludes(SafeConfigParser): """ Class adds functionality to SafeConfigParser to handle included other configuration files (or may be urls, whatever in the future) File should have section [includes] and only 2 options implemented are 'files_before' and 'files_after' where files are listed 1 per line. Example: [INCLUDES] before = 1.conf 3.conf after = 1.conf It is a simple implementation, so just basic care is taken about recursion. Includes preserve right order, ie new files are inserted to the list of read configs before original, and their includes correspondingly so the list should follow the leaves of the tree. I wasn't sure what would be the right way to implement generic (aka c++ template) so we could base at any *configparser class... so I will leave it for the future """ SECTION_NAME = "INCLUDES" SECTION_OPTNAME_CRE = re.compile(r'^([\w\-]+)/([^\s>]+)$') SECTION_OPTSUBST_CRE = re.compile(r'%\(([\w\-]+/([^\)]+))\)s') CONDITIONAL_RE = re.compile(r"^(\w+)(\?.+)$") if sys.version_info >= (3,2): # overload constructor only for fancy new Python3's def __init__(self, share_config=None, *args, **kwargs): kwargs = kwargs.copy() kwargs['interpolation'] = BasicInterpolationWithName() kwargs['inline_comment_prefixes'] = ";" super(SafeConfigParserWithIncludes, self).__init__( *args, **kwargs) self._cfg_share = share_config else: def __init__(self, share_config=None, *args, **kwargs): SafeConfigParser.__init__(self, *args, **kwargs) self._cfg_share = share_config def get_ex(self, section, option, raw=False, vars={}): """Get an option value for a given section. In opposite to `get`, it differentiate session-related option name like `sec/opt`. """ sopt = None # if option name contains section: if '/' in option: sopt = SafeConfigParserWithIncludes.SECTION_OPTNAME_CRE.search(option) # try get value from named section/option: if sopt: sec = sopt.group(1) opt = sopt.group(2) seclwr = sec.lower() if seclwr == 'known': # try get value firstly from known options, hereafter from current section: sopt = ('KNOWN/'+section, section) else: sopt = (sec,) if seclwr != 'default' else ("DEFAULT",) for sec in sopt: try: v = self.get(sec, opt, raw=raw) return v except (NoSectionError, NoOptionError) as e: pass # get value of section/option using given section and vars (fallback): v = self.get(section, option, raw=raw, vars=vars) return v def _map_section_options(self, section, option, rest, defaults): """ Interpolates values of the section options (name syntax `%(section/option)s`). Fallback: try to wrap missing default options as "default/options" resp. "known/options" """ if '/' not in rest or '%(' not in rest: # pragma: no cover return 0 rplcmnt = 0 soptrep = SafeConfigParserWithIncludes.SECTION_OPTSUBST_CRE.findall(rest) if not soptrep: # pragma: no cover return 0 for sopt, opt in soptrep: if sopt not in defaults: sec = sopt[:~len(opt)] seclwr = sec.lower() if seclwr != 'default': usedef = 0 if seclwr == 'known': # try get raw value from known options: try: v = self._sections['KNOWN/'+section][opt] except KeyError: # fallback to default: usedef = 1 else: # get raw value of opt in section: try: # if section not found - ignore: try: sec = self._sections[sec] except KeyError: # pragma: no cover continue v = sec[opt] except KeyError: # pragma: no cover # fallback to default: usedef = 1 else: usedef = 1 if usedef: try: v = self._defaults[opt] except KeyError: # pragma: no cover continue # replacement found: rplcmnt = 1 try: # set it in map-vars (consider different python versions): defaults[sopt] = v except: # try to set in first default map (corresponding vars): try: defaults._maps[0][sopt] = v except: # pragma: no cover # no way to update vars chain map - overwrite defaults: self._defaults[sopt] = v return rplcmnt @property def share_config(self): return self._cfg_share def _getSharedSCPWI(self, filename): SCPWI = SafeConfigParserWithIncludes # read single one, add to return list, use sharing if possible: if self._cfg_share: # cache/share each file as include (ex: filter.d/common could be included in each filter config): hashv = 'inc:'+(filename if not isinstance(filename, list) else '\x01'.join(filename)) cfg, i = self._cfg_share.get(hashv, (None, None)) if cfg is None: cfg = SCPWI(share_config=self._cfg_share) i = cfg.read(filename, get_includes=False) self._cfg_share[hashv] = (cfg, i) elif logSys.getEffectiveLevel() <= logLevel: logSys.log(logLevel, " Shared file: %s", filename) else: # don't have sharing: cfg = SCPWI() i = cfg.read(filename, get_includes=False) return (cfg, i) def _getIncludes(self, filenames, seen=[]): if not isinstance(filenames, list): filenames = [ filenames ] filenames = _expandConfFilesWithLocal(filenames) # retrieve or cache include paths: if self._cfg_share: # cache/share include list: hashv = 'inc-path:'+('\x01'.join(filenames)) fileNamesFull = self._cfg_share.get(hashv) if fileNamesFull is None: fileNamesFull = [] for filename in filenames: fileNamesFull += self.__getIncludesUncached(filename, seen) self._cfg_share[hashv] = fileNamesFull return fileNamesFull # don't have sharing: fileNamesFull = [] for filename in filenames: fileNamesFull += self.__getIncludesUncached(filename, seen) return fileNamesFull def __getIncludesUncached(self, resource, seen=[]): """ Given 1 config resource returns list of included files (recursively) with the original one as well Simple loops are taken care about """ SCPWI = SafeConfigParserWithIncludes try: parser, i = self._getSharedSCPWI(resource) if not i: return [] except UnicodeDecodeError as e: logSys.error("Error decoding config file '%s': %s" % (resource, e)) return [] resourceDir = os.path.dirname(resource) newFiles = [ ('before', []), ('after', []) ] if SCPWI.SECTION_NAME in parser.sections(): for option_name, option_list in newFiles: if option_name in parser.options(SCPWI.SECTION_NAME): newResources = parser.get(SCPWI.SECTION_NAME, option_name) for newResource in newResources.split('\n'): if os.path.isabs(newResource): r = newResource else: r = os.path.join(resourceDir, newResource) if r in seen: continue s = seen + [resource] option_list += self._getIncludes(r, s) # combine lists return newFiles[0][1] + [resource] + newFiles[1][1] def get_defaults(self): return self._defaults def get_sections(self): return self._sections def options(self, section, withDefault=True): """Return a list of option names for the given section name. Parameter `withDefault` controls the include of names from section `[DEFAULT]` """ try: opts = self._sections[section] except KeyError: # pragma: no cover raise NoSectionError(section) if withDefault: # mix it with defaults: return set(opts.keys()) | set(self._defaults) # only own option names: return list(opts.keys()) def read(self, filenames, get_includes=True): if not isinstance(filenames, list): filenames = [ filenames ] # retrieve (and cache) includes: fileNamesFull = [] if get_includes: fileNamesFull += self._getIncludes(filenames) else: fileNamesFull = filenames if not fileNamesFull: return [] logSys.info(" Loading files: %s", fileNamesFull) if get_includes or len(fileNamesFull) > 1: # read multiple configs: ret = [] alld = self.get_defaults() alls = self.get_sections() for filename in fileNamesFull: # read single one, add to return list, use sharing if possible: cfg, i = self._getSharedSCPWI(filename) if i: ret += i # merge defaults and all sections to self: alld.update(cfg.get_defaults()) for n, s in cfg.get_sections().items(): # conditional sections cond = SafeConfigParserWithIncludes.CONDITIONAL_RE.match(n) if cond: n, cond = cond.groups() s = s.copy() try: del(s['__name__']) except KeyError: pass for k in list(s.keys()): v = s.pop(k) s[k + cond] = v s2 = alls.get(n) if isinstance(s2, dict): # save previous known values, for possible using in local interpolations later: self.merge_section('KNOWN/'+n, dict([i for i in iter(s2.items()) if i[0] in s]), '') # merge section s2.update(s) else: alls[n] = s.copy() return ret # read one config : if logSys.getEffectiveLevel() <= logLevel: logSys.log(logLevel, " Reading file: %s", fileNamesFull[0]) # read file(s) : if sys.version_info >= (3,2): # pragma: no cover return SafeConfigParser.read(self, fileNamesFull, encoding='utf-8') else: return SafeConfigParser.read(self, fileNamesFull) def merge_section(self, section, options, pref=None): alls = self.get_sections() try: sec = alls[section] except KeyError: alls[section] = sec = dict() if not pref: sec.update(options) return sk = {} for k, v in options.items(): if not k.startswith(pref) and k != '__name__': sk[pref+k] = v sec.update(sk) client/configreader.py000064400000032115152231502140011021 0ustar00# 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. # Author: Cyril Jaquier # Modified by: Yaroslav Halchenko (SafeConfigParserWithIncludes) __author__ = "Cyril Jaquier, Yaroslav Halchenko, Serg G. Brester (aka sebres)" __copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2007 Yaroslav Halchenko, 2015 Serg G. Brester (aka sebres)" __license__ = "GPL" import glob import os from configparser import NoOptionError, NoSectionError from .configparserinc import sys, SafeConfigParserWithIncludes, logLevel from ..helpers import getLogger, _as_bool, _merge_dicts, substituteRecursiveTags # Gets the instance of the logger. logSys = getLogger(__name__) CONVERTER = { "bool": _as_bool, "int": int, } def _OptionsTemplateGen(options): """Iterator over the options template with default options. Each options entry is composed of an array or tuple with: [[type, name, ?default?], ...] Or it is a dict: {name: [type, default], ...} """ if isinstance(options, (list,tuple)): for optname in options: if len(optname) > 2: opttype, optname, optvalue = optname else: (opttype, optname), optvalue = optname, None yield opttype, optname, optvalue else: for optname in options: opttype, optvalue = options[optname] yield opttype, optname, optvalue class ConfigReader(): """Generic config reader class. A caching adapter which automatically reuses already shared configuration. """ def __init__(self, use_config=None, share_config=None, **kwargs): # use given shared config if possible (see read): self._cfg_share = None self._cfg = None if use_config is not None: self._cfg = use_config # share config if possible: if share_config is not None: self._cfg_share = share_config self._cfg_share_kwargs = kwargs self._cfg_share_basedir = None elif self._cfg is None: self._cfg = ConfigReaderUnshared(**kwargs) def setBaseDir(self, basedir): if self._cfg: self._cfg.setBaseDir(basedir) else: self._cfg_share_basedir = basedir def getBaseDir(self): if self._cfg: return self._cfg.getBaseDir() else: return self._cfg_share_basedir @property def share_config(self): return self._cfg_share def read(self, name, once=True): """ Overloads a default (not shared) read of config reader. To prevent mutiple reads of config files with it includes, reads into the config reader, if it was not yet cached/shared by 'name'. """ # already shared ? if not self._cfg: self._create_unshared(name) # performance feature - read once if using shared config reader: if once and self._cfg.read_cfg_files is not None: return self._cfg.read_cfg_files # load: logSys.info("Loading configs for %s under %s ", name, self._cfg.getBaseDir()) ret = self._cfg.read(name) # save already read and return: self._cfg.read_cfg_files = ret return ret def _create_unshared(self, name=''): """ Allocates and share a config file by it name. Automatically allocates unshared or reuses shared handle by given 'name' and init arguments inside a given shared storage. """ if not self._cfg and self._cfg_share is not None: self._cfg = self._cfg_share.get(name) if not self._cfg: self._cfg = ConfigReaderUnshared(share_config=self._cfg_share, **self._cfg_share_kwargs) if self._cfg_share_basedir is not None: self._cfg.setBaseDir(self._cfg_share_basedir) self._cfg_share[name] = self._cfg else: self._cfg = ConfigReaderUnshared(**self._cfg_share_kwargs) def sections(self): try: return (n for n in self._cfg.sections() if not n.startswith('KNOWN/')) except AttributeError: return [] def has_section(self, sec): try: return self._cfg.has_section(sec) except AttributeError: return False def has_option(self, sec, opt, withDefault=True): return self._cfg.has_option(sec, opt) if withDefault \ else opt in self._cfg._sections.get(sec, {}) def merge_defaults(self, d): self._cfg.get_defaults().update(d) def merge_section(self, section, *args, **kwargs): try: return self._cfg.merge_section(section, *args, **kwargs) except AttributeError: raise NoSectionError(section) def options(self, section, withDefault=False): """Return a list of option names for the given section name. Parameter `withDefault` controls the include of names from section `[DEFAULT]` """ try: return self._cfg.options(section, withDefault) except AttributeError: raise NoSectionError(section) def get(self, sec, opt, raw=False, vars={}): try: return self._cfg.get(sec, opt, raw=raw, vars=vars) except AttributeError: raise NoSectionError(sec) def getOptions(self, section, *args, **kwargs): try: return self._cfg.getOptions(section, *args, **kwargs) except AttributeError: raise NoSectionError(section) class ConfigReaderUnshared(SafeConfigParserWithIncludes): """Unshared config reader (previously ConfigReader). Do not use this class (internal not shared/cached represenation). Use ConfigReader instead. """ DEFAULT_BASEDIR = '/etc/fail2ban' def __init__(self, basedir=None, *args, **kwargs): SafeConfigParserWithIncludes.__init__(self, *args, **kwargs) self.read_cfg_files = None self.setBaseDir(basedir) def setBaseDir(self, basedir): if basedir is None: basedir = ConfigReaderUnshared.DEFAULT_BASEDIR # stock system location self._basedir = basedir.rstrip('/') def getBaseDir(self): return self._basedir def read(self, filename): if not os.path.exists(self._basedir): raise ValueError("Base configuration directory %s does not exist " % self._basedir) if filename.startswith("./"): # pragma: no cover filename = os.path.abspath(filename) basename = os.path.join(self._basedir, filename) logSys.debug("Reading configs for %s under %s " , filename, self._basedir) config_files = [ basename + ".conf" ] # possible further customizations under a .conf.d directory config_dir = basename + '.d' config_files += sorted(glob.glob('%s/*.conf' % config_dir)) config_files.append(basename + ".local") config_files += sorted(glob.glob('%s/*.local' % config_dir)) # choose only existing ones config_files = list(filter(os.path.exists, config_files)) if len(config_files): # at least one config exists and accessible logSys.debug("Reading config files: %s", ', '.join(config_files)) config_files_read = SafeConfigParserWithIncludes.read(self, config_files) missed = [ cf for cf in config_files if cf not in config_files_read ] if missed: logSys.error("Could not read config files: %s", ', '.join(missed)) if config_files_read: return True logSys.error("Found no accessible config files for %r under %s", filename, self.getBaseDir()) return False else: logSys.error("Found no accessible config files for %r " % filename + (["under %s" % self.getBaseDir(), "among existing ones: " + ', '.join(config_files)][bool(len(config_files))])) return False ## # Read the options. # # Read the given option in the configuration file. Default values # are used... # Each options entry is composed of an array with: # [[type, name, default], ...] # Or it is a dict: # {name: [type, default], ...} def getOptions(self, sec, options, pOptions=None, shouldExist=False, convert=True): values = dict() if pOptions is None: pOptions = {} # Get only specified options: for opttype, optname, optvalue in _OptionsTemplateGen(options): if optname in pOptions: continue try: v = self.get(sec, optname, vars=pOptions) values[optname] = v if convert: conv = CONVERTER.get(opttype) if conv: if v is None: continue values[optname] = conv(v) except NoSectionError as e: if shouldExist: raise # No "Definition" section or wrong basedir logSys.error(e) values[optname] = optvalue # TODO: validate error handling here. except NoOptionError: if not optvalue is None: logSys.debug("'%s' not defined in '%s'. Using default one: %r" % (optname, sec, optvalue)) values[optname] = optvalue # elif logSys.getEffectiveLevel() <= logLevel: # logSys.log(logLevel, "Non essential option '%s' not defined in '%s'.", optname, sec) except ValueError: logSys.warning("Wrong value for '" + optname + "' in '" + sec + "'. Using default one: '" + repr(optvalue) + "'") values[optname] = optvalue return values class DefinitionInitConfigReader(ConfigReader): """Config reader for files with options grouped in [Definition] and [Init] sections. Is a base class for readers of filters and actions, where definitions in jails might provide custom values for options defined in [Init] section. """ _configOpts = [] def __init__(self, file_, jailName, initOpts, **kwargs): ConfigReader.__init__(self, **kwargs) if file_.startswith("./"): # pragma: no cover file_ = os.path.abspath(file_) self.setFile(file_) self.setJailName(jailName) self._initOpts = initOpts self._pOpts = dict() self._defCache = dict() def setFile(self, fileName): self._file = fileName self._initOpts = {} def getFile(self): return self._file def setJailName(self, jailName): self._jailName = jailName def getJailName(self): return self._jailName def read(self): return ConfigReader.read(self, self._file) # needed for fail2ban-regex that doesn't need fancy directories def readexplicit(self): if not self._cfg: self._create_unshared(self._file) return SafeConfigParserWithIncludes.read(self._cfg, self._file) def getOptions(self, pOpts, all=False): # overwrite static definition options with init values, supplied as # direct parameters from jail-config via action[xtra1="...", xtra2=...]: if not pOpts: pOpts = dict() if self._initOpts: pOpts = _merge_dicts(pOpts, self._initOpts) # type-convert only in combined (otherwise int/bool converting prevents substitution): self._opts = ConfigReader.getOptions( self, "Definition", self._configOpts, pOpts, convert=False) self._pOpts = pOpts if self.has_section("Init"): # get only own options (without options from default): getopt = lambda opt: self.get("Init", opt) for opt in self.options("Init", withDefault=False): if opt == '__name__': continue v = None if not opt.startswith('known/'): if v is None: v = getopt(opt) self._initOpts['known/'+opt] = v if opt not in self._initOpts: if v is None: v = getopt(opt) self._initOpts[opt] = v if all and self.has_section("Definition"): # merge with all definition options (and options from default), # bypass already converted option (so merge only new options): for opt in self.options("Definition"): if opt == '__name__' or opt in self._opts: continue self._opts[opt] = self.get("Definition", opt) def convertOptions(self, opts, configOpts): """Convert interpolated combined options to expected type. """ for opttype, optname, optvalue in _OptionsTemplateGen(configOpts): conv = CONVERTER.get(opttype) if conv: v = opts.get(optname) if v is None: continue try: opts[optname] = conv(v) except ValueError: logSys.warning("Wrong %s value %r for %r. Using default one: %r", opttype, v, optname, optvalue) opts[optname] = optvalue def getCombOption(self, optname): """Get combined definition option (as string) using pre-set and init options as preselection (values with higher precedence as specified in section). Can be used only after calling of getOptions. """ try: return self._defCache[optname] except KeyError: try: v = self._cfg.get_ex("Definition", optname, vars=self._pOpts) except (NoSectionError, NoOptionError, ValueError): v = None self._defCache[optname] = v return v def getCombined(self, ignore=()): combinedopts = self._opts if self._initOpts: combinedopts = _merge_dicts(combinedopts, self._initOpts) if not len(combinedopts): return {} # ignore conditional options: ignore = set(ignore).copy() for n in combinedopts: cond = SafeConfigParserWithIncludes.CONDITIONAL_RE.match(n) if cond: n, cond = cond.groups() ignore.add(n) # substiture options already specified direct: opts = substituteRecursiveTags(combinedopts, ignore=ignore, addrepl=self.getCombOption) if not opts: raise ValueError('recursive tag definitions unable to be resolved') # convert options after all interpolations: self.convertOptions(opts, self._configOpts) return opts def convert(self): raise NotImplementedError client/configurator.py000064400000005465152231502140011103 0ustar00# 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. # Author: Cyril Jaquier # __author__ = "Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" from .fail2banreader import Fail2banReader from .jailsreader import JailsReader from ..helpers import getLogger # Gets the instance of the logger. logSys = getLogger(__name__) class Configurator: def __init__(self, force_enable=False, share_config=None): self.__settings = dict() self.__streams = dict() # always share all config readers: if share_config is None: share_config = dict() self.__share_config = share_config self.__fail2ban = Fail2banReader(share_config=share_config) self.__jails = JailsReader(force_enable=force_enable, share_config=share_config) def Reload(self): # clear all shared handlers: self.__share_config.clear() def setBaseDir(self, folderName): self.__fail2ban.setBaseDir(folderName) self.__jails.setBaseDir(folderName) def getBaseDir(self): fail2ban_basedir = self.__fail2ban.getBaseDir() jails_basedir = self.__jails.getBaseDir() if fail2ban_basedir != jails_basedir: logSys.error("fail2ban.conf and jails.conf readers have differing " "basedirs: %r and %r. " "Returning the one for fail2ban.conf" % (fail2ban_basedir, jails_basedir)) return fail2ban_basedir def readEarly(self): self.__fail2ban.read() def readAll(self): self.readEarly() self.__jails.read() def getEarlyOptions(self): return self.__fail2ban.getEarlyOptions() def getOptions(self, jail=None, updateMainOpt=None, ignoreWrong=True): self.__fail2ban.getOptions(updateMainOpt) return self.__jails.getOptions(jail, ignoreWrong=ignoreWrong) def convertToProtocol(self, allow_no_files=False): self.__streams["general"] = self.__fail2ban.convert() self.__streams["jails"] = self.__jails.convert(allow_no_files=allow_no_files) def getConfigStream(self): cmds = list() for opt in self.__streams["general"]: cmds.append(opt) for opt in self.__streams["jails"]: cmds.append(opt) return cmds client/csocket.py000064400000005663152231502140010034 0ustar00# 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. # Author: Cyril Jaquier # __author__ = "Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" #from cPickle import dumps, loads, HIGHEST_PROTOCOL from pickle import dumps, loads, HIGHEST_PROTOCOL from ..protocol import CSPROTO import socket import sys class CSocket: def __init__(self, sock="/var/run/fail2ban/fail2ban.sock", timeout=-1): # Create an INET, STREAMing socket #self.csock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.__csock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) self.__deftout = self.__csock.gettimeout() if timeout != -1: self.settimeout(timeout) #self.csock.connect(("localhost", 2222)) self.__csock.connect(sock) def __del__(self): self.close() def send(self, msg, nonblocking=False, timeout=None): # Convert every list member to string obj = dumps(list(map(CSocket.convert, msg)), HIGHEST_PROTOCOL) self.__csock.send(obj) self.__csock.send(CSPROTO.END) return self.receive(self.__csock, nonblocking, timeout) def settimeout(self, timeout): self.__csock.settimeout(timeout if timeout != -1 else self.__deftout) def close(self): if not self.__csock: return try: self.__csock.sendall(CSPROTO.CLOSE + CSPROTO.END) self.__csock.shutdown(socket.SHUT_RDWR) except socket.error: # pragma: no cover - normally unreachable pass try: self.__csock.close() except socket.error: # pragma: no cover - normally unreachable pass self.__csock = None @staticmethod def convert(m): """Convert every "unexpected" member of message to string""" if isinstance(m, (str, bool, int, float, list, dict, set)): return m else: # pragma: no cover return str(m) @staticmethod def receive(sock, nonblocking=False, timeout=None): msg = CSPROTO.EMPTY if nonblocking: sock.setblocking(0) if timeout: sock.settimeout(timeout) bufsize = 1024 while msg.rfind(CSPROTO.END, -32) == -1: chunk = sock.recv(bufsize) if not len(chunk): raise socket.error(104, 'Connection reset by peer') if chunk == CSPROTO.END: break msg = msg + chunk if bufsize < 32768: bufsize <<= 1 return loads(msg) client/fail2banclient.py000064400000036472152231502140011260 0ustar00# 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. __author__ = "Fail2Ban Developers" __copyright__ = "Copyright (c) 2004-2008 Cyril Jaquier, 2012-2014 Yaroslav Halchenko, 2014-2016 Serg G. Brester" __license__ = "GPL" import os import shlex import signal import socket import sys import time import threading from threading import Thread from ..version import version from .csocket import CSocket from .beautifier import Beautifier from .fail2bancmdline import Fail2banCmdLine, ServerExecutionException, ExitException, \ logSys, exit, output from ..server.utils import Utils PROMPT = "fail2ban> " def _thread_name(): return threading.current_thread().__class__.__name__ def input_command(): # pragma: no cover return input(PROMPT) ## # # @todo This class needs cleanup. class Fail2banClient(Fail2banCmdLine, Thread): def __init__(self): Fail2banCmdLine.__init__(self) Thread.__init__(self) self._alive = True self._server = None self._beautifier = None def dispInteractive(self): output("Fail2Ban v" + version + " reads log file that contains password failure report") output("and bans the corresponding IP addresses using firewall rules.") output("") def __sigTERMhandler(self, signum, frame): # pragma: no cover # Print a new line because we probably come from wait output("") logSys.warning("Caught signal %d. Exiting" % signum) exit(255) def __ping(self, timeout=0.1): return self.__processCmd([["ping"] + ([timeout] if timeout != -1 else [])], False, timeout=timeout) @property def beautifier(self): if self._beautifier: return self._beautifier self._beautifier = Beautifier() return self._beautifier def __processCmd(self, cmd, showRet=True, timeout=-1): client = None try: beautifier = self.beautifier streamRet = True for c in cmd: beautifier.setInputCmd(c) try: if not client: client = CSocket(self._conf["socket"], timeout=timeout) elif timeout != -1: client.settimeout(timeout) if self._conf["verbose"] > 2: logSys.log(5, "CMD: %r", c) ret = client.send(c) if ret[0] == 0: logSys.log(5, "OK : %r", ret[1]) if showRet or c[0] in ('echo', 'server-status'): output(beautifier.beautify(ret[1])) else: logSys.error("NOK: %r", ret[1].args) if showRet: output(beautifier.beautifyError(ret[1])) streamRet = False except socket.error as e: if showRet or self._conf["verbose"] > 1: if showRet or c[0] != "ping": self.__logSocketError(e, c[0] == "ping") else: logSys.log(5, " -- %s failed -- %r", c, e) return False except Exception as e: # pragma: no cover if showRet or self._conf["verbose"] > 1: if self._conf["verbose"] > 1: logSys.exception(e) else: logSys.error(e) return False finally: # prevent errors by close during shutdown (on exit command): if client: try : client.close() except Exception as e: # pragma: no cover if showRet or self._conf["verbose"] > 1: logSys.debug(e) if showRet or c[0] in ('echo', 'server-status'): sys.stdout.flush() return streamRet def __logSocketError(self, prevError="", errorOnly=False): try: if os.access(self._conf["socket"], os.F_OK): # pragma: no cover # This doesn't check if path is a socket, # but socket.error should be raised if os.access(self._conf["socket"], os.W_OK): # Permissions look good, but socket.error was raised if errorOnly: logSys.error(prevError) else: logSys.error("%sUnable to contact server. Is it running?", ("[%s] " % prevError) if prevError else '') else: logSys.error("Permission denied to socket: %s," " (you must be root)", self._conf["socket"]) else: logSys.error("Failed to access socket path: %s." " Is fail2ban running?", self._conf["socket"]) except Exception as e: # pragma: no cover logSys.error("Exception while checking socket access: %s", self._conf["socket"]) logSys.error(e) ## def __prepareStartServer(self): if self.__ping(): logSys.error("Server already running") return None # Read the config ret, stream = self.readConfig() # Do not continue if configuration is not 100% valid if not ret: return None # Check already running if not self._conf["force"] and os.path.exists(self._conf["socket"]): logSys.error("Fail2ban seems to be in unexpected state (not running but the socket exists)") return None return [["server-stream", stream], ['server-status']] def _set_server(self, s): self._server = s ## def __startServer(self, background=True): from .fail2banserver import Fail2banServer # read configuration here (in client only, in server we do that in the config-thread): stream = self.__prepareStartServer() self._alive = True if not stream: return False # Start the server or just initialize started one: try: if background: # Start server daemon as fork of client process (or new process): Fail2banServer.startServerAsync(self._conf) # Send config stream to server: if not self.__processStartStreamAfterWait(stream, False): return False else: # In foreground mode we should make server/client communication in different threads: phase = dict() self.configureServer(phase=phase, stream=stream) # Mark current (main) thread as daemon: self.daemon = True # Start server direct here in main thread (not fork): self._server = Fail2banServer.startServerDirect(self._conf, False, self._set_server) if not phase.get('done', False): if self._server: # pragma: no cover self._server.quit() self._server = None exit(255) except ExitException: # pragma: no cover raise except Exception as e: # pragma: no cover output("") logSys.error("Exception while starting server " + ("background" if background else "foreground")) if self._conf["verbose"] > 1: logSys.exception(e) else: logSys.error(e) return False return True ## def configureServer(self, nonsync=True, phase=None, stream=None): # if asynchronous start this operation in the new thread: if nonsync: if phase is not None: # event for server ready flag: def _server_ready(): phase['start-ready'] = True logSys.log(5, ' server phase %s', phase) # notify waiting thread if server really ready self._conf['onstart'] = _server_ready th = Thread(target=Fail2banClient.configureServer, args=(self, False, phase, stream)) th.daemon = True th.start() # if we need to read configuration stream: if stream is None and phase is not None: # wait, do not continue if configuration is not 100% valid: Utils.wait_for(lambda: phase.get('ready', None) is not None, self._conf["timeout"], 0.001) logSys.log(5, ' server phase %s', phase) if not phase.get('start', False): raise ServerExecutionException('Async configuration of server failed') return True # prepare: read config, check configuration is valid, etc.: if phase is not None: phase['start'] = True logSys.log(5, ' client phase %s', phase) if stream is None: stream = self.__prepareStartServer() if phase is not None: phase['ready'] = phase['start'] = (True if stream else False) logSys.log(5, ' client phase %s', phase) if not stream: return False # wait a little bit for phase "start-ready" before enter active waiting: if phase is not None: Utils.wait_for(lambda: phase.get('start-ready', None) is not None, 0.5, 0.001) phase['configure'] = (True if stream else False) logSys.log(5, ' client phase %s', phase) # configure server with config stream: ret = self.__processStartStreamAfterWait(stream, False) if phase is not None: phase['done'] = ret return ret ## # Process a command line. # # Process one command line and exit. # @param cmd the command line def __processCommand(self, cmd): # wrap tuple to list (because could be modified here): if not isinstance(cmd, list): cmd = list(cmd) # process: if len(cmd) == 1 and cmd[0] == "start": ret = self.__startServer(self._conf["background"]) if not ret: return False return ret elif len(cmd) >= 1 and cmd[0] == "restart": # if restart jail - re-operate via "reload --restart ...": if len(cmd) > 1: cmd[0:1] = ["reload", "--restart"] return self.__processCommand(cmd) # restart server: if self._conf.get("interactive", False): output(' ## stop ... ') self.__processCommand(['stop']) if not self.__waitOnServer(False): # pragma: no cover logSys.error("Could not stop server") return False # in interactive mode reset config, to make full-reload if there something changed: if self._conf.get("interactive", False): output(' ## load configuration ... ') self.resetConf() ret = self.initCmdLine(self._argv) if ret is not None: return ret if self._conf.get("interactive", False): output(' ## start ... ') return self.__processCommand(['start']) elif len(cmd) >= 1 and cmd[0] == "reload": # reload options: opts = [] while len(cmd) >= 2: if cmd[1] in ('--restart', "--unban", "--if-exists"): opts.append(cmd[1]) del cmd[1] else: if len(cmd) > 2: logSys.error("Unexpected argument(s) for reload: %r", cmd[1:]) return False # stop options - jail name or --all break if self.__ping(timeout=-1): if len(cmd) == 1 or cmd[1] == '--all': jail = '--all' ret, stream = self.readConfig() else: jail = cmd[1] ret, stream = self.readConfig(jail) # Do not continue if configuration is not 100% valid if not ret: return False if self._conf.get("interactive", False): output(' ## reload ... ') # Reconfigure the server return self.__processCmd([['reload', jail, opts, stream]], True) else: logSys.error("Could not find server") return False elif len(cmd) > 1 and cmd[0] == "ping": return self.__processCmd([cmd], timeout=float(cmd[1])) else: return self.__processCmd([cmd]) def __processStartStreamAfterWait(self, *args): ret = False try: # Wait for the server to start if not self.__waitOnServer(): # pragma: no cover logSys.error("Could not find server, waiting failed") return False # Configure the server ret = self.__processCmd(*args) except ServerExecutionException as e: # pragma: no cover if self._conf["verbose"] > 1: logSys.exception(e) logSys.error("Could not start server. Maybe an old " "socket file is still present. Try to " "remove " + self._conf["socket"] + ". If " "you used fail2ban-client to start the " "server, adding the -x option will do it") if not ret and self._server: # stop on error (foreground, config read in another thread): self._server.quit() self._server = None return ret def __waitOnServer(self, alive=True, maxtime=None): if maxtime is None: maxtime = self._conf["timeout"] # Wait for the server to start (the server has 30 seconds to answer ping) starttime = time.time() logSys.log(5, "__waitOnServer: %r", (alive, maxtime)) sltime = 0.0125 / 2 test = lambda: os.path.exists(self._conf["socket"]) and self.__ping(timeout=sltime) with VisualWait(self._conf["verbose"]) as vis: while self._alive: runf = test() if runf == alive: return True waittime = time.time() - starttime logSys.log(5, " wait-time: %s", waittime) # Wonderful visual :) if waittime > 1: vis.heartbeat() # f end time reached: if waittime >= maxtime: raise ServerExecutionException("Failed to start server") # first 200ms faster: sltime = min(sltime * 2, 0.5 if waittime > 0.2 else 0.1) time.sleep(sltime) return False def start(self, argv): # Install signal handlers _prev_signals = {} if _thread_name() == '_MainThread': for s in (signal.SIGTERM, signal.SIGINT): _prev_signals[s] = signal.getsignal(s) signal.signal(s, self.__sigTERMhandler) try: # Command line options if self._argv is None: ret = self.initCmdLine(argv) if ret is not None: if ret: return True raise ServerExecutionException("Init of command line failed") # Commands args = self._args # Interactive mode if self._conf.get("interactive", False): try: import readline except ImportError: raise ServerExecutionException("Readline not available") try: ret = True if len(args) > 0: ret = self.__processCommand(args) if ret: readline.parse_and_bind("tab: complete") self.dispInteractive() while True: cmd = input_command() if cmd == "exit" or cmd == "quit": # Exit return True if cmd == "help": self.dispUsage() elif not cmd == "": try: self.__processCommand(shlex.split(cmd)) except Exception as e: # pragma: no cover if self._conf["verbose"] > 1: logSys.exception(e) else: logSys.error(e) except (EOFError, KeyboardInterrupt): # pragma: no cover output("") raise # Single command mode else: if len(args) < 1: self.dispUsage() return False return self.__processCommand(args) except Exception as e: if self._conf["verbose"] > 1: logSys.exception(e) else: logSys.error(e) return False finally: self._alive = False for s, sh in _prev_signals.items(): signal.signal(s, sh) class _VisualWait: """Small progress indication (as "wonderful visual") during waiting process """ pos = 0 delta = 1 def __init__(self, maxpos=10): self.maxpos = maxpos def __enter__(self): return self def __exit__(self, *args): if self.pos: sys.stdout.write('\r'+(' '*(35+self.maxpos))+'\r') sys.stdout.flush() def heartbeat(self): """Show or step for progress indicator """ if not self.pos: sys.stdout.write("\nINFO [#" + (' '*self.maxpos) + "] Waiting on the server...\r\x1b[8C") self.pos += self.delta if self.delta > 0: s = " #\x1b[1D" if self.pos > 1 else "# \x1b[2D" else: s = "\x1b[1D# \x1b[2D" sys.stdout.write(s) sys.stdout.flush() if self.pos > self.maxpos: self.delta = -1 elif self.pos < 2: self.delta = 1 class _NotVisualWait: """Mockup for invisible progress indication (not verbose) """ def __enter__(self): return self def __exit__(self, *args): pass def heartbeat(self): pass def VisualWait(verbose, *args, **kwargs): """Wonderful visual progress indication (if verbose) """ return _VisualWait(*args, **kwargs) if verbose > 1 else _NotVisualWait() def exec_command_line(argv): client = Fail2banClient() # Exit with correct return value if client.start(argv): exit(0) else: exit(255) client/fail2bancmdline.py000064400000026041152231502140011404 0ustar00# 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. __author__ = "Fail2Ban Developers" __copyright__ = "Copyright (c) 2004-2008 Cyril Jaquier, 2012-2014 Yaroslav Halchenko, 2014-2016 Serg G. Brester" __license__ = "GPL" import getopt import logging import os import sys from ..version import version, normVersion from ..protocol import printFormatted from ..helpers import getLogger, str2LogLevel, getVerbosityFormat, BrokenPipeError # Gets the instance of the logger. logSys = getLogger("fail2ban") def output(s): # pragma: no cover try: print(s) except (BrokenPipeError, IOError) as e: # pragma: no cover if e.errno != 32: # closed / broken pipe raise # Config parameters required to start fail2ban which can be also set via command line (overwrite fail2ban.conf), CONFIG_PARAMS = ("socket", "pidfile", "logtarget", "loglevel", "syslogsocket") # Used to signal - we are in test cases (ex: prevents change logging params, log capturing, etc) PRODUCTION = True MAX_WAITTIME = 30 class Fail2banCmdLine(): def __init__(self): self._argv = self._args = None self._configurator = None self.cleanConfOnly = False self.resetConf() def resetConf(self): self._conf = { "async": False, "conf": "/etc/fail2ban", "force": False, "background": True, "verbose": 1, "socket": None, "pidfile": None, "timeout": MAX_WAITTIME } @property def configurator(self): if self._configurator: return self._configurator # New configurator from .configurator import Configurator self._configurator = Configurator() # Set the configuration path self._configurator.setBaseDir(self._conf["conf"]) return self._configurator def applyMembers(self, obj): for o in obj.__dict__: self.__dict__[o] = obj.__dict__[o] def dispVersion(self, short=False): if not short: output("Fail2Ban v" + version) else: output(normVersion()) def dispUsage(self): """ Prints Fail2Ban command line options and exits """ caller = os.path.basename(self._argv[0]) output("Usage: "+caller+" [OPTIONS]" + (" " if not caller.endswith('server') else "")) output("") output("Fail2Ban v" + version + " reads log file that contains password failure report") output("and bans the corresponding IP addresses using firewall rules.") output("") output("Options:") output(" -c, --conf configuration directory") output(" -s, --socket socket path") output(" -p, --pidfile pidfile path") output(" --pname name of the process (main thread) to identify instance (default fail2ban-server)") output(" --loglevel logging level") output(" --logtarget logging target, use file-name or stdout, stderr, syslog or sysout.") output(" --syslogsocket auto|") output(" -d dump configuration. For debugging") output(" --dp, --dump-pretty dump the configuration using more human readable representation") output(" -t, --test test configuration (can be also specified with start parameters)") output(" -i interactive mode") output(" -v increase verbosity") output(" -q decrease verbosity") output(" -x force execution of the server (remove socket file)") output(" -b start server in background (default)") output(" -f start server in foreground") output(" --async start server in async mode (for internal usage only, don't read configuration)") output(" --timeout timeout to wait for the server (for internal usage only, don't read configuration)") output(" --str2sec convert time abbreviation format to seconds") output(" -h, --help display this help message") output(" -V, --version print the version (-V returns machine-readable short format)") if not caller.endswith('server'): output("") output("Command:") # Prints the protocol printFormatted() output("") output("Report bugs to https://github.com/fail2ban/fail2ban/issues") def __getCmdLineOptions(self, optList): """ Gets the command line options """ for opt in optList: o = opt[0] if o in ("-c", "--conf"): self._conf["conf"] = opt[1] elif o in ("-s", "--socket"): self._conf["socket"] = opt[1] elif o in ("-p", "--pidfile"): self._conf["pidfile"] = opt[1] elif o in ("-d", "--dp", "--dump-pretty"): self._conf["dump"] = True if o == "-d" else 2 elif o in ("-t", "--test"): self.cleanConfOnly = True self._conf["test"] = True elif o == "-v": self._conf["verbose"] += 1 elif o == "-q": self._conf["verbose"] -= 1 elif o == "-x": self._conf["force"] = True elif o == "-i": self._conf["interactive"] = True elif o == "-b": self._conf["background"] = True elif o == "-f": self._conf["background"] = False elif o == "--async": self._conf["async"] = True elif o == "--timeout": from ..server.mytime import MyTime self._conf["timeout"] = MyTime.str2seconds(opt[1]) elif o == "--str2sec": from ..server.mytime import MyTime output(MyTime.str2seconds(opt[1])) return True elif o in ("-h", "--help"): self.dispUsage() return True elif o in ("-V", "--version"): self.dispVersion(o == "-V") return True elif o.startswith("--"): # other long named params (see also resetConf) self._conf[ o[2:] ] = opt[1] return None def initCmdLine(self, argv): verbose = 1 try: # First time? initial = (self._argv is None) # Command line options self._argv = argv logSys.info("Using start params %s", argv[1:]) # Reads the command line options. try: cmdOpts = 'hc:s:p:xfbdtviqV' cmdLongOpts = ['loglevel=', 'logtarget=', 'syslogsocket=', 'test', 'async', 'conf=', 'pidfile=', 'pname=', 'socket=', 'timeout=', 'str2sec=', 'help', 'version', 'dp', 'dump-pretty'] optList, self._args = getopt.getopt(self._argv[1:], cmdOpts, cmdLongOpts) except getopt.GetoptError: self.dispUsage() return False ret = self.__getCmdLineOptions(optList) if ret is not None: return ret logSys.debug(" conf: %r, args: %r", self._conf, self._args) if initial and PRODUCTION: # pragma: no cover - can't test verbose = self._conf["verbose"] if verbose <= 0: logSys.setLevel(logging.ERROR) elif verbose == 1: logSys.setLevel(logging.WARNING) elif verbose == 2: logSys.setLevel(logging.INFO) elif verbose == 3: logSys.setLevel(logging.DEBUG) else: logSys.setLevel(logging.HEAVYDEBUG) # Add the default logging handler to dump to stderr logout = logging.StreamHandler(sys.stderr) # Custom log format for the verbose run (-1, because default verbosity here is 1): fmt = getVerbosityFormat(verbose-1) formatter = logging.Formatter(fmt) # tell the handler to use this format logout.setFormatter(formatter) logSys.addHandler(logout) # Set expected parameters (like socket, pidfile, etc) from configuration, # if those not yet specified, in which read configuration only if needed here: conf = None for o in CONFIG_PARAMS: if self._conf.get(o, None) is None: if not conf: self.configurator.readEarly() conf = self.configurator.getEarlyOptions() if o in conf: self._conf[o] = conf[o] logSys.info("Using socket file %s", self._conf["socket"]) # Check log-level before start (or transmit to server), to prevent error in background: llev = str2LogLevel(self._conf["loglevel"]) logSys.info("Using pid file %s, [%s] logging to %s", self._conf["pidfile"], logging.getLevelName(llev), self._conf["logtarget"]) readcfg = True if self._conf.get("dump", False): if readcfg: ret, stream = self.readConfig() readcfg = False if stream is not None: self.dumpConfig(stream, self._conf["dump"] == 2) else: # pragma: no cover output("ERROR: The configuration stream failed because of the invalid syntax.") if not self._conf.get("test", False): return ret if self._conf.get("test", False): if readcfg: readcfg = False ret, stream = self.readConfig() if not ret: raise ServerExecutionException("ERROR: test configuration failed") # exit after test if no commands specified (test only): if not len(self._args): output("OK: configuration test is successful") return ret # Nothing to do here, process in client/server return None except ServerExecutionException: raise except Exception as e: output("ERROR: %s" % (e,)) if verbose > 2: logSys.exception(e) return False def readConfig(self, jail=None): # Read the configuration # TODO: get away from stew of return codes and exception # handling -- handle via exceptions stream = None try: self.configurator.Reload() self.configurator.readAll() ret = self.configurator.getOptions(jail, self._conf, ignoreWrong=not self.cleanConfOnly) self.configurator.convertToProtocol( allow_no_files=self._conf.get("dump", False)) stream = self.configurator.getConfigStream() except Exception as e: logSys.error("Failed during configuration: %s" % e) ret = False return ret, stream @staticmethod def dumpConfig(cmd, pretty=False): if pretty: from pprint import pformat def _output(s): output(pformat(s, width=1000, indent=2)) else: _output = output for c in cmd: _output(c) return True # # _exit is made to ease mocking out of the behaviour in tests, # since method is also exposed in API via globally bound variable @staticmethod def _exit(code=0): # implicit flush without to produce broken pipe error (32): sys.stderr.close() try: sys.stdout.flush() # exit: if hasattr(sys, 'exit') and sys.exit: sys.exit(code) else: os._exit(code) except (BrokenPipeError, IOError) as e: # pragma: no cover if e.errno != 32: # closed / broken pipe raise @staticmethod def exit(code=0): logSys.debug("Exit with code %s", code) # because of possible buffered output in python, we should flush it before exit: logging.shutdown() # exit Fail2banCmdLine._exit(code) # global exit handler: exit = Fail2banCmdLine.exit class ExitException(Exception): pass class ServerExecutionException(Exception): pass client/fail2banreader.py000064400000005563152231502140011241 0ustar00# 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. # Author: Cyril Jaquier # __author__ = "Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" from .configreader import ConfigReader from ..helpers import getLogger, str2LogLevel # Gets the instance of the logger. logSys = getLogger(__name__) class Fail2banReader(ConfigReader): def __init__(self, **kwargs): ConfigReader.__init__(self, **kwargs) def read(self): ConfigReader.read(self, "fail2ban") def getEarlyOptions(self): opts = [ ["string", "socket", "/var/run/fail2ban/fail2ban.sock"], ["string", "pidfile", "/var/run/fail2ban/fail2ban.pid"], ["string", "loglevel", "INFO"], ["string", "logtarget", "/var/log/fail2ban.log"], ["string", "syslogsocket", "auto"] ] return ConfigReader.getOptions(self, "Definition", opts) def getOptions(self, updateMainOpt=None): opts = [["string", "loglevel", "INFO" ], ["string", "logtarget", "STDERR"], ["string", "syslogsocket", "auto"], ["string", "allowipv6", "auto"], ["string", "dbfile", "/var/lib/fail2ban/fail2ban.sqlite3"], ["int", "dbmaxmatches", None], ["string", "dbpurgeage", "1d"]] self.__opts = ConfigReader.getOptions(self, "Definition", opts) if updateMainOpt: self.__opts.update(updateMainOpt) # check given log-level: str2LogLevel(self.__opts.get('loglevel', 0)) # thread options: opts = [["int", "stacksize", ], ] if self.has_section("Thread"): thopt = ConfigReader.getOptions(self, "Thread", opts) if thopt: self.__opts['thread'] = thopt def convert(self): # Ensure logtarget/level set first so any db errors are captured # Also dbfile should be set before all other database options. # So adding order indices into items, to be stripped after sorting, upon return order = {"thread":0, "syslogsocket":11, "loglevel":12, "logtarget":13, "allowipv6": 14, "dbfile":50, "dbmaxmatches":51, "dbpurgeage":51} stream = list() for opt in self.__opts: if opt in order: stream.append((order[opt], ["set", opt, self.__opts[opt]])) return [opt[1] for opt in sorted(stream)] client/fail2banregex.py000064400000070206152231502140011105 0ustar00# 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. """ Fail2Ban reads log file that contains password failure report and bans the corresponding IP addresses using firewall rules. This tools can test regular expressions for "fail2ban". """ __author__ = "Fail2Ban Developers" __copyright__ = """Copyright (c) 2004-2008 Cyril Jaquier, 2008- Fail2Ban Contributors Copyright of modifications held by their respective authors. Licensed under the GNU General Public License v2 (GPL). Written by Cyril Jaquier . Many contributions by Yaroslav O. Halchenko, Steven Hiscocks, Sergey G. Brester (sebres).""" __license__ = "GPL" import getopt import logging import re import os import shlex import sys import time import urllib.request, urllib.parse, urllib.error from optparse import OptionParser, Option from configparser import NoOptionError, NoSectionError, MissingSectionHeaderError try: # pragma: no cover from ..server.filtersystemd import FilterSystemd except ImportError: FilterSystemd = None from ..version import version, normVersion from .filterreader import FilterReader from ..server.filter import Filter, FileContainer, MyTime from ..server.failregex import Regex, RegexException from ..helpers import str2LogLevel, getVerbosityFormat, FormatterWithTraceBack, getLogger, \ extractOptions, PREFER_ENC # Gets the instance of the logger. logSys = getLogger("fail2ban") def debuggexURL(sample, regex, multiline=False, useDns="yes"): args = { 're': Regex._resolveHostTag(regex, useDns=useDns), 'str': sample, 'flavor': 'python' } if multiline: args['flags'] = 'm' return 'https://www.debuggex.com/?' + urllib.parse.urlencode(args) def output(args): # pragma: no cover (overriden in test-cases) print(args) def shortstr(s, l=53): """Return shortened string """ if len(s) > l: return s[:l-3] + '...' return s def pprint_list(l, header=None): if not len(l): return if header: s = "|- %s\n" % header else: s = '' output( s + "| " + "\n| ".join(l) + '\n`-' ) def journal_lines_gen(flt, myjournal): # pragma: no cover while True: try: entry = myjournal.get_next() except OSError: continue if not entry: break yield flt.formatJournalEntry(entry) def dumpNormVersion(*args): output(normVersion()) sys.exit(0) usage = lambda: "%s [OPTIONS] [IGNOREREGEX]" % sys.argv[0] class _f2bOptParser(OptionParser): def format_help(self, *args, **kwargs): """ Overwritten format helper with full ussage.""" self.usage = '' return "Usage: " + usage() + "\n" + __doc__ + """ LOG: string a string representing a log line filename path to a log file (/var/log/auth.log) systemd-journal search systemd journal (systemd-python required), optionally with backend parameters, see `man jail.conf` for usage and examples (systemd-journal[journalflags=1]). REGEX: string a string representing a 'failregex' filter name of filter, optionally with options (sshd[mode=aggressive]) filename path to a filter file (filter.d/sshd.conf) IGNOREREGEX: string a string representing an 'ignoreregex' filename path to a filter file (filter.d/sshd.conf) \n""" + OptionParser.format_help(self, *args, **kwargs) + """\n Report bugs to https://github.com/fail2ban/fail2ban/issues\n """ + __copyright__ + "\n" def get_opt_parser(): # use module docstring for help output p = _f2bOptParser( usage=usage(), version="%prog " + version) p.add_options([ Option("-c", "--config", default='/etc/fail2ban', help="set alternate config directory"), Option("-d", "--datepattern", help="set custom pattern used to match date/times"), Option("--timezone", "--TZ", action='store', default=None, help="set time-zone used by convert time format"), Option("-e", "--encoding", default=PREFER_ENC, help="File encoding. Default: system locale"), Option("-r", "--raw", action='store_true', default=False, help="Raw hosts, don't resolve dns"), Option("--usedns", action='store', default=None, help="DNS specified replacement of tags in regexp " "('yes' - matches all form of hosts, 'no' - IP addresses only)"), Option("-L", "--maxlines", type=int, default=0, help="maxlines for multi-line regex."), Option("-m", "--journalmatch", help="journalctl style matches overriding filter file. " "\"systemd-journal\" only"), Option('-l', "--log-level", dest="log_level", default='critical', help="Log level for the Fail2Ban logger to use"), Option('-V', action="callback", callback=dumpNormVersion, help="get version in machine-readable short format"), Option('-v', '--verbose', action="count", dest="verbose", default=0, help="Increase verbosity"), Option("--verbosity", action="store", dest="verbose", type=int, help="Set numerical level of verbosity (0..4)"), Option("--verbose-date", "--VD", action='store_true', help="Verbose date patterns/regex in output"), Option("-D", "--debuggex", action='store_true', help="Produce debuggex.com urls for debugging there"), Option("--no-check-all", action="store_false", dest="checkAllRegex", default=True, help="Disable check for all regex's"), Option("-o", "--out", action="store", dest="out", default=None, help="Set token to print failure information only (row, id, ip, msg, host, ip4, ip6, dns, matches, ...)"), Option("--print-no-missed", action='store_true', help="Do not print any missed lines"), Option("--print-no-ignored", action='store_true', help="Do not print any ignored lines"), Option("--print-all-matched", action='store_true', help="Print all matched lines"), Option("--print-all-missed", action='store_true', help="Print all missed lines, no matter how many"), Option("--print-all-ignored", action='store_true', help="Print all ignored lines, no matter how many"), Option("-t", "--log-traceback", action='store_true', help="Enrich log-messages with compressed tracebacks"), Option("--full-traceback", action='store_true', help="Either to make the tracebacks full, not compressed (as by default)"), ]) return p class RegexStat(object): def __init__(self, failregex): self._stats = 0 self._failregex = failregex self._ipList = list() def __str__(self): return "%s(%r) %d failed: %s" \ % (self.__class__, self._failregex, self._stats, self._ipList) def inc(self): self._stats += 1 def getStats(self): return self._stats def getFailRegex(self): return self._failregex def appendIP(self, value): self._ipList.append(value) def getIPList(self): return self._ipList class LineStats(object): """Just a convenience container for stats """ def __init__(self, opts): self.tested = self.matched = 0 self.matched_lines = [] self.missed = 0 self.missed_lines = [] self.ignored = 0 self.ignored_lines = [] if opts.debuggex: self.matched_lines_timeextracted = [] self.missed_lines_timeextracted = [] self.ignored_lines_timeextracted = [] def __str__(self): return "%(tested)d lines, %(ignored)d ignored, %(matched)d matched, %(missed)d missed" % self # just for convenient str def __getitem__(self, key): return getattr(self, key) if hasattr(self, key) else '' class Fail2banRegex(object): def __init__(self, opts): # set local protected members from given options: self.__dict__.update(dict(('_'+o,v) for o,v in opts.__dict__.items())) self._opts = opts self._maxlines_set = False # so we allow to override maxlines in cmdline self._datepattern_set = False self._journalmatch = None self.share_config=dict() self._filter = Filter(None) self._prefREMatched = 0 self._prefREGroups = list() self._ignoreregex = list() self._failregex = list() self._time_elapsed = None self._line_stats = LineStats(opts) if opts.maxlines: self.setMaxLines(opts.maxlines) else: self._maxlines = 20 if opts.journalmatch is not None: self.setJournalMatch(shlex.split(opts.journalmatch)) if opts.timezone: self._filter.setLogTimeZone(opts.timezone) self._filter.checkFindTime = False if True: # not opts.out: MyTime.setAlternateNow(0); # accept every date (years from 19xx up to end of current century, '%ExY' and 'Exy' patterns) from ..server.strptime import _updateTimeRE _updateTimeRE() if opts.datepattern: self.setDatePattern(opts.datepattern) if opts.usedns: self._filter.setUseDns(opts.usedns) self._filter.returnRawHost = opts.raw self._filter.checkAllRegex = opts.checkAllRegex and not opts.out # ignore pending (without ID/IP), added to matches if it hits later (if ID/IP can be retreved) self._filter.ignorePending = bool(opts.out) # callback to increment ignored RE's by index (during process): self._filter.onIgnoreRegex = self._onIgnoreRegex self._backend = 'auto' def output(self, line): if not self._opts.out: output(line) def encode_line(self, line): return line.encode(self._encoding, 'ignore') def setDatePattern(self, pattern): if not self._datepattern_set: self._filter.setDatePattern(pattern) self._datepattern_set = True if pattern is not None: self.output( "Use datepattern : %s : %s" % ( pattern, self._filter.getDatePattern()[1], ) ) def setMaxLines(self, v): if not self._maxlines_set: self._filter.setMaxLines(int(v)) self._maxlines_set = True self.output( "Use maxlines : %d" % self._filter.getMaxLines() ) def setJournalMatch(self, v): self._journalmatch = v def _dumpRealOptions(self, reader, fltOpt): realopts = {} combopts = reader.getCombined() # output all options that are specified in filter-argument as well as some special (mostly interested): for k in ['logtype', 'datepattern'] + list(fltOpt.keys()): # combined options win, but they contain only a sub-set in filter expected keys, # so get the rest from definition section: try: realopts[k] = combopts[k] if k in combopts else reader.get('Definition', k) except NoOptionError: # pragma: no cover pass self.output("Real filter options : %r" % realopts) def readRegex(self, value, regextype): assert(regextype in ('fail', 'ignore')) regex = regextype + 'regex' # try to check - we've case filter?[options...]?: basedir = self._opts.config fltName = value fltFile = None fltOpt = {} if regextype == 'fail': if re.search(r'(?ms)^/{0,3}[\w/_\-.]+(?:\[.*\])?$', value): try: fltName, fltOpt = extractOptions(value) if "." in fltName[~5:]: tryNames = (fltName,) else: tryNames = (fltName, fltName + '.conf', fltName + '.local') for fltFile in tryNames: if not "/" in fltFile: if os.path.basename(basedir) == 'filter.d': fltFile = os.path.join(basedir, fltFile) else: fltFile = os.path.join(basedir, 'filter.d', fltFile) else: basedir = os.path.dirname(fltFile) if os.path.isfile(fltFile): break fltFile = None except Exception as e: output("ERROR: Wrong filter name or options: %s" % (str(e),)) output(" while parsing: %s" % (value,)) if self._verbose: raise(e) return False # if it is filter file: if fltFile is not None: if (basedir == self._opts.config or os.path.basename(basedir) == 'filter.d' or ("." not in fltName[~5:] and "/" not in fltName) ): ## within filter.d folder - use standard loading algorithm to load filter completely (with .local etc.): if os.path.basename(basedir) == 'filter.d': basedir = os.path.dirname(basedir) fltName = os.path.splitext(os.path.basename(fltName))[0] self.output( "Use %11s filter file : %s, basedir: %s" % (regex, fltName, basedir) ) else: ## foreign file - readexplicit this file and includes if possible: self.output( "Use %11s file : %s" % (regex, fltName) ) basedir = None if not os.path.isabs(fltName): # avoid join with "filter.d" inside FilterReader fltName = os.path.abspath(fltName) if fltOpt: self.output( "Use filter options : %r" % fltOpt ) reader = FilterReader(fltName, 'fail2ban-regex-jail', fltOpt, share_config=self.share_config, basedir=basedir) ret = None try: if basedir is not None: ret = reader.read() else: ## foreign file - readexplicit this file and includes if possible: reader.setBaseDir(None) ret = reader.readexplicit() except Exception as e: output("Wrong config file: %s" % (str(e),)) if self._verbose: raise(e) if not ret: output( "ERROR: failed to load filter %s" % value ) return False # set backend-related options (logtype): reader.applyAutoOptions(self._backend) # get, interpolate and convert options: reader.getOptions(None) # show real options if expected: if self._verbose > 1 or logSys.getEffectiveLevel()<=logging.DEBUG: self._dumpRealOptions(reader, fltOpt) # to stream: readercommands = reader.convert() regex_values = {} for opt in readercommands: if opt[0] == 'multi-set': optval = opt[3] elif opt[0] == 'set': optval = opt[3:] else: # pragma: no cover continue try: if opt[2] == "prefregex": for optval in optval: self._filter.prefRegex = optval elif opt[2] == "addfailregex": stor = regex_values.get('fail') if not stor: stor = regex_values['fail'] = list() for optval in optval: stor.append(RegexStat(optval)) #self._filter.addFailRegex(optval) elif opt[2] == "addignoreregex": stor = regex_values.get('ignore') if not stor: stor = regex_values['ignore'] = list() for optval in optval: stor.append(RegexStat(optval)) #self._filter.addIgnoreRegex(optval) elif opt[2] == "maxlines": for optval in optval: self.setMaxLines(optval) elif opt[2] == "datepattern": for optval in optval: self.setDatePattern(optval) elif opt[2] == "addjournalmatch": # pragma: no cover if self._opts.journalmatch is None: self.setJournalMatch(optval) except ValueError as e: # pragma: no cover output( "ERROR: Invalid value for %s (%r) " \ "read from %s: %s" % (opt[2], optval, value, e) ) return False else: self.output( "Use %11s line : %s" % (regex, shortstr(value)) ) regex_values = {regextype: [RegexStat(value)]} for regextype, regex_values in regex_values.items(): regex = regextype + 'regex' setattr(self, "_" + regex, regex_values) for regex in regex_values: getattr( self._filter, 'add%sRegex' % regextype.title())(regex.getFailRegex()) return True def _onIgnoreRegex(self, idx, ignoreRegex): self._lineIgnored = True self._ignoreregex[idx].inc() def testRegex(self, line, date=None): orgLineBuffer = self._filter._Filter__lineBuffer # duplicate line buffer (list can be changed inplace during processLine): if self._filter.getMaxLines() > 1: orgLineBuffer = orgLineBuffer[:] fullBuffer = len(orgLineBuffer) >= self._filter.getMaxLines() is_ignored = self._lineIgnored = False try: found = self._filter.processLine(line, date) lines = [] ret = [] for match in found: if not self._opts.out: # Append True/False flag depending if line was matched by # more than one regex match.append(len(ret)>1) regex = self._failregex[match[0]] regex.inc() regex.appendIP(match) if not match[3].get('nofail'): ret.append(match) else: is_ignored = True if self._opts.out: # (formated) output - don't need stats: return None, ret, None # prefregex stats: if self._filter.prefRegex: pre = self._filter.prefRegex if pre.hasMatched(): self._prefREMatched += 1 if self._verbose: if len(self._prefREGroups) < self._maxlines: self._prefREGroups.append(pre.getGroups()) else: if len(self._prefREGroups) == self._maxlines: self._prefREGroups.append('...') except RegexException as e: # pragma: no cover output( 'ERROR: %s' % e ) return None, 0, None if self._filter.getMaxLines() > 1: for bufLine in orgLineBuffer[int(fullBuffer):]: if bufLine not in self._filter._Filter__lineBuffer: try: self._line_stats.missed_lines.pop( self._line_stats.missed_lines.index("".join(bufLine))) if self._debuggex: self._line_stats.missed_lines_timeextracted.pop( self._line_stats.missed_lines_timeextracted.index( "".join(bufLine[::2]))) except ValueError: pass # if buffering - add also another lines from match: if self._print_all_matched: if not self._debuggex: self._line_stats.matched_lines.append("".join(bufLine)) else: lines.append(bufLine[0] + bufLine[2]) self._line_stats.matched += 1 self._line_stats.missed -= 1 if lines: # pre-lines parsed in multiline mode (buffering) lines.append(self._filter.processedLine()) line = "\n".join(lines) return line, ret, (is_ignored or self._lineIgnored) def _prepaireOutput(self): """Prepares output- and fetch-function corresponding given '--out' option (format)""" ofmt = self._opts.out if ofmt in ('id', 'fid'): def _out(ret): for r in ret: output(r[1]) elif ofmt == 'ip': def _out(ret): for r in ret: output(r[3].get('ip', r[1])) elif ofmt == 'msg': def _out(ret): for r in ret: for r in r[3].get('matches'): if not isinstance(r, str): r = ''.join(r for r in r) output(r) elif ofmt == 'row': def _out(ret): for r in ret: output('[%r,\t%r,\t%r],' % (r[1],r[2],dict((k,v) for k, v in r[3].items() if k != 'matches'))) elif '<' not in ofmt: def _out(ret): for r in ret: output(r[3].get(ofmt)) else: # extended format with tags substitution: from ..server.actions import Actions, CommandAction, BanTicket def _escOut(t, v): # use safe escape (avoid inject on pseudo tag "\x00msg\x00"): if t not in ('msg',): return v.replace('\x00', '\\x00') return v def _out(ret): rows = [] wrap = {'NL':0} for r in ret: ticket = BanTicket(r[1], time=r[2], data=r[3]) aInfo = Actions.ActionInfo(ticket) # if msg tag is used - output if single line (otherwise let it as is to wrap multilines later): def _get_msg(self): if not wrap['NL'] and len(r[3].get('matches', [])) <= 1: return self['matches'] else: # pseudo tag for future replacement: wrap['NL'] = 1 return "\x00msg\x00" aInfo['msg'] = _get_msg # not recursive interpolation (use safe escape): v = CommandAction.replaceDynamicTags(ofmt, aInfo, escapeVal=_escOut) if wrap['NL']: # contains multiline tags (msg): rows.append((r, v)) continue output(v) # wrap multiline tag (msg) interpolations to single line: for r, v in rows: for r in r[3].get('matches'): if not isinstance(r, str): r = ''.join(r for r in r) r = v.replace("\x00msg\x00", r) output(r) return _out def process(self, test_lines): t0 = time.time() if self._opts.out: # get out function out = self._prepaireOutput() for line in test_lines: if isinstance(line, tuple): line_datetimestripped, ret, is_ignored = self.testRegex(line[0], line[1]) line = "".join(line[0]) else: line = line.rstrip('\r\n') if line.startswith('#') or not line: # skip comment and empty lines continue line_datetimestripped, ret, is_ignored = self.testRegex(line) if self._opts.out: # (formated) output: if len(ret) > 0 and not is_ignored: out(ret) continue if is_ignored: self._line_stats.ignored += 1 if not self._print_no_ignored and (self._print_all_ignored or self._line_stats.ignored <= self._maxlines + 1): self._line_stats.ignored_lines.append(line) if self._debuggex: self._line_stats.ignored_lines_timeextracted.append(line_datetimestripped) elif len(ret) > 0: self._line_stats.matched += 1 if self._print_all_matched: self._line_stats.matched_lines.append(line) if self._debuggex: self._line_stats.matched_lines_timeextracted.append(line_datetimestripped) else: self._line_stats.missed += 1 if not self._print_no_missed and (self._print_all_missed or self._line_stats.missed <= self._maxlines + 1): self._line_stats.missed_lines.append(line) if self._debuggex: self._line_stats.missed_lines_timeextracted.append(line_datetimestripped) self._line_stats.tested += 1 self._time_elapsed = time.time() - t0 def printLines(self, ltype): lstats = self._line_stats assert(lstats.missed == lstats.tested - (lstats.matched + lstats.ignored)) lines = lstats[ltype] l = lstats[ltype + '_lines'] multiline = self._filter.getMaxLines() > 1 if lines: header = "%s line(s):" % (ltype.capitalize(),) if self._debuggex: if ltype == 'missed' or ltype == 'matched': regexlist = self._failregex else: regexlist = self._ignoreregex l = lstats[ltype + '_lines_timeextracted'] if lines < self._maxlines or getattr(self, '_print_all_' + ltype): ans = [[]] for arg in [l, regexlist]: ans = [ x + [y] for x in ans for y in arg ] b = [a[0] + ' | ' + a[1].getFailRegex() + ' | ' + debuggexURL(self.encode_line(a[0]), a[1].getFailRegex(), multiline, self._opts.usedns) for a in ans] pprint_list([x.rstrip() for x in b], header) else: output( "%s too many to print. Use --print-all-%s " \ "to print all %d lines" % (header, ltype, lines) ) elif lines < self._maxlines or getattr(self, '_print_all_' + ltype): pprint_list([x.rstrip() for x in l], header) else: output( "%s too many to print. Use --print-all-%s " \ "to print all %d lines" % (header, ltype, lines) ) def printStats(self): if self._opts.out: return True output( "" ) output( "Results" ) output( "=======" ) def print_failregexes(title, failregexes): # Print title total, out = 0, [] for cnt, failregex in enumerate(failregexes): match = failregex.getStats() total += match if (match or self._verbose): out.append("%2d) [%d] %s" % (cnt+1, match, failregex.getFailRegex())) if self._verbose and len(failregex.getIPList()): for ip in failregex.getIPList(): timeTuple = time.localtime(ip[2]) timeString = time.strftime("%a %b %d %H:%M:%S %Y", timeTuple) out.append( " %s %s%s" % ( ip[1], timeString, ip[-1] and " (multiple regex matched)" or "")) output( "\n%s: %d total" % (title, total) ) pprint_list(out, " #) [# of hits] regular expression") return total # Print prefregex: if self._filter.prefRegex: #self._filter.prefRegex.hasMatched() pre = self._filter.prefRegex out = [pre.getRegex()] if self._verbose: for grp in self._prefREGroups: out.append(" %s" % (grp,)) output( "\n%s: %d total" % ("Prefregex", self._prefREMatched) ) pprint_list(out) # Print regex's: total = print_failregexes("Failregex", self._failregex) _ = print_failregexes("Ignoreregex", self._ignoreregex) if self._filter.dateDetector is not None: output( "\nDate template hits:" ) out = [] for template in self._filter.dateDetector.templates: if self._verbose or template.hits: out.append("[%d] %s" % (template.hits, template.name)) if self._verbose_date: out.append(" # weight: %.3f (%.3f), pattern: %s" % ( template.weight, template.template.weight, getattr(template, 'pattern', ''),)) out.append(" # regex: %s" % (getattr(template, 'regex', ''),)) pprint_list(out, "[# of hits] date format") output( "\nLines: %s" % self._line_stats, ) if self._time_elapsed is not None: output( "[processed in %.2f sec]" % self._time_elapsed, ) output( "" ) if self._print_all_matched: self.printLines('matched') if not self._print_no_ignored: self.printLines('ignored') if not self._print_no_missed: self.printLines('missed') return True def start(self, args): cmd_log, cmd_regex = args[:2] if cmd_log.startswith("systemd-journal"): # pragma: no cover self._backend = 'systemd' try: if not self.readRegex(cmd_regex, 'fail'): # pragma: no cover return False if len(args) == 3 and not self.readRegex(args[2], 'ignore'): # pragma: no cover return False except RegexException as e: output( 'ERROR: %s' % e ) return False if os.path.isfile(cmd_log): try: test_lines = FileContainer(cmd_log, self._encoding, doOpen=True) self.output( "Use log file : %s" % cmd_log ) self.output( "Use encoding : %s" % self._encoding ) except IOError as e: # pragma: no cover output( e ) return False elif cmd_log.startswith("systemd-journal"): # pragma: no cover if not FilterSystemd: output( "Error: systemd library not found. Exiting..." ) return False self.output( "Use systemd journal" ) self.output( "Use encoding : %s" % self._encoding ) backend, beArgs = extractOptions(cmd_log) flt = FilterSystemd(None, **beArgs) flt.setLogEncoding(self._encoding) myjournal = flt.getJournalReader() journalmatch = self._journalmatch self.setDatePattern(None) if journalmatch: flt.addJournalMatch(journalmatch) self.output( "Use journal match : %s" % " ".join(journalmatch) ) test_lines = journal_lines_gen(flt, myjournal) else: # if single line parsing (without buffering) if self._filter.getMaxLines() <= 1 and '\n' not in cmd_log: self.output( "Use single line : %s" % shortstr(cmd_log.replace("\n", r"\n")) ) test_lines = [ cmd_log ] else: # multi line parsing (with and without buffering) test_lines = cmd_log.split("\n") self.output( "Use multi line : %s line(s)" % len(test_lines) ) for i, l in enumerate(test_lines): if i >= 5: self.output( "| ..." ); break self.output( "| %2.2s: %s" % (i+1, shortstr(l)) ) self.output( "`-" ) self.output( "" ) self.process(test_lines) if not self.printStats(): return False return True def exec_command_line(*args): logging.exitOnIOError = True parser = get_opt_parser() (opts, args) = parser.parse_args(*args) errors = [] if opts.print_no_missed and opts.print_all_missed: # pragma: no cover errors.append("ERROR: --print-no-missed and --print-all-missed are mutually exclusive.") if opts.print_no_ignored and opts.print_all_ignored: # pragma: no cover errors.append("ERROR: --print-no-ignored and --print-all-ignored are mutually exclusive.") # We need 2 or 3 parameters if not len(args) in (2, 3): errors.append("ERROR: provide both and .") if errors: parser.print_help() sys.stderr.write("\n" + "\n".join(errors) + "\n") sys.exit(255) if not opts.out: output( "" ) output( "Running tests" ) output( "=============" ) output( "" ) # Log level (default critical): opts.log_level = str2LogLevel(opts.log_level) logSys.setLevel(opts.log_level) # Add the default logging handler stdout = logging.StreamHandler(sys.stdout) fmt = '%(levelname)-1.1s: %(message)s' if opts.verbose <= 1 else ' %(message)s' if opts.log_traceback: Formatter = FormatterWithTraceBack fmt = (opts.full_traceback and ' %(tb)s' or ' %(tbc)s') + fmt else: Formatter = logging.Formatter # Custom log format for the verbose tests runs stdout.setFormatter(Formatter(getVerbosityFormat(opts.verbose, fmt))) logSys.addHandler(stdout) try: fail2banRegex = Fail2banRegex(opts) except Exception as e: if opts.verbose or logSys.getEffectiveLevel()<=logging.DEBUG: logSys.critical(e, exc_info=True) else: output( 'ERROR: %s' % e ) sys.exit(255) if not fail2banRegex.start(args): sys.exit(255) client/fail2banserver.py000064400000017012152231502140011275 0ustar00# 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. __author__ = "Fail2Ban Developers" __copyright__ = "Copyright (c) 2004-2008 Cyril Jaquier, 2012-2014 Yaroslav Halchenko, 2014-2016 Serg G. Brester" __license__ = "GPL" import os import sys from .fail2bancmdline import Fail2banCmdLine, ServerExecutionException, \ logSys, PRODUCTION, exit SERVER = "fail2ban-server" ## # \mainpage Fail2Ban # # \section Introduction # class Fail2banServer(Fail2banCmdLine): # def __init__(self): # Fail2banCmdLine.__init__(self) ## # Start Fail2Ban server in main thread without fork (direct, it can fork itself in Server if daemon=True). # # Start the Fail2ban server in background/foreground (daemon mode or not). @staticmethod def startServerDirect(conf, daemon=True, setServer=None): logSys.debug(" direct starting of server in %s, deamon: %s", os.getpid(), daemon) from ..server.server import Server server = None try: # Start it in foreground (current thread, not new process), # server object will internally fork self if daemon is True server = Server(daemon) # notify caller - set server handle: if setServer: setServer(server) # run: server.start(conf["socket"], conf["pidfile"], conf["force"], conf=conf) except Exception as e: # pragma: no cover try: if server: server.quit() except Exception as e2: if conf["verbose"] > 1: logSys.exception(e2) raise finally: # notify waiting thread server ready resp. done (background execution, error case, etc): if conf.get('onstart'): conf['onstart']() return server ## # Start Fail2Ban server. # # Start the Fail2ban server in daemon mode (background, start from client). @staticmethod def startServerAsync(conf): # Forks the current process, don't fork if async specified (ex: test cases) pid = 0 frk = not conf["async"] and PRODUCTION if frk: # pragma: no cover pid = os.fork() logSys.debug(" async starting of server in %s, fork: %s - %s", os.getpid(), frk, pid) if pid == 0: args = list() args.append(SERVER) # Start async (don't read config) and in background as requested. args.append("--async") args.append("-b") # Set the socket path. args.append("-s") args.append(conf["socket"]) # Set the pidfile args.append("-p") args.append(conf["pidfile"]) # Force the execution if needed. if conf["force"]: args.append("-x") if conf["verbose"] > 1: args.append("-" + "v"*(conf["verbose"]-1)) # Logging parameters: for o in ('loglevel', 'logtarget', 'syslogsocket'): args.append("--"+o) args.append(conf[o]) try: # Directory of client (to try the first start from current or the same directory as client, and from relative bin): exe = Fail2banServer.getServerPath() if not frk: # Wrapr args to use the same python version in client/server (important for multi-python systems): args[0] = exe exe = sys.executable args[0:0] = [exe] logSys.debug("Starting %r with args %r", exe, args) if frk: # pragma: no cover os.execv(exe, args) else: # use P_WAIT instead of P_NOWAIT (to prevent defunct-zomby process), it startet as daemon, so parent exit fast after fork): ret = os.spawnv(os.P_WAIT, exe, args) if ret != 0: # pragma: no cover raise OSError(ret, "Unknown error by executing server %r with %r" % (args[1], exe)) except OSError as e: # pragma: no cover if not frk: #not PRODUCTION: raise # Use the PATH env. logSys.warning("Initial start attempt failed (%s). Starting %r with the same args", e, SERVER) if frk: # pragma: no cover os.execvp(SERVER, args) @staticmethod def getServerPath(): startdir = sys.path[0] exe = os.path.abspath(os.path.join(startdir, SERVER)) if not os.path.isfile(exe): # may be unresolved in test-cases, so get relative starter (client): startdir = os.path.dirname(sys.argv[0]) exe = os.path.abspath(os.path.join(startdir, SERVER)) if not os.path.isfile(exe): # may be unresolved in test-cases, so try to get relative bin-directory: startdir = os.path.dirname(os.path.abspath(__file__)) startdir = os.path.join(os.path.dirname(os.path.dirname(startdir)), "bin") exe = os.path.abspath(os.path.join(startdir, SERVER)) return exe def _Fail2banClient(self): from .fail2banclient import Fail2banClient cli = Fail2banClient() cli.applyMembers(self) return cli def start(self, argv): server = None try: # Command line options ret = self.initCmdLine(argv) if ret is not None: return ret # Commands args = self._args cli = None # Just start: if len(args) == 1 and args[0] == 'start' and not self._conf.get("interactive", False): pass else: # If client mode - whole processing over client: if len(args) or self._conf.get("interactive", False): cli = self._Fail2banClient() return cli.start(argv) # Start the server, corresponding options: # background = True, if should be new process running in background, otherwise start in # foreground process will be forked in daemonize, inside of Server module. # nonsync = True, normally internal call only, if started from client, so configures # the server via asynchronous thread. background = self._conf["background"] nonsync = self._conf.get("async", False) # If was started not from the client: if not nonsync: # Load requirements on demand (we need utils only when asynchronous handling): from ..server.utils import Utils # Start new thread with client to read configuration and # transfer it to the server: cli = self._Fail2banClient() cli._conf = self._conf phase = dict() logSys.debug('Configure via async client thread') cli.configureServer(phase=phase) # Start server, daemonize it, etc. pid = os.getpid() server = Fail2banServer.startServerDirect(self._conf, background, cli._set_server if cli else None) # If forked - just exit other processes if pid != os.getpid(): # pragma: no cover os._exit(0) if cli: cli._server = server # wait for client answer "done": if not nonsync and cli: Utils.wait_for(lambda: phase.get('done', None) is not None, self._conf["timeout"], 0.001) if not phase.get('done', False): if server: # pragma: no cover server.quit() exit(255) if background: logSys.debug('Starting server done') except Exception as e: if self._conf["verbose"] > 1: logSys.exception(e) else: logSys.error(e) if server: # pragma: no cover server.quit() exit(255) return True @staticmethod def exit(code=0): # pragma: no cover if code != 0: logSys.error("Could not start %s", SERVER) exit(code) def exec_command_line(argv): server = Fail2banServer() if server.start(argv): exit(0) else: exit(255) client/filterreader.py000064400000006236152231502140011046 0ustar00# 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. # Author: Cyril Jaquier # __author__ = "Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" import os import shlex from .configreader import DefinitionInitConfigReader from ..helpers import getLogger # Gets the instance of the logger. logSys = getLogger(__name__) class FilterReader(DefinitionInitConfigReader): _configOpts = { "usedns": ["string", None], "prefregex": ["string", None], "ignoreregex": ["string", None], "failregex": ["string", None], "maxlines": ["int", None], "datepattern": ["string", None], "journalmatch": ["string", None], } def setFile(self, fileName): self.__file = fileName DefinitionInitConfigReader.setFile(self, os.path.join("filter.d", fileName)) def getFile(self): return self.__file def applyAutoOptions(self, backend): # set init option to backend-related logtype, considering # that the filter settings may be overwritten in its local: if (not self._initOpts.get('logtype') and not self.has_option('Definition', 'logtype', False) ): self._initOpts['logtype'] = ['file','journal'][int(backend.startswith("systemd"))] def convert(self): stream = list() opts = self.getCombined() if not len(opts): return stream return FilterReader._fillStream(stream, opts, self._jailName) @staticmethod def _fillStream(stream, opts, jailName): prio0idx = 0 for opt, value in opts.items(): # Do not send a command if the value is not set (empty). if value is None: continue if opt in ("failregex", "ignoreregex"): multi = [] for regex in value.split('\n'): # Do not send a command if the rule is empty. if regex != '': multi.append(regex) if len(multi) > 1: stream.append(["multi-set", jailName, "add" + opt, multi]) elif len(multi): stream.append(["set", jailName, "add" + opt, multi[0]]) elif opt in ('usedns', 'maxlines', 'prefregex'): # Be sure we set this options first, and usedns is before all regex(s). stream.insert(0 if opt == 'usedns' else prio0idx, ["set", jailName, opt, value]) prio0idx += 1 elif opt in ('datepattern'): stream.append(["set", jailName, opt, value]) elif opt == 'journalmatch': for match in value.split("\n"): if match == '': continue stream.append( ["set", jailName, "addjournalmatch"] + shlex.split(match)) return stream client/jailreader.py000064400000021257152231502140010500 0ustar00# 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. # Author: Cyril Jaquier # __author__ = "Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" import glob import json import os.path import re from .configreader import ConfigReaderUnshared, ConfigReader from .filterreader import FilterReader from .actionreader import ActionReader from ..version import version from ..helpers import getLogger, extractOptions, splitWithOptions, splitwords # Gets the instance of the logger. logSys = getLogger(__name__) class JailReader(ConfigReader): def __init__(self, name, force_enable=False, **kwargs): ConfigReader.__init__(self, **kwargs) self.__name = name self.__filter = None self.__force_enable = force_enable self.__actions = list() self.__opts = None @property def options(self): return self.__opts def setName(self, value): self.__name = value def getName(self): return self.__name def read(self): out = ConfigReader.read(self, "jail") # Before returning -- verify that requested section # exists at all if not (self.__name in self.sections()): raise ValueError("Jail %r was not found among available" % self.__name) return out def isEnabled(self): return self.__force_enable or ( self.__opts and self.__opts.get("enabled", False)) @staticmethod def _glob(path): """Given a path for glob return list of files to be passed to server. Dangling symlinks are warned about and not returned """ pathList = [] for p in glob.glob(path): if os.path.exists(p): pathList.append(p) else: logSys.warning("File %s is a dangling link, thus cannot be monitored" % p) return pathList _configOpts1st = { "enabled": ["bool", False], "backend": ["string", "auto"], "filter": ["string", ""] } _configOpts = { "enabled": ["bool", False], "backend": ["string", "auto"], "maxretry": ["int", None], "maxmatches": ["int", None], "findtime": ["string", None], "bantime": ["string", None], "bantime.increment": ["bool", None], "bantime.factor": ["string", None], "bantime.formula": ["string", None], "bantime.multipliers": ["string", None], "bantime.maxtime": ["string", None], "bantime.rndtime": ["string", None], "bantime.overalljails": ["bool", None], "ignorecommand": ["string", None], "ignoreself": ["bool", None], "ignoreip": ["string", None], "ignorecache": ["string", None], "filter": ["string", ""], "logtimezone": ["string", None], "logencoding": ["string", None], "logpath": ["string", None], "action": ["string", ""] } _configOpts.update(FilterReader._configOpts) _ignoreOpts = set(['action', 'filter', 'enabled'] + list(FilterReader._configOpts.keys())) def getOptions(self): basedir = self.getBaseDir() # Before interpolation (substitution) add static options always available as default: self.merge_defaults({ "fail2ban_version": version, "fail2ban_confpath": basedir }) try: # Read first options only needed for merge defaults ('known/...' from filter): self.__opts = ConfigReader.getOptions(self, self.__name, self._configOpts1st, shouldExist=True) if not self.__opts: # pragma: no cover raise JailDefError("Init jail options failed") if not self.isEnabled(): return True # Read filter flt = self.__opts["filter"] if flt: try: filterName, filterOpt = extractOptions(flt) except ValueError as e: raise JailDefError("Invalid filter definition %r: %s" % (flt, e)) self.__filter = FilterReader( filterName, self.__name, filterOpt, share_config=self.share_config, basedir=basedir) ret = self.__filter.read() if not ret: raise JailDefError("Unable to read the filter %r" % filterName) # set backend-related options (logtype): self.__filter.applyAutoOptions(self.__opts.get('backend', '')) # merge options from filter as 'known/...' (all options unfiltered): self.__filter.getOptions(self.__opts, all=True) ConfigReader.merge_section(self, self.__name, self.__filter.getCombined(), 'known/') else: self.__filter = None logSys.warning("No filter set for jail %s" % self.__name) # Read second all options (so variables like %(known/param) can be interpolated): self.__opts = ConfigReader.getOptions(self, self.__name, self._configOpts) if not self.__opts: # pragma: no cover raise JailDefError("Read jail options failed") # cumulate filter options again (ignore given in jail): if self.__filter: self.__filter.getOptions(self.__opts) # Read action for act in splitWithOptions(self.__opts["action"]): try: act = act.strip() if not act: # skip empty actions continue # join with previous line if needed (consider possible new-line): try: actName, actOpt = extractOptions(act) except ValueError as e: raise JailDefError("Invalid action definition %r: %s" % (act, e)) if actName.endswith(".py"): self.__actions.append([ "set", self.__name, "addaction", actOpt.pop("actname", os.path.splitext(actName)[0]), os.path.join( basedir, "action.d", actName), json.dumps(actOpt), ]) else: action = ActionReader( actName, self.__name, actOpt, share_config=self.share_config, basedir=basedir) ret = action.read() if ret: action.getOptions(self.__opts) self.__actions.append(action) else: raise JailDefError("Unable to read action %r" % actName) except JailDefError: raise except Exception as e: logSys.debug("Caught exception: %s", e, exc_info=True) raise ValueError("Error in action definition %r: %r" % (act, e)) if not len(self.__actions): logSys.warning("No actions were defined for %s" % self.__name) except JailDefError as e: e = str(e) logSys.error(e) if not self.__opts: self.__opts = dict() self.__opts['config-error'] = e return False return True def convert(self, allow_no_files=False): """Convert read before __opts to the commands stream Parameters ---------- allow_missing : bool Either to allow log files to be missing entirely. Primarily is used for testing """ stream = [] stream2 = [] e = self.__opts.get('config-error') if e: stream.extend([['config-error', "Jail '%s' skipped, because of wrong configuration: %s" % (self.__name, e)]]) return stream # fill jail with filter options, using filter (only not overriden in jail): if self.__filter: stream.extend(self.__filter.convert()) # and using options from jail: FilterReader._fillStream(stream, self.__opts, self.__name) for opt, value in self.__opts.items(): if opt == "logpath": if self.__opts.get('backend', '').startswith("systemd"): continue found_files = 0 for path in value.split("\n"): path = path.rsplit(" ", 1) path, tail = path if len(path) > 1 else (path[0], "head") pathList = JailReader._glob(path) if len(pathList) == 0: logSys.notice("No file(s) found for glob %s" % path) for p in pathList: found_files += 1 # logpath after all log-related data (backend, date-pattern, etc) stream2.append( ["set", self.__name, "addlogpath", p, tail]) if not found_files: msg = "Have not found any log file for %s jail" % self.__name if not allow_no_files: raise ValueError(msg) logSys.warning(msg) elif opt == "backend": backend = value elif opt == "ignoreip": stream.append(["set", self.__name, "addignoreip"] + splitwords(value)) elif opt not in JailReader._ignoreOpts: stream.append(["set", self.__name, opt, value]) # consider options order (after other options): if stream2: stream += stream2 for action in self.__actions: if isinstance(action, (ConfigReaderUnshared, ConfigReader)): stream.extend(action.convert()) else: stream.append(action) stream.insert(0, ["add", self.__name, backend]) return stream class JailDefError(Exception): pass client/jailsreader.py000064400000006277152231502140010670 0ustar00# 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. # Author: Cyril Jaquier # __author__ = "Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" from .configreader import ConfigReader from .jailreader import JailReader from ..helpers import getLogger # Gets the instance of the logger. logSys = getLogger(__name__) class JailsReader(ConfigReader): def __init__(self, force_enable=False, **kwargs): """ Parameters ---------- force_enable : bool, optional Passed to JailReader to force enable the jails. It is for internal use """ ConfigReader.__init__(self, **kwargs) self.__jails = list() self.__force_enable = force_enable @property def jails(self): return self.__jails def read(self): self.__jails = list() return ConfigReader.read(self, "jail") def getOptions(self, section=None, ignoreWrong=True): """Reads configuration for jail(s) and adds enabled jails to __jails """ opts = [] self.__opts = ConfigReader.getOptions(self, "Definition", opts) if section is None: sections = self.sections() else: sections = [ section ] # Get the options of all jails. parse_status = 0 for sec in sections: if sec == 'INCLUDES': continue # use the cfg_share for filter/action caching and the same config for all # jails (use_config=...), therefore don't read it here: jail = JailReader(sec, force_enable=self.__force_enable, share_config=self.share_config, use_config=self._cfg) ret = jail.getOptions() if ret: if jail.isEnabled(): # at least one jail was successful: parse_status |= 1 # We only add enabled jails self.__jails.append(jail) else: logSys.error("Errors in jail %r.%s", sec, " Skipping..." if ignoreWrong else "") self.__jails.append(jail) # at least one jail was invalid: parse_status |= 2 return ((ignoreWrong and parse_status & 1) or not (parse_status & 2)) def convert(self, allow_no_files=False): """Convert read before __opts and jails to the commands stream Parameters ---------- allow_missing : bool Either to allow log files to be missing entirely. Primarily is used for testing """ stream = list() # Convert jails for jail in self.__jails: stream.extend(jail.convert(allow_no_files=allow_no_files)) # Start jails for jail in self.__jails: if not jail.options.get('config-error'): stream.append(["start", jail.getName()]) return stream server/__pycache__/database.cpython-36.opt-1.pyc000064400000062004152231502140015410 0ustar003 UB(dtã@sdZdZdZddlZddlZddlZddlZddlZddlZddl m Z ddl m Z ddl mZdd lmZdd lmZd d lmZmZmZeeƒZd d„Zejdkr¸dd„Zdd„Zndd„Zdd„Zdd„Zejeeƒej deƒdd„Z!Gdd„de"ƒZ#dS)zSteven Hiscocksz"Copyright (c) 2013 Steven HiscocksZGPLéN)Úwraps)ÚRLocké)ÚMyTime)Ú FailTicket)ÚUtilsé)Ú getLoggerÚ uni_stringÚ PREFER_ENCcCst|tƒrt|ƒ}t|ƒS)z/Avoid errors on types unknown in json-adapters.)Ú isinstanceÚsetÚlistr )Úx©rú/usr/lib/python3.6/database.pyÚ _json_default*s récCs`ytj|dtd�jtdƒ}Wn>tk rZ}z"tjd|tjƒdkd�d}WYdd}~XnX|S)NF)Ú ensure_asciiÚdefaultÚreplacezjson dumps failed: %ré)Úexc_infoz{}) ÚjsonÚdumpsrÚencoder Ú ExceptionÚlogSysÚerrorÚgetEffectiveLevel)rÚerrrÚ_json_dumps_safe1s r!cCsZytj|jtdƒƒ}Wn>tk rT}z"tjd|tjƒdkd�i}WYdd}~XnX|S)Nrzjson loads failed: %rr)r)rÚloadsÚdecoder rrrr)rr rrrÚ_json_loads_safe;s r$cCsrt|tƒr tdd„|jƒDƒƒSt|ttfƒrFsz_normalize..cSsg|] }t|ƒ‘qSr)r%)r&Úelementrrrú Hsz_normalize..r) r ÚdictÚitemsrr Ústrrr r#)rrrrr%Ds    r%cCs\ytjt|ƒdtd�}Wn>tk rV}z"tjd|tjƒdkd�d}WYdd}~XnX|S)NF)rrzjson dumps failed: %rr)rz{})rrr%rrrrr)rr rrrr!Ps cCsZytj|jtdƒƒ}Wn>tk rT}z"tjd|tjƒdkd�i}WYdd}~XnX|S)Nrzjson loads failed: %rr)r)rr"r#r rrrr)rr rrrr$Ys ZJSONcstˆƒ‡fdd„ƒ}|S)NcsP|j�@|j�.|jjƒ}zˆ||f|ž|ŽS|jƒXWdQRXWdQRXdS)N)Ú_lockÚ_dbÚcursorÚclose)ÚselfÚargsÚkwargsÚcur)ÚfrrÚwrappergs  z"commitandrollback..wrapper)r)r7r8r)r7rÚcommitandrollbackfs r9c@sœeZdZdZdZd\ZeeƒZd_dd„Zd`dd„Z dd„Z e dd„ƒZ dd„Z e dd„ƒZe dd„ƒZejdd„ƒZdad d!„Zedbd"d#„ƒZd$d%„Zed&d'„ƒZed(d)„ƒZed*d+„ƒZed,d-„ƒZedcd/d0„ƒZed1d2„ƒZddd4d5„Zeded6d7„ƒZed8d9„ƒZd:d;„Zedfdd?„ƒZ ed@dA„ƒZ!edBdC„ƒZ"edgdDdE„ƒZ#dFdG„Z$dhdHdI„Z%edidJdK„ƒZ&djdLdM„Z'dkdOdP„Z(dQdR„Z)dSdT„Z*edUdV„ƒZ+d.S)lÚ Fail2BanDbaÌFail2Ban database for storing persistent data. This allows after Fail2Ban is restarted to reinstated bans and to continue monitoring logs from the same point. This will either create a new Fail2Ban database, connect to an existing, and if applicable upgrade the schema in the process. Parameters ---------- filename : str File name for SQLite3 database, which will be created if doesn't already exist. purgeAge : int Purge age in seconds, used to remove old bans from database during purge. Raises ------ sqlite3.OperationalError Error connecting/creating a SQLite3 database. RuntimeError If exisiting database fails to update to new schema. Attributes ---------- filename purgeage rÚ fail2banDbú7CREATE TABLE IF NOT EXISTS fail2banDb(version INTEGER);Újailsú•CREATE TABLE IF NOT EXISTS jails(name TEXT NOT NULL UNIQUE, enabled INTEGER NOT NULL DEFAULT 1);CREATE INDEX IF NOT EXISTS jails_name ON jails(name);ÚlogsáSCREATE TABLE IF NOT EXISTS logs(jail TEXT NOT NULL, path TEXT, firstlinemd5 TEXT, lastfilepos INTEGER DEFAULT 0, FOREIGN KEY(jail) REFERENCES jails(name) ON DELETE CASCADE, UNIQUE(jail, path),UNIQUE(jail, path, firstlinemd5));CREATE INDEX IF NOT EXISTS logs_path ON logs(path);CREATE INDEX IF NOT EXISTS logs_jail_path ON logs(jail, path);Úbansá‚CREATE TABLE IF NOT EXISTS bans(jail TEXT NOT NULL, ip TEXT, timeofban INTEGER NOT NULL, bantime INTEGER NOT NULL, bancount INTEGER NOT NULL default 1, data JSON, FOREIGN KEY(jail) REFERENCES jails(name) );CREATE INDEX IF NOT EXISTS bans_jail_timeofban_ip ON bans(jail, timeofban);CREATE INDEX IF NOT EXISTS bans_jail_ip ON bans(jail, ip);CREATE INDEX IF NOT EXISTS bans_ip ON bans(ip);ÚbipsáZCREATE TABLE IF NOT EXISTS bips(ip TEXT NOT NULL, jail TEXT NOT NULL, timeofban INTEGER NOT NULL, bantime INTEGER NOT NULL, bancount INTEGER NOT NULL default 1, data JSON, PRIMARY KEY(ip, jail), FOREIGN KEY(jail) REFERENCES jails(name) );CREATE INDEX IF NOT EXISTS bips_timeofban ON bips(timeofban);CREATE INDEX IF NOT EXISTS bips_ip ON bips(ip);éé<rcCs,d|_tƒ|_||_||_||_|jƒdS)Né )Ú maxMatchesrr/Ú _dbFilenameÚ _purgeAgeÚ_outDatedFactorÚ _connectDB)r3ÚfilenameZpurgeAgeZoutDatedFactorrrrÚ__init__Ås zFail2BanDb.__init__Fc 0CsP|j}y*tj|dtjd�|_i|_tjd|ƒWn:tjk rj}ztj d||j dƒ‚WYdd}~XnXyddl }d}Wnt k r”d}YnX|jj ƒ}�zy:|jdƒ|jdƒ|sÈ|jd ƒ|jd ƒ|jd ƒWn„tjk �rtjd |jƒƒYn¶tjk �rb}z>tj d ||j dƒtjj|ƒ�s>‚|jƒd}|jƒWYdd}~XnZX|jƒd}|tjk�r¼|j|ƒ}|tjk�r¢tjd||ƒntj dtj||ƒtdƒ‚Wd|�r,tjdƒ|j|dd�tjdƒtjdƒ|jdƒx$|jƒD]} tjddj| ƒƒ�qW|jj ƒ|�rJ|�rB|jd ƒ|jƒXdS)NF)Zcheck_same_threadZ detect_typesz.Connected to fail2ban persistent database '%s'z9Error connecting to fail2ban persistent database '%s': %srTzPRAGMA foreign_keys = ONzPRAGMA synchronous = OFFzPRAGMA journal_mode = MEMORYzPRAGMA temp_store = MEMORYz&SELECT version FROM fail2banDb LIMIT 1z"New database created. Version '%r'z3Error opening fail2ban persistent database '%s': %sz"Database updated from '%r' to '%r'zIDatabase update failed to achieve version '%r': updated from '%r' to '%r'zFailed to fully updatez# Create missing tables/indices ...)Ú incrementalz -> okz Check integrity ...zPRAGMA integrity_checkz -> %sú )!rIÚsqlite3ZconnectZPARSE_DECLTYPESr0Ú_bansMergedCacherÚinfoZOperationalErrorrr4Ú__pypy__Ú ImportErrorr1ÚexecuteZwarningÚcreateDbÚErrorÚosÚpathÚisfiler2ÚrepairDBÚfetchoner:Ú __version__ÚupdateDbÚ RuntimeErrorÚdebugÚ _createDbZfetchallÚjoinZcommit) r3ÚcheckIntegrityrMr rTZpypyr6ÚversionZ newversionÚsrrrrLÍsx                     zFail2BanDb._connectDBcCs"tjdƒ|jjƒtjdƒdS)Nz Close connection to database ...zConnection to database closed.)rrar0r2rS)r3rrrr2s  zFail2BanDb.closec Cs>y|jStk r8|jdtjdtjƒƒ|_|jSXdS)NÚ.z %Y%m%d-%H%M%S)Z_Fail2BanDb__dbBackupFilenameÚAttributeErrorrIÚtimeZstrftimerZgmtime)r3rrrÚ_dbBackupFilename#s zFail2BanDb._dbBackupFilenamecCsLGdd„dtƒ}|j}d|_�z y®tjd|jƒtjj|jƒs^t j |j|jƒtjd|jƒntjj|jƒrxtj |jƒt j d|j|jfƒtj|jƒj}|r¸tjd|ƒ|jdd�ntjd |ƒ|d ƒ‚Wnltk �r:}zNtjd |j|jd t||ƒ �o tjƒd kd�tj |jƒ|jdd�WYdd}~XnXWd||_XdS)Nc@s eZdZdS)z,Fail2BanDb.repairDB..RepairExceptionN)Ú__name__Ú __module__Ú __qualname__rrrrÚRepairException,srnzTrying to repair database %sz Database backup created: %szHf2b_db=$0; f2b_dbbk=$1; sqlite3 "$f2b_dbbk" ".dump" | sqlite3 "$f2b_db" z5 Repair seems to be successful, restored %d byte(s).T)rdz1 Repair seems to be failed, restored %d byte(s).z Recreate ...z/ Error repairing of fail2ban database '%s': %srrG)r)rr\rrSrIrYrZr[rjÚshutilZmoveÚremoverZ executeCmdÚstatÚst_sizerLrr4r r)r3rnZ _repairDBZ dbFileSizer rrrr\+s2      "zFail2BanDb.repairDBcCs|jS)z&File name of SQLite3 database file. )rI)r3rrrrMNszFail2BanDb.filenamecCs|jS)zPurge age in seconds. )rJ)r3rrrÚpurgeageTszFail2BanDb.purgeagecCstj|ƒ|_dS)N)rZ str2secondsrJ)r3ÚvaluerrrrsZscCsDxtjD]\}}|j|ƒqW|jdtjfƒ|jdƒ|jƒdS)z8Creates a new database, called during initialisation. z\INSERT INTO fail2banDb(version) SELECT ? WHERE NOT EXISTS (SELECT 1 FROM fail2banDb LIMIT 1)z&SELECT version FROM fail2banDb LIMIT 1r)r:Ú_CREATE_SCRIPTSÚ executescriptrVr^r])r3r6rOÚnrfrrrrb^s   zFail2BanDb._createDbcCs |j||ƒS)N)rb)r3r6rOrrrrWkszFail2BanDb.createDbcCs&|jd|fƒ|jƒ}|dk o$|dS)NzQselect 1 where exists (select 1 from sqlite_master WHERE type='table' AND name=?)r)rVr])r3r6ÚtableÚresrrrÚ _tableExistsoszFail2BanDb._tableExistscCsH|tjkrtdƒ‚yâtjd|j|ƒtjj|jƒsPt j |j |jƒtjd|jƒ|dkrx|j |dƒrx|j dtjdƒ|dkr |j |dƒr |j d tjdƒ|d krà|j |d ƒ rà|j d tjd ƒ|j |dƒrà|jd ƒ|jdƒ|jƒdStk �rB}z0tjd|j|jdtjƒdkd�|jƒWYdd}~XnXdS)z…Update an existing database, called during initialisation. A timestamped backup is also created prior to attempting the update. zIAttempt to travel to future version of database ...how did you get here??z&Upgrade database: %s from version '%r'z Database backup created: %srr?zÃBEGIN TRANSACTION;CREATE TEMPORARY TABLE logs_temp AS SELECT * FROM logs;DROP TABLE logs;%s;INSERT INTO logs SELECT * from logs_temp;DROP TABLE logs_temp;UPDATE fail2banDb SET version = 2;COMMIT;rrAzØBEGIN TRANSACTION;CREATE TEMPORARY TABLE bans_temp AS SELECT jail, ip, timeofban, -2 as bantime, 1 as bancount, data FROM bans;DROP TABLE bans;%s; INSERT INTO bans SELECT * from bans_temp;DROP TABLE bans_temp;COMMIT;rrCz?BEGIN TRANSACTION;%s; UPDATE fail2banDb SET version = 4;COMMIT;z›INSERT OR REPLACE INTO bips(ip, jail, timeofban, bantime, bancount, data) SELECT ip, jail, timeofban, bantime, bancount, data FROM bans order by timeofbanz&SELECT version FROM fail2banDb LIMIT 1rz#Failed to upgrade database '%s': %srG)rN)r:r^ÚNotImplementedErrorrrSrjrYrZr[roZcopyfilerMrzrvÚ _CREATE_TABSrVr]rrrIr4rr\)r3r6rer rrrr_us6     zFail2BanDb.updateDbcCs.|jd|jfƒ|jdkr*|jd|jfƒdS)zmAdds a jail to the database. Parameters ---------- jail : Jail Jail to be added to the database. z7INSERT OR IGNORE INTO jails(name, enabled) VALUES(?, 1)rz|dkr|jdƒn|jdt|ƒfƒtdd„|jƒDƒƒS)zGet name of jails in database. Currently only used for testing purposes. Returns ------- set Set of jail names. NzSELECT name FROM jailsz'SELECT name FROM jails WHERE enabled=%scss|]}|dVqdS)rNr)r&Úrowrrrr)ßsz*Fail2BanDb.getJailNames..)rVÚintr Ú fetchmany)r3r6ZenabledrrrÚ getJailNamesÏs  zFail2BanDb.getJailNamescCs|j|||jƒ|jƒ|jƒƒS)a7Adds a log to the database. Parameters ---------- jail : Jail Jail that log is being monitored by. container : FileContainer File container of the log file being added. Returns ------- int If log was already present in database, value of last position in the log file; else `None` )Ú_addLogÚ getFileNameÚgetPosÚgetHash)r3r6rÚ containerrrrÚaddLogászFail2BanDb.addLogrcCs‚d}|jd|j|fƒy|jƒ\}}Wntk r>d}YnX|dkrj|sT|dk rj|jd|j|||fƒ|dk r~||kr~d}|S)NzBSELECT firstlinemd5, lastfilepos FROM logs WHERE jail=? AND path=?zUINSERT OR REPLACE INTO logs(jail, path, firstlinemd5, lastfilepos) VALUES(?, ?, ?, ?))rVr}r]Ú TypeError)r3r6rr}ÚposÚmd5Z lastLinePosZ firstLineMD5rrrr‡ôs  zFail2BanDb._addLogcCsFd}g}|dk r$|d7}|j|jƒ|j||ƒtdd„|jƒDƒƒS)zëGets all the log paths from the database. Currently only for testing purposes. Parameters ---------- jail : Jail If specified, will only reutrn logs belonging to the jail. Returns ------- set Set of log paths. zSELECT path FROM logsNz WHERE jail=?css|]}|dVqdS)rNr)r&rƒrrrr)sz)Fail2BanDb.getLogPaths..)Úappendr}rVr r…)r3r6rÚqueryÚ queryArgsrrrÚ getLogPathss  zFail2BanDb.getLogPathscCs"|j|||jƒ|jƒ|jƒƒdS)zÎUpdates hash and last position in log file. Parameters ---------- jail : Jail Jail of which the log file belongs to. container : FileContainer File container of the log file being updated. N)Ú _updateLogrˆr‰rŠ)r3r6rr‹rrrÚ updateLogs zFail2BanDb.updateLogcCs6|jd|||j|fƒ|js2|jd|j|||fƒdS)NzEUPDATE logs SET firstlinemd5=?, lastfilepos=? WHERE jail=? AND path=?zUINSERT OR REPLACE INTO logs(jail, path, firstlinemd5, lastfilepos) VALUES(?, ?, ?, ?))rVr}r~)r3r6rr}rŽr�rrrr”,szFail2BanDb._updateLogcCs|j|||||ƒS)aAGet journal position from database. Parameters ---------- jail : Jail Jail of which the journal belongs to. name, time, iso : Journal name (typically systemd-journal) and last known time. Returns ------- int (or float) Last position (as time) if it was already present in database; else `None` )r‡)r3r6rr}riÚisorrrÚ getJournalPos6szFail2BanDb.getJournalPoscCs|j|||||ƒdS)zÕUpdates last position (as time) of journal. Parameters ---------- jail : Jail Jail of which the journal belongs to. name, time, iso : Journal name (typically systemd-journal) and last known time. N)r”)r3r6rr}rir–rrrÚ updateJournalHs zFail2BanDb.updateJournalcCs"t|jƒƒ}y|j||f=Wntk r0YnXy|j|df=Wntk rVYnX|jƒ}|jdƒ}|jr |r²t|ƒ|jkr²|jƒ}||j d…|d<n|r²|jƒ}|d=|j d|j |t t |j ƒƒƒ|j|jjƒƒ|jƒ|fƒ|j d||j t t |j ƒƒƒ|j|jjƒƒ|jƒ|fƒdS)z¤Add a ban to the database. Parameters ---------- jail : Jail Jail in which the ban has occurred. ticket : BanTicket Ticket of the ban to be added. NÚmatcheszWINSERT INTO bans(jail, ip, timeofban, bantime, bancount, data) VALUES(?, ?, ?, ?, ?, ?)zbINSERT OR REPLACE INTO bips(ip, jail, timeofban, bantime, bancount, data) VALUES(?, ?, ?, ?, ?, ?))r.ZgetIDrRÚKeyErrorZgetDataÚgetrHÚlenÚcopyrVr}r„ÚroundZgetTimeÚ getBanTimeÚactionsZ getBanCount)r3r6rÚticketÚipÚdatar™rrrÚaddBanUs4  ((zFail2BanDb.addBancGs„d}d}|jg}t|ƒs4|j||ƒ|j||ƒdS|d7}|d7}|jdƒx0|D](}t|ƒ|d<|j||ƒ|j||ƒqTWdS)zðDelete a single or multiple tickets from the database. Parameters ---------- jail : Jail Jail in which the ticket(s) should be removed. args : list of IP IPs to be removed, if not given all tickets of jail will be removed. zDELETE FROM bips WHERE jail = ?zDELETE FROM bans WHERE jail = ?Nz AND ip = ?Úr)r}rœrVr�r.)r3r6rr4Zquery1Zquery2r’r¢rrrÚdelBan|s       zFail2BanDb.delBancCs„d}g}|dk r$|d7}|j|jƒ|dk rN|dkrN|d7}|jtjƒ|ƒ|dk rl|d7}|jt|ƒƒ|d7}t|j||ƒƒS)Nz,SELECT ip, timeofban, data FROM bans WHERE 1z AND jail=?rz AND timeofban > ?z AND ip=?z ORDER BY ip, timeofban desc)r�r}rrir.rrV)r3r6rÚbantimer¢r‘r’rrrÚ_getBans–s zFail2BanDb._getBanscKsBg}x8|jf|ŽD](\}}}|jt||ƒƒ|dj|ƒqW|S)a»Get bans from the database. Parameters ---------- jail : Jail Jail that the ban belongs to. Default `None`; all jails. bantime : int Ban time in seconds, such that bans returned would still be valid now. Negative values are equivalent to `None`. Default `None`; no limit. ip : str IP Address to filter bans by. Default `None`; all IPs. Returns ------- list List of `Ticket`s for bans stored in database. réÿÿÿÿ)r¨r�rZsetData)r3r5Úticketsr¢Ú timeofbanr£rrrÚgetBans©s zFail2BanDb.getBansc CsŒ|j��zd}|dks|dkr:||f}||jkr:|j|Sg}d}t|j|||d�ƒ}|�rR|dd}g} d} i} xÄ|D]¼\} } }| |kr¼t||| ƒ}|j| ƒ|j|ƒ| }g} d} i} |jdgƒ}|jt | ƒ}|dk�rt |ƒ|krö|| } n|| d…| } | |jddƒ7} | |d<| |d<| j |ƒ| }qzWt| || d�}|j|ƒ|�rp|dk�rf|n||j|<|dk�r~|S|SQRXdS)a}Get bans from the database, merged into single ticket. This is the same as `getBans`, but bans merged into single ticket. Parameters ---------- jail : Jail Jail that the ban belongs to. Default `None`; all jails. bantime : int Ban time in seconds, such that bans returned would still be valid now. Negative values are equivalent to `None`. Default `None`; no limit. ip : str IP Address to filter bans by. Default `None`; all IPs. Returns ------- list or Ticket Single ticket representing bans stored in database per IP in a list. When `ip` argument passed, a single `Ticket` is returned. Nr)r¢rr§r™Úfailuresr)r£) r/rRrr¨rZ setAttemptr�r›rHrœÚupdate)r3r¢rr§ZcacheKeyrªr¡ÚresultsZ prev_banipr™r­ZtickdataÚbanipr«r£Zprev_timeofbanÚmZmaxaddrrrÚ getBansMergedÃsN             zFail2BanDb.getBansMergedc Cs¦t|ƒ}|sd}nd}|d7}|g}| rF|dk rF|d7}|j|jƒ|dk rh|d7}|jtjƒ|ƒ|dk r‚|d7}|j|ƒ|sŽ|dkr–|d7}t|j||ƒƒS)Nz-SELECT bancount, timeofban, bantime FROM bipsz ?z, GROUP BY ip ORDER BY timeofban DESC LIMIT 1)r.r�r}rrirrV) r3r6r¢rÚ forbantimeZ overalljailsÚfromtimer‘r’rrrÚgetBan s$   zFail2BanDb.getBancCs’g}|dk rd}|j|jƒnd}|dk r<|d7}|j|ƒ|d7}|j|ƒ|d krl|d7}|j||ƒ|dkr~|d7}n|d7}|j||ƒS) NzDSELECT ip, timeofban, bantime, bancount, data FROM bips WHERE jail=?zDSELECT ip, max(timeofban), bantime, bancount, data FROM bips WHERE 1z AND ip=?z/ AND (timeofban + bantime > ? OR bantime <= -1)rz AND timeofban > ?z( GROUP BY ip ORDER BY ip, timeofban DESCz ORDER BY timeofban DESC LIMIT 1r©)Nr©)r�r}rV)r3r6rr¢r³r´r’r‘rrrÚ_getCurrentBans!s"   zFail2BanDb._getCurrentBansTc!Cs|jjƒ}�zæ|dkrtjƒ}g}d} |dkrN|dk r>|jƒnd}|d krNd}|j�|j|||||d�} WdQRX�xx| D�]n} y¨| \} } } }}| dks¢| dkr°td| fƒ‚| dkrØ|dk rÊ|jj ƒn |rÒ|nd} n |rø|d krø| dksô| |krø|} | dk�r(| | |k�r(t j d | | | || ƒw|Wn4tk �r^}zt j d | |ƒw|WYdd}~XnXt | | |d �} |dk�r~|j }|�r¶| jƒ}|�rÀt|ƒ|k�rÀ| j|| d…ƒn | jdƒ| j| ƒ| j|ƒ|dk �râ| S|j| ƒq|WWd|jƒX|S)aœReads tickets (with merged info) currently affected from ban from the database. There are all the tickets corresponding parameters jail/ip, forbantime, fromtime (normally now). If correctBanTime specified (default True) it will fix the restored ban-time (and therefore endOfBan) of the ticket (normally it is ban-time of jail as maximum) for all tickets with ban-time greater (or persistent). NTr)rr¢r³r´r¥zunexpected value %rriXrzFignore ticket (with new max ban-time %r): too old %r <= %r, ticket: %rz$get current bans: ignore row %r - %s)r£r©éþÿÿÿr©r©)r0r1rriZ getMaxBanTimer/r¶Ú ValueErrorr rŸrrarrHZ getMatchesrœZ setMatchesZ setBanTimeZ setBanCountr�r2)r3rr¢r³r´ZcorrectBanTimeZ maxmatchesr6rªr¡rAr°r«r§Zbancountr£r r™rrrÚgetCurrentBans6s\         zFail2BanDb.getCurrentBanscCs|jdƒdS)z8Remove empty jails jails and log files from database. z™DELETE FROM jails WHERE enabled = 0 AND NOT EXISTS(SELECT * FROM bans WHERE jail = jails.name) AND NOT EXISTS(SELECT * FROM bips WHERE jail = jails.name)N)rV)r3r6rrrÚ _cleanjails~szFail2BanDb._cleanjailscCs4|jdttjƒƒ|j|jttjƒƒ|jfƒdS)zÛPurge old bad ips (jails and log files from database). Currently it is timed out IP, whose time since last ban is several times out-dated (outDatedFactor is default 3). Permanent banned ips will be never removed. zZDELETE FROM bips WHERE timeofban < ? and bantime != -1 and (timeofban + (bantime * ?)) < ?N)rVr„rrirJrK)r3r6rrrÚ _purge_bips†szFail2BanDb._purge_bipscCs6i|_|jdtjƒ|jfƒ|j|ƒ|j|ƒdS)z5Purge old bans, jails and log files from database. z$DELETE FROM bans WHERE timeofban < ?N)rRrVrrirJr»rº)r3r6rrrÚpurge�s  zFail2BanDb.purge©r;r<©r=r>©r?r@©rArB©rCrD)r½r¾r¿rÀrÁi é€Q)rÂr)F)F)F)N)rN)N)rN)NNN)NNN)NNNN)NNNN)NNNNTN),rkrlrmÚ__doc__r^rur,r|rNrLr2Úpropertyrjr\rMrsÚsetterrbr9rWrzr_r€r�r‚r†rŒr‡r“r•r”r—r˜r¤r¦r¨r¬r²rµr¶r¹rºr»r¼rrrrr:ssb    Q #     5          '   G   G r:)r)$Ú __author__Z __copyright__Z __license__rrYrorQÚsysriÚ functoolsrZ threadingrZmytimerr¡rZutilsrZhelpersr r r rkrrÚ version_infor!r$r%Zregister_adapterr,Zregister_converterr9Úobjectr:rrrrÚs4            server/__pycache__/datedetector.cpython-36.opt-1.pyc000064400000032421152231502140016313 0ustar003 UB(dLã@sâdZdZdZddlZddlZddlmZddlmZm Z m Z m Z m Z m Z ddlmZdd lmZd d lmZeeƒZd Zejd ejƒZejddd�Zddd„Zdd„fdd„ZGdd„deƒZGdd„deƒZ Gdd„deƒZ!dS)z'Cyril Jaquier and Fail2Ban Contributorsz Copyright (c) 2004 Cyril JaquierZGPLéN)ÚLocké)ÚreÚ DateTemplateÚDatePatternRegexÚ DateTai64nÚ DateEpochÚRE_EPOCH_PATTERN)ÚvalidateTimeZone)ÚUtilsé)Ú getLoggeréz(?Dsr)cCsb||jƒ}tj|ƒ}|s^|t|d|jƒƒ}t|dƒr>tj|ƒ}|s^t|dƒsVt|ƒ}nt|ƒ}|S)Nr)Únamerr ÚgetattrÚregexÚhasattrr')r$Úwrapr*Ú template2r,r%r%r&Ú_getAnchoredTemplateDs      r0c@sfeZdZdZdd„Zedd„ƒZdd„Zdd d d d d dddddddddddgZedd„ƒZ dd„Z dS)ÚDateDetectorCachez7Implements the caching of the default templates list. cCstƒ|_tƒ|_dS)N)rÚ_DateDetectorCache__lockÚlistÚ_DateDetectorCache__templates)Úselfr%r%r&Ú__init__\szDateDetectorCache.__init__c Cs8|jr |jS|j�|jr |jS|jƒ|jSQRXdS)z6List of template instances managed by the detector. N)r4r2Ú_addDefaultTemplate)r5r%r%r&Ú templates`szDateDetectorCache.templatescCsd|j}|jdƒ rP|jdƒ rPt|dƒrPt|ƒ}|j|krPd|_|jdj|ƒ|jdj|ƒdS)z&Cache Fail2Ban's default template. z {^LN-BEG}ú^r,gY@rrN)r*Ú startswithr-r0ÚweightÚ_DateDetectorCache__tmpcacheÚappend)r5r$r*r/r%r%r&Ú_cacheTemplatels" z DateDetectorCache._cacheTemplatezF%ExY(?P<_sep>[-/.])%m(?P=_sep)%d(?:T| ?)%H:%M:%S(?:[.,]%f)?(?:\s*%z)?z)(?:%a )?%b %d %k:%M:%S(?:\.%f)?(?: %ExY)?z$(?:%a )?%b %d %ExY %k:%M:%S(?:\.%f)?z1%d(?P<_sep>[-/])%m(?P=_sep)(?:%ExY|%Exy) %k:%M:%Sz=%d(?P<_sep>[-/])%b(?P=_sep)%ExY[ :]?%H:%M:%S(?:\.%f)?(?: %z)?z%m/%d/%ExY:%H:%M:%Sz%m-%d-%ExY %k:%M:%S(?:\.%f)?rz{^LN-BEG}%H:%M:%Sz^<%m/%d/%Exy@%H:%M:%S>z%Exy%Exm%Exd ?%H:%M:%Sz%b %d, %ExY %I:%M:%S %pz^%b-%d-%Exy %k:%M:%Sz6%ExY%Exm%Exd(?:T| ?)%ExH%ExM%ExS(?:[.,]%f)?(?:\s*%z)?z1(?:%Z )?(?:%a )?%b %d %k:%M:%S(?:\.%f)?(?: %ExY)?z1(?:%z )?(?:%a )?%b %d %k:%M:%S(?:\.%f)?(?: %ExY)?rcCs@ttjdtƒr:x(ttjƒD]\}}t|ƒ}|tj|<qWtjS)Nr)Ú isinstancer1ÚDEFAULT_TEMPLATESÚstrÚ enumerater')r5ÚiÚdtr%r%r&ÚdefaultTemplates³s z"DateDetectorCache.defaultTemplatescCsBggf|_x|jD]}|j|ƒqW|jd|jd|_|`dS)zr4)r5rDr%r%r&r7»s   z%DateDetectorCache._addDefaultTemplateN) Ú__name__Ú __module__Ú __qualname__Ú__doc__r6Úpropertyr8r>r@rEr7r%r%r%r&r1Ys.  r1c@s0eZdZdZd Zdd„Zedd „ƒZd d „Zd S)ÚDateDetectorTemplateztUsed for "shallow copy" of the template object. Prevents collectively usage of hits/lastUsed in cached templates r$ÚhitsÚlastUsedÚdistancecCs||_d|_d|_d|_dS)Nriÿÿÿ)r$rLrMrN)r5r$r%r%r&r6ÍszDateDetectorTemplate.__init__cCs|j|jjtd|jƒS)Nr)rLr$r;ÚmaxrN)r5r%r%r&r;ÔszDateDetectorTemplate.weightcCs t|j|ƒS)zF Returns attribute of template (called for parameters not in slots) )r+r$)r5r*r%r%r&Ú __getattr__Øsz DateDetectorTemplate.__getattr__N)r$rLrMrN) rFrGrHrIÚ __slots__r6rJr;rPr%r%r%r&rKÇs  rKc@szeZdZdZeƒZdd„Zddd„Zdd„Zdd d „Z e d d„ƒZ dd„Z e dd„ƒZ e jdd„ƒZ ddd„Zdd„Zd S)Ú DateDetectorzjManages one or more date templates to find a date within a log line. Attributes ---------- templates cCs>tƒ|_tƒ|_d|_d|_d|_d|_d|_d|_ d|_ dS)Ni,réÿÿÿr)rN)rSN) r3Ú_DateDetector__templatesr"Ú_DateDetector__known_namesÚ_DateDetector__unusedTimeÚ_DateDetector__lastPosÚ_DateDetector__lastEndPosÚ_DateDetector__lastTemplIdxÚ_DateDetector__firstUnusedZ_DateDetector__preMatchÚ_DateDetector__default_tz)r5r%r%r&r6çszDateDetector.__init__FcCsD|j}||jkr$|rdStd|ƒ‚|jj|ƒ|jjt|ƒƒdS)Nz(There is already a template with name %s)r*rUÚ ValueErrorÚaddrTr=rK)r5r$Ú ignoreDupr*r%r%r&Ú_appendTemplate÷s   zDateDetector._appendTemplatecsÔt|tƒr–|‰}d|kr"|jƒ‰tjˆƒ}|sŠˆdkrR‡fdd„}|j|ƒdSdˆkrl|j|dd �dSˆd kr€td ˆƒ}n t|ˆƒ}tjˆ|ƒ|j|ƒt j d t |d dƒ|j ƒt j dt |d dƒ|jƒdS)a‚Add a date template to manage and use in search of dates. Parameters ---------- template : DateTemplate or str Can be either a `DateTemplate` instance, or a string which will be used as the pattern for the `DatePatternRegex` template. The template will then be added to the detector. Raises ------ ValueError If a template already exists with the same name. rú {^LN-BEG}ú {DEFAULT}csˆdkr|jtj@SdS)Nz {^LN-BEG})ÚflagsrÚ LINE_BEGIN)r$)r#r%r&r)sz-DateDetector.appendTemplate..Nz{DATE}F)ÚpreMatchÚ allDefaultsz{NONE}z{UNB}^z date pattern `%r`: `%s`rÚz date pattern regex for %r: %s)r`ra)r?rArrr ÚaddDefaultTemplater'r"r_ÚlogSysÚinfor+r*Údebugr,)r5r$rZfltr%)r#r&ÚappendTemplates,        zDateDetector.appendTemplateNTcsnt|jƒdk}xZ|rtjjntjjD]B}|dk r<||ƒ r...)ÚRE_DATE_PREMATCHÚsub)r()rd)r(r&r)7sz1DateDetector.addDefaultTemplate..)r.)r^)ÚlenrTrRÚ _defCacher8rEr0r_)r5ZfilterTemplaterdrer^r$r%)rdr&rg*szDateDetector.addDefaultTemplatecCs|jS)z6List of template instances managed by the detector. )rT)r5r%r%r&r8;szDateDetector.templatesc Cs¤t|jƒs|jƒtjƒtkr$tjndd„}|tdd|ƒd}d}d}|j}|t|jƒk�rh|j|}|j}|j t j t j B@r¢|tdd|ƒ|j |ƒ}|}�n8|jd|jd} } |tdd || | || d| …|jd|| | …|| | d…|jd ƒ || d| …|jdk�sL|| | d…|jd k�r¾|jd jƒ �r¾|| | d…|jd k�s–|| d| …|jdk�r¾|jdjƒ �r¾|tdd || | …ƒ|j || | ƒ}n|tdd ƒ|j |ƒ}|}|�r^|jƒ} |jƒ} t|jƒdk�s4|j t j t j B@�s4| |jdk�rB| |jdk�rB|td |ƒn|tdƒ|| | |f}d}n |tdƒ|�sò|tdt|jƒƒd}�x0|jD�]$}||k�rª|d7}�qŽ|tdd||jƒ|j}|j |ƒ}|�rª|jƒ} |jƒ} |td|| |j|jd|jƒ|dt|jƒk�rP|j t j t j B@�r.P| dk�rX|j�rX|j|djj �rXP| |jk�st| |jdk�r¨|tdƒ| |dk�r˜|| | |f}d}|d7}�qŽP|d7}�qŽW| �rò|d�rò|\}} } }|td|ƒ|j|}|j}|�r–|jd7_tjƒ|_| |_|j|k�r0|jd7_| || d| …|| f|_| || d|| | d…f|_|�rˆ||jk�rˆ|j|ƒ}||_||fS|tdƒdS)a£Attempts to find date on a log line using templates. This uses the templates' `matchDate` method in an attempt to find a date. It also increments the match hit count for the winning template. Parameters ---------- line : str Line which is searched by the date templates. Returns ------- re.MatchObject, DateTemplate The regex match returned from the first successfully matched template. cWsdS)Nr%)Úargsr%r%r&r)Vsz(DateDetector.matchTime..rz"try to match time for line: %.120sNéÿÿÿz/ try to match last anchored template #%02i ...rzJ try to match last template #%02i (from %r to %r): ...%r==%r %s %r==%r...r z+ boundaries are correct, search in part %rz, boundaries show conflict, try whole searchz" matched last time template #%02izB ** last pattern collision - pattern change, reserve & search ...z8 ** last pattern not found - pattern change, search ...z search template (%i) ...z try template #%02i: %sz2 matched time template #%02i (at %r <= %r, %r) %sz1 ** distance collision - pattern change, reservez use best time template #%02iz no template.éÿÿÿÿ)Nrrrrrs)NN)rorTrgrhÚgetEffectiveLevelÚlogLevelÚlogrYr$rbrrcZLINE_ENDZ matchDaterWrXÚisalnumrÚendr*rNrLÚtimerMrZÚ_reorderTemplate) r5ÚlinervÚmatchÚfoundZignoreBySearchrCÚddtemplr$rNÚendposr%r%r&Ú matchTimeAs´     ..       (          zDateDetector.matchTimecCs|jS)N)r[)r5r%r%r&Ú default_tzÍszDateDetector.default_tzcCst|ƒ|_dS)N)r r[)r5Úvaluer%r%r&r�Ñsc Cs�|dkr|j|ƒ}|d}|dk rŒyT|j||d|jd�}|dk rttjƒtkrptjtd|d|djdƒ|jƒ|SWnt k rŠYnXdS)aAttempts to return the date on a log line using templates. This uses the templates' `getDate` method in an attempt to find a date. For the faster usage, always specify a parameter timeMatch (the previous tuple result of the matchTime), then this will work without locking and without cycle over templates. Parameters ---------- line : str Line which is searched by the date templates. Returns ------- float The Unix timestamp returned from the first successfully matched template or None if not found. Nrr)r�z& got time %f for %r using template %s) r€ZgetDater[rhrtrurvÚgroupr*r\)r5r{Z timeMatchr$Zdater%r%r&ÚgetTimeÕs  zDateDetector.getTimecsþˆrú|j‰ˆˆ‰tjƒtkr0tjtdˆˆjƒˆj|j‰ˆj‰|j ˆkrR|j nˆd‰‡‡‡‡‡‡fdd„}|ƒs˜ˆˆdkr†ˆSˆd‰|ƒs˜ˆSˆˆ=ˆgˆˆd…<x,|j t ˆƒkr؈|j jrØ|j d7_ q®WtjƒtkrötjtdˆˆƒˆSˆS)z“Reorder template (bubble up) in template list if hits grows enough. Parameters ---------- num : int Index of template should be moved. z% -> reorder template #%02i, hits: %rr c sLˆˆj}tjƒtkr6tjtdˆˆˆ|ˆjˆˆjƒˆ|kpJˆˆˆjkS)NzE -> compare template #%02i & #%02i, weight %.3f > %.3f, hits %r > %r)r;rhrtrurvrLrM)Zpweight)r~ÚnumÚposr8Úuntimer;r%r&Ú _moveable s   z0DateDetector._reorderTemplate.._moveablerrz" -> moved template #%02i -> #%02i) rTrhrtrurvrLrMrVr;rZro)r5r…rˆr%)r~r…r†r8r‡r;r&rzùs.    zDateDetector._reorderTemplate)F)NNT)N)rFrGrHrIr1rpr6r_rkrgrJr8r€r�Úsetterr„rzr%r%r%r&rRÞs *    $rRi)N)"Ú __author__Z __copyright__Z __license__ÚcopyryZ threadingrZ datetemplaterrrrrr Zstrptimer Zutilsr Zhelpersr rFrhruÚcompileÚ IGNORECASErmZCacherr'r0Úobjectr1rKrRr%r%r%r&Ús$      nserver/__pycache__/datedetector.cpython-36.pyc000064400000032421152231502140015354 0ustar003 UB(dLã@sâdZdZdZddlZddlZddlmZddlmZm Z m Z m Z m Z m Z ddlmZdd lmZd d lmZeeƒZd Zejd ejƒZejddd�Zddd„Zdd„fdd„ZGdd„deƒZGdd„deƒZ Gdd„deƒZ!dS)z'Cyril Jaquier and Fail2Ban Contributorsz Copyright (c) 2004 Cyril JaquierZGPLéN)ÚLocké)ÚreÚ DateTemplateÚDatePatternRegexÚ DateTai64nÚ DateEpochÚRE_EPOCH_PATTERN)ÚvalidateTimeZone)ÚUtilsé)Ú getLoggeréz(?Dsr)cCsb||jƒ}tj|ƒ}|s^|t|d|jƒƒ}t|dƒr>tj|ƒ}|s^t|dƒsVt|ƒ}nt|ƒ}|S)Nr)Únamerr ÚgetattrÚregexÚhasattrr')r$Úwrapr*Ú template2r,r%r%r&Ú_getAnchoredTemplateDs      r0c@sfeZdZdZdd„Zedd„ƒZdd„Zdd d d d d dddddddddddgZedd„ƒZ dd„Z dS)ÚDateDetectorCachez7Implements the caching of the default templates list. cCstƒ|_tƒ|_dS)N)rÚ_DateDetectorCache__lockÚlistÚ_DateDetectorCache__templates)Úselfr%r%r&Ú__init__\szDateDetectorCache.__init__c Cs8|jr |jS|j�|jr |jS|jƒ|jSQRXdS)z6List of template instances managed by the detector. N)r4r2Ú_addDefaultTemplate)r5r%r%r&Ú templates`szDateDetectorCache.templatescCsd|j}|jdƒ rP|jdƒ rPt|dƒrPt|ƒ}|j|krPd|_|jdj|ƒ|jdj|ƒdS)z&Cache Fail2Ban's default template. z {^LN-BEG}ú^r,gY@rrN)r*Ú startswithr-r0ÚweightÚ_DateDetectorCache__tmpcacheÚappend)r5r$r*r/r%r%r&Ú_cacheTemplatels" z DateDetectorCache._cacheTemplatezF%ExY(?P<_sep>[-/.])%m(?P=_sep)%d(?:T| ?)%H:%M:%S(?:[.,]%f)?(?:\s*%z)?z)(?:%a )?%b %d %k:%M:%S(?:\.%f)?(?: %ExY)?z$(?:%a )?%b %d %ExY %k:%M:%S(?:\.%f)?z1%d(?P<_sep>[-/])%m(?P=_sep)(?:%ExY|%Exy) %k:%M:%Sz=%d(?P<_sep>[-/])%b(?P=_sep)%ExY[ :]?%H:%M:%S(?:\.%f)?(?: %z)?z%m/%d/%ExY:%H:%M:%Sz%m-%d-%ExY %k:%M:%S(?:\.%f)?rz{^LN-BEG}%H:%M:%Sz^<%m/%d/%Exy@%H:%M:%S>z%Exy%Exm%Exd ?%H:%M:%Sz%b %d, %ExY %I:%M:%S %pz^%b-%d-%Exy %k:%M:%Sz6%ExY%Exm%Exd(?:T| ?)%ExH%ExM%ExS(?:[.,]%f)?(?:\s*%z)?z1(?:%Z )?(?:%a )?%b %d %k:%M:%S(?:\.%f)?(?: %ExY)?z1(?:%z )?(?:%a )?%b %d %k:%M:%S(?:\.%f)?(?: %ExY)?rcCs@ttjdtƒr:x(ttjƒD]\}}t|ƒ}|tj|<qWtjS)Nr)Ú isinstancer1ÚDEFAULT_TEMPLATESÚstrÚ enumerater')r5ÚiÚdtr%r%r&ÚdefaultTemplates³s z"DateDetectorCache.defaultTemplatescCsBggf|_x|jD]}|j|ƒqW|jd|jd|_|`dS)zr4)r5rDr%r%r&r7»s   z%DateDetectorCache._addDefaultTemplateN) Ú__name__Ú __module__Ú __qualname__Ú__doc__r6Úpropertyr8r>r@rEr7r%r%r%r&r1Ys.  r1c@s0eZdZdZd Zdd„Zedd „ƒZd d „Zd S)ÚDateDetectorTemplateztUsed for "shallow copy" of the template object. Prevents collectively usage of hits/lastUsed in cached templates r$ÚhitsÚlastUsedÚdistancecCs||_d|_d|_d|_dS)Nriÿÿÿ)r$rLrMrN)r5r$r%r%r&r6ÍszDateDetectorTemplate.__init__cCs|j|jjtd|jƒS)Nr)rLr$r;ÚmaxrN)r5r%r%r&r;ÔszDateDetectorTemplate.weightcCs t|j|ƒS)zF Returns attribute of template (called for parameters not in slots) )r+r$)r5r*r%r%r&Ú __getattr__Øsz DateDetectorTemplate.__getattr__N)r$rLrMrN) rFrGrHrIÚ __slots__r6rJr;rPr%r%r%r&rKÇs  rKc@szeZdZdZeƒZdd„Zddd„Zdd„Zdd d „Z e d d„ƒZ dd„Z e dd„ƒZ e jdd„ƒZ ddd„Zdd„Zd S)Ú DateDetectorzjManages one or more date templates to find a date within a log line. Attributes ---------- templates cCs>tƒ|_tƒ|_d|_d|_d|_d|_d|_d|_ d|_ dS)Ni,réÿÿÿr)rN)rSN) r3Ú_DateDetector__templatesr"Ú_DateDetector__known_namesÚ_DateDetector__unusedTimeÚ_DateDetector__lastPosÚ_DateDetector__lastEndPosÚ_DateDetector__lastTemplIdxÚ_DateDetector__firstUnusedZ_DateDetector__preMatchÚ_DateDetector__default_tz)r5r%r%r&r6çszDateDetector.__init__FcCsD|j}||jkr$|rdStd|ƒ‚|jj|ƒ|jjt|ƒƒdS)Nz(There is already a template with name %s)r*rUÚ ValueErrorÚaddrTr=rK)r5r$Ú ignoreDupr*r%r%r&Ú_appendTemplate÷s   zDateDetector._appendTemplatecsÔt|tƒr–|‰}d|kr"|jƒ‰tjˆƒ}|sŠˆdkrR‡fdd„}|j|ƒdSdˆkrl|j|dd �dSˆd kr€td ˆƒ}n t|ˆƒ}tjˆ|ƒ|j|ƒt j d t |d dƒ|j ƒt j dt |d dƒ|jƒdS)a‚Add a date template to manage and use in search of dates. Parameters ---------- template : DateTemplate or str Can be either a `DateTemplate` instance, or a string which will be used as the pattern for the `DatePatternRegex` template. The template will then be added to the detector. Raises ------ ValueError If a template already exists with the same name. rú {^LN-BEG}ú {DEFAULT}csˆdkr|jtj@SdS)Nz {^LN-BEG})ÚflagsrÚ LINE_BEGIN)r$)r#r%r&r)sz-DateDetector.appendTemplate..Nz{DATE}F)ÚpreMatchÚ allDefaultsz{NONE}z{UNB}^z date pattern `%r`: `%s`rÚz date pattern regex for %r: %s)r`ra)r?rArrr ÚaddDefaultTemplater'r"r_ÚlogSysÚinfor+r*Údebugr,)r5r$rZfltr%)r#r&ÚappendTemplates,        zDateDetector.appendTemplateNTcsnt|jƒdk}xZ|rtjjntjjD]B}|dk r<||ƒ r...)ÚRE_DATE_PREMATCHÚsub)r()rd)r(r&r)7sz1DateDetector.addDefaultTemplate..)r.)r^)ÚlenrTrRÚ _defCacher8rEr0r_)r5ZfilterTemplaterdrer^r$r%)rdr&rg*szDateDetector.addDefaultTemplatecCs|jS)z6List of template instances managed by the detector. )rT)r5r%r%r&r8;szDateDetector.templatesc Cs¤t|jƒs|jƒtjƒtkr$tjndd„}|tdd|ƒd}d}d}|j}|t|jƒk�rh|j|}|j}|j t j t j B@r¢|tdd|ƒ|j |ƒ}|}�n8|jd|jd} } |tdd || | || d| …|jd|| | …|| | d…|jd ƒ || d| …|jdk�sL|| | d…|jd k�r¾|jd jƒ �r¾|| | d…|jd k�s–|| d| …|jdk�r¾|jdjƒ �r¾|tdd || | …ƒ|j || | ƒ}n|tdd ƒ|j |ƒ}|}|�r^|jƒ} |jƒ} t|jƒdk�s4|j t j t j B@�s4| |jdk�rB| |jdk�rB|td |ƒn|tdƒ|| | |f}d}n |tdƒ|�sò|tdt|jƒƒd}�x0|jD�]$}||k�rª|d7}�qŽ|tdd||jƒ|j}|j |ƒ}|�rª|jƒ} |jƒ} |td|| |j|jd|jƒ|dt|jƒk�rP|j t j t j B@�r.P| dk�rX|j�rX|j|djj �rXP| |jk�st| |jdk�r¨|tdƒ| |dk�r˜|| | |f}d}|d7}�qŽP|d7}�qŽW| �rò|d�rò|\}} } }|td|ƒ|j|}|j}|�r–|jd7_tjƒ|_| |_|j|k�r0|jd7_| || d| …|| f|_| || d|| | d…f|_|�rˆ||jk�rˆ|j|ƒ}||_||fS|tdƒdS)a£Attempts to find date on a log line using templates. This uses the templates' `matchDate` method in an attempt to find a date. It also increments the match hit count for the winning template. Parameters ---------- line : str Line which is searched by the date templates. Returns ------- re.MatchObject, DateTemplate The regex match returned from the first successfully matched template. cWsdS)Nr%)Úargsr%r%r&r)Vsz(DateDetector.matchTime..rz"try to match time for line: %.120sNéÿÿÿz/ try to match last anchored template #%02i ...rzJ try to match last template #%02i (from %r to %r): ...%r==%r %s %r==%r...r z+ boundaries are correct, search in part %rz, boundaries show conflict, try whole searchz" matched last time template #%02izB ** last pattern collision - pattern change, reserve & search ...z8 ** last pattern not found - pattern change, search ...z search template (%i) ...z try template #%02i: %sz2 matched time template #%02i (at %r <= %r, %r) %sz1 ** distance collision - pattern change, reservez use best time template #%02iz no template.éÿÿÿÿ)Nrrrrrs)NN)rorTrgrhÚgetEffectiveLevelÚlogLevelÚlogrYr$rbrrcZLINE_ENDZ matchDaterWrXÚisalnumrÚendr*rNrLÚtimerMrZÚ_reorderTemplate) r5ÚlinervÚmatchÚfoundZignoreBySearchrCÚddtemplr$rNÚendposr%r%r&Ú matchTimeAs´     ..       (          zDateDetector.matchTimecCs|jS)N)r[)r5r%r%r&Ú default_tzÍszDateDetector.default_tzcCst|ƒ|_dS)N)r r[)r5Úvaluer%r%r&r�Ñsc Cs�|dkr|j|ƒ}|d}|dk rŒyT|j||d|jd�}|dk rttjƒtkrptjtd|d|djdƒ|jƒ|SWnt k rŠYnXdS)aAttempts to return the date on a log line using templates. This uses the templates' `getDate` method in an attempt to find a date. For the faster usage, always specify a parameter timeMatch (the previous tuple result of the matchTime), then this will work without locking and without cycle over templates. Parameters ---------- line : str Line which is searched by the date templates. Returns ------- float The Unix timestamp returned from the first successfully matched template or None if not found. Nrr)r�z& got time %f for %r using template %s) r€ZgetDater[rhrtrurvÚgroupr*r\)r5r{Z timeMatchr$Zdater%r%r&ÚgetTimeÕs  zDateDetector.getTimecsþˆrú|j‰ˆˆ‰tjƒtkr0tjtdˆˆjƒˆj|j‰ˆj‰|j ˆkrR|j nˆd‰‡‡‡‡‡‡fdd„}|ƒs˜ˆˆdkr†ˆSˆd‰|ƒs˜ˆSˆˆ=ˆgˆˆd…<x,|j t ˆƒkr؈|j jrØ|j d7_ q®WtjƒtkrötjtdˆˆƒˆSˆS)z“Reorder template (bubble up) in template list if hits grows enough. Parameters ---------- num : int Index of template should be moved. z% -> reorder template #%02i, hits: %rr c sLˆˆj}tjƒtkr6tjtdˆˆˆ|ˆjˆˆjƒˆ|kpJˆˆˆjkS)NzE -> compare template #%02i & #%02i, weight %.3f > %.3f, hits %r > %r)r;rhrtrurvrLrM)Zpweight)r~ÚnumÚposr8Úuntimer;r%r&Ú _moveable s   z0DateDetector._reorderTemplate.._moveablerrz" -> moved template #%02i -> #%02i) rTrhrtrurvrLrMrVr;rZro)r5r…rˆr%)r~r…r†r8r‡r;r&rzùs.    zDateDetector._reorderTemplate)F)NNT)N)rFrGrHrIr1rpr6r_rkrgrJr8r€r�Úsetterr„rzr%r%r%r&rRÞs *    $rRi)N)"Ú __author__Z __copyright__Z __license__ÚcopyryZ threadingrZ datetemplaterrrrrr Zstrptimer Zutilsr Zhelpersr rFrhruÚcompileÚ IGNORECASErmZCacherr'r0Úobjectr1rKrRr%r%r%r&Ús$      nserver/__pycache__/datetemplate.cpython-36.opt-1.pyc000064400000025401152231502140016315 0ustar003 UB(d¶1ã@s0dZdZdZddlZddlZddlmZddlmZm Z m Z dd l m Z e e ƒZejd ƒZejd ƒd fZejd ƒZejdƒZejdƒZejdƒZejdƒZejdƒZejdƒZejdƒdd„fZejdƒZejdƒZejdƒZejdejƒZGdd„deƒZ Gdd„de ƒZ!Gdd „d e ƒZ"Gd!d"„d"e ƒZ#dS)#z Cyril Jaquierz Copyright (c) 2004 Cyril JaquierZGPLéN)Úabstractmethodé)ÚreGroupDictStrptimeÚtimeREÚgetTimePatternREé)Ú getLoggerz(?/srz(^(?:\(\?\w+\))?(?:\^|\((?:\?:)?\^(?!\|))z(?rrAÚ staticmethodrGr r r rr9s   @   rc@s$eZdZdZddd„Zd dd„ZdS) Ú DateEpochzãA date template which searches for Unix timestamps. This includes Unix timestamps which appear at start of a line, optionally within square braces (nsd), or on SELinux audit log lines. Attributes ---------- name regex FNcs¨tj|ƒ|sdn||_||_d|_d‰|r>|s4dn||_d‰|rpdtj‡fdd„|ƒd }d |_|j|ƒn4|sŒd ˆ}|j|d d �ndˆ}|j|ddd�dS)NZEpochrz\d{10,11}\b(?:\.\d{3,6})?Z LongEpochz'\d{10,11}(?:\d{3}(?:\.\d{1,6}|\d{3})?)?ú(csdˆS)Nz(%s)r )Úv)ÚepochREr rrçsz$DateEpoch.__init__..ú)rzf((?:^|(?P(?<=^\[))|(?P(?<=\baudit\()))%s)(?:(?(selinux)(?=:\d+\)))|(?(square)(?=\])))F)r1z*((?P(?<=^\[))?%s)(?(square)(?=\]))rT)r1r2)rrrÚ_longFrmÚ_grpIdxÚRE_EPOCH_PATTERNr!r3)rZ lineBeginOnlyrFZlongFrmr0r )rQrrÜs"  zDateEpoch.__init__cCsn|s|j|ƒ}|rj|j|jƒ}|jr^t|ƒdkr^t|ƒdkrRd|krRt|ƒd}n t|ƒd}t|ƒ|fSdS)axMethod to return the date for a log line. Parameters ---------- line : str Log line, of which the date should be extracted from. default_tz: ignored, Unix timestamps are time zone independent Returns ------- (float, str) Tuple containing a Unix timestamp, and the string of the date which was matched and in turned used to calculated the timestamp. é éÚ.i@BièN)r>r rTrSÚlenÚfloat)rr;r=r@rPr r rrAñs   zDateEpoch.getDate)FNF)NN)rHrIrJrKrrAr r r rrNÐs  rNcsheZdZdZeƒ\ZZejeƒZd ‡fdd„ Z e dd„ƒZ e j dd„ƒZ d‡fd d „ Z dd d „Z‡ZS)ÚDatePatternRegexz�Date template, with regex/pattern Parameters ---------- pattern : str Sets the date templates pattern. Attributes ---------- name regex pattern Nc s.tt|ƒjƒd|_|dk r*|j|f|ŽdS)N)Úsuperr[rÚ_patternr3)rrFÚkwargs)Ú __class__r rrszDatePatternRegex.__init__cCs|jS)aVThe pattern used for regex with strptime "%" time fields. This should be a valid regular expression, of which matching string will be extracted from the log line. strptime style "%" fields will be replaced by appropriate regular expressions, or custom regex groups with names as per the strptime fields can also be used instead. )r])rr r rrF%s zDatePatternRegex.patterncCs|j|ƒdS)N)r3)rrFr r rrF1sTcsÆ||_tj|ƒr$tjd|ƒ}d}}|rBtj|ƒrBtjd|ƒ}d}yL|jjd|ƒ}||j|_|t}t j|ƒrxd|}t t |ƒj |||ƒWn2t k rÀ}ztd||fƒ‚WYdd}~XnXdS)Nr Frz%(\1)sz(?iu)zWFailed to set datepattern '%s' (may be an invalid format or unescaped percent char): %s)r]rEr r!rDÚ _patternREÚ _patternNamerrÚRE_ALPHA_PATTERNr\r[r3r7Ú TypeError)rrFr1r2Zfmtr0r9)r_r rr35s      zDatePatternRegex.setRegexcCs*|s|j|ƒ}|r&t|jƒ|d�|fSdS)aÔMethod to return the date for a log line. This uses a custom version of strptime, using the named groups from the instances `pattern` property. Parameters ---------- line : str Log line, of which the date should be extracted from. default_tz: optionally used to correct timezone Returns ------- (float, str) Tuple containing a Unix timestamp, and the string of the date which was matched and in turned used to calculated the timestamp. )r@N)r>rÚ groupdict)rr;r=r@r r rrALs  zDatePatternRegex.getDate)N)TT)NN)rHrIrJrKrr`rar5r6rrLrFÚsetterr3rAÚ __classcell__r r )r_rr[ s    r[c@s$eZdZdZddd„Zd dd„ZdS) Ú DateTai64nz`A date template which matches TAI64N formate timestamps. Attributes ---------- name regex FcCs"tj|ƒd|_|jd|d�dS)NZTAI64Nz @[0-9a-f]{24})r1)rrrr3)rr1r r rrns zDateTai64n.__init__NcCs:|s|j|ƒ}|r6|jdƒ}|dd…}t|dƒ|fSdS)aqMethod to return the date for a log line. Parameters ---------- line : str Log line, of which the date should be extracted from. default_tz: ignored, since TAI is time zone independent Returns ------- (float, str) Tuple containing a Unix timestamp, and the string of the date which was matched and in turned used to calculated the timestamp. rrérWN)r>r Úint)rr;r=r@ÚvalueZseconds_since_epochr r rrAts    zDateTai64n.getDate)F)NN)rHrIrJrKrrAr r r rrges rg)$Ú __author__Z __copyright__Z __license__r5rÚabcrZstrptimerrrZhelpersrrHr.r6r$r%rrErDrCrBr"r#r-r*r,rbÚ IGNORECASErUÚobjectrrNr[rgr r r rÚs6              =Xserver/__pycache__/datetemplate.cpython-36.pyc000064400000025401152231502140015356 0ustar003 UB(d¶1ã@s0dZdZdZddlZddlZddlmZddlmZm Z m Z dd l m Z e e ƒZejd ƒZejd ƒd fZejd ƒZejdƒZejdƒZejdƒZejdƒZejdƒZejdƒZejdƒdd„fZejdƒZejdƒZejdƒZejdejƒZGdd„deƒZ Gdd„de ƒZ!Gdd „d e ƒZ"Gd!d"„d"e ƒZ#dS)#z Cyril Jaquierz Copyright (c) 2004 Cyril JaquierZGPLéN)Úabstractmethodé)ÚreGroupDictStrptimeÚtimeREÚgetTimePatternREé)Ú getLoggerz(?/srz(^(?:\(\?\w+\))?(?:\^|\((?:\?:)?\^(?!\|))z(?rrAÚ staticmethodrGr r r rr9s   @   rc@s$eZdZdZddd„Zd dd„ZdS) Ú DateEpochzãA date template which searches for Unix timestamps. This includes Unix timestamps which appear at start of a line, optionally within square braces (nsd), or on SELinux audit log lines. Attributes ---------- name regex FNcs¨tj|ƒ|sdn||_||_d|_d‰|r>|s4dn||_d‰|rpdtj‡fdd„|ƒd }d |_|j|ƒn4|sŒd ˆ}|j|d d �ndˆ}|j|ddd�dS)NZEpochrz\d{10,11}\b(?:\.\d{3,6})?Z LongEpochz'\d{10,11}(?:\d{3}(?:\.\d{1,6}|\d{3})?)?ú(csdˆS)Nz(%s)r )Úv)ÚepochREr rrçsz$DateEpoch.__init__..ú)rzf((?:^|(?P(?<=^\[))|(?P(?<=\baudit\()))%s)(?:(?(selinux)(?=:\d+\)))|(?(square)(?=\])))F)r1z*((?P(?<=^\[))?%s)(?(square)(?=\]))rT)r1r2)rrrÚ_longFrmÚ_grpIdxÚRE_EPOCH_PATTERNr!r3)rZ lineBeginOnlyrFZlongFrmr0r )rQrrÜs"  zDateEpoch.__init__cCsn|s|j|ƒ}|rj|j|jƒ}|jr^t|ƒdkr^t|ƒdkrRd|krRt|ƒd}n t|ƒd}t|ƒ|fSdS)axMethod to return the date for a log line. Parameters ---------- line : str Log line, of which the date should be extracted from. default_tz: ignored, Unix timestamps are time zone independent Returns ------- (float, str) Tuple containing a Unix timestamp, and the string of the date which was matched and in turned used to calculated the timestamp. é éÚ.i@BièN)r>r rTrSÚlenÚfloat)rr;r=r@rPr r rrAñs   zDateEpoch.getDate)FNF)NN)rHrIrJrKrrAr r r rrNÐs  rNcsheZdZdZeƒ\ZZejeƒZd ‡fdd„ Z e dd„ƒZ e j dd„ƒZ d‡fd d „ Z dd d „Z‡ZS)ÚDatePatternRegexz�Date template, with regex/pattern Parameters ---------- pattern : str Sets the date templates pattern. Attributes ---------- name regex pattern Nc s.tt|ƒjƒd|_|dk r*|j|f|ŽdS)N)Úsuperr[rÚ_patternr3)rrFÚkwargs)Ú __class__r rrszDatePatternRegex.__init__cCs|jS)aVThe pattern used for regex with strptime "%" time fields. This should be a valid regular expression, of which matching string will be extracted from the log line. strptime style "%" fields will be replaced by appropriate regular expressions, or custom regex groups with names as per the strptime fields can also be used instead. )r])rr r rrF%s zDatePatternRegex.patterncCs|j|ƒdS)N)r3)rrFr r rrF1sTcsÆ||_tj|ƒr$tjd|ƒ}d}}|rBtj|ƒrBtjd|ƒ}d}yL|jjd|ƒ}||j|_|t}t j|ƒrxd|}t t |ƒj |||ƒWn2t k rÀ}ztd||fƒ‚WYdd}~XnXdS)Nr Frz%(\1)sz(?iu)zWFailed to set datepattern '%s' (may be an invalid format or unescaped percent char): %s)r]rEr r!rDÚ _patternREÚ _patternNamerrÚRE_ALPHA_PATTERNr\r[r3r7Ú TypeError)rrFr1r2Zfmtr0r9)r_r rr35s      zDatePatternRegex.setRegexcCs*|s|j|ƒ}|r&t|jƒ|d�|fSdS)aÔMethod to return the date for a log line. This uses a custom version of strptime, using the named groups from the instances `pattern` property. Parameters ---------- line : str Log line, of which the date should be extracted from. default_tz: optionally used to correct timezone Returns ------- (float, str) Tuple containing a Unix timestamp, and the string of the date which was matched and in turned used to calculated the timestamp. )r@N)r>rÚ groupdict)rr;r=r@r r rrALs  zDatePatternRegex.getDate)N)TT)NN)rHrIrJrKrr`rar5r6rrLrFÚsetterr3rAÚ __classcell__r r )r_rr[ s    r[c@s$eZdZdZddd„Zd dd„ZdS) Ú DateTai64nz`A date template which matches TAI64N formate timestamps. Attributes ---------- name regex FcCs"tj|ƒd|_|jd|d�dS)NZTAI64Nz @[0-9a-f]{24})r1)rrrr3)rr1r r rrns zDateTai64n.__init__NcCs:|s|j|ƒ}|r6|jdƒ}|dd…}t|dƒ|fSdS)aqMethod to return the date for a log line. Parameters ---------- line : str Log line, of which the date should be extracted from. default_tz: ignored, since TAI is time zone independent Returns ------- (float, str) Tuple containing a Unix timestamp, and the string of the date which was matched and in turned used to calculated the timestamp. rrérWN)r>r Úint)rr;r=r@ÚvalueZseconds_since_epochr r rrAts    zDateTai64n.getDate)F)NN)rHrIrJrKrrAr r r rrges rg)$Ú __author__Z __copyright__Z __license__r5rÚabcrZstrptimerrrZhelpersrrHr.r6r$r%rrErDrCrBr"r#r-r*r,rbÚ IGNORECASErUÚobjectrrNr[rgr r r rÚs6              =Xserver/__pycache__/failmanager.cpython-36.opt-1.pyc000064400000011314152231502140016110 0ustar003 UB(dåã@spdZdZdZddlmZddlZddlmZmZdd l m Z m Z e e ƒZ ejZGd d „d ƒZGd d „d eƒZdS)z Cyril Jaquierz Copyright (c) 2004 Cyril JaquierZGPLé)ÚLockNé)Ú FailTicketÚ BanTicketé)Ú getLoggerÚ BgServicec@sxeZdZdd„Zdd„Zdd„Zdd„Zd d „Zd d „Zd d„Z dd„Z ddd„Z dd„Z dd„Z dd„Zddd„ZdS) Ú FailManagercCs4tƒ|_tƒ|_d|_d|_d|_d|_tƒ|_ dS)NéiXré) rÚ_FailManager__lockÚdictÚ_FailManager__failListÚ_FailManager__maxRetryÚ_FailManager__maxTimeÚ_FailManager__failTotalÚ maxMatchesrÚ_FailManager__bgSvc)Úself©rú!/usr/lib/python3.6/failmanager.pyÚ__init__(szFailManager.__init__cCs ||_dS)N)r)rÚvaluerrrÚ setFailTotal1szFailManager.setFailTotalcCs|jS)N)r)rrrrÚ getFailTotal4szFailManager.getFailTotalc Cs8|j�(t|jƒtdd„t|jjƒƒDƒƒfSQRXdS)NcSsg|] }|jƒ‘qSr)ÚgetRetry)Ú.0Úfrrrú :sz,FailManager.getFailCount..)r ÚlenrÚsumÚlistÚvalues)rrrrÚ getFailCount7szFailManager.getFailCountcCs ||_dS)N)r)rrrrrÚ setMaxRetry<szFailManager.setMaxRetrycCs|jS)N)r)rrrrÚ getMaxRetry?szFailManager.getMaxRetrycCs ||_dS)N)r)rrrrrÚ setMaxTimeBszFailManager.setMaxTimecCs|jS)N)r)rrrrÚ getMaxTimeEszFailManager.getMaxTimerFc Cs d}|j��€|jƒ}y¬|j|}||kr4d}d}n*|jrB|jƒnd}|jƒ}|dkr^|d7}|jƒ} |j| |jƒ|j |||ƒ|jr¶|jƒ}t |ƒ|jkrÀ|j ||j d…ƒn |j dƒWnht k �r*|sât |tƒrê|jƒSt |tƒrú|}n tj|ƒ}||jƒk�r|j|ƒ||j|<YnX|jƒ}|jd7_tjƒtk�rˆdjdd„|jjƒDƒƒ} tjtd|jt |jƒ| fƒWdQRX|jjƒ|S)Nrrz, cSs g|]\}}d||jƒf‘qS)z%s:%d)r)rÚkÚvrrrrvsz*FailManager.addFailure..zMTotal # of detected failures: %d. Current failures from %d IPs (IP:count): %s)r ZgetIDrrZ getMatchesZ getAttemptÚgetTimeZ adjustTimerZincrZ setMatchesÚKeyErrorÚ isinstancerrrZwrapZsetRetryrÚlogSysZgetEffectiveLevelÚlogLevelÚjoinÚitemsÚlogrÚservice) rÚticketÚcountZobservedZattemptsÚfidZfDataZmatchesZattemptZunixTimeZfailures_summaryrrrÚ addFailureHsL        zFailManager.addFailurecCs t|jƒS)N)rr)rrrrÚsize~szFailManager.sizec sºˆ|j8‰|j�–‡fdd„|jjƒDƒ}t|ƒt|jƒkrFtƒ|_n t|ƒsRdSt|ƒdt|jƒdkr„x4|D] }|j|=qrWnt‡fdd„|jjƒDƒƒ|_WdQRX|jjƒdS)Ncs g|]\}}|jƒˆkr|‘qSr)r*)rr5Úitem)Útimerrr„sz'FailManager.cleanup..g@g@c3s&|]\}}|jƒˆkr||fVqdS)N)r*)rr5r8)r9rrú ’sz&FailManager.cleanup..)rr rr0rr rr2)rr9Ztodeleter5r)r9rÚcleanup�s   (zFailManager.cleanupcCs8|j�(y |j|=Wntk r(YnXWdQRXdS)N)r rr+)rr5rrrÚ delFailure–s  zFailManager.delFailureNc Csp|j�RxJ|dk r"||jkr"|gn|jD](}|j|}|jƒ|jkr(|j|=|Sq(WWdQRX|jjƒt‚dS)N)r rrrrr2ÚFailManagerEmpty)rr5ÚdatarrrÚtoBan�s$  zFailManager.toBan)rF)N)Ú__name__Ú __module__Ú __qualname__rrrr#r$r%r&r'r6r7r;r<r?rrrrr &s  6r c@s eZdZdS)r=N)r@rArBrrrrr=¨sr=)Ú __author__Z __copyright__Z __license__Z threadingrZloggingr3rrZhelpersrrr@r-ÚDEBUGr.r Ú Exceptionr=rrrrÚs server/__pycache__/failmanager.cpython-36.pyc000064400000011314152231502140015151 0ustar003 UB(dåã@spdZdZdZddlmZddlZddlmZmZdd l m Z m Z e e ƒZ ejZGd d „d ƒZGd d „d eƒZdS)z Cyril Jaquierz Copyright (c) 2004 Cyril JaquierZGPLé)ÚLockNé)Ú FailTicketÚ BanTicketé)Ú getLoggerÚ BgServicec@sxeZdZdd„Zdd„Zdd„Zdd„Zd d „Zd d „Zd d„Z dd„Z ddd„Z dd„Z dd„Z dd„Zddd„ZdS) Ú FailManagercCs4tƒ|_tƒ|_d|_d|_d|_d|_tƒ|_ dS)NéiXré) rÚ_FailManager__lockÚdictÚ_FailManager__failListÚ_FailManager__maxRetryÚ_FailManager__maxTimeÚ_FailManager__failTotalÚ maxMatchesrÚ_FailManager__bgSvc)Úself©rú!/usr/lib/python3.6/failmanager.pyÚ__init__(szFailManager.__init__cCs ||_dS)N)r)rÚvaluerrrÚ setFailTotal1szFailManager.setFailTotalcCs|jS)N)r)rrrrÚ getFailTotal4szFailManager.getFailTotalc Cs8|j�(t|jƒtdd„t|jjƒƒDƒƒfSQRXdS)NcSsg|] }|jƒ‘qSr)ÚgetRetry)Ú.0Úfrrrú :sz,FailManager.getFailCount..)r ÚlenrÚsumÚlistÚvalues)rrrrÚ getFailCount7szFailManager.getFailCountcCs ||_dS)N)r)rrrrrÚ setMaxRetry<szFailManager.setMaxRetrycCs|jS)N)r)rrrrÚ getMaxRetry?szFailManager.getMaxRetrycCs ||_dS)N)r)rrrrrÚ setMaxTimeBszFailManager.setMaxTimecCs|jS)N)r)rrrrÚ getMaxTimeEszFailManager.getMaxTimerFc Cs d}|j��€|jƒ}y¬|j|}||kr4d}d}n*|jrB|jƒnd}|jƒ}|dkr^|d7}|jƒ} |j| |jƒ|j |||ƒ|jr¶|jƒ}t |ƒ|jkrÀ|j ||j d…ƒn |j dƒWnht k �r*|sât |tƒrê|jƒSt |tƒrú|}n tj|ƒ}||jƒk�r|j|ƒ||j|<YnX|jƒ}|jd7_tjƒtk�rˆdjdd„|jjƒDƒƒ} tjtd|jt |jƒ| fƒWdQRX|jjƒ|S)Nrrz, cSs g|]\}}d||jƒf‘qS)z%s:%d)r)rÚkÚvrrrrvsz*FailManager.addFailure..zMTotal # of detected failures: %d. Current failures from %d IPs (IP:count): %s)r ZgetIDrrZ getMatchesZ getAttemptÚgetTimeZ adjustTimerZincrZ setMatchesÚKeyErrorÚ isinstancerrrZwrapZsetRetryrÚlogSysZgetEffectiveLevelÚlogLevelÚjoinÚitemsÚlogrÚservice) rÚticketÚcountZobservedZattemptsÚfidZfDataZmatchesZattemptZunixTimeZfailures_summaryrrrÚ addFailureHsL        zFailManager.addFailurecCs t|jƒS)N)rr)rrrrÚsize~szFailManager.sizec sºˆ|j8‰|j�–‡fdd„|jjƒDƒ}t|ƒt|jƒkrFtƒ|_n t|ƒsRdSt|ƒdt|jƒdkr„x4|D] }|j|=qrWnt‡fdd„|jjƒDƒƒ|_WdQRX|jjƒdS)Ncs g|]\}}|jƒˆkr|‘qSr)r*)rr5Úitem)Útimerrr„sz'FailManager.cleanup..g@g@c3s&|]\}}|jƒˆkr||fVqdS)N)r*)rr5r8)r9rrú ’sz&FailManager.cleanup..)rr rr0rr rr2)rr9Ztodeleter5r)r9rÚcleanup�s   (zFailManager.cleanupcCs8|j�(y |j|=Wntk r(YnXWdQRXdS)N)r rr+)rr5rrrÚ delFailure–s  zFailManager.delFailureNc Csp|j�RxJ|dk r"||jkr"|gn|jD](}|j|}|jƒ|jkr(|j|=|Sq(WWdQRX|jjƒt‚dS)N)r rrrrr2ÚFailManagerEmpty)rr5ÚdatarrrÚtoBan�s$  zFailManager.toBan)rF)N)Ú__name__Ú __module__Ú __qualname__rrrr#r$r%r&r'r6r7r;r<r?rrrrr &s  6r c@s eZdZdS)r=N)r@rArBrrrrr=¨sr=)Ú __author__Z __copyright__Z __license__Z threadingrZloggingr3rrZhelpersrrr@r-ÚDEBUGr.r Ú Exceptionr=rrrrÚs server/__pycache__/failregex.cpython-36.opt-1.pyc000064400000021217152231502140015613 0ustar003 UB(dÜ4ã@sÊdZdZdZddlZddlZddlZddlmZejdƒZ ejdƒZ d ej fd ej fd d d d d gZ dZdZdZdZdZdZdZde ee efe e<de ee efe e<de ee ee efe e<e ee ee ee ee ee ee ee ee ee ee ee edddœZdddœZyejddƒejdƒdfZWnfZYnXd d!„Zd"Zd#ZeefZejd$d%jeƒd&ƒZGd'd(„d(ƒZGd)d*„d*e ƒZ!d1Z"e"d2Z#Gd/d0„d0eƒZ$dS)3z Cyril Jaquierz Copyright (c) 2004 Cyril JaquierZGPLéNé)ÚIPAddrz z^(/?)F-([A-Z0-9_\-]+)$z(?:::f{4,6}:)?(?P%s)z (?P%s)z(?P[\w\-.^_]*\w)Úz (?P\d+)éééééz\[?(?:%s|%s)\]?z (?:%s|%s)z\[?(?:%s|%s)(?:/%s)?\]?z (?P\S+)z(?P\w+))ZIP4zF-IP4/ZIP6zF-IP6/ZADDRzF-ADDR/ZCIDRzF-CIDR/ZSUBNETz F-SUBNET/ZDNSzF-DNS/zF-ID/zF-PORT/ÚfidZfport)ÚidZportz ^re(?i:val)$ZreVALz(?’sz"Regex.__init__..rz-Unable to compile regular expression '%s': %s)Ú _matchCacherÚ_resolveHostTagÚlstripÚRegexExceptionÚR_GLOB2LOCFLAGSÚsubÚreÚcompileÚ MULTILINEÚ _regexObjÚ_regexÚ _altValuesÚ _tupleValuesÚ groupindexÚ COMPLNAME_CREÚmatchÚgrouprÚ ALTNAME_PREÚappendÚsortrÚ sre_constantsÚerrorÚ_getGroupsWithAltÚ _getGroupsÚ getGroups)ÚselfÚregexZ multilineÚkwargsrÚnÚgÚerrrÚ__init__‚s4    zRegex.__init__cCsd|jj|jfS)Nz%s(%r))Ú __class__Ú__name__r$)r3rrrÚ__str__¤sz Regex.__str__Úyescs*tƒ‰ddi‰‡‡‡fdd„}tj||ƒS)NÚnlrc sÊ|jƒ}|dd …}|dkr0tˆd kr*tntS|dkrVˆd}|dˆd<d|fSyt|Stk rrYnXtj|ƒ}|rÆ|jƒ}|d}|dr¬ˆj |ƒr¨dS|Sdˆ|<t |ƒ}d |fS|S) NrZHOSTÚnoZ SKIPLINESr>z!\n(?P(?:(?:.*\n)*?))rú)z(?P<%s>éÿÿÿÿ)r?) r*ÚR_HOSTÚRI_HOSTÚRI_ADDRÚRH4TAGÚKeyErrorÚ FCUSTNAME_CREr)Úgroupsrr)ÚmrZtnr>)ÚopenTagsÚpropsÚuseDnsrrÚsubstTagµs0      z'Regex._resolveHostTag..substTag)ÚdictÚFTAG_CREr)r4rLrMr)rJrKrLrr­s'zRegex._resolveHostTagcCs|jS)N)r$)r3rrrÚgetRegexäszRegex.getRegexcCsdjdd„|DƒƒdS)NÚ cSs g|]}dj|ddd…ƒ‘qS)rNr)Újoin)rÚvrrrrìsz(Regex._tupleLinesBuf..)rR)Ú tupleLinesrrrÚ_tupleLinesBufêszRegex._tupleLinesBufNc Cs¦|}t|tƒstj|ƒ}|jj|ƒ|_|j�r¢|dkr:|}t|ƒdkrV||_g|_ �nLy$|jj j dd|jj ƒdƒd}Wnt k r’d}YnXy"|jj jd|jjƒdƒd}Wn t k rÖt|jj ƒ}YnX|jj jdd|ƒ}|jj jdd|ƒ}|||…|_|d|…|_ d}xp|jƒD]d} x\t|j|d…ƒD]F\} } dj| ddd…ƒ| k�r>|j j|jj|| ƒƒ|| 7}P�q>W�q&W|j j||d…ƒdS)NrrQrrr)Ú isinstanceÚstrrrUr#ÚsearchrrÚ_matchedTupleLinesÚ_unmatchedTupleLinesÚstringÚrindexÚstartÚ ValueErrorÚindexÚendÚcountÚgetSkippedLinesÚ enumeraterRr,ÚpopÚextend) r3rTZorgLinesZbufZmatchLineStartZ matchLineEndZ lineCount1Z lineCount2r6Z skippedLinerIZmatchedTupleLinerrrrXösH       z Regex.searchcCs|jr dSdSdS)NTF)r)r3rrrÚ hasMatched$szRegex.hasMatchedcCs |jjƒS)N)rÚ groupdict)r3rrrr1.szRegex._getGroupscCsž|jjƒ}|jrFx4|jD]*\}}|j|ƒ}|r|j|ƒ r|||<qW|jršxL|jD]B\}}|j|ƒ}|j|ƒ}t|tƒr†||f7}n||f}|||<qTW|S)N)rrgr%rr&rVÚtuple)r3Úfailrr6rSÚtrrrr01s        zRegex._getGroupsWithAltcCsdS)Nr)r3rrrr2FszRegex.getGroupsc CsŽ|js gSd}d}xpy4|jjd|ƒdk r>||jjd|ƒ7}|d7}Wqtk r^PYqtk r~dtjkrx‚PYqXqW|jdƒS)Nrrz skiplines%irZPyPyF)rr*Ú IndexErrorrFÚsysÚversionÚ splitlines)r3Z skippedLinesr6rrrrbOs    zRegex.getSkippedLinescCs|jƒs gS|jSdS)N)rfrZ)r3rrrÚgetUnmatchedTupleLineshszRegex.getUnmatchedTupleLinescCs |jƒs gSdd„|jDƒSdS)NcSsg|]}dj|ƒ‘qS)r)rR)rÚlinerrrrrsz+Regex.getUnmatchedLines..)rfrZ)r3rrrÚgetUnmatchedLinesnszRegex.getUnmatchedLinescCs|jƒs gS|jSdS)N)rfrY)r3rrrÚgetMatchedTupleLines{szRegex.getMatchedTupleLinescCs |jƒs gSdd„|jDƒSdS)NcSsg|]}dj|ƒ‘qS)r)rR)rrprrrr…sz)Regex.getMatchedLines..)rfrY)r3rrrÚgetMatchedLines�szRegex.getMatchedLines)F)r=)N)r;Ú __module__Ú __qualname__r9r<Ú staticmethodrrPrUrXrfr1r0r2rbrorqrrrsrrrrrys  "  6 .   rc@s eZdZdS)rN)r;rtrurrrrr‹srÚip4Úip6ÚdnsÚmlfidc@s2eZdZd dd„Zefdd„Zdd„Zdd „ZdS) Ú FailRegexNc sRtjˆ|f|އfdd„tDƒ rNˆdks@‡fdd„tDƒ rNtdˆjƒ‚dS)Ncsg|]}|ˆjjkr|‘qSr)r#r')rÚgrp)r3rrr¬sz&FailRegex.__init__..csg|]}|ˆjjkr|‘qSr)r#r')rr|)Ú prefRegexrrr®szNo failure-id group in '%s')rr9ÚFAILURE_ID_PRESENTSrr$)r3r4r}r5r)r}r3rr9¨s zFailRegex.__init__c Csxd}xB|D]:}y|jj|ƒ}Wnttfk r8w YnX|dk r Pq W|dkrp|jj}|jj}td||fƒ‚t|ƒS)Nz!No group found in '%s' using '%s')rr*rkrFr[r rrW)r3rHr r|ÚsÚrrrrÚ getFailID¸s zFailRegex.getFailIDcCs |jdƒS)Nrwrxry)rwrxry)r�)r3rrrÚgetHostÎszFailRegex.getHostcCs(|jƒ}t|jdƒt|jdƒp"tjƒƒS)NrwrxZcidr)rwrx)r2rr�ÚintrZ CIDR_UNSPEC)r3rirrrÚgetIPÑszFailRegex.getIP)N)r;rtrur9ÚFAILURE_ID_GROPSr�r‚r„rrrrr{Ÿs  r{)r rwrxry)rz)%Ú __author__Z __copyright__Z __license__r r.rlZipdnsrr!rOrGZIP_4_REZIP_6_RErBZRI_IPV4ZRI_IPV6ZRI_DNSrDrCZRI_CIDRZ RI_SUBNETrEr rXrrr+Z TUPNAME_PRErrRr(rÚ Exceptionrr…r~r{rrrrÚst        server/__pycache__/failregex.cpython-36.pyc000064400000021217152231502140014654 0ustar003 UB(dÜ4ã@sÊdZdZdZddlZddlZddlZddlmZejdƒZ ejdƒZ d ej fd ej fd d d d d gZ dZdZdZdZdZdZdZde ee efe e<de ee efe e<de ee ee efe e<e ee ee ee ee ee ee ee ee ee ee ee edddœZdddœZyejddƒejdƒdfZWnfZYnXd d!„Zd"Zd#ZeefZejd$d%jeƒd&ƒZGd'd(„d(ƒZGd)d*„d*e ƒZ!d1Z"e"d2Z#Gd/d0„d0eƒZ$dS)3z Cyril Jaquierz Copyright (c) 2004 Cyril JaquierZGPLéNé)ÚIPAddrz z^(/?)F-([A-Z0-9_\-]+)$z(?:::f{4,6}:)?(?P%s)z (?P%s)z(?P[\w\-.^_]*\w)Úz (?P\d+)éééééz\[?(?:%s|%s)\]?z (?:%s|%s)z\[?(?:%s|%s)(?:/%s)?\]?z (?P\S+)z(?P\w+))ZIP4zF-IP4/ZIP6zF-IP6/ZADDRzF-ADDR/ZCIDRzF-CIDR/ZSUBNETz F-SUBNET/ZDNSzF-DNS/zF-ID/zF-PORT/ÚfidZfport)ÚidZportz ^re(?i:val)$ZreVALz(?’sz"Regex.__init__..rz-Unable to compile regular expression '%s': %s)Ú _matchCacherÚ_resolveHostTagÚlstripÚRegexExceptionÚR_GLOB2LOCFLAGSÚsubÚreÚcompileÚ MULTILINEÚ _regexObjÚ_regexÚ _altValuesÚ _tupleValuesÚ groupindexÚ COMPLNAME_CREÚmatchÚgrouprÚ ALTNAME_PREÚappendÚsortrÚ sre_constantsÚerrorÚ_getGroupsWithAltÚ _getGroupsÚ getGroups)ÚselfÚregexZ multilineÚkwargsrÚnÚgÚerrrÚ__init__‚s4    zRegex.__init__cCsd|jj|jfS)Nz%s(%r))Ú __class__Ú__name__r$)r3rrrÚ__str__¤sz Regex.__str__Úyescs*tƒ‰ddi‰‡‡‡fdd„}tj||ƒS)NÚnlrc sÊ|jƒ}|dd …}|dkr0tˆd kr*tntS|dkrVˆd}|dˆd<d|fSyt|Stk rrYnXtj|ƒ}|rÆ|jƒ}|d}|dr¬ˆj |ƒr¨dS|Sdˆ|<t |ƒ}d |fS|S) NrZHOSTÚnoZ SKIPLINESr>z!\n(?P(?:(?:.*\n)*?))rú)z(?P<%s>éÿÿÿÿ)r?) r*ÚR_HOSTÚRI_HOSTÚRI_ADDRÚRH4TAGÚKeyErrorÚ FCUSTNAME_CREr)Úgroupsrr)ÚmrZtnr>)ÚopenTagsÚpropsÚuseDnsrrÚsubstTagµs0      z'Regex._resolveHostTag..substTag)ÚdictÚFTAG_CREr)r4rLrMr)rJrKrLrr­s'zRegex._resolveHostTagcCs|jS)N)r$)r3rrrÚgetRegexäszRegex.getRegexcCsdjdd„|DƒƒdS)NÚ cSs g|]}dj|ddd…ƒ‘qS)rNr)Újoin)rÚvrrrrìsz(Regex._tupleLinesBuf..)rR)Ú tupleLinesrrrÚ_tupleLinesBufêszRegex._tupleLinesBufNc Cs¦|}t|tƒstj|ƒ}|jj|ƒ|_|j�r¢|dkr:|}t|ƒdkrV||_g|_ �nLy$|jj j dd|jj ƒdƒd}Wnt k r’d}YnXy"|jj jd|jjƒdƒd}Wn t k rÖt|jj ƒ}YnX|jj jdd|ƒ}|jj jdd|ƒ}|||…|_|d|…|_ d}xp|jƒD]d} x\t|j|d…ƒD]F\} } dj| ddd…ƒ| k�r>|j j|jj|| ƒƒ|| 7}P�q>W�q&W|j j||d…ƒdS)NrrQrrr)Ú isinstanceÚstrrrUr#ÚsearchrrÚ_matchedTupleLinesÚ_unmatchedTupleLinesÚstringÚrindexÚstartÚ ValueErrorÚindexÚendÚcountÚgetSkippedLinesÚ enumeraterRr,ÚpopÚextend) r3rTZorgLinesZbufZmatchLineStartZ matchLineEndZ lineCount1Z lineCount2r6Z skippedLinerIZmatchedTupleLinerrrrXösH       z Regex.searchcCs|jr dSdSdS)NTF)r)r3rrrÚ hasMatched$szRegex.hasMatchedcCs |jjƒS)N)rÚ groupdict)r3rrrr1.szRegex._getGroupscCsž|jjƒ}|jrFx4|jD]*\}}|j|ƒ}|r|j|ƒ r|||<qW|jršxL|jD]B\}}|j|ƒ}|j|ƒ}t|tƒr†||f7}n||f}|||<qTW|S)N)rrgr%rr&rVÚtuple)r3Úfailrr6rSÚtrrrr01s        zRegex._getGroupsWithAltcCsdS)Nr)r3rrrr2FszRegex.getGroupsc CsŽ|js gSd}d}xpy4|jjd|ƒdk r>||jjd|ƒ7}|d7}Wqtk r^PYqtk r~dtjkrx‚PYqXqW|jdƒS)Nrrz skiplines%irZPyPyF)rr*Ú IndexErrorrFÚsysÚversionÚ splitlines)r3Z skippedLinesr6rrrrbOs    zRegex.getSkippedLinescCs|jƒs gS|jSdS)N)rfrZ)r3rrrÚgetUnmatchedTupleLineshszRegex.getUnmatchedTupleLinescCs |jƒs gSdd„|jDƒSdS)NcSsg|]}dj|ƒ‘qS)r)rR)rÚlinerrrrrsz+Regex.getUnmatchedLines..)rfrZ)r3rrrÚgetUnmatchedLinesnszRegex.getUnmatchedLinescCs|jƒs gS|jSdS)N)rfrY)r3rrrÚgetMatchedTupleLines{szRegex.getMatchedTupleLinescCs |jƒs gSdd„|jDƒSdS)NcSsg|]}dj|ƒ‘qS)r)rR)rrprrrr…sz)Regex.getMatchedLines..)rfrY)r3rrrÚgetMatchedLines�szRegex.getMatchedLines)F)r=)N)r;Ú __module__Ú __qualname__r9r<Ú staticmethodrrPrUrXrfr1r0r2rbrorqrrrsrrrrrys  "  6 .   rc@s eZdZdS)rN)r;rtrurrrrr‹srÚip4Úip6ÚdnsÚmlfidc@s2eZdZd dd„Zefdd„Zdd„Zdd „ZdS) Ú FailRegexNc sRtjˆ|f|އfdd„tDƒ rNˆdks@‡fdd„tDƒ rNtdˆjƒ‚dS)Ncsg|]}|ˆjjkr|‘qSr)r#r')rÚgrp)r3rrr¬sz&FailRegex.__init__..csg|]}|ˆjjkr|‘qSr)r#r')rr|)Ú prefRegexrrr®szNo failure-id group in '%s')rr9ÚFAILURE_ID_PRESENTSrr$)r3r4r}r5r)r}r3rr9¨s zFailRegex.__init__c Csxd}xB|D]:}y|jj|ƒ}Wnttfk r8w YnX|dk r Pq W|dkrp|jj}|jj}td||fƒ‚t|ƒS)Nz!No group found in '%s' using '%s')rr*rkrFr[r rrW)r3rHr r|ÚsÚrrrrÚ getFailID¸s zFailRegex.getFailIDcCs |jdƒS)Nrwrxry)rwrxry)r�)r3rrrÚgetHostÎszFailRegex.getHostcCs(|jƒ}t|jdƒt|jdƒp"tjƒƒS)NrwrxZcidr)rwrx)r2rr�ÚintrZ CIDR_UNSPEC)r3rirrrÚgetIPÑszFailRegex.getIP)N)r;rtrur9ÚFAILURE_ID_GROPSr�r‚r„rrrrr{Ÿs  r{)r rwrxry)rz)%Ú __author__Z __copyright__Z __license__r r.rlZipdnsrr!rOrGZIP_4_REZIP_6_RErBZRI_IPV4ZRI_IPV6ZRI_DNSrDrCZRI_CIDRZ RI_SUBNETrEr rXrrr+Z TUPNAME_PRErrRr(rÚ Exceptionrr…r~r{rrrrÚst        server/__pycache__/filter.cpython-36.opt-1.pyc000064400000102354152231502140015134 0ustar003 UB(d,¾ã@s¨dZdZdZddlZddlZddlZddlZddlZddlZddl Z ddl Z ddl m Z ddl mZmZddlmZmZdd lmZdd lmZdd lmZdd lmZmZdd lmZddlmZm Z m!Z!ddl"m#Z#ddl$m%Z%ddl&m'Z'm(Z(e'e)ƒZ*Gdd„deƒZ+Gdd„de+ƒZ,y6ddl-Z-ye-j.Z/e/dƒj0ƒWne-j1Z/YnXWn$e2k �rvddl.Z.e.j3Z/YnXGdd„dƒZ4e%j5dd!d�a6Gdd„de+ƒZ7dS)"z'Cyril Jaquier and Fail2Ban Contributorsz>Copyright (c) 2004 Cyril Jaquier, 2011-2013 Yaroslav HalchenkoZGPLéNé)ÚActions)ÚFailManagerEmptyÚ FailManager)ÚDNSUtilsÚIPAddr)Ú Observers)Ú FailTicket)Ú JailThread)Ú DateDetectorÚvalidateTimeZone)ÚMyTime)Ú FailRegexÚRegexÚRegexException)Ú CommandAction)ÚUtilsé)Ú getLoggerÚ PREFER_ENCc@seZdZdndd„Zdd„Zedd„ƒZdd „Zdod d „Zed d„ƒZ edd„ƒZ e j dd„ƒZ dd„Z dpdd„Z dd„Zdd„Zdqdd„Zdd„Zdd „Zd!d"„Zd#d$„Zd%d&„Zd'd(„Zd)d*„Zd+d,„Zd-d.„Zd/d0„Zd1d2„Zd3d4„Zd5d6„Zd7d8„Zd9d:„Zd;d<„Z ed=d>„ƒZ!e!j d?d>„ƒZ!ed@dA„ƒZ"e"j dBdA„ƒZ"drdCdD„Z#dsdFdG„Z$dHdI„Z%edJdK„ƒZ&e&j dLdK„ƒZ&dMdN„Z'dtdOdP„Z(dudRdS„Z)dTdU„Z*dvdVdW„Z+dwdXdY„Z,dZd[„Z-dxd\d]„Z.dyd^d_„Z/dzdadb„Z0d{dcdd„Z1ffdedf„Z2dgdh„Z3d|didj„Z4d}dldm„Z5dS)~ÚFilterÚwarncCsôtj|ƒ||_tƒ|_d|_tƒ|_tƒ|_|j |ƒd|_ d|_ t ƒ|_ g|_d|_d|_d|_g|_d|_d|_d |_d|_t|_d|_d|_d|_i|_d|_d|_d|_d|_ d|_!d|_"d|_#d|_$d|j%|_&t'ƒ|_(t)j*d |ƒdS) NiXTFrÚé?rzf2b/f.z Created %sllûÿÿÿ)+r Ú__init__ÚjailrÚ failManagerÚ_Filter__prefRegexÚlistÚ_Filter__failRegexÚ_Filter__ignoreRegexÚ setUseDnsÚ_Filter__findTimeÚ_Filter__ignoreSelfÚsetÚ_Filter__ignoreIpSetÚ_Filter__ignoreIpListÚ_Filter__ignoreCommandÚ_Filter__ignoreCacheÚ_Filter__lineBufferSizeÚ_Filter__lineBufferÚ_Filter__lastTimeTextÚ_Filter__lastDateÚ_Filter__nextSvcTimeÚ_Filter__logtimezonerÚ_Filter__encodingÚ_Filter__mlfidCacheÚ_errorsÚ _nextUpdateTMÚ_pendDBUpdatesÚ returnRawHostÚ checkAllRegexÚ ignorePendingÚ onIgnoreRegexÚ checkFindTimeÚ inOperationÚticksÚ procLinesÚjailNameÚnamer Ú dateDetectorÚlogSysÚdebug)ÚselfrÚuseDns©rCú/usr/lib/python3.6/filter.pyrAsF   zFilter.__init__cCsd|jj|jfS)Nz%s(%r))Ú __class__Ú__name__r)rArCrCrDÚ__repr__†szFilter.__repr__cCs|jdk r|jjpdS)Nz ~jailless~)rr=)rArCrCrDr<‰szFilter.jailNamecCs|jƒ|jƒ|jƒdS)z8 Clear all lists/dicts parameters (used by reloading) N)Ú delFailRegexÚdelIgnoreRegexÚ delIgnoreIP)rArCrCrDÚclearAllParams�szFilter.clearAllParamsTcCsb|r0|jƒt|dƒr^tdd„|jƒDƒƒ|_n.t|dƒr^x|jD]}|j|ƒqBWt|dƒdS)z@ Begin or end of reloading resp. refreshing of all parameters Ú getLogPathscss|]}|dfVqdS)rNrC)Ú.0ÚkrCrCrDú šsz Filter.reload..Ú _reload_logsN)rKÚhasattrÚdictrLrPÚ delLogPathÚdelattr)rAZbeginÚpathrCrCrDÚreload”s   z Filter.reloadcCs"|jr |jStjddd�|_|jS)Nédéé<)ÚmaxCountÚmaxTimei,)r0rÚCache)rArCrCrDÚ mlfidCache¢szFilter.mlfidCachecCs|jS)N)r)rArCrCrDÚ prefRegex©szFilter.prefRegexcCs |rt||jd�|_nd|_dS)N)rB)rÚ_Filter__useDnsr)rAÚvaluerCrCrDr^¬scCsd|jdk}y$t||j||jd�}|jj|ƒWn0tk r^}ztj|ƒ|‚WYdd}~XnXdS)Nr)r^Z multilinerB) r)rrr_rÚappendrr?Úerror)rAr`Z multiLineÚregexÚerCrCrDÚ addFailRegexºs    zFilter.addFailRegexNc CsJy$|dkr|jdd…=dS|j|=Wn tk rDtjd|ƒYnXdS)Nz7Cannot remove regular expression. Index %d is not valid)rÚ IndexErrorr?rb)rAÚindexrCrCrDrHÄs  zFilter.delFailRegexcCsdd„|jDƒS)NcSsg|] }|jƒ‘qSrC)ÚgetRegex)rMrcrCrCrDú Ösz'Filter.getFailRegex..)r)rArCrCrDÚ getFailRegexÕszFilter.getFailRegexcCsTyt||jd�}|jj|ƒWn0tk rN}ztj|ƒ|‚WYdd}~XnXdS)N)rB)rr_r rarr?rb)rAr`rcrdrCrCrDÚaddIgnoreRegexßs  zFilter.addIgnoreRegexc CsJy$|dkr|jdd…=dS|j|=Wn tk rDtjd|ƒYnXdS)Nz7Cannot remove regular expression. Index %d is not valid)r rfr?rb)rArgrCrCrDrIçs  zFilter.delIgnoreRegexcCs(tƒ}x|jD]}|j|jƒƒqW|S)N)rr rarh)rAÚ ignoreRegexrcrCrCrDÚgetIgnoreRegexøs zFilter.getIgnoreRegexcCsPt|tƒrdddœ|}|jƒ}|dkr8tjd|ƒd}tjd||ƒ||_dS) NÚyesÚno)TFrÚrawz8Incorrect value %r specified for usedns. Using safe 'no'zSetting usedns = %s for %s)rnrrorp)Ú isinstanceÚboolÚlowerr?rbr@r_)rAr`rCrCrDr!s zFilter.setUseDnscCs|jS)N)r_)rArCrCrDÚ getUseDnsszFilter.getUseDnscCs,tj|ƒ}||_|jj|ƒtjd|ƒdS)Nz findtime: %s)r Ú str2secondsr"rZ setMaxTimer?Úinfo)rAr`rCrCrDÚ setFindTimes  zFilter.setFindTimecCs|jS)N)r")rArCrCrDÚ getFindTime&szFilter.getFindTimec Csr|dkrd|_dStƒ}|j|_t|ttfƒsPttttt t j t j d|ƒƒƒƒƒ}x|D]}|j|ƒqVW||_dS)Nz +)r>r r.Ú default_tzrqrÚtupleÚfilterrrÚmapÚstrÚstripÚreÚsplitZappendTemplate)rAÚpatternZddrCrCrDÚsetDatePattern.s" zFilter.setDatePatterncCsd|jdk r`|jj}t|ƒ s(t|ƒdkr,dSt|ƒr`t|ddƒrN|dj}nd}||djfSdS)NrúDefault Detectorsrr�)Nrƒ)r>Ú templatesÚlenrQr�r=)rAr„r�rCrCrDÚgetDatePattern@s  zFilter.getDatePatterncCs"t|ƒ||_|jr|j|j_dS)N)r r.r>ry)rAZtzrCrCrDÚsetLogTimeZoneSszFilter.setLogTimeZonecCs|jS)N)r.)rArCrCrDÚgetLogTimeZone]szFilter.getLogTimeZonecCs|jj|ƒtjd|ƒdS)Nz maxRetry: %s)rÚ setMaxRetryr?rv)rAr`rCrCrDr‰es zFilter.setMaxRetrycCs |jjƒS)N)rÚ getMaxRetry)rArCrCrDrŠnszFilter.getMaxRetrycCs0t|ƒdkrtdƒ‚t|ƒ|_tjd|jƒdS)Nrz*maxlines must be integer greater than zeroz maxLines: %i)ÚintÚ ValueErrorr)r?rv)rAr`rCrCrDÚ setMaxLinesvs  zFilter.setMaxLinescCs|jS)N)r))rArCrCrDÚ getMaxLines�szFilter.getMaxLinescCs0|jƒdkrt}tj|ƒ||_tjd|ƒ|S)NÚautoz encoding: %s)rsrÚcodecsÚlookupr/r?rv)rAÚencodingrCrCrDÚsetLogEncoding‰s    zFilter.setLogEncodingcCs|jS)N)r/)rArCrCrDÚgetLogEncoding–szFilter.getLogEncodingcCs tdƒ‚dS)Nzrun() is abstract)Ú Exception)rArCrCrDÚrun sz Filter.runcCs|jS)N)r')rArCrCrDÚ ignoreCommand§szFilter.ignoreCommandcCs ||_dS)N)r')rAÚcommandrCrCrDr—«scCs*|jr&|jd|jdj|jdjgSdS)Nrr)r(rZr[)rArCrCrDÚ ignoreCache³szFilter.ignoreCachecCsB|r8|dtjt|jddƒƒtj|jddƒƒd�f|_nd|_dS) NÚkeyz max-countrWzmax-timerXrY)rZr[i,)rr\r‹Úgetr rur()rAr˜rCrCrDr™¸s *c CsLx>y|jj|ƒ}Wntk r(PYnX|jj|ƒ|rPqW|jƒdS)zKPerforms a ban for IPs (or given ip) that are reached maxretry of the jail.N)rZtoBanrrZ putFailTicketÚ performSvc)rAÚipÚticketrCrCrDÚ performBanÁs zFilter.performBanFcCs0tjƒ}|s||jkr,|d|_|jj|ƒdS)z.Performs a service tasks (clean failure list).rXN)r Útimer-rZcleanup)rAÚforceZtmrCrCrDrœÌs zFilter.performSvccGs‚t|tƒst|ƒ}t|ƒ}tjƒ}t|||d�}tjd|j|t j j |ƒj dƒƒ|j j |t|ƒpbdƒ}||j jƒkr~|j|ƒdS)z Generate a failed attempt for ip)Úmatchesz[%s] Attempt %s - %sz%Y-%m-%d %H:%M:%Sr)rqrrr r r r?rvr<ÚdatetimeZ fromtimestampZstrftimerÚ addFailurer…rŠrŸ)rAr�r¢ÚunixTimeržÚattemptsrCrCrDÚ addAttemptÕs  zFilter.addAttemptcCs|jS)N)r#)rArCrCrDÚ ignoreSelfëszFilter.ignoreSelfcCs ||_dS)N)r#)rAr`rCrCrDr¨ïscCsp|dkr dSt|ƒ}||jks(||jkr>tjtjd||ƒdStjd||ƒ|jr`|jj |ƒn |jj |ƒdS)Nrz2 Ignore duplicate %r (%r), already in ignore listz Add %r to ignore list (%r)) rr%r&r?ÚlogÚloggingÚMSGr@ZisSingleÚaddra)rAZipstrr�rCrCrDÚ addIgnoreIPúszFilter.addIgnoreIPcCsV|dkr"|jjƒ|jdd…=dStjd|ƒ||jkrF|jj|ƒn |jj|ƒdS)Nz Remove %r from ignore list)r%Úclearr&r?r@Úremove)rAr�rCrCrDrJ s    zFilter.delIgnoreIPúunknown sourcecCs|rtjd|j||ƒdS)Nz[%s] Ignore %s by %s)r?rvr<)rAr�Ú log_ignoreÚ ignore_sourcerCrCrDÚ logIgnoreIpszFilter.logIgnoreIpcCs|jt|jƒS)N)r&rr%)rArCrCrDÚ getIgnoreIPszFilter.getIgnoreIPcCs<d}t|tƒr|}|jƒ}nt|tƒs.t|ƒ}|j|||ƒS)N)rqr ZgetIDrÚ_inIgnoreIPList)rAr�r±ržrCrCrDÚinIgnoreIPList(s   zFilter.inIgnoreIPListc Cs¾d}|jr^|j\}}|r4tj||jƒ}tj||ƒ}nd|i}tj||ƒ}|j|ƒ}|dk r^|S|jr–|t j ƒkr–|j ||dd�|jr’|j |dƒdS||j kr´|j ||dd�dSxH|jD]>}|j|ƒr¼|j |||jrÜdndd�|jrö|j |dƒdSq¼W|j�r¦|�r0|�s tj||jƒ}tj|j|ƒ} n|�s>d|i}tj|j|ƒ} tjd| ƒtj| d d �\} } | �ov| dk} |j ||�o†| d d�|j�r¢|j || ƒ| S|j�rº|j |d ƒd S) Nr�zignoreself rule)r²TÚdnszignore command: %srr)Z success_codesr˜F)rr)r(rZ ActionInforrZreplaceDynamicTagsZ replaceTagr›r#rZ getSelfIPsr³r$r%r&ZisInNetZisValidr'r?r@Z executeCmd) rAr�ržr±ZaInforšÚcÚvZnetr˜ÚretZ ret_ignorerCrCrDrµ1sV           zFilter._inIgnoreIPListcGs~tjƒt||dƒkr4tjƒdkrztjd|džŽnFt||tjƒd ƒx0|D](}tjd|d|jf|dd…žŽqNWdS) z9Log some issue as warning once per day, otherwise level 7réérYz[%s] rN)r»i i€Q) r r Úgetattrr?ÚgetEffectiveLevelr©ÚsetattrZwarningr<)rAZnextLTMÚargsrCrCrDÚ _logWarnOnceas   zFilter._logWarnOncec sJtjdd|ƒd}|r8|‰dj|ƒ}ˆd|_||_�n&|jj|ƒ}|d}|rþ|jdƒ}|jdƒ}|||…}|d|…|||d…f‰|rÈ|jj ||ƒ}|dk rº|d}||_||_qütj d|ƒn4|jrô|jt j ƒd krôd|j|f‰|j}nt j ƒ}n dd|f‰|dk�r^d }|j�rF|jt j ƒd k�rFd|j|f‰|j}n|j �r^|j�r^t j ƒ}|j �r,|dk �r,|j�røt|t j ƒƒ}t|ƒd k�r,|jd d t jt|ƒƒ|dk�r¸d ndd|k�oÎdkn�rÚdndfd|fƒt j ƒ}||_n4|t j ƒ|jƒk�r,|jdd|jƒfd|fƒgS‡fdd„|_|jˆ||d�S)zFSplit the time portion from log msg and return findFailures on them r»zWorking on line %rFrrrNz(findFailure failed to parse timeText: %srYTZ_next_simByTimeWarnzŒDetected a log entry %s %s the current time in operation mode. This looks like a %s problem. Treating such entries as if they just happened.ZbeforeÚafteriä ZlatencyZtimezonezCPlease check a jail for a timing issue. Line with odd timestamp: %sZ_next_ignByTimeWarnz;Ignoring all log entries older than %ss; these are probablyz3 messages generated while fail2ban was not running.csdjˆddd…ƒS)Nrr)ÚjoinrC)Ú tupleLinerCrDÚºsz$Filter.processLine..)ÚnoDateióÿÿznIgnoring all log entries older than %ss; these are probably messages generated while fail2ban was not running.)r?r©rÃr+r,r>Ú matchTimeÚstartÚendÚgetTimerbr r r8r9r‹ÚabsrÁZ seconds2strrxZ processedLineÚ findFailure) rAÚlineÚdaterÆÚ timeMatchÚmÚsrdZdeltarC)rÄrDÚ processLinejsn            $zFilter.processLinec CsDyöx¸|j||ƒD]¨\}}}}tjd||ƒ|jrD|tjƒkrDtjƒ}t|||d�}|j||ƒr`qtjd|j |tj |ƒƒ|j j |ƒ}||j j ƒkrœ|j|ƒtjdk rtjjd|j|ƒqW|jd7_|jddkrà|jƒ|jrô|jd _WnHtk �r>} z*tjd || tjƒtjkd �|jƒWYdd} ~ XnXdS) z.znofail-matcheséé róiöÿÿÿ) r0r]r›ÚpoprìÚupdateÚitemsZunsetr,r$r…rß) rAÚmlfidrÙrçZ mlfidFailrêZnfflgsZ mlfidGroupsrÐrCrCrDÚ _mergeFailure sV           "     zFilter._mergeFailurecCsîtƒ}tjƒ}tj}|jdks$|jr*tj}|jdkrZ|j j |ƒ|j |j d…}|_ n |g}|_ |dkr|tj dd|ƒt j |ƒ}|jr¸t|jƒt|jƒdkr¸|j||ƒdk r¸|Si} |j�r\|dkràtj dd|jjƒƒ|jj||ƒ|jjƒ�s|dk�rtj ddƒ|S|jjƒ} |dk�r6tj dd | ƒ| jd dƒ} | �r\d d | fgd|_ }�xŠt|jƒD�]z\} } �y>|dk�rŽt j |j ƒ}|dk�r¬tj dd | | jƒƒ| j||ƒ| jƒ�sÆ�wj| jƒ} |dk�rètj dd | | ƒ|j�r|j||| ƒdk �rd}|j�sP�wj|�rX|jdd|dfddj| jƒƒfd#ƒ|dk�rX|j�rX�wj|j �sn|jdk�r~| jƒd|_ }|}|tjk}| �rª| | jƒ}} | j |ƒ| j!dƒ}|dk �r |j"|| | ƒ} | j!dƒ�r"|dk�rütj dd|| | j!ddƒƒ|j�s"|Sn| j!dgƒ| j#ƒ| d<| j!dƒ}| j!dƒ}|dk �r\t$| j!dƒ�pRtj%ƒ}d}nh| j!dƒ}|dk �rŒt$| j!dƒ�p‚tj&ƒ}d}n8| j!dƒ}|dk�rÄ|dk�r¼|dk�r¼| j'ƒ}|}d}|dk�r|dk�ròtj dd|| | j!dd ƒƒd| d!<|j �r|j(�r|Sdg}nT|�r^|dk�s2||k�r>t||ƒ}nt||ƒ| d"<t||ƒ}|g}nt)j*||jƒ}|j�r†|dk �r†| jƒ} x |D]}|j | ||| gƒ�qŒW|j�s²PWn.t+k �râ}ztj,|ƒWYdd}~XnX�qjW|S)$NrprrXzLooking for match of %rrz Looking for prefregex %rz Prefregex not matchedr»z Pre-filter matched %sZcontentrz Looking for failregex %d - %rz Matched failregex %d: %sZ_next_noTimeWarnz2Found a match but no valid date/time found for %r.zMatch without a timestamp: %sÚ úEPlease try setting a custom date pattern (see man page jail.conf(5)).r÷rîz"Nofail by mlfid %r in regex %s: %srïzwaiting for failurer¢ÚfidZip4ÚcidrTZip6r·z)No failure-id by mlfid %r in regex %s: %szwaiting for identifierrñr�)rú)-rr?r¾rZ CIDR_UNSPECr_r4ZCIDR_RAWr)r*rar©rràr r…rrérrhrârãZ getGroupsrôrár5rÁrÃZgetMatchedLinesr8räÚcopyrõr›rørßr‹ZFAM_IPv4ZFAM_IPv6Z getFailIDr6rZtextToIprrb)rArÄrÎrÆZfailListZllZdefcidrræråZ preGroupsÚreplZfailRegexIndexrçrÙrürpZcurrFailr÷rûr�ZfidsrdrCrCrDrÌGsÜ                                  "zFilter.findFailureÚbasiccCs d|jjƒfd|jjƒfg}|S)z)Status of failures detected by filter. zCurrently failedz Total failed)rÚsizeZ getFailTotal)rAÚflavorrºrCrCrDÚstatusÒs z Filter.status)r)T)N)N)N)F)N)r°)T)T)N)N)rÛN)N)F)rÿ)6rFÚ __module__Ú __qualname__rrGÚpropertyr<rKrVr]r^ÚsetterrerHrjrkrIrmr!rtrwrxr‚r†r‡rˆr‰rŠr�rŽr“r”r–r—r™rŸrœr§r¨r­rJr³r´r¶rµrÁrÒrÚr×rérìrørÌrrCrCrCrDr9sl E                      0 S %  <  rcs¬eZdZdd„Zd(dd„Zdd„Zd d „Zd d „Zd d„Zdd„Z dd„Z dd„Z ‡fdd„Z dd„Z d)dd„Zd*dd„Zd+‡fd d!„ Zd"d#„Zd$d%„Z‡fd&d'„Z‡ZS),Ú FileFiltercKs$tj||f|Žtƒ|_tƒ|_dS)N)rrrRÚ_FileFilter__logsÚ_FileFilter__autoSeek)rArÚkwargsrCrCrDrÜszFileFilter.__init__FTcCs¾||jkr8t|dƒr(||jkr(|j|=qºtj|dƒn‚t||jƒ|ƒ}|jj}|dk rz|j |j|ƒ}|rz| rz|j |ƒ||j|<tj d||j ƒ|j ƒƒ|r°| r°||j|<|j|ƒdS)NrPz already existsz'Added logfile: %r (pos = %s, hash = %s))rrQrPr?rbÚ FileContainerr”rÚdatabaseZaddLogÚsetPosrvÚgetPosÚgetHashr Ú _addLogPath)rArUÚtailZautoSeekr©ÚdbZlastposrCrCrDÚ addLogPathçs       zFileFilter.addLogPathcCsdS)NrC)rArUrCrCrDrúszFileFilter._addLogPathc Cs@y|jj|ƒ}Wntk r$dSXtjd|ƒ|j|ƒdS)NzRemoved logfile: %r)rrôÚKeyErrorr?rvÚ _delLogPath)rArUr©rCrCrDrSs  zFileFilter.delLogPathcCsdS)NrC)rArUrCrCrDr szFileFilter._delLogPathcCst|jjƒƒS)N)rrÚkeys)rArCrCrDrLszFileFilter.getLogPathscCst|jjƒƒS)N)rrÚvalues)rArCrCrDÚgetLogsszFileFilter.getLogscCs t|jƒS)N)r…r)rArCrCrDÚ getLogCount'szFileFilter.getLogCountcCs ||jkS)N)r)rArUrCrCrDÚcontainsLogPath0szFileFilter.containsLogPathcs2tt|ƒj|ƒ}x|jjƒD]}|j|ƒqWdS)N)Úsuperrr“rrÚ setEncoding)rAr’r©)rErCrDr“8szFileFilter.setLogEncodingcCs|jj|dƒS)N)rr›)rArUrCrCrDÚgetLog=szFileFilter.getLogNc%Cs2|jr dS|j|ƒ}|dkr,tjd|ƒdS�z–y |jƒ}Wn¦tk r|}z$tjd|ƒ|jdkrntj|ƒdSd}~Xnhtk r°}ztjd|ƒtj|ƒdSd}~Xn4t k râ}ztjdƒtj|ƒdSd}~XnX|j �rh|j j |dƒ}|�rht |t ƒ�rtjƒ|jƒ}y|j||ƒWn:t k �rf}ztjd|ƒ‚tj|ƒdSd}~XnX|�rÂxR|j�sÀ|jƒ}|j�sŠP|dk�rœd|_P|dk �rª|n|j|_|j|ƒ�qpWWd|jƒX|jjdk �r.d |j|<|jd d k�stjƒ|jk�s|j �r.|jƒtjƒtjd |_dS) NFzUnable to get failures in %szUnable to open %srzError opening %szsInternal error in FileContainer open method - please report as a bug to https://github.com/fail2ban/fail2ban/issuesz'Error during seek to start time in "%s"TrrWrrX)rÜrr?rbÚopenÚIOErrorÚerrnoZ exceptionÚOSErrorr•r rôrqrrr r rxÚ seekToTimeÚreadlineZactiver9rÚÚcloserr r3r:r2Ú_updateDBPendingrZDEFAULT_SLEEP_TIME)rAÚfilenamer9r©Z has_contentrdZ startTimerÍrCrCrDÚ getFailuresGsj                  zFileFilter.getFailuresròcCsl|jƒ}tjƒtjkr,tjd|tj|ƒ|ƒ|s4dS|jƒ}|}|}d }d} d} d} d} |} �xº||k�r|dkrˆt |||dƒ}n |d}}t d|dƒ}|j |ƒ}}| d7} d}d}d}xˆ|j dƒ}|dkrØP|j j|ƒ\}}|�r|j j||jƒ|jƒ…||fƒ}n|jƒ}||k�r*|}P|}| �rF|�rF|d8}qÄPqÄW|�rÞ|d} | |k�rž| dk�sx| | k�r€|} | } ||k�rŽ|}||k�rÞ|}n@| dk�s²| | k�rº|} | } |dk�rÌ|jƒ}|}||k�rÞ|}||k�r| d8} | dk�rüP||k�r|}}q`P|}q`W|j |dƒ} |j| ƒtjƒtjk�rhtjd||| | dk �r`tj| ƒnd| ƒdS) Nz'Seek to find time %s (%s), file size %srrrrXFz7Position %s from %s, found time %s (%s) within %s seeksréÿÿÿÿ)Ú getFileSizer?r¾rªrÖr@r rÕrr‹ÚmaxÚseekr#r>rÇrÊrÈrÉÚtellr )rAZ containerrÎZaccuracyZfsZminpZmaxpZtryPosZlastPosZfoundPosZ foundTimeZcntrr¥ZmovecntrÚposZseekposZlncntrZ dateTimeMatchZnextprÍrÏÚtemplaterCrCrDr"�s–               zFileFilter.seekToTimerÿcs2tt|ƒj|d�}t|jjƒƒ}|jd|fƒ|S)z/Status of Filter plus files being monitored. )rz File list)rrrrrrra)rArrºrU)rErCrDræszFileFilter.statusc CsJ|jj}xrArUÚgetsizer6)rAÚhrErCrCrDr)Qs zFileContainer.getFileSizecCstj|ƒ||_dS)N)r�r‘Ú_FileContainer__encoding)rAr’rCrCrDrXs zFileContainer.setEncodingcCs|jS)N)rO)rArCrCrDÚ getEncoding\szFileContainer.getEncodingcCs|jS)N)r:)rArCrCrDr_szFileContainer.getHashcCs|jS)N)r9)rArCrCrDrbszFileContainer.getPoscCs ||_dS)N)r9)rAr`rCrCrDr eszFileContainer.setPosNc Cs|t|jdƒ}�zT|jƒ}tj|tjƒ}tj|tj|tjBƒ|j}tj |jƒƒ}|j |j kpf|j |j k}|s„t|ƒ s„tjƒ|jkrºd}|jƒ}||jdƒkrÌt|ƒjƒ}tjƒd|_n|j |j krÌ|j}|sÜ||jk�r&|jdk�r tjtjd|j|j |j |j |j ||jfƒ|j |_ d|_ ||_|dk �r8||_ n|j |j k�rJdS|j|j ƒ||_d}Wd|�rv|jƒd}XdS) Nr5rs r4z(Log rotation detected for %s, reason: %rrFT)rr6r>ÚfcntlZF_GETFDZF_SETFDZ FD_CLOEXECr:r<r=rAr9r?r@r…r r;r#rBrCrDr?r©rªr«r+r8r$) rAZforcePosrNÚfdÚflagsZmyHashrEZrotflgrFrCrCrDrhsD      zFileContainer.openTcCsB|j}|dkr|j|ƒ|j}|j|dƒ|r:|r:|jƒ|jƒS)Nr)r8rr+r#r,)rAZoffsZendLinerNrCrCrDr+“s  zFileContainer.seekcCs |jjƒS)N)r8r,)rArCrCrDr, szFileContainer.tellcCsÂy |j|dƒSttfk r¼}z�|jt|ƒkrT||jdkrT|d|j…j|dƒSd}tj|dƒsvtj }tj |dƒt j |d||ƒt j ƒ|kr t j |d|ƒ|j|dƒ}WYdd}~XnX|S) NÚstricts rÚreplacer»rz(Error decoding line from '%s' with '%s'.z~Consider setting logencoding to appropriate encoding for this jail. Continuing to process line ignoring invalid characters: %r)ÚdecodeÚUnicodeDecodeErrorÚUnicodeEncodeErrorrÉr…rÈÚ_decode_line_warnr›rªZWARNINGr$r?r©r¾)r&ÚencrÍrdZlevrCrCrDÚ decode_line¤s"     zFileContainer.decode_linecCs@|jdkrdS|jjƒ}|s dSt|ƒ}tj|jƒ|jƒ|ƒ}|jdƒ}|�r<||k�rs`        (; cserver/__pycache__/filter.cpython-36.pyc000064400000102354152231502150014176 0ustar003 UB(d,¾ã@s¨dZdZdZddlZddlZddlZddlZddlZddlZddl Z ddl Z ddl m Z ddl mZmZddlmZmZdd lmZdd lmZdd lmZdd lmZmZdd lmZddlmZm Z m!Z!ddl"m#Z#ddl$m%Z%ddl&m'Z'm(Z(e'e)ƒZ*Gdd„deƒZ+Gdd„de+ƒZ,y6ddl-Z-ye-j.Z/e/dƒj0ƒWne-j1Z/YnXWn$e2k �rvddl.Z.e.j3Z/YnXGdd„dƒZ4e%j5dd!d�a6Gdd„de+ƒZ7dS)"z'Cyril Jaquier and Fail2Ban Contributorsz>Copyright (c) 2004 Cyril Jaquier, 2011-2013 Yaroslav HalchenkoZGPLéNé)ÚActions)ÚFailManagerEmptyÚ FailManager)ÚDNSUtilsÚIPAddr)Ú Observers)Ú FailTicket)Ú JailThread)Ú DateDetectorÚvalidateTimeZone)ÚMyTime)Ú FailRegexÚRegexÚRegexException)Ú CommandAction)ÚUtilsé)Ú getLoggerÚ PREFER_ENCc@seZdZdndd„Zdd„Zedd„ƒZdd „Zdod d „Zed d„ƒZ edd„ƒZ e j dd„ƒZ dd„Z dpdd„Z dd„Zdd„Zdqdd„Zdd„Zdd „Zd!d"„Zd#d$„Zd%d&„Zd'd(„Zd)d*„Zd+d,„Zd-d.„Zd/d0„Zd1d2„Zd3d4„Zd5d6„Zd7d8„Zd9d:„Zd;d<„Z ed=d>„ƒZ!e!j d?d>„ƒZ!ed@dA„ƒZ"e"j dBdA„ƒZ"drdCdD„Z#dsdFdG„Z$dHdI„Z%edJdK„ƒZ&e&j dLdK„ƒZ&dMdN„Z'dtdOdP„Z(dudRdS„Z)dTdU„Z*dvdVdW„Z+dwdXdY„Z,dZd[„Z-dxd\d]„Z.dyd^d_„Z/dzdadb„Z0d{dcdd„Z1ffdedf„Z2dgdh„Z3d|didj„Z4d}dldm„Z5dS)~ÚFilterÚwarncCsôtj|ƒ||_tƒ|_d|_tƒ|_tƒ|_|j |ƒd|_ d|_ t ƒ|_ g|_d|_d|_d|_g|_d|_d|_d |_d|_t|_d|_d|_d|_i|_d|_d|_d|_d|_ d|_!d|_"d|_#d|_$d|j%|_&t'ƒ|_(t)j*d |ƒdS) NiXTFrÚé?rzf2b/f.z Created %sllûÿÿÿ)+r Ú__init__ÚjailrÚ failManagerÚ_Filter__prefRegexÚlistÚ_Filter__failRegexÚ_Filter__ignoreRegexÚ setUseDnsÚ_Filter__findTimeÚ_Filter__ignoreSelfÚsetÚ_Filter__ignoreIpSetÚ_Filter__ignoreIpListÚ_Filter__ignoreCommandÚ_Filter__ignoreCacheÚ_Filter__lineBufferSizeÚ_Filter__lineBufferÚ_Filter__lastTimeTextÚ_Filter__lastDateÚ_Filter__nextSvcTimeÚ_Filter__logtimezonerÚ_Filter__encodingÚ_Filter__mlfidCacheÚ_errorsÚ _nextUpdateTMÚ_pendDBUpdatesÚ returnRawHostÚ checkAllRegexÚ ignorePendingÚ onIgnoreRegexÚ checkFindTimeÚ inOperationÚticksÚ procLinesÚjailNameÚnamer Ú dateDetectorÚlogSysÚdebug)ÚselfrÚuseDns©rCú/usr/lib/python3.6/filter.pyrAsF   zFilter.__init__cCsd|jj|jfS)Nz%s(%r))Ú __class__Ú__name__r)rArCrCrDÚ__repr__†szFilter.__repr__cCs|jdk r|jjpdS)Nz ~jailless~)rr=)rArCrCrDr<‰szFilter.jailNamecCs|jƒ|jƒ|jƒdS)z8 Clear all lists/dicts parameters (used by reloading) N)Ú delFailRegexÚdelIgnoreRegexÚ delIgnoreIP)rArCrCrDÚclearAllParams�szFilter.clearAllParamsTcCsb|r0|jƒt|dƒr^tdd„|jƒDƒƒ|_n.t|dƒr^x|jD]}|j|ƒqBWt|dƒdS)z@ Begin or end of reloading resp. refreshing of all parameters Ú getLogPathscss|]}|dfVqdS)rNrC)Ú.0ÚkrCrCrDú šsz Filter.reload..Ú _reload_logsN)rKÚhasattrÚdictrLrPÚ delLogPathÚdelattr)rAZbeginÚpathrCrCrDÚreload”s   z Filter.reloadcCs"|jr |jStjddd�|_|jS)Nédéé<)ÚmaxCountÚmaxTimei,)r0rÚCache)rArCrCrDÚ mlfidCache¢szFilter.mlfidCachecCs|jS)N)r)rArCrCrDÚ prefRegex©szFilter.prefRegexcCs |rt||jd�|_nd|_dS)N)rB)rÚ_Filter__useDnsr)rAÚvaluerCrCrDr^¬scCsd|jdk}y$t||j||jd�}|jj|ƒWn0tk r^}ztj|ƒ|‚WYdd}~XnXdS)Nr)r^Z multilinerB) r)rrr_rÚappendrr?Úerror)rAr`Z multiLineÚregexÚerCrCrDÚ addFailRegexºs    zFilter.addFailRegexNc CsJy$|dkr|jdd…=dS|j|=Wn tk rDtjd|ƒYnXdS)Nz7Cannot remove regular expression. Index %d is not valid)rÚ IndexErrorr?rb)rAÚindexrCrCrDrHÄs  zFilter.delFailRegexcCsdd„|jDƒS)NcSsg|] }|jƒ‘qSrC)ÚgetRegex)rMrcrCrCrDú Ösz'Filter.getFailRegex..)r)rArCrCrDÚ getFailRegexÕszFilter.getFailRegexcCsTyt||jd�}|jj|ƒWn0tk rN}ztj|ƒ|‚WYdd}~XnXdS)N)rB)rr_r rarr?rb)rAr`rcrdrCrCrDÚaddIgnoreRegexßs  zFilter.addIgnoreRegexc CsJy$|dkr|jdd…=dS|j|=Wn tk rDtjd|ƒYnXdS)Nz7Cannot remove regular expression. Index %d is not valid)r rfr?rb)rArgrCrCrDrIçs  zFilter.delIgnoreRegexcCs(tƒ}x|jD]}|j|jƒƒqW|S)N)rr rarh)rAÚ ignoreRegexrcrCrCrDÚgetIgnoreRegexøs zFilter.getIgnoreRegexcCsPt|tƒrdddœ|}|jƒ}|dkr8tjd|ƒd}tjd||ƒ||_dS) NÚyesÚno)TFrÚrawz8Incorrect value %r specified for usedns. Using safe 'no'zSetting usedns = %s for %s)rnrrorp)Ú isinstanceÚboolÚlowerr?rbr@r_)rAr`rCrCrDr!s zFilter.setUseDnscCs|jS)N)r_)rArCrCrDÚ getUseDnsszFilter.getUseDnscCs,tj|ƒ}||_|jj|ƒtjd|ƒdS)Nz findtime: %s)r Ú str2secondsr"rZ setMaxTimer?Úinfo)rAr`rCrCrDÚ setFindTimes  zFilter.setFindTimecCs|jS)N)r")rArCrCrDÚ getFindTime&szFilter.getFindTimec Csr|dkrd|_dStƒ}|j|_t|ttfƒsPttttt t j t j d|ƒƒƒƒƒ}x|D]}|j|ƒqVW||_dS)Nz +)r>r r.Ú default_tzrqrÚtupleÚfilterrrÚmapÚstrÚstripÚreÚsplitZappendTemplate)rAÚpatternZddrCrCrDÚsetDatePattern.s" zFilter.setDatePatterncCsd|jdk r`|jj}t|ƒ s(t|ƒdkr,dSt|ƒr`t|ddƒrN|dj}nd}||djfSdS)NrúDefault Detectorsrr�)Nrƒ)r>Ú templatesÚlenrQr�r=)rAr„r�rCrCrDÚgetDatePattern@s  zFilter.getDatePatterncCs"t|ƒ||_|jr|j|j_dS)N)r r.r>ry)rAZtzrCrCrDÚsetLogTimeZoneSszFilter.setLogTimeZonecCs|jS)N)r.)rArCrCrDÚgetLogTimeZone]szFilter.getLogTimeZonecCs|jj|ƒtjd|ƒdS)Nz maxRetry: %s)rÚ setMaxRetryr?rv)rAr`rCrCrDr‰es zFilter.setMaxRetrycCs |jjƒS)N)rÚ getMaxRetry)rArCrCrDrŠnszFilter.getMaxRetrycCs0t|ƒdkrtdƒ‚t|ƒ|_tjd|jƒdS)Nrz*maxlines must be integer greater than zeroz maxLines: %i)ÚintÚ ValueErrorr)r?rv)rAr`rCrCrDÚ setMaxLinesvs  zFilter.setMaxLinescCs|jS)N)r))rArCrCrDÚ getMaxLines�szFilter.getMaxLinescCs0|jƒdkrt}tj|ƒ||_tjd|ƒ|S)NÚautoz encoding: %s)rsrÚcodecsÚlookupr/r?rv)rAÚencodingrCrCrDÚsetLogEncoding‰s    zFilter.setLogEncodingcCs|jS)N)r/)rArCrCrDÚgetLogEncoding–szFilter.getLogEncodingcCs tdƒ‚dS)Nzrun() is abstract)Ú Exception)rArCrCrDÚrun sz Filter.runcCs|jS)N)r')rArCrCrDÚ ignoreCommand§szFilter.ignoreCommandcCs ||_dS)N)r')rAÚcommandrCrCrDr—«scCs*|jr&|jd|jdj|jdjgSdS)Nrr)r(rZr[)rArCrCrDÚ ignoreCache³szFilter.ignoreCachecCsB|r8|dtjt|jddƒƒtj|jddƒƒd�f|_nd|_dS) NÚkeyz max-countrWzmax-timerXrY)rZr[i,)rr\r‹Úgetr rur()rAr˜rCrCrDr™¸s *c CsLx>y|jj|ƒ}Wntk r(PYnX|jj|ƒ|rPqW|jƒdS)zKPerforms a ban for IPs (or given ip) that are reached maxretry of the jail.N)rZtoBanrrZ putFailTicketÚ performSvc)rAÚipÚticketrCrCrDÚ performBanÁs zFilter.performBanFcCs0tjƒ}|s||jkr,|d|_|jj|ƒdS)z.Performs a service tasks (clean failure list).rXN)r Útimer-rZcleanup)rAÚforceZtmrCrCrDrœÌs zFilter.performSvccGs‚t|tƒst|ƒ}t|ƒ}tjƒ}t|||d�}tjd|j|t j j |ƒj dƒƒ|j j |t|ƒpbdƒ}||j jƒkr~|j|ƒdS)z Generate a failed attempt for ip)Úmatchesz[%s] Attempt %s - %sz%Y-%m-%d %H:%M:%Sr)rqrrr r r r?rvr<ÚdatetimeZ fromtimestampZstrftimerÚ addFailurer…rŠrŸ)rAr�r¢ÚunixTimeržÚattemptsrCrCrDÚ addAttemptÕs  zFilter.addAttemptcCs|jS)N)r#)rArCrCrDÚ ignoreSelfëszFilter.ignoreSelfcCs ||_dS)N)r#)rAr`rCrCrDr¨ïscCsp|dkr dSt|ƒ}||jks(||jkr>tjtjd||ƒdStjd||ƒ|jr`|jj |ƒn |jj |ƒdS)Nrz2 Ignore duplicate %r (%r), already in ignore listz Add %r to ignore list (%r)) rr%r&r?ÚlogÚloggingÚMSGr@ZisSingleÚaddra)rAZipstrr�rCrCrDÚ addIgnoreIPúszFilter.addIgnoreIPcCsV|dkr"|jjƒ|jdd…=dStjd|ƒ||jkrF|jj|ƒn |jj|ƒdS)Nz Remove %r from ignore list)r%Úclearr&r?r@Úremove)rAr�rCrCrDrJ s    zFilter.delIgnoreIPúunknown sourcecCs|rtjd|j||ƒdS)Nz[%s] Ignore %s by %s)r?rvr<)rAr�Ú log_ignoreÚ ignore_sourcerCrCrDÚ logIgnoreIpszFilter.logIgnoreIpcCs|jt|jƒS)N)r&rr%)rArCrCrDÚ getIgnoreIPszFilter.getIgnoreIPcCs<d}t|tƒr|}|jƒ}nt|tƒs.t|ƒ}|j|||ƒS)N)rqr ZgetIDrÚ_inIgnoreIPList)rAr�r±ržrCrCrDÚinIgnoreIPList(s   zFilter.inIgnoreIPListc Cs¾d}|jr^|j\}}|r4tj||jƒ}tj||ƒ}nd|i}tj||ƒ}|j|ƒ}|dk r^|S|jr–|t j ƒkr–|j ||dd�|jr’|j |dƒdS||j kr´|j ||dd�dSxH|jD]>}|j|ƒr¼|j |||jrÜdndd�|jrö|j |dƒdSq¼W|j�r¦|�r0|�s tj||jƒ}tj|j|ƒ} n|�s>d|i}tj|j|ƒ} tjd| ƒtj| d d �\} } | �ov| dk} |j ||�o†| d d�|j�r¢|j || ƒ| S|j�rº|j |d ƒd S) Nr�zignoreself rule)r²TÚdnszignore command: %srr)Z success_codesr˜F)rr)r(rZ ActionInforrZreplaceDynamicTagsZ replaceTagr›r#rZ getSelfIPsr³r$r%r&ZisInNetZisValidr'r?r@Z executeCmd) rAr�ržr±ZaInforšÚcÚvZnetr˜ÚretZ ret_ignorerCrCrDrµ1sV           zFilter._inIgnoreIPListcGs~tjƒt||dƒkr4tjƒdkrztjd|džŽnFt||tjƒd ƒx0|D](}tjd|d|jf|dd…žŽqNWdS) z9Log some issue as warning once per day, otherwise level 7réérYz[%s] rN)r»i i€Q) r r Úgetattrr?ÚgetEffectiveLevelr©ÚsetattrZwarningr<)rAZnextLTMÚargsrCrCrDÚ _logWarnOnceas   zFilter._logWarnOncec sJtjdd|ƒd}|r8|‰dj|ƒ}ˆd|_||_�n&|jj|ƒ}|d}|rþ|jdƒ}|jdƒ}|||…}|d|…|||d…f‰|rÈ|jj ||ƒ}|dk rº|d}||_||_qütj d|ƒn4|jrô|jt j ƒd krôd|j|f‰|j}nt j ƒ}n dd|f‰|dk�r^d }|j�rF|jt j ƒd k�rFd|j|f‰|j}n|j �r^|j�r^t j ƒ}|j �r,|dk �r,|j�røt|t j ƒƒ}t|ƒd k�r,|jd d t jt|ƒƒ|dk�r¸d ndd|k�oÎdkn�rÚdndfd|fƒt j ƒ}||_n4|t j ƒ|jƒk�r,|jdd|jƒfd|fƒgS‡fdd„|_|jˆ||d�S)zFSplit the time portion from log msg and return findFailures on them r»zWorking on line %rFrrrNz(findFailure failed to parse timeText: %srYTZ_next_simByTimeWarnzŒDetected a log entry %s %s the current time in operation mode. This looks like a %s problem. Treating such entries as if they just happened.ZbeforeÚafteriä ZlatencyZtimezonezCPlease check a jail for a timing issue. Line with odd timestamp: %sZ_next_ignByTimeWarnz;Ignoring all log entries older than %ss; these are probablyz3 messages generated while fail2ban was not running.csdjˆddd…ƒS)Nrr)ÚjoinrC)Ú tupleLinerCrDÚºsz$Filter.processLine..)ÚnoDateióÿÿznIgnoring all log entries older than %ss; these are probably messages generated while fail2ban was not running.)r?r©rÃr+r,r>Ú matchTimeÚstartÚendÚgetTimerbr r r8r9r‹ÚabsrÁZ seconds2strrxZ processedLineÚ findFailure) rAÚlineÚdaterÆÚ timeMatchÚmÚsrdZdeltarC)rÄrDÚ processLinejsn            $zFilter.processLinec CsDyöx¸|j||ƒD]¨\}}}}tjd||ƒ|jrD|tjƒkrDtjƒ}t|||d�}|j||ƒr`qtjd|j |tj |ƒƒ|j j |ƒ}||j j ƒkrœ|j|ƒtjdk rtjjd|j|ƒqW|jd7_|jddkrà|jƒ|jrô|jd _WnHtk �r>} z*tjd || tjƒtjkd �|jƒWYdd} ~ XnXdS) z.znofail-matcheséé róiöÿÿÿ) r0r]r›ÚpoprìÚupdateÚitemsZunsetr,r$r…rß) rAÚmlfidrÙrçZ mlfidFailrêZnfflgsZ mlfidGroupsrÐrCrCrDÚ _mergeFailure sV           "     zFilter._mergeFailurecCsîtƒ}tjƒ}tj}|jdks$|jr*tj}|jdkrZ|j j |ƒ|j |j d…}|_ n |g}|_ |dkr|tj dd|ƒt j |ƒ}|jr¸t|jƒt|jƒdkr¸|j||ƒdk r¸|Si} |j�r\|dkràtj dd|jjƒƒ|jj||ƒ|jjƒ�s|dk�rtj ddƒ|S|jjƒ} |dk�r6tj dd | ƒ| jd dƒ} | �r\d d | fgd|_ }�xŠt|jƒD�]z\} } �y>|dk�rŽt j |j ƒ}|dk�r¬tj dd | | jƒƒ| j||ƒ| jƒ�sÆ�wj| jƒ} |dk�rètj dd | | ƒ|j�r|j||| ƒdk �rd}|j�sP�wj|�rX|jdd|dfddj| jƒƒfd#ƒ|dk�rX|j�rX�wj|j �sn|jdk�r~| jƒd|_ }|}|tjk}| �rª| | jƒ}} | j |ƒ| j!dƒ}|dk �r |j"|| | ƒ} | j!dƒ�r"|dk�rütj dd|| | j!ddƒƒ|j�s"|Sn| j!dgƒ| j#ƒ| d<| j!dƒ}| j!dƒ}|dk �r\t$| j!dƒ�pRtj%ƒ}d}nh| j!dƒ}|dk �rŒt$| j!dƒ�p‚tj&ƒ}d}n8| j!dƒ}|dk�rÄ|dk�r¼|dk�r¼| j'ƒ}|}d}|dk�r|dk�ròtj dd|| | j!dd ƒƒd| d!<|j �r|j(�r|Sdg}nT|�r^|dk�s2||k�r>t||ƒ}nt||ƒ| d"<t||ƒ}|g}nt)j*||jƒ}|j�r†|dk �r†| jƒ} x |D]}|j | ||| gƒ�qŒW|j�s²PWn.t+k �râ}ztj,|ƒWYdd}~XnX�qjW|S)$NrprrXzLooking for match of %rrz Looking for prefregex %rz Prefregex not matchedr»z Pre-filter matched %sZcontentrz Looking for failregex %d - %rz Matched failregex %d: %sZ_next_noTimeWarnz2Found a match but no valid date/time found for %r.zMatch without a timestamp: %sÚ úEPlease try setting a custom date pattern (see man page jail.conf(5)).r÷rîz"Nofail by mlfid %r in regex %s: %srïzwaiting for failurer¢ÚfidZip4ÚcidrTZip6r·z)No failure-id by mlfid %r in regex %s: %szwaiting for identifierrñr�)rú)-rr?r¾rZ CIDR_UNSPECr_r4ZCIDR_RAWr)r*rar©rràr r…rrérrhrârãZ getGroupsrôrár5rÁrÃZgetMatchedLinesr8räÚcopyrõr›rørßr‹ZFAM_IPv4ZFAM_IPv6Z getFailIDr6rZtextToIprrb)rArÄrÎrÆZfailListZllZdefcidrræråZ preGroupsÚreplZfailRegexIndexrçrÙrürpZcurrFailr÷rûr�ZfidsrdrCrCrDrÌGsÜ                                  "zFilter.findFailureÚbasiccCs d|jjƒfd|jjƒfg}|S)z)Status of failures detected by filter. zCurrently failedz Total failed)rÚsizeZ getFailTotal)rAÚflavorrºrCrCrDÚstatusÒs z Filter.status)r)T)N)N)N)F)N)r°)T)T)N)N)rÛN)N)F)rÿ)6rFÚ __module__Ú __qualname__rrGÚpropertyr<rKrVr]r^ÚsetterrerHrjrkrIrmr!rtrwrxr‚r†r‡rˆr‰rŠr�rŽr“r”r–r—r™rŸrœr§r¨r­rJr³r´r¶rµrÁrÒrÚr×rérìrørÌrrCrCrCrDr9sl E                      0 S %  <  rcs¬eZdZdd„Zd(dd„Zdd„Zd d „Zd d „Zd d„Zdd„Z dd„Z dd„Z ‡fdd„Z dd„Z d)dd„Zd*dd„Zd+‡fd d!„ Zd"d#„Zd$d%„Z‡fd&d'„Z‡ZS),Ú FileFiltercKs$tj||f|Žtƒ|_tƒ|_dS)N)rrrRÚ_FileFilter__logsÚ_FileFilter__autoSeek)rArÚkwargsrCrCrDrÜszFileFilter.__init__FTcCs¾||jkr8t|dƒr(||jkr(|j|=qºtj|dƒn‚t||jƒ|ƒ}|jj}|dk rz|j |j|ƒ}|rz| rz|j |ƒ||j|<tj d||j ƒ|j ƒƒ|r°| r°||j|<|j|ƒdS)NrPz already existsz'Added logfile: %r (pos = %s, hash = %s))rrQrPr?rbÚ FileContainerr”rÚdatabaseZaddLogÚsetPosrvÚgetPosÚgetHashr Ú _addLogPath)rArUÚtailZautoSeekr©ÚdbZlastposrCrCrDÚ addLogPathçs       zFileFilter.addLogPathcCsdS)NrC)rArUrCrCrDrúszFileFilter._addLogPathc Cs@y|jj|ƒ}Wntk r$dSXtjd|ƒ|j|ƒdS)NzRemoved logfile: %r)rrôÚKeyErrorr?rvÚ _delLogPath)rArUr©rCrCrDrSs  zFileFilter.delLogPathcCsdS)NrC)rArUrCrCrDr szFileFilter._delLogPathcCst|jjƒƒS)N)rrÚkeys)rArCrCrDrLszFileFilter.getLogPathscCst|jjƒƒS)N)rrÚvalues)rArCrCrDÚgetLogsszFileFilter.getLogscCs t|jƒS)N)r…r)rArCrCrDÚ getLogCount'szFileFilter.getLogCountcCs ||jkS)N)r)rArUrCrCrDÚcontainsLogPath0szFileFilter.containsLogPathcs2tt|ƒj|ƒ}x|jjƒD]}|j|ƒqWdS)N)Úsuperrr“rrÚ setEncoding)rAr’r©)rErCrDr“8szFileFilter.setLogEncodingcCs|jj|dƒS)N)rr›)rArUrCrCrDÚgetLog=szFileFilter.getLogNc%Cs2|jr dS|j|ƒ}|dkr,tjd|ƒdS�z–y |jƒ}Wn¦tk r|}z$tjd|ƒ|jdkrntj|ƒdSd}~Xnhtk r°}ztjd|ƒtj|ƒdSd}~Xn4t k râ}ztjdƒtj|ƒdSd}~XnX|j �rh|j j |dƒ}|�rht |t ƒ�rtjƒ|jƒ}y|j||ƒWn:t k �rf}ztjd|ƒ‚tj|ƒdSd}~XnX|�rÂxR|j�sÀ|jƒ}|j�sŠP|dk�rœd|_P|dk �rª|n|j|_|j|ƒ�qpWWd|jƒX|jjdk �r.d |j|<|jd d k�stjƒ|jk�s|j �r.|jƒtjƒtjd |_dS) NFzUnable to get failures in %szUnable to open %srzError opening %szsInternal error in FileContainer open method - please report as a bug to https://github.com/fail2ban/fail2ban/issuesz'Error during seek to start time in "%s"TrrWrrX)rÜrr?rbÚopenÚIOErrorÚerrnoZ exceptionÚOSErrorr•r rôrqrrr r rxÚ seekToTimeÚreadlineZactiver9rÚÚcloserr r3r:r2Ú_updateDBPendingrZDEFAULT_SLEEP_TIME)rAÚfilenamer9r©Z has_contentrdZ startTimerÍrCrCrDÚ getFailuresGsj                  zFileFilter.getFailuresròcCsl|jƒ}tjƒtjkr,tjd|tj|ƒ|ƒ|s4dS|jƒ}|}|}d }d} d} d} d} |} �xº||k�r|dkrˆt |||dƒ}n |d}}t d|dƒ}|j |ƒ}}| d7} d}d}d}xˆ|j dƒ}|dkrØP|j j|ƒ\}}|�r|j j||jƒ|jƒ…||fƒ}n|jƒ}||k�r*|}P|}| �rF|�rF|d8}qÄPqÄW|�rÞ|d} | |k�rž| dk�sx| | k�r€|} | } ||k�rŽ|}||k�rÞ|}n@| dk�s²| | k�rº|} | } |dk�rÌ|jƒ}|}||k�rÞ|}||k�r| d8} | dk�rüP||k�r|}}q`P|}q`W|j |dƒ} |j| ƒtjƒtjk�rhtjd||| | dk �r`tj| ƒnd| ƒdS) Nz'Seek to find time %s (%s), file size %srrrrXFz7Position %s from %s, found time %s (%s) within %s seeksréÿÿÿÿ)Ú getFileSizer?r¾rªrÖr@r rÕrr‹ÚmaxÚseekr#r>rÇrÊrÈrÉÚtellr )rAZ containerrÎZaccuracyZfsZminpZmaxpZtryPosZlastPosZfoundPosZ foundTimeZcntrr¥ZmovecntrÚposZseekposZlncntrZ dateTimeMatchZnextprÍrÏÚtemplaterCrCrDr"�s–               zFileFilter.seekToTimerÿcs2tt|ƒj|d�}t|jjƒƒ}|jd|fƒ|S)z/Status of Filter plus files being monitored. )rz File list)rrrrrrra)rArrºrU)rErCrDræszFileFilter.statusc CsJ|jj}xrArUÚgetsizer6)rAÚhrErCrCrDr)Qs zFileContainer.getFileSizecCstj|ƒ||_dS)N)r�r‘Ú_FileContainer__encoding)rAr’rCrCrDrXs zFileContainer.setEncodingcCs|jS)N)rO)rArCrCrDÚ getEncoding\szFileContainer.getEncodingcCs|jS)N)r:)rArCrCrDr_szFileContainer.getHashcCs|jS)N)r9)rArCrCrDrbszFileContainer.getPoscCs ||_dS)N)r9)rAr`rCrCrDr eszFileContainer.setPosNc Cs|t|jdƒ}�zT|jƒ}tj|tjƒ}tj|tj|tjBƒ|j}tj |jƒƒ}|j |j kpf|j |j k}|s„t|ƒ s„tjƒ|jkrºd}|jƒ}||jdƒkrÌt|ƒjƒ}tjƒd|_n|j |j krÌ|j}|sÜ||jk�r&|jdk�r tjtjd|j|j |j |j |j ||jfƒ|j |_ d|_ ||_|dk �r8||_ n|j |j k�rJdS|j|j ƒ||_d}Wd|�rv|jƒd}XdS) Nr5rs r4z(Log rotation detected for %s, reason: %rrFT)rr6r>ÚfcntlZF_GETFDZF_SETFDZ FD_CLOEXECr:r<r=rAr9r?r@r…r r;r#rBrCrDr?r©rªr«r+r8r$) rAZforcePosrNÚfdÚflagsZmyHashrEZrotflgrFrCrCrDrhsD      zFileContainer.openTcCsB|j}|dkr|j|ƒ|j}|j|dƒ|r:|r:|jƒ|jƒS)Nr)r8rr+r#r,)rAZoffsZendLinerNrCrCrDr+“s  zFileContainer.seekcCs |jjƒS)N)r8r,)rArCrCrDr, szFileContainer.tellcCsÂy |j|dƒSttfk r¼}z�|jt|ƒkrT||jdkrT|d|j…j|dƒSd}tj|dƒsvtj }tj |dƒt j |d||ƒt j ƒ|kr t j |d|ƒ|j|dƒ}WYdd}~XnX|S) NÚstricts rÚreplacer»rz(Error decoding line from '%s' with '%s'.z~Consider setting logencoding to appropriate encoding for this jail. Continuing to process line ignoring invalid characters: %r)ÚdecodeÚUnicodeDecodeErrorÚUnicodeEncodeErrorrÉr…rÈÚ_decode_line_warnr›rªZWARNINGr$r?r©r¾)r&ÚencrÍrdZlevrCrCrDÚ decode_line¤s"     zFileContainer.decode_linecCs@|jdkrdS|jjƒ}|s dSt|ƒ}tj|jƒ|jƒ|ƒ}|jdƒ}|�r<||k�rs`        (; cserver/__pycache__/filtergamin.cpython-36.opt-1.pyc000064400000005637152231502150016157 0ustar003 UB(dã@s|dZdZdZddlZddlZddlZddlmZddlm Z ddl m Z dd l m Z d d lmZeeƒZGd d „d e ƒZdS)z!Cyril Jaquier, Yaroslav Halchenkoz9Copyright (c) 2004 Cyril Jaquier, 2012 Yaroslav HalchenkoZGPLéNé)ÚFailManagerEmpty)Ú FileFilter)ÚMyTime)ÚUtilsé)Ú getLoggercsTeZdZdd„Zdd„Zdd„Zdd„Zd d „Zd d „Z‡fd d„Z dd„Z ‡Z S)Ú FilterGamincCsRtj||ƒtjƒ|_|jjƒ}tj|tjƒ}tj|tj|tj Bƒt j dƒdS)NzCreated FilterGamin) rÚ__init__ÚgaminZ WatchMonitorÚmonitorZget_fdÚfcntlZF_GETFDZF_SETFDZ FD_CLOEXECÚlogSysÚdebug)ÚselfZjailÚfdÚflags©rú!/usr/lib/python3.6/filtergamin.pyr 8s    zFilterGamin.__init__cCsZtjddt|ƒd|ƒ|tjtjtjfkr>tjd|ƒ|jd7_|j |ƒdS)Néz Got event: z for zFile changed: r) rÚlogÚreprr Z GAMCreatedZ GAMChangedZ GAMExistsrÚticksZ getFailures)rÚpathZeventrrrÚcallbackAs zFilterGamin.callbackcCs|jj||jƒdS)N)r Z watch_filer)rrrrrÚ _addLogPathNszFilterGamin._addLogPathcCs|jj|ƒdS)N)r Ú stop_watch)rrrrrÚ _delLogPathVszFilterGamin._delLogPathcCs6d}|j}x&|r0|jƒdkr0|jƒ|j}d}q W|S)NFrT)r Z event_pendingZ handle_events)rÚretZmonrrrÚ _handleEventsYszFilterGamin._handleEventscsŒxxˆjrxˆjrnsz!FilterGamin.run..é rcsˆj pˆjƒS)N)r rr)rrrr"ssrz[%s] filter terminatedT) r r!rZwait_forZ sleeptimerZ performSvcrrZjailName)rr)rrÚrunis zFilterGamin.runcstt|ƒjƒ|jƒdS)N)Úsuperr ÚstopÚ_FilterGamin__cleanup)r)Ú __class__rrr&|szFilterGamin.stopcCs2|js dSx|jƒD]}|jj|ƒqWd|_dS)N)r Z getLogPathsr)rÚfilenamerrrZ __cleanupƒs zFilterGamin.__cleanup) Ú__name__Ú __module__Ú __qualname__r rrrrr$r&r'Ú __classcell__rr)r(rr 0s   r )Ú __author__Z __copyright__Z __license__r Ztimer Z failmanagerrÚfilterrZmytimerZutilsrZhelpersrr*rr rrrrÚs      server/__pycache__/filtergamin.cpython-36.pyc000064400000005637152231502150015220 0ustar003 UB(dã@s|dZdZdZddlZddlZddlZddlmZddlm Z ddl m Z dd l m Z d d lmZeeƒZGd d „d e ƒZdS)z!Cyril Jaquier, Yaroslav Halchenkoz9Copyright (c) 2004 Cyril Jaquier, 2012 Yaroslav HalchenkoZGPLéNé)ÚFailManagerEmpty)Ú FileFilter)ÚMyTime)ÚUtilsé)Ú getLoggercsTeZdZdd„Zdd„Zdd„Zdd„Zd d „Zd d „Z‡fd d„Z dd„Z ‡Z S)Ú FilterGamincCsRtj||ƒtjƒ|_|jjƒ}tj|tjƒ}tj|tj|tj Bƒt j dƒdS)NzCreated FilterGamin) rÚ__init__ÚgaminZ WatchMonitorÚmonitorZget_fdÚfcntlZF_GETFDZF_SETFDZ FD_CLOEXECÚlogSysÚdebug)ÚselfZjailÚfdÚflags©rú!/usr/lib/python3.6/filtergamin.pyr 8s    zFilterGamin.__init__cCsZtjddt|ƒd|ƒ|tjtjtjfkr>tjd|ƒ|jd7_|j |ƒdS)Néz Got event: z for zFile changed: r) rÚlogÚreprr Z GAMCreatedZ GAMChangedZ GAMExistsrÚticksZ getFailures)rÚpathZeventrrrÚcallbackAs zFilterGamin.callbackcCs|jj||jƒdS)N)r Z watch_filer)rrrrrÚ _addLogPathNszFilterGamin._addLogPathcCs|jj|ƒdS)N)r Ú stop_watch)rrrrrÚ _delLogPathVszFilterGamin._delLogPathcCs6d}|j}x&|r0|jƒdkr0|jƒ|j}d}q W|S)NFrT)r Z event_pendingZ handle_events)rÚretZmonrrrÚ _handleEventsYszFilterGamin._handleEventscsŒxxˆjrxˆjrnsz!FilterGamin.run..é rcsˆj pˆjƒS)N)r rr)rrrr"ssrz[%s] filter terminatedT) r r!rZwait_forZ sleeptimerZ performSvcrrZjailName)rr)rrÚrunis zFilterGamin.runcstt|ƒjƒ|jƒdS)N)Úsuperr ÚstopÚ_FilterGamin__cleanup)r)Ú __class__rrr&|szFilterGamin.stopcCs2|js dSx|jƒD]}|jj|ƒqWd|_dS)N)r Z getLogPathsr)rÚfilenamerrrZ __cleanupƒs zFilterGamin.__cleanup) Ú__name__Ú __module__Ú __qualname__r rrrrr$r&r'Ú __classcell__rr)r(rr 0s   r )Ú __author__Z __copyright__Z __license__r Ztimer Z failmanagerrÚfilterrZmytimerZutilsrZhelpersrr*rr rrrrÚs      server/__pycache__/filterpoll.cpython-36.opt-1.pyc000064400000007072152231502150016025 0ustar003 UB(dã@s`dZdZdZddlZddlZddlmZddlmZdd l m Z m Z e e ƒZ Gd d „d eƒZdS) z!Cyril Jaquier, Yaroslav Halchenkoz9Copyright (c) 2004 Cyril Jaquier; 2012 Yaroslav HalchenkoZGPLéNé)Ú FileFilter)ÚUtilsé)Ú getLoggerÚloggingc@sDeZdZdd„Zdd„Zdd„Zdd„Zd d „Zd d „Zd d„Z dS)Ú FilterPollcCs*tj||ƒtƒ|_tƒ|_tjdƒdS)NzCreated FilterPoll)rÚ__init__ÚdictÚ_FilterPoll__prevStatsÚ_FilterPoll__file404CntÚlogSysÚdebug)ÚselfZjail©rú /usr/lib/python3.6/filterpoll.pyr 6s zFilterPoll.__init__cCsd|j|<d|j|<dS)Nr)rNN)r r )rÚpathrrrÚ _addLogPathBs zFilterPoll._addLogPathcCs|j|=|j|=dS)N)r r )rrrrrÚ _delLogPathKszFilterPoll._delLogPathcCs*x$|jƒD]}|j|ƒr |j|ƒq W|S)N)Z getLogPathsÚ isModifiedÚappend)rÚmodlstÚfilenamerrrÚ getModifiedRs zFilterPoll.getModifiedcs0�xˆj�ryºtjƒdkr0tjddˆjˆjƒƒˆjrdtj‡fdd„ˆjdˆjƒsdˆj d7_ wg‰tj‡‡fdd„ˆjƒˆjsˆPxˆD]}ˆj |ƒqŽWˆj d7_ ˆj ddkrĈj ƒWqt k �r}z4ˆjsâPtj d |tjƒtjkd �ˆjd |ƒWYdd}~XqXqWtjd ˆjƒd S)Néz'Woke up idle=%s with %d files monitoredcsˆj pˆj S)N)ÚactiveÚidler)rrrÚfsz FilterPoll.run..é rcsˆj pˆjˆƒS)N)rrr)rrrrrmsrz,Caught unhandled exception in main cycle: %r)Úexc_infoZ unhandledz[%s] filter terminatedT)rr ÚgetEffectiveLevelÚlogrZ getLogCountrZwait_forZ sleeptimeZticksZ getFailuresZ performSvcÚ ExceptionÚerrorrÚDEBUGÚ commonErrorrZjailName)rrÚer)rrrÚrun_s8    "zFilterPoll.runcCsly†tj|ƒ}|j|j|jf}|jj|dƒ}tjƒdkrX|j|d}tj dd||||ƒd|j |<||krndStj d|ƒ||j|<dSt k �rf}zÂ|j |ƒ s¶|jj|ƒdkrÈtjd||ƒdS|j |dk�r|jdkròtj d ||ƒntjd ||tjƒtjkd �|j |d 7<|jƒ|j |d k�rXtjd|ƒd|j |<|j|ƒdSd}~XnXdS)NrrzGChecking %s for being modified. Previous/current stats: %s / %s. dt: %sFz%s has been modifiedTzLog %r seems to be down: %srz;Log absence detected (possibly rotation) for %s, reason: %sz'Unable to get stat on %s because of: %s)rré2z7Too many errors. Remove file %r from monitoring process)r)ÚosÚstatÚst_mtimeÚst_inoÚst_sizer Úgetr r r!r rr"ZgetLogZwarningÚerrnor#rr$r%Z delLogPath)rrZlogStatsZstatsZpstatsZdtr&rrrr‡s@           zFilterPoll.isModifiedcCst|jjƒƒS)N)Úlistr Úkeys)rrrrÚgetPendingPaths¯szFilterPoll.getPendingPathsN) Ú__name__Ú __module__Ú __qualname__r rrrr'rr2rrrrr.s   ((r)Ú __author__Z __copyright__Z __license__r)ZtimeÚfilterrZutilsrZhelpersrrr3r rrrrrÚs   server/__pycache__/filterpoll.cpython-36.pyc000064400000007072152231502150015066 0ustar003 UB(dã@s`dZdZdZddlZddlZddlmZddlmZdd l m Z m Z e e ƒZ Gd d „d eƒZdS) z!Cyril Jaquier, Yaroslav Halchenkoz9Copyright (c) 2004 Cyril Jaquier; 2012 Yaroslav HalchenkoZGPLéNé)Ú FileFilter)ÚUtilsé)Ú getLoggerÚloggingc@sDeZdZdd„Zdd„Zdd„Zdd„Zd d „Zd d „Zd d„Z dS)Ú FilterPollcCs*tj||ƒtƒ|_tƒ|_tjdƒdS)NzCreated FilterPoll)rÚ__init__ÚdictÚ_FilterPoll__prevStatsÚ_FilterPoll__file404CntÚlogSysÚdebug)ÚselfZjail©rú /usr/lib/python3.6/filterpoll.pyr 6s zFilterPoll.__init__cCsd|j|<d|j|<dS)Nr)rNN)r r )rÚpathrrrÚ _addLogPathBs zFilterPoll._addLogPathcCs|j|=|j|=dS)N)r r )rrrrrÚ _delLogPathKszFilterPoll._delLogPathcCs*x$|jƒD]}|j|ƒr |j|ƒq W|S)N)Z getLogPathsÚ isModifiedÚappend)rÚmodlstÚfilenamerrrÚ getModifiedRs zFilterPoll.getModifiedcs0�xˆj�ryºtjƒdkr0tjddˆjˆjƒƒˆjrdtj‡fdd„ˆjdˆjƒsdˆj d7_ wg‰tj‡‡fdd„ˆjƒˆjsˆPxˆD]}ˆj |ƒqŽWˆj d7_ ˆj ddkrĈj ƒWqt k �r}z4ˆjsâPtj d |tjƒtjkd �ˆjd |ƒWYdd}~XqXqWtjd ˆjƒd S)Néz'Woke up idle=%s with %d files monitoredcsˆj pˆj S)N)ÚactiveÚidler)rrrÚfsz FilterPoll.run..é rcsˆj pˆjˆƒS)N)rrr)rrrrrmsrz,Caught unhandled exception in main cycle: %r)Úexc_infoZ unhandledz[%s] filter terminatedT)rr ÚgetEffectiveLevelÚlogrZ getLogCountrZwait_forZ sleeptimeZticksZ getFailuresZ performSvcÚ ExceptionÚerrorrÚDEBUGÚ commonErrorrZjailName)rrÚer)rrrÚrun_s8    "zFilterPoll.runcCsly†tj|ƒ}|j|j|jf}|jj|dƒ}tjƒdkrX|j|d}tj dd||||ƒd|j |<||krndStj d|ƒ||j|<dSt k �rf}zÂ|j |ƒ s¶|jj|ƒdkrÈtjd||ƒdS|j |dk�r|jdkròtj d ||ƒntjd ||tjƒtjkd �|j |d 7<|jƒ|j |d k�rXtjd|ƒd|j |<|j|ƒdSd}~XnXdS)NrrzGChecking %s for being modified. Previous/current stats: %s / %s. dt: %sFz%s has been modifiedTzLog %r seems to be down: %srz;Log absence detected (possibly rotation) for %s, reason: %sz'Unable to get stat on %s because of: %s)rré2z7Too many errors. Remove file %r from monitoring process)r)ÚosÚstatÚst_mtimeÚst_inoÚst_sizer Úgetr r r!r rr"ZgetLogZwarningÚerrnor#rr$r%Z delLogPath)rrZlogStatsZstatsZpstatsZdtr&rrrr‡s@           zFilterPoll.isModifiedcCst|jjƒƒS)N)Úlistr Úkeys)rrrrÚgetPendingPaths¯szFilterPoll.getPendingPathsN) Ú__name__Ú __module__Ú __qualname__r rrrr'rr2rrrrr.s   ((r)Ú __author__Z __copyright__Z __license__r)ZtimeÚfilterrZutilsrZhelpersrrr3r rrrrrÚs   server/__pycache__/filterpyinotify.cpython-36.opt-1.pyc000064400000024104152231502150017104 0ustar003 UB(d÷2ã@sdZdZdZddlZddlmZddlZddlmZm Z ddl Z ddl m Z dd lmZdd lmZmZdd lmZd d lmZee dƒ sšee jƒdkr¢edƒ‚ye jƒZ[Wn2ek râZzedeeƒƒ‚WYddZ[XnXeeƒZ dd„Z!e!e _"e e _#Gdd„deƒZ$dS)z.Cyril Jaquier, Lee Clemens, Yaroslav HalchenkozPCopyright (c) 2004 Cyril Jaquier, 2011-2012 Lee Clemens, 2012 Yaroslav HalchenkoZGPLéN)Ú LooseVersion)ÚdirnameÚsepé)ÚFailManagerEmpty)Ú FileFilter)ÚMyTimeÚtime)ÚUtilsé)Ú getLoggerÚ __version__z0.8.3z$Fail2Ban requires pyinotify >= 0.8.3z7Pyinotify is probably not functional on this system: %scCstS)N)ÚlogSys©rrú%/usr/lib/python3.6/filterpyinotify.pyÚ_pyinotify_logger_init9srcsÊeZdZdd„Zd.dd„Zdd„Zd/d d „Zd d „Zd d„Zdd„Z d0dd„Z dd„Z dd„Z dd„Z dd„Zdd„Zdd„Zd d!„Zd"d#„Zed$d%„ƒZd&d'„Z‡fd(d)„Z‡fd*d+„Zd,d-„Z‡ZS)1ÚFilterPyinotifycCsNtj||ƒtjƒ|_d|_tƒ|_tƒ|_tƒ|_ d|_ d|_ t j dƒdS)Nré<zCreated FilterPyinotify)rÚ__init__Ú pyinotifyÚ WatchManagerÚ_FilterPyinotify__monitorÚ_FilterPyinotify__notifierÚdictÚ_FilterPyinotify__watchFilesÚ_FilterPyinotify__watchDirsÚ_FilterPyinotify__pendingÚ _FilterPyinotify__pendingChkTimeÚ _FilterPyinotify__pendingMinTimerÚdebug)ÚselfZjailrrrrLs  zFilterPyinotify.__init__ÚcCsÆtjdd|j||ƒ|j}d}||jk}| r<||jkr|}q>|rrtjjntjj }||ƒs®|dkr’|d9}||krž|}||j|d<q>t j t j d|rÀdnd|ƒ|||<q>Wtjƒ|_||_x¢|jƒD]–\}}|j|ƒ|dk �r|j||d�|�rzxdt|jƒD]J}|j|tƒ�r*tjj |ƒ�s^|j|d|fƒn|j|ƒ|j|ƒ�q*Wqî|j|ƒqîWdS) Nrr rzLog presence detected for %s %sZ directoryÚfile)r#Z FROM_PARDIR)rr rrrDÚitemsr0r1r2r6rr$r?r@rCr+rr4r5r3r9) r ZntmÚfoundZminTimer1ZretardTMr#Zchkpathr<rrrÚ _checkPending sJ      zFilterPyinotify._checkPendingNcCs:|s|}|s"|j|ƒ|j|ƒn|j|ƒ|j|ƒdS)N)Ú_delFileWatcherÚ_addFileWatcherÚ_delDirWatcherÚ_addDirWatcher)r ZoldPathZnewPathr#rrrr+Ës   zFilterPyinotify._refreshWatchercCs:|jt|ƒƒ|jj|tjƒ}|jj|ƒtj d|ƒdS)NzAdded file watcher for %s) rNrrÚ add_watchrZ IN_MODIFYrÚupdaterr)r r1ÚwdrrrrLÖs zFilterPyinotify._addFileWatchercCs‚|j}y$|j|ƒdk r(|j|dd�}dSWnRtjk r|}z4|j|ƒdk rlt|ƒjdƒ rltjd|ƒ|‚WYdd}~XnXdS)NF)ÚquietTz(EINVAL)zRemove watch causes: %s) rÚget_pathZrm_watchrZWatchManagerErrorÚstrr.rr)r ÚwdIntÚmrQÚerrrÚ _delWatchÞs zFilterPyinotify._delWatchc CsNy4|jj|ƒ}|j|ƒs&tjd||ƒtjd|ƒdStk rHYnXdS)Nz(Non-existing file watcher %r for file %szRemoved file watcher for %sTF)rÚpoprXrrrB)r r1rUrrrrKês   zFilterPyinotify._delFileWatchercCsJ||jkrF|jj|jj|tjtjBtjBtjBtj Bƒƒt j d|ƒdS)Nz)Added monitor for the parent directory %s) rrPrrOrr(r)r,r-r*rr)r Úpath_dirrrrrNõs  "zFilterPyinotify._addDirWatcherc CsNy4|jj|ƒ}|j|ƒs&tjd||ƒtjd|ƒWntk rHYnXdS)Nz-Non-existing file watcher %r for directory %sz+Removed monitor for the parent directory %s)rrYrXrrrB)r rZrUrrrrMþs  zFilterPyinotify._delDirWatchercCs.|j|ƒ|jrd|_|j|d|fdd�dS)NrZINITIAL)r#)rLÚactiverr3)r r1rrrÚ _addLogPath s zFilterPyinotify._addLogPathcCsl|j|ƒ|j|ƒs tjd|ƒt|ƒ}x&t|jƒD]}|j|tƒr4d}Pq4W|rh|j|ƒ|j |ƒdS)Nz"Failed to remove watch on path: %s) rCrKrÚerrorrrDrr4r5rM)r r1rZÚkrrrÚ _delLogPaths    zFilterPyinotify._delLogPathcCsjy|j|dd�WnDtk rV}z(tjd|tjƒtjkd�|jƒWYdd}~XnX|jd7_dS)NzDefault )r;z%Error in FilterPyinotify callback: %s)Úexc_infor) r=Ú Exceptionrr]ÚgetEffectiveLevelr?ÚDEBUGÚ commonErrorÚticks)r r:rWrrrZ__process_default+sz!FilterPyinotify.__process_defaultcCst|jd|jƒdS)Ngà?iè)ÚminÚ sleeptimer)r rrrZ__notify_maxtout5sz FilterPyinotify.__notify_maxtoutcsttjƒ}ˆj|_tjˆj|ˆjd�ˆ_tj dˆj ƒ�x$ˆj �rZyˆj r~t j‡fdd„tˆjdˆjƒtˆjˆjƒƒr~ˆj s~Pˆjjƒ‡fdd„}t j|tˆjˆjƒƒ}|rʈj s¶Pt|tƒsʈjjƒˆjd7_ˆj ràw8ˆjƒˆjdd k�rˆjƒWq8tk �rV}z6ˆj �s Ptjd |tjƒtjkd �ˆjd |ƒWYdd}~Xq8Xq8Wtj d ˆj ƒdˆ_dS)N)Útimeoutz![%s] filter started (pyinotifier)csˆj pˆj S)N)r[r7r)r rrÚLsz%FilterPyinotify.run..é cs(ˆj p&tˆjjˆjd�ƒp&ˆjo&ˆjS)N)rh)r[ÚboolrZ check_eventsÚ _FilterPyinotify__notify_maxtoutrrr)r rrZ__check_eventsVsz+FilterPyinotify.run..__check_eventsrrz,Caught unhandled exception in main cycle: %r)r`Z unhandledz [%s] filter exited (pyinotifier)T)rZ ProcessEventÚ!_FilterPyinotify__process_defaultZprocess_defaultZNotifierrrlrrrr%r[r7r Úwait_forrfrgrZprocess_eventsr>rZ read_eventsrerJZ performSvcrar]rbr?rcrd)r ZprceventZ_FilterPyinotify__check_eventsZwresrWr)r rÚrunAsH      "zFilterPyinotify.runc sDtt|ƒjƒy|jr |jjƒWntk r>|jr:‚YnXdS)N)ÚsuperrÚstoprÚAttributeError)r )Ú __class__rrrq{szFilterPyinotify.stopcs2dd„|_|jƒtt|ƒjƒtjd|jƒdS)NcWsdS)Nrr)Úargsrrrriˆsz&FilterPyinotify.join..z$[%s] filter terminated (pyinotifier))ÚjoinÚ_FilterPyinotify__cleanuprprrrr%)r )rsrrru‡s zFilterPyinotify.joincs0ˆjr,tj‡fdd„ˆjdƒr,dˆ_dˆ_dS)Ncsˆj S)N)rr)r rrri’sz+FilterPyinotify.__cleanup..rj)rr rnrgr)r r)r rZ __cleanup�szFilterPyinotify.__cleanup)r!)F)NF)Ú__name__Ú __module__Ú __qualname__rr=r9r3rCrFrJr+rLrXrKrNrMr\r_rmÚpropertyrlrorqrurvÚ __classcell__rr)rsrrEs* . +      : r)%Ú __author__Z __copyright__Z __license__r?Zdistutils.versionrr0Zos.pathrrr5rZ failmanagerrÚfilterrZmytimerr Zutilsr Zhelpersr Úhasattrr Ú ImportErrorrZmanagerrarWrTrwrrZ _logger_initr$rrrrrÚs4       server/__pycache__/filterpyinotify.cpython-36.pyc000064400000024104152231502150016145 0ustar003 UB(d÷2ã@sdZdZdZddlZddlmZddlZddlmZm Z ddl Z ddl m Z dd lmZdd lmZmZdd lmZd d lmZee dƒ sšee jƒdkr¢edƒ‚ye jƒZ[Wn2ek râZzedeeƒƒ‚WYddZ[XnXeeƒZ dd„Z!e!e _"e e _#Gdd„deƒZ$dS)z.Cyril Jaquier, Lee Clemens, Yaroslav HalchenkozPCopyright (c) 2004 Cyril Jaquier, 2011-2012 Lee Clemens, 2012 Yaroslav HalchenkoZGPLéN)Ú LooseVersion)ÚdirnameÚsepé)ÚFailManagerEmpty)Ú FileFilter)ÚMyTimeÚtime)ÚUtilsé)Ú getLoggerÚ __version__z0.8.3z$Fail2Ban requires pyinotify >= 0.8.3z7Pyinotify is probably not functional on this system: %scCstS)N)ÚlogSys©rrú%/usr/lib/python3.6/filterpyinotify.pyÚ_pyinotify_logger_init9srcsÊeZdZdd„Zd.dd„Zdd„Zd/d d „Zd d „Zd d„Zdd„Z d0dd„Z dd„Z dd„Z dd„Z dd„Zdd„Zdd„Zd d!„Zd"d#„Zed$d%„ƒZd&d'„Z‡fd(d)„Z‡fd*d+„Zd,d-„Z‡ZS)1ÚFilterPyinotifycCsNtj||ƒtjƒ|_d|_tƒ|_tƒ|_tƒ|_ d|_ d|_ t j dƒdS)Nré<zCreated FilterPyinotify)rÚ__init__Ú pyinotifyÚ WatchManagerÚ_FilterPyinotify__monitorÚ_FilterPyinotify__notifierÚdictÚ_FilterPyinotify__watchFilesÚ_FilterPyinotify__watchDirsÚ_FilterPyinotify__pendingÚ _FilterPyinotify__pendingChkTimeÚ _FilterPyinotify__pendingMinTimerÚdebug)ÚselfZjailrrrrLs  zFilterPyinotify.__init__ÚcCsÆtjdd|j||ƒ|j}d}||jk}| r<||jkr|}q>|rrtjjntjj }||ƒs®|dkr’|d9}||krž|}||j|d<q>t j t j d|rÀdnd|ƒ|||<q>Wtjƒ|_||_x¢|jƒD]–\}}|j|ƒ|dk �r|j||d�|�rzxdt|jƒD]J}|j|tƒ�r*tjj |ƒ�s^|j|d|fƒn|j|ƒ|j|ƒ�q*Wqî|j|ƒqîWdS) Nrr rzLog presence detected for %s %sZ directoryÚfile)r#Z FROM_PARDIR)rr rrrDÚitemsr0r1r2r6rr$r?r@rCr+rr4r5r3r9) r ZntmÚfoundZminTimer1ZretardTMr#Zchkpathr<rrrÚ _checkPending sJ      zFilterPyinotify._checkPendingNcCs:|s|}|s"|j|ƒ|j|ƒn|j|ƒ|j|ƒdS)N)Ú_delFileWatcherÚ_addFileWatcherÚ_delDirWatcherÚ_addDirWatcher)r ZoldPathZnewPathr#rrrr+Ës   zFilterPyinotify._refreshWatchercCs:|jt|ƒƒ|jj|tjƒ}|jj|ƒtj d|ƒdS)NzAdded file watcher for %s) rNrrÚ add_watchrZ IN_MODIFYrÚupdaterr)r r1ÚwdrrrrLÖs zFilterPyinotify._addFileWatchercCs‚|j}y$|j|ƒdk r(|j|dd�}dSWnRtjk r|}z4|j|ƒdk rlt|ƒjdƒ rltjd|ƒ|‚WYdd}~XnXdS)NF)ÚquietTz(EINVAL)zRemove watch causes: %s) rÚget_pathZrm_watchrZWatchManagerErrorÚstrr.rr)r ÚwdIntÚmrQÚerrrÚ _delWatchÞs zFilterPyinotify._delWatchc CsNy4|jj|ƒ}|j|ƒs&tjd||ƒtjd|ƒdStk rHYnXdS)Nz(Non-existing file watcher %r for file %szRemoved file watcher for %sTF)rÚpoprXrrrB)r r1rUrrrrKês   zFilterPyinotify._delFileWatchercCsJ||jkrF|jj|jj|tjtjBtjBtjBtj Bƒƒt j d|ƒdS)Nz)Added monitor for the parent directory %s) rrPrrOrr(r)r,r-r*rr)r Úpath_dirrrrrNõs  "zFilterPyinotify._addDirWatcherc CsNy4|jj|ƒ}|j|ƒs&tjd||ƒtjd|ƒWntk rHYnXdS)Nz-Non-existing file watcher %r for directory %sz+Removed monitor for the parent directory %s)rrYrXrrrB)r rZrUrrrrMþs  zFilterPyinotify._delDirWatchercCs.|j|ƒ|jrd|_|j|d|fdd�dS)NrZINITIAL)r#)rLÚactiverr3)r r1rrrÚ _addLogPath s zFilterPyinotify._addLogPathcCsl|j|ƒ|j|ƒs tjd|ƒt|ƒ}x&t|jƒD]}|j|tƒr4d}Pq4W|rh|j|ƒ|j |ƒdS)Nz"Failed to remove watch on path: %s) rCrKrÚerrorrrDrr4r5rM)r r1rZÚkrrrÚ _delLogPaths    zFilterPyinotify._delLogPathcCsjy|j|dd�WnDtk rV}z(tjd|tjƒtjkd�|jƒWYdd}~XnX|jd7_dS)NzDefault )r;z%Error in FilterPyinotify callback: %s)Úexc_infor) r=Ú Exceptionrr]ÚgetEffectiveLevelr?ÚDEBUGÚ commonErrorÚticks)r r:rWrrrZ__process_default+sz!FilterPyinotify.__process_defaultcCst|jd|jƒdS)Ngà?iè)ÚminÚ sleeptimer)r rrrZ__notify_maxtout5sz FilterPyinotify.__notify_maxtoutcsttjƒ}ˆj|_tjˆj|ˆjd�ˆ_tj dˆj ƒ�x$ˆj �rZyˆj r~t j‡fdd„tˆjdˆjƒtˆjˆjƒƒr~ˆj s~Pˆjjƒ‡fdd„}t j|tˆjˆjƒƒ}|rʈj s¶Pt|tƒsʈjjƒˆjd7_ˆj ràw8ˆjƒˆjdd k�rˆjƒWq8tk �rV}z6ˆj �s Ptjd |tjƒtjkd �ˆjd |ƒWYdd}~Xq8Xq8Wtj d ˆj ƒdˆ_dS)N)Útimeoutz![%s] filter started (pyinotifier)csˆj pˆj S)N)r[r7r)r rrÚLsz%FilterPyinotify.run..é cs(ˆj p&tˆjjˆjd�ƒp&ˆjo&ˆjS)N)rh)r[ÚboolrZ check_eventsÚ _FilterPyinotify__notify_maxtoutrrr)r rrZ__check_eventsVsz+FilterPyinotify.run..__check_eventsrrz,Caught unhandled exception in main cycle: %r)r`Z unhandledz [%s] filter exited (pyinotifier)T)rZ ProcessEventÚ!_FilterPyinotify__process_defaultZprocess_defaultZNotifierrrlrrrr%r[r7r Úwait_forrfrgrZprocess_eventsr>rZ read_eventsrerJZ performSvcrar]rbr?rcrd)r ZprceventZ_FilterPyinotify__check_eventsZwresrWr)r rÚrunAsH      "zFilterPyinotify.runc sDtt|ƒjƒy|jr |jjƒWntk r>|jr:‚YnXdS)N)ÚsuperrÚstoprÚAttributeError)r )Ú __class__rrrq{szFilterPyinotify.stopcs2dd„|_|jƒtt|ƒjƒtjd|jƒdS)NcWsdS)Nrr)Úargsrrrriˆsz&FilterPyinotify.join..z$[%s] filter terminated (pyinotifier))ÚjoinÚ_FilterPyinotify__cleanuprprrrr%)r )rsrrru‡s zFilterPyinotify.joincs0ˆjr,tj‡fdd„ˆjdƒr,dˆ_dˆ_dS)Ncsˆj S)N)rr)r rrri’sz+FilterPyinotify.__cleanup..rj)rr rnrgr)r r)r rZ __cleanup�szFilterPyinotify.__cleanup)r!)F)NF)Ú__name__Ú __module__Ú __qualname__rr=r9r3rCrFrJr+rLrXrKrNrMr\r_rmÚpropertyrlrorqrurvÚ __classcell__rr)rsrrEs* . +      : r)%Ú __author__Z __copyright__Z __license__r?Zdistutils.versionrr0Zos.pathrrr5rZ failmanagerrÚfilterrZmytimerr Zutilsr Zhelpersr Úhasattrr Ú ImportErrorrZmanagerrarWrTrwrrZ _logger_initr$rrrrrÚs4       server/__pycache__/filtersystemd.cpython-36.opt-1.pyc000064400000024045152231502150016546 0ustar003 UB(dÆ9ã@s¸dZdZdZddlZddlZddlmZddlmZee eddƒƒd krPe d ƒ‚d d l m Z d d l mZmZd dlmZd dlmZddlmZmZmZmZeeƒZGdd„deƒZdS)zSteven Hiscocksz"Copyright (c) 2013 Steven HiscocksZGPLéN)Ú LooseVersion)ÚjournalÚ __version__Ú0Z204z Fail2Ban requires systemd >= 204é)ÚFailManagerEmpty)Ú JournalFilterÚFilter)ÚMyTime)ÚUtilsé)Ú getLoggerÚloggingÚ splitwordsÚ uni_decodecs¤eZdZdd„Zedd„ƒZdd„Zdd„Zd d „Zd%d d „Z dd„Z dd„Z dd„Z dd„Z dd„Zdd„Zdd„Zdd„Zd&‡fdd „ Zd!d"„Zd#d$„Z‡ZS)'Ú FilterSystemdcKsLtj|ƒ}tj||f|Žd|_tjf|Ž|_g|_|j dƒt j dƒdS)NrzCreated FilterSystemd) rÚ_getJournalArgsrÚ__init__Ú_FilterSystemd__modifiedrÚReaderÚ_FilterSystemd__journalÚ_FilterSystemd__matchesZsetDatePatternÚlogSysÚdebug)ÚselfÚjailÚkwargsZjrnlargs©rú#/usr/lib/python3.6/filtersystemd.pyr9s  zFilterSystemd.__init__c#CsXdddd„ii}y|jdƒ|d<Wntk r6YnXy|jdƒ|d<Wntk r^Yn\Xd dl}|d}t|tttfƒsˆt|ƒ}g}x|D]}|j|j|ƒƒq’Wtt|ƒƒ|d<yt |jd ƒƒ|d <WnXtk �r(d|ksút |dƒ �r$d|k�s|d �r$t t j d d ƒƒ|d <YnXy|jdƒ|d<Wntk �rRYnX|S)NZ convertersÚ__CURSORcSs|S)Nr)ÚxrrrÚEsz/FilterSystemd._getJournalArgs..Z journalpathÚpathZ journalfilesÚfilesrZ journalflagsÚflagsZF2B_SYSTEMD_DEFAULT_FLAGSéÚ namespace) ÚpopÚKeyErrorÚglobÚ isinstanceÚlistÚsetÚtuplerÚextendÚintÚlenÚosÚgetenv)rÚargsr)Úpr#rrrrCs6 .zFilterSystemd._getJournalArgscCsn|jr|jjƒg}xH|D]@}|jgƒx&|D]}|jj|ƒ|dj|ƒq.W|jjƒqW|jj|ƒdS)Nréÿÿÿÿ)rrZadd_disjunctionÚappendZ add_matchr.)rZmatchesÚ newMatchesÚmatchÚ match_elementrrrÚ_addJournalMatchesns     z FilterSystemd._addJournalMatchesc Cs�gg}x.|D]&}|dkr$|jgƒq |dj|ƒq Wy|j|ƒWn0tk rttjddj|ƒƒ|jƒ‚YnXtjd|jdj|ƒƒdS)Nú+rz"Error adding journal match for: %rú z [%s] Added journal match for: %rr5) r6r:Ú ValueErrorrÚerrorÚjoinÚresetJournalMatchesÚinfoÚjailName)rr8r7r9rrrÚaddJournalMatchs   zFilterSystemd.addJournalMatchc Csj|jjƒtjd|jƒ|jdd…}g|_y|j|ƒWn tk rZtjdƒ‚Yn XtjdƒdS)Nz [%s] Flushed all journal matcheszError restoring journal matcheszJournal matches restored) rZ flush_matchesrrrBrr:r=r>)rZ match_copyrrrr@•s  z!FilterSystemd.resetJournalMatchesNcCsl|dkr |jsdS|jdd…=n(||jkr<|j|jj|ƒ=n td|ƒ‚|jƒtjd|j|rb|ndƒdS)NzMatch %r not foundz"[%s] Removed journal match for: %rÚ*)rÚindexr=r@rrArB)rr8rrrÚdelJournalMatch§s   zFilterSystemd.delJournalMatchcCs|jS)N)r)rrrrÚgetJournalMatch»szFilterSystemd.getJournalMatchcCs|jS)N)r)rrrrÚgetJournalReaderÃszFilterSystemd.getJournalReadercCs<|jdƒ}|dkr|jdƒ}|jƒtj|jƒƒ|jdfS)z1 Returns time of entry as tuple (ISO-str, Posix).Z_SOURCE_REALTIME_TIMESTAMPNZ__REALTIME_TIMESTAMPg€„.A)ÚgetZ isoformatÚtimeZmktimeZ timetupleZ microsecond)rÚlogentryÚdaterrrÚ getJrnEntTimeÆs  zFilterSystemd.getJrnEntTimecsÊ|jƒ‰g}|jdƒ}|r*|jt|ˆƒƒ|jdƒ}|sB|jdƒ}|�r,|jt|ˆƒƒ|jdƒ}|sp|jdƒ}|rÚy d|}WnHtk rÈydt|dƒ}Wn ttfk rÂd|}YnXYnX|d|7<|dd 7<|dd k�r,|jd ƒ}|dk�r|jd ƒd}|jd|jƒƒ|jddƒ}t|t ƒ�rd|jdj ‡fdd„|Dƒƒƒn|jt|ˆƒƒdj |ƒ}|j |ƒ}t j dd|j|d|ƒ|dd…|dd|jddƒf|d fS)NZ _HOSTNAMEZSYSLOG_IDENTIFIERZ_COMMZ SYSLOG_PIDZ_PIDz[%i]rz[%s]rú:zkernel:Z_SOURCE_MONOTONIC_TIMESTAMPZ__MONOTONIC_TIMESTAMPz[%12.6f]ZMESSAGEÚr<c3s|]}t|ˆƒVqdS)N)r)Ú.0Úv)Úencrrú ósz3FilterSystemd.formatJournalEntry..éz&[%s] Read systemd journal entry: %s %sÚ z\nr5r5r5)ZgetLogEncodingrIr6rÚ TypeErrorr/r=Z total_secondsr*r+r?rMrÚlogrBÚreplace)rrKZ logelementsrQZ monotonicÚmsgZloglinerLr)rRrÚformatJournalEntryÓsJ               z FilterSystemd.formatJournalEntrycCs"t|tƒrt|ƒ}|jj|ƒdS)N)r*r/ÚfloatrZ seek_realtime)rrLrrrÚ seekToTimeÿs zFilterSystemd.seekToTimecCsd|_tjd|jƒdS)NTz;[%s] Jail is in operation now (process new journal entries))Ú inOperationrrArB)rrrrÚinOperationModeszFilterSystemd.inOperationModec0s–ˆjƒstjdˆjƒd}y&ˆjjƒˆjjƒ}|r>ˆjjƒWntk rXd}YnX|rÄd}ˆj j dk r„ˆj j j ˆj dƒp‚d}t |t jƒtˆjƒƒƒ}ˆj|ƒdˆ_dt jƒ|jdƒf}n"ˆjƒt jƒ}ˆj|ƒd|f}yˆjjƒWntk �r YnXtj}d}�xTˆj�rl�yè|tjk�ræˆj�rætj‡fdd„ˆjd ƒ}ˆj�ræ|tjk�ræˆj�r�tjtjd ˆjƒtj ˆjd ƒtj‡fd d„ˆjd d ƒˆj�ræyˆjjƒ�r̈jjƒWntk �räYnXˆj!�r tj‡fdd„ˆjdˆjƒ�s ˆjd7_�wdˆ_"�xNˆj�rvd}yˆjjƒ}Wn>tk �r‚}z tj#d|tj$ƒtjkd�WYdd}~XnXˆjd7_|�rZˆj%|ƒ\}}ˆj�s*|t jƒdk�rʈjƒn`|ddk�r|jdƒ|dk�sú||dk�r*dt jƒd|df}n||dk�r*ˆjƒˆj&||ƒˆj"d7_"ˆj"dk�rrtj'}Pnˆj�sjˆjƒtj}P�q*Wdˆ_"ˆjddk�r–ˆj(ƒˆj j �r |�r¼||dfˆj)d<d}ˆj)�r ˆjddk�sît jƒˆj*k�sîˆj �r ˆj+ƒt jƒtj,dˆ_*WnZt-k �rf}z<ˆj�s*Ptj}tj#d|tj$ƒtjkd�ˆj.d|ƒWYdd}~XnX�qWtj/dˆjƒˆj0ƒtj/dˆjƒdS)Nz”[%s] Jail started without 'journalmatch' set. Jail regexs will be checked against all journal entries, which is not advised for performance reasons.rzsystemd-journalFrrcsˆj rtjpˆjjtjƒS)N)ÚactiverÚAPPENDrÚwaitr ÚDEFAULT_SLEEP_INTERVALr)rrrr!Hsz#FilterSystemd.run..gñh㈵øä>z=[%s] Invalidate signaled, take a little break (rotation ends)gÐ?csˆj pˆjjtjƒtjkS)N)r_rrar rbrÚ INVALIDATEr)rrrr!Psécsˆj pˆj S)N)r_Úidler)rrrr!]sé z+Error reading line from systemd journal: %s)Úexc_infor édrTz,Caught unhandled exception in main cycle: %rZ unhandledz[%s] filter terminatedz[%s] filter exited (systemd)T)1rGrZnoticerBrZ seek_tailZ get_previousZget_nextÚOSErrorrÚdatabaseZ getJournalPosÚmaxr rJr/Z getFindTimer\r]rIr^rZNOPr_r Zwait_forZ sleeptimercZticksrWrÚDEBUGZsleeprerr>ÚgetEffectiveLevelrZZprocessLineAndAddr`Z performSvcÚ_pendDBUpdatesZ _nextUpdateTMÚ_updateDBPendingZDEFAULT_SLEEP_TIMEÚ ExceptionZ commonErrorrÚ closeJournal)rrKZ startTimeZwcodeÚlineÚeZtmr)rrÚrunsÐ          & "    $zFilterSystemd.runcCs`y|jd}|_|r|jƒWn<tk rZ}z tjd|tjƒtjkd�WYdd}~XnXdS)NzClose journal failed: %r)rg)rÚcloserprr>rmrrl)rZjnlrsrrrrq¤s zFilterSystemd.closeJournalÚbasiccs8tt|ƒj|d�}|jddjdd„|jDƒƒgfƒ|S)N)ÚflavorzJournal matchesz + css|]}dj|ƒVqdS)r<N)r?)rPr8rrrrS±sz'FilterSystemd.status..)ÚsuperrÚstatusr6r?r)rrwÚret)Ú __class__rrry®szFilterSystemd.statusc CsP|jj}xBy|jjƒ\}}Wntk r2PYnX|j|j|f|žŽq WdS)z7Apply pending updates (jornal position) to database. N)rrjrnÚpopitemr(Z updateJournal)rZdbrWr3rrrro´szFilterSystemd._updateDBPendingcCs"|jƒ|jr|jjr|jƒdS)z8Stop monitoring of journal. Invoked after run method. N)rqrnrrjro)rrrrÚonStop¿szFilterSystemd.onStop)N)rv)Ú__name__Ú __module__Ú __qualname__rÚ staticmethodrr:rCr@rFrGrHrMrZr\r^rtrqryror}Ú __classcell__rr)r{rr2s$ +  ,   r)Ú __author__Z __copyright__Z __license__r1rJZdistutils.versionrZsystemdrÚgetattrÚ ImportErrorZ failmanagerrÚfilterrr Zmytimer Zutilsr Zhelpersr rrrr~rrrrrrÚs      server/__pycache__/filtersystemd.cpython-36.pyc000064400000024045152231502150015607 0ustar003 UB(dÆ9ã@s¸dZdZdZddlZddlZddlmZddlmZee eddƒƒd krPe d ƒ‚d d l m Z d d l mZmZd dlmZd dlmZddlmZmZmZmZeeƒZGdd„deƒZdS)zSteven Hiscocksz"Copyright (c) 2013 Steven HiscocksZGPLéN)Ú LooseVersion)ÚjournalÚ __version__Ú0Z204z Fail2Ban requires systemd >= 204é)ÚFailManagerEmpty)Ú JournalFilterÚFilter)ÚMyTime)ÚUtilsé)Ú getLoggerÚloggingÚ splitwordsÚ uni_decodecs¤eZdZdd„Zedd„ƒZdd„Zdd„Zd d „Zd%d d „Z dd„Z dd„Z dd„Z dd„Z dd„Zdd„Zdd„Zdd„Zd&‡fdd „ Zd!d"„Zd#d$„Z‡ZS)'Ú FilterSystemdcKsLtj|ƒ}tj||f|Žd|_tjf|Ž|_g|_|j dƒt j dƒdS)NrzCreated FilterSystemd) rÚ_getJournalArgsrÚ__init__Ú_FilterSystemd__modifiedrÚReaderÚ_FilterSystemd__journalÚ_FilterSystemd__matchesZsetDatePatternÚlogSysÚdebug)ÚselfÚjailÚkwargsZjrnlargs©rú#/usr/lib/python3.6/filtersystemd.pyr9s  zFilterSystemd.__init__c#CsXdddd„ii}y|jdƒ|d<Wntk r6YnXy|jdƒ|d<Wntk r^Yn\Xd dl}|d}t|tttfƒsˆt|ƒ}g}x|D]}|j|j|ƒƒq’Wtt|ƒƒ|d<yt |jd ƒƒ|d <WnXtk �r(d|ksút |dƒ �r$d|k�s|d �r$t t j d d ƒƒ|d <YnXy|jdƒ|d<Wntk �rRYnX|S)NZ convertersÚ__CURSORcSs|S)Nr)ÚxrrrÚEsz/FilterSystemd._getJournalArgs..Z journalpathÚpathZ journalfilesÚfilesrZ journalflagsÚflagsZF2B_SYSTEMD_DEFAULT_FLAGSéÚ namespace) ÚpopÚKeyErrorÚglobÚ isinstanceÚlistÚsetÚtuplerÚextendÚintÚlenÚosÚgetenv)rÚargsr)Úpr#rrrrCs6 .zFilterSystemd._getJournalArgscCsn|jr|jjƒg}xH|D]@}|jgƒx&|D]}|jj|ƒ|dj|ƒq.W|jjƒqW|jj|ƒdS)Nréÿÿÿÿ)rrZadd_disjunctionÚappendZ add_matchr.)rZmatchesÚ newMatchesÚmatchÚ match_elementrrrÚ_addJournalMatchesns     z FilterSystemd._addJournalMatchesc Cs�gg}x.|D]&}|dkr$|jgƒq |dj|ƒq Wy|j|ƒWn0tk rttjddj|ƒƒ|jƒ‚YnXtjd|jdj|ƒƒdS)Nú+rz"Error adding journal match for: %rú z [%s] Added journal match for: %rr5) r6r:Ú ValueErrorrÚerrorÚjoinÚresetJournalMatchesÚinfoÚjailName)rr8r7r9rrrÚaddJournalMatchs   zFilterSystemd.addJournalMatchc Csj|jjƒtjd|jƒ|jdd…}g|_y|j|ƒWn tk rZtjdƒ‚Yn XtjdƒdS)Nz [%s] Flushed all journal matcheszError restoring journal matcheszJournal matches restored) rZ flush_matchesrrrBrr:r=r>)rZ match_copyrrrr@•s  z!FilterSystemd.resetJournalMatchesNcCsl|dkr |jsdS|jdd…=n(||jkr<|j|jj|ƒ=n td|ƒ‚|jƒtjd|j|rb|ndƒdS)NzMatch %r not foundz"[%s] Removed journal match for: %rÚ*)rÚindexr=r@rrArB)rr8rrrÚdelJournalMatch§s   zFilterSystemd.delJournalMatchcCs|jS)N)r)rrrrÚgetJournalMatch»szFilterSystemd.getJournalMatchcCs|jS)N)r)rrrrÚgetJournalReaderÃszFilterSystemd.getJournalReadercCs<|jdƒ}|dkr|jdƒ}|jƒtj|jƒƒ|jdfS)z1 Returns time of entry as tuple (ISO-str, Posix).Z_SOURCE_REALTIME_TIMESTAMPNZ__REALTIME_TIMESTAMPg€„.A)ÚgetZ isoformatÚtimeZmktimeZ timetupleZ microsecond)rÚlogentryÚdaterrrÚ getJrnEntTimeÆs  zFilterSystemd.getJrnEntTimecsÊ|jƒ‰g}|jdƒ}|r*|jt|ˆƒƒ|jdƒ}|sB|jdƒ}|�r,|jt|ˆƒƒ|jdƒ}|sp|jdƒ}|rÚy d|}WnHtk rÈydt|dƒ}Wn ttfk rÂd|}YnXYnX|d|7<|dd 7<|dd k�r,|jd ƒ}|dk�r|jd ƒd}|jd|jƒƒ|jddƒ}t|t ƒ�rd|jdj ‡fdd„|Dƒƒƒn|jt|ˆƒƒdj |ƒ}|j |ƒ}t j dd|j|d|ƒ|dd…|dd|jddƒf|d fS)NZ _HOSTNAMEZSYSLOG_IDENTIFIERZ_COMMZ SYSLOG_PIDZ_PIDz[%i]rz[%s]rú:zkernel:Z_SOURCE_MONOTONIC_TIMESTAMPZ__MONOTONIC_TIMESTAMPz[%12.6f]ZMESSAGEÚr<c3s|]}t|ˆƒVqdS)N)r)Ú.0Úv)Úencrrú ósz3FilterSystemd.formatJournalEntry..éz&[%s] Read systemd journal entry: %s %sÚ z\nr5r5r5)ZgetLogEncodingrIr6rÚ TypeErrorr/r=Z total_secondsr*r+r?rMrÚlogrBÚreplace)rrKZ logelementsrQZ monotonicÚmsgZloglinerLr)rRrÚformatJournalEntryÓsJ               z FilterSystemd.formatJournalEntrycCs"t|tƒrt|ƒ}|jj|ƒdS)N)r*r/ÚfloatrZ seek_realtime)rrLrrrÚ seekToTimeÿs zFilterSystemd.seekToTimecCsd|_tjd|jƒdS)NTz;[%s] Jail is in operation now (process new journal entries))Ú inOperationrrArB)rrrrÚinOperationModeszFilterSystemd.inOperationModec0s–ˆjƒstjdˆjƒd}y&ˆjjƒˆjjƒ}|r>ˆjjƒWntk rXd}YnX|rÄd}ˆj j dk r„ˆj j j ˆj dƒp‚d}t |t jƒtˆjƒƒƒ}ˆj|ƒdˆ_dt jƒ|jdƒf}n"ˆjƒt jƒ}ˆj|ƒd|f}yˆjjƒWntk �r YnXtj}d}�xTˆj�rl�yè|tjk�ræˆj�rætj‡fdd„ˆjd ƒ}ˆj�ræ|tjk�ræˆj�r�tjtjd ˆjƒtj ˆjd ƒtj‡fd d„ˆjd d ƒˆj�ræyˆjjƒ�r̈jjƒWntk �räYnXˆj!�r tj‡fdd„ˆjdˆjƒ�s ˆjd7_�wdˆ_"�xNˆj�rvd}yˆjjƒ}Wn>tk �r‚}z tj#d|tj$ƒtjkd�WYdd}~XnXˆjd7_|�rZˆj%|ƒ\}}ˆj�s*|t jƒdk�rʈjƒn`|ddk�r|jdƒ|dk�sú||dk�r*dt jƒd|df}n||dk�r*ˆjƒˆj&||ƒˆj"d7_"ˆj"dk�rrtj'}Pnˆj�sjˆjƒtj}P�q*Wdˆ_"ˆjddk�r–ˆj(ƒˆj j �r |�r¼||dfˆj)d<d}ˆj)�r ˆjddk�sît jƒˆj*k�sîˆj �r ˆj+ƒt jƒtj,dˆ_*WnZt-k �rf}z<ˆj�s*Ptj}tj#d|tj$ƒtjkd�ˆj.d|ƒWYdd}~XnX�qWtj/dˆjƒˆj0ƒtj/dˆjƒdS)Nz”[%s] Jail started without 'journalmatch' set. Jail regexs will be checked against all journal entries, which is not advised for performance reasons.rzsystemd-journalFrrcsˆj rtjpˆjjtjƒS)N)ÚactiverÚAPPENDrÚwaitr ÚDEFAULT_SLEEP_INTERVALr)rrrr!Hsz#FilterSystemd.run..gñh㈵øä>z=[%s] Invalidate signaled, take a little break (rotation ends)gÐ?csˆj pˆjjtjƒtjkS)N)r_rrar rbrÚ INVALIDATEr)rrrr!Psécsˆj pˆj S)N)r_Úidler)rrrr!]sé z+Error reading line from systemd journal: %s)Úexc_infor édrTz,Caught unhandled exception in main cycle: %rZ unhandledz[%s] filter terminatedz[%s] filter exited (systemd)T)1rGrZnoticerBrZ seek_tailZ get_previousZget_nextÚOSErrorrÚdatabaseZ getJournalPosÚmaxr rJr/Z getFindTimer\r]rIr^rZNOPr_r Zwait_forZ sleeptimercZticksrWrÚDEBUGZsleeprerr>ÚgetEffectiveLevelrZZprocessLineAndAddr`Z performSvcÚ_pendDBUpdatesZ _nextUpdateTMÚ_updateDBPendingZDEFAULT_SLEEP_TIMEÚ ExceptionZ commonErrorrÚ closeJournal)rrKZ startTimeZwcodeÚlineÚeZtmr)rrÚrunsÐ          & "    $zFilterSystemd.runcCs`y|jd}|_|r|jƒWn<tk rZ}z tjd|tjƒtjkd�WYdd}~XnXdS)NzClose journal failed: %r)rg)rÚcloserprr>rmrrl)rZjnlrsrrrrq¤s zFilterSystemd.closeJournalÚbasiccs8tt|ƒj|d�}|jddjdd„|jDƒƒgfƒ|S)N)ÚflavorzJournal matchesz + css|]}dj|ƒVqdS)r<N)r?)rPr8rrrrS±sz'FilterSystemd.status..)ÚsuperrÚstatusr6r?r)rrwÚret)Ú __class__rrry®szFilterSystemd.statusc CsP|jj}xBy|jjƒ\}}Wntk r2PYnX|j|j|f|žŽq WdS)z7Apply pending updates (jornal position) to database. N)rrjrnÚpopitemr(Z updateJournal)rZdbrWr3rrrro´szFilterSystemd._updateDBPendingcCs"|jƒ|jr|jjr|jƒdS)z8Stop monitoring of journal. Invoked after run method. N)rqrnrrjro)rrrrÚonStop¿szFilterSystemd.onStop)N)rv)Ú__name__Ú __module__Ú __qualname__rÚ staticmethodrr:rCr@rFrGrHrMrZr\r^rtrqryror}Ú __classcell__rr)r{rr2s$ +  ,   r)Ú __author__Z __copyright__Z __license__r1rJZdistutils.versionrZsystemdrÚgetattrÚ ImportErrorZ failmanagerrÚfilterrr Zmytimer Zutilsr Zhelpersr rrrr~rrrrrrÚs      server/__pycache__/ipdns.cpython-36.opt-1.pyc000064400000033701152231502150014764 0ustar003 UB(dŒCã@s„dZdZdZddlZddlZddlZddlmZddlm Z e e ƒZ d d „Z dd d „Z Gdd„dƒZGdd„deƒZeddƒe_dS)zJFail2Ban Developers, Alexander Koeppe, Serg G. Brester, Yaroslav Halchenkoz+Copyright (c) 2004-2016 Fail2ban DevelopersZGPLéNé)ÚUtilsé)Ú getLoggercCst|tƒr|St|ƒS)z8A little helper to guarantee ip being an IPAddr instance)Ú isinstanceÚIPAddr)Úip©r ú/usr/lib/python3.6/ipdns.pyÚasip's r ÚcCsŒyj|p tjƒ}dd„tj|ddtjdtjƒDƒ}|rh|d}d}x"|D]}|j|ƒrX|S|sF|}qFW|SWntjk r€YnXtj|ƒS)z¡Get fully-qualified hostname of given host, thereby resolve of an external IPs and name will be preferred before the local domain (or a loopback), see gh-2438 css|]}|dr|dVqdS)éNr )Ú.0Úair r r ú 4szgetfqdn..NrÚ.)ÚsocketÚ gethostnameÚ getaddrinfoZ SOCK_DGRAMZ AI_CANONNAMEÚ startswithÚerrorÚgetfqdn)ÚnameÚnamesZprefÚfirstrr r r r-s     rc@sšeZdZejddd�Zejddd�Zedd„ƒZedd„ƒZ ed d „ƒZ edd d „ƒZ dZ edd„ƒZ dZedd„ƒZdZedd„ƒZd Zedd„ƒZdS)!ÚDNSUtilsièéé<)ÚmaxCountÚmaxTimecCsþtjj|ƒ}|dk r|Stƒ}d}x°tjtjftjtj ffD]”\}}ydx^tj |d|dtj ƒD]F}t |ƒdks\t |dƒ r|q\tt |ddƒ|ƒ}|jr\|j|ƒq\WWq<tk rÎ}z |}WYdd}~Xqr/cCsNtjjtjƒ}|dkr:tdtjdƒtjdƒgƒtdgƒ}tjjtj|ƒ|S)zGet own host names of selfNZ localhostFTr )rr4r"Ú_getSelfNames_keyr#rA)rr r r Ú getSelfNames¯s zDNSUtils.getSelfNamesr0cCsŒtjjtjƒ}|dkrxtƒ}xZtjƒD]N}y|ttj|dƒƒO}Wq&tk rr}ztj d||ƒWYdd}~Xq&Xq&Wtjjtj|ƒ|S)zGet own IP addresses of selfNr8z#Retrieving own IPs of %s failed: %s) rr!r"Ú_getSelfIPs_keyr#rCr=r,r-r.)r0r?r2r r r Ú getSelfIPsÀs$zDNSUtils.getSelfIPsNcCs*|t_tjd|dk r |rdq"dndƒ|S)Nz IPv6 is %sZonZoffÚauto)rÚ_IPv6IsAllowedr-r5)Úvaluer r r ÚsetIPv6IsAllowedÓs zDNSUtils.setIPv6IsAllowedú ipv6-allowedcCsTtjdk rtjStjjtjƒ}|dk r*|Stdd„tjƒDƒƒ}tjjtj|ƒ|S)Ncss|]}d|jkVqdS)ú:N)Úntoa)rrr r r rãsz)DNSUtils.IPv6IsAllowed..)rrGr!r"Ú_IPv6IsAllowed_keyÚanyrEr#)r6r r r Ú IPv6IsAllowedÜs zDNSUtils.IPv6IsAllowedi,i,)T)r>r/)r>r0)r>rJ)Ú__name__Ú __module__Ú __qualname__rÚCacher!r4Ú staticmethodr3r7r=rArBrCrDrErGrIrMrOr r r r rMs       rcs¸eZdZdZdZdZejdeefƒZdZ dQZ e j d dRd�Z dSZdTZeejZeejZef‡fdd„ Zedd„ƒZefdd„Zdd„Zdd„Zdd„Zedd„ƒZedd „ƒZejd!ejd"iZed#d$„ƒZ ed%d&„ƒZ!ed'd(„ƒZ"ed)d*„ƒZ#ed+d,„ƒZ$d-d.„Z%d/d0„Z&d1d2„Z'd3d4„Z(d5d6„Z)d7d8„Z*ed9d:„ƒZ+ed;d<„ƒZ,dUd=d>„Z-d?d@„Z.edAdB„ƒZ/edCdD„ƒZ0dEdF„Z1dGdH„Z2dIdJ„Z3e3ƒZ4edKdL„ƒZ5edMdN„ƒZ6edOdP„ƒZ7‡Z8S)Vrz7Encapsulate functionality for IPv4 and IPv6 addresses z(?:\d{1,3}\.){3}\d{1,3}z;(?:[0-9a-fA-F]{1,4}::?|::){1,7}(?:[0-9a-fA-F]{1,4}|(?<=:):)z%^(?:(?P%s)|\[?(?P%s)\]?)$NÚ_familyÚ_addrÚ_plenÚ _maskplenÚ_rawi'rr)rrrrcsä|tjkrt|ttfƒrtj}|tjkrHtt|ƒj|ƒ}|j||ƒ|S||f}tj j |ƒ}|dk rh|S|tjkrªtj |ƒ\}}||f}|tjkrªtj j |ƒ}|dk rª|Stt|ƒj|ƒ}|j||ƒ|j tjkràtj j ||ƒ|S)N)rÚ CIDR_UNSPECrÚtupleÚlistÚCIDR_RAWÚsuperÚ__new__Ú _IPAddr__initÚ CACHE_OBJr"Ú_IPAddr__wrap_ipstrrUr#)ÚclsÚipstrÚcidrrÚargs)Ú __class__r r r_s,        zIPAddr.__new__cCs¦t|ƒdkr0|ddkr0|d dkr0|dd …}d|krB|tjfS|jddƒ}t|ƒdkrhtd|fƒ‚d|dks€d |dkr’tj|dƒ|d<t|dƒ|d<|S) Nrrú[rú]ú/z.invalid ipstr %r, too many plen representationrrKéÿÿÿÿrk)r(rrZÚsplitÚ ValueErrorÚ masktoplenÚint)rdÚsr r r Z __wrap_ipstrs$    zIPAddr.__wrap_ipstrc Cs„tj|_d|_d|_d|_||_|tjk�rx|dk rL|tjkrLtj|g}n tj tj g}x>|D]6}ytj ||ƒ}||_PWq^tj k r’w^Yq^Xq^W|jtj krêt jd|ƒ\|_d|_|dk rè|dkrèd|?}|j|M_||_nŒ|jtj k�r€t jd|ƒ\}}|d>|B|_d|_|dk �rP|dk�rPd |?}|j|M_||_n&|jtjƒ�r€|d@|_tj |_d|_ntj|_dS) zP initialize IP object by converting IP address string to binary to integer rNz!Lé lÿÿz!QQé@é€l ÿÿÿÿÿÿÿÿÿ)rÚ AF_UNSPECrUrVrWrXrYrr]r$r&Z inet_ptonrÚstructÚunpackÚisInNetÚ IP6_4COMPAT)r>rdreÚfamilyÚbinaryÚmaskÚhiÚlor r r Z__init0sH         z IPAddr.__initcCs t|jƒS)N)ÚreprrL)r>r r r Ú__repr__gszIPAddr.__repr__cCst|jtƒr|jSt|jƒS)N)rrLr))r>r r r Ú__str__jszIPAddr.__str__cCs t|jffS)z§IPAddr pickle-handler, that simply wraps IPAddr to the str Returns a string as instance to be pickled, because fail2ban-client can't unserialize IPAddr objects )r)rL)r>r r r Ú __reduce__mszIPAddr.__reduce__cCs|jS)N)rV)r>r r r Úaddrusz IPAddr.addrcCs|jS)N)rU)r>r r r ryysz IPAddr.familyZinet4Zinet6cCstjj|jƒS)N)rÚFAM2STRr"rU)r>r r r Ú familyStr~szIPAddr.familyStrcCs|jS)N)rW)r>r r r Úplen‚sz IPAddr.plencCs|jS)zlThe raw address Should only be set to a non-empty string if prior address conversion wasn't possible )rY)r>r r r Úraw†sz IPAddr.rawcCs |jtjkS)z6Either the object corresponds to a valid IP address )rUrrt)r>r r r r*�szIPAddr.isValidcCs |jtjdtjdij|jdƒkS)zIReturns whether the object is a single IP address (not DNS and subnet) rqrsièiüÿÿ)rWrr$r&r"rU)r>r r r ÚisSingle•szIPAddr.isSinglecCs€|jtjkr"t|tƒ r"|j|kSt|tƒs@|dkr8dSt|ƒ}|j|jkrPdS|jtjkrh|j|jkS|j|jko~|j|jkS)NF) rUrr]rrYrrtrVrW)r>Úotherr r r Ú__eq__›s      z IPAddr.__eq__cCs ||k S)Nr )r>rˆr r r Ú__ne__©sz IPAddr.__ne__cCsX|jtjkr"t|tƒ r"|j|kSt|tƒs@|dkr8dSt|ƒ}|j|jkpV|j|jkS)NF)rUrr]rrYrV)r>rˆr r r Ú__lt__¬s  z IPAddr.__lt__cCst|tƒst|ƒ}d||fS)Nz%s%s)rr)r>rˆr r r Ú__add__´s zIPAddr.__add__cCst|tƒst|ƒ}d||fS)Nz%s%s)rr)r>rˆr r r Ú__radd__¹s zIPAddr.__radd__cCs t|jƒS)N)ÚhashrL)r>r r r Ú__hash__¾szIPAddr.__hash__cCs4|jtjkrd|jS|jtjkr,d|jSdSdS)zr r r ÚhexdumpÃs     zIPAddr.hexdumpcCs�d}|jr4tjd|jƒ}|jr~|jdkr~d|j}nJ|jrx|jd?}|jd@}tjd||ƒ}|jr~|jdkr~d|j}n|jStj|j |ƒ|S) zi represent IP object as text like the deprecated C pendant inet.ntoa but address family independent r z!Lrqz/%drrlÿÿÿÿz!QQrs) ÚisIPv4ruÚpackrVrWÚisIPv6rYrZ inet_ntoprU)r>r+rzr|r}r r r rLÏs    z IPAddr.ntoacCsT|jr |jjdƒ}|dkr>d}n|jr:|j}|dkr>d}ndSddjt|ƒƒ|fS)a return the DNS PTR string of the provided IP address object If "suffix" is provided it will be appended as the second and top level reverse domain. If omitted it is implicitly set to the second and top level reverse domain of the according IP address family rNz in-addr.arpa.z ip6.arpa.r z%s.%s)r‘rLrlr“r�ÚjoinÚreversed)r>ÚsuffixZ exploded_ipr r r ÚgetPTRæs z IPAddr.getPTRcCs tj|jƒS)z?Return the host name (DNS) of the provided IP address object )rr7rL)r>r r r ÚgetHostûszIPAddr.getHostcCs |jtjkS)z4Either the IP object is of address family AF_INET )ryrr$)r>r r r r‘sz IPAddr.isIPv4cCs |jtjkS)z5Either the IP object is of address family AF_INET6 )ryrr&)r>r r r r“sz IPAddr.isIPv6cCsn|j r"|jdkr"|tj|jƒkS|j|jkr2dS|jrFd|j?}n|jrZd|j?}ndS|j|@|jkS)z9Return either the IP object is in the provided network r Flÿÿl ÿÿÿÿÿÿÿÿÿ) r*r†rr3ryr‘r…r“r‚)r>Znetr{r r r rw s zIPAddr.isInNetcCst|tƒo||kp|j|ƒS)z;Return whether the object (as network) contains given IP )rrrw)r>rr r r ÚcontainsszIPAddr.containscCspdd>d}d}|d|dddi}d}xFtddƒD]8}|d|>O}|dkrXd||||A<d||||A<q0W|S) Nrrsrqrllÿÿéé)Úrange)Zm6Zm4ZmmapÚmÚir r r Z __getMaskMap%s  zIPAddr.__getMaskMapcCsFd}|jdk r|jStjj|jƒ}|dkrZmplenr r r Úmaskplen3s zIPAddr.maskplencCs t|ƒjS)zIConvert mask string to prefix length To be used only for IPv4 masks )rr )r{r r r rn>szIPAddr.masktoplencCs4tjj|ƒ}|sdS|jdƒ}|dkr*|S|jdƒS)zBSearch if text is an IP address, and return it if so, else None NZIPv4r ZIPv6)rÚ IP_4_6_CREÚmatchÚgroup)r<r¢rdr r r r:Fs  zIPAddr.searchIP)rUrVrWrXrYi,éþÿÿÿrk)N)9rPrQrRÚ__doc__ZIP_4_REZIP_6_REÚreÚcompiler¡rxÚ __slots__rrSrar]rZrr$r%r&r'r_rTrbr`rr€r�Úpropertyr‚ryrƒr„r…r†r*r‡r‰rŠr‹rŒr�r�r�rLr—r˜r‘r“rwr™Z_IPAddr__getMaskMaprŸr rnr:Ú __classcell__r r )rgr rísZ    7            rz ::ffff:0:0é`)r )Ú __author__Z __copyright__Z __license__rrur¦ZutilsrZhelpersrrPr-r rrÚobjectrrxr r r r Ús    !iserver/__pycache__/ipdns.cpython-36.pyc000064400000033701152231502150014025 0ustar003 UB(dŒCã@s„dZdZdZddlZddlZddlZddlmZddlm Z e e ƒZ d d „Z dd d „Z Gdd„dƒZGdd„deƒZeddƒe_dS)zJFail2Ban Developers, Alexander Koeppe, Serg G. Brester, Yaroslav Halchenkoz+Copyright (c) 2004-2016 Fail2ban DevelopersZGPLéNé)ÚUtilsé)Ú getLoggercCst|tƒr|St|ƒS)z8A little helper to guarantee ip being an IPAddr instance)Ú isinstanceÚIPAddr)Úip©r ú/usr/lib/python3.6/ipdns.pyÚasip's r ÚcCsŒyj|p tjƒ}dd„tj|ddtjdtjƒDƒ}|rh|d}d}x"|D]}|j|ƒrX|S|sF|}qFW|SWntjk r€YnXtj|ƒS)z¡Get fully-qualified hostname of given host, thereby resolve of an external IPs and name will be preferred before the local domain (or a loopback), see gh-2438 css|]}|dr|dVqdS)éNr )Ú.0Úair r r ú 4szgetfqdn..NrÚ.)ÚsocketÚ gethostnameÚ getaddrinfoZ SOCK_DGRAMZ AI_CANONNAMEÚ startswithÚerrorÚgetfqdn)ÚnameÚnamesZprefÚfirstrr r r r-s     rc@sšeZdZejddd�Zejddd�Zedd„ƒZedd„ƒZ ed d „ƒZ edd d „ƒZ dZ edd„ƒZ dZedd„ƒZdZedd„ƒZd Zedd„ƒZdS)!ÚDNSUtilsièéé<)ÚmaxCountÚmaxTimecCsþtjj|ƒ}|dk r|Stƒ}d}x°tjtjftjtj ffD]”\}}ydx^tj |d|dtj ƒD]F}t |ƒdks\t |dƒ r|q\tt |ddƒ|ƒ}|jr\|j|ƒq\WWq<tk rÎ}z |}WYdd}~Xqr/cCsNtjjtjƒ}|dkr:tdtjdƒtjdƒgƒtdgƒ}tjjtj|ƒ|S)zGet own host names of selfNZ localhostFTr )rr4r"Ú_getSelfNames_keyr#rA)rr r r Ú getSelfNames¯s zDNSUtils.getSelfNamesr0cCsŒtjjtjƒ}|dkrxtƒ}xZtjƒD]N}y|ttj|dƒƒO}Wq&tk rr}ztj d||ƒWYdd}~Xq&Xq&Wtjjtj|ƒ|S)zGet own IP addresses of selfNr8z#Retrieving own IPs of %s failed: %s) rr!r"Ú_getSelfIPs_keyr#rCr=r,r-r.)r0r?r2r r r Ú getSelfIPsÀs$zDNSUtils.getSelfIPsNcCs*|t_tjd|dk r |rdq"dndƒ|S)Nz IPv6 is %sZonZoffÚauto)rÚ_IPv6IsAllowedr-r5)Úvaluer r r ÚsetIPv6IsAllowedÓs zDNSUtils.setIPv6IsAllowedú ipv6-allowedcCsTtjdk rtjStjjtjƒ}|dk r*|Stdd„tjƒDƒƒ}tjjtj|ƒ|S)Ncss|]}d|jkVqdS)ú:N)Úntoa)rrr r r rãsz)DNSUtils.IPv6IsAllowed..)rrGr!r"Ú_IPv6IsAllowed_keyÚanyrEr#)r6r r r Ú IPv6IsAllowedÜs zDNSUtils.IPv6IsAllowedi,i,)T)r>r/)r>r0)r>rJ)Ú__name__Ú __module__Ú __qualname__rÚCacher!r4Ú staticmethodr3r7r=rArBrCrDrErGrIrMrOr r r r rMs       rcs¸eZdZdZdZdZejdeefƒZdZ dQZ e j d dRd�Z dSZdTZeejZeejZef‡fdd„ Zedd„ƒZefdd„Zdd„Zdd„Zdd„Zedd„ƒZedd „ƒZejd!ejd"iZed#d$„ƒZ ed%d&„ƒZ!ed'd(„ƒZ"ed)d*„ƒZ#ed+d,„ƒZ$d-d.„Z%d/d0„Z&d1d2„Z'd3d4„Z(d5d6„Z)d7d8„Z*ed9d:„ƒZ+ed;d<„ƒZ,dUd=d>„Z-d?d@„Z.edAdB„ƒZ/edCdD„ƒZ0dEdF„Z1dGdH„Z2dIdJ„Z3e3ƒZ4edKdL„ƒZ5edMdN„ƒZ6edOdP„ƒZ7‡Z8S)Vrz7Encapsulate functionality for IPv4 and IPv6 addresses z(?:\d{1,3}\.){3}\d{1,3}z;(?:[0-9a-fA-F]{1,4}::?|::){1,7}(?:[0-9a-fA-F]{1,4}|(?<=:):)z%^(?:(?P%s)|\[?(?P%s)\]?)$NÚ_familyÚ_addrÚ_plenÚ _maskplenÚ_rawi'rr)rrrrcsä|tjkrt|ttfƒrtj}|tjkrHtt|ƒj|ƒ}|j||ƒ|S||f}tj j |ƒ}|dk rh|S|tjkrªtj |ƒ\}}||f}|tjkrªtj j |ƒ}|dk rª|Stt|ƒj|ƒ}|j||ƒ|j tjkràtj j ||ƒ|S)N)rÚ CIDR_UNSPECrÚtupleÚlistÚCIDR_RAWÚsuperÚ__new__Ú _IPAddr__initÚ CACHE_OBJr"Ú_IPAddr__wrap_ipstrrUr#)ÚclsÚipstrÚcidrrÚargs)Ú __class__r r r_s,        zIPAddr.__new__cCs¦t|ƒdkr0|ddkr0|d dkr0|dd …}d|krB|tjfS|jddƒ}t|ƒdkrhtd|fƒ‚d|dks€d |dkr’tj|dƒ|d<t|dƒ|d<|S) Nrrú[rú]ú/z.invalid ipstr %r, too many plen representationrrKéÿÿÿÿrk)r(rrZÚsplitÚ ValueErrorÚ masktoplenÚint)rdÚsr r r Z __wrap_ipstrs$    zIPAddr.__wrap_ipstrc Cs„tj|_d|_d|_d|_||_|tjk�rx|dk rL|tjkrLtj|g}n tj tj g}x>|D]6}ytj ||ƒ}||_PWq^tj k r’w^Yq^Xq^W|jtj krêt jd|ƒ\|_d|_|dk rè|dkrèd|?}|j|M_||_nŒ|jtj k�r€t jd|ƒ\}}|d>|B|_d|_|dk �rP|dk�rPd |?}|j|M_||_n&|jtjƒ�r€|d@|_tj |_d|_ntj|_dS) zP initialize IP object by converting IP address string to binary to integer rNz!Lé lÿÿz!QQé@é€l ÿÿÿÿÿÿÿÿÿ)rÚ AF_UNSPECrUrVrWrXrYrr]r$r&Z inet_ptonrÚstructÚunpackÚisInNetÚ IP6_4COMPAT)r>rdreÚfamilyÚbinaryÚmaskÚhiÚlor r r Z__init0sH         z IPAddr.__initcCs t|jƒS)N)ÚreprrL)r>r r r Ú__repr__gszIPAddr.__repr__cCst|jtƒr|jSt|jƒS)N)rrLr))r>r r r Ú__str__jszIPAddr.__str__cCs t|jffS)z§IPAddr pickle-handler, that simply wraps IPAddr to the str Returns a string as instance to be pickled, because fail2ban-client can't unserialize IPAddr objects )r)rL)r>r r r Ú __reduce__mszIPAddr.__reduce__cCs|jS)N)rV)r>r r r Úaddrusz IPAddr.addrcCs|jS)N)rU)r>r r r ryysz IPAddr.familyZinet4Zinet6cCstjj|jƒS)N)rÚFAM2STRr"rU)r>r r r Ú familyStr~szIPAddr.familyStrcCs|jS)N)rW)r>r r r Úplen‚sz IPAddr.plencCs|jS)zlThe raw address Should only be set to a non-empty string if prior address conversion wasn't possible )rY)r>r r r Úraw†sz IPAddr.rawcCs |jtjkS)z6Either the object corresponds to a valid IP address )rUrrt)r>r r r r*�szIPAddr.isValidcCs |jtjdtjdij|jdƒkS)zIReturns whether the object is a single IP address (not DNS and subnet) rqrsièiüÿÿ)rWrr$r&r"rU)r>r r r ÚisSingle•szIPAddr.isSinglecCs€|jtjkr"t|tƒ r"|j|kSt|tƒs@|dkr8dSt|ƒ}|j|jkrPdS|jtjkrh|j|jkS|j|jko~|j|jkS)NF) rUrr]rrYrrtrVrW)r>Úotherr r r Ú__eq__›s      z IPAddr.__eq__cCs ||k S)Nr )r>rˆr r r Ú__ne__©sz IPAddr.__ne__cCsX|jtjkr"t|tƒ r"|j|kSt|tƒs@|dkr8dSt|ƒ}|j|jkpV|j|jkS)NF)rUrr]rrYrV)r>rˆr r r Ú__lt__¬s  z IPAddr.__lt__cCst|tƒst|ƒ}d||fS)Nz%s%s)rr)r>rˆr r r Ú__add__´s zIPAddr.__add__cCst|tƒst|ƒ}d||fS)Nz%s%s)rr)r>rˆr r r Ú__radd__¹s zIPAddr.__radd__cCs t|jƒS)N)ÚhashrL)r>r r r Ú__hash__¾szIPAddr.__hash__cCs4|jtjkrd|jS|jtjkr,d|jSdSdS)zr r r ÚhexdumpÃs     zIPAddr.hexdumpcCs�d}|jr4tjd|jƒ}|jr~|jdkr~d|j}nJ|jrx|jd?}|jd@}tjd||ƒ}|jr~|jdkr~d|j}n|jStj|j |ƒ|S) zi represent IP object as text like the deprecated C pendant inet.ntoa but address family independent r z!Lrqz/%drrlÿÿÿÿz!QQrs) ÚisIPv4ruÚpackrVrWÚisIPv6rYrZ inet_ntoprU)r>r+rzr|r}r r r rLÏs    z IPAddr.ntoacCsT|jr |jjdƒ}|dkr>d}n|jr:|j}|dkr>d}ndSddjt|ƒƒ|fS)a return the DNS PTR string of the provided IP address object If "suffix" is provided it will be appended as the second and top level reverse domain. If omitted it is implicitly set to the second and top level reverse domain of the according IP address family rNz in-addr.arpa.z ip6.arpa.r z%s.%s)r‘rLrlr“r�ÚjoinÚreversed)r>ÚsuffixZ exploded_ipr r r ÚgetPTRæs z IPAddr.getPTRcCs tj|jƒS)z?Return the host name (DNS) of the provided IP address object )rr7rL)r>r r r ÚgetHostûszIPAddr.getHostcCs |jtjkS)z4Either the IP object is of address family AF_INET )ryrr$)r>r r r r‘sz IPAddr.isIPv4cCs |jtjkS)z5Either the IP object is of address family AF_INET6 )ryrr&)r>r r r r“sz IPAddr.isIPv6cCsn|j r"|jdkr"|tj|jƒkS|j|jkr2dS|jrFd|j?}n|jrZd|j?}ndS|j|@|jkS)z9Return either the IP object is in the provided network r Flÿÿl ÿÿÿÿÿÿÿÿÿ) r*r†rr3ryr‘r…r“r‚)r>Znetr{r r r rw s zIPAddr.isInNetcCst|tƒo||kp|j|ƒS)z;Return whether the object (as network) contains given IP )rrrw)r>rr r r ÚcontainsszIPAddr.containscCspdd>d}d}|d|dddi}d}xFtddƒD]8}|d|>O}|dkrXd||||A<d||||A<q0W|S) Nrrsrqrllÿÿéé)Úrange)Zm6Zm4ZmmapÚmÚir r r Z __getMaskMap%s  zIPAddr.__getMaskMapcCsFd}|jdk r|jStjj|jƒ}|dkrZmplenr r r Úmaskplen3s zIPAddr.maskplencCs t|ƒjS)zIConvert mask string to prefix length To be used only for IPv4 masks )rr )r{r r r rn>szIPAddr.masktoplencCs4tjj|ƒ}|sdS|jdƒ}|dkr*|S|jdƒS)zBSearch if text is an IP address, and return it if so, else None NZIPv4r ZIPv6)rÚ IP_4_6_CREÚmatchÚgroup)r<r¢rdr r r r:Fs  zIPAddr.searchIP)rUrVrWrXrYi,éþÿÿÿrk)N)9rPrQrRÚ__doc__ZIP_4_REZIP_6_REÚreÚcompiler¡rxÚ __slots__rrSrar]rZrr$r%r&r'r_rTrbr`rr€r�Úpropertyr‚ryrƒr„r…r†r*r‡r‰rŠr‹rŒr�r�r�rLr—r˜r‘r“rwr™Z_IPAddr__getMaskMaprŸr rnr:Ú __classcell__r r )rgr rísZ    7            rz ::ffff:0:0é`)r )Ú __author__Z __copyright__Z __license__rrur¦ZutilsrZhelpersrrPr-r rrÚobjectrrxr r r r Ús    !iserver/__pycache__/jail.cpython-36.opt-1.pyc000064400000023641152231502150014570 0ustar003 UB(d1-ã@sxdZdZdZddlZddlZddlZddlZddlmZddl m Z m Z m Z m Z dd lm Z e eƒZGd d „d eƒZdS) z.Cyril Jaquier, Lee Clemens, Yaroslav HalchenkozPCopyright (c) 2004 Cyril Jaquier, 2011-2012 Lee Clemens, 2012 Yaroslav HalchenkoZGPLéNé)ÚActionsé)Ú getLoggerÚ_as_boolÚextractOptionsÚMyTime)rc@seZdZdZddddgZd:dd „Zd d „Zd d „Zdd„Zdd„Z dd„Z dd„Z e dd„ƒZ e dd„ƒZejdd„ƒZe dd„ƒZe dd„ƒZe dd „ƒZejd!d „ƒZd;d#d$„Ze d%d&„ƒZd'd(„Zd)d*„Zd+d,„Zdd6d7„Zd8d9„ZdS)?ÚJailagFail2Ban jail, which manages a filter and associated actions. The class handles the initialisation of a filter, and actions. It's role is then to act as an interface between the filter and actions, passing bans detected by the filter, for the actions to then act upon. Parameters ---------- name : str Name assigned to the jail. backend : str Backend to be used for filter. "auto" will attempt to pick the most preferred backend method. Default: "auto" db : Fail2BanDb Fail2Ban persistent database instance. Default: `None` Attributes ---------- name database filter actions idle status Z pyinotifyZgaminZpollingZsystemdÚautoNcCsh||_t|ƒdkr tjd|ƒ||_tjƒ|_d|_i|_ tj d|j ƒ|dk r^|j |ƒ||_ dS)Néz]Jail name %r might be too long and some commands might not function correctly. Please shortenzCreating new jail '%s')Ú _Jail__dbÚlenÚlogSysÚwarningÚ _Jail__nameÚqueueZQueueÚ _Jail__queueÚ _Jail__filterÚ _banExtraÚinfoÚnameÚ _setBackendÚbackend)ÚselfrrZdb©rú/usr/lib/python3.6/jail.pyÚ__init__Gs   z Jail.__init__cCsd|jj|jfS)Nz%s(%r))Ú __class__Ú__name__r)rrrrÚ__repr__Xsz Jail.__repr__cCs6t|ƒ\}}|jƒ}|j}|dkr`||jkrNtjd||fƒtd||fƒ‚||j|ƒd…}x²|D]ª}t|d|jƒƒ}yJ|f|Ž|dkr¬||kr¬tj d||fƒntj d|ƒt |ƒ|_ dSt k �r}z*tj|dkrìtjntjd||fƒWYdd}~XqfXqfWtjd|jƒtd|jƒ‚dS)Nr z.Unknown backend %s. Must be among %s or 'auto'z_init%sz9Could only initiated %r backend whenever %r was requestedzInitiated %r backendz)Backend %r failed to initialize due to %sz,Failed to initialize any backend for Jail %r)rÚlowerÚ _BACKENDSrÚerrorÚ ValueErrorÚindexÚgetattrÚ capitalizerrrÚ_Jail__actionsÚ ImportErrorÚlogÚloggingÚDEBUGZERRORrÚ RuntimeError)rrZbeArgsZbackendsÚbZ initmethodÚerrrr[s8       $ zJail._setBackendcKs2ddlm}tjd|j|fƒ||f|Ž|_dS)Nr)Ú FilterPollzJail '%s' uses poller %r)Z filterpollr/rrrr)rÚkwargsr/rrrÚ _initPolling�s zJail._initPollingcKs2ddlm}tjd|j|fƒ||f|Ž|_dS)Nr)Ú FilterGaminzJail '%s' uses Gamin %r)Z filtergaminr2rrrr)rr0r2rrrÚ _initGamin†s zJail._initGamincKs2ddlm}tjd|j|fƒ||f|Ž|_dS)Nr)ÚFilterPyinotifyzJail '%s' uses pyinotify %r)Zfilterpyinotifyr4rrrr)rr0r4rrrÚ_initPyinotifyŒs zJail._initPyinotifycKs2ddlm}tjd|j|fƒ||f|Ž|_dS)Nr)Ú FilterSystemdzJail '%s' uses systemd %r)Z filtersystemdr6rrrr)rr0r6rrrÚ _initSystemd’s zJail._initSystemdcCs|jS)zName of jail. )r)rrrrr˜sz Jail.namecCs|jS)z;The database used to store persistent data for the jail. )r )rrrrÚdatabasežsz Jail.databasecCs ||_dS)N)r )rÚvaluerrrr8¤scCs|jS)z;The filter which the jail is using to monitor log files. )r)rrrrÚfilter¨sz Jail.filtercCs|jS)z2Actions object used to manage actions for jail. )r')rrrrÚactions®sz Jail.actionscCs|jjp|jjS)z-A boolean indicating whether jail is idle. )r:Úidler;)rrrrr<´sz Jail.idlecCs||j_||j_dS)N)r:r<r;)rr9rrrr<ºsÚbasiccCs$d|jj|d�fd|jj|d�fgS)zThe status of the jail. ÚFilter)Úflavorr)r:Ústatusr;)rr?rrrr@¿sz Jail.statuscCs |jjƒ S)z-Retrieve whether queue has tickets to ban. )rÚempty)rrrrÚhasFailTicketsÇszJail.hasFailTicketscCs|jj|ƒdS)zQAdd a fail ticket to the jail. Used by filter to add a failure for banning. N)rZput)rÚticketrrrÚ putFailTicketÍszJail.putFailTicketc Cs,y|jjdƒ}|Stjk r&dSXdS)zTGet a fail ticket from the jail. Used by actions to get a failure for banning. FN)rÚgetrZEmpty)rrCrrrÚ getFailTicketÖs  zJail.getFailTicketcsœ|j}|dkrd}|dk r$|||<n||kr2||=tjd||ƒ|dkrrt|ƒ||<|j|ƒrr|jdkrrtjdƒ|dkr�|dk r�tj|ƒ||<|dksª|jd dƒdk�r˜|d krÞd d „|dk rÒ|dkrÒ|j d ƒngDƒ|d<|jdgƒ‰t |jddƒƒ}t ˆƒ�r|f‡fdd„ }n&|jddƒ}t |ddƒ}||fdd„}|jddƒdk �rf|d‰|f‡fdd„ }|jddƒdk �r�|d‰|f‡fdd„ }||d <dS)NÚzSet banTime.%s = %sÚ incrementzDban time increment is not available as long jail database is not setÚmaxtimeÚrndtimeÚformulaÚfactorÚ multipliersÚ evformulacSsg|] }t|ƒ‘qSr)Úint)Ú.0Úirrrú ösz(Jail.setBanTimeExtra..ú Z evmultipliersÚ1cs&|j|ˆ|jtˆƒkr|jndS)Nréÿÿÿÿ)ÚTimeZCountr )ÚbanÚ banFactor)rMrrÚûsz&Jail.setBanTimeExtra..z?ban.Time * (1<<(ban.Count if ban.Count<20 else 20)) * banFactorz~inline-conf-expr~ÚevalcSst|jt|ƒƒS)N)ÚmaxrVrZ)rWrXrKrrrrYscst||ƒˆƒS)N)Úmin)rWrN)rIrrrYscs||ƒtjƒˆS)N)Úrandom)rWrN)rJrrrY s)rIrJ)rKrLrIrJrM) rrrrrEr8rrZ str2secondsÚsplitrZr Úcompile)rÚoptr9ZberXrNrKr)rIrMrJrÚsetBanTimeExtraás@   ,    zJail.setBanTimeExtracCs|dk r|jj|dƒS|jS)N)rrE)rr`rrrÚgetBanTimeExtraszJail.getBanTimeExtracCs$|jjdƒr|jjddƒS|jjƒS)z)Returns max possible ban-time of jail. rHrIrrU)rrEr;Ú getBanTime)rrrrÚ getMaxBanTimeszJail.getMaxBanTimeTcCsP�y |jdk �r |jjdƒr.d}|r8|jƒ}n |jjƒ}xÐ|jj||||jjj d�D]²}yn|jj |j ƒdd�rpwTd|_ |j|ƒ}t jƒ|jƒ}|d kr¨|dkr¨||8}|d krº|dkrºwT|j|ƒWqTtk �r}z tjd|tjƒtjkd �WYdd}~XqTXqTWWn>tk �rJ}z tjd |tjƒtjkd �WYdd}~XnXdS) z5Restore any previous valid bans from the database. NrH)ZjailÚ forbantimeÚcorrectBanTimeZ maxmatchesT)Z log_ignorerrzRestore ticket failed: %s)Úexc_infozRestore bans failed: %srUrU)r8rrErdr;rcZgetCurrentBansr:Z failManagerZ maxMatchesZinIgnoreIPListZgetIDZrestoredrZtimeZgetTimerDÚ Exceptionrr"ÚgetEffectiveLevelr*r+)rrfrerCZbtmZdiftmr.rrrÚrestoreCurrentBanss4      ,zJail.restoreCurrentBanscCs<tjd|jƒ|jjƒ|jjƒ|jƒtjd|jƒdS)zŽStart the jail, by starting filter and actions threads. Once stated, also queries the persistent database to reinstate any valid bans. zStarting jail %rzJail %r startedN)rÚdebugrr:Ústartr;rjr)rrrrrl=s   z Jail.startcCsž|rtjd|jƒxt|j|jfD]d}y|r2|jƒ|r>|jƒWq tk r‚}z&tjd||j|tj ƒt j kd�WYdd}~Xq Xq W|rštj d|jƒdS)z9Stop the jail, by stopping filter and actions threads. zStopping jail %rzStop %r of jail %r failed: %s)rgNzJail %r stopped) rrkrr:r;ÚstopÚjoinrhr"rir*r+r)rrmrnÚobjr.rrrrmIs (z Jail.stopcCs|jjƒp|jjƒS)z?Check jail "isAlive" by checking filter and actions threads. )r:ÚisAliver;)rrrrrp\sz Jail.isAlive)r N)r=)N)T)TT)rÚ __module__Ú __qualname__Ú__doc__r!rrrr1r3r5r7Úpropertyrr8Úsetterr:r;r<r@rBrDrFrarbrdrjrlrmrprrrrr 's6  &         -  $ r )Ú __author__Z __copyright__Z __license__r*Zmathr]rr;rZhelpersrrrrZmytimerrÚobjectr rrrrÚs  server/__pycache__/jail.cpython-36.pyc000064400000023641152231502150013631 0ustar003 UB(d1-ã@sxdZdZdZddlZddlZddlZddlZddlmZddl m Z m Z m Z m Z dd lm Z e eƒZGd d „d eƒZdS) z.Cyril Jaquier, Lee Clemens, Yaroslav HalchenkozPCopyright (c) 2004 Cyril Jaquier, 2011-2012 Lee Clemens, 2012 Yaroslav HalchenkoZGPLéNé)ÚActionsé)Ú getLoggerÚ_as_boolÚextractOptionsÚMyTime)rc@seZdZdZddddgZd:dd „Zd d „Zd d „Zdd„Zdd„Z dd„Z dd„Z e dd„ƒZ e dd„ƒZejdd„ƒZe dd„ƒZe dd„ƒZe dd „ƒZejd!d „ƒZd;d#d$„Ze d%d&„ƒZd'd(„Zd)d*„Zd+d,„Zdd6d7„Zd8d9„ZdS)?ÚJailagFail2Ban jail, which manages a filter and associated actions. The class handles the initialisation of a filter, and actions. It's role is then to act as an interface between the filter and actions, passing bans detected by the filter, for the actions to then act upon. Parameters ---------- name : str Name assigned to the jail. backend : str Backend to be used for filter. "auto" will attempt to pick the most preferred backend method. Default: "auto" db : Fail2BanDb Fail2Ban persistent database instance. Default: `None` Attributes ---------- name database filter actions idle status Z pyinotifyZgaminZpollingZsystemdÚautoNcCsh||_t|ƒdkr tjd|ƒ||_tjƒ|_d|_i|_ tj d|j ƒ|dk r^|j |ƒ||_ dS)Néz]Jail name %r might be too long and some commands might not function correctly. Please shortenzCreating new jail '%s')Ú _Jail__dbÚlenÚlogSysÚwarningÚ _Jail__nameÚqueueZQueueÚ _Jail__queueÚ _Jail__filterÚ _banExtraÚinfoÚnameÚ _setBackendÚbackend)ÚselfrrZdb©rú/usr/lib/python3.6/jail.pyÚ__init__Gs   z Jail.__init__cCsd|jj|jfS)Nz%s(%r))Ú __class__Ú__name__r)rrrrÚ__repr__Xsz Jail.__repr__cCs6t|ƒ\}}|jƒ}|j}|dkr`||jkrNtjd||fƒtd||fƒ‚||j|ƒd…}x²|D]ª}t|d|jƒƒ}yJ|f|Ž|dkr¬||kr¬tj d||fƒntj d|ƒt |ƒ|_ dSt k �r}z*tj|dkrìtjntjd||fƒWYdd}~XqfXqfWtjd|jƒtd|jƒ‚dS)Nr z.Unknown backend %s. Must be among %s or 'auto'z_init%sz9Could only initiated %r backend whenever %r was requestedzInitiated %r backendz)Backend %r failed to initialize due to %sz,Failed to initialize any backend for Jail %r)rÚlowerÚ _BACKENDSrÚerrorÚ ValueErrorÚindexÚgetattrÚ capitalizerrrÚ_Jail__actionsÚ ImportErrorÚlogÚloggingÚDEBUGZERRORrÚ RuntimeError)rrZbeArgsZbackendsÚbZ initmethodÚerrrr[s8       $ zJail._setBackendcKs2ddlm}tjd|j|fƒ||f|Ž|_dS)Nr)Ú FilterPollzJail '%s' uses poller %r)Z filterpollr/rrrr)rÚkwargsr/rrrÚ _initPolling�s zJail._initPollingcKs2ddlm}tjd|j|fƒ||f|Ž|_dS)Nr)Ú FilterGaminzJail '%s' uses Gamin %r)Z filtergaminr2rrrr)rr0r2rrrÚ _initGamin†s zJail._initGamincKs2ddlm}tjd|j|fƒ||f|Ž|_dS)Nr)ÚFilterPyinotifyzJail '%s' uses pyinotify %r)Zfilterpyinotifyr4rrrr)rr0r4rrrÚ_initPyinotifyŒs zJail._initPyinotifycKs2ddlm}tjd|j|fƒ||f|Ž|_dS)Nr)Ú FilterSystemdzJail '%s' uses systemd %r)Z filtersystemdr6rrrr)rr0r6rrrÚ _initSystemd’s zJail._initSystemdcCs|jS)zName of jail. )r)rrrrr˜sz Jail.namecCs|jS)z;The database used to store persistent data for the jail. )r )rrrrÚdatabasežsz Jail.databasecCs ||_dS)N)r )rÚvaluerrrr8¤scCs|jS)z;The filter which the jail is using to monitor log files. )r)rrrrÚfilter¨sz Jail.filtercCs|jS)z2Actions object used to manage actions for jail. )r')rrrrÚactions®sz Jail.actionscCs|jjp|jjS)z-A boolean indicating whether jail is idle. )r:Úidler;)rrrrr<´sz Jail.idlecCs||j_||j_dS)N)r:r<r;)rr9rrrr<ºsÚbasiccCs$d|jj|d�fd|jj|d�fgS)zThe status of the jail. ÚFilter)Úflavorr)r:Ústatusr;)rr?rrrr@¿sz Jail.statuscCs |jjƒ S)z-Retrieve whether queue has tickets to ban. )rÚempty)rrrrÚhasFailTicketsÇszJail.hasFailTicketscCs|jj|ƒdS)zQAdd a fail ticket to the jail. Used by filter to add a failure for banning. N)rZput)rÚticketrrrÚ putFailTicketÍszJail.putFailTicketc Cs,y|jjdƒ}|Stjk r&dSXdS)zTGet a fail ticket from the jail. Used by actions to get a failure for banning. FN)rÚgetrZEmpty)rrCrrrÚ getFailTicketÖs  zJail.getFailTicketcsœ|j}|dkrd}|dk r$|||<n||kr2||=tjd||ƒ|dkrrt|ƒ||<|j|ƒrr|jdkrrtjdƒ|dkr�|dk r�tj|ƒ||<|dksª|jd dƒdk�r˜|d krÞd d „|dk rÒ|dkrÒ|j d ƒngDƒ|d<|jdgƒ‰t |jddƒƒ}t ˆƒ�r|f‡fdd„ }n&|jddƒ}t |ddƒ}||fdd„}|jddƒdk �rf|d‰|f‡fdd„ }|jddƒdk �r�|d‰|f‡fdd„ }||d <dS)NÚzSet banTime.%s = %sÚ incrementzDban time increment is not available as long jail database is not setÚmaxtimeÚrndtimeÚformulaÚfactorÚ multipliersÚ evformulacSsg|] }t|ƒ‘qSr)Úint)Ú.0Úirrrú ösz(Jail.setBanTimeExtra..ú Z evmultipliersÚ1cs&|j|ˆ|jtˆƒkr|jndS)Nréÿÿÿÿ)ÚTimeZCountr )ÚbanÚ banFactor)rMrrÚûsz&Jail.setBanTimeExtra..z?ban.Time * (1<<(ban.Count if ban.Count<20 else 20)) * banFactorz~inline-conf-expr~ÚevalcSst|jt|ƒƒS)N)ÚmaxrVrZ)rWrXrKrrrrYscst||ƒˆƒS)N)Úmin)rWrN)rIrrrYscs||ƒtjƒˆS)N)Úrandom)rWrN)rJrrrY s)rIrJ)rKrLrIrJrM) rrrrrEr8rrZ str2secondsÚsplitrZr Úcompile)rÚoptr9ZberXrNrKr)rIrMrJrÚsetBanTimeExtraás@   ,    zJail.setBanTimeExtracCs|dk r|jj|dƒS|jS)N)rrE)rr`rrrÚgetBanTimeExtraszJail.getBanTimeExtracCs$|jjdƒr|jjddƒS|jjƒS)z)Returns max possible ban-time of jail. rHrIrrU)rrEr;Ú getBanTime)rrrrÚ getMaxBanTimeszJail.getMaxBanTimeTcCsP�y |jdk �r |jjdƒr.d}|r8|jƒ}n |jjƒ}xÐ|jj||||jjj d�D]²}yn|jj |j ƒdd�rpwTd|_ |j|ƒ}t jƒ|jƒ}|d kr¨|dkr¨||8}|d krº|dkrºwT|j|ƒWqTtk �r}z tjd|tjƒtjkd �WYdd}~XqTXqTWWn>tk �rJ}z tjd |tjƒtjkd �WYdd}~XnXdS) z5Restore any previous valid bans from the database. NrH)ZjailÚ forbantimeÚcorrectBanTimeZ maxmatchesT)Z log_ignorerrzRestore ticket failed: %s)Úexc_infozRestore bans failed: %srUrU)r8rrErdr;rcZgetCurrentBansr:Z failManagerZ maxMatchesZinIgnoreIPListZgetIDZrestoredrZtimeZgetTimerDÚ Exceptionrr"ÚgetEffectiveLevelr*r+)rrfrerCZbtmZdiftmr.rrrÚrestoreCurrentBanss4      ,zJail.restoreCurrentBanscCs<tjd|jƒ|jjƒ|jjƒ|jƒtjd|jƒdS)zŽStart the jail, by starting filter and actions threads. Once stated, also queries the persistent database to reinstate any valid bans. zStarting jail %rzJail %r startedN)rÚdebugrr:Ústartr;rjr)rrrrrl=s   z Jail.startcCsž|rtjd|jƒxt|j|jfD]d}y|r2|jƒ|r>|jƒWq tk r‚}z&tjd||j|tj ƒt j kd�WYdd}~Xq Xq W|rštj d|jƒdS)z9Stop the jail, by stopping filter and actions threads. zStopping jail %rzStop %r of jail %r failed: %s)rgNzJail %r stopped) rrkrr:r;ÚstopÚjoinrhr"rir*r+r)rrmrnÚobjr.rrrrmIs (z Jail.stopcCs|jjƒp|jjƒS)z?Check jail "isAlive" by checking filter and actions threads. )r:ÚisAliver;)rrrrrp\sz Jail.isAlive)r N)r=)N)T)TT)rÚ __module__Ú __qualname__Ú__doc__r!rrrr1r3r5r7Úpropertyrr8Úsetterr:r;r<r@rBrDrFrarbrdrjrlrmrprrrrr 's6  &         -  $ r )Ú __author__Z __copyright__Z __license__r*Zmathr]rr;rZhelpersrrrrZmytimerrÚobjectr rrrrÚs  server/__pycache__/jails.cpython-36.opt-1.pyc000064400000005233152231502150014750 0ustar003 UB(d ã @szdZdZdZddlmZyddlmZWn ek rHddlmZYnXddl m Z m Z dd l m Z Gd d „d eƒZd S) z!Cyril Jaquier, Yaroslav Halchenkoz:Copyright (c) 2004 Cyril Jaquier, 2013- Yaroslav HalchenkoZGPLé)ÚLock)ÚMappingé)ÚDuplicateJailExceptionÚUnknownJailExceptioné)ÚJailc@sJeZdZdZdd„Zddd„Zdd„Zd d „Zd d „Zd d„Z dd„Z dS)ÚJailsaBHandles the jails. This class handles the jails. Creation, deletion or access to a jail must be done through this class. This class is thread-safe which is not the case of the jail itself, including filter and actions. This class is based on Mapping type, and the `add` method must be used to add additional jails. cCstƒ|_tƒ|_dS)N)rÚ _Jails__lockÚdictÚ_jails)Úself©rú/usr/lib/python3.6/jails.pyÚ__init__,szJails.__init__Nc Cs@|j�0||jkr tr2t|ƒ‚nt|||ƒ|j|<WdQRXdS)aUAdds a jail. Adds a new jail if not already present which should use the given backend. Parameters ---------- name : str The name of the jail. backend : str The backend to use. db : Fail2BanDb Fail2Ban's persistent database instance. Raises ------ DuplicateJailException If jail name is already present. N)r r Z noduplicatesrr)r ÚnameZbackendZdbrrrÚadd0s   z Jails.addcCs ||jkS)N)r )r rrrrÚexistsKsz Jails.existscCsHz6y|jjƒ|j|Stk r2t|ƒ‚YnXWd|jjƒXdS)N)r Úacquirer ÚKeyErrorrÚrelease)r rrrrÚ __getitem__Ns   zJails.__getitem__cCsJz8y|jjƒ|j|=Wntk r4t|ƒ‚YnXWd|jjƒXdS)N)r rr rrr)r rrrrÚ __delitem__Ws   zJails.__delitem__c Cs&z|jjƒt|jƒS|jjƒXdS)N)r rÚlenr r)r rrrÚ__len__`s  z Jails.__len__c Cs&z|jjƒt|jƒS|jjƒXdS)N)r rÚiterr r)r rrrÚ__iter__gs  zJails.__iter__)N) Ú__name__Ú __module__Ú __qualname__Ú__doc__rrrrrrrrrrrr "s   r N)Ú __author__Z __copyright__Z __license__Z threadingrÚcollections.abcrÚ ImportErrorÚ collectionsÚ exceptionsrrZjailrr rrrrÚs  server/__pycache__/jails.cpython-36.pyc000064400000005233152231502150014011 0ustar003 UB(d ã @szdZdZdZddlmZyddlmZWn ek rHddlmZYnXddl m Z m Z dd l m Z Gd d „d eƒZd S) z!Cyril Jaquier, Yaroslav Halchenkoz:Copyright (c) 2004 Cyril Jaquier, 2013- Yaroslav HalchenkoZGPLé)ÚLock)ÚMappingé)ÚDuplicateJailExceptionÚUnknownJailExceptioné)ÚJailc@sJeZdZdZdd„Zddd„Zdd„Zd d „Zd d „Zd d„Z dd„Z dS)ÚJailsaBHandles the jails. This class handles the jails. Creation, deletion or access to a jail must be done through this class. This class is thread-safe which is not the case of the jail itself, including filter and actions. This class is based on Mapping type, and the `add` method must be used to add additional jails. cCstƒ|_tƒ|_dS)N)rÚ _Jails__lockÚdictÚ_jails)Úself©rú/usr/lib/python3.6/jails.pyÚ__init__,szJails.__init__Nc Cs@|j�0||jkr tr2t|ƒ‚nt|||ƒ|j|<WdQRXdS)aUAdds a jail. Adds a new jail if not already present which should use the given backend. Parameters ---------- name : str The name of the jail. backend : str The backend to use. db : Fail2BanDb Fail2Ban's persistent database instance. Raises ------ DuplicateJailException If jail name is already present. N)r r Z noduplicatesrr)r ÚnameZbackendZdbrrrÚadd0s   z Jails.addcCs ||jkS)N)r )r rrrrÚexistsKsz Jails.existscCsHz6y|jjƒ|j|Stk r2t|ƒ‚YnXWd|jjƒXdS)N)r Úacquirer ÚKeyErrorrÚrelease)r rrrrÚ __getitem__Ns   zJails.__getitem__cCsJz8y|jjƒ|j|=Wntk r4t|ƒ‚YnXWd|jjƒXdS)N)r rr rrr)r rrrrÚ __delitem__Ws   zJails.__delitem__c Cs&z|jjƒt|jƒS|jjƒXdS)N)r rÚlenr r)r rrrÚ__len__`s  z Jails.__len__c Cs&z|jjƒt|jƒS|jjƒXdS)N)r rÚiterr r)r rrrÚ__iter__gs  zJails.__iter__)N) Ú__name__Ú __module__Ú __qualname__Ú__doc__rrrrrrrrrrrr "s   r N)Ú __author__Z __copyright__Z __license__Z threadingrÚcollections.abcrÚ ImportErrorÚ collectionsÚ exceptionsrrZjailrr rrrrÚs  server/__pycache__/jailthread.cpython-36.opt-1.pyc000064400000006236152231502150015761 0ustar003 UB(dÓã@s‚dZdZdZddlZddlmZddlmZddlm Z d d l m Z m Z Gd d „d eƒZ ejdkrle je _nee dƒs~e je _dS)z Cyril Jaquierz Copyright (c) 2004 Cyril JaquierZGPLéN)ÚThread)Úabstractmethodé)ÚUtilsé)Ú excepthookÚprctl_set_th_namecsˆeZdZdZd‡fdd„ Zejdkr2‡fdd„Zndd „Ze dd d „ƒZ ‡fd d„Z e dd„ƒZ dd„Z e dd„ƒZ‡fdd„Z‡ZS)Ú JailThreadaAbstract class for threading elements in Fail2Ban. Attributes ---------- daemon ident name status active : bool Control the state of the thread. idle : bool Control the idle state of the thread. sleeptime : int The time the thread sleeps for in the loop. NcsJttˆƒj|d�dˆ_dˆ_dˆ_tjˆ_ˆj ‰‡‡fdd„}|ˆ_ dS)N)ÚnameTFcs\yˆ||ŽˆjƒWn@tk rV}z$tdk r>ttjƒŽnt|ƒWYdd}~XnXdS)N)ÚonStopÚ ExceptionÚsysrÚexc_infoÚprint)ÚargsÚkwargsÚe)ÚrunÚself©ú /usr/lib/python3.6/jailthread.pyÚrun_with_except_hookCs  z1JailThread.__init__..run_with_except_hook) Úsuperr Ú__init__ZdaemonÚactiveZidlerZDEFAULT_SLEEP_TIMEZ sleeptimer)rr r)Ú __class__)rrrr4s zJailThread.__init__écst|jƒtt|ƒjƒS)N)rr rr Ú _bootstrap)r)rrrrRs zJailThread._bootstrapcCst|jƒtj|ƒS)N)rr rÚ_Thread__bootstrap)rrrrZ __bootstrapVs zJailThread.__bootstrapÚbasiccCsdS)z0Abstract - Should provide status information. Nr)rZflavorrrrÚstatusZszJailThread.statuscsd|_tt|ƒjƒdS)z&Sets active flag and starts thread. TN)rrr Ústart)r)rrrr!`szJailThread.startcCsdS)z2Abstract - Called when thread ends (after run). Nr)rrrrr fszJailThread.onStopcCs d|_dS)zASets `active` property to False, to flag run method to return. FN)r)rrrrÚstoplszJailThread.stopcCsdS)zCAbstract - Called when thread starts, thread stops when returns. Nr)rrrrrqszJailThread.runcs|jdk rtt|ƒjƒdS)z` Safer join, that could be called also for not started (or ended) threads (used for cleanup). N)rrr Újoin)r)rrrr#ws zJailThread.join)N)r)r)Ú__name__Ú __module__Ú __qualname__Ú__doc__rr Ú version_inforÚ_JailThread__bootstraprr r!r r"rr#Ú __classcell__rr)rrr #s     r rÚisAlive)r)Ú __author__Z __copyright__Z __license__r Z threadingrÚabcrZutilsrZhelpersrrr r(r)rÚhasattrZis_aliver+rrrrÚs   ^   server/__pycache__/jailthread.cpython-36.pyc000064400000006236152231502150015022 0ustar003 UB(dÓã@s‚dZdZdZddlZddlmZddlmZddlm Z d d l m Z m Z Gd d „d eƒZ ejdkrle je _nee dƒs~e je _dS)z Cyril Jaquierz Copyright (c) 2004 Cyril JaquierZGPLéN)ÚThread)Úabstractmethodé)ÚUtilsé)Ú excepthookÚprctl_set_th_namecsˆeZdZdZd‡fdd„ Zejdkr2‡fdd„Zndd „Ze dd d „ƒZ ‡fd d„Z e dd„ƒZ dd„Z e dd„ƒZ‡fdd„Z‡ZS)Ú JailThreadaAbstract class for threading elements in Fail2Ban. Attributes ---------- daemon ident name status active : bool Control the state of the thread. idle : bool Control the idle state of the thread. sleeptime : int The time the thread sleeps for in the loop. NcsJttˆƒj|d�dˆ_dˆ_dˆ_tjˆ_ˆj ‰‡‡fdd„}|ˆ_ dS)N)ÚnameTFcs\yˆ||ŽˆjƒWn@tk rV}z$tdk r>ttjƒŽnt|ƒWYdd}~XnXdS)N)ÚonStopÚ ExceptionÚsysrÚexc_infoÚprint)ÚargsÚkwargsÚe)ÚrunÚself©ú /usr/lib/python3.6/jailthread.pyÚrun_with_except_hookCs  z1JailThread.__init__..run_with_except_hook) Úsuperr Ú__init__ZdaemonÚactiveZidlerZDEFAULT_SLEEP_TIMEZ sleeptimer)rr r)Ú __class__)rrrr4s zJailThread.__init__écst|jƒtt|ƒjƒS)N)rr rr Ú _bootstrap)r)rrrrRs zJailThread._bootstrapcCst|jƒtj|ƒS)N)rr rÚ_Thread__bootstrap)rrrrZ __bootstrapVs zJailThread.__bootstrapÚbasiccCsdS)z0Abstract - Should provide status information. Nr)rZflavorrrrÚstatusZszJailThread.statuscsd|_tt|ƒjƒdS)z&Sets active flag and starts thread. TN)rrr Ústart)r)rrrr!`szJailThread.startcCsdS)z2Abstract - Called when thread ends (after run). Nr)rrrrr fszJailThread.onStopcCs d|_dS)zASets `active` property to False, to flag run method to return. FN)r)rrrrÚstoplszJailThread.stopcCsdS)zCAbstract - Called when thread starts, thread stops when returns. Nr)rrrrrqszJailThread.runcs|jdk rtt|ƒjƒdS)z` Safer join, that could be called also for not started (or ended) threads (used for cleanup). N)rrr Újoin)r)rrrr#ws zJailThread.join)N)r)r)Ú__name__Ú __module__Ú __qualname__Ú__doc__rr Ú version_inforÚ_JailThread__bootstraprr r!r r"rr#Ú __classcell__rr)rrr #s     r rÚisAlive)r)Ú __author__Z __copyright__Z __license__r Z threadingrÚabcrZutilsrZhelpersrrr r(r)rÚhasattrZis_aliver+rrrrÚs   ^   server/__pycache__/mytime.cpython-36.opt-1.pyc000064400000015037152231502150015155 0ustar003 UB(dÿã@s6dZdZdZddlZddlZddlZGdd„dƒZdS)z Cyril Jaquierz Copyright (c) 2004 Cyril JaquierZGPLéNc @sReZdZdZdZdZdZedd„ƒZedd„ƒZ edd„ƒZ ed d „ƒZ ed d „ƒZ ed*d d„ƒZ ed+dd„ƒZejdƒZejdƒZdZejedƒded-ƒfejedƒded0ƒfejedƒded7ƒfejed!ƒded=ƒfejed"ƒded#ƒfejed$ƒdedƒfejed%ƒded>ƒffZed&d'„ƒZGd(d)„d)ƒZdS)?ÚMyTimea`A wrapper around time module primarily for testing purposes This class is a wrapper around time.time() and time.gmtime(). When performing unit test, it is very useful to get a fixed value from these functions. Thus, time.time() and time.gmtime() should never be called directly. This wrapper should be called instead. The API are equivalent. NcCs$|t_|dk rtjj|ƒndt_dS)zrSet current time. Use None in order to always get the real current time. @param t the time to set or None N)rÚalternateNowTimeÚdatetimeÚ fromtimestampÚ alternateNow)Út©rú/usr/lib/python3.6/mytime.pyÚsetAlternateNow/s zMyTime.setAlternateNowcCs |t_dS)zrSet current time. Use None in order to always get the real current time. @param t the time to set or None N)rÚmyTime)rrrr ÚsetTime<s zMyTime.setTimecCstjdkrtjƒStjSdS)zqDecorate time.time() for the purpose of testing mocking @return time.time() if setTime was called with None N)rr Útimerrrr r Gs z MyTime.timecCs"tjdkrtjƒStjtjƒSdS)zuDecorate time.gmtime() for the purpose of testing mocking @return time.gmtime() if setTime was called with None N)rr r Úgmtimerrrr rSs z MyTime.gmtimecCs4tjdkrtjjƒStjtjkr&tjStjjtjƒS)zwDecorate datetime.now() for the purpose of testing mocking @return datetime.now() if setTime was called with None N)rr rÚnowrrrrrrr r^s    z MyTime.nowcCs,tjdks|dk rtj|ƒStjtjƒSdS)z{Decorate time.localtime() for the purpose of testing mocking @return time.localtime() if setTime was called with None N)rr r Ú localtime)Úxrrr rjs zMyTime.localtimeú%Y-%m-%d %H:%M:%ScCs:|dkrtjj|ƒjdd�ntjddddddƒ}|j|ƒS) zÔConvert time to a string representing as date and time using given format. Default format is ISO 8601, YYYY-MM-DD HH:MM:SS without microseconds. @return ISO-capable string representation of given unixTime lpåër)Z microsecondi'é ééé;)rrÚreplaceZstrftime)ZunixTimeÚformatZdtrrr Útime2strus 0zMyTime.time2strz(?i)(?<=[a-z])(\d)z (\d)\s+(\d)z(?i)(?<=[\d\s])(%s)\bz days?|da|dd?Ú*éé<zweeks?|wee?|ww?éz months?|mon?iméinérzyears?|yea?|yy?zseconds?|sec?|ss?ézminutes?|min?|mm?zhours?|hou?|hh?cCsXt|tttfƒr|Stjjd|ƒ}xtjD]\}}|j||ƒ}q*Wtjjd|ƒ}t |ƒS)a)Wraps string expression like "1h 2m 3s" into number contains seconds (3723). The string expression will be evaluated as mathematical expression, spaces between each groups will be wrapped to "+" operand (only if any operand does not specified between). Because of case insensitivity and overwriting with minutes ("m" or "mm"), the short replacement for month are "mo" or "mon". Ex: 1hour+30min = 5400 0d 1h 30m = 5400 1year-6mo = 15778800 6 months = 15778800 warn: month is not 30 days, it is a year in seconds / 12, the leap years will be respected also: >>>> float(str2seconds("1month")) / 60 / 60 / 24 30.4375 >>>> float(str2seconds("1year")) / 60 / 60 / 24 365.25 @returns number (calculated seconds from expression "val") z \1z\1+\2) Ú isinstanceÚintÚfloatÚcomplexrÚ _str2sec_prepÚsubÚ_str2sec_partsÚ _str2sec_finiÚeval)ÚvalZrexpZrplrrr Ú str2seconds•szMyTime.str2secondsc@s(eZdZdZdd„Zdd„Zdd„ZdS) zMyTime.seconds2straËConverts seconds to string on demand (if string representation needed). Ex: seconds2str(86400*390) = 1y 3w 4d seconds2str(86400*368) = 1y 3d seconds2str(86400*365.5) = 1y seconds2str(86400*2+3600*7+60*15) = 2d 7h 15m seconds2str(86400*2+3599) = 2d 1h seconds2str(3600-5) = 1h seconds2str(3600-10) = 59m 50s seconds2str(59) = 59s cCs ||_dS)N)Úsec)Úselfr,rrr Ú__init__½szMyTime.seconds2str.__init__cCs:|j}d}|dkr‚ttt|ƒdƒƒ}t|dƒd}|d;}|dkrb|t|dƒd7}|d;}|rv|t|ƒd7}|dd…S|d krúttt|ƒd ƒƒ}t|d ƒd}|d ;}|d krÚ|t|d ƒd7}|d ;}|rî|t|ƒd7}|dd…S|dk�rxttt|ƒdƒƒ}t|dƒd}|d;}|dk�rV|t|dƒd7}|d;}|�rl|t|ƒd7}|dd…S|dk�ràttt|ƒdƒƒ}t|dƒd}|d;}|dk�rÔ|t|dƒd7}|d;}|dd…Sd}|dk�r |t|dƒd7}|d;}|�r"|t|ƒd7}n |j�s.d}|dd…S)Nri€3ái€Qimzy rzw zd r i€: ié¨rzh ri zm i é ihéÚzs z0 éÿÿÿÿr3r3r3r3)r,r"Úroundr#Ústr)r-ÚsÚcÚrrrr Ú__str__¿sb         zMyTime.seconds2str.__str__cCs|jƒS)N)r9)r-rrr Ú__repr__êszMyTime.seconds2str.__repr__N)Ú__name__Ú __module__Ú __qualname__Ú__doc__r.r9r:rrrr Ú seconds2str²s +r?)N)ri i€Qr/i`'i€: iGiµiøˆi  i€†g~~AgTDAiGiµiøˆi  i€†g~~Ai)r;r<r=r>r rrÚ staticmethodr r r rrrrÚreÚcompiler%r(Z_str2sec_subpartr5r'r+r?rrrr r!s2      r)Ú __author__Z __copyright__Z __license__rrAr rrrrr Ús server/__pycache__/mytime.cpython-36.pyc000064400000015037152231502150014216 0ustar003 UB(dÿã@s6dZdZdZddlZddlZddlZGdd„dƒZdS)z Cyril Jaquierz Copyright (c) 2004 Cyril JaquierZGPLéNc @sReZdZdZdZdZdZedd„ƒZedd„ƒZ edd„ƒZ ed d „ƒZ ed d „ƒZ ed*d d„ƒZ ed+dd„ƒZejdƒZejdƒZdZejedƒded-ƒfejedƒded0ƒfejedƒded7ƒfejed!ƒded=ƒfejed"ƒded#ƒfejed$ƒdedƒfejed%ƒded>ƒffZed&d'„ƒZGd(d)„d)ƒZdS)?ÚMyTimea`A wrapper around time module primarily for testing purposes This class is a wrapper around time.time() and time.gmtime(). When performing unit test, it is very useful to get a fixed value from these functions. Thus, time.time() and time.gmtime() should never be called directly. This wrapper should be called instead. The API are equivalent. NcCs$|t_|dk rtjj|ƒndt_dS)zrSet current time. Use None in order to always get the real current time. @param t the time to set or None N)rÚalternateNowTimeÚdatetimeÚ fromtimestampÚ alternateNow)Út©rú/usr/lib/python3.6/mytime.pyÚsetAlternateNow/s zMyTime.setAlternateNowcCs |t_dS)zrSet current time. Use None in order to always get the real current time. @param t the time to set or None N)rÚmyTime)rrrr ÚsetTime<s zMyTime.setTimecCstjdkrtjƒStjSdS)zqDecorate time.time() for the purpose of testing mocking @return time.time() if setTime was called with None N)rr Útimerrrr r Gs z MyTime.timecCs"tjdkrtjƒStjtjƒSdS)zuDecorate time.gmtime() for the purpose of testing mocking @return time.gmtime() if setTime was called with None N)rr r Úgmtimerrrr rSs z MyTime.gmtimecCs4tjdkrtjjƒStjtjkr&tjStjjtjƒS)zwDecorate datetime.now() for the purpose of testing mocking @return datetime.now() if setTime was called with None N)rr rÚnowrrrrrrr r^s    z MyTime.nowcCs,tjdks|dk rtj|ƒStjtjƒSdS)z{Decorate time.localtime() for the purpose of testing mocking @return time.localtime() if setTime was called with None N)rr r Ú localtime)Úxrrr rjs zMyTime.localtimeú%Y-%m-%d %H:%M:%ScCs:|dkrtjj|ƒjdd�ntjddddddƒ}|j|ƒS) zÔConvert time to a string representing as date and time using given format. Default format is ISO 8601, YYYY-MM-DD HH:MM:SS without microseconds. @return ISO-capable string representation of given unixTime lpåër)Z microsecondi'é ééé;)rrÚreplaceZstrftime)ZunixTimeÚformatZdtrrr Útime2strus 0zMyTime.time2strz(?i)(?<=[a-z])(\d)z (\d)\s+(\d)z(?i)(?<=[\d\s])(%s)\bz days?|da|dd?Ú*éé<zweeks?|wee?|ww?éz months?|mon?iméinérzyears?|yea?|yy?zseconds?|sec?|ss?ézminutes?|min?|mm?zhours?|hou?|hh?cCsXt|tttfƒr|Stjjd|ƒ}xtjD]\}}|j||ƒ}q*Wtjjd|ƒ}t |ƒS)a)Wraps string expression like "1h 2m 3s" into number contains seconds (3723). The string expression will be evaluated as mathematical expression, spaces between each groups will be wrapped to "+" operand (only if any operand does not specified between). Because of case insensitivity and overwriting with minutes ("m" or "mm"), the short replacement for month are "mo" or "mon". Ex: 1hour+30min = 5400 0d 1h 30m = 5400 1year-6mo = 15778800 6 months = 15778800 warn: month is not 30 days, it is a year in seconds / 12, the leap years will be respected also: >>>> float(str2seconds("1month")) / 60 / 60 / 24 30.4375 >>>> float(str2seconds("1year")) / 60 / 60 / 24 365.25 @returns number (calculated seconds from expression "val") z \1z\1+\2) Ú isinstanceÚintÚfloatÚcomplexrÚ _str2sec_prepÚsubÚ_str2sec_partsÚ _str2sec_finiÚeval)ÚvalZrexpZrplrrr Ú str2seconds•szMyTime.str2secondsc@s(eZdZdZdd„Zdd„Zdd„ZdS) zMyTime.seconds2straËConverts seconds to string on demand (if string representation needed). Ex: seconds2str(86400*390) = 1y 3w 4d seconds2str(86400*368) = 1y 3d seconds2str(86400*365.5) = 1y seconds2str(86400*2+3600*7+60*15) = 2d 7h 15m seconds2str(86400*2+3599) = 2d 1h seconds2str(3600-5) = 1h seconds2str(3600-10) = 59m 50s seconds2str(59) = 59s cCs ||_dS)N)Úsec)Úselfr,rrr Ú__init__½szMyTime.seconds2str.__init__cCs:|j}d}|dkr‚ttt|ƒdƒƒ}t|dƒd}|d;}|dkrb|t|dƒd7}|d;}|rv|t|ƒd7}|dd…S|d krúttt|ƒd ƒƒ}t|d ƒd}|d ;}|d krÚ|t|d ƒd7}|d ;}|rî|t|ƒd7}|dd…S|dk�rxttt|ƒdƒƒ}t|dƒd}|d;}|dk�rV|t|dƒd7}|d;}|�rl|t|ƒd7}|dd…S|dk�ràttt|ƒdƒƒ}t|dƒd}|d;}|dk�rÔ|t|dƒd7}|d;}|dd…Sd}|dk�r |t|dƒd7}|d;}|�r"|t|ƒd7}n |j�s.d}|dd…S)Nri€3ái€Qimzy rzw zd r i€: ié¨rzh ri zm i é ihéÚzs z0 éÿÿÿÿr3r3r3r3)r,r"Úroundr#Ústr)r-ÚsÚcÚrrrr Ú__str__¿sb         zMyTime.seconds2str.__str__cCs|jƒS)N)r9)r-rrr Ú__repr__êszMyTime.seconds2str.__repr__N)Ú__name__Ú __module__Ú __qualname__Ú__doc__r.r9r:rrrr Ú seconds2str²s +r?)N)ri i€Qr/i`'i€: iGiµiøˆi  i€†g~~AgTDAiGiµiøˆi  i€†g~~Ai)r;r<r=r>r rrÚ staticmethodr r r rrrrÚreÚcompiler%r(Z_str2sec_subpartr5r'r+r?rrrr r!s2      r)Ú __author__Z __copyright__Z __license__rrAr rrrrr Ús server/__pycache__/observer.cpython-36.opt-1.pyc000064400000032072152231502150015476 0ustar003 UB(d®Dã@sÀdZdZdZddlZddlmZddlmZddlZddl Z ddl Z ddl Z ddl Z ddl Z ddlZddlZdd lmZdd lmZdd lmZeeƒZGd d „d eƒZGdd„dƒZeƒZdS)zSerg G. Brester (sebres)z"Copyright (c) 2014 Serg G. BresterZGPLéNé)Ú JailThread)ÚFailManagerEmptyé)Ú getLogger)ÚMyTime)ÚUtilscsNeZdZdZejdZ‡fdd„Zdd„Zdd„Zd d „Z d d „Z d d„Z dd„Z dd„Z dd„Zdd„Zdd„Zdd„Zdd„Zdd„Zdd „Zd!d"„ZdGd$d%„Z‡fd&d'„ZdHd*d+„Zed,d-„ƒZdId.d/„ZdJd0d1„Zed2d3„ƒZejd4d3„ƒZed5d6„ƒZd7d8„Z d9d:„Z!d;d<„Z"Gd=d>„d>ƒZ#d?d@„Z$dAdB„Z%dCdD„Z&dEdF„Z'‡Z(S)KÚObserverThreada8Handles observing a database, managing bad ips and ban increment. Parameters ---------- Attributes ---------- daemon ident name status active : bool Control the state of the thread. idle : bool Control the idle state of the thread. sleeptime : int The time the thread sleeps for in the loop. é csZtt|ƒjdd�d|_tjƒ|_g|_tjƒ|_ d|_ i|_ d|_ d|_ d|_d|_dS)Nz f2b/observer)ÚnameTé<Fi)Úsuperr Ú__init__ÚidleÚ threadingÚRLockÚ _queue_lockÚ_queueZEventÚ_notifyÚ sleeptimeÚ_timersÚ_pausedÚ_ObserverThread__dbÚ"_ObserverThread__db_purge_intervalZdaemon)Úself)Ú __class__©ú/usr/lib/python3.6/observer.pyr?s  zObserverThread.__init__c Cs0y |j|Stk r*td|ƒ‚YnXdS)NzInvalid event index : %s)rÚKeyError)rÚirrrÚ __getitem__Ts zObserverThread.__getitem__c Cs2y |j|=Wn tk r,td|ƒ‚YnXdS)NzInvalid event index: %s)rr)rrrrrÚ __delitem__Zs zObserverThread.__delitem__cCs t|jƒS)N)Úiterr)rrrrÚ__iter__`szObserverThread.__iter__cCs t|jƒS)N)Úlenr)rrrrÚ__len__cszObserverThread.__len__cCsdS)NFr)rÚotherrrrÚ__eq__fszObserverThread.__eq__cCst|ƒS)N)Úid)rrrrÚ__hash__iszObserverThread.__hash__cGsD|jj|dƒ}|dk r|jƒtj||j|ƒ}||j|<|jƒdS)zÅAdd a named timer event to queue will start (and wake) in 'starttime' seconds Previous timer event with same name will be canceled and trigger self into queue after new 'starttime' value N)rÚgetZcancelrÚTimerÚaddÚstart)rr Ú starttimeÚeventÚtrrrÚadd_named_timerls  zObserverThread.add_named_timercGs^tjdk rB|rBtjtj|jtjƒ|tjƒ||fƒ}|jƒdStj||j |ƒ}|jƒdS)zJAdd a timer event to queue will start (and wake) in 'starttime' seconds N) rZmyTimerr+rÚDEFAULT_SLEEP_INTERVALÚ _delayedEventÚtimer-r,)rr.r/r0rrrÚ add_timerys zObserverThread.add_timercCsNtjƒ|kstjƒ|kr*|jd|žŽdStjtj|j|||fƒ}|jƒdS)Nr)r) rr4r5rr+rr2r3r-)rZ endMyTimeZendTimer/r0rrrr3ˆs   zObserverThread._delayedEventcCs|js|j}|r|jƒdS)z1Notify wakeup (sets /and resets/ notify event) N)rrÚset)rÚnrrrÚ pulse_notify’szObserverThread.pulse_notifyc Gs*|j�|jj|ƒWdQRX|jƒdS)z5Add a event to queue and notify thread to wake up. N)rrÚappendr8)rr/rrrr,›szObserverThread.addc Gs"|j�|jj|ƒWdQRXdS)z=Add a event to queue withouth notifying thread to wake up. N)rrr9)rr/rrrÚadd_wn£szObserverThread.add_wncGs ||ŽdS)Nr)rÚlÚargsrrrÚ call_lambdaªszObserverThread.call_lambdacCsÚtjdƒ|jd|jdƒ|j|j|j|j|j|j |j dd„dd„dœ }�y8|j dƒ�x|j �rfd |_ x´|j�syrd }|j�t|jƒr–|jjd ƒ}Wd QRX|d krªP|d }t|d ƒsÒ|j|ƒpÐt||ƒ}||d d …ŽWqjtk �r}ztjd |dd�WYd d }~XqjXqjW|j}|�rPd|_ |j|jƒ|jƒ|j�rdqZqZtjtjƒ|j sZPqZWtjdt|jƒƒd |_Wn4tk �r¶}ztjd|dd�WYd d }~XnX|j� g|_Wd QRXd|_ dS)zŽMain loop for Threading. This function is the main loop of the thread. Returns ------- bool True when the thread exits nicely. zObserver start...ÚDB_PURGEÚdb_purgecSsfS)NrrrrrÚÆsz$ObserverThread.run..cSsfS)Nrrrrrr@Çs) ZcallÚdb_setr?Úis_aliveZ is_activer-ÚstopÚnopÚshutdownrBFNrrz%sT)Úexc_infoz&Observer stopped, %s events remaining.z Observer stopped after error: %s)!ÚlogSysÚinfor1rr=rAr?ÚisAliveÚisActiver-rCr,Úactiverrrr$rÚpopÚcallabler*ÚgetattrÚ ExceptionÚerrorrÚwaitrÚclearr4Úsleepr r2Úis_full)rZ_ObserverThread__methZevÚmethÚer7rrrÚrun­s^       &   "zObserverThread.runcCsdS)NTr)rrrrrIýszObserverThread.isAliveNcCs|jS)N)rK)rZfromStrrrrrJszObserverThread.isActivec s*|j�|jstt|ƒjƒWdQRXdS)N)rrKr r r-)r)rrrr-szObserverThread.startéTc Cs–|jr’|jr’tjd|ƒ|j�&|jdƒ|j}|jjƒd|_WdQRX|j|ƒsX|rt|jƒd|_d|_ d|_ n||_|j t |dƒƒo�|j SdS)Nz-Observer stop ... try to end queue %s secondsrEFTgà?)rKrrGrHrr:r6Ú wait_emptyrRrrÚ wait_idleÚminrT)rZwtimeZ forceQuitr7rrrrC s    zObserverThread.stopc Cs$|j�t|jƒrdSdSQRXdS)NTF)rr$r)rrrrrT#szObserverThread.is_fullcCsˆtjtjƒ|dk r tjƒ|}|jdk rH|jdƒ|jrH|jrH|j ƒx,|jrt|dk rftjƒ|krfPtjtjƒqJW|j dƒ|j S)zWWait observer is running and returns if observer has no more events (queue is empty) NrDgü©ñÒMbP?) r4rSr r2rrr:rTrr8rZ)rrrVrrrrY(s      zObserverThread.wait_emptycCs^tjtjƒ|jrdS|dk r*tjƒ|}x,|jsV|dk rHtjƒ|krHPtjtjƒq,W|jS)zJWait observer is running and returns if observer idle (observer sleeps) TN)r4rSr r2rr)rrrVrrrrZ<s  zObserverThread.wait_idlecCs|jS)N)r)rrrrÚpausedJszObserverThread.pausedcCs |j|krdS||_|jƒdS)N)rr8)rÚpauserrrr\Ns cCsdS)z/Status of observer to be implemented. [TODO] Ú)r^r^r)rrrrÚstatusWszObserverThread.statuscCs ||_dS)N)r)rÚdbrrrrAaszObserverThread.db_setcCs2tjdƒ|jdk r|jjƒ|jd|jdƒdS)NzPurge database event occurredr>r?)rGÚdebugrÚpurger1r)rrrrr?ds   zObserverThread.db_purgec Cs’|jƒ s|jdƒ rdS|jƒ}|jƒ}tjd|j|ƒd}d}d}�y|jjj ƒ}|j } | dk rÞxF| j ||ƒD]6\}}} t ||j ƒƒ}d|dkr˜|nd>dd}PqrWt||ƒ}|dk rÞ||krÞtjd|j|||ƒdS|dkrêdStjd |j|tj|ƒ||||k�rd nd ƒ|jjj||dd ƒ}|j|ƒ||k�rL|jj|ƒWn>tk �rŒ} z tjd | tjƒtjkd�WYdd} ~ XnXdS)z} Notify observer a failure for ip was found Observer will check ip was known (bad) and possibly increase an retry count Ú incrementNz[%s] Observer: failure found %srrérz8[%s] Ignore failure %s before last ban %s < %s, restoredz%[%s] Found %s, bad - %s, %s # -> %s%sz, Banr^Tz%s)rF)rIÚgetBanTimeExtraÚgetIDÚgetTimerGrar ÚfilterZ failManagerZ getMaxRetryÚdatabaseÚgetBanÚmaxÚ getBanCountr[rHrÚtime2strZ addFailureÚ setBanCountZ performBanrOrPÚgetEffectiveLevelÚloggingÚDEBUG) rÚjailÚticketÚipZunixTimeÚbanCountZ retryCountÚ timeOfBanZmaxRetryr`Ú lastBanTimerVrrrÚ failureFoundos@      zObserverThread.failureFoundc@seZdZdd„ZdS)zObserverThread.BanTimeIncrcCs||_||_dS)N)ZTimeZCount)rÚbanTimerurrrr¢sz#ObserverThread.BanTimeIncr.__init__N)Ú__name__Ú __module__Ú __qualname__rrrrrÚ BanTimeIncr¡sr}cCs|jƒ}|d|j||ƒƒS)NÚ evformula)rer})rrrryruÚberrrÚ calcBanTime¦szObserverThread.calcBanTimec CsD|jƒ s|j r|S|jƒ}|jƒ}|}yÖ|dkrþ|jddƒrþx¼|jj|||jddƒd�D]ž\}}} ||jƒkr€|j|dƒtj d|||ƒ|dkr¬|d|j ||ƒƒ}|j |ƒ|j ƒ|kròtj d |j||tj|ƒtj|ƒtj|ƒfƒnd |_Pq\WWn>tk �r>} z tjd | tjƒtjkd �WYd d } ~ XnX|S)ztCheck for IP address to increment ban time (if was already banned). Returns ------- float new ban time. rrcFÚ overalljails)r�rz"IP %s was already banned: %s #, %sr~z/[%s] IP %s is bad: %s # last %s - incr %s to %sTz%s)rFN)rIrirerfr*rjrlrnrGrar}Ú setBanTimergrHr rrmÚ seconds2strÚrestoredrOrProrprq) rrrryrsrrtZ orgBanTimerurvrwrVrrrÚ incrBanTimeªs0 $    ,zObserverThread.incrBanTimec Cs„|jr dS�y4|}|jƒ}tjd|j||ƒ|dkrh|jƒdkrh|j|||ƒ}|dks^||krh|j|ƒ|dkr²|jƒ|}t j |ƒt j |ƒf}|t j ƒkr¶tjd|dƒdSnd}||k�rtj d|j||jƒf|žŽtjd d |j|||fƒ|jtd td ||d ƒƒ|j||ƒ|jdk �r>|j �r>|jj||ƒWn>tk �r~}z tjd |tjƒtjkd�WYdd}~XnXdS)zÀ Notify observer a ban occured for ip Observer will check ip was known (bad) and possibly increase/prolong a ban time Secondary we will actualize the bans and bips (bad ip) in database Nz[%s] Observer: ban found %s, %srzIgnore old bantime %sFÚ permanentÚinfinitez$[%s] Increase Ban %s (%d # %s -> %s)rXz[%s] Observer: prolong %s in %sr rz%s)rFéÿÿÿÿrˆrˆ)r†r‡)r„rfrGrar Ú getBanTimer…r‚rgrrƒrmr4ZnoticerlÚlogr5r[rkÚ prolongBanriZaddBanrOrProrprq) rrsrrÚbtimeZoldbtimertZbendtimeZlogtimerVrrrÚbanFoundÓs4     &zObserverThread.banFoundcCsty2|jƒ}|jƒ}tjd|j||ƒ|jj|ƒWn<tk rn}z tjd|tj ƒt j kd�WYdd}~XnXdS)zÀ Notify observer a ban occured for ip Observer will check ip was known (bad) and possibly increase/prolong a ban time Secondary we will actualize the bans and bips (bad ip) in database z[%s] Observer: prolong %s, %sz%s)rFN) r‰rfrGrar ZactionsZ _prolongBanrOrProrprq)rrsrrrŒrtrVrrrr‹ýszObserverThread.prolongBan)N)rXT)N)N))rzr{r|Ú__doc__rr2rr r!r#r%r'r)r1r5r3r8r,r:r=rWrIrJr-rCÚpropertyrTrYrZr\Úsetterr_rAr?rxr}r€r…r�r‹Ú __classcell__rr)rrr (sF     P         2)*r c@seZdZdd„ZdS)Ú _ObserverscCs d|_dS)N)ZMain)rrrrrsz_Observers.__init__N)rzr{r|rrrrrr’ sr’)Ú __author__Z __copyright__Z __license__rZ jailthreadrZ failmanagerrÚosrpr4ZdatetimeZmathZjsonZrandomÚsysZhelpersrZmytimerZutilsrrzrGr r’Z ObserversrrrrÚs"  8   hserver/__pycache__/observer.cpython-36.pyc000064400000032072152231502150014537 0ustar003 UB(d®Dã@sÀdZdZdZddlZddlmZddlmZddlZddl Z ddl Z ddl Z ddl Z ddl Z ddlZddlZdd lmZdd lmZdd lmZeeƒZGd d „d eƒZGdd„dƒZeƒZdS)zSerg G. Brester (sebres)z"Copyright (c) 2014 Serg G. BresterZGPLéNé)Ú JailThread)ÚFailManagerEmptyé)Ú getLogger)ÚMyTime)ÚUtilscsNeZdZdZejdZ‡fdd„Zdd„Zdd„Zd d „Z d d „Z d d„Z dd„Z dd„Z dd„Zdd„Zdd„Zdd„Zdd„Zdd„Zdd „Zd!d"„ZdGd$d%„Z‡fd&d'„ZdHd*d+„Zed,d-„ƒZdId.d/„ZdJd0d1„Zed2d3„ƒZejd4d3„ƒZed5d6„ƒZd7d8„Z d9d:„Z!d;d<„Z"Gd=d>„d>ƒZ#d?d@„Z$dAdB„Z%dCdD„Z&dEdF„Z'‡Z(S)KÚObserverThreada8Handles observing a database, managing bad ips and ban increment. Parameters ---------- Attributes ---------- daemon ident name status active : bool Control the state of the thread. idle : bool Control the idle state of the thread. sleeptime : int The time the thread sleeps for in the loop. é csZtt|ƒjdd�d|_tjƒ|_g|_tjƒ|_ d|_ i|_ d|_ d|_ d|_d|_dS)Nz f2b/observer)ÚnameTé<Fi)Úsuperr Ú__init__ÚidleÚ threadingÚRLockÚ _queue_lockÚ_queueZEventÚ_notifyÚ sleeptimeÚ_timersÚ_pausedÚ_ObserverThread__dbÚ"_ObserverThread__db_purge_intervalZdaemon)Úself)Ú __class__©ú/usr/lib/python3.6/observer.pyr?s  zObserverThread.__init__c Cs0y |j|Stk r*td|ƒ‚YnXdS)NzInvalid event index : %s)rÚKeyError)rÚirrrÚ __getitem__Ts zObserverThread.__getitem__c Cs2y |j|=Wn tk r,td|ƒ‚YnXdS)NzInvalid event index: %s)rr)rrrrrÚ __delitem__Zs zObserverThread.__delitem__cCs t|jƒS)N)Úiterr)rrrrÚ__iter__`szObserverThread.__iter__cCs t|jƒS)N)Úlenr)rrrrÚ__len__cszObserverThread.__len__cCsdS)NFr)rÚotherrrrÚ__eq__fszObserverThread.__eq__cCst|ƒS)N)Úid)rrrrÚ__hash__iszObserverThread.__hash__cGsD|jj|dƒ}|dk r|jƒtj||j|ƒ}||j|<|jƒdS)zÅAdd a named timer event to queue will start (and wake) in 'starttime' seconds Previous timer event with same name will be canceled and trigger self into queue after new 'starttime' value N)rÚgetZcancelrÚTimerÚaddÚstart)rr Ú starttimeÚeventÚtrrrÚadd_named_timerls  zObserverThread.add_named_timercGs^tjdk rB|rBtjtj|jtjƒ|tjƒ||fƒ}|jƒdStj||j |ƒ}|jƒdS)zJAdd a timer event to queue will start (and wake) in 'starttime' seconds N) rZmyTimerr+rÚDEFAULT_SLEEP_INTERVALÚ _delayedEventÚtimer-r,)rr.r/r0rrrÚ add_timerys zObserverThread.add_timercCsNtjƒ|kstjƒ|kr*|jd|žŽdStjtj|j|||fƒ}|jƒdS)Nr)r) rr4r5rr+rr2r3r-)rZ endMyTimeZendTimer/r0rrrr3ˆs   zObserverThread._delayedEventcCs|js|j}|r|jƒdS)z1Notify wakeup (sets /and resets/ notify event) N)rrÚset)rÚnrrrÚ pulse_notify’szObserverThread.pulse_notifyc Gs*|j�|jj|ƒWdQRX|jƒdS)z5Add a event to queue and notify thread to wake up. N)rrÚappendr8)rr/rrrr,›szObserverThread.addc Gs"|j�|jj|ƒWdQRXdS)z=Add a event to queue withouth notifying thread to wake up. N)rrr9)rr/rrrÚadd_wn£szObserverThread.add_wncGs ||ŽdS)Nr)rÚlÚargsrrrÚ call_lambdaªszObserverThread.call_lambdacCsÚtjdƒ|jd|jdƒ|j|j|j|j|j|j |j dd„dd„dœ }�y8|j dƒ�x|j �rfd |_ x´|j�syrd }|j�t|jƒr–|jjd ƒ}Wd QRX|d krªP|d }t|d ƒsÒ|j|ƒpÐt||ƒ}||d d …ŽWqjtk �r}ztjd |dd�WYd d }~XqjXqjW|j}|�rPd|_ |j|jƒ|jƒ|j�rdqZqZtjtjƒ|j sZPqZWtjdt|jƒƒd |_Wn4tk �r¶}ztjd|dd�WYd d }~XnX|j� g|_Wd QRXd|_ dS)zŽMain loop for Threading. This function is the main loop of the thread. Returns ------- bool True when the thread exits nicely. zObserver start...ÚDB_PURGEÚdb_purgecSsfS)NrrrrrÚÆsz$ObserverThread.run..cSsfS)Nrrrrrr@Çs) ZcallÚdb_setr?Úis_aliveZ is_activer-ÚstopÚnopÚshutdownrBFNrrz%sT)Úexc_infoz&Observer stopped, %s events remaining.z Observer stopped after error: %s)!ÚlogSysÚinfor1rr=rAr?ÚisAliveÚisActiver-rCr,Úactiverrrr$rÚpopÚcallabler*ÚgetattrÚ ExceptionÚerrorrÚwaitrÚclearr4Úsleepr r2Úis_full)rZ_ObserverThread__methZevÚmethÚer7rrrÚrun­s^       &   "zObserverThread.runcCsdS)NTr)rrrrrIýszObserverThread.isAliveNcCs|jS)N)rK)rZfromStrrrrrJszObserverThread.isActivec s*|j�|jstt|ƒjƒWdQRXdS)N)rrKr r r-)r)rrrr-szObserverThread.startéTc Cs–|jr’|jr’tjd|ƒ|j�&|jdƒ|j}|jjƒd|_WdQRX|j|ƒsX|rt|jƒd|_d|_ d|_ n||_|j t |dƒƒo�|j SdS)Nz-Observer stop ... try to end queue %s secondsrEFTgà?)rKrrGrHrr:r6Ú wait_emptyrRrrÚ wait_idleÚminrT)rZwtimeZ forceQuitr7rrrrC s    zObserverThread.stopc Cs$|j�t|jƒrdSdSQRXdS)NTF)rr$r)rrrrrT#szObserverThread.is_fullcCsˆtjtjƒ|dk r tjƒ|}|jdk rH|jdƒ|jrH|jrH|j ƒx,|jrt|dk rftjƒ|krfPtjtjƒqJW|j dƒ|j S)zWWait observer is running and returns if observer has no more events (queue is empty) NrDgü©ñÒMbP?) r4rSr r2rrr:rTrr8rZ)rrrVrrrrY(s      zObserverThread.wait_emptycCs^tjtjƒ|jrdS|dk r*tjƒ|}x,|jsV|dk rHtjƒ|krHPtjtjƒq,W|jS)zJWait observer is running and returns if observer idle (observer sleeps) TN)r4rSr r2rr)rrrVrrrrZ<s  zObserverThread.wait_idlecCs|jS)N)r)rrrrÚpausedJszObserverThread.pausedcCs |j|krdS||_|jƒdS)N)rr8)rÚpauserrrr\Ns cCsdS)z/Status of observer to be implemented. [TODO] Ú)r^r^r)rrrrÚstatusWszObserverThread.statuscCs ||_dS)N)r)rÚdbrrrrAaszObserverThread.db_setcCs2tjdƒ|jdk r|jjƒ|jd|jdƒdS)NzPurge database event occurredr>r?)rGÚdebugrÚpurger1r)rrrrr?ds   zObserverThread.db_purgec Cs’|jƒ s|jdƒ rdS|jƒ}|jƒ}tjd|j|ƒd}d}d}�y|jjj ƒ}|j } | dk rÞxF| j ||ƒD]6\}}} t ||j ƒƒ}d|dkr˜|nd>dd}PqrWt||ƒ}|dk rÞ||krÞtjd|j|||ƒdS|dkrêdStjd |j|tj|ƒ||||k�rd nd ƒ|jjj||dd ƒ}|j|ƒ||k�rL|jj|ƒWn>tk �rŒ} z tjd | tjƒtjkd�WYdd} ~ XnXdS)z} Notify observer a failure for ip was found Observer will check ip was known (bad) and possibly increase an retry count Ú incrementNz[%s] Observer: failure found %srrérz8[%s] Ignore failure %s before last ban %s < %s, restoredz%[%s] Found %s, bad - %s, %s # -> %s%sz, Banr^Tz%s)rF)rIÚgetBanTimeExtraÚgetIDÚgetTimerGrar ÚfilterZ failManagerZ getMaxRetryÚdatabaseÚgetBanÚmaxÚ getBanCountr[rHrÚtime2strZ addFailureÚ setBanCountZ performBanrOrPÚgetEffectiveLevelÚloggingÚDEBUG) rÚjailÚticketÚipZunixTimeÚbanCountZ retryCountÚ timeOfBanZmaxRetryr`Ú lastBanTimerVrrrÚ failureFoundos@      zObserverThread.failureFoundc@seZdZdd„ZdS)zObserverThread.BanTimeIncrcCs||_||_dS)N)ZTimeZCount)rÚbanTimerurrrr¢sz#ObserverThread.BanTimeIncr.__init__N)Ú__name__Ú __module__Ú __qualname__rrrrrÚ BanTimeIncr¡sr}cCs|jƒ}|d|j||ƒƒS)NÚ evformula)rer})rrrryruÚberrrÚ calcBanTime¦szObserverThread.calcBanTimec CsD|jƒ s|j r|S|jƒ}|jƒ}|}yÖ|dkrþ|jddƒrþx¼|jj|||jddƒd�D]ž\}}} ||jƒkr€|j|dƒtj d|||ƒ|dkr¬|d|j ||ƒƒ}|j |ƒ|j ƒ|kròtj d |j||tj|ƒtj|ƒtj|ƒfƒnd |_Pq\WWn>tk �r>} z tjd | tjƒtjkd �WYd d } ~ XnX|S)ztCheck for IP address to increment ban time (if was already banned). Returns ------- float new ban time. rrcFÚ overalljails)r�rz"IP %s was already banned: %s #, %sr~z/[%s] IP %s is bad: %s # last %s - incr %s to %sTz%s)rFN)rIrirerfr*rjrlrnrGrar}Ú setBanTimergrHr rrmÚ seconds2strÚrestoredrOrProrprq) rrrryrsrrtZ orgBanTimerurvrwrVrrrÚ incrBanTimeªs0 $    ,zObserverThread.incrBanTimec Cs„|jr dS�y4|}|jƒ}tjd|j||ƒ|dkrh|jƒdkrh|j|||ƒ}|dks^||krh|j|ƒ|dkr²|jƒ|}t j |ƒt j |ƒf}|t j ƒkr¶tjd|dƒdSnd}||k�rtj d|j||jƒf|žŽtjd d |j|||fƒ|jtd td ||d ƒƒ|j||ƒ|jdk �r>|j �r>|jj||ƒWn>tk �r~}z tjd |tjƒtjkd�WYdd}~XnXdS)zÀ Notify observer a ban occured for ip Observer will check ip was known (bad) and possibly increase/prolong a ban time Secondary we will actualize the bans and bips (bad ip) in database Nz[%s] Observer: ban found %s, %srzIgnore old bantime %sFÚ permanentÚinfinitez$[%s] Increase Ban %s (%d # %s -> %s)rXz[%s] Observer: prolong %s in %sr rz%s)rFéÿÿÿÿrˆrˆ)r†r‡)r„rfrGrar Ú getBanTimer…r‚rgrrƒrmr4ZnoticerlÚlogr5r[rkÚ prolongBanriZaddBanrOrProrprq) rrsrrÚbtimeZoldbtimertZbendtimeZlogtimerVrrrÚbanFoundÓs4     &zObserverThread.banFoundcCsty2|jƒ}|jƒ}tjd|j||ƒ|jj|ƒWn<tk rn}z tjd|tj ƒt j kd�WYdd}~XnXdS)zÀ Notify observer a ban occured for ip Observer will check ip was known (bad) and possibly increase/prolong a ban time Secondary we will actualize the bans and bips (bad ip) in database z[%s] Observer: prolong %s, %sz%s)rFN) r‰rfrGrar ZactionsZ _prolongBanrOrProrprq)rrsrrrŒrtrVrrrr‹ýszObserverThread.prolongBan)N)rXT)N)N))rzr{r|Ú__doc__rr2rr r!r#r%r'r)r1r5r3r8r,r:r=rWrIrJr-rCÚpropertyrTrYrZr\Úsetterr_rAr?rxr}r€r…r�r‹Ú __classcell__rr)rrr (sF     P         2)*r c@seZdZdd„ZdS)Ú _ObserverscCs d|_dS)N)ZMain)rrrrrsz_Observers.__init__N)rzr{r|rrrrrr’ sr’)Ú __author__Z __copyright__Z __license__rZ jailthreadrZ failmanagerrÚosrpr4ZdatetimeZmathZjsonZrandomÚsysZhelpersrZmytimerZutilsrrzrGr r’Z ObserversrrrrÚs"  8   hserver/__pycache__/server.cpython-36.opt-1.pyc000064400000061660152231502150015162 0ustar003 UB(dwã@s^dZdZdZddlZddlmZmZddlZddlZddlZddl Z ddl Z ddl m Z m Z ddlmZdd lmZmZmZdd lmZdd lmZmZd d lmZd dlmZmZmZmZm Z m!Z!m"Z"ee#ƒZ$dZ%dZ&dZ'yddl(m)Z)Wne*k �rdZ)YnXdd„Z+ye,Wne-k �r2e.Z,YnXdd„Z/Gdd„dƒZ0Gdd„de1ƒZ2dS)z Cyril Jaquierz Copyright (c) 2004 Cyril JaquierZGPLéN)ÚLockÚRLocké)Ú ObserversÚObserverThread)ÚJails)ÚDNSUtilsÚ FileFilterÚ JournalFilter)Ú Transmitter)Ú AsyncServerÚAsyncServerExceptioné)Úversion)Ú getLoggerÚ_as_boolÚextractOptionsÚ str2LogLevelÚgetVerbosityFormatÚ excepthookÚprctl_set_th_nameÚautoÚINFOÚSTDOUT)Ú Fail2BanDbcCs tjƒjjS)N)Ú threadingZcurrent_threadÚ __class__Ú__name__©rrú/usr/lib/python3.6/server.pyÚ _thread_name:sr cCs^tjj|ƒ}tjj|ƒrZytj|ƒWn2ttfk rX}z|jdkrH‚WYdd}~XnXdS)z0Creates path of file (last level only) on demandéN)ÚosÚpathÚdirnameÚisabsÚmkdirÚOSErrorÚFileExistsErrorÚerrno)ÚnameÚerrrÚ_make_file_pathBs   r,c@sÚeZdZd¯dd„Zdd„Zdd„Zdd „Zdd ifd d „Zd d„Zdd„Z d°dd„Z dd„Z dd„Z dd„Z dd„Zdd„Zdd„Zdd „Zd!d"„Zd#d$„Zd%d&„Zd'd(„Zd)d*„Zd±d+d,„Zd-d.„Zd/d0„Zd1d2„Zd3d4„Zd5d6„Zd7d8„Zd9d:„Zd;d<„Zd=d>„Z d?d@„Z!dAdB„Z"dCdD„Z#dEdF„Z$dGdH„Z%dIdJ„Z&dKdL„Z'dMdN„Z(dOdP„Z)dQdR„Z*d²dSdT„Z+d³dVdW„Z,dXdY„Z-d´dZd[„Z.d\d]„Z/d^d_„Z0d`da„Z1dbdc„Z2ddde„Z3dfdg„Z4dhdi„Z5djdk„Z6dldm„Z7dndo„Z8dpdq„Z9drds„Z:dtdu„Z;dvdw„Zd|d}„Z?dµd~d„Z@d¶d€d�„ZAd‚dƒ„ZBd·d„d…„ZCd†d‡„ZDdˆd‰„ZEdŠd‹„ZFd¸dŒd�„ZGdŽd�„ZHd¹d‘d’„ZId“d”„ZJd•d–„ZKd—d˜„ZLd™dš„ZMd›dœ„ZNd�dž„ZOdŸd „ZPeQd¡d¢„ƒZRd£d¤„ZSd¥d¦„ZTd§d¨„ZUd©dª„ZVeQd«d¬„ƒZWd­d®„ZXdUS)ºÚServerFcCsjtƒ|_tƒ|_tƒ|_d|_||_t|ƒ|_ i|_ d|_ d|_ d|_ d|_d|_ddddœ|_i|_dS)Nz/var/run/syslogz /var/run/logz/dev/log)ZDarwinZFreeBSDZLinux)rÚ_Server__loggingLockrÚ _Server__lockrÚ_Server__jailsÚ _Server__dbÚ_Server__daemonr Ú_Server__transmÚ_Server__reload_stateÚ_Server__asyncServerÚ_Server__logLevelÚ_Server__logTargetÚ_Server__verboseÚ_Server__syslogSocketÚ_Server__autoSyslogSocketPathsÚ_Server__prev_signals)ÚselfZdaemonrrrÚ__init__Qs   zServer.__init__cCstjd|ƒ|jƒdS)NzCaught signal %d. Exiting)ÚlogSysÚdebugÚquit)r<ÚsignumÚframerrrZ__sigTERMhandlerfs zServer.__sigTERMhandlercCstjd|ƒ|jƒdS)NzCaught signal %d. Flushing logs)r>r?Ú flushLogs)r<rAZfnamerrrZ__sigUSR1handlerjs zServer.__sigUSR1handlercCs tj|ƒ|j|<tj||ƒdS)z>Bind new signal handler while storing old one in _prev_signalsN)ÚsignalÚ getsignalr;)r<ÚsÚnewrrrÚ _rebindSignalnszServer._rebindSignalTc ,Cs´tjdƒ|jrXtjdƒ|jƒ}|dkr.dS|dsXd|dd…f}tj|ƒt|ƒ‚t|j ddƒƒ|j d dƒ|_ |j |j d |j dk r�|j nt ƒƒ|j|j d |jdk r²|jntƒƒ|j|j d |jdk rÔ|jntƒƒtjd dƒtjdtjƒ|j�r tjdƒtƒdk�rLx$tjtjfD]}|j||jƒ�q$W|jtj|jƒtt_yÚinfoÚ_Server__createDaemonÚerrorÚServerInitializationErrorrÚgetr8ÚsetSyslogSocketr9ÚDEF_SYSLOGSOCKETÚ setLogLevelr6Ú DEF_LOGLEVELÚ setLogTargetr7Ú DEF_LOGTARGETrr rDÚSIGTERMÚSIGINTrHÚ_Server__sigTERMhandlerÚSIGUSR1Ú_Server__sigUSR1handlerrÚsysr?r,ÚopenÚwriteÚgetpidÚcloser'ÚIOErrorrÚMainrÚstartr r3r5rPr r@Úremove) r<ZsockZpidfileÚforceÚobserverZconfÚretÚerrrFZpidFiler+rrrrissj                z Server.startcCsÐdd„|_tjdƒ|jdk r(|jjƒtƒdkrVx"|jjƒD]\}}tj||ƒq>Wt j }|dk rz|j dd�rtd}dt _ |j ƒ|dk r’|j ƒ|j r¨|j jƒd|_ |jdk rÂ|jj ƒd|_tjdƒdS)NcSsdS)NFrrrrrÚÅszServer.quit..zShutdown in progress...rNF)Z forceQuitzExiting Fail2ban)r@r>rRr5Zstop_communicationr r;ÚitemsrDrrhÚstopÚ stopAllJailr1rf)r<rFZshZobsMainrrrr@Ãs,         z Server.quitcCsªd}|jj|ƒrt|jj|ƒrt|j|}|j|krLd}tjd|ƒd|j|<n(tjd||j|ƒ|j|dd�|j|=|rŠ|jj|||j ƒ|j dk r¦|j j |j|ƒdS)NTFzReload jail %rz"Restart jail %r (reason: %r != %r))rq) r4rVr0ÚexistsÚbackendr>rRÚdelJailÚaddr1ÚaddJail)r<r*rtZaddflgÚjailrrrrwòs     zServer.addJailcCsJ|j|}|s|jƒr$|j||d�|rF|jdk r>|jj|ƒ|j|=dS)N)rqÚjoin)r0ÚisAliverqr1ru)r<r*rqryrxrrrrus    zServer.delJailc Cs\|j�L|j|}|jƒs$|jƒn||jkrBtjd|ƒ|j|=|jrNd|_WdQRXdS)NzJail %r reloadedF)r/r0rzrir4r>rRÚidle)r<r*rxrrrÚ startJails    zServer.startJailc Cs$|j�|j|dd�WdQRXdS)NT)rq)r/ru)r<r*rrrÚstopJailszServer.stopJailc Csptjdƒ|j�Vx&t|jjƒƒD]}|j|ddd�q"Wx&t|jjƒƒD]}|j|ddd�qJWWdQRXdS)NzStopping all jailsTF)rqry)r>rRr/Úlistr0Úkeysru)r<r*rrrrrs  zServer.stopAllJailcCstjjƒtjjƒdS)N)rZCACHE_nameToIpÚclearZCACHE_ipToName)r<rrrÚ clearCaches(s zServer.clearCachesc Cs¼|�r*|jr(|dks |jj|ƒr(tdƒ‚tjd|dkr>d|ndƒ|j�Ö|dkr¢d}d|ksn|jj|ƒrx|j|}|rÊd|krŽ|j|ƒd|krÊ|j |ƒn(|j ƒd|krº|jƒd|krÊ|j ƒxR|jj ƒD]D\}}|dksî||krÖd |_ ||j|<|jjd d �|jjd d �qÖWWdQRXnŽ|j�rg}xL|jj ƒD]>\}}||jk�rb|j|ƒn|jjd d �|jjd d ��qBWx|D]}|j|ƒ�qŠWWdQRXi|_tjd ƒdS) Nz--allzReload already in progresszReload zjail %sz all jailsz --if-existsz--unbanz --restartT)ÚbeginFzReload finished.)r4rVÚ ValueErrorr>rRr/r0rsÚ setUnbanIPr}r�rrrpr{ÚfilterÚreloadÚactionsÚappendru)r<r*Zoptsr‚rxZjnZdeljailsrrrÚ reloadJails-sJ        zServer.reloadJailscCs||j|_dS)NT)r0r{)r<r*ÚvaluerrrÚ setIdleJailds zServer.setIdleJailcCs |j|jS)N)r0r{)r<r*rrrÚ getIdleJailhszServer.getIdleJailcCst|ƒ|j|j_dS)N)rr0r…Ú ignoreSelf)r<r*rŠrrrÚ setIgnoreSelflszServer.setIgnoreSelfcCs|j|jjS)N)r0r…r�)r<r*rrrÚ getIgnoreSelfoszServer.getIgnoreSelfcCs|j|jj|ƒdS)N)r0r…Ú addIgnoreIP)r<r*Úiprrrr�rszServer.addIgnoreIPcCs|j|jj|ƒdS)N)r0r…Ú delIgnoreIP)r<r*r‘rrrr’uszServer.delIgnoreIPcCs|j|jjƒS)N)r0r…Ú getIgnoreIP)r<r*rrrr“xszServer.getIgnoreIPcCs&|j|j}t|tƒr"|j||ƒdS)N)r0r…Ú isinstancer Ú addLogPath)r<r*ÚfileNameÚtailÚfilter_rrrr•{s  zServer.addLogPathcCs$|j|j}t|tƒr |j|ƒdS)N)r0r…r”r Ú delLogPath)r<r*r–r˜rrrr™€s  zServer.delLogPathcCs4|j|j}t|tƒr|jƒStjd|ƒgSdS)Nz$Jail %s is not a FileFilter instance)r0r…r”r Z getLogPathsr>r?)r<r*r˜rrrÚ getLogPath…s   zServer.getLogPathcCs$|j|j}t|tƒr |j|ƒdS)N)r0r…r”r ÚaddJournalMatch)r<r*Úmatchr˜rrrr›�s  zServer.addJournalMatchcCs$|j|j}t|tƒr |j|ƒdS)N)r0r…r”r ÚdelJournalMatch)r<r*rœr˜rrrr�’s  zServer.delJournalMatchcCs4|j|j}t|tƒr|jƒStjd|ƒgSdS)Nz'Jail %s is not a JournalFilter instance)r0r…r”r ÚgetJournalMatchr>r?)r<r*r˜rrrrž—s   zServer.getJournalMatchcCs|j|j}|j|ƒdS)N)r0r…ÚsetLogEncoding)r<r*Úencodingr˜rrrrŸŸs zServer.setLogEncodingcCs|j|j}|jƒS)N)r0r…ÚgetLogEncoding)r<r*r˜rrrr¡£s zServer.getLogEncodingcCs|j|jj|ƒdS)N)r0r…Ú setFindTime)r<r*rŠrrrr¢§szServer.setFindTimecCs|j|jjƒS)N)r0r…Ú getFindTime)r<r*rrrr£ªszServer.getFindTimecCs|j|jj|ƒdS)N)r0r…ÚsetDatePattern)r<r*Úpatternrrrr¤­szServer.setDatePatterncCs|j|jjƒS)N)r0r…ÚgetDatePattern)r<r*rrrr¦°szServer.getDatePatterncCs|j|jj|ƒdS)N)r0r…ÚsetLogTimeZone)r<r*Ztzrrrr§³szServer.setLogTimeZonecCs|j|jjƒS)N)r0r…ÚgetLogTimeZone)r<r*rrrr¨¶szServer.getLogTimeZonecCs||j|j_dS)N)r0r…Ú ignoreCommand)r<r*rŠrrrÚsetIgnoreCommand¹szServer.setIgnoreCommandcCs|j|jjS)N)r0r…r©)r<r*rrrÚgetIgnoreCommand¼szServer.getIgnoreCommandcCs&td|dƒ\}}||j|j_dS)Nzcache[ú])rr0r…Ú ignoreCache)r<r*rŠZoptionsrrrÚsetIgnoreCache¿szServer.setIgnoreCachecCs|j|jjS)N)r0r…r­)r<r*rrrÚgetIgnoreCacheÃszServer.getIgnoreCachecCs"|j|j}tjd|ƒ||_dS)Nz prefregex: %r)r0r…r>r?Ú prefRegex)r<r*rŠÚfltrrrÚ setPrefRegexÆs  zServer.setPrefRegexcCs|j|jjS)N)r0r…r°)r<r*rrrÚ getPrefRegexËszServer.getPrefRegexcCs>|j|j}|s|f}x"|D]}tjd|ƒ|j|ƒqWdS)Nz failregex: %r)r0r…r>r?Ú addFailRegex)r<r*rŠÚmultipler±rrrr´Îs    zServer.addFailRegexNcCs|j|jj|ƒdS)N)r0r…Ú delFailRegex)r<r*Úindexrrrr¶ÕszServer.delFailRegexcCs|j|jjƒS)N)r0r…Ú getFailRegex)r<r*rrrr¸ØszServer.getFailRegexcCs>|j|j}|s|f}x"|D]}tjd|ƒ|j|ƒqWdS)Nz ignoreregex: %r)r0r…r>r?ÚaddIgnoreRegex)r<r*rŠrµr±rrrr¹Ûs    zServer.addIgnoreRegexcCs|j|jj|ƒdS)N)r0r…ÚdelIgnoreRegex)r<r*r·rrrrºâszServer.delIgnoreRegexcCs|j|jjƒS)N)r0r…ÚgetIgnoreRegex)r<r*rrrr»åszServer.getIgnoreRegexcCs|j|jj|ƒdS)N)r0r…Ú setUseDns)r<r*rŠrrrr¼èszServer.setUseDnscCs|j|jjƒS)N)r0r…Ú getUseDns)r<r*rrrr½ëszServer.getUseDnscCs||j|jj_dS)N)r0r…Ú failManagerÚ maxMatches)r<r*rŠrrrÚ setMaxMatchesîszServer.setMaxMatchescCs|j|jjjS)N)r0r…r¾r¿)r<r*rrrÚ getMaxMatchesñszServer.getMaxMatchescCs|j|jj|ƒdS)N)r0r…Ú setMaxRetry)r<r*rŠrrrrÂôszServer.setMaxRetrycCs|j|jjƒS)N)r0r…Ú getMaxRetry)r<r*rrrrÃ÷szServer.getMaxRetrycCs|j|jj|ƒdS)N)r0r…Ú setMaxLines)r<r*rŠrrrrÄúszServer.setMaxLinescCs|j|jjƒS)N)r0r…Ú getMaxLines)r<r*rrrrÅýszServer.getMaxLinescGs(|j|jj|f|žd||jkiŽdS)Nr†)r0r‡rvr4)r<r*rŠÚargsrrrÚ addActionszServer.addActioncCs |j|jS)N)r0r‡)r<r*rrrÚ getActionsszServer.getActionscCs|j|j|=dS)N)r0r‡)r<r*rŠrrrÚ delAction szServer.delActioncCs|j|j|S)N)r0r‡)r<r*rŠrrrÚ getAction szServer.getActioncCs|j|jj|ƒdS)N)r0r‡Ú setBanTime)r<r*rŠrrrrËszServer.setBanTimecGs|j|jj|ŽS)N)r0r…Z addAttempt)r<r*rÆrrrÚ addAttemptIPszServer.addAttemptIPcCs|j|jj|ƒS)N)r0r‡Z addBannedIP)r<r*rŠrrrÚsetBanIPszServer.setBanIPcCsZ|dk r|j|g}nt|jjƒƒ}d}||dkO}x |D]}||jj||d�7}q:W|S)Nr)Úifexists)r0r~Úvaluesr‡ZremoveBannedIP)r<r*rŠrÎÚjailsZcntrxrrrr„s  zServer.setUnbanIPcCs´|dk r|j|g}nt|jjƒƒ}g}|dkrz|rzxz|D]:}g}x&|D]}|jj|gƒrH|j|jƒqHW|j|ƒq:Wn6x4|D],}|jj|ƒ}|dk rœ|S|j|j|iƒq€W|S)N)r0r~rÏr‡Z getBannedrˆr*)r<r*ZidsrÐÚresr‘rmrxrrrÚbanned&s$     z Server.bannedcCs|j|jjƒS)N)r0r‡Ú getBanTime)r<r*rrrrÓ@szServer.getBanTimecCs|j|jj|ƒS)zµReturns the list of banned IP addresses for a jail. Parameters ---------- name : str The name of a jail. Returns ------- list The list of banned IP addresses. )r0r‡Ú getBanList)r<r*ZwithTimerrrrÔCs zServer.getBanListcCs|j|j||ƒdS)N)r0ÚsetBanTimeExtra)r<r*ÚoptrŠrrrrÕRszServer.setBanTimeExtracCs|j|j|ƒS)N)r0ÚgetBanTimeExtra)r<r*rÖrrrr×UszServer.getBanTimeExtracCs|jdk o|jjƒS)N)r5ZisActive)r<rrrÚ isStartedXszServer.isStartedcCsB|dk rt|jƒ|krdSx"t|jjƒƒD]}|jƒs*dSq*WdS)Nrr)Úlenr0r~rÏrz)r<Zjailnumrxrrrrz[s zServer.isAlivec CsRz@|jjƒt|jƒ}|jƒdj|ƒ}dt|jƒfd|fg}|S|jjƒXdS)Nz, zNumber of jailz Jail list)r/Úacquirer~r0ÚsortryrÙÚrelease)r<rÐZjailListrmrrrÚstatusds     z Server.statusÚbasiccCs|j|j|d�S)N)Úflavor)r0rÝ)r<r*rßrrrÚ statusJailpszServer.statusJailc Cs^|jƒ}|j�F|j|krdSt|ƒ}tdƒjtdks@|tjkrD|nt ƒ||_WdQRXdS)NÚfail2banÚ INHERITED) Úupperr.r6rrZsetLevelr\ÚloggingÚDEBUGrZ)r<rŠZllrrrrY€s zServer.setLogLevelc Cs|j�|jSQRXdS)N)r.r6)r<rrrÚ getLogLevel‘szServer.getLogLevelcCsŽt|ƒ\}}|jƒ}|j��h|j|kr,dS|dkr>||_dS|jdƒ}|dkrjddlm}|dd�}�nn|d k�rX|jd d ƒjƒ}|dkr�d }yttj j d |ƒ}Wn0t k rÖt j d|jd ƒƒtj j j}YnX|jdkrüddl}|jj|jƒƒ|_|jdk �rBtjj|jƒ�rBtjtj|jƒjƒ�rBtj j |j|d�}nt j d|jƒdSn€|d#k�rptjtjƒ}nh|dk�rˆtjtjƒ}nPyt|dƒjƒtj j|ƒ}Wn0t k �rÖt j d|ƒt j!d|jƒdSXt"dƒ} xn| j ddd$…D]Z} | j#| ƒy| j$ƒ| jƒWn4t%t&fk �rHtj'd%k�sBtj'd&k�rD‚YnX�qòW| j(ƒtj)k�r€|j*dk�r€tj)| j(ƒd|_*|jdƒ} | dk �ržt+| ƒ} n|d'k} |dk �rºt+|ƒ}nd}|jddƒdk�rÜ|jdƒ} n4d} |j*dk �r|j*dk�r|j*d} t,| | |d�} |dk�r*|j-tj.| ƒƒ| j/|ƒ|jdk �rzt j!d t0j0ƒt j!d!|d k�rb|n d"||jft0j0fƒ||_dSQRXdS)(NTrâÚpaddingzSYSTEMD-JOURNALr)ÚJournalHandlerrá)ZSYSLOG_IDENTIFIERÚSYSLOGÚfacilityZDAEMONÚ0ZLOG_z)Unable to set facility %r, using 'DAEMON'r)rêz9Syslog socket file: %s does not exists or is not a socketFrÚSYSOUTÚSTDERRÚazUnable to log to %rzLogging to previous target %rrérZdatetimeÚformatÚ)ÚaddtimerçzStart Fail2ban v%sz-Changed logging target to %s for Fail2ban v%sz%s (%s))rrìéÿÿÿÿ)rï)rïr)rérì)1rrãr.r7rVZsystemd.journalrèÚgetattrräÚhandlersZ SysLogHandlerÚAttributeErrorr>rTZ LOG_DAEMONr9Úplatformr:Úsystemr"r#rsÚstatÚS_ISSOCKÚst_modeZ StreamHandlerrbÚstdoutÚstderrrcrfZRotatingFileHandlerrgrRrZ removeHandlerÚflushrƒÚKeyErrorÚ version_infoZgetEffectiveLevelrår8rrZ setFormatterZ FormatterZ addHandlerr)r<ÚtargetZ logOptionsZ systargetrçrèZhdlrrêr÷ZloggerÚhandlerròZfmtrJrrrr[›s¨                            zServer.setLogTargetc Cs<|j�|j|krdS||_WdQRX|jdkp:|j|jƒS)NTré)r.r9r7r[)r<rKrrrrWs   zServer.setSyslogSocketc Cs|j�|jSQRXdS)N)r.r7)r<rrrÚ getLogTargetszServer.getLogTargetc Cs|j�|jSQRXdS)N)r.r9)r<rrrÚgetSyslogSocketszServer.getSyslogSocketc Cs |jd krlx\tdƒjD]N}y|jƒtjd|jƒWqtk rb|jƒtjd|jƒYqXqWdSx*tdƒjD]}|jƒtjd|jƒqxWd SdS) NrírréúSYSTEMD-JOURNALrázrollover performed on %szflush performed on %sz rolled overZflushed)rírrér)r7rrõZ doRolloverr>rRrörþ)r<rrrrrCs zServer.flushLogscCs|dkrt|ƒnd}tj|ƒS)Nr)rrÚsetIPv6IsAllowed)rŠrrrr/szServer.setIPv6IsAllowedcCsBx<|jƒD]0\}}|dkr.tjt|ƒdƒq td|ƒ‚q WdS)NÚ stacksizeizunknown option %r)rprÚ stack_sizeÚintrÿ)r<rŠÚoÚvrrrÚsetThreadOptions4szServer.setThreadOptionscCsdtjƒdiS)Nri)rr)r<rrrÚgetThreadOptions;szServer.getThreadOptionscCs¤|jr|jj|krdS|j r.|jƒdkr.dSt|jƒdkrDtdƒ‚|jƒdkrXd|_n0tdk r~t|ƒt|ƒ|_|jjƒn t j dƒt j dk r t j j |jƒdS)NZnonerz3Cannot change database when there are jails presentzEUnable to import fail2ban database module as sqlite is not available.)r1ÚfilenameÚlowerrÙr0Ú RuntimeErrorrr,Z delAllJailsr>rTrrhZdb_set)r<rrrrÚ setDatabase>s"    zServer.setDatabasecCs|jS)N)r1)r<rrrÚ getDatabaseUszServer.getDatabasecs8x*dD]"‰tjjˆƒr‡fdd„}|ƒSqWtdƒ‚dS)zµGenerate a list of open file descriptors. This wouldn't work on some platforms, or if proc/fdescfs not mounted, or a chroot environment, then it'd raise a FileExistsError. ú /proc/self/fdú/proc/fdc3s*x$tjˆƒD]}|jƒr t|ƒVq WdS)N)r"ÚlistdirÚisdigitr )r*)r#rrÚfdlistdsz#Server.__get_fdlist..fdlistzfd-list not foundN)rr)r"r#rsr()rr)r#rZ __get_fdlistXs   zServer.__get_fdlistc6Cs|jtjtjƒy tjƒ}Wn,tk rH}zd|j|jffSd}~XnX|dkr´tj ƒy tjƒ}Wn,tk r’}zd|j|jffSd}~XnX|dkr¨tj dƒq¸tj dƒndSy|j ƒ}d }WnFytj dƒ}Wnttfk �rüd}YnXt|dƒ}YnXtjdd…dk�r„tjd tjƒ}xD|D]<}ytjj||ƒ�sXtj|ƒWntk �rpYnX�q8Wtj|ƒnN|dk�rÆxB|D],}ytj|ƒWntk �r¼YnX�q”Wn tjd|ƒtjd tjƒtjd tjƒtjd tjƒdS)z¤ Detach a process from the controlling terminal and run it in the background as a daemon. http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/278731 FNrú/rÚ SC_OPEN_MAXérïéz /dev/urandomz /dev/nullTró)rïrrró)T)rHrDÚSIGHUPÚSIG_IGNr"Úforkr'r)ÚstrerrorÚsetsidÚchdirÚ_exitÚ_Server__get_fdlistÚsysconfrörƒÚrangerbrrcÚO_RDONLYr#Ú sameopenfilerfÚ closerangeÚO_RDWR)r<Úpidr+rZmaxfdZ urandom_fdÚfdrrrZ__createDaemonlsX             zServer.__createDaemon)F)TT)F)F)N)F)NNT)NN)F)N)rÞ)YrÚ __module__Ú __qualname__r=r_rarHrir@rwrur|r}rrr�r‰r‹rŒrŽr�r�r’r“r•r™ršr›r�ržrŸr¡r¢r£r¤r¦r§r¨rªr«r®r¯r²r³r´r¶r¸r¹rºr»r¼r½rÀrÁrÂrÃrÄrÅrÇrÈrÉrÊrËrÌrÍr„rÒrÓrÔrÕr×rØrzrÝràrYrær[rWrrrCÚ staticmethodrr r rrr#rSrrrrr-Osª P/   7          s   r-c@s eZdZdS)rUN)rr,r-rrrrrUÎsrU)3Ú __author__Z __copyright__Z __license__rrrrär"rDrùrbrlrrrÐrr…rr r Z transmitterr Z asyncserverr r rñrZhelpersrrrrrrrrr>rXrZr\ZdatabaserÚ ImportErrorr r(Ú NameErrorr'r,r-Ú ExceptionrUrrrrÚsN   $   server/__pycache__/server.cpython-36.pyc000064400000061660152231502150014223 0ustar003 UB(dwã@s^dZdZdZddlZddlmZmZddlZddlZddlZddl Z ddl Z ddl m Z m Z ddlmZdd lmZmZmZdd lmZdd lmZmZd d lmZd dlmZmZmZmZm Z m!Z!m"Z"ee#ƒZ$dZ%dZ&dZ'yddl(m)Z)Wne*k �rdZ)YnXdd„Z+ye,Wne-k �r2e.Z,YnXdd„Z/Gdd„dƒZ0Gdd„de1ƒZ2dS)z Cyril Jaquierz Copyright (c) 2004 Cyril JaquierZGPLéN)ÚLockÚRLocké)Ú ObserversÚObserverThread)ÚJails)ÚDNSUtilsÚ FileFilterÚ JournalFilter)Ú Transmitter)Ú AsyncServerÚAsyncServerExceptioné)Úversion)Ú getLoggerÚ_as_boolÚextractOptionsÚ str2LogLevelÚgetVerbosityFormatÚ excepthookÚprctl_set_th_nameÚautoÚINFOÚSTDOUT)Ú Fail2BanDbcCs tjƒjjS)N)Ú threadingZcurrent_threadÚ __class__Ú__name__©rrú/usr/lib/python3.6/server.pyÚ _thread_name:sr cCs^tjj|ƒ}tjj|ƒrZytj|ƒWn2ttfk rX}z|jdkrH‚WYdd}~XnXdS)z0Creates path of file (last level only) on demandéN)ÚosÚpathÚdirnameÚisabsÚmkdirÚOSErrorÚFileExistsErrorÚerrno)ÚnameÚerrrÚ_make_file_pathBs   r,c@sÚeZdZd¯dd„Zdd„Zdd„Zdd „Zdd ifd d „Zd d„Zdd„Z d°dd„Z dd„Z dd„Z dd„Z dd„Zdd„Zdd„Zdd „Zd!d"„Zd#d$„Zd%d&„Zd'd(„Zd)d*„Zd±d+d,„Zd-d.„Zd/d0„Zd1d2„Zd3d4„Zd5d6„Zd7d8„Zd9d:„Zd;d<„Zd=d>„Z d?d@„Z!dAdB„Z"dCdD„Z#dEdF„Z$dGdH„Z%dIdJ„Z&dKdL„Z'dMdN„Z(dOdP„Z)dQdR„Z*d²dSdT„Z+d³dVdW„Z,dXdY„Z-d´dZd[„Z.d\d]„Z/d^d_„Z0d`da„Z1dbdc„Z2ddde„Z3dfdg„Z4dhdi„Z5djdk„Z6dldm„Z7dndo„Z8dpdq„Z9drds„Z:dtdu„Z;dvdw„Zd|d}„Z?dµd~d„Z@d¶d€d�„ZAd‚dƒ„ZBd·d„d…„ZCd†d‡„ZDdˆd‰„ZEdŠd‹„ZFd¸dŒd�„ZGdŽd�„ZHd¹d‘d’„ZId“d”„ZJd•d–„ZKd—d˜„ZLd™dš„ZMd›dœ„ZNd�dž„ZOdŸd „ZPeQd¡d¢„ƒZRd£d¤„ZSd¥d¦„ZTd§d¨„ZUd©dª„ZVeQd«d¬„ƒZWd­d®„ZXdUS)ºÚServerFcCsjtƒ|_tƒ|_tƒ|_d|_||_t|ƒ|_ i|_ d|_ d|_ d|_ d|_d|_ddddœ|_i|_dS)Nz/var/run/syslogz /var/run/logz/dev/log)ZDarwinZFreeBSDZLinux)rÚ_Server__loggingLockrÚ _Server__lockrÚ_Server__jailsÚ _Server__dbÚ_Server__daemonr Ú_Server__transmÚ_Server__reload_stateÚ_Server__asyncServerÚ_Server__logLevelÚ_Server__logTargetÚ_Server__verboseÚ_Server__syslogSocketÚ_Server__autoSyslogSocketPathsÚ_Server__prev_signals)ÚselfZdaemonrrrÚ__init__Qs   zServer.__init__cCstjd|ƒ|jƒdS)NzCaught signal %d. Exiting)ÚlogSysÚdebugÚquit)r<ÚsignumÚframerrrZ__sigTERMhandlerfs zServer.__sigTERMhandlercCstjd|ƒ|jƒdS)NzCaught signal %d. Flushing logs)r>r?Ú flushLogs)r<rAZfnamerrrZ__sigUSR1handlerjs zServer.__sigUSR1handlercCs tj|ƒ|j|<tj||ƒdS)z>Bind new signal handler while storing old one in _prev_signalsN)ÚsignalÚ getsignalr;)r<ÚsÚnewrrrÚ _rebindSignalnszServer._rebindSignalTc ,Cs´tjdƒ|jrXtjdƒ|jƒ}|dkr.dS|dsXd|dd…f}tj|ƒt|ƒ‚t|j ddƒƒ|j d dƒ|_ |j |j d |j dk r�|j nt ƒƒ|j|j d |jdk r²|jntƒƒ|j|j d |jdk rÔ|jntƒƒtjd dƒtjdtjƒ|j�r tjdƒtƒdk�rLx$tjtjfD]}|j||jƒ�q$W|jtj|jƒtt_yÚinfoÚ_Server__createDaemonÚerrorÚServerInitializationErrorrÚgetr8ÚsetSyslogSocketr9ÚDEF_SYSLOGSOCKETÚ setLogLevelr6Ú DEF_LOGLEVELÚ setLogTargetr7Ú DEF_LOGTARGETrr rDÚSIGTERMÚSIGINTrHÚ_Server__sigTERMhandlerÚSIGUSR1Ú_Server__sigUSR1handlerrÚsysr?r,ÚopenÚwriteÚgetpidÚcloser'ÚIOErrorrÚMainrÚstartr r3r5rPr r@Úremove) r<ZsockZpidfileÚforceÚobserverZconfÚretÚerrrFZpidFiler+rrrrissj                z Server.startcCsÐdd„|_tjdƒ|jdk r(|jjƒtƒdkrVx"|jjƒD]\}}tj||ƒq>Wt j }|dk rz|j dd�rtd}dt _ |j ƒ|dk r’|j ƒ|j r¨|j jƒd|_ |jdk rÂ|jj ƒd|_tjdƒdS)NcSsdS)NFrrrrrÚÅszServer.quit..zShutdown in progress...rNF)Z forceQuitzExiting Fail2ban)r@r>rRr5Zstop_communicationr r;ÚitemsrDrrhÚstopÚ stopAllJailr1rf)r<rFZshZobsMainrrrr@Ãs,         z Server.quitcCsªd}|jj|ƒrt|jj|ƒrt|j|}|j|krLd}tjd|ƒd|j|<n(tjd||j|ƒ|j|dd�|j|=|rŠ|jj|||j ƒ|j dk r¦|j j |j|ƒdS)NTFzReload jail %rz"Restart jail %r (reason: %r != %r))rq) r4rVr0ÚexistsÚbackendr>rRÚdelJailÚaddr1ÚaddJail)r<r*rtZaddflgÚjailrrrrwòs     zServer.addJailcCsJ|j|}|s|jƒr$|j||d�|rF|jdk r>|jj|ƒ|j|=dS)N)rqÚjoin)r0ÚisAliverqr1ru)r<r*rqryrxrrrrus    zServer.delJailc Cs\|j�L|j|}|jƒs$|jƒn||jkrBtjd|ƒ|j|=|jrNd|_WdQRXdS)NzJail %r reloadedF)r/r0rzrir4r>rRÚidle)r<r*rxrrrÚ startJails    zServer.startJailc Cs$|j�|j|dd�WdQRXdS)NT)rq)r/ru)r<r*rrrÚstopJailszServer.stopJailc Csptjdƒ|j�Vx&t|jjƒƒD]}|j|ddd�q"Wx&t|jjƒƒD]}|j|ddd�qJWWdQRXdS)NzStopping all jailsTF)rqry)r>rRr/Úlistr0Úkeysru)r<r*rrrrrs  zServer.stopAllJailcCstjjƒtjjƒdS)N)rZCACHE_nameToIpÚclearZCACHE_ipToName)r<rrrÚ clearCaches(s zServer.clearCachesc Cs¼|�r*|jr(|dks |jj|ƒr(tdƒ‚tjd|dkr>d|ndƒ|j�Ö|dkr¢d}d|ksn|jj|ƒrx|j|}|rÊd|krŽ|j|ƒd|krÊ|j |ƒn(|j ƒd|krº|jƒd|krÊ|j ƒxR|jj ƒD]D\}}|dksî||krÖd |_ ||j|<|jjd d �|jjd d �qÖWWdQRXnŽ|j�rg}xL|jj ƒD]>\}}||jk�rb|j|ƒn|jjd d �|jjd d ��qBWx|D]}|j|ƒ�qŠWWdQRXi|_tjd ƒdS) Nz--allzReload already in progresszReload zjail %sz all jailsz --if-existsz--unbanz --restartT)ÚbeginFzReload finished.)r4rVÚ ValueErrorr>rRr/r0rsÚ setUnbanIPr}r�rrrpr{ÚfilterÚreloadÚactionsÚappendru)r<r*Zoptsr‚rxZjnZdeljailsrrrÚ reloadJails-sJ        zServer.reloadJailscCs||j|_dS)NT)r0r{)r<r*ÚvaluerrrÚ setIdleJailds zServer.setIdleJailcCs |j|jS)N)r0r{)r<r*rrrÚ getIdleJailhszServer.getIdleJailcCst|ƒ|j|j_dS)N)rr0r…Ú ignoreSelf)r<r*rŠrrrÚ setIgnoreSelflszServer.setIgnoreSelfcCs|j|jjS)N)r0r…r�)r<r*rrrÚ getIgnoreSelfoszServer.getIgnoreSelfcCs|j|jj|ƒdS)N)r0r…Ú addIgnoreIP)r<r*Úiprrrr�rszServer.addIgnoreIPcCs|j|jj|ƒdS)N)r0r…Ú delIgnoreIP)r<r*r‘rrrr’uszServer.delIgnoreIPcCs|j|jjƒS)N)r0r…Ú getIgnoreIP)r<r*rrrr“xszServer.getIgnoreIPcCs&|j|j}t|tƒr"|j||ƒdS)N)r0r…Ú isinstancer Ú addLogPath)r<r*ÚfileNameÚtailÚfilter_rrrr•{s  zServer.addLogPathcCs$|j|j}t|tƒr |j|ƒdS)N)r0r…r”r Ú delLogPath)r<r*r–r˜rrrr™€s  zServer.delLogPathcCs4|j|j}t|tƒr|jƒStjd|ƒgSdS)Nz$Jail %s is not a FileFilter instance)r0r…r”r Z getLogPathsr>r?)r<r*r˜rrrÚ getLogPath…s   zServer.getLogPathcCs$|j|j}t|tƒr |j|ƒdS)N)r0r…r”r ÚaddJournalMatch)r<r*Úmatchr˜rrrr›�s  zServer.addJournalMatchcCs$|j|j}t|tƒr |j|ƒdS)N)r0r…r”r ÚdelJournalMatch)r<r*rœr˜rrrr�’s  zServer.delJournalMatchcCs4|j|j}t|tƒr|jƒStjd|ƒgSdS)Nz'Jail %s is not a JournalFilter instance)r0r…r”r ÚgetJournalMatchr>r?)r<r*r˜rrrrž—s   zServer.getJournalMatchcCs|j|j}|j|ƒdS)N)r0r…ÚsetLogEncoding)r<r*Úencodingr˜rrrrŸŸs zServer.setLogEncodingcCs|j|j}|jƒS)N)r0r…ÚgetLogEncoding)r<r*r˜rrrr¡£s zServer.getLogEncodingcCs|j|jj|ƒdS)N)r0r…Ú setFindTime)r<r*rŠrrrr¢§szServer.setFindTimecCs|j|jjƒS)N)r0r…Ú getFindTime)r<r*rrrr£ªszServer.getFindTimecCs|j|jj|ƒdS)N)r0r…ÚsetDatePattern)r<r*Úpatternrrrr¤­szServer.setDatePatterncCs|j|jjƒS)N)r0r…ÚgetDatePattern)r<r*rrrr¦°szServer.getDatePatterncCs|j|jj|ƒdS)N)r0r…ÚsetLogTimeZone)r<r*Ztzrrrr§³szServer.setLogTimeZonecCs|j|jjƒS)N)r0r…ÚgetLogTimeZone)r<r*rrrr¨¶szServer.getLogTimeZonecCs||j|j_dS)N)r0r…Ú ignoreCommand)r<r*rŠrrrÚsetIgnoreCommand¹szServer.setIgnoreCommandcCs|j|jjS)N)r0r…r©)r<r*rrrÚgetIgnoreCommand¼szServer.getIgnoreCommandcCs&td|dƒ\}}||j|j_dS)Nzcache[ú])rr0r…Ú ignoreCache)r<r*rŠZoptionsrrrÚsetIgnoreCache¿szServer.setIgnoreCachecCs|j|jjS)N)r0r…r­)r<r*rrrÚgetIgnoreCacheÃszServer.getIgnoreCachecCs"|j|j}tjd|ƒ||_dS)Nz prefregex: %r)r0r…r>r?Ú prefRegex)r<r*rŠÚfltrrrÚ setPrefRegexÆs  zServer.setPrefRegexcCs|j|jjS)N)r0r…r°)r<r*rrrÚ getPrefRegexËszServer.getPrefRegexcCs>|j|j}|s|f}x"|D]}tjd|ƒ|j|ƒqWdS)Nz failregex: %r)r0r…r>r?Ú addFailRegex)r<r*rŠÚmultipler±rrrr´Îs    zServer.addFailRegexNcCs|j|jj|ƒdS)N)r0r…Ú delFailRegex)r<r*Úindexrrrr¶ÕszServer.delFailRegexcCs|j|jjƒS)N)r0r…Ú getFailRegex)r<r*rrrr¸ØszServer.getFailRegexcCs>|j|j}|s|f}x"|D]}tjd|ƒ|j|ƒqWdS)Nz ignoreregex: %r)r0r…r>r?ÚaddIgnoreRegex)r<r*rŠrµr±rrrr¹Ûs    zServer.addIgnoreRegexcCs|j|jj|ƒdS)N)r0r…ÚdelIgnoreRegex)r<r*r·rrrrºâszServer.delIgnoreRegexcCs|j|jjƒS)N)r0r…ÚgetIgnoreRegex)r<r*rrrr»åszServer.getIgnoreRegexcCs|j|jj|ƒdS)N)r0r…Ú setUseDns)r<r*rŠrrrr¼èszServer.setUseDnscCs|j|jjƒS)N)r0r…Ú getUseDns)r<r*rrrr½ëszServer.getUseDnscCs||j|jj_dS)N)r0r…Ú failManagerÚ maxMatches)r<r*rŠrrrÚ setMaxMatchesîszServer.setMaxMatchescCs|j|jjjS)N)r0r…r¾r¿)r<r*rrrÚ getMaxMatchesñszServer.getMaxMatchescCs|j|jj|ƒdS)N)r0r…Ú setMaxRetry)r<r*rŠrrrrÂôszServer.setMaxRetrycCs|j|jjƒS)N)r0r…Ú getMaxRetry)r<r*rrrrÃ÷szServer.getMaxRetrycCs|j|jj|ƒdS)N)r0r…Ú setMaxLines)r<r*rŠrrrrÄúszServer.setMaxLinescCs|j|jjƒS)N)r0r…Ú getMaxLines)r<r*rrrrÅýszServer.getMaxLinescGs(|j|jj|f|žd||jkiŽdS)Nr†)r0r‡rvr4)r<r*rŠÚargsrrrÚ addActionszServer.addActioncCs |j|jS)N)r0r‡)r<r*rrrÚ getActionsszServer.getActionscCs|j|j|=dS)N)r0r‡)r<r*rŠrrrÚ delAction szServer.delActioncCs|j|j|S)N)r0r‡)r<r*rŠrrrÚ getAction szServer.getActioncCs|j|jj|ƒdS)N)r0r‡Ú setBanTime)r<r*rŠrrrrËszServer.setBanTimecGs|j|jj|ŽS)N)r0r…Z addAttempt)r<r*rÆrrrÚ addAttemptIPszServer.addAttemptIPcCs|j|jj|ƒS)N)r0r‡Z addBannedIP)r<r*rŠrrrÚsetBanIPszServer.setBanIPcCsZ|dk r|j|g}nt|jjƒƒ}d}||dkO}x |D]}||jj||d�7}q:W|S)Nr)Úifexists)r0r~Úvaluesr‡ZremoveBannedIP)r<r*rŠrÎÚjailsZcntrxrrrr„s  zServer.setUnbanIPcCs´|dk r|j|g}nt|jjƒƒ}g}|dkrz|rzxz|D]:}g}x&|D]}|jj|gƒrH|j|jƒqHW|j|ƒq:Wn6x4|D],}|jj|ƒ}|dk rœ|S|j|j|iƒq€W|S)N)r0r~rÏr‡Z getBannedrˆr*)r<r*ZidsrÐÚresr‘rmrxrrrÚbanned&s$     z Server.bannedcCs|j|jjƒS)N)r0r‡Ú getBanTime)r<r*rrrrÓ@szServer.getBanTimecCs|j|jj|ƒS)zµReturns the list of banned IP addresses for a jail. Parameters ---------- name : str The name of a jail. Returns ------- list The list of banned IP addresses. )r0r‡Ú getBanList)r<r*ZwithTimerrrrÔCs zServer.getBanListcCs|j|j||ƒdS)N)r0ÚsetBanTimeExtra)r<r*ÚoptrŠrrrrÕRszServer.setBanTimeExtracCs|j|j|ƒS)N)r0ÚgetBanTimeExtra)r<r*rÖrrrr×UszServer.getBanTimeExtracCs|jdk o|jjƒS)N)r5ZisActive)r<rrrÚ isStartedXszServer.isStartedcCsB|dk rt|jƒ|krdSx"t|jjƒƒD]}|jƒs*dSq*WdS)Nrr)Úlenr0r~rÏrz)r<Zjailnumrxrrrrz[s zServer.isAlivec CsRz@|jjƒt|jƒ}|jƒdj|ƒ}dt|jƒfd|fg}|S|jjƒXdS)Nz, zNumber of jailz Jail list)r/Úacquirer~r0ÚsortryrÙÚrelease)r<rÐZjailListrmrrrÚstatusds     z Server.statusÚbasiccCs|j|j|d�S)N)Úflavor)r0rÝ)r<r*rßrrrÚ statusJailpszServer.statusJailc Cs^|jƒ}|j�F|j|krdSt|ƒ}tdƒjtdks@|tjkrD|nt ƒ||_WdQRXdS)NÚfail2banÚ INHERITED) Úupperr.r6rrZsetLevelr\ÚloggingÚDEBUGrZ)r<rŠZllrrrrY€s zServer.setLogLevelc Cs|j�|jSQRXdS)N)r.r6)r<rrrÚ getLogLevel‘szServer.getLogLevelcCsŽt|ƒ\}}|jƒ}|j��h|j|kr,dS|dkr>||_dS|jdƒ}|dkrjddlm}|dd�}�nn|d k�rX|jd d ƒjƒ}|dkr�d }yttj j d |ƒ}Wn0t k rÖt j d|jd ƒƒtj j j}YnX|jdkrüddl}|jj|jƒƒ|_|jdk �rBtjj|jƒ�rBtjtj|jƒjƒ�rBtj j |j|d�}nt j d|jƒdSn€|d#k�rptjtjƒ}nh|dk�rˆtjtjƒ}nPyt|dƒjƒtj j|ƒ}Wn0t k �rÖt j d|ƒt j!d|jƒdSXt"dƒ} xn| j ddd$…D]Z} | j#| ƒy| j$ƒ| jƒWn4t%t&fk �rHtj'd%k�sBtj'd&k�rD‚YnX�qòW| j(ƒtj)k�r€|j*dk�r€tj)| j(ƒd|_*|jdƒ} | dk �ržt+| ƒ} n|d'k} |dk �rºt+|ƒ}nd}|jddƒdk�rÜ|jdƒ} n4d} |j*dk �r|j*dk�r|j*d} t,| | |d�} |dk�r*|j-tj.| ƒƒ| j/|ƒ|jdk �rzt j!d t0j0ƒt j!d!|d k�rb|n d"||jft0j0fƒ||_dSQRXdS)(NTrâÚpaddingzSYSTEMD-JOURNALr)ÚJournalHandlerrá)ZSYSLOG_IDENTIFIERÚSYSLOGÚfacilityZDAEMONÚ0ZLOG_z)Unable to set facility %r, using 'DAEMON'r)rêz9Syslog socket file: %s does not exists or is not a socketFrÚSYSOUTÚSTDERRÚazUnable to log to %rzLogging to previous target %rrérZdatetimeÚformatÚ)ÚaddtimerçzStart Fail2ban v%sz-Changed logging target to %s for Fail2ban v%sz%s (%s))rrìéÿÿÿÿ)rï)rïr)rérì)1rrãr.r7rVZsystemd.journalrèÚgetattrräÚhandlersZ SysLogHandlerÚAttributeErrorr>rTZ LOG_DAEMONr9Úplatformr:Úsystemr"r#rsÚstatÚS_ISSOCKÚst_modeZ StreamHandlerrbÚstdoutÚstderrrcrfZRotatingFileHandlerrgrRrZ removeHandlerÚflushrƒÚKeyErrorÚ version_infoZgetEffectiveLevelrår8rrZ setFormatterZ FormatterZ addHandlerr)r<ÚtargetZ logOptionsZ systargetrçrèZhdlrrêr÷ZloggerÚhandlerròZfmtrJrrrr[›s¨                            zServer.setLogTargetc Cs<|j�|j|krdS||_WdQRX|jdkp:|j|jƒS)NTré)r.r9r7r[)r<rKrrrrWs   zServer.setSyslogSocketc Cs|j�|jSQRXdS)N)r.r7)r<rrrÚ getLogTargetszServer.getLogTargetc Cs|j�|jSQRXdS)N)r.r9)r<rrrÚgetSyslogSocketszServer.getSyslogSocketc Cs |jd krlx\tdƒjD]N}y|jƒtjd|jƒWqtk rb|jƒtjd|jƒYqXqWdSx*tdƒjD]}|jƒtjd|jƒqxWd SdS) NrírréúSYSTEMD-JOURNALrázrollover performed on %szflush performed on %sz rolled overZflushed)rírrér)r7rrõZ doRolloverr>rRrörþ)r<rrrrrCs zServer.flushLogscCs|dkrt|ƒnd}tj|ƒS)Nr)rrÚsetIPv6IsAllowed)rŠrrrr/szServer.setIPv6IsAllowedcCsBx<|jƒD]0\}}|dkr.tjt|ƒdƒq td|ƒ‚q WdS)NÚ stacksizeizunknown option %r)rprÚ stack_sizeÚintrÿ)r<rŠÚoÚvrrrÚsetThreadOptions4szServer.setThreadOptionscCsdtjƒdiS)Nri)rr)r<rrrÚgetThreadOptions;szServer.getThreadOptionscCs¤|jr|jj|krdS|j r.|jƒdkr.dSt|jƒdkrDtdƒ‚|jƒdkrXd|_n0tdk r~t|ƒt|ƒ|_|jjƒn t j dƒt j dk r t j j |jƒdS)NZnonerz3Cannot change database when there are jails presentzEUnable to import fail2ban database module as sqlite is not available.)r1ÚfilenameÚlowerrÙr0Ú RuntimeErrorrr,Z delAllJailsr>rTrrhZdb_set)r<rrrrÚ setDatabase>s"    zServer.setDatabasecCs|jS)N)r1)r<rrrÚ getDatabaseUszServer.getDatabasecs8x*dD]"‰tjjˆƒr‡fdd„}|ƒSqWtdƒ‚dS)zµGenerate a list of open file descriptors. This wouldn't work on some platforms, or if proc/fdescfs not mounted, or a chroot environment, then it'd raise a FileExistsError. ú /proc/self/fdú/proc/fdc3s*x$tjˆƒD]}|jƒr t|ƒVq WdS)N)r"ÚlistdirÚisdigitr )r*)r#rrÚfdlistdsz#Server.__get_fdlist..fdlistzfd-list not foundN)rr)r"r#rsr()rr)r#rZ __get_fdlistXs   zServer.__get_fdlistc6Cs|jtjtjƒy tjƒ}Wn,tk rH}zd|j|jffSd}~XnX|dkr´tj ƒy tjƒ}Wn,tk r’}zd|j|jffSd}~XnX|dkr¨tj dƒq¸tj dƒndSy|j ƒ}d }WnFytj dƒ}Wnttfk �rüd}YnXt|dƒ}YnXtjdd…dk�r„tjd tjƒ}xD|D]<}ytjj||ƒ�sXtj|ƒWntk �rpYnX�q8Wtj|ƒnN|dk�rÆxB|D],}ytj|ƒWntk �r¼YnX�q”Wn tjd|ƒtjd tjƒtjd tjƒtjd tjƒdS)z¤ Detach a process from the controlling terminal and run it in the background as a daemon. http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/278731 FNrú/rÚ SC_OPEN_MAXérïéz /dev/urandomz /dev/nullTró)rïrrró)T)rHrDÚSIGHUPÚSIG_IGNr"Úforkr'r)ÚstrerrorÚsetsidÚchdirÚ_exitÚ_Server__get_fdlistÚsysconfrörƒÚrangerbrrcÚO_RDONLYr#Ú sameopenfilerfÚ closerangeÚO_RDWR)r<Úpidr+rZmaxfdZ urandom_fdÚfdrrrZ__createDaemonlsX             zServer.__createDaemon)F)TT)F)F)N)F)NNT)NN)F)N)rÞ)YrÚ __module__Ú __qualname__r=r_rarHrir@rwrur|r}rrr�r‰r‹rŒrŽr�r�r’r“r•r™ršr›r�ržrŸr¡r¢r£r¤r¦r§r¨rªr«r®r¯r²r³r´r¶r¸r¹rºr»r¼r½rÀrÁrÂrÃrÄrÅrÇrÈrÉrÊrËrÌrÍr„rÒrÓrÔrÕr×rØrzrÝràrYrær[rWrrrCÚ staticmethodrr r rrr#rSrrrrr-Osª P/   7          s   r-c@s eZdZdS)rUN)rr,r-rrrrrUÎsrU)3Ú __author__Z __copyright__Z __license__rrrrär"rDrùrbrlrrrÐrr…rr r Z transmitterr Z asyncserverr r rñrZhelpersrrrrrrrrr>rXrZr\ZdatabaserÚ ImportErrorr r(Ú NameErrorr'r,r-Ú ExceptionrUrrrrÚsN   $   server/__pycache__/strptime.cpython-36.opt-1.pyc000064400000023137152231502150015520 0ustar003 UB(df4ã@sVddlZddlZddlZddlZddlmZmZmZddlm Z eƒZ dZ ej de fƒZ eƒZded<d ed <d ed <d ed<de fed<de e fed<ded<ded<ded<ded<ded<ded<ded <d!ed"<d#ed$<d%ed&<d'ed(<d)ed*<d+ed,<d-d.„Zeƒd/d0„Zd1d2„Zd3d4„Zd[0-2]?\d)Úkz ?(?P1?\d)Úlz(?PZ|[A-Z]{3,5})ÚZz'(?PZ|UTC|GMT|[+-][01]\d(?::?\d{2})?)Úzz (?P%s)ZExZz'(?P(?:%s)?[+-][01]\d(?::?\d{2})?|%s)ZExzz (?P[1-2]\d|[0 ]?[1-9]|3[0-1])Údz(?P0?[1-9]|1[0-2])Úmz (?P\d{4})ÚYz(?P[0-1]?\d|2[0-3])ÚHz(?P[0-5]?\d)ÚMz(?P[0-5]?\d|6[0-1])ÚSz(?P[1-2]\d|0[1-9]|3[0-1])ZExdz(?P0[1-9]|1[0-2])ZExmz(?P[0-1]\d|2[0-3])ZExHz ?(?P[0-1]?\d|2[0-3])ZExkz ?(?P1[0-2]|\d)ZExlz(?P[0-5]\d)ZExMz(?P[0-5]\d|6[0-1])ZExSc CsZd dtjƒtjffdd„}d|d dtjjƒtjjttjp:ddƒƒfd�td<d td <dS) Nrécsšˆdˆdfdd„‰‡fdd„‰t‡‡fdd„d |fDƒƒ}tˆƒdkrŠˆdrŠ|t‡‡fd d„td ˆdjˆdjd|ƒDƒƒO}ˆtt|ƒƒƒS) z• Build century regex for last year and the next years (distance). Thereby respect possible run in the test-cases (alternate date used there) rrcSst|ƒ||…S)N)Ústr)ÚyearÚfÚt©rú/usr/lib/python3.6/strptime.pyÚOsz7_updateTimeRE.._getYearCentRE..csäd‰t|ƒdkr¢x8|D]0}ˆdks2|dd …ˆkr@|dd …‰qd‰PqWˆs€x0|D](}ˆdksl|dˆkrv|d‰qTd‰PqTWˆr¢dˆˆ‡fdd„|DƒƒfSt|ƒdkrÚt|dƒdkrÌddj|ƒqâdd j|ƒSd j|ƒS) Nrrz%s%scsg|]}|tˆƒd…‘qS)N)Úlen)Ú.0Úi)Úcrrú aszF_updateTimeRE.._getYearCentRE..grp..z(?:%s)ú|z[%s]Úéÿÿÿÿr )rÚjoin)Úexprsetr)Úgrp)rrr#Ps     z2_updateTimeRE.._getYearCentRE..grpc3s |]}ˆˆdj|ƒVqdS)rN)r)rr)ÚcentÚnowrrú dsz8_updateTimeRE.._getYearCentRE..c3s |]}ˆˆdj|ƒVqdS)rN)r)rr)r$r%rrr&fsr r )ÚsetrÚrangerÚsortedÚlist)r$Údistancer%r"r)r$r#r%rÚ_getYearCentREJs  8z%_updateTimeRE.._getYearCentREz (?P%s\d)iQ:)r$r+r%ZExYz (?P\d{2})ZExy)rr)rr)rr%Z alternateNowÚdatetimeZ fromtimestampÚminZalternateNowTimeÚtimeRE)r,rrrÚ _updateTimeREIs #r0cCsÄttjƒƒ}ddjdd„|Dƒƒdjdd„|Dƒƒf}ddd d d d d dddddddddddddddœ}xRt|ƒt|ƒD]>}|jdƒr¬|j|dd…ƒ}|r¬d|||<qzd|||<qzW||fS)Nz%%(%%|%s|[%s])rcSsg|]}t|ƒdkr|‘qS)r)r)rrrrrrysz$getTimePatternRE..rcSsg|]}t|ƒdkr|‘qS)r)r)rrrrrrzsZDAYZDAYNAMEZMONZMONTHZDayZ24hourZ12hourZYeardayZMonthZMinuteZAMPMZSecondZYearweekZWeekdayZYear2ZYearú%z Zone offsetZ Microsecondsz Zone name)ÚaÚAÚbÚBr rÚIÚjr rÚprÚUÚwÚWÚyr r1r rr ZExéz%%%s)r*r/Úkeysr!r'Ú startswithÚget)r>ZpattÚnamesÚkeyZknrrrÚgetTimePatternREvs"      rCcCs<|dkr dStj|ƒ}|dkr*td|ƒ‚|jƒ}t|dƒS)a&Validate a timezone and convert it to offset if it can (offset-based TZ). For now this accepts the UTC[+-]hhmm format (UTC has aliases GMT/Z and optional). Additionally it accepts all zone abbreviations mentioned below in TZ_STR. Note that currently this zone abbreviations are offset-based and used fixed offset without automatically DST-switch (if CET used then no automatically CEST-switch). In the future, it may be extended for named time zones (such as Europe/Paris) present on the system, if a suitable tz library is present (pytz). Nz$Unknown or unsupported time zone: %rr)ÚFIXED_OFFSET_TZ_REÚmatchÚ ValueErrorÚgroupsÚ zone2offset)Útzr rrrÚvalidateTimeZone�s   rJcCsðt|tƒr|St|tƒr t|ƒS|\}}|dks8|dkr@t|St|ƒdkr`t|t|ƒdS|ddkr¬t||ddkr‚d ndt|dd…ƒdt|dd …ƒSt||ddkrÂd ndt|dd…ƒdt|d d …ƒSdS)a”Return the proper offset, in minutes according to given timezone at a given time. Parameters ---------- tz: symbolic timezone or offset (for now only TZA?([+-]hh:?mm?)? is supported, as value are accepted: int offset; string in form like 'CET+0100' or 'UTC' or '-0400'; tuple (or list) in form (zone name, zone offset); dt: datetime instance for offset computation (currently unused) Nrré<ú:rú-rééér r )Ú isinstanceÚintrrJÚ TZ_ABBR_OFFSr)rIZdtZtzorrrrH s    @rHFcCsNd}}}}}}} } d} } } }�x@|jƒD�]2\}}|dkrPq<|dkrrt|ƒ}|dkrp|d7}q<|dkr„t|ƒ}q<|dkr–t|ƒ}q<|dkr°tjj|jƒƒ}q<|dkrÊtjj|jƒƒ}q<|d krÜt|ƒ}q<|d krît|ƒ} q<|d k�rXt|ƒ} |jd d ƒjƒ}|d tjdfk�r4| dk�rVd} n"|tjdk�rp| dk�rp| d7} q<|dk�rlt|ƒ} q<|dk�r€t|ƒ} q<|dk�r²|�rp|}|ddt |ƒ7}t|ƒ}q<|dk�rÎtj j|jƒƒ}q<|dk�rêtj j|jƒƒ}q<|dk�rt|ƒd}|dk�rpd}q<|dk�r$t|ƒ} q<|d&k�rJt|ƒ} |dk�rDdnd}q<|d'kr<|}|d(k�rfd}q offset in minutes)Ú rrKrN)ÚmaprÚsplitÚTZ_STRrrRÚfloatrS)ZtzlinerarIrrrÚ _init_TZ_ABBR€s rk)FN)Úrer`r_r-Z _strptimerrrZmytimerr[Z TZ_ABBR_REÚcompilerDr/r0rCrJrHrerSrirkrrrrÚsN +  (server/__pycache__/strptime.cpython-36.pyc000064400000023137152231502150014561 0ustar003 UB(df4ã@sVddlZddlZddlZddlZddlmZmZmZddlm Z eƒZ dZ ej de fƒZ eƒZded<d ed <d ed <d ed<de fed<de e fed<ded<ded<ded<ded<ded<ded<ded <d!ed"<d#ed$<d%ed&<d'ed(<d)ed*<d+ed,<d-d.„Zeƒd/d0„Zd1d2„Zd3d4„Zd[0-2]?\d)Úkz ?(?P1?\d)Úlz(?PZ|[A-Z]{3,5})ÚZz'(?PZ|UTC|GMT|[+-][01]\d(?::?\d{2})?)Úzz (?P%s)ZExZz'(?P(?:%s)?[+-][01]\d(?::?\d{2})?|%s)ZExzz (?P[1-2]\d|[0 ]?[1-9]|3[0-1])Údz(?P0?[1-9]|1[0-2])Úmz (?P\d{4})ÚYz(?P[0-1]?\d|2[0-3])ÚHz(?P[0-5]?\d)ÚMz(?P[0-5]?\d|6[0-1])ÚSz(?P[1-2]\d|0[1-9]|3[0-1])ZExdz(?P0[1-9]|1[0-2])ZExmz(?P[0-1]\d|2[0-3])ZExHz ?(?P[0-1]?\d|2[0-3])ZExkz ?(?P1[0-2]|\d)ZExlz(?P[0-5]\d)ZExMz(?P[0-5]\d|6[0-1])ZExSc CsZd dtjƒtjffdd„}d|d dtjjƒtjjttjp:ddƒƒfd�td<d td <dS) Nrécsšˆdˆdfdd„‰‡fdd„‰t‡‡fdd„d |fDƒƒ}tˆƒdkrŠˆdrŠ|t‡‡fd d„td ˆdjˆdjd|ƒDƒƒO}ˆtt|ƒƒƒS) z• Build century regex for last year and the next years (distance). Thereby respect possible run in the test-cases (alternate date used there) rrcSst|ƒ||…S)N)Ústr)ÚyearÚfÚt©rú/usr/lib/python3.6/strptime.pyÚOsz7_updateTimeRE.._getYearCentRE..csäd‰t|ƒdkr¢x8|D]0}ˆdks2|dd …ˆkr@|dd …‰qd‰PqWˆs€x0|D](}ˆdksl|dˆkrv|d‰qTd‰PqTWˆr¢dˆˆ‡fdd„|DƒƒfSt|ƒdkrÚt|dƒdkrÌddj|ƒqâdd j|ƒSd j|ƒS) Nrrz%s%scsg|]}|tˆƒd…‘qS)N)Úlen)Ú.0Úi)Úcrrú aszF_updateTimeRE.._getYearCentRE..grp..z(?:%s)ú|z[%s]Úéÿÿÿÿr )rÚjoin)Úexprsetr)Úgrp)rrr#Ps     z2_updateTimeRE.._getYearCentRE..grpc3s |]}ˆˆdj|ƒVqdS)rN)r)rr)ÚcentÚnowrrú dsz8_updateTimeRE.._getYearCentRE..c3s |]}ˆˆdj|ƒVqdS)rN)r)rr)r$r%rrr&fsr r )ÚsetrÚrangerÚsortedÚlist)r$Údistancer%r"r)r$r#r%rÚ_getYearCentREJs  8z%_updateTimeRE.._getYearCentREz (?P%s\d)iQ:)r$r+r%ZExYz (?P\d{2})ZExy)rr)rr)rr%Z alternateNowÚdatetimeZ fromtimestampÚminZalternateNowTimeÚtimeRE)r,rrrÚ _updateTimeREIs #r0cCsÄttjƒƒ}ddjdd„|Dƒƒdjdd„|Dƒƒf}ddd d d d d dddddddddddddddœ}xRt|ƒt|ƒD]>}|jdƒr¬|j|dd…ƒ}|r¬d|||<qzd|||<qzW||fS)Nz%%(%%|%s|[%s])rcSsg|]}t|ƒdkr|‘qS)r)r)rrrrrrysz$getTimePatternRE..rcSsg|]}t|ƒdkr|‘qS)r)r)rrrrrrzsZDAYZDAYNAMEZMONZMONTHZDayZ24hourZ12hourZYeardayZMonthZMinuteZAMPMZSecondZYearweekZWeekdayZYear2ZYearú%z Zone offsetZ Microsecondsz Zone name)ÚaÚAÚbÚBr rÚIÚjr rÚprÚUÚwÚWÚyr r1r rr ZExéz%%%s)r*r/Úkeysr!r'Ú startswithÚget)r>ZpattÚnamesÚkeyZknrrrÚgetTimePatternREvs"      rCcCs<|dkr dStj|ƒ}|dkr*td|ƒ‚|jƒ}t|dƒS)a&Validate a timezone and convert it to offset if it can (offset-based TZ). For now this accepts the UTC[+-]hhmm format (UTC has aliases GMT/Z and optional). Additionally it accepts all zone abbreviations mentioned below in TZ_STR. Note that currently this zone abbreviations are offset-based and used fixed offset without automatically DST-switch (if CET used then no automatically CEST-switch). In the future, it may be extended for named time zones (such as Europe/Paris) present on the system, if a suitable tz library is present (pytz). Nz$Unknown or unsupported time zone: %rr)ÚFIXED_OFFSET_TZ_REÚmatchÚ ValueErrorÚgroupsÚ zone2offset)Útzr rrrÚvalidateTimeZone�s   rJcCsðt|tƒr|St|tƒr t|ƒS|\}}|dks8|dkr@t|St|ƒdkr`t|t|ƒdS|ddkr¬t||ddkr‚d ndt|dd…ƒdt|dd …ƒSt||ddkrÂd ndt|dd…ƒdt|d d …ƒSdS)a”Return the proper offset, in minutes according to given timezone at a given time. Parameters ---------- tz: symbolic timezone or offset (for now only TZA?([+-]hh:?mm?)? is supported, as value are accepted: int offset; string in form like 'CET+0100' or 'UTC' or '-0400'; tuple (or list) in form (zone name, zone offset); dt: datetime instance for offset computation (currently unused) Nrré<ú:rú-rééér r )Ú isinstanceÚintrrJÚ TZ_ABBR_OFFSr)rIZdtZtzorrrrH s    @rHFcCsNd}}}}}}} } d} } } }�x@|jƒD�]2\}}|dkrPq<|dkrrt|ƒ}|dkrp|d7}q<|dkr„t|ƒ}q<|dkr–t|ƒ}q<|dkr°tjj|jƒƒ}q<|dkrÊtjj|jƒƒ}q<|d krÜt|ƒ}q<|d krît|ƒ} q<|d k�rXt|ƒ} |jd d ƒjƒ}|d tjdfk�r4| dk�rVd} n"|tjdk�rp| dk�rp| d7} q<|dk�rlt|ƒ} q<|dk�r€t|ƒ} q<|dk�r²|�rp|}|ddt |ƒ7}t|ƒ}q<|dk�rÎtj j|jƒƒ}q<|dk�rêtj j|jƒƒ}q<|dk�rt|ƒd}|dk�rpd}q<|dk�r$t|ƒ} q<|d&k�rJt|ƒ} |dk�rDdnd}q<|d'kr<|}|d(k�rfd}q offset in minutes)Ú rrKrN)ÚmaprÚsplitÚTZ_STRrrRÚfloatrS)ZtzlinerarIrrrÚ _init_TZ_ABBR€s rk)FN)Úrer`r_r-Z _strptimerrrZmytimerr[Z TZ_ABBR_REÚcompilerDr/r0rCrJrHrerSrirkrrrrÚsN +  (server/__pycache__/ticket.cpython-36.opt-1.pyc000064400000021450152231502150015130 0ustar003 UB(d!ã@sldZdZdZddlmZddlmZddlmZee ƒZ Gdd „d e ƒZ Gd d „d e ƒZ Gd d „d e ƒZdS)z Cyril Jaquierz Copyright (c) 2004 Cyril JaquierZGPLé)Ú getLoggeré)ÚIPAddr)ÚMyTimec@s:eZdZdEZd Zd Zd Zd d d id fd d„Zdd„Zdd„Z dd„Z dd„Z dd„Z dd„Z dd„Zdd„Zdd „Zd!d"„ZdFd#d$„ZdGd&d'„ZdHd(d)„Zd*d+„ZdId,d-„ZdJd.d/„Zd0d1„Zd2d3„Zd4d5„Zd6d7„Zed8d9„ƒZejd:d9„ƒZed;d<„ƒZejd=d<„ƒZd>d?„Z dKd@dA„Z!edBdC„ƒZ"e"jdDdC„ƒZ"d S)LÚTicketÚ_idÚ_flagsÚ _banCountÚ_banTimeÚ_timeÚ_dataÚ_retryÚ _lastResetlÿÿÿréNcCs„|j|ƒd|_d|_d|_|dk r(|ntjƒ|_|p8gddœ|_|dk rrx&|jƒD]\}}|dk rT||j|<qTW|r€|j |ƒdS)z{Ticket constructor @param ip the IP address @param time the ban time @param matches (log) lines caused the ticket éN)ÚmatchesÚfailures) ÚsetIDrr r rÚtimer r ÚitemsÚupdate)ÚselfÚiprrÚdataÚticketÚkÚv©rú/usr/lib/python3.6/ticket.pyÚ__init__+s zTicket.__init__c Cs<d|jjjdƒd|j|j|j|j|jd|jjdgƒfS)Nz@%s: ip=%s time=%s bantime=%s bancount=%s #attempts=%d matches=%rÚ.rrréÿÿÿÿ) Ú __class__Ú__name__Úsplitrr r r r Úget)rrrrÚ__str__BszTicket.__str__cCst|ƒS)N)Ústr)rrrrÚ__repr__HszTicket.__repr__c CsJy0|j|jko0t|jdƒt|jdƒko0|j|jkStk rDdSXdS)NrF)rÚroundr r ÚAttributeError)rÚotherrrrÚ__eq__Ks   z Ticket.__eq__cCs4x.|jD]$}t||dƒ}|dk rt|||ƒqWdS)N)Ú __slots__ÚgetattrÚsetattr)rrÚnrrrrrSs  z Ticket.updatecCst|tƒrt|ƒ}||_dS)N)Ú isinstancer'rr)rÚvaluerrrrYs z Ticket.setIDcCs|jS)N)r)rrrrÚgetID_sz Ticket.getIDcCs|jjd|jƒS)Nr)r r%r)rrrrÚgetIPbsz Ticket.getIPcCs ||_dS)N)r )rr2rrrÚsetTimeeszTicket.setTimecCs|jS)N)r )rrrrÚgetTimehszTicket.getTimecCs ||_dS)N)r )rr2rrrÚ setBanTimekszTicket.setBanTimecCs|jdk r|jS|S)N)r )rÚ defaultBTrrrÚ getBanTimenszTicket.getBanTimeFcCs|s||jkr||_dS)N)r )rr2ÚalwaysrrrÚ setBanCountqszTicket.setBanCountcCs|j|7_dS)N)r )rr2rrrÚ incrBanCountuszTicket.incrBanCountcCs|jS)N)r )rrrrÚ getBanCountxszTicket.getBanCountcCs,|jdk r|jn|}|dkr"tjS|j|S)Nrr!)r rÚMAX_TIMEr )rr8ÚbantimerrrÚgetEndOfBanTime{szTicket.getEndOfBanTimecCs.|jdk r|jn|}|dkr dS||j|kS)NrFr!)r r )rrr8r?rrrÚ isTimedOutƒszTicket.isTimedOutcCs||jd<dS)Nr)r )rr2rrrÚ setAttempt‹szTicket.setAttemptcCs |jdS)Nr)r )rrrrÚ getAttemptŽszTicket.getAttemptc Cs6|r||jd<n"y |jd=Wntk r0YnXdS)Nr)r ÚKeyError)rrrrrÚ setMatches‘s   zTicket.setMatchescCsdd„|jjdfƒDƒS)NcSs(g|] }t|ttfƒs|ndj|ƒ‘qS)Ú)r1ÚlistÚtupleÚjoin)Ú.0Úlinerrrú ›sz%Ticket.getMatches..r)r r%)rrrrÚ getMatchesšszTicket.getMatchescCs |jtj@S)N)rrÚRESTORED)rrrrÚrestoredžszTicket.restoredcCs,|r|jtjO_n|jtjM_dS)N)rrrN)rr2rrrrO¡scCs |jtj@S)N)rrÚBANNED)rrrrÚbanned¨sz Ticket.bannedcCs,|r|jtjO_n|jtjM_dS)N)rrrP)rr2rrrrQ«scOs¨t|ƒdkr*tdd„|djƒDƒƒ|_nLt|ƒdkrF|jj|fƒn0t|ƒdkrv|jjdd„tt|ƒgdŽDƒƒt|ƒrŠ|jj|ƒtdd„|jjƒDƒƒ|_dS) NrcSs g|]\}}|dk r||f‘qS)Nr)rJrrrrrrL·sz"Ticket.setData..rrcss|]\}}||fVqdS)Nr)rJrrrrrú ¼sz!Ticket.setData..cSs g|]\}}|dk r||f‘qS)Nr)rJrrrrrrLÂs)ÚlenÚdictrr rÚzipÚiter)rÚargsÚargvrrrÚsetData²s   $ zTicket.setDatacsŠˆdkr|jS|js|Stˆttdƒttttfƒs|tˆƒrVt ‡fdd„|jj ƒDƒƒSt ˆdƒr|t ‡fdd„|jj ƒDƒƒS|jj ˆ|ƒS)Ncs g|]\}}ˆ|ƒr||f‘qSrr)rJrr)ÚkeyrrrLÐsz"Ticket.getData..Ú__iter__cs g|]\}}|ˆkr||f‘qSrr)rJrr)rZrrrLÕs) r r1r'ÚtypeÚintÚfloatÚboolÚcomplexÚcallablerTrÚhasattrr%)rrZÚdefaultr)rZrÚgetDataÄs zTicket.getDatacCs t|ddƒS)NÚ _banEpochr)r.)rrrrÚbanEpochÙszTicket.banEpochcCs ||_dS)N)re)rr2rrrrfÜs)rrr r r r r r)N)F)r)N)N)NN)#r#Ú __module__Ú __qualname__r-r>rNrPrr&r(r,rrr3r4r5r6r7r9r;r<r=r@rArBrCrErMÚpropertyrOÚsetterrQrYrdrfrrrrr#sB          rc@sNeZdZdddidfdd„Zdd„Zdd„Zdd „Zdd d „Zed d„ƒZ dS)Ú FailTicketNcCsTd|_d|_tj||||||ƒt|tƒsP|dk r6|n|jƒ|_|jjddƒ|_dS)Nrr) Ú _firstTimer rrr1rkr6r r%)rrrrrrrrrrãs  zFailTicket.__init__cCs6||_|jdsd|jd<|s2d|jd<g|jd<dS)z“ Set artificial retry count, normally equal failures / attempt, used in incremental features (BanTimeIncr) to increase retry count for bad IPs rrrrN)r r )rr2rrrÚsetRetryîs    zFailTicket.setRetrycCs|jS)zV Returns failures / attempt count or artificial retry count increased for bad IPs )r )rrrrÚgetRetryùszFailTicket.getRetrycCsN||jkrJ|j||krDtt|jt||jƒ|ƒƒ|_|||_||_dS)z  Adjust time of ticket and current attempts count considering given maxTime as estimation from rate by previous known interval (if it exceeds the findTime) N)r rlr]r)r r^)rrZmaxTimerrrÚ adjustTimeÿs  " zFailTicket.adjustTimercCsR|j|7_|jd|7<|rN|jdrD|jd||jd<n ||jd<dS)Nrr)r r )rrZattemptÚcountrrrÚinc s  zFailTicket.inccCs t|_|S)N)rkr")ÚorrrÚwrapszFailTicket.wrap)Nrr) r#rgrhrrmrnrorqÚ staticmethodrsrrrrrkás    rkc@seZdZedd„ƒZdS)Ú BanTicketcCs t|_|S)N)rur")rrrrrrs"szBanTicket.wrapN)r#rgrhrtrsrrrrru sruN)Ú __author__Z __copyright__Z __license__ZhelpersrZipdnsrZmytimerr#ZlogSysÚobjectrrkrurrrrÚs   ??server/__pycache__/ticket.cpython-36.pyc000064400000021450152231502150014171 0ustar003 UB(d!ã@sldZdZdZddlmZddlmZddlmZee ƒZ Gdd „d e ƒZ Gd d „d e ƒZ Gd d „d e ƒZdS)z Cyril Jaquierz Copyright (c) 2004 Cyril JaquierZGPLé)Ú getLoggeré)ÚIPAddr)ÚMyTimec@s:eZdZdEZd Zd Zd Zd d d id fd d„Zdd„Zdd„Z dd„Z dd„Z dd„Z dd„Z dd„Zdd„Zdd „Zd!d"„ZdFd#d$„ZdGd&d'„ZdHd(d)„Zd*d+„ZdId,d-„ZdJd.d/„Zd0d1„Zd2d3„Zd4d5„Zd6d7„Zed8d9„ƒZejd:d9„ƒZed;d<„ƒZejd=d<„ƒZd>d?„Z dKd@dA„Z!edBdC„ƒZ"e"jdDdC„ƒZ"d S)LÚTicketÚ_idÚ_flagsÚ _banCountÚ_banTimeÚ_timeÚ_dataÚ_retryÚ _lastResetlÿÿÿréNcCs„|j|ƒd|_d|_d|_|dk r(|ntjƒ|_|p8gddœ|_|dk rrx&|jƒD]\}}|dk rT||j|<qTW|r€|j |ƒdS)z{Ticket constructor @param ip the IP address @param time the ban time @param matches (log) lines caused the ticket éN)ÚmatchesÚfailures) ÚsetIDrr r rÚtimer r ÚitemsÚupdate)ÚselfÚiprrÚdataÚticketÚkÚv©rú/usr/lib/python3.6/ticket.pyÚ__init__+s zTicket.__init__c Cs<d|jjjdƒd|j|j|j|j|jd|jjdgƒfS)Nz@%s: ip=%s time=%s bantime=%s bancount=%s #attempts=%d matches=%rÚ.rrréÿÿÿÿ) Ú __class__Ú__name__Úsplitrr r r r Úget)rrrrÚ__str__BszTicket.__str__cCst|ƒS)N)Ústr)rrrrÚ__repr__HszTicket.__repr__c CsJy0|j|jko0t|jdƒt|jdƒko0|j|jkStk rDdSXdS)NrF)rÚroundr r ÚAttributeError)rÚotherrrrÚ__eq__Ks   z Ticket.__eq__cCs4x.|jD]$}t||dƒ}|dk rt|||ƒqWdS)N)Ú __slots__ÚgetattrÚsetattr)rrÚnrrrrrSs  z Ticket.updatecCst|tƒrt|ƒ}||_dS)N)Ú isinstancer'rr)rÚvaluerrrrYs z Ticket.setIDcCs|jS)N)r)rrrrÚgetID_sz Ticket.getIDcCs|jjd|jƒS)Nr)r r%r)rrrrÚgetIPbsz Ticket.getIPcCs ||_dS)N)r )rr2rrrÚsetTimeeszTicket.setTimecCs|jS)N)r )rrrrÚgetTimehszTicket.getTimecCs ||_dS)N)r )rr2rrrÚ setBanTimekszTicket.setBanTimecCs|jdk r|jS|S)N)r )rÚ defaultBTrrrÚ getBanTimenszTicket.getBanTimeFcCs|s||jkr||_dS)N)r )rr2ÚalwaysrrrÚ setBanCountqszTicket.setBanCountcCs|j|7_dS)N)r )rr2rrrÚ incrBanCountuszTicket.incrBanCountcCs|jS)N)r )rrrrÚ getBanCountxszTicket.getBanCountcCs,|jdk r|jn|}|dkr"tjS|j|S)Nrr!)r rÚMAX_TIMEr )rr8ÚbantimerrrÚgetEndOfBanTime{szTicket.getEndOfBanTimecCs.|jdk r|jn|}|dkr dS||j|kS)NrFr!)r r )rrr8r?rrrÚ isTimedOutƒszTicket.isTimedOutcCs||jd<dS)Nr)r )rr2rrrÚ setAttempt‹szTicket.setAttemptcCs |jdS)Nr)r )rrrrÚ getAttemptŽszTicket.getAttemptc Cs6|r||jd<n"y |jd=Wntk r0YnXdS)Nr)r ÚKeyError)rrrrrÚ setMatches‘s   zTicket.setMatchescCsdd„|jjdfƒDƒS)NcSs(g|] }t|ttfƒs|ndj|ƒ‘qS)Ú)r1ÚlistÚtupleÚjoin)Ú.0Úlinerrrú ›sz%Ticket.getMatches..r)r r%)rrrrÚ getMatchesšszTicket.getMatchescCs |jtj@S)N)rrÚRESTORED)rrrrÚrestoredžszTicket.restoredcCs,|r|jtjO_n|jtjM_dS)N)rrrN)rr2rrrrO¡scCs |jtj@S)N)rrÚBANNED)rrrrÚbanned¨sz Ticket.bannedcCs,|r|jtjO_n|jtjM_dS)N)rrrP)rr2rrrrQ«scOs¨t|ƒdkr*tdd„|djƒDƒƒ|_nLt|ƒdkrF|jj|fƒn0t|ƒdkrv|jjdd„tt|ƒgdŽDƒƒt|ƒrŠ|jj|ƒtdd„|jjƒDƒƒ|_dS) NrcSs g|]\}}|dk r||f‘qS)Nr)rJrrrrrrL·sz"Ticket.setData..rrcss|]\}}||fVqdS)Nr)rJrrrrrú ¼sz!Ticket.setData..cSs g|]\}}|dk r||f‘qS)Nr)rJrrrrrrLÂs)ÚlenÚdictrr rÚzipÚiter)rÚargsÚargvrrrÚsetData²s   $ zTicket.setDatacsŠˆdkr|jS|js|Stˆttdƒttttfƒs|tˆƒrVt ‡fdd„|jj ƒDƒƒSt ˆdƒr|t ‡fdd„|jj ƒDƒƒS|jj ˆ|ƒS)Ncs g|]\}}ˆ|ƒr||f‘qSrr)rJrr)ÚkeyrrrLÐsz"Ticket.getData..Ú__iter__cs g|]\}}|ˆkr||f‘qSrr)rJrr)rZrrrLÕs) r r1r'ÚtypeÚintÚfloatÚboolÚcomplexÚcallablerTrÚhasattrr%)rrZÚdefaultr)rZrÚgetDataÄs zTicket.getDatacCs t|ddƒS)NÚ _banEpochr)r.)rrrrÚbanEpochÙszTicket.banEpochcCs ||_dS)N)re)rr2rrrrfÜs)rrr r r r r r)N)F)r)N)N)NN)#r#Ú __module__Ú __qualname__r-r>rNrPrr&r(r,rrr3r4r5r6r7r9r;r<r=r@rArBrCrErMÚpropertyrOÚsetterrQrYrdrfrrrrr#sB          rc@sNeZdZdddidfdd„Zdd„Zdd„Zdd „Zdd d „Zed d„ƒZ dS)Ú FailTicketNcCsTd|_d|_tj||||||ƒt|tƒsP|dk r6|n|jƒ|_|jjddƒ|_dS)Nrr) Ú _firstTimer rrr1rkr6r r%)rrrrrrrrrrãs  zFailTicket.__init__cCs6||_|jdsd|jd<|s2d|jd<g|jd<dS)z“ Set artificial retry count, normally equal failures / attempt, used in incremental features (BanTimeIncr) to increase retry count for bad IPs rrrrN)r r )rr2rrrÚsetRetryîs    zFailTicket.setRetrycCs|jS)zV Returns failures / attempt count or artificial retry count increased for bad IPs )r )rrrrÚgetRetryùszFailTicket.getRetrycCsN||jkrJ|j||krDtt|jt||jƒ|ƒƒ|_|||_||_dS)z  Adjust time of ticket and current attempts count considering given maxTime as estimation from rate by previous known interval (if it exceeds the findTime) N)r rlr]r)r r^)rrZmaxTimerrrÚ adjustTimeÿs  " zFailTicket.adjustTimercCsR|j|7_|jd|7<|rN|jdrD|jd||jd<n ||jd<dS)Nrr)r r )rrZattemptÚcountrrrÚinc s  zFailTicket.inccCs t|_|S)N)rkr")ÚorrrÚwrapszFailTicket.wrap)Nrr) r#rgrhrrmrnrorqÚ staticmethodrsrrrrrkás    rkc@seZdZedd„ƒZdS)Ú BanTicketcCs t|_|S)N)rur")rrrrrrs"szBanTicket.wrapN)r#rgrhrtrsrrrrru sruN)Ú __author__Z __copyright__Z __license__ZhelpersrZipdnsrZmytimerr#ZlogSysÚobjectrrkrurrrrÚs   ??server/__pycache__/transmitter.cpython-36.opt-1.pyc000064400000025246152231502150016230 0ustar003 UB(dG?ã@sRdZdZdZddlZddlZddlmZmZddlm Z ee ƒZ Gdd „d ƒZ dS) z Cyril Jaquierz Copyright (c) 2004 Cyril JaquierZGPLéNé)Ú getLoggerÚlogging)Úversionc@s>eZdZdd„Zdd„Zdd„Zddd „Zd d „Zd d „ZdS)Ú TransmittercCs||_d|_dS)Nr)Ú_Transmitter__serverÚ_Transmitter__quiet)ÚselfZserver©r ú!/usr/lib/python3.6/transmitter.pyÚ__init__,szTransmitter.__init__cCsptjdd|ƒy|j|ƒ}d|f}WnFtk rj}z*tjd||tjƒtjkd�d|f}WYdd}~XnX|S)Néz Command: %rrz"Command %r has failed. Received %r)Úexc_infoé)ÚlogSysÚlogÚ_Transmitter__commandHandlerÚ ExceptionÚerrorZgetEffectiveLevelrÚDEBUG)r ÚcommandÚretZackÚer r r Úproceed6s  zTransmitter.proceedc Cs|d}|dkrdS|dkrr|d}|dkr:td|fƒ‚y |d}Wntk r^d }YnX|jj||ƒ|S|d krŽ|j|dd…d ƒS|d kr¨|j|dd…ƒS|d krÈ|d}|jj|ƒdS|dk�rt|ƒdkrê|jjƒn.|ddk�r|jjƒn|d}|jj |ƒdS|dk�rŒ|dd…}d|_ z4|jj |dd iŽx|dD]}|j |ƒ�qVWWdd|_ |jj |ddiŽXdS|dk�rät|ƒdk�rä|dd…}t|ƒdk�rÖ|ddk�rÖ|jj ƒS|jj d|ƒS|dk�r|jjd|dd…ƒS|dk�r|dd…S|dk�r2tjdƒdS|dk�rrd|_ z"x|dD]}|j |ƒ�qNWWdd|_ XdS|dk�r–|d}tjt|ƒƒdS|dk�rª|jjƒS|dk�rÆ|j|dd…ƒS|dk�râ|j|dd…ƒS|dk�ròtjS|d k�rtj|dƒdStd!ƒ‚dS)"NrZpingZpongÚaddrz--allzReserved name %rrÚautoz multi-setTÚsetÚstartÚstopÚreloadéZbeginFZOKZunbanÚbannedZechoz server-statusz Status: readyz Server readyz server-streamÚsleepZ flushlogsÚgetÚstatusrz config-errorzInvalid command)rÚ IndexErrorrZaddJailÚ_Transmitter__commandSetZ startJailÚlenÚquitZ stopAllJailZstopJailrZ reloadJailsrÚ setUnbanIPr!rÚdebugÚtimer"ÚfloatZ flushLogsÚ_Transmitter__commandGetr$rr)r rÚnameZbackendZoptsÚcmdÚvaluer r r Z__commandHandlerHs”                         zTransmitter.__commandHandlerFcCsF |d}|dkr8|d}|jj|ƒ|jr.dS|jjƒS|dkrt|d}|jj|ƒrh|jr^dS|jjƒStdƒ‚� n¼|dkr°|d}|jj|ƒr¤|jršdS|jjƒStdƒ‚� n€|dkrÚ|d}|jj |ƒ|jrÖdS|S|d k�rø|d}|jj |ƒS|d k�r@|jj |dƒ|jj ƒ}|dk�r*dS|j�r6dS|j S� nð|d k�r”|jj ƒ}|dk�rptjtjd ƒdSt|dƒ|_|j�rŠdS|jS� nœ|d k�rä|jj ƒ}|dk�rÄtjtjdƒdS|d|_|j�rÚdS|jS� nL|ddk�rN|ddk�r|jj|dƒn&|ddk�r.|jj|dƒntdƒ‚|j�rBdS|jj|ƒS|ddk�rŠ|d}|jj||ƒ|j�r~dS|jj|ƒS|ddk�rÖx$|dd…D]}|jj||ƒ�q¦W|j�rÊdS|jj|ƒS|ddk�r|d}|jj||ƒ|j�rdS|jj|ƒS|ddk�rN|d}|jj||ƒ|j�rBdS|jj|ƒS|ddk�rŠ|d}|jj||ƒ|j�r~dS|jj|ƒS|ddk�r$|d}d}t |ƒdk�ræ|dj!ƒdk�rÊd}n|dj!ƒdk�rüt"d ƒ‚nt |ƒdk�rüt"d!ƒ‚|jj#|||ƒ|j�rdS|jj$|ƒS|dd"k�r`|d}|jj%||ƒ|j�rTdS|jj$|ƒS|dd#k�rœ|d}|jj&||ƒ|j�r�dS|jj'|ƒS|dd$k�rÜ|dd…}|jj(||ƒ|j�rÐdS|jj)|ƒS|dd%k�r|dd…}|jj*||ƒ|j�rdS|jj)|ƒS|dd&k�rj|d}|jj+||ƒ|j�rLdS|jj,|ƒ}|�rf|j-ƒSd'S|dd(k�r´|d}|jj.|||d)�|�rœdS|j�r¨dS|jj/|ƒS|dd*k�rôt|dƒ}|jj0||ƒ|j�rèdS|jj/|ƒS|dd+k�r>|d}|jj1|||d)�|�r&dS|j�r2dS|jj2|ƒS|dd,k�r~t|dƒ}|jj3||ƒ|j�rrdS|jj2|ƒS|dd-k�rº|d}|jj4||ƒ|j�r®dS|jj5|ƒS|dd.k�rö|d}|jj6||ƒ|j�rêdS|jj7|ƒS|dd/k�r2|d}|jj8||ƒ|j�r&dS|jj9|ƒS|dd0k�rn|d}|jj:||ƒ|j�rbdS|jj;|ƒS|dd1k�r®|d}|jj<|t|ƒƒ|j�r¢dS|jj=|ƒS|dd2k�rî|d}|jj>|t|ƒƒ|j�râdS|jj?|ƒS|dd3k�r.|d}|jj@|t|ƒƒ|j�r"dS|jjA|ƒS|dd4k�rj|d}|jjB||ƒ|j�r^dS|jjC|ƒS|dd5k�r¢|dd…}|j�r�dS|jjD|f|žŽS|djEd6ƒ�rø|d}|dt d6ƒd…}|jjF|||ƒ|j�rêdS|jjG||ƒS|dd7k� r |dd…}|jjH||ƒS|dd8k� rpd} |dd9k� rN|dd…}nd} |dd…}|jjI||| d:�S|dd;k� rØ|dg} t |ƒdk� r²| jJ|dtKjL|dƒgƒ|jjM|f| žŽ|j� rÐdS| dS|ddd?| ƒ| d}tPtQ| |dƒƒ� r’t | ƒdk� r|tKjL| dƒni}tQ| |ƒf|Žn| d}tR| ||ƒ� q4WdS|d}tPtQ| |dƒƒ� rt |ƒdk� rætKjL|dƒni}|j� rödStQ| |ƒf|ŽS|d}tR| ||ƒ|j� r&dStQ| |ƒStd@|dfƒ‚dS)ANrÚloglevelrÚ logtargetzFailed to change log targetÚ syslogsocketzFailed to change syslog socketZ allowipv6ÚthreadÚdbfileÚ dbmaxmatchesz6dbmaxmatches setting was not in effect since no db yetÚ dbpurgeagez4dbpurgeage setting was not in effect since no db yetZidlerZonTZoffFz*Invalid idle option, must be 'on' or 'off'Ú ignoreselfZ addignoreipZ delignoreipÚ ignorecommandÚ ignorecacheZ addlogpathér ÚtailÚheadz$File option must be 'head' or 'tail'z$Only one file can be added at a timeZ dellogpathÚ logencodingZaddjournalmatchZdeljournalmatchÚ prefregexÚZ addfailregex)ÚmultipleZ delfailregexZaddignoreregexZdelignoreregexÚusednsÚfindtimeÚ datepatternÚ logtimezoneÚ maxmatchesÚmaxretryÚmaxlinesÚbantimeZattemptzbantime.ÚbanipZunbanipz--report-absent)ÚifexistsZ addactionZ delactionÚactionr z %rz9Invalid command %r (no set action or not yet implemented))SrZ setLogLevelrÚ getLogLevelZ setLogTargetÚ getLogTargetrZsetSyslogSocketÚgetSyslogSocketZsetIPv6IsAllowedZsetThreadOptionsZ setDatabaseÚ getDatabaseÚfilenamerrrZMSGÚintÚ maxMatchesÚpurgeageZ setIdleJailZ getIdleJailZ setIgnoreSelfÚ getIgnoreSelfZ addIgnoreIPÚ getIgnoreIPZ delIgnoreIPZsetIgnoreCommandÚgetIgnoreCommandZsetIgnoreCacheÚgetIgnoreCacher'ÚlowerÚ ValueErrorZ addLogPathÚ getLogPathZ delLogPathZsetLogEncodingÚgetLogEncodingZaddJournalMatchÚgetJournalMatchZdelJournalMatchZ setPrefRegexÚ getPrefRegexÚgetRegexZ addFailRegexÚ getFailRegexZ delFailRegexZaddIgnoreRegexÚgetIgnoreRegexZdelIgnoreRegexZ setUseDnsÚ getUseDnsZ setFindTimeÚ getFindTimeZsetDatePatternÚgetDatePatternZsetLogTimeZoneÚgetLogTimeZoneZ setMaxMatchesÚ getMaxMatchesZ setMaxRetryÚ getMaxRetryZ setMaxLinesÚ getMaxLinesZ setBanTimeÚ getBanTimeZ addAttemptIPÚ startswithZsetBanTimeExtraÚgetBanTimeExtraZsetBanIPr)ÚextendÚjsonÚloadsZ addActionZ delActionÚ getActionÚcallableÚgetattrÚsetattr)r rrAr.r0Údbr<ÚvÚoptrKÚargsÚ actionnamerLr/Z actionkeyÚ actionvaluer r r Z __commandSetšs&                                                             zTransmitter.__commandSetcs|d}|dkr|jjƒS|dkr,|jjƒS|dkr>|jjƒS|dkrP|jjƒS|dkrx|jjƒ}|dkrndS|jS�nŒ|dkr |jjƒ}|dkr–dS|jS�nd|dkrÈ|jjƒ}|dkr¾dS|jS�n<|d d k�rì|jj ||d d…ƒS|d d k�r|jj |ƒS|d d k�r |jj |ƒS|d dk�r:|jj |ƒS|d dk�rT|jj |ƒS|d dk�rn|jj|ƒS|d dk�rˆ|jj|ƒS|d dk�r¢|jj|ƒS|d dk�rÎ|jj|ƒ}|�rÊ|jƒSdS|d dk�rè|jj|ƒS|d dk�r|jj|ƒS|d dk�r|jj|ƒS|d dk�r6|jj|ƒS|d dk�rP|jj|ƒS|d dk�rj|jj|ƒS|d dk�r„|jj|ƒS|d dk�rž|jj|ƒS|d dk�r¸|jj|ƒS|d dk�rÒ|jj|ƒS|d dk�r|jj|t|ƒd k�o|d d kd!�S|d jd"ƒ�r8|d td"ƒd…}|jj ||ƒS|d d#k�rZt!|jj"|ƒj#ƒƒS|d d$k�r�|d }|d%}|jj$||ƒ‰t%ˆ|ƒS|d d&k�rÊ|d }|jj$||ƒ‰‡fd'd(„t&ˆƒDƒS|d d)k�r|d }|jj$||ƒ‰‡fd*d(„t&ˆƒDƒSt'd+ƒ‚dS),Nrr1r2r3r4r5r6r7rr!rZlogpathr>Z journalmatchr8Zignoreipr9r:r?r@Z failregexZ ignoreregexrBrCrDrErFrGrHrIrJz --with-time)ZwithTimezbantime.ZactionsrLr Zactionpropertiescs,g|]$}|jdƒ rttˆ|ƒƒ r|‘qS)Ú_)rjrprq)Ú.0Úkey)rLr r ú õs z,Transmitter.__commandGet..Z actionmethodscs*g|]"}|jdƒ rttˆ|ƒƒr|‘qS)ry)rjrprq)rzr{)rLr r r|üsz6Invalid command (no get action or not yet implemented))(rrMrNrOZgetThreadOptionsrPrQrSrTr!r[r\r]rUrVrWrXr^r_r`rarbrcrdrerfrgrhriZ getBanListr'rjrkÚlistZ getActionsÚkeysrorqÚdirr)r rr.rsrtrurwrxr )rLr Z __commandGet›sª                             zTransmitter.__commandGetcCsnt|ƒdkr|jjƒSt|ƒdkr6|d}|jj|ƒSt|ƒdkrb|d}|d}|jj||d�Stdƒ‚dS)Nrrr)ÚflavorzInvalid command (no status))r'rr$Z statusJailr)r rr.r€r r r r$s     zTransmitter.statusN)F) Ú__name__Ú __module__Ú __qualname__r rrr&r-r$r r r r r%s R er) Ú __author__Z __copyright__Z __license__r+rmZhelpersrrr@rr�rrr r r r Ús server/__pycache__/transmitter.cpython-36.pyc000064400000025246152231502150015271 0ustar003 UB(dG?ã@sRdZdZdZddlZddlZddlmZmZddlm Z ee ƒZ Gdd „d ƒZ dS) z Cyril Jaquierz Copyright (c) 2004 Cyril JaquierZGPLéNé)Ú getLoggerÚlogging)Úversionc@s>eZdZdd„Zdd„Zdd„Zddd „Zd d „Zd d „ZdS)Ú TransmittercCs||_d|_dS)Nr)Ú_Transmitter__serverÚ_Transmitter__quiet)ÚselfZserver©r ú!/usr/lib/python3.6/transmitter.pyÚ__init__,szTransmitter.__init__cCsptjdd|ƒy|j|ƒ}d|f}WnFtk rj}z*tjd||tjƒtjkd�d|f}WYdd}~XnX|S)Néz Command: %rrz"Command %r has failed. Received %r)Úexc_infoé)ÚlogSysÚlogÚ_Transmitter__commandHandlerÚ ExceptionÚerrorZgetEffectiveLevelrÚDEBUG)r ÚcommandÚretZackÚer r r Úproceed6s  zTransmitter.proceedc Cs|d}|dkrdS|dkrr|d}|dkr:td|fƒ‚y |d}Wntk r^d }YnX|jj||ƒ|S|d krŽ|j|dd…d ƒS|d kr¨|j|dd…ƒS|d krÈ|d}|jj|ƒdS|dk�rt|ƒdkrê|jjƒn.|ddk�r|jjƒn|d}|jj |ƒdS|dk�rŒ|dd…}d|_ z4|jj |dd iŽx|dD]}|j |ƒ�qVWWdd|_ |jj |ddiŽXdS|dk�rät|ƒdk�rä|dd…}t|ƒdk�rÖ|ddk�rÖ|jj ƒS|jj d|ƒS|dk�r|jjd|dd…ƒS|dk�r|dd…S|dk�r2tjdƒdS|dk�rrd|_ z"x|dD]}|j |ƒ�qNWWdd|_ XdS|dk�r–|d}tjt|ƒƒdS|dk�rª|jjƒS|dk�rÆ|j|dd…ƒS|dk�râ|j|dd…ƒS|dk�ròtjS|d k�rtj|dƒdStd!ƒ‚dS)"NrZpingZpongÚaddrz--allzReserved name %rrÚautoz multi-setTÚsetÚstartÚstopÚreloadéZbeginFZOKZunbanÚbannedZechoz server-statusz Status: readyz Server readyz server-streamÚsleepZ flushlogsÚgetÚstatusrz config-errorzInvalid command)rÚ IndexErrorrZaddJailÚ_Transmitter__commandSetZ startJailÚlenÚquitZ stopAllJailZstopJailrZ reloadJailsrÚ setUnbanIPr!rÚdebugÚtimer"ÚfloatZ flushLogsÚ_Transmitter__commandGetr$rr)r rÚnameZbackendZoptsÚcmdÚvaluer r r Z__commandHandlerHs”                         zTransmitter.__commandHandlerFcCsF |d}|dkr8|d}|jj|ƒ|jr.dS|jjƒS|dkrt|d}|jj|ƒrh|jr^dS|jjƒStdƒ‚� n¼|dkr°|d}|jj|ƒr¤|jršdS|jjƒStdƒ‚� n€|dkrÚ|d}|jj |ƒ|jrÖdS|S|d k�rø|d}|jj |ƒS|d k�r@|jj |dƒ|jj ƒ}|dk�r*dS|j�r6dS|j S� nð|d k�r”|jj ƒ}|dk�rptjtjd ƒdSt|dƒ|_|j�rŠdS|jS� nœ|d k�rä|jj ƒ}|dk�rÄtjtjdƒdS|d|_|j�rÚdS|jS� nL|ddk�rN|ddk�r|jj|dƒn&|ddk�r.|jj|dƒntdƒ‚|j�rBdS|jj|ƒS|ddk�rŠ|d}|jj||ƒ|j�r~dS|jj|ƒS|ddk�rÖx$|dd…D]}|jj||ƒ�q¦W|j�rÊdS|jj|ƒS|ddk�r|d}|jj||ƒ|j�rdS|jj|ƒS|ddk�rN|d}|jj||ƒ|j�rBdS|jj|ƒS|ddk�rŠ|d}|jj||ƒ|j�r~dS|jj|ƒS|ddk�r$|d}d}t |ƒdk�ræ|dj!ƒdk�rÊd}n|dj!ƒdk�rüt"d ƒ‚nt |ƒdk�rüt"d!ƒ‚|jj#|||ƒ|j�rdS|jj$|ƒS|dd"k�r`|d}|jj%||ƒ|j�rTdS|jj$|ƒS|dd#k�rœ|d}|jj&||ƒ|j�r�dS|jj'|ƒS|dd$k�rÜ|dd…}|jj(||ƒ|j�rÐdS|jj)|ƒS|dd%k�r|dd…}|jj*||ƒ|j�rdS|jj)|ƒS|dd&k�rj|d}|jj+||ƒ|j�rLdS|jj,|ƒ}|�rf|j-ƒSd'S|dd(k�r´|d}|jj.|||d)�|�rœdS|j�r¨dS|jj/|ƒS|dd*k�rôt|dƒ}|jj0||ƒ|j�rèdS|jj/|ƒS|dd+k�r>|d}|jj1|||d)�|�r&dS|j�r2dS|jj2|ƒS|dd,k�r~t|dƒ}|jj3||ƒ|j�rrdS|jj2|ƒS|dd-k�rº|d}|jj4||ƒ|j�r®dS|jj5|ƒS|dd.k�rö|d}|jj6||ƒ|j�rêdS|jj7|ƒS|dd/k�r2|d}|jj8||ƒ|j�r&dS|jj9|ƒS|dd0k�rn|d}|jj:||ƒ|j�rbdS|jj;|ƒS|dd1k�r®|d}|jj<|t|ƒƒ|j�r¢dS|jj=|ƒS|dd2k�rî|d}|jj>|t|ƒƒ|j�râdS|jj?|ƒS|dd3k�r.|d}|jj@|t|ƒƒ|j�r"dS|jjA|ƒS|dd4k�rj|d}|jjB||ƒ|j�r^dS|jjC|ƒS|dd5k�r¢|dd…}|j�r�dS|jjD|f|žŽS|djEd6ƒ�rø|d}|dt d6ƒd…}|jjF|||ƒ|j�rêdS|jjG||ƒS|dd7k� r |dd…}|jjH||ƒS|dd8k� rpd} |dd9k� rN|dd…}nd} |dd…}|jjI||| d:�S|dd;k� rØ|dg} t |ƒdk� r²| jJ|dtKjL|dƒgƒ|jjM|f| žŽ|j� rÐdS| dS|ddd?| ƒ| d}tPtQ| |dƒƒ� r’t | ƒdk� r|tKjL| dƒni}tQ| |ƒf|Žn| d}tR| ||ƒ� q4WdS|d}tPtQ| |dƒƒ� rt |ƒdk� rætKjL|dƒni}|j� rödStQ| |ƒf|ŽS|d}tR| ||ƒ|j� r&dStQ| |ƒStd@|dfƒ‚dS)ANrÚloglevelrÚ logtargetzFailed to change log targetÚ syslogsocketzFailed to change syslog socketZ allowipv6ÚthreadÚdbfileÚ dbmaxmatchesz6dbmaxmatches setting was not in effect since no db yetÚ dbpurgeagez4dbpurgeage setting was not in effect since no db yetZidlerZonTZoffFz*Invalid idle option, must be 'on' or 'off'Ú ignoreselfZ addignoreipZ delignoreipÚ ignorecommandÚ ignorecacheZ addlogpathér ÚtailÚheadz$File option must be 'head' or 'tail'z$Only one file can be added at a timeZ dellogpathÚ logencodingZaddjournalmatchZdeljournalmatchÚ prefregexÚZ addfailregex)ÚmultipleZ delfailregexZaddignoreregexZdelignoreregexÚusednsÚfindtimeÚ datepatternÚ logtimezoneÚ maxmatchesÚmaxretryÚmaxlinesÚbantimeZattemptzbantime.ÚbanipZunbanipz--report-absent)ÚifexistsZ addactionZ delactionÚactionr z %rz9Invalid command %r (no set action or not yet implemented))SrZ setLogLevelrÚ getLogLevelZ setLogTargetÚ getLogTargetrZsetSyslogSocketÚgetSyslogSocketZsetIPv6IsAllowedZsetThreadOptionsZ setDatabaseÚ getDatabaseÚfilenamerrrZMSGÚintÚ maxMatchesÚpurgeageZ setIdleJailZ getIdleJailZ setIgnoreSelfÚ getIgnoreSelfZ addIgnoreIPÚ getIgnoreIPZ delIgnoreIPZsetIgnoreCommandÚgetIgnoreCommandZsetIgnoreCacheÚgetIgnoreCacher'ÚlowerÚ ValueErrorZ addLogPathÚ getLogPathZ delLogPathZsetLogEncodingÚgetLogEncodingZaddJournalMatchÚgetJournalMatchZdelJournalMatchZ setPrefRegexÚ getPrefRegexÚgetRegexZ addFailRegexÚ getFailRegexZ delFailRegexZaddIgnoreRegexÚgetIgnoreRegexZdelIgnoreRegexZ setUseDnsÚ getUseDnsZ setFindTimeÚ getFindTimeZsetDatePatternÚgetDatePatternZsetLogTimeZoneÚgetLogTimeZoneZ setMaxMatchesÚ getMaxMatchesZ setMaxRetryÚ getMaxRetryZ setMaxLinesÚ getMaxLinesZ setBanTimeÚ getBanTimeZ addAttemptIPÚ startswithZsetBanTimeExtraÚgetBanTimeExtraZsetBanIPr)ÚextendÚjsonÚloadsZ addActionZ delActionÚ getActionÚcallableÚgetattrÚsetattr)r rrAr.r0Údbr<ÚvÚoptrKÚargsÚ actionnamerLr/Z actionkeyÚ actionvaluer r r Z __commandSetšs&                                                             zTransmitter.__commandSetcs|d}|dkr|jjƒS|dkr,|jjƒS|dkr>|jjƒS|dkrP|jjƒS|dkrx|jjƒ}|dkrndS|jS�nŒ|dkr |jjƒ}|dkr–dS|jS�nd|dkrÈ|jjƒ}|dkr¾dS|jS�n<|d d k�rì|jj ||d d…ƒS|d d k�r|jj |ƒS|d d k�r |jj |ƒS|d dk�r:|jj |ƒS|d dk�rT|jj |ƒS|d dk�rn|jj|ƒS|d dk�rˆ|jj|ƒS|d dk�r¢|jj|ƒS|d dk�rÎ|jj|ƒ}|�rÊ|jƒSdS|d dk�rè|jj|ƒS|d dk�r|jj|ƒS|d dk�r|jj|ƒS|d dk�r6|jj|ƒS|d dk�rP|jj|ƒS|d dk�rj|jj|ƒS|d dk�r„|jj|ƒS|d dk�rž|jj|ƒS|d dk�r¸|jj|ƒS|d dk�rÒ|jj|ƒS|d dk�r|jj|t|ƒd k�o|d d kd!�S|d jd"ƒ�r8|d td"ƒd…}|jj ||ƒS|d d#k�rZt!|jj"|ƒj#ƒƒS|d d$k�r�|d }|d%}|jj$||ƒ‰t%ˆ|ƒS|d d&k�rÊ|d }|jj$||ƒ‰‡fd'd(„t&ˆƒDƒS|d d)k�r|d }|jj$||ƒ‰‡fd*d(„t&ˆƒDƒSt'd+ƒ‚dS),Nrr1r2r3r4r5r6r7rr!rZlogpathr>Z journalmatchr8Zignoreipr9r:r?r@Z failregexZ ignoreregexrBrCrDrErFrGrHrIrJz --with-time)ZwithTimezbantime.ZactionsrLr Zactionpropertiescs,g|]$}|jdƒ rttˆ|ƒƒ r|‘qS)Ú_)rjrprq)Ú.0Úkey)rLr r ú õs z,Transmitter.__commandGet..Z actionmethodscs*g|]"}|jdƒ rttˆ|ƒƒr|‘qS)ry)rjrprq)rzr{)rLr r r|üsz6Invalid command (no get action or not yet implemented))(rrMrNrOZgetThreadOptionsrPrQrSrTr!r[r\r]rUrVrWrXr^r_r`rarbrcrdrerfrgrhriZ getBanListr'rjrkÚlistZ getActionsÚkeysrorqÚdirr)r rr.rsrtrurwrxr )rLr Z __commandGet›sª                             zTransmitter.__commandGetcCsnt|ƒdkr|jjƒSt|ƒdkr6|d}|jj|ƒSt|ƒdkrb|d}|d}|jj||d�Stdƒ‚dS)Nrrr)ÚflavorzInvalid command (no status))r'rr$Z statusJailr)r rr.r€r r r r$s     zTransmitter.statusN)F) Ú__name__Ú __module__Ú __qualname__r rrr&r-r$r r r r r%s R er) Ú __author__Z __copyright__Z __license__r+rmZhelpersrrr@rr�rrr r r r Ús server/__pycache__/utils.cpython-36.opt-1.pyc000064400000023122152231502150015003 0ustar003 UB(dØ.ã@sÆdZdZdZddlZddlZddlZddlZddlZddlZddl m Z ddl Z ddl m Z mZmZddlmZejdkr„ddlZnddlZe eƒZd d iZed d „ejjƒDƒƒZGdd„dƒZdS)z2Serg G. Brester (sebres) and Fail2Ban ContributorszYCopyright (c) 2004 Cyril Jaquier, 2011-2012 Yaroslav Halchenko, 2012-2015 Serg G. BresterZGPLéN)ÚLocké)Ú getLoggerÚ _merge_dictsÚ uni_decode)Ú OrderedDictééam"Command not found". Make sure that all commands in %(realCmd)r are in the PATH of fail2ban-server process (grep -a PATH= /proc/`pidof -x fail2ban-server`/environ). You may want to start "fail2ban-server -f" separately, initiate it with "fail2ban-client reload" in another shell session and observe if additional informative error messages appear in the terminals.ccs$|]\}}|jdƒr||fVqdS)ZSIGN)Ú startswith)Ú.0ÚnameZnum©r ú/usr/lib/python3.6/utils.pyú 7src@s˜eZdZdZdZdZdZedZGdd„deƒZ e dd „ƒZ e d d „ƒZ e ddd„ƒZ e ddd„ƒZejdkr|e dd„ƒZn e dd„ƒZe dd„ƒZdS)ÚUtilszPUtilities provide diverse static methods like executes OS shell commands, etc. rgš™™™™™É?gü©ñÒMbP?édc@sLeZdZdZdd„Zddd„Zdd „Zdd d „Zd d„Zdd„Z dd„Z d S)z Utils.Cachez.A simple cache with a TTL and limit on size cOs |j||Žtƒ|_tƒ|_dS)N)Ú setOptionsrÚ_cacherÚ _Cache__lock)ÚselfÚargsÚkwargsr r rÚ__init__Hs zUtils.Cache.__init__éèé<cCs||_||_dS)N)ÚmaxCountÚmaxTime)rrrr r rrMszUtils.Cache.setOptionscCs t|jƒS)N)Úlenr)rr r rÚ__len__QszUtils.Cache.__len__NcCs6|jj|ƒ}|r2|dtjƒkr(|dS|j|ƒ|S)Nér)rÚgetÚtimeÚunset)rÚkZdefvÚvr r rr Ts   zUtils.Cache.getc Csztjƒ}|j}|j�\t|ƒ|jkrZx4|rX|jdd�\}}|d|kr&t|ƒ|jkr&Pq&W|||jf||<WdQRXdS)NF)Zlastr)r!rrrrÚpopitemr)rr#r$ÚtÚcacheZckZcvr r rÚset\szUtils.Cache.setc Cs$|j�|jj|dƒWdQRXdS)N)rrÚpop)rr#r r rr"lszUtils.Cache.unsetc Cs |j�|jjƒWdQRXdS)N)rrÚclear)rr r rr*pszUtils.Cache.clear)rr)N) Ú__name__Ú __module__Ú __qualname__Ú__doc__rrrr r(r"r*r r r rÚCacheDs  r/cCs>tj|tjƒ}|s|tjO}n |tjM}tj|tj|ƒ|S)N)ÚfcntlZF_GETFLÚosÚ O_NONBLOCKZF_SETFL)ZfhandleÚvalueÚflagsr r rÚ setFBlockModeus   zUtils.setFBlockModecCspd}t|tƒs|g}t|ƒd}x6|jƒD]*\}}|d||f7}|j|ƒ|d7}q*W|d|d|d<|S)ašGenerates new shell command as array, contains map as variables to arguments statement (varsStat), the command (realCmd) used this variables and the list of the arguments, mapped from varsDict Example: buildShellCmd('echo "V2: $v2, V1: $v1"', {"v1": "val 1", "v2": "val 2", "vUnused": "unused var"}) returns: ['v1=$0 v2=$1 vUnused=$2 echo "V2: $v2, V1: $v1"', 'val 1', 'val 2', 'unused var'] Úrz%s=$%s Ú r)Ú isinstanceÚlistrÚitemsÚappend)ÚrealCmdÚvarsDictZvarsStatÚir#r$r r rÚ buildShellCmds     zUtils.buildShellCmdrTFrNc1sœd}}d} d‰} |r6|r*tjˆ|ƒ‰n ttj|ƒ} tˆƒ‰‡‡fdd„} �ytjˆtjtj|| tj d�‰ˆj ƒ} | dkr¤‡fdd„} tj | |tj ƒ} | r¤| d} | dk�rZ| rÀ| t jƒd} tjdˆ|fƒtjˆjƒ} tj| tjƒtjtjƒˆj ƒ} | dk�s|�rÀsz"Utils.executeCmd..)ÚstdoutÚstderrÚshellÚenvZ preexec_fncsˆjƒ}|dk rd|fSdS)NT)Úpoll)Úretcode)Úpopenr rÚ_popen_wait_endÉsz)Utils.executeCmd.._popen_wait_endrz!%x -- timed out after %s seconds.z%s -- failed with %sFrz ... -- failed to read stdout %sr6z%x -- stdout: %rz ... -- failed to read stderr %sz%x -- stderr: %rz%x -- returned successfully %iTz%x -- unable to kill PID %ié€z&%x -- killed with %s (return code: %s)z signal %iz%x -- returned %izHINT on %i: %s).rr?rr1ÚenvironÚidÚ subprocessÚPopenÚPIPEÚsetsidrIÚwait_forÚDEFAULT_SHORTEST_INTERVALÚloggingZERRORr@ÚerrorÚgetpgidÚpidÚkillpgÚsignalÚSIGTERMr!ÚsleepÚDEFAULT_SLEEP_INTERVALÚSIGKILLÚ pid_existsÚOSErrorÚDEBUGZgetEffectiveLevelrEr5ÚreadÚIOErrorÚ splitlinesrArrFÚcloseÚdebugÚsignamer Ú_RETCODE_HINTSÚinfoÚlocalsr)r<ÚtimeoutrGÚoutputZtout_kill_treeZ success_codesr=rErFrJrHZlogCmdrLZpgidÚeZ std_levelÚlÚsuccessZsigcodeÚmsgr )rKr<rCrÚ executeCmd–s¶               (""      zUtils.executeCmdcsvd}xl|ƒ}|r|S|rFd}}t|ƒsBtjƒ|‰‡fdd„}n|}|ƒrNPt||pZtjtjƒ}tj|ƒqW|S)a5Wait until condition expression `cond` is True, up to `timeout` sec Parameters ---------- cond : callable The expression to check condition (should return equivalent to bool True if wait successful). timeout : float or callable The time out for end of wait (in seconds or callable that returns True if timeout occurred). interval : float (optional) Polling start interval for wait cycle in seconds. Returns ------- variable The return value of the last call of `cond`, logical False (or None, 0, etc) if timeout occurred. rrcs tjƒˆkS)N)r!r )Útime0r rrD<sz Utils.wait_for..)Úcallabler!Úminrr^ÚDEFAULT_SLEEP_TIMEr])ZcondrlZintervalZiniÚretZstmZ timeout_exprr )rsrrTs  zUtils.wait_forÚposixcCsVddl}|dkrdSytj|dƒWn(tk rL}z |j|jkSd}~XnXdSdS)z6Check whether pid exists in the current process table.rNFT)Úerrnor1ÚkillraZEPERM)rYryrnr r rr`HszUtils.pid_existscCs@ddl}|jj}d}|j|d|ƒ}|dkr8|j|ƒdSdSdS)NriTF)ÚctypesZwindllÚkernel32Z OpenProcessZ CloseHandle)rYr{r|Z SYNCHRONIZEZprocessr r rr`Us cCsFtjjtjj|ƒƒd}tjdkr6tjj||ƒj ƒ}n t j ||ƒ}|S)Nrr)rr) r1ÚpathÚsplitextÚbasenameÚsysÚ version_infoÚ importlibÚ machineryÚSourceFileLoaderÚ load_moduleÚimpZ load_source)Z pythonModuleZpythonModuleNameÚmodr r rÚload_python_modulebs zUtils.load_python_module©r)rTFTr‰N)N)r+r,r-r.rvr^ZDEFAULT_SHORT_INTERVALrUÚobjectr/Ú staticmethodr5r?rrrTr1r r`rˆr r r rr:s$1   )  r)rr)Ú __author__Z __copyright__Z __license__r0rVr1r[rPr€Z threadingrr!ZhelpersrrrÚ collectionsrr�Zimportlib.machineryr‚r†r+r@riÚdictÚ__dict__r:rhrr r r rÚs(     server/__pycache__/utils.cpython-36.pyc000064400000023122152231502150014044 0ustar003 UB(dØ.ã@sÆdZdZdZddlZddlZddlZddlZddlZddlZddl m Z ddl Z ddl m Z mZmZddlmZejdkr„ddlZnddlZe eƒZd d iZed d „ejjƒDƒƒZGdd„dƒZdS)z2Serg G. Brester (sebres) and Fail2Ban ContributorszYCopyright (c) 2004 Cyril Jaquier, 2011-2012 Yaroslav Halchenko, 2012-2015 Serg G. BresterZGPLéN)ÚLocké)Ú getLoggerÚ _merge_dictsÚ uni_decode)Ú OrderedDictééam"Command not found". Make sure that all commands in %(realCmd)r are in the PATH of fail2ban-server process (grep -a PATH= /proc/`pidof -x fail2ban-server`/environ). You may want to start "fail2ban-server -f" separately, initiate it with "fail2ban-client reload" in another shell session and observe if additional informative error messages appear in the terminals.ccs$|]\}}|jdƒr||fVqdS)ZSIGN)Ú startswith)Ú.0ÚnameZnum©r ú/usr/lib/python3.6/utils.pyú 7src@s˜eZdZdZdZdZdZedZGdd„deƒZ e dd „ƒZ e d d „ƒZ e ddd„ƒZ e ddd„ƒZejdkr|e dd„ƒZn e dd„ƒZe dd„ƒZdS)ÚUtilszPUtilities provide diverse static methods like executes OS shell commands, etc. rgš™™™™™É?gü©ñÒMbP?édc@sLeZdZdZdd„Zddd„Zdd „Zdd d „Zd d„Zdd„Z dd„Z d S)z Utils.Cachez.A simple cache with a TTL and limit on size cOs |j||Žtƒ|_tƒ|_dS)N)Ú setOptionsrÚ_cacherÚ _Cache__lock)ÚselfÚargsÚkwargsr r rÚ__init__Hs zUtils.Cache.__init__éèé<cCs||_||_dS)N)ÚmaxCountÚmaxTime)rrrr r rrMszUtils.Cache.setOptionscCs t|jƒS)N)Úlenr)rr r rÚ__len__QszUtils.Cache.__len__NcCs6|jj|ƒ}|r2|dtjƒkr(|dS|j|ƒ|S)Nér)rÚgetÚtimeÚunset)rÚkZdefvÚvr r rr Ts   zUtils.Cache.getc Csztjƒ}|j}|j�\t|ƒ|jkrZx4|rX|jdd�\}}|d|kr&t|ƒ|jkr&Pq&W|||jf||<WdQRXdS)NF)Zlastr)r!rrrrÚpopitemr)rr#r$ÚtÚcacheZckZcvr r rÚset\szUtils.Cache.setc Cs$|j�|jj|dƒWdQRXdS)N)rrÚpop)rr#r r rr"lszUtils.Cache.unsetc Cs |j�|jjƒWdQRXdS)N)rrÚclear)rr r rr*pszUtils.Cache.clear)rr)N) Ú__name__Ú __module__Ú __qualname__Ú__doc__rrrr r(r"r*r r r rÚCacheDs  r/cCs>tj|tjƒ}|s|tjO}n |tjM}tj|tj|ƒ|S)N)ÚfcntlZF_GETFLÚosÚ O_NONBLOCKZF_SETFL)ZfhandleÚvalueÚflagsr r rÚ setFBlockModeus   zUtils.setFBlockModecCspd}t|tƒs|g}t|ƒd}x6|jƒD]*\}}|d||f7}|j|ƒ|d7}q*W|d|d|d<|S)ašGenerates new shell command as array, contains map as variables to arguments statement (varsStat), the command (realCmd) used this variables and the list of the arguments, mapped from varsDict Example: buildShellCmd('echo "V2: $v2, V1: $v1"', {"v1": "val 1", "v2": "val 2", "vUnused": "unused var"}) returns: ['v1=$0 v2=$1 vUnused=$2 echo "V2: $v2, V1: $v1"', 'val 1', 'val 2', 'unused var'] Úrz%s=$%s Ú r)Ú isinstanceÚlistrÚitemsÚappend)ÚrealCmdÚvarsDictZvarsStatÚir#r$r r rÚ buildShellCmds     zUtils.buildShellCmdrTFrNc1sœd}}d} d‰} |r6|r*tjˆ|ƒ‰n ttj|ƒ} tˆƒ‰‡‡fdd„} �ytjˆtjtj|| tj d�‰ˆj ƒ} | dkr¤‡fdd„} tj | |tj ƒ} | r¤| d} | dk�rZ| rÀ| t jƒd} tjdˆ|fƒtjˆjƒ} tj| tjƒtjtjƒˆj ƒ} | dk�s|�rÀsz"Utils.executeCmd..)ÚstdoutÚstderrÚshellÚenvZ preexec_fncsˆjƒ}|dk rd|fSdS)NT)Úpoll)Úretcode)Úpopenr rÚ_popen_wait_endÉsz)Utils.executeCmd.._popen_wait_endrz!%x -- timed out after %s seconds.z%s -- failed with %sFrz ... -- failed to read stdout %sr6z%x -- stdout: %rz ... -- failed to read stderr %sz%x -- stderr: %rz%x -- returned successfully %iTz%x -- unable to kill PID %ié€z&%x -- killed with %s (return code: %s)z signal %iz%x -- returned %izHINT on %i: %s).rr?rr1ÚenvironÚidÚ subprocessÚPopenÚPIPEÚsetsidrIÚwait_forÚDEFAULT_SHORTEST_INTERVALÚloggingZERRORr@ÚerrorÚgetpgidÚpidÚkillpgÚsignalÚSIGTERMr!ÚsleepÚDEFAULT_SLEEP_INTERVALÚSIGKILLÚ pid_existsÚOSErrorÚDEBUGZgetEffectiveLevelrEr5ÚreadÚIOErrorÚ splitlinesrArrFÚcloseÚdebugÚsignamer Ú_RETCODE_HINTSÚinfoÚlocalsr)r<ÚtimeoutrGÚoutputZtout_kill_treeZ success_codesr=rErFrJrHZlogCmdrLZpgidÚeZ std_levelÚlÚsuccessZsigcodeÚmsgr )rKr<rCrÚ executeCmd–s¶               (""      zUtils.executeCmdcsvd}xl|ƒ}|r|S|rFd}}t|ƒsBtjƒ|‰‡fdd„}n|}|ƒrNPt||pZtjtjƒ}tj|ƒqW|S)a5Wait until condition expression `cond` is True, up to `timeout` sec Parameters ---------- cond : callable The expression to check condition (should return equivalent to bool True if wait successful). timeout : float or callable The time out for end of wait (in seconds or callable that returns True if timeout occurred). interval : float (optional) Polling start interval for wait cycle in seconds. Returns ------- variable The return value of the last call of `cond`, logical False (or None, 0, etc) if timeout occurred. rrcs tjƒˆkS)N)r!r )Útime0r rrD<sz Utils.wait_for..)Úcallabler!Úminrr^ÚDEFAULT_SLEEP_TIMEr])ZcondrlZintervalZiniÚretZstmZ timeout_exprr )rsrrTs  zUtils.wait_forÚposixcCsVddl}|dkrdSytj|dƒWn(tk rL}z |j|jkSd}~XnXdSdS)z6Check whether pid exists in the current process table.rNFT)Úerrnor1ÚkillraZEPERM)rYryrnr r rr`HszUtils.pid_existscCs@ddl}|jj}d}|j|d|ƒ}|dkr8|j|ƒdSdSdS)NriTF)ÚctypesZwindllÚkernel32Z OpenProcessZ CloseHandle)rYr{r|Z SYNCHRONIZEZprocessr r rr`Us cCsFtjjtjj|ƒƒd}tjdkr6tjj||ƒj ƒ}n t j ||ƒ}|S)Nrr)rr) r1ÚpathÚsplitextÚbasenameÚsysÚ version_infoÚ importlibÚ machineryÚSourceFileLoaderÚ load_moduleÚimpZ load_source)Z pythonModuleZpythonModuleNameÚmodr r rÚload_python_modulebs zUtils.load_python_module©r)rTFTr‰N)N)r+r,r-r.rvr^ZDEFAULT_SHORT_INTERVALrUÚobjectr/Ú staticmethodr5r?rrrTr1r r`rˆr r r rr:s$1   )  r)rr)Ú __author__Z __copyright__Z __license__r0rVr1r[rPr€Z threadingrr!ZhelpersrrrÚ collectionsrr�Zimportlib.machineryr‚r†r+r@riÚdictÚ__dict__r:rhrr r r rÚs(     server/__pycache__/__init__.cpython-36.opt-1.pyc000064400000000334152231502150015402 0ustar003 TB(dÎã@sdZdZdZdS)z Cyril Jaquierz Copyright (c) 2004 Cyril JaquierZGPLN)Ú __author__Z __copyright__Z __license__©rrú/usr/lib/python3.6/__init__.pyÚsserver/__pycache__/__init__.cpython-36.pyc000064400000000334152231502150014443 0ustar003 TB(dÎã@sdZdZdZdS)z Cyril Jaquierz Copyright (c) 2004 Cyril JaquierZGPLN)Ú __author__Z __copyright__Z __license__©rrú/usr/lib/python3.6/__init__.pyÚsserver/__pycache__/action.cpython-36.opt-1.pyc000064400000064736152231502150015140 0ustar003 TB(d@wã@svdZdZdZddlZddlZddlZddlZddlZddlZddl Z ddl Z ddl m Z yddl mZWn ek rˆddlmZYnXddlmZdd lmZdd lmZdd lmZd d lmZmZmZmZmZmZm Z ee!ƒZ"e j#ƒZ$ej%Z&ej'dƒZ(d#Z)ej'dƒZ*dd„dd„dœZ+dddœZ,e,j-e+ƒGdd„dee.ƒZ/Gdd„de.e d�Z0ddd œZ1Gd!d"„d"e0ƒZ2dS)$z'Cyril Jaquier and Fail2Ban Contributorsz>Copyright (c) 2004 Cyril Jaquier, 2011-2012 Yaroslav HalchenkoZGPLéN)ÚABCMeta)ÚMutableMappingé)Ú mapTag2Opt)ÚDNSUtils)ÚMyTime)ÚUtilsé)Ú getLoggerÚ_merge_copy_dictsÚ splitwordsÚsubstituteRecursiveTagsÚ uni_stringÚTAG_CREÚMAX_TAG_REPLACE_COUNTzÚinet4Úinet6z^(\w+)\?(family)=(.*)$cCsttjdd�ƒS)NT)Úfqdn)ÚstrrÚ getHostname©rrú/usr/lib/python3.6/action.pyÚ?srcCsttjdd�ƒS)NF)r)rrrrrrrr@s)z fq-hostnamez sh-hostnameÚ ú )ÚbrZspc@s‚eZdZdZfZd!Zdd„Zd"d d „Zd#d d „ZeZ d$dd„Z dd„Z dd„Z dd„Z dd„Zdd„Zdd„Zdd„Zdd „ZdS)%Ú CallingMapa"A Mapping type which returns the result of callable values. `CallingMap` behaves similar to a standard python dictionary, with the exception that any values which are callable, are called and the result is returned as the value. No error handling is in place, such that any errors raised in the callable will raised as usual. Actual dictionary is stored in property `data`, and can be accessed to obtain original callable values. Attributes ---------- data : dict The dictionary data which can be accessed to obtain items uncalled ÚdataÚstorageÚ immutableÚ __org_datacOstƒ|_d|_t||Ž|_dS)NT)Údictrrr)ÚselfÚargsÚkwargsrrrÚ__init___szCallingMap.__init__Tc Cs4tƒ|_y |j|_Wntk r(YnX||_dS)N)r!rÚ_CallingMap__org_datarÚAttributeErrorr)r"rrrrÚresetds  zCallingMap.resetFcCsd|jj|j|tƒfS)Nz%s(%r))Ú __class__Ú__name__Ú_asdictr)r"Ú calculatedrrrÚ_asreprlszCallingMap._asreprNc s„tˆjfˆjŽ}|s.t‡fdd„|jƒDƒƒSxPt|jƒƒD]@\}}t|ƒrusz%CallingMap._asdict..)r!rrÚitemsÚlistr.Ú __getitem__)r"r,ZcheckerÚdr1r2r)r"rr+rs   zCallingMap._asdictc Cs2y|j|}Wntk r,|j|}YnX|S)N)rÚKeyErrorr)r"ÚkeyÚvaluerrrÚ getRawItem„s zCallingMap.getRawItemc Csdy|j|}Wntk r,|j|}YnXt|ƒr`t|dƒrP|jjrP||ƒn|ƒ}||j|<|S)NÚ__code__)rr8rr.Úhasattrr<Ú co_argcount)r"r9r:rrrr6‹s  zCallingMap.__getitem__cCs:|jr,|jjƒ|_|j|_|jjƒ|_d|_||j|<dS)NF)rrÚcopyrr&)r"r9r:rrrÚ __setitem__–s   zCallingMap.__setitem__cCstd|ƒ‚dS)NzKey %r was deleted)r8)r"r9rrrZ __unavailableŸszCallingMap.__unavailablec CsZ|jr,|jjƒ|_|j|_|jjƒ|_d|_y |j|=Wntk rLYnX|j|=dS)NF)rrr?rr&r8)r"r9rrrÚ __delitem__¢s   zCallingMap.__delitem__cCs t|jƒS)N)Úiterr)r"rrrÚ__iter__¯szCallingMap.__iter__cCs t|jƒS)N)Úlenr)r"rrrÚ__len__²szCallingMap.__len__cCs|jt|j|jƒƒS)N)r)r rr)r"rrrr?µszCallingMap.copy)rrrr )T)F)FN)r*Ú __module__Ú __qualname__Ú__doc__r/Ú __slots__r%r(r-Ú__repr__r+r;r6r@Z_CallingMap__unavailablerArCrEr?rrrrrJs       rc@sXeZdZdZedd„ƒZdd„Zdd„Zdd „Zd d „Z d d „Z e dd„ƒZ dd„Z dS)Ú ActionBaseaìAn abstract base class for actions in Fail2Ban. Action Base is a base definition of what methods need to be in place to create a Python based action for Fail2Ban. This class can be inherited from to ease implementation. Required methods: - __init__(jail, name) - start() - stop() - ban(aInfo) - unban(aInfo) Called when action is created, but before the jail/actions is started. This should carry out necessary methods to initialise the action but not "start" the action. Parameters ---------- jail : Jail The jail in which the action belongs to. name : str Name assigned to the action. Notes ----- Any additional arguments specified in `jail.conf` or passed via `fail2ban-client` will be passed as keyword arguments. cCs*d}x |D]}tt||dƒƒs dSq WdS) NÚstartÚstopÚbanÚrebanÚunbanFT)rLrMrNrOrP)r.Úgetattr)ÚclsÚCZrequiredÚmethodrrrÚ__subclasshook__Øs zActionBase.__subclasshook__cCs"||_||_td|jjƒ|_dS)Nz fail2ban.%s)Ú_jailÚ_namer r)r*Ú_logSys)r"ÚjailÚnamerrrr%æszActionBase.__init__cCsdS)z,Executed when the jail/action is started. Nr)r"rrrrLëszActionBase.startcCsdS)z,Executed when the jail/action is stopped. Nr)r"rrrrMðszActionBase.stopcCsdS)zŒExecuted when a ban occurs. Parameters ---------- aInfo : dict Dictionary which includes information in relation to the ban. Nr)r"ÚaInforrrrNõs zActionBase.bancCs |j|ƒS)zŒExecuted when a ban occurs. Parameters ---------- aInfo : dict Dictionary which includes information in relation to the ban. )rN)r"r[rrrrOs zActionBase.rebancCsdS)NFr)r"rrrÚ _prolongable szActionBase._prolongablecCsdS)z�Executed when a ban expires. Parameters ---------- aInfo : dict Dictionary which includes information in relation to the ban. Nr)r"r[rrrrPs zActionBase.unbanN)r*rFrGrHÚ classmethodrUr%rLrMrNrOÚpropertyr\rPrrrrrK¹s   rK)Ú metaclassÚ str2secondsÚignore)ÚtimeoutZbantimecs’eZdZdZedPƒZdd„Z‡fdd„Zed d „ƒZ d d „Z e Z d d„Z e dd„ƒZe dd„ƒZdd„Zdd„Zgdfdd„Ze dd„ƒZe dd„ƒZe dd„ƒZd d!„ZdQd#d$„ZdRd&d'„Ze d(d)„ƒZd*d+„Zd,d-„Zd.d/„Zd0d1„Zd2d3„ZdSd4d5„Zd6d7„Z dTd8d9„Z!e"j#d:ƒZ$ed;d<„ƒZ%edUd>d?„ƒZ&e"j#d@ƒZ$e"j#dAƒZ'edVdBdC„ƒZ(e dDdE„ƒZ)dFdG„Z*dWdIdJ„Z+dXdKdL„Z,e-dYdNdO„ƒZ.‡Z/S)ZÚ CommandActionaÂA action which executes OS shell commands. This is the default type of action which Fail2Ban uses. Default sets all commands for actions as empty string, such no command is executed. Parameters ---------- jail : Jail The jail in which the action belongs to. name : str Name assigned to the action. Attributes ---------- actionban actioncheck actionreban actionreload actionrepair actionstart actionstop actionunban timeout ÚmatchesÚ ipmatchesÚ ipjailmatchesc CsTd|_z@d|_d|_d|_d|_d|_d|_d|_d|_d|_ d|_ Wdd|_XdS)z8 Clear all lists/dicts parameters (used by reloading) ré<ÚNr) Ú_CommandAction__initrbZ actionstartZ actionbanÚ actionrebanZ actionunbanÚ actioncheckZ actionrepairZ actionflushZ actionstopZ actionreload)r"rrrÚclearAllParams>s zCommandAction.clearAllParamscsHtt|ƒj||ƒd|_d|_i|_i|_|jƒ|jj d|j ƒdS)Nrz Created %s) Úsuperrcr%riÚ_CommandAction__propertiesÚ_CommandAction__startedÚ_CommandAction__substCacherlrXÚdebugr))r"rYrZ)r)rrr%XszCommandAction.__init__cCstS)N)ÚNotImplemented)rRrSrrrrUaszCommandAction.__subclasshook__cCst|jdƒ rf|j rft|ƒ rftj|ƒ}|dkr4dS|dkrFtj|ƒ}d|_|jj ƒ|j j d||ƒ||j |<dS)NÚ_rar`z Set %s = %r) Ú startswithrir.ÚWRAP_CMD_PARAMSÚgetrr`rnrpÚclearrXrqÚ__dict__)r"rZr:ZwrprrrÚ __setattr__es   zCommandAction.__setattr__cCs4|jdƒs(d|_|jjƒ|jjd|ƒ|j|=dS)Nrsz Unset %s)rtrnrprwrXrqrx)r"rZrrrÚ __delattr__ws   zCommandAction.__delattr__cs2ˆjdk rˆjSt‡fdd„tˆƒDƒƒˆ_ˆjS)z`A dictionary of the actions properties. This is used to substitute "tags" in the commands. Nc3s8|]0}|jdƒ rttˆ|ƒƒ r|tˆ|ƒfVqdS)rsN)rtr.rQ)r0r9)r"rrr3Œsz,CommandAction._properties..)rnr!Údir)r"r)r"rÚ _properties�s   zCommandAction._propertiescCs|jS)N)rp)r"rrrÚ _substCache’szCommandAction._substCachecCsH|j||j|rd|nd|jd�}| s0d|kr4|S|j|d|iƒ}|S)Nzfamily=rh)Ú conditionalÚcacheú<Úfamily)Ú replaceTagr|rpÚreplaceDynamicTags)r"Útagr�ÚcmdrrrÚ _getOperation–s  zCommandAction._getOperationc sîd|f}t|ƒsLtˆƒs,|jj|iƒjˆƒS‡fdd„|jj|iƒjƒDƒS|d}|r”y|j|}Wn"tk rˆi}|j|<YnX||ˆ<nVy@|j|}|jˆƒ}x&t|jƒƒD]\‰}||kr¸|ˆ=q¸WWntk rèYnXdS)z? Get, set or delete command of operation considering family. Z__eOpCmdcsg|]\}}ˆ|ƒr|‘qSrr)r0Úfr2)r�rrú ©sz4CommandAction._operationExecuted..rN)rDr.rprvr4r8Úpopr5)r"r„r�r#r9r…Zfamdr2r)r�rÚ_operationExecuted¡s(    z CommandAction._operationExecutedNc sFd}d}|s dd„|jjƒDƒ}�x|D]ø‰yÆ|j|ˆƒ}d}|rÌ||j|‡fdd„ƒkrÌ|} |jr¶t|jjddƒ} | s–|jjjdƒ} t|jjd| ƒt j ƒ| d <ˆ| d <|j || ƒ} |j | |j ƒ}||M}|rÚ|ˆ|ƒ|j|ˆ|rê|ndƒWq(tk �r} zd }| }WYdd} ~ Xq(Xq(W|�sBtd ||j|j|fƒ‚|S) z³Executes the operation commands (like "actionstart", "actionstop", etc). Replace the tags in the action command with actions properties and executes the resulting command. Tz Script errorcSsg|]\}}|r|‘qSrr)r0Úfamoperr2rrrrˆÅsz3CommandAction._executeOperation..cs|ˆkS)Nr)r‡)r‹rrrËsz1CommandAction._executeOperation..Z actionInfoNÚtimer�FzError %s action %s/%s: %r)ror4r†rŠrVrQÚactionsZ_getActionInfoÚsetattrrrŒrƒÚ executeCmdrbÚ ValueErrorÚ RuntimeErrorrW) r"r„Z operationr�Ú afterExecÚresÚerrr…ÚretÚrealCmdr[Úer)r‹rÚ_executeOperation»s:     zCommandAction._executeOperationcCsJ|jjdƒ}|dk r|Sd}x|jD]}tj|ƒr$d}Pq$W||jd<|S)NZ__hasCondSectionFT)r|rvÚCONDITIONAL_FAM_REÚmatch)r"r2r1rrrÚ_hasCondSectionâs    zCommandAction._hasCondSectioncCsn|jjdƒ}|r|S|jjdƒ}|r>t|ttfƒ r>t|ƒ}n"|jrZtƒrRddgndg}ndg}||jd<|S)NZ __familiesZfamiliesrrrh)r|rvÚ isinstancer5Úsetr r›Ú allowed_ipv6)r"r2rrrÚ _familiesïs    zCommandAction._familiescCs,|jjdƒ}|dk r|S|j}||jd<|S)z1Checks the action depends on family (conditional)Zactionstart_on_demandN)r|rvr›)r"r2rrrÚ_startOnDemandþs   zCommandAction._startOnDemandcCs|jƒS)zŽExecutes the "actionstart" command. Replace the tags in the action command with actions properties and executes the resulting command. )Ú_start)r"rrrrL szCommandAction.startFcs\ˆjr|s&dSn| r&ˆjj|ƒr&dS|dk r4|gnˆj}‡fdd„}ˆjdd||d�}|S)zŽExecutes the "actionstart" command. Replace the tags in the action command with actions properties and executes the resulting command. TNcs |rˆjd|dƒdˆj|<dS)Nz r)rŠro)r�r•)r"rrÚ_startedsz&CommandAction._start.._startedz Zstarting)r�r’)r rorvrŸr˜)r"r�Ú forceStartr¢r•r)r"rr¡s zCommandAction._startú cCs`|jddƒ}|jr,|jj|ƒs,|j|dd�|j||ƒsDtd|ƒ‚|jj|dƒdB|j|<dS) a)Executes the given command ("actionban" or "actionreban"). Replaces the tags in the action command with actions properties and ban information, and executes the resulting command. Parameters ---------- aInfo : dict Dictionary which includes information in relation to the ban. r�rhT)r£zError banning %(ip)sréN)rvr ror¡Ú _processCmdr‘)r"r[r…r�rrrrN%s    zCommandAction.bancCs t|dƒo|jot|jƒjƒ S)NÚ actionprolong)r=r§rÚisspace)r"rrrr\;szCommandAction._prolongablecCs|jd|ƒstd|ƒ‚dS)aExecutes the "actionprolong" command. Replaces the tags in the action command with actions properties and ban information, and executes the resulting command. Parameters ---------- aInfo : dict Dictionary which includes information in relation to the ban. zzError prolonging %(ip)sN)r¦r‘)r"r[rrrÚprolong@s zCommandAction.prolongcCs:|jddƒ}|jj|dƒd@r6|jd|ƒs6td|ƒ‚dS)aExecutes the "actionunban" command. Replaces the tags in the action command with actions properties and ban information, and executes the resulting command. Parameters ---------- aInfo : dict Dictionary which includes information in relation to the ban. r�rhrr z zError unbanning %(ip)sN)rvror¦r‘)r"r[r�rrrrPOs  zCommandAction.unbancCs|j||jrdndƒS)aDExecutes the "actionreban" command if available, otherwise simply repeat "actionban". Replaces the tags in the action command with actions properties and ban information, and executes the resulting command. Parameters ---------- aInfo : dict Dictionary which includes information in relation to the ban. z z )rNrj)r"r[rrrrO`s zCommandAction.rebancs:dd„ˆjjƒDƒ}|sdS‡fdd„}ˆjdd||d�S) aExecutes the "actionflush" command. Command executed in order to flush all bans at once (e. g. by stop/shutdown the system), instead of unbanning of each single ticket. Replaces the tags in the action command with actions properties and executes the resulting command. cSs g|]\}}|d@dkr|‘qS)r¥r)r0r‡r2rrrrˆysz'CommandAction.flush..Tcs&|r"ˆjj|ƒr"ˆj|dM<dS)Nr éýÿÿÿ)rorv)r�r•)r"rrÚ _afterFlush}sz(CommandAction.flush.._afterFlushz Zflushing)r�r’)ror4r˜)r"r�r«r)r"rÚflushos  zCommandAction.flushcCs|jƒS)zŽExecutes the "actionstop" command. Replaces the tags in the action command with actions properties and executes the resulting command. )Ú_stop)r"rrrrM‚szCommandAction.stopcs||dkr,dd„ˆjjƒDƒ}|s$dSiˆ_n2yˆj|dM<|g}Wntk r\dSX‡fdd„}ˆjdd ||d �S) zŽExecutes the "actionstop" command. Replaces the tags in the action command with actions properties and executes the resulting command. NcSsg|]\}}|r|‘qSrr)r0r‡r2rrrrˆ’sz'CommandAction._stop..Trcs|rˆjd|dƒdS)Nz )rŠ)r�r•)r"rrÚ_stoppedœsz%CommandAction._stop.._stoppedz Zstopping)r�r’)ror4r8r˜)r"r�r®r)r"rr­Šs  zCommandAction._stopcKs |jddƒS)zÿExecutes the "actionreload" command. Parameters ---------- kwargs : dict Currently unused, because CommandAction do not support initOpts Replaces the tags in the action command with actions properties and executes the resulting command. zZ reloading)r˜)r"r$rrrÚreload¡s zCommandAction.reloadcCs\d}|jrXxLt|jjƒƒD]:\}}|r|j||ƒ rd|j|<|jd|dƒ|dM}qW|S)zFExecutes the invariant check with repair if expected (conditional). Trz NF)rkr5ror4Ú_invariantCheckrŠ)r"Ú beforeRepairr•r�ZstartedrrrÚconsistencyCheck®s  zCommandAction.consistencyCheckz[\\#&;`|*?~<>^()\[\]{}$'"\n\r]cs(dddœ‰‡fdd„}|jj||ƒ}|S)a5Escape characters which may be used for command injection. Parameters ---------- value : str A string of which characters will be escaped. Returns ------- str `value` with certain characters escaped. Notes ----- The following characters are escaped:: \#&;`|*?~<>^()[]{}$'" r1Úr)rú cs|jƒ}dˆj||ƒS)Nú\)Úgrouprv)ÚmÚc)Ú_map2crrÚ substCharÔsz*CommandAction.escapeTag..substChar)Ú ESCAPE_CREÚsub)rRr:rºr)r¹rÚ escapeTag¾s  zCommandAction.escapeTagrhc s>d|kr |S|dk r:|ˆf}y||Stk r8YnXt|tƒ}|‰|s¶d‰}|dk rŒdt|ƒˆf}y ||‰Wntk rŠYnXˆdkr¦t|ˆˆj|d�‰|dk r¶ˆ||<ttƒ‰‡‡‡‡fdd„} td} xNtj | |ƒ} |rìP| |k�sd| k�rP| }| d8} | dkrÚt d |fƒ‚qÚW|dk �r:| ||<| S) zþReplaces tags in `query` with property values. Parameters ---------- query : str String with tags. aInfo : dict Tags(keys) and associated values for substitution in query. Returns ------- str `query` string with tags replaced. r€Nz subst-tags)raÚaddreplcsn|jdƒ}d}ˆr$ˆj|dˆƒ}|dkrNˆj|ƒ}|dkrNˆj||jƒƒSt|ƒ}|ˆjkrjˆj|ƒ}|S)Nrú?)r¶rvrÚ _escapedTagsr½)r·r„r:)ÚADD_REPL_TAGS_CMrRr~ÚsubInforrÚsubstVals    z*CommandAction.replaceTag..substValrrzaunexpected too long replacement interpolation, possible self referencing definitions in query: %s) r8rœrÚidr rÀÚ ADD_REPL_TAGSrrr¼r�) rRZqueryr[r~r¾rZckeyZ noRecReplZcsubkeyrÃÚcountr:r)rÁrRr~rÂrr‚ÛsR     zCommandAction.replaceTagz![\\#&;`|*?~<>\^\(\)\[\]{}$'"\n\r]z\Wcs„tƒ‰ˆs‡‡fdd„‰ttƒ‰‡‡‡fdd„}tj||ƒ}d|krpˆjdƒ‰ˆsVi‰‡‡fdd„}tj||ƒ}ˆr€tj|ˆƒ}|S) a%Replaces dynamical tags in `query` with property values. **Important** ------------- Because this tags are dynamic resp. foreign (user) input: - values should be escaped (using "escape" as shell variable) - no recursive substitution (no interpolation for >) - don't use cache Parameters ---------- query : str String with tags. aInfo : dict Tags(keys) and associated values for substitution in query. Returns ------- str shell script as string or array with tags replaced (direct or as variables). cs2ˆjj|ƒr.dˆjjd|ƒ}|ˆ|<d|}|S)Nzf2bV_%srsú$)r»ÚsearchÚ ESCAPE_VN_CREr¼)r„r:)rRÚvarsDictrrÚ escapeValSs  z3CommandAction.replaceDynamicTags..escapeValc sJ|jdƒ}y ˆ|}Wn tk r6ˆj||jƒƒSXt|ƒ}ˆ||ƒS)Nr)r¶r8rvr)r·r„r:)rÁr[rËrrrÃas  z2CommandAction.replaceDynamicTags..substValr€zF-*c sBt|jdƒƒ}ytˆ|ƒ}Wntk r2dSXˆd||ƒS)NrrhZF_)rr¶rr8)r·r„r:)rËÚtickDatarrÚsubstTagss z2CommandAction.replaceDynamicTags..substTag) r!rrÅrr¼rvÚ FCUSTAG_CRErZ buildShellCmd)rRr–r[rËrÃrÍr)rÁr[rRrËrÌrÊrrƒ8s     z CommandAction.replaceDynamicTagscCs t|ddƒS)NÚ _banEpochr)rQ)r"rrrÚbanEpoch‚szCommandAction.banEpochcCs6|jdk r&|jjjd|_|jj_n |jd|_dS)zƒIncrements ban epoch of jail and this action, so already banned tickets would cause a re-ban for all tickets with previous epoch.Nr)rVr�rÐrÏ)r"rrrÚinvalidateBanEpoch…s z CommandAction.invalidateBanEpochTc Cs| r|dk r||jkrdS|jd|ƒ}| s<|j||jƒr@dS|rP|ƒ rPd S|jjdƒ|jƒ|jd|ƒ}|r¨|j||jƒsœd|j|<|jjdƒdSd|j|<n:y|j|ƒWnt k rÊYnX|j ||pÜ|j d�|jj |ƒ�r|j||jƒ �r|jjdƒdSdS) z0Executes a substituted `actioncheck` command. Nrz zrzUnable to restore environment)r£éÿÿÿÿ) ror†r�rbrXÚerrorrÑZcriticalr­r‘r¡r rv)r"r�r±r£ZcheckCmdZ repairCmdrrrr°�s4        zCommandAction._invariantCheckcsêˆdkrˆjjdƒdSy |d}Wnttfk r@d}YnXd}xž|r€ˆjr€‡‡fdd„}ˆj||ˆdkd �}|d kr€d Sˆjˆˆj|r–d |ndˆjd �}|dk r¸ˆj ||ƒ}nˆ}ˆj |ˆj ƒ}|d 7}|sÞ|d krH|SqHWdS)aÅExecutes a command with preliminary checks and substitutions. Before executing any commands, executes the "check" command first in order to check if pre-requirements are met. If this check fails, it tries to restore a sane environment before executing the real command. Parameters ---------- cmd : str The command to execute. aInfo : dictionary Dynamic properties. Returns ------- bool True if the command succeeded. rhz Nothing to doTr�rcs*ˆdkr&ˆjjdƒ r&ˆjjdƒdSdS)Nz Zactionrepair_on_unbanz,Invariant check failed. Unban is impossible.FT)r|rvrXrÓr)r…r"rrÚ _beforeRepairÙs z0CommandAction._processCmd.._beforeRepairz )r£rFzfamily=)r~rN) rXrqr8Ú TypeErrorrkr°r‚r|rprƒr�rb)r"r…r[r�ZrepcntrÔr•r–r)r…r"rr¦µs.      zCommandAction._processCmdrgc KsVtjƒtjkrtjd|ƒ|s,tjdƒdSt�tj||fdddœ|—ŽSQRXdS)aIExecutes a command. Parameters ---------- realCmd : str The command to execute. timeout : int The time out in seconds for the command. Returns ------- bool True if the command succeeded. Raises ------ OSError If command fails to be executed. RuntimeError If command execution times out. é z Nothing to doTF)ÚshellÚoutputN) ÚlogSysZgetEffectiveLevelÚloggingÚDEBUGÚlogrqÚ _cmd_lockrr�)r–rbr$rrrr�ôs  zCommandAction.executeCmd)rdrerf)NF)r¤)N)N)rhNN)N)NNT)N)rg)0r*rFrGrHr�rÀrlr%r]rUryr@rzr^r|r}r†rŠr˜r›rŸr rLr¡rNr\r©rPrOr¬rMr­r¯r²ÚreÚcompiler»r½r‚rÉrƒrÐrÑr°r¦Ú staticmethodr�Ú __classcell__rr)r)rrc sT    '          Y   I  ( ?rc)rr)3Ú __author__Z __copyright__Z __license__rÚÚosrÞÚsignalÚ subprocessZtempfileZ threadingrŒÚabcrÚcollections.abcrÚ ImportErrorÚ collectionsZ failregexrZipdnsrZmytimerZutilsrZhelpersr r r r rrrr*rÙZLockrÝZ IPv6IsAllowedržrßrÎZ COND_FAMILIESr™Z DYN_REPL_TAGSrÅÚupdateÚobjectrrKrurcrrrrÚsH     $    ocserver/__pycache__/action.cpython-36.pyc000064400000064736152231502150014201 0ustar003 TB(d@wã@svdZdZdZddlZddlZddlZddlZddlZddlZddl Z ddl Z ddl m Z yddl mZWn ek rˆddlmZYnXddlmZdd lmZdd lmZdd lmZd d lmZmZmZmZmZmZm Z ee!ƒZ"e j#ƒZ$ej%Z&ej'dƒZ(d#Z)ej'dƒZ*dd„dd„dœZ+dddœZ,e,j-e+ƒGdd„dee.ƒZ/Gdd„de.e d�Z0ddd œZ1Gd!d"„d"e0ƒZ2dS)$z'Cyril Jaquier and Fail2Ban Contributorsz>Copyright (c) 2004 Cyril Jaquier, 2011-2012 Yaroslav HalchenkoZGPLéN)ÚABCMeta)ÚMutableMappingé)Ú mapTag2Opt)ÚDNSUtils)ÚMyTime)ÚUtilsé)Ú getLoggerÚ_merge_copy_dictsÚ splitwordsÚsubstituteRecursiveTagsÚ uni_stringÚTAG_CREÚMAX_TAG_REPLACE_COUNTzÚinet4Úinet6z^(\w+)\?(family)=(.*)$cCsttjdd�ƒS)NT)Úfqdn)ÚstrrÚ getHostname©rrú/usr/lib/python3.6/action.pyÚ?srcCsttjdd�ƒS)NF)r)rrrrrrrr@s)z fq-hostnamez sh-hostnameÚ ú )ÚbrZspc@s‚eZdZdZfZd!Zdd„Zd"d d „Zd#d d „ZeZ d$dd„Z dd„Z dd„Z dd„Z dd„Zdd„Zdd„Zdd„Zdd „ZdS)%Ú CallingMapa"A Mapping type which returns the result of callable values. `CallingMap` behaves similar to a standard python dictionary, with the exception that any values which are callable, are called and the result is returned as the value. No error handling is in place, such that any errors raised in the callable will raised as usual. Actual dictionary is stored in property `data`, and can be accessed to obtain original callable values. Attributes ---------- data : dict The dictionary data which can be accessed to obtain items uncalled ÚdataÚstorageÚ immutableÚ __org_datacOstƒ|_d|_t||Ž|_dS)NT)Údictrrr)ÚselfÚargsÚkwargsrrrÚ__init___szCallingMap.__init__Tc Cs4tƒ|_y |j|_Wntk r(YnX||_dS)N)r!rÚ_CallingMap__org_datarÚAttributeErrorr)r"rrrrÚresetds  zCallingMap.resetFcCsd|jj|j|tƒfS)Nz%s(%r))Ú __class__Ú__name__Ú_asdictr)r"Ú calculatedrrrÚ_asreprlszCallingMap._asreprNc s„tˆjfˆjŽ}|s.t‡fdd„|jƒDƒƒSxPt|jƒƒD]@\}}t|ƒrusz%CallingMap._asdict..)r!rrÚitemsÚlistr.Ú __getitem__)r"r,ZcheckerÚdr1r2r)r"rr+rs   zCallingMap._asdictc Cs2y|j|}Wntk r,|j|}YnX|S)N)rÚKeyErrorr)r"ÚkeyÚvaluerrrÚ getRawItem„s zCallingMap.getRawItemc Csdy|j|}Wntk r,|j|}YnXt|ƒr`t|dƒrP|jjrP||ƒn|ƒ}||j|<|S)NÚ__code__)rr8rr.Úhasattrr<Ú co_argcount)r"r9r:rrrr6‹s  zCallingMap.__getitem__cCs:|jr,|jjƒ|_|j|_|jjƒ|_d|_||j|<dS)NF)rrÚcopyrr&)r"r9r:rrrÚ __setitem__–s   zCallingMap.__setitem__cCstd|ƒ‚dS)NzKey %r was deleted)r8)r"r9rrrZ __unavailableŸszCallingMap.__unavailablec CsZ|jr,|jjƒ|_|j|_|jjƒ|_d|_y |j|=Wntk rLYnX|j|=dS)NF)rrr?rr&r8)r"r9rrrÚ __delitem__¢s   zCallingMap.__delitem__cCs t|jƒS)N)Úiterr)r"rrrÚ__iter__¯szCallingMap.__iter__cCs t|jƒS)N)Úlenr)r"rrrÚ__len__²szCallingMap.__len__cCs|jt|j|jƒƒS)N)r)r rr)r"rrrr?µszCallingMap.copy)rrrr )T)F)FN)r*Ú __module__Ú __qualname__Ú__doc__r/Ú __slots__r%r(r-Ú__repr__r+r;r6r@Z_CallingMap__unavailablerArCrEr?rrrrrJs       rc@sXeZdZdZedd„ƒZdd„Zdd„Zdd „Zd d „Z d d „Z e dd„ƒZ dd„Z dS)Ú ActionBaseaìAn abstract base class for actions in Fail2Ban. Action Base is a base definition of what methods need to be in place to create a Python based action for Fail2Ban. This class can be inherited from to ease implementation. Required methods: - __init__(jail, name) - start() - stop() - ban(aInfo) - unban(aInfo) Called when action is created, but before the jail/actions is started. This should carry out necessary methods to initialise the action but not "start" the action. Parameters ---------- jail : Jail The jail in which the action belongs to. name : str Name assigned to the action. Notes ----- Any additional arguments specified in `jail.conf` or passed via `fail2ban-client` will be passed as keyword arguments. cCs*d}x |D]}tt||dƒƒs dSq WdS) NÚstartÚstopÚbanÚrebanÚunbanFT)rLrMrNrOrP)r.Úgetattr)ÚclsÚCZrequiredÚmethodrrrÚ__subclasshook__Øs zActionBase.__subclasshook__cCs"||_||_td|jjƒ|_dS)Nz fail2ban.%s)Ú_jailÚ_namer r)r*Ú_logSys)r"ÚjailÚnamerrrr%æszActionBase.__init__cCsdS)z,Executed when the jail/action is started. Nr)r"rrrrLëszActionBase.startcCsdS)z,Executed when the jail/action is stopped. Nr)r"rrrrMðszActionBase.stopcCsdS)zŒExecuted when a ban occurs. Parameters ---------- aInfo : dict Dictionary which includes information in relation to the ban. Nr)r"ÚaInforrrrNõs zActionBase.bancCs |j|ƒS)zŒExecuted when a ban occurs. Parameters ---------- aInfo : dict Dictionary which includes information in relation to the ban. )rN)r"r[rrrrOs zActionBase.rebancCsdS)NFr)r"rrrÚ _prolongable szActionBase._prolongablecCsdS)z�Executed when a ban expires. Parameters ---------- aInfo : dict Dictionary which includes information in relation to the ban. Nr)r"r[rrrrPs zActionBase.unbanN)r*rFrGrHÚ classmethodrUr%rLrMrNrOÚpropertyr\rPrrrrrK¹s   rK)Ú metaclassÚ str2secondsÚignore)ÚtimeoutZbantimecs’eZdZdZedPƒZdd„Z‡fdd„Zed d „ƒZ d d „Z e Z d d„Z e dd„ƒZe dd„ƒZdd„Zdd„Zgdfdd„Ze dd„ƒZe dd„ƒZe dd„ƒZd d!„ZdQd#d$„ZdRd&d'„Ze d(d)„ƒZd*d+„Zd,d-„Zd.d/„Zd0d1„Zd2d3„ZdSd4d5„Zd6d7„Z dTd8d9„Z!e"j#d:ƒZ$ed;d<„ƒZ%edUd>d?„ƒZ&e"j#d@ƒZ$e"j#dAƒZ'edVdBdC„ƒZ(e dDdE„ƒZ)dFdG„Z*dWdIdJ„Z+dXdKdL„Z,e-dYdNdO„ƒZ.‡Z/S)ZÚ CommandActionaÂA action which executes OS shell commands. This is the default type of action which Fail2Ban uses. Default sets all commands for actions as empty string, such no command is executed. Parameters ---------- jail : Jail The jail in which the action belongs to. name : str Name assigned to the action. Attributes ---------- actionban actioncheck actionreban actionreload actionrepair actionstart actionstop actionunban timeout ÚmatchesÚ ipmatchesÚ ipjailmatchesc CsTd|_z@d|_d|_d|_d|_d|_d|_d|_d|_d|_ d|_ Wdd|_XdS)z8 Clear all lists/dicts parameters (used by reloading) ré<ÚNr) Ú_CommandAction__initrbZ actionstartZ actionbanÚ actionrebanZ actionunbanÚ actioncheckZ actionrepairZ actionflushZ actionstopZ actionreload)r"rrrÚclearAllParams>s zCommandAction.clearAllParamscsHtt|ƒj||ƒd|_d|_i|_i|_|jƒ|jj d|j ƒdS)Nrz Created %s) Úsuperrcr%riÚ_CommandAction__propertiesÚ_CommandAction__startedÚ_CommandAction__substCacherlrXÚdebugr))r"rYrZ)r)rrr%XszCommandAction.__init__cCstS)N)ÚNotImplemented)rRrSrrrrUaszCommandAction.__subclasshook__cCst|jdƒ rf|j rft|ƒ rftj|ƒ}|dkr4dS|dkrFtj|ƒ}d|_|jj ƒ|j j d||ƒ||j |<dS)NÚ_rar`z Set %s = %r) Ú startswithrir.ÚWRAP_CMD_PARAMSÚgetrr`rnrpÚclearrXrqÚ__dict__)r"rZr:ZwrprrrÚ __setattr__es   zCommandAction.__setattr__cCs4|jdƒs(d|_|jjƒ|jjd|ƒ|j|=dS)Nrsz Unset %s)rtrnrprwrXrqrx)r"rZrrrÚ __delattr__ws   zCommandAction.__delattr__cs2ˆjdk rˆjSt‡fdd„tˆƒDƒƒˆ_ˆjS)z`A dictionary of the actions properties. This is used to substitute "tags" in the commands. Nc3s8|]0}|jdƒ rttˆ|ƒƒ r|tˆ|ƒfVqdS)rsN)rtr.rQ)r0r9)r"rrr3Œsz,CommandAction._properties..)rnr!Údir)r"r)r"rÚ _properties�s   zCommandAction._propertiescCs|jS)N)rp)r"rrrÚ _substCache’szCommandAction._substCachecCsH|j||j|rd|nd|jd�}| s0d|kr4|S|j|d|iƒ}|S)Nzfamily=rh)Ú conditionalÚcacheú<Úfamily)Ú replaceTagr|rpÚreplaceDynamicTags)r"Útagr�ÚcmdrrrÚ _getOperation–s  zCommandAction._getOperationc sîd|f}t|ƒsLtˆƒs,|jj|iƒjˆƒS‡fdd„|jj|iƒjƒDƒS|d}|r”y|j|}Wn"tk rˆi}|j|<YnX||ˆ<nVy@|j|}|jˆƒ}x&t|jƒƒD]\‰}||kr¸|ˆ=q¸WWntk rèYnXdS)z? Get, set or delete command of operation considering family. Z__eOpCmdcsg|]\}}ˆ|ƒr|‘qSrr)r0Úfr2)r�rrú ©sz4CommandAction._operationExecuted..rN)rDr.rprvr4r8Úpopr5)r"r„r�r#r9r…Zfamdr2r)r�rÚ_operationExecuted¡s(    z CommandAction._operationExecutedNc sFd}d}|s dd„|jjƒDƒ}�x|D]ø‰yÆ|j|ˆƒ}d}|rÌ||j|‡fdd„ƒkrÌ|} |jr¶t|jjddƒ} | s–|jjjdƒ} t|jjd| ƒt j ƒ| d <ˆ| d <|j || ƒ} |j | |j ƒ}||M}|rÚ|ˆ|ƒ|j|ˆ|rê|ndƒWq(tk �r} zd }| }WYdd} ~ Xq(Xq(W|�sBtd ||j|j|fƒ‚|S) z³Executes the operation commands (like "actionstart", "actionstop", etc). Replace the tags in the action command with actions properties and executes the resulting command. Tz Script errorcSsg|]\}}|r|‘qSrr)r0Úfamoperr2rrrrˆÅsz3CommandAction._executeOperation..cs|ˆkS)Nr)r‡)r‹rrrËsz1CommandAction._executeOperation..Z actionInfoNÚtimer�FzError %s action %s/%s: %r)ror4r†rŠrVrQÚactionsZ_getActionInfoÚsetattrrrŒrƒÚ executeCmdrbÚ ValueErrorÚ RuntimeErrorrW) r"r„Z operationr�Ú afterExecÚresÚerrr…ÚretÚrealCmdr[Úer)r‹rÚ_executeOperation»s:     zCommandAction._executeOperationcCsJ|jjdƒ}|dk r|Sd}x|jD]}tj|ƒr$d}Pq$W||jd<|S)NZ__hasCondSectionFT)r|rvÚCONDITIONAL_FAM_REÚmatch)r"r2r1rrrÚ_hasCondSectionâs    zCommandAction._hasCondSectioncCsn|jjdƒ}|r|S|jjdƒ}|r>t|ttfƒ r>t|ƒ}n"|jrZtƒrRddgndg}ndg}||jd<|S)NZ __familiesZfamiliesrrrh)r|rvÚ isinstancer5Úsetr r›Ú allowed_ipv6)r"r2rrrÚ _familiesïs    zCommandAction._familiescCs,|jjdƒ}|dk r|S|j}||jd<|S)z1Checks the action depends on family (conditional)Zactionstart_on_demandN)r|rvr›)r"r2rrrÚ_startOnDemandþs   zCommandAction._startOnDemandcCs|jƒS)zŽExecutes the "actionstart" command. Replace the tags in the action command with actions properties and executes the resulting command. )Ú_start)r"rrrrL szCommandAction.startFcs\ˆjr|s&dSn| r&ˆjj|ƒr&dS|dk r4|gnˆj}‡fdd„}ˆjdd||d�}|S)zŽExecutes the "actionstart" command. Replace the tags in the action command with actions properties and executes the resulting command. TNcs |rˆjd|dƒdˆj|<dS)Nz r)rŠro)r�r•)r"rrÚ_startedsz&CommandAction._start.._startedz Zstarting)r�r’)r rorvrŸr˜)r"r�Ú forceStartr¢r•r)r"rr¡s zCommandAction._startú cCs`|jddƒ}|jr,|jj|ƒs,|j|dd�|j||ƒsDtd|ƒ‚|jj|dƒdB|j|<dS) a)Executes the given command ("actionban" or "actionreban"). Replaces the tags in the action command with actions properties and ban information, and executes the resulting command. Parameters ---------- aInfo : dict Dictionary which includes information in relation to the ban. r�rhT)r£zError banning %(ip)sréN)rvr ror¡Ú _processCmdr‘)r"r[r…r�rrrrN%s    zCommandAction.bancCs t|dƒo|jot|jƒjƒ S)NÚ actionprolong)r=r§rÚisspace)r"rrrr\;szCommandAction._prolongablecCs|jd|ƒstd|ƒ‚dS)aExecutes the "actionprolong" command. Replaces the tags in the action command with actions properties and ban information, and executes the resulting command. Parameters ---------- aInfo : dict Dictionary which includes information in relation to the ban. zzError prolonging %(ip)sN)r¦r‘)r"r[rrrÚprolong@s zCommandAction.prolongcCs:|jddƒ}|jj|dƒd@r6|jd|ƒs6td|ƒ‚dS)aExecutes the "actionunban" command. Replaces the tags in the action command with actions properties and ban information, and executes the resulting command. Parameters ---------- aInfo : dict Dictionary which includes information in relation to the ban. r�rhrr z zError unbanning %(ip)sN)rvror¦r‘)r"r[r�rrrrPOs  zCommandAction.unbancCs|j||jrdndƒS)aDExecutes the "actionreban" command if available, otherwise simply repeat "actionban". Replaces the tags in the action command with actions properties and ban information, and executes the resulting command. Parameters ---------- aInfo : dict Dictionary which includes information in relation to the ban. z z )rNrj)r"r[rrrrO`s zCommandAction.rebancs:dd„ˆjjƒDƒ}|sdS‡fdd„}ˆjdd||d�S) aExecutes the "actionflush" command. Command executed in order to flush all bans at once (e. g. by stop/shutdown the system), instead of unbanning of each single ticket. Replaces the tags in the action command with actions properties and executes the resulting command. cSs g|]\}}|d@dkr|‘qS)r¥r)r0r‡r2rrrrˆysz'CommandAction.flush..Tcs&|r"ˆjj|ƒr"ˆj|dM<dS)Nr éýÿÿÿ)rorv)r�r•)r"rrÚ _afterFlush}sz(CommandAction.flush.._afterFlushz Zflushing)r�r’)ror4r˜)r"r�r«r)r"rÚflushos  zCommandAction.flushcCs|jƒS)zŽExecutes the "actionstop" command. Replaces the tags in the action command with actions properties and executes the resulting command. )Ú_stop)r"rrrrM‚szCommandAction.stopcs||dkr,dd„ˆjjƒDƒ}|s$dSiˆ_n2yˆj|dM<|g}Wntk r\dSX‡fdd„}ˆjdd ||d �S) zŽExecutes the "actionstop" command. Replaces the tags in the action command with actions properties and executes the resulting command. NcSsg|]\}}|r|‘qSrr)r0r‡r2rrrrˆ’sz'CommandAction._stop..Trcs|rˆjd|dƒdS)Nz )rŠ)r�r•)r"rrÚ_stoppedœsz%CommandAction._stop.._stoppedz Zstopping)r�r’)ror4r8r˜)r"r�r®r)r"rr­Šs  zCommandAction._stopcKs |jddƒS)zÿExecutes the "actionreload" command. Parameters ---------- kwargs : dict Currently unused, because CommandAction do not support initOpts Replaces the tags in the action command with actions properties and executes the resulting command. zZ reloading)r˜)r"r$rrrÚreload¡s zCommandAction.reloadcCs\d}|jrXxLt|jjƒƒD]:\}}|r|j||ƒ rd|j|<|jd|dƒ|dM}qW|S)zFExecutes the invariant check with repair if expected (conditional). Trz NF)rkr5ror4Ú_invariantCheckrŠ)r"Ú beforeRepairr•r�ZstartedrrrÚconsistencyCheck®s  zCommandAction.consistencyCheckz[\\#&;`|*?~<>^()\[\]{}$'"\n\r]cs(dddœ‰‡fdd„}|jj||ƒ}|S)a5Escape characters which may be used for command injection. Parameters ---------- value : str A string of which characters will be escaped. Returns ------- str `value` with certain characters escaped. Notes ----- The following characters are escaped:: \#&;`|*?~<>^()[]{}$'" r1Úr)rú cs|jƒ}dˆj||ƒS)Nú\)Úgrouprv)ÚmÚc)Ú_map2crrÚ substCharÔsz*CommandAction.escapeTag..substChar)Ú ESCAPE_CREÚsub)rRr:rºr)r¹rÚ escapeTag¾s  zCommandAction.escapeTagrhc s>d|kr |S|dk r:|ˆf}y||Stk r8YnXt|tƒ}|‰|s¶d‰}|dk rŒdt|ƒˆf}y ||‰Wntk rŠYnXˆdkr¦t|ˆˆj|d�‰|dk r¶ˆ||<ttƒ‰‡‡‡‡fdd„} td} xNtj | |ƒ} |rìP| |k�sd| k�rP| }| d8} | dkrÚt d |fƒ‚qÚW|dk �r:| ||<| S) zþReplaces tags in `query` with property values. Parameters ---------- query : str String with tags. aInfo : dict Tags(keys) and associated values for substitution in query. Returns ------- str `query` string with tags replaced. r€Nz subst-tags)raÚaddreplcsn|jdƒ}d}ˆr$ˆj|dˆƒ}|dkrNˆj|ƒ}|dkrNˆj||jƒƒSt|ƒ}|ˆjkrjˆj|ƒ}|S)Nrú?)r¶rvrÚ _escapedTagsr½)r·r„r:)ÚADD_REPL_TAGS_CMrRr~ÚsubInforrÚsubstVals    z*CommandAction.replaceTag..substValrrzaunexpected too long replacement interpolation, possible self referencing definitions in query: %s) r8rœrÚidr rÀÚ ADD_REPL_TAGSrrr¼r�) rRZqueryr[r~r¾rZckeyZ noRecReplZcsubkeyrÃÚcountr:r)rÁrRr~rÂrr‚ÛsR     zCommandAction.replaceTagz![\\#&;`|*?~<>\^\(\)\[\]{}$'"\n\r]z\Wcs„tƒ‰ˆs‡‡fdd„‰ttƒ‰‡‡‡fdd„}tj||ƒ}d|krpˆjdƒ‰ˆsVi‰‡‡fdd„}tj||ƒ}ˆr€tj|ˆƒ}|S) a%Replaces dynamical tags in `query` with property values. **Important** ------------- Because this tags are dynamic resp. foreign (user) input: - values should be escaped (using "escape" as shell variable) - no recursive substitution (no interpolation for >) - don't use cache Parameters ---------- query : str String with tags. aInfo : dict Tags(keys) and associated values for substitution in query. Returns ------- str shell script as string or array with tags replaced (direct or as variables). cs2ˆjj|ƒr.dˆjjd|ƒ}|ˆ|<d|}|S)Nzf2bV_%srsú$)r»ÚsearchÚ ESCAPE_VN_CREr¼)r„r:)rRÚvarsDictrrÚ escapeValSs  z3CommandAction.replaceDynamicTags..escapeValc sJ|jdƒ}y ˆ|}Wn tk r6ˆj||jƒƒSXt|ƒ}ˆ||ƒS)Nr)r¶r8rvr)r·r„r:)rÁr[rËrrrÃas  z2CommandAction.replaceDynamicTags..substValr€zF-*c sBt|jdƒƒ}ytˆ|ƒ}Wntk r2dSXˆd||ƒS)NrrhZF_)rr¶rr8)r·r„r:)rËÚtickDatarrÚsubstTagss z2CommandAction.replaceDynamicTags..substTag) r!rrÅrr¼rvÚ FCUSTAG_CRErZ buildShellCmd)rRr–r[rËrÃrÍr)rÁr[rRrËrÌrÊrrƒ8s     z CommandAction.replaceDynamicTagscCs t|ddƒS)NÚ _banEpochr)rQ)r"rrrÚbanEpoch‚szCommandAction.banEpochcCs6|jdk r&|jjjd|_|jj_n |jd|_dS)zƒIncrements ban epoch of jail and this action, so already banned tickets would cause a re-ban for all tickets with previous epoch.Nr)rVr�rÐrÏ)r"rrrÚinvalidateBanEpoch…s z CommandAction.invalidateBanEpochTc Cs| r|dk r||jkrdS|jd|ƒ}| s<|j||jƒr@dS|rP|ƒ rPd S|jjdƒ|jƒ|jd|ƒ}|r¨|j||jƒsœd|j|<|jjdƒdSd|j|<n:y|j|ƒWnt k rÊYnX|j ||pÜ|j d�|jj |ƒ�r|j||jƒ �r|jjdƒdSdS) z0Executes a substituted `actioncheck` command. Nrz zrzUnable to restore environment)r£éÿÿÿÿ) ror†r�rbrXÚerrorrÑZcriticalr­r‘r¡r rv)r"r�r±r£ZcheckCmdZ repairCmdrrrr°�s4        zCommandAction._invariantCheckcsêˆdkrˆjjdƒdSy |d}Wnttfk r@d}YnXd}xž|r€ˆjr€‡‡fdd„}ˆj||ˆdkd �}|d kr€d Sˆjˆˆj|r–d |ndˆjd �}|dk r¸ˆj ||ƒ}nˆ}ˆj |ˆj ƒ}|d 7}|sÞ|d krH|SqHWdS)aÅExecutes a command with preliminary checks and substitutions. Before executing any commands, executes the "check" command first in order to check if pre-requirements are met. If this check fails, it tries to restore a sane environment before executing the real command. Parameters ---------- cmd : str The command to execute. aInfo : dictionary Dynamic properties. Returns ------- bool True if the command succeeded. rhz Nothing to doTr�rcs*ˆdkr&ˆjjdƒ r&ˆjjdƒdSdS)Nz Zactionrepair_on_unbanz,Invariant check failed. Unban is impossible.FT)r|rvrXrÓr)r…r"rrÚ _beforeRepairÙs z0CommandAction._processCmd.._beforeRepairz )r£rFzfamily=)r~rN) rXrqr8Ú TypeErrorrkr°r‚r|rprƒr�rb)r"r…r[r�ZrepcntrÔr•r–r)r…r"rr¦µs.      zCommandAction._processCmdrgc KsVtjƒtjkrtjd|ƒ|s,tjdƒdSt�tj||fdddœ|—ŽSQRXdS)aIExecutes a command. Parameters ---------- realCmd : str The command to execute. timeout : int The time out in seconds for the command. Returns ------- bool True if the command succeeded. Raises ------ OSError If command fails to be executed. RuntimeError If command execution times out. é z Nothing to doTF)ÚshellÚoutputN) ÚlogSysZgetEffectiveLevelÚloggingÚDEBUGÚlogrqÚ _cmd_lockrr�)r–rbr$rrrr�ôs  zCommandAction.executeCmd)rdrerf)NF)r¤)N)N)rhNN)N)NNT)N)rg)0r*rFrGrHr�rÀrlr%r]rUryr@rzr^r|r}r†rŠr˜r›rŸr rLr¡rNr\r©rPrOr¬rMr­r¯r²ÚreÚcompiler»r½r‚rÉrƒrÐrÑr°r¦Ú staticmethodr�Ú __classcell__rr)r)rrc sT    '          Y   I  ( ?rc)rr)3Ú __author__Z __copyright__Z __license__rÚÚosrÞÚsignalÚ subprocessZtempfileZ threadingrŒÚabcrÚcollections.abcrÚ ImportErrorÚ collectionsZ failregexrZipdnsrZmytimerZutilsrZhelpersr r r r rrrr*rÙZLockrÝZ IPv6IsAllowedržrßrÎZ COND_FAMILIESr™Z DYN_REPL_TAGSrÅÚupdateÚobjectrrKrurcrrrrÚsH     $    ocserver/__pycache__/actions.cpython-36.opt-1.pyc000064400000053175152231502150015316 0ustar003 TB(d=aã @sôdZdZdZddlZddlZddlZddlZyddlmZWn e k r\ddl mZYnXddl m Z ddl m Z mZdd lmZdd lmZdd lmZmZmZdd lmZdd lmZddlmZddlmZeeƒZ Gdd„deeƒZ!dS)z Cyril Jaquierz Copyright (c) 2004 Cyril JaquierZGPLéN)ÚMapping)Ú OrderedDicté)Ú BanManagerÚ BanTicket)ÚIPAddr)Ú JailThread)Ú ActionBaseÚ CommandActionÚ CallingMap)ÚMyTime)Ú Observers)ÚUtilsé)Ú getLoggerc@seZdZdZdd„Zedd„ƒZd?dd „Zd@d d „Zd d„Z dd„Z dd„Z dd„Z dd„Z dd„Zdd„Zdd„Zdd„ZdAdd „Zd!d"„ZdBd#d$„ZdCd%d&„Zd'd(„ZGd)d*„d*eƒZd+d,„ZdDd.d/„ZdEd0d1„ZdFd2d3„Zd4d5„ZdGd6d7„ZdHd8d9„ZdId:d;„Z dJd=d>„Z!dS)KÚActionsaŠHandles jail actions. This class handles the actions of the jail. Creation, deletion or to actions must be done through this class. This class is based on the Mapping type, and the `add` method must be used to add new actions. This class also starts and stops the actions, and fetches bans from the jail executing these bans via the actions. Parameters ---------- jail: Jail The jail of which the actions belongs to. Attributes ---------- daemon ident name status active : bool Control the state of the thread. idle : bool Control the idle state of the thread. sleeptime : int The time the thread sleeps for in the loop. cCsLtj|d|jd�||_tƒ|_tƒ|_d|_d|_ d|_ |j d|_ dS)Nzf2b/a.)Únameré r) rÚ__init__rÚ_jailrÚ_actionsrÚ banManagerÚbanEpochÚ _Actions__lastConsistencyCheckTMÚ banPrecedenceÚ unbanMaxCount)ÚselfÚjail©rú/usr/lib/python3.6/actions.pyrNszActions.__init__cCsFtj|ƒ}t|dƒs"td|ƒ‚n t|jtƒsBtd||jjfƒ‚|S)NÚActionz&%s module does not have 'Action' classz0%s module %s does not implement required methods)rZload_python_moduleÚhasattrÚ RuntimeErrorÚ issubclassr r Ú__name__)Ú pythonModuleÚmodrrrÚ_load_python_module\s    zActions._load_python_moduleNFcCsŽ||jkrN|std|ƒ‚|j|}t|dƒrNt|dƒrJ|jƒ||j|<dS|dkrdt|j|ƒ}n|j|ƒ}|j|j|f|Ž}||j|<dS)a¯Adds a new action. Add a new action if not already present, defaulting to standard `CommandAction`, or specified Python module. Parameters ---------- name : str The name of the action. pythonModule : str, optional Path to Python file which must contain `Action` class. Default None, which means `CommandAction` is used. initOpts : dict, optional Options for Python Action, used as keyword arguments for initialisation. Default None. Raises ------ ValueError If action name already exists. RuntimeError If external Python module does not have `Action` class or does not implement necessary methods as per `ActionBase` abstract class. zAction %s already existsÚreloadÚclearAllParamsN) rÚ ValueErrorr!r)Ú_reload_actionsr rr'r )rrr%ÚinitOptsr(ÚactionZcustomActionModulerrrÚaddis       z Actions.addTcs¢|rtƒˆ_n�tˆdƒržx:ˆjjƒD],\}}|ˆjkr$ˆj|jf|rJ|niŽq$Wt‡fdd„ˆjjƒDƒƒ}t|ƒr”ˆjd|dd�ˆj |d�t ˆdƒdS) z@ Begin or end of reloading resp. refreshing of all parameters r+c3s$|]\}}|ˆjkr||fVqdS)N)r+)Ú.0rr-)rrrú £sz!Actions.reload..FT)ÚdbÚactionsÚstop)r2N) Údictr+r!Úitemsrr(rÚlenÚ_Actions__flushBanÚ stopActionsÚdelattr)rZbeginrr,Zdelactsr)rrr(—s    zActions.reloadc Cs0y |j|Stk r*td|ƒ‚YnXdS)NzInvalid Action name: %s)rÚKeyError)rrrrrÚ __getitem__¬s zActions.__getitem__c Cs2y |j|=Wn tk r,td|ƒ‚YnXdS)NzInvalid Action name: %s)rr:)rrrrrÚ __delitem__²s zActions.__delitem__cCs t|jƒS)N)Úiterr)rrrrÚ__iter__¸szActions.__iter__cCs t|jƒS)N)r6r)rrrrÚ__len__»szActions.__len__cCsdS)NFr)rÚotherrrrÚ__eq__¾szActions.__eq__cCst|ƒS)N)Úid)rrrrÚ__hash__ÁszActions.__hash__cCs(tj|ƒ}|jj|ƒtjd|ƒdS)Nz banTime: %s)r Z str2secondsrÚ setBanTimeÚlogSysÚinfo)rÚvaluerrrrDÉs  zActions.setBanTimecCs |jjƒS)N)rÚ getBanTime)rrrrrHÓszActions.getBanTimecsD|jjƒ‰|sˆSt|ƒdkr2|dˆkr.dSdS‡fdd„|DƒS)Nrrcsg|]}|ˆkrdnd‘qS)rrr)r/Úip)Úlstrrú Üsz%Actions.getBanned..)rÚ getBanListr6)rZidsr)rJrÚ getBannedÖs   zActions.getBannedcCs|jjd|d�S)zkReturns the list of banned IP addresses. Returns ------- list The list of banned IP addresses. T)ZorderedÚwithTime)rrL)rrNrrrrLÞszActions.getBanListcs<tjƒ‰t|tƒr&‡fdd„|Dƒ}n t|ˆƒf}|j|ƒS)zBan an IP or list of IPs.c3s|]}t|ˆƒVqdS)N)r)r/rI)ÚunixTimerrr0îsz&Actions.addBannedIP..)r ÚtimeÚ isinstanceÚlistrÚ_Actions__checkBan)rrIÚticketsr)rOrÚ addBannedIPès   zActions.addBannedIPc Cs8|dkr|j|ƒSt|ttfƒr„g}d}xF|D]>}y||j|||ƒ7}Wq.tk rj|sf|j|ƒYq.Xq.W|r€td|ƒ‚|S|r¦|jjdk r¦|jjj |j|ƒ|j j |ƒ}|dk rÆ|j |ƒnnt|t ƒ�s t |ƒ}|j�s tt|j|j jƒƒƒ} | �r |j| ||ƒSd|} tjtj| ƒ|�r,dSt| ƒ‚dS)aORemoves banned IP calling actions' unban method Remove a banned IP now, rather than waiting for it to expire, even if set to never expire. Parameters ---------- ip : list, str, IPAddr or None The IP address (or multiple IPs as list) to unban or all IPs if None Raises ------ ValueError If `ip` is not banned Nrznot banned: %rz%s is not bannedr)r7rQrRÚtupleÚremoveBannedIPr*ÚappendrÚdatabaseÚdelBanrZ getTicketByIDÚ_Actions__unBanrZisSingleÚfilterÚcontainsrLrEÚlogÚloggingZMSG) rrIr1ZifexistsZmissedÚcntÚiÚticketZipaZipsÚmsgrrrrWõs>      zActions.removeBannedIPcCsœ|dkr|j}xˆtt|jƒƒƒD]t\}}y |jƒWnDtk rx}z(tjd|jj ||tj ƒt j kd�WYdd}~XnX|j|=tj d|jj |ƒq WdS)z>Stops the actions in reverse sequence (optionally filtered) Nz(Failed to stop jail '%s' action '%s': %s)Úexc_infoz%s: action %s terminated)rÚreversedrRr5r3Ú ExceptionrEÚerrorrrÚgetEffectiveLevelr_ÚDEBUGÚdebug)rr2rr-Úerrrr8/s  $zActions.stopActionscsäd}xhˆjjƒD]Z\}}y |jƒWqtk rh}z(tjdˆjj||tjƒt j kd�WYdd}~XqXqW�xZˆj �rÊ�yˆj r¶tj dƒtj‡fdd„dd„ˆjƒtj d ƒwrd}tˆjˆjjtjƒƒ}tjd d |ˆjƒtj‡fd d„|ƒ�r ˆjƒ}||7}| �s|ˆjk�r€ˆj �r||d 9}tjd d|�rL|ˆjk�rL|nˆj|ˆjƒˆj|�rt|ˆjk�rt|nˆjƒd}Wqrtk �rÆ}z&tjdˆjj|tjƒt j kd�WYdd}~XqrXqrWˆjdd�ˆjƒdS)zÝMain loop for Threading. This function is the main loop of the thread. It checks the jail queue and executes commands when an IP address is banned. Returns ------- bool True when the thread exits nicely. rz)Failed to start jail '%s' action '%s': %s)rdNzActions: enter idle modecsˆj pˆj S)N)ÚactiveÚidler)rrrÚVszActions.run..cSsdS)NFrrrrrrnWszActions: leave idle modeéz1Actions: wait for pending tickets %s (default %s)csˆj pˆjjS)N)rlrZhasFailTicketsr)rrrrn^srz+Actions: check-unban %s, bancnt %s, max: %sz*[%s] unhandled error in actions thread: %sT)r3)rr5ÚstartrfrErgrrrhr_rirlrmrjrZwait_forZ sleeptimeÚminrZ_nextUnbanTimer rPr^rSrrÚ_Actions__checkUnBanr7r8)rr`rr-rkZbancntZwtr)rrÚrun?sF   (   ,"( z Actions.runc@sÊeZdZd(Zdd„dd„dd„dd„dd„d d„d d„d d„d d„d d„dd„d)dd„dd„dd„dd„dd„dd„dd„dd„dd„dd„dœZejd*Zddefdd „Zd!d"„Zd#d$„Z d+d&d'„Z dS),zActions.ActionInfoÚfidú raw-ticketcCs |jjƒS)N)Ú_ActionInfo__ticketZgetIP)rrrrrnwszActions.ActionInfo.cCs |djS)NrI)Z familyStr)rrrrrnxscCs|djdƒS)NrIÚ)ZgetPTR)rrrrrnyscCs |djƒS)NrI)ZgetHost)rrrrrnzscCs |jjƒS)N)rvÚgetID)rrrrrn{scCs |jjƒS)N)rvÚ getAttempt)rrrrrn|scCs |jjƒS)N)rvÚgetTime)rrrrrn}scCs|jƒS)N)Ú _getBanTime)rrrrrn~scCs |jjƒS)N)rvZ getBanCount)rrrrrnscCsdj|jjƒƒS)NÚ )ÚjoinrvÚ getMatches)rrrrrn€scCs|jjr dSdS)Nrr)rvÚrestored)rrrrrn‚sNcCs |jj|ƒS)N)rvZgetData)rÚtagrrrrn„scCsdj|jdƒjƒƒS)Nr|T)r}Ú_mi4ipr~)rrrrrn†scCsdj|jƒjƒƒS)Nr|)r}r�r~)rrrrrn‡scCs|jdƒjƒS)NT)r�ry)rrrrrnˆscCs |jƒjƒS)N)r�ry)rrrrrn‰scCs t|jƒS)N)Úreprrv)rrrrrn‹scCs|jjjjƒS)N)Ú_ActionInfo__jailr2rÚsize)rrrrrn�scCs|jjjjƒS)N)rƒr2rÚ getBanTotal)rrrrrnŽscCs|jjjjƒS)N)rƒr\Ú failManagerr„)rrrrrn�scCs|jjjjƒS)N)rƒr\r†Z getFailTotal)rrrrrn�s)rIZfamilyzip-revzip-hostrtZfailuresrPZbantimeZbancountZmatchesrzF-*Z ipmatchesZ ipjailmatchesZ ipfailuresZipjailfailuresz raw-ticketz jail.bannedzjail.banned_totalz jail.foundzjail.found_totalÚ__ticketÚ__jailÚ__mi4ipTcCs$||_||_tƒ|_||_||_dS)N)rvrƒr4ZstorageÚ immutableÚdata)rrbrrŠr‹rrrr•s zActions.ActionInfo.__init__cCs|j|j|j|j|jjƒƒS)N)Ú __class__rvrƒrŠr‹Úcopy)rrrrr�œszActions.ActionInfo.copycCs&|jjƒ}|dkr|jjjƒ}t|ƒS)N)rvrHrƒr2Úint)rÚbtimerrrr{Ÿs  zActions.ActionInfo._getBanTimeFcCsôt|dƒsi|_|j}|rdnd}||krD||dk r>||S|jSyR|j}|d}d||<|jsh|jS|r€|jj|d�||<n|jj||d�||<WnBtk rØ}z&tjd||j |tj ƒt j kd �WYdd}~XnX||dk rî||S|jS) a¡Gets bans merged once, a helper for lambda(s), prevents stop of executing action by any exception inside. This function never returns None for ainfo lambdas - always a ticket (merged or single one) and prevents any errors through merging (to guarantee ban actions will be executed). [TODO] move merging to observer - here we could wait for merge and read already merged info from a database Parameters ---------- overalljails : bool switch to get a merged bans : False - (default) bans merged for current jail only True - bans merged for all jails of current ip address Returns ------- BanTicket merged or self ticket only r‰ÚallrNrI)rI)rIrz+Failed to get %s bans merged, jail '%s': %s)rd) r!Z_ActionInfo__mi4iprvrƒrYZ getBansMergedrfrErgrrhr_ri)rZ overalljailsZmiÚidxrrIrkrrrr�¤s*  $zActions.ActionInfo._mi4ip)rtru)N)r‡rˆr‰)F) r$Ú __module__Ú __qualname__Z CM_REPR_ITEMSZAI_DICTr Ú __slots__rr�r{r�rrrrÚ ActionInfors6  r•cCs$|stdtjƒƒ}tj||jƒ}|S)Nrw)rr rPrr•r)rrbÚaInforrrÚ_getActionInfoÏszActions._getActionInfoédccs4d}x*||kr.|jjƒ}|sP|V|d7}qWdS)zAGenerator to get maximal count failure tickets from fail-manager.rrN)rZ getFailTicket)rÚcountr`rbrrrZ__getFailTicketsÕs  zActions.__getFailTicketscsÈd}|s|j|jƒ}d}�x€|D�]v}tj|ƒ‰|j|jjƒƒ}ˆjƒ}|jˆƒ}i}|jjˆ|d��r^|d7}t j dk r–ˆj r–t j j dˆ|j |ƒtjd|j jˆj s¬dnd|ƒx�|jjƒD]‚\} } y0ˆj rÞt| d d ƒrÞwÀ|jsì|jƒ| j|ƒWqÀtk �r@} z*tjd |j j| || tjƒtjkd �WYdd} ~ XqÀXqÀWd ˆ_|j�r˜|jˆ_q |jddƒ�r€tjd|j j|ƒq |jdˆƒ‰ˆj�rŠ|j ƒˆj ƒ} | dk�r´tjn| dk�rÄtj!ntj"} tj#| d|j j|ƒˆj|jk�rF| dk�rF| �rFt$j%ƒ|j&dk�rFt$j%ƒ|_&x(|jj'ƒD]} t(| dƒ�r(| j)ƒ�q(Wˆj|jk�r˜|�svt*‡fdd„|jjƒDƒƒ}||j+ˆ|d�7}q ||j+ˆƒ7}q W|�rÄtj,d||jj-ƒ|jj.ƒ|j jƒ|S)aCheck for IP address to ban. If tickets are not specified look in the jail queue for FailTicket. If a ticket is available, it executes the "ban" command and adds a ticket to the BanManager. Returns ------- bool True if an IP address get banned. rN)ÚreasonrZbanFoundz [%s] %sBan %srwzRestore Ú norestoredFz9Failed to execute ban jail '%s' action '%s' info '%r': %s)rdTZexpiredz[%s] Ignore %s, expired bantimerbéé<z[%s] %s already bannedÚconsistencyCheckc3s&|]\}}|jˆjkr||fVqdS)N)r)r/rr-)ÚbTicketrrr0(sz%Actions.__checkBan..)r2z"Banned %s / %s, %s ticket(s) in %r)/Ú_Actions__getFailTicketsrrZwraprHrrxr—Z addBanTicketr ZMainrr.rrEÚnoticerrr5ÚgetattrrŠÚresetZbanrfrgrhr_riÚbannedrÚgetrFrzZNOTICEZWARNINGr^r rPrÚvaluesr!ržr4Ú_Actions__reBanrjr…r„)rrTr`Z rebanactsrbr�rIr–ršrr-rkZdiftmZllr)rŸrZ __checkBanßsp     (    zActions.__checkBanc Csø|p|j}|jƒ}|j|ƒ}|rTtjd|jj|t|ƒdkrNdt|j ƒƒdndƒxŠ|j ƒD]~\}}y0tj d|jj||ƒ|j sŠ|j ƒ|j|ƒWq^tk rÚ}z(tjd|jj|||tjƒtjkd�dSd }~Xq^Xq^Wd |_|jrô|j|_dS) z¨Repeat bans for the ticket. Executes the actions in order to reban the host given in the ticket. Parameters ---------- ticket : Ticket Ticket to reban z[%s] Reban %s%srz , action %rrrwz[%s] action %r: reban %sz;Failed to execute reban jail '%s' action '%s' info '%r': %s)rdNT)rrxr—rEr¡rrr6rRÚkeysr5rjrŠr£Zrebanrfrgrhr_rir¤r) rrbr2r^rIr–rr-rkrrrZ__reBan4s*  4 zActions.__reBancCsÂ|jj|ƒsdSd}x¨|jjƒD]š\}}yJ|jr>t|ddƒr>w |jsFw |dkrX|j|ƒ}|jsf|j ƒ|j |ƒWq t k r¸}z*t j d|jj|||t jƒtjkd�WYdd}~Xq Xq WdS)Nr›Fz9Failed to execute ban jail '%s' action '%s' info '%r': %s)rd)rZ _inBanListrr5rr¢Z _prolongabler—rŠr£ZprolongrfrErgrrrhr_ri)rrbr–rr-rkrrrÚ _prolongBanVs&   zActions._prolongBancCsT|jjtjƒ|ƒ}x|D]}|j|ƒqWt|ƒ}|rPtjd||jjƒ|j j ƒ|S)zKCheck for IP address to unban. Unban IP addresses which are outdated. zUnbanned %s, %s ticket(s) in %r) rZ unBanListr rPr[r6rErjr„rr)rZmaxCountrJrbr`rrrZ __checkUnBanms zActions.__checkUnBanc s˜d}|dkr"tjdƒ|jjƒ}nd}t|jƒ}d}i}xæ|dk rF|n|jjƒD]Ì\}‰y._beforeRepairz Unban tickets each individualyz Flush jail in database)r2r^rz! Unbanned %s, %s ticket(s) in %r)rErjrZ flushBanListr=rr5r!rQr Z actionflushr¡rrrªrfrgrhr_rirFržrYrZr[r„) rr1r2r3r^rJr`Ú unbactionsrrkr«rbr)r-r3rZ __flushBan{sF            zActions.__flushBanc CsÎ|dkr|j}n|}|jƒ}|j|ƒ}|rr?rArCrDrHrMrLrUrWr8rsr r•r—r rSr§r©rrr7r[r¯rrrrr2s: .    : 3] U "  6 r)"Ú __author__Z __copyright__Z __license__r_ÚosÚsysrPÚcollections.abcrÚ ImportErrorÚ collectionsrZ banmanagerrrZipdnsrZ jailthreadrr-r r r Zmytimer Zobserverr ZutilsrZhelpersrr$rErrrrrÚs*       server/__pycache__/actions.cpython-36.pyc000064400000053175152231502150014357 0ustar003 TB(d=aã @sôdZdZdZddlZddlZddlZddlZyddlmZWn e k r\ddl mZYnXddl m Z ddl m Z mZdd lmZdd lmZdd lmZmZmZdd lmZdd lmZddlmZddlmZeeƒZ Gdd„deeƒZ!dS)z Cyril Jaquierz Copyright (c) 2004 Cyril JaquierZGPLéN)ÚMapping)Ú OrderedDicté)Ú BanManagerÚ BanTicket)ÚIPAddr)Ú JailThread)Ú ActionBaseÚ CommandActionÚ CallingMap)ÚMyTime)Ú Observers)ÚUtilsé)Ú getLoggerc@seZdZdZdd„Zedd„ƒZd?dd „Zd@d d „Zd d„Z dd„Z dd„Z dd„Z dd„Z dd„Zdd„Zdd„Zdd„ZdAdd „Zd!d"„ZdBd#d$„ZdCd%d&„Zd'd(„ZGd)d*„d*eƒZd+d,„ZdDd.d/„ZdEd0d1„ZdFd2d3„Zd4d5„ZdGd6d7„ZdHd8d9„ZdId:d;„Z dJd=d>„Z!dS)KÚActionsaŠHandles jail actions. This class handles the actions of the jail. Creation, deletion or to actions must be done through this class. This class is based on the Mapping type, and the `add` method must be used to add new actions. This class also starts and stops the actions, and fetches bans from the jail executing these bans via the actions. Parameters ---------- jail: Jail The jail of which the actions belongs to. Attributes ---------- daemon ident name status active : bool Control the state of the thread. idle : bool Control the idle state of the thread. sleeptime : int The time the thread sleeps for in the loop. cCsLtj|d|jd�||_tƒ|_tƒ|_d|_d|_ d|_ |j d|_ dS)Nzf2b/a.)Únameré r) rÚ__init__rÚ_jailrÚ_actionsrÚ banManagerÚbanEpochÚ _Actions__lastConsistencyCheckTMÚ banPrecedenceÚ unbanMaxCount)ÚselfÚjail©rú/usr/lib/python3.6/actions.pyrNszActions.__init__cCsFtj|ƒ}t|dƒs"td|ƒ‚n t|jtƒsBtd||jjfƒ‚|S)NÚActionz&%s module does not have 'Action' classz0%s module %s does not implement required methods)rZload_python_moduleÚhasattrÚ RuntimeErrorÚ issubclassr r Ú__name__)Ú pythonModuleÚmodrrrÚ_load_python_module\s    zActions._load_python_moduleNFcCsŽ||jkrN|std|ƒ‚|j|}t|dƒrNt|dƒrJ|jƒ||j|<dS|dkrdt|j|ƒ}n|j|ƒ}|j|j|f|Ž}||j|<dS)a¯Adds a new action. Add a new action if not already present, defaulting to standard `CommandAction`, or specified Python module. Parameters ---------- name : str The name of the action. pythonModule : str, optional Path to Python file which must contain `Action` class. Default None, which means `CommandAction` is used. initOpts : dict, optional Options for Python Action, used as keyword arguments for initialisation. Default None. Raises ------ ValueError If action name already exists. RuntimeError If external Python module does not have `Action` class or does not implement necessary methods as per `ActionBase` abstract class. zAction %s already existsÚreloadÚclearAllParamsN) rÚ ValueErrorr!r)Ú_reload_actionsr rr'r )rrr%ÚinitOptsr(ÚactionZcustomActionModulerrrÚaddis       z Actions.addTcs¢|rtƒˆ_n�tˆdƒržx:ˆjjƒD],\}}|ˆjkr$ˆj|jf|rJ|niŽq$Wt‡fdd„ˆjjƒDƒƒ}t|ƒr”ˆjd|dd�ˆj |d�t ˆdƒdS) z@ Begin or end of reloading resp. refreshing of all parameters r+c3s$|]\}}|ˆjkr||fVqdS)N)r+)Ú.0rr-)rrrú £sz!Actions.reload..FT)ÚdbÚactionsÚstop)r2N) Údictr+r!Úitemsrr(rÚlenÚ_Actions__flushBanÚ stopActionsÚdelattr)rZbeginrr,Zdelactsr)rrr(—s    zActions.reloadc Cs0y |j|Stk r*td|ƒ‚YnXdS)NzInvalid Action name: %s)rÚKeyError)rrrrrÚ __getitem__¬s zActions.__getitem__c Cs2y |j|=Wn tk r,td|ƒ‚YnXdS)NzInvalid Action name: %s)rr:)rrrrrÚ __delitem__²s zActions.__delitem__cCs t|jƒS)N)Úiterr)rrrrÚ__iter__¸szActions.__iter__cCs t|jƒS)N)r6r)rrrrÚ__len__»szActions.__len__cCsdS)NFr)rÚotherrrrÚ__eq__¾szActions.__eq__cCst|ƒS)N)Úid)rrrrÚ__hash__ÁszActions.__hash__cCs(tj|ƒ}|jj|ƒtjd|ƒdS)Nz banTime: %s)r Z str2secondsrÚ setBanTimeÚlogSysÚinfo)rÚvaluerrrrDÉs  zActions.setBanTimecCs |jjƒS)N)rÚ getBanTime)rrrrrHÓszActions.getBanTimecsD|jjƒ‰|sˆSt|ƒdkr2|dˆkr.dSdS‡fdd„|DƒS)Nrrcsg|]}|ˆkrdnd‘qS)rrr)r/Úip)Úlstrrú Üsz%Actions.getBanned..)rÚ getBanListr6)rZidsr)rJrÚ getBannedÖs   zActions.getBannedcCs|jjd|d�S)zkReturns the list of banned IP addresses. Returns ------- list The list of banned IP addresses. T)ZorderedÚwithTime)rrL)rrNrrrrLÞszActions.getBanListcs<tjƒ‰t|tƒr&‡fdd„|Dƒ}n t|ˆƒf}|j|ƒS)zBan an IP or list of IPs.c3s|]}t|ˆƒVqdS)N)r)r/rI)ÚunixTimerrr0îsz&Actions.addBannedIP..)r ÚtimeÚ isinstanceÚlistrÚ_Actions__checkBan)rrIÚticketsr)rOrÚ addBannedIPès   zActions.addBannedIPc Cs8|dkr|j|ƒSt|ttfƒr„g}d}xF|D]>}y||j|||ƒ7}Wq.tk rj|sf|j|ƒYq.Xq.W|r€td|ƒ‚|S|r¦|jjdk r¦|jjj |j|ƒ|j j |ƒ}|dk rÆ|j |ƒnnt|t ƒ�s t |ƒ}|j�s tt|j|j jƒƒƒ} | �r |j| ||ƒSd|} tjtj| ƒ|�r,dSt| ƒ‚dS)aORemoves banned IP calling actions' unban method Remove a banned IP now, rather than waiting for it to expire, even if set to never expire. Parameters ---------- ip : list, str, IPAddr or None The IP address (or multiple IPs as list) to unban or all IPs if None Raises ------ ValueError If `ip` is not banned Nrznot banned: %rz%s is not bannedr)r7rQrRÚtupleÚremoveBannedIPr*ÚappendrÚdatabaseÚdelBanrZ getTicketByIDÚ_Actions__unBanrZisSingleÚfilterÚcontainsrLrEÚlogÚloggingZMSG) rrIr1ZifexistsZmissedÚcntÚiÚticketZipaZipsÚmsgrrrrWõs>      zActions.removeBannedIPcCsœ|dkr|j}xˆtt|jƒƒƒD]t\}}y |jƒWnDtk rx}z(tjd|jj ||tj ƒt j kd�WYdd}~XnX|j|=tj d|jj |ƒq WdS)z>Stops the actions in reverse sequence (optionally filtered) Nz(Failed to stop jail '%s' action '%s': %s)Úexc_infoz%s: action %s terminated)rÚreversedrRr5r3Ú ExceptionrEÚerrorrrÚgetEffectiveLevelr_ÚDEBUGÚdebug)rr2rr-Úerrrr8/s  $zActions.stopActionscsäd}xhˆjjƒD]Z\}}y |jƒWqtk rh}z(tjdˆjj||tjƒt j kd�WYdd}~XqXqW�xZˆj �rÊ�yˆj r¶tj dƒtj‡fdd„dd„ˆjƒtj d ƒwrd}tˆjˆjjtjƒƒ}tjd d |ˆjƒtj‡fd d„|ƒ�r ˆjƒ}||7}| �s|ˆjk�r€ˆj �r||d 9}tjd d|�rL|ˆjk�rL|nˆj|ˆjƒˆj|�rt|ˆjk�rt|nˆjƒd}Wqrtk �rÆ}z&tjdˆjj|tjƒt j kd�WYdd}~XqrXqrWˆjdd�ˆjƒdS)zÝMain loop for Threading. This function is the main loop of the thread. It checks the jail queue and executes commands when an IP address is banned. Returns ------- bool True when the thread exits nicely. rz)Failed to start jail '%s' action '%s': %s)rdNzActions: enter idle modecsˆj pˆj S)N)ÚactiveÚidler)rrrÚVszActions.run..cSsdS)NFrrrrrrnWszActions: leave idle modeéz1Actions: wait for pending tickets %s (default %s)csˆj pˆjjS)N)rlrZhasFailTicketsr)rrrrn^srz+Actions: check-unban %s, bancnt %s, max: %sz*[%s] unhandled error in actions thread: %sT)r3)rr5ÚstartrfrErgrrrhr_rirlrmrjrZwait_forZ sleeptimeÚminrZ_nextUnbanTimer rPr^rSrrÚ_Actions__checkUnBanr7r8)rr`rr-rkZbancntZwtr)rrÚrun?sF   (   ,"( z Actions.runc@sÊeZdZd(Zdd„dd„dd„dd„dd„d d„d d„d d„d d„d d„dd„d)dd„dd„dd„dd„dd„dd„dd„dd„dd„dd„dœZejd*Zddefdd „Zd!d"„Zd#d$„Z d+d&d'„Z dS),zActions.ActionInfoÚfidú raw-ticketcCs |jjƒS)N)Ú_ActionInfo__ticketZgetIP)rrrrrnwszActions.ActionInfo.cCs |djS)NrI)Z familyStr)rrrrrnxscCs|djdƒS)NrIÚ)ZgetPTR)rrrrrnyscCs |djƒS)NrI)ZgetHost)rrrrrnzscCs |jjƒS)N)rvÚgetID)rrrrrn{scCs |jjƒS)N)rvÚ getAttempt)rrrrrn|scCs |jjƒS)N)rvÚgetTime)rrrrrn}scCs|jƒS)N)Ú _getBanTime)rrrrrn~scCs |jjƒS)N)rvZ getBanCount)rrrrrnscCsdj|jjƒƒS)NÚ )ÚjoinrvÚ getMatches)rrrrrn€scCs|jjr dSdS)Nrr)rvÚrestored)rrrrrn‚sNcCs |jj|ƒS)N)rvZgetData)rÚtagrrrrn„scCsdj|jdƒjƒƒS)Nr|T)r}Ú_mi4ipr~)rrrrrn†scCsdj|jƒjƒƒS)Nr|)r}r�r~)rrrrrn‡scCs|jdƒjƒS)NT)r�ry)rrrrrnˆscCs |jƒjƒS)N)r�ry)rrrrrn‰scCs t|jƒS)N)Úreprrv)rrrrrn‹scCs|jjjjƒS)N)Ú_ActionInfo__jailr2rÚsize)rrrrrn�scCs|jjjjƒS)N)rƒr2rÚ getBanTotal)rrrrrnŽscCs|jjjjƒS)N)rƒr\Ú failManagerr„)rrrrrn�scCs|jjjjƒS)N)rƒr\r†Z getFailTotal)rrrrrn�s)rIZfamilyzip-revzip-hostrtZfailuresrPZbantimeZbancountZmatchesrzF-*Z ipmatchesZ ipjailmatchesZ ipfailuresZipjailfailuresz raw-ticketz jail.bannedzjail.banned_totalz jail.foundzjail.found_totalÚ__ticketÚ__jailÚ__mi4ipTcCs$||_||_tƒ|_||_||_dS)N)rvrƒr4ZstorageÚ immutableÚdata)rrbrrŠr‹rrrr•s zActions.ActionInfo.__init__cCs|j|j|j|j|jjƒƒS)N)Ú __class__rvrƒrŠr‹Úcopy)rrrrr�œszActions.ActionInfo.copycCs&|jjƒ}|dkr|jjjƒ}t|ƒS)N)rvrHrƒr2Úint)rÚbtimerrrr{Ÿs  zActions.ActionInfo._getBanTimeFcCsôt|dƒsi|_|j}|rdnd}||krD||dk r>||S|jSyR|j}|d}d||<|jsh|jS|r€|jj|d�||<n|jj||d�||<WnBtk rØ}z&tjd||j |tj ƒt j kd �WYdd}~XnX||dk rî||S|jS) a¡Gets bans merged once, a helper for lambda(s), prevents stop of executing action by any exception inside. This function never returns None for ainfo lambdas - always a ticket (merged or single one) and prevents any errors through merging (to guarantee ban actions will be executed). [TODO] move merging to observer - here we could wait for merge and read already merged info from a database Parameters ---------- overalljails : bool switch to get a merged bans : False - (default) bans merged for current jail only True - bans merged for all jails of current ip address Returns ------- BanTicket merged or self ticket only r‰ÚallrNrI)rI)rIrz+Failed to get %s bans merged, jail '%s': %s)rd) r!Z_ActionInfo__mi4iprvrƒrYZ getBansMergedrfrErgrrhr_ri)rZ overalljailsZmiÚidxrrIrkrrrr�¤s*  $zActions.ActionInfo._mi4ip)rtru)N)r‡rˆr‰)F) r$Ú __module__Ú __qualname__Z CM_REPR_ITEMSZAI_DICTr Ú __slots__rr�r{r�rrrrÚ ActionInfors6  r•cCs$|stdtjƒƒ}tj||jƒ}|S)Nrw)rr rPrr•r)rrbÚaInforrrÚ_getActionInfoÏszActions._getActionInfoédccs4d}x*||kr.|jjƒ}|sP|V|d7}qWdS)zAGenerator to get maximal count failure tickets from fail-manager.rrN)rZ getFailTicket)rÚcountr`rbrrrZ__getFailTicketsÕs  zActions.__getFailTicketscsÈd}|s|j|jƒ}d}�x€|D�]v}tj|ƒ‰|j|jjƒƒ}ˆjƒ}|jˆƒ}i}|jjˆ|d��r^|d7}t j dk r–ˆj r–t j j dˆ|j |ƒtjd|j jˆj s¬dnd|ƒx�|jjƒD]‚\} } y0ˆj rÞt| d d ƒrÞwÀ|jsì|jƒ| j|ƒWqÀtk �r@} z*tjd |j j| || tjƒtjkd �WYdd} ~ XqÀXqÀWd ˆ_|j�r˜|jˆ_q |jddƒ�r€tjd|j j|ƒq |jdˆƒ‰ˆj�rŠ|j ƒˆj ƒ} | dk�r´tjn| dk�rÄtj!ntj"} tj#| d|j j|ƒˆj|jk�rF| dk�rF| �rFt$j%ƒ|j&dk�rFt$j%ƒ|_&x(|jj'ƒD]} t(| dƒ�r(| j)ƒ�q(Wˆj|jk�r˜|�svt*‡fdd„|jjƒDƒƒ}||j+ˆ|d�7}q ||j+ˆƒ7}q W|�rÄtj,d||jj-ƒ|jj.ƒ|j jƒ|S)aCheck for IP address to ban. If tickets are not specified look in the jail queue for FailTicket. If a ticket is available, it executes the "ban" command and adds a ticket to the BanManager. Returns ------- bool True if an IP address get banned. rN)ÚreasonrZbanFoundz [%s] %sBan %srwzRestore Ú norestoredFz9Failed to execute ban jail '%s' action '%s' info '%r': %s)rdTZexpiredz[%s] Ignore %s, expired bantimerbéé<z[%s] %s already bannedÚconsistencyCheckc3s&|]\}}|jˆjkr||fVqdS)N)r)r/rr-)ÚbTicketrrr0(sz%Actions.__checkBan..)r2z"Banned %s / %s, %s ticket(s) in %r)/Ú_Actions__getFailTicketsrrZwraprHrrxr—Z addBanTicketr ZMainrr.rrEÚnoticerrr5ÚgetattrrŠÚresetZbanrfrgrhr_riÚbannedrÚgetrFrzZNOTICEZWARNINGr^r rPrÚvaluesr!ržr4Ú_Actions__reBanrjr…r„)rrTr`Z rebanactsrbr�rIr–ršrr-rkZdiftmZllr)rŸrZ __checkBanßsp     (    zActions.__checkBanc Csø|p|j}|jƒ}|j|ƒ}|rTtjd|jj|t|ƒdkrNdt|j ƒƒdndƒxŠ|j ƒD]~\}}y0tj d|jj||ƒ|j sŠ|j ƒ|j|ƒWq^tk rÚ}z(tjd|jj|||tjƒtjkd�dSd }~Xq^Xq^Wd |_|jrô|j|_dS) z¨Repeat bans for the ticket. Executes the actions in order to reban the host given in the ticket. Parameters ---------- ticket : Ticket Ticket to reban z[%s] Reban %s%srz , action %rrrwz[%s] action %r: reban %sz;Failed to execute reban jail '%s' action '%s' info '%r': %s)rdNT)rrxr—rEr¡rrr6rRÚkeysr5rjrŠr£Zrebanrfrgrhr_rir¤r) rrbr2r^rIr–rr-rkrrrZ__reBan4s*  4 zActions.__reBancCsÂ|jj|ƒsdSd}x¨|jjƒD]š\}}yJ|jr>t|ddƒr>w |jsFw |dkrX|j|ƒ}|jsf|j ƒ|j |ƒWq t k r¸}z*t j d|jj|||t jƒtjkd�WYdd}~Xq Xq WdS)Nr›Fz9Failed to execute ban jail '%s' action '%s' info '%r': %s)rd)rZ _inBanListrr5rr¢Z _prolongabler—rŠr£ZprolongrfrErgrrrhr_ri)rrbr–rr-rkrrrÚ _prolongBanVs&   zActions._prolongBancCsT|jjtjƒ|ƒ}x|D]}|j|ƒqWt|ƒ}|rPtjd||jjƒ|j j ƒ|S)zKCheck for IP address to unban. Unban IP addresses which are outdated. zUnbanned %s, %s ticket(s) in %r) rZ unBanListr rPr[r6rErjr„rr)rZmaxCountrJrbr`rrrZ __checkUnBanms zActions.__checkUnBanc s˜d}|dkr"tjdƒ|jjƒ}nd}t|jƒ}d}i}xæ|dk rF|n|jjƒD]Ì\}‰y._beforeRepairz Unban tickets each individualyz Flush jail in database)r2r^rz! Unbanned %s, %s ticket(s) in %r)rErjrZ flushBanListr=rr5r!rQr Z actionflushr¡rrrªrfrgrhr_rirFržrYrZr[r„) rr1r2r3r^rJr`Ú unbactionsrrkr«rbr)r-r3rZ __flushBan{sF            zActions.__flushBanc CsÎ|dkr|j}n|}|jƒ}|j|ƒ}|rr?rArCrDrHrMrLrUrWr8rsr r•r—r rSr§r©rrr7r[r¯rrrrr2s: .    : 3] U "  6 r)"Ú __author__Z __copyright__Z __license__r_ÚosÚsysrPÚcollections.abcrÚ ImportErrorÚ collectionsrZ banmanagerrrZipdnsrZ jailthreadrr-r r r Zmytimer Zobserverr ZutilsrZhelpersrr$rErrrrrÚs*       server/__pycache__/asyncserver.cpython-36.opt-1.pyc000064400000020145152231502150016211 0ustar003 TB(då'ã@sÞdZdZdZddlmZmZmZddlZddlZddl Z ddl Z ddl Z ddl Z ddl Z ddlZddlZddlmZdd lmZdd lmZmZmZeeƒZGd d „d ejƒZddd„ZGdd„dejƒZGdd„deƒZ dS)z Cyril Jaquierz Copyright (c) 2004 Cyril JaquierZGPLé)ÚdumpsÚloadsÚHIGHEST_PROTOCOLNé)ÚUtilsé)ÚCSPROTO)ÚloggingÚ getLoggerÚformatExceptionInfoc@sLeZdZdd„Zdd„Zdd„Zdd„ZGd d „d eƒZd d „Z d d„Z dS)ÚRequestHandlercCs0tjj||ƒ||_||_g|_|jtjƒdS)N) ÚasynchatÚ async_chatÚ__init__Ú_RequestHandler__connÚ_RequestHandler__transmitterÚ_RequestHandler__bufferZset_terminatorrÚEND)ÚselfÚconnÚ transmitter©rú!/usr/lib/python3.6/asyncserver.pyr5s zRequestHandler.__init__c CsF|jrB|j}d|_y|jtjƒ|jƒWntjk r@YnXdS)N)rÚshutdownÚsocketÚ SHUT_RDWRÚcloseÚerror)rrrrrZ__close=s  zRequestHandler.__closecCs|jƒtjj|ƒdS)N)Ú_RequestHandler__closer rÚ handle_close)rrrrrGszRequestHandler.handle_closecCs|jj|ƒdS)N)rÚappend)rÚdatarrrÚcollect_incoming_dataKsz$RequestHandler.collect_incoming_datac@s eZdZdS)zRequestHandler.LoadErrorN)Ú__name__Ú __module__Ú __qualname__rrrrÚ LoadErrorPsr&cCs(yº|j}g|_tjj|ƒ}|tjkr0|jƒdSy t|ƒ}WnFtk r‚}z*tj d|tj ƒt j kd�t j|ƒ‚WYdd}~XnX|jr˜|jj|ƒ}ndg}t|tƒ}|j|tjƒWnhtk �r"}zJt|t jƒsôtj d|tj ƒt j kd�td|tƒ}|j|tjƒWYdd}~XnXdS)Nz$PROTO-error: load message failed: %s)Úexc_infoZSHUTDOWNzCaught unhandled exception: %rz ERROR: %s)rrZEMPTYÚjoinZCLOSEÚclose_when_donerÚ ExceptionÚlogSysrZgetEffectiveLevelr ÚDEBUGr r&rZproceedrrÚpushrÚ isinstance)rÚmessageÚerrrÚfound_terminatorXs0     zRequestHandler.found_terminatorcCs€yPtƒ\}}tjdt|ƒƒtjtjƒjƒƒtd|tƒ}|j |t j ƒWn"t k rr}zWYdd}~XnX|j ƒdS)Nz"Unexpected communication error: %sz ERROR: %s)r r+rÚstrÚ tracebackÚ format_excÚ splitlinesrrr-rrr*r))rZe1Ze2r/r0rrrÚ handle_error~s zRequestHandler.handle_errorN) r#r$r%rrrr"r*r&r1r6rrrrr 3s &r FcCs†|si}d|d<|dkrtj}tj}t|ƒr2|}n2|rdtjrdttjdƒrdtj dƒt |ƒd}tj}�x|ƒ�r€y$||ƒ|dr’|dd8<Wqht k �r|}zÊ|ƒs°P|dd7<|ddk�r|j dt jt jfkrötjd t|ƒƒntjd t|ƒƒnd|ddk�r,tj|ƒtjd ƒn@|dd k�rl|j dt jk�s^t|jƒƒdk�rltjd |ƒPWYdd}~XqhXqhWdS) zôCustom event loop implementation Uses poll instead of loop to respect `active` flag, to avoid loop timeout mistake: different in poll and poll2 (sec vs ms), and to prevent sporadic errors like EBADF 'Bad file descriptor' etc. (see gh-161) rÚlistenNÚpollz"Server listener (select) uses pollièréz Server connection was closed: %sz0Too many errors - stop logging connection errorsédz+Too many errors - critical count reached %r)rZDEFAULT_SLEEP_TIMEÚasyncorer8ÚcallableZpoll2ÚhasattrZselectr+ÚdebugÚfloatr*ÚargsÚerrnoZENOTCONNZEBADFÚinfor2rZ exceptionÚEMFILEÚsumÚvaluesÚcritical)ZactiveÚtimeoutÚuse_pollÚ err_countr8r0rrrÚloop‹sB      rJc@sbeZdZdd„Zdd„Zdd„Zdd d „Zd d „Zd d„Zdd„Z dd„Z dd„Z e dd„ƒZ dS)Ú AsyncServercCs:tjj|ƒ||_d|_d|_d|_dddœ|_d|_dS)Nz/var/run/fail2ban/fail2ban.sockFr)Úacceptr7) r;Ú dispatcherrÚ_AsyncServer__transmitterÚ_AsyncServer__sockÚ_AsyncServer__initÚ_AsyncServer__activeÚ_AsyncServer__errCountÚonstart)rrrrrrÃs  zAsyncServer.__init__cCsdS)NFr)rrrrÚwritableÏszAsyncServer.writablecCsy|jƒ\}}WnÈtk rØ}z¬|jdd7<|jddkr^tjd||jddkd�nl|jddkrxtjdƒnR|jddkrÊt|tjƒr¢|jdt j ks´t |jj ƒƒd krÊtj d |jƒ|jƒdSd}~XnX|jdrö|jdd8<tj|ƒt||jƒdS) NrLrr9zAccept socket error: %s)r'z.Too many acceptor errors - stop logging errorsr:rièz+Too many errors - critical count reached %r)rLr*rRr+Úwarningrr.rr@rArCrDrErFÚstoprKÚ_AsyncServer__markCloseOnExecr rN)rrZaddrr0rrrÚ handle_acceptÒs&   zAsyncServer.handle_acceptNFcsòtjƒˆ_|ˆ_tjj|ƒrFtjdƒ|r>tj dƒˆj ƒnt dƒ‚ˆj t jt jƒˆjƒyˆj|ƒWn"tk rŽt dˆjƒ‚YnXtjˆj ƒˆjdƒdˆ_ˆ_ˆ_ˆjrƈjƒt‡fdd„||ˆjd �d ˆ_ˆjƒdS) Nz$Fail2ban seems to be already runningzForcing execution of the serverzServer already runningzUnable to bind socket %srTcsˆjS)N)Ú_AsyncServer__loopr)rrrÚ sz#AsyncServer.start..)rGrHrIF)Ú threadingÚcurrent_threadÚ_AsyncServer__workerrOÚosÚpathÚexistsr+rrUÚ _remove_sockÚAsyncServerExceptionZ create_socketrZAF_UNIXZ SOCK_STREAMZset_reuse_addrZbindr*rKrWr7rPrYrQrSrJrRrV)rÚsockÚforcerGrHr)rrÚstartñs,       zAsyncServer.startc s¶d}ˆjrrdˆ_ˆjr@yˆjjtjƒWntjk r>YnXtjjˆƒt j ƒˆj krnt j ‡fdd„dƒd}ˆjržtjjˆjƒržˆjƒtjdˆjƒ|r¬tjdƒdˆ_dS)NFcsˆj S)N)rQr)rrrrZsz#AsyncServer.close..rTzRemoved socket file zSocket shutdown)rQrYrrrrr;rMrr[r\r]rZwait_forrPr^r_r`rOrar+r>)rZstopflgr)rrrs$  zAsyncServer.closecCs|jrtjdƒd|_dS)NzStop communication, shutdown)rNr+r>)rrrrÚstop_communication,s zAsyncServer.stop_communicationcCs|jƒ|jƒdS)N)rfr)rrrrrV4szAsyncServer.stopcCs|jS)N)rQ)rrrrÚisActive9szAsyncServer.isActivecCsFytj|jƒWn0tk r@}z|jtjkr0‚WYdd}~XnXdS)N)r^ÚremoverOÚOSErrorrAÚENOENT)rr0rrrra?s  zAsyncServer._remove_sockcCs0|jƒ}tj|tjƒ}tj|tj|tjBƒdS)N)ÚfilenoÚfcntlZF_GETFDZF_SETFDZ FD_CLOEXEC)rcÚfdÚflagsrrrZ__markCloseOnExecLszAsyncServer.__markCloseOnExec)NF)r#r$r%rrTrXrerrfrVrgraÚ staticmethodrWrrrrrKÁs   rKc@s eZdZdS)rbN)r#r$r%rrrrrbVsrb)NFN)!Ú __author__Z __copyright__Z __license__Úpicklerrrr r;rArlr^rÚsysr[r3ZutilsrZprotocolrZhelpersr r r r#r+rr rJrMrKr*rbrrrrÚs*  X 6server/__pycache__/asyncserver.cpython-36.pyc000064400000020145152231502150015252 0ustar003 TB(då'ã@sÞdZdZdZddlmZmZmZddlZddlZddl Z ddl Z ddl Z ddl Z ddl Z ddlZddlZddlmZdd lmZdd lmZmZmZeeƒZGd d „d ejƒZddd„ZGdd„dejƒZGdd„deƒZ dS)z Cyril Jaquierz Copyright (c) 2004 Cyril JaquierZGPLé)ÚdumpsÚloadsÚHIGHEST_PROTOCOLNé)ÚUtilsé)ÚCSPROTO)ÚloggingÚ getLoggerÚformatExceptionInfoc@sLeZdZdd„Zdd„Zdd„Zdd„ZGd d „d eƒZd d „Z d d„Z dS)ÚRequestHandlercCs0tjj||ƒ||_||_g|_|jtjƒdS)N) ÚasynchatÚ async_chatÚ__init__Ú_RequestHandler__connÚ_RequestHandler__transmitterÚ_RequestHandler__bufferZset_terminatorrÚEND)ÚselfÚconnÚ transmitter©rú!/usr/lib/python3.6/asyncserver.pyr5s zRequestHandler.__init__c CsF|jrB|j}d|_y|jtjƒ|jƒWntjk r@YnXdS)N)rÚshutdownÚsocketÚ SHUT_RDWRÚcloseÚerror)rrrrrZ__close=s  zRequestHandler.__closecCs|jƒtjj|ƒdS)N)Ú_RequestHandler__closer rÚ handle_close)rrrrrGszRequestHandler.handle_closecCs|jj|ƒdS)N)rÚappend)rÚdatarrrÚcollect_incoming_dataKsz$RequestHandler.collect_incoming_datac@s eZdZdS)zRequestHandler.LoadErrorN)Ú__name__Ú __module__Ú __qualname__rrrrÚ LoadErrorPsr&cCs(yº|j}g|_tjj|ƒ}|tjkr0|jƒdSy t|ƒ}WnFtk r‚}z*tj d|tj ƒt j kd�t j|ƒ‚WYdd}~XnX|jr˜|jj|ƒ}ndg}t|tƒ}|j|tjƒWnhtk �r"}zJt|t jƒsôtj d|tj ƒt j kd�td|tƒ}|j|tjƒWYdd}~XnXdS)Nz$PROTO-error: load message failed: %s)Úexc_infoZSHUTDOWNzCaught unhandled exception: %rz ERROR: %s)rrZEMPTYÚjoinZCLOSEÚclose_when_donerÚ ExceptionÚlogSysrZgetEffectiveLevelr ÚDEBUGr r&rZproceedrrÚpushrÚ isinstance)rÚmessageÚerrrÚfound_terminatorXs0     zRequestHandler.found_terminatorcCs€yPtƒ\}}tjdt|ƒƒtjtjƒjƒƒtd|tƒ}|j |t j ƒWn"t k rr}zWYdd}~XnX|j ƒdS)Nz"Unexpected communication error: %sz ERROR: %s)r r+rÚstrÚ tracebackÚ format_excÚ splitlinesrrr-rrr*r))rZe1Ze2r/r0rrrÚ handle_error~s zRequestHandler.handle_errorN) r#r$r%rrrr"r*r&r1r6rrrrr 3s &r FcCs†|si}d|d<|dkrtj}tj}t|ƒr2|}n2|rdtjrdttjdƒrdtj dƒt |ƒd}tj}�x|ƒ�r€y$||ƒ|dr’|dd8<Wqht k �r|}zÊ|ƒs°P|dd7<|ddk�r|j dt jt jfkrötjd t|ƒƒntjd t|ƒƒnd|ddk�r,tj|ƒtjd ƒn@|dd k�rl|j dt jk�s^t|jƒƒdk�rltjd |ƒPWYdd}~XqhXqhWdS) zôCustom event loop implementation Uses poll instead of loop to respect `active` flag, to avoid loop timeout mistake: different in poll and poll2 (sec vs ms), and to prevent sporadic errors like EBADF 'Bad file descriptor' etc. (see gh-161) rÚlistenNÚpollz"Server listener (select) uses pollièréz Server connection was closed: %sz0Too many errors - stop logging connection errorsédz+Too many errors - critical count reached %r)rZDEFAULT_SLEEP_TIMEÚasyncorer8ÚcallableZpoll2ÚhasattrZselectr+ÚdebugÚfloatr*ÚargsÚerrnoZENOTCONNZEBADFÚinfor2rZ exceptionÚEMFILEÚsumÚvaluesÚcritical)ZactiveÚtimeoutÚuse_pollÚ err_countr8r0rrrÚloop‹sB      rJc@sbeZdZdd„Zdd„Zdd„Zdd d „Zd d „Zd d„Zdd„Z dd„Z dd„Z e dd„ƒZ dS)Ú AsyncServercCs:tjj|ƒ||_d|_d|_d|_dddœ|_d|_dS)Nz/var/run/fail2ban/fail2ban.sockFr)Úacceptr7) r;Ú dispatcherrÚ_AsyncServer__transmitterÚ_AsyncServer__sockÚ_AsyncServer__initÚ_AsyncServer__activeÚ_AsyncServer__errCountÚonstart)rrrrrrÃs  zAsyncServer.__init__cCsdS)NFr)rrrrÚwritableÏszAsyncServer.writablecCsy|jƒ\}}WnÈtk rØ}z¬|jdd7<|jddkr^tjd||jddkd�nl|jddkrxtjdƒnR|jddkrÊt|tjƒr¢|jdt j ks´t |jj ƒƒd krÊtj d |jƒ|jƒdSd}~XnX|jdrö|jdd8<tj|ƒt||jƒdS) NrLrr9zAccept socket error: %s)r'z.Too many acceptor errors - stop logging errorsr:rièz+Too many errors - critical count reached %r)rLr*rRr+Úwarningrr.rr@rArCrDrErFÚstoprKÚ_AsyncServer__markCloseOnExecr rN)rrZaddrr0rrrÚ handle_acceptÒs&   zAsyncServer.handle_acceptNFcsòtjƒˆ_|ˆ_tjj|ƒrFtjdƒ|r>tj dƒˆj ƒnt dƒ‚ˆj t jt jƒˆjƒyˆj|ƒWn"tk rŽt dˆjƒ‚YnXtjˆj ƒˆjdƒdˆ_ˆ_ˆ_ˆjrƈjƒt‡fdd„||ˆjd �d ˆ_ˆjƒdS) Nz$Fail2ban seems to be already runningzForcing execution of the serverzServer already runningzUnable to bind socket %srTcsˆjS)N)Ú_AsyncServer__loopr)rrrÚ sz#AsyncServer.start..)rGrHrIF)Ú threadingÚcurrent_threadÚ_AsyncServer__workerrOÚosÚpathÚexistsr+rrUÚ _remove_sockÚAsyncServerExceptionZ create_socketrZAF_UNIXZ SOCK_STREAMZset_reuse_addrZbindr*rKrWr7rPrYrQrSrJrRrV)rÚsockÚforcerGrHr)rrÚstartñs,       zAsyncServer.startc s¶d}ˆjrrdˆ_ˆjr@yˆjjtjƒWntjk r>YnXtjjˆƒt j ƒˆj krnt j ‡fdd„dƒd}ˆjržtjjˆjƒržˆjƒtjdˆjƒ|r¬tjdƒdˆ_dS)NFcsˆj S)N)rQr)rrrrZsz#AsyncServer.close..rTzRemoved socket file zSocket shutdown)rQrYrrrrr;rMrr[r\r]rZwait_forrPr^r_r`rOrar+r>)rZstopflgr)rrrs$  zAsyncServer.closecCs|jrtjdƒd|_dS)NzStop communication, shutdown)rNr+r>)rrrrÚstop_communication,s zAsyncServer.stop_communicationcCs|jƒ|jƒdS)N)rfr)rrrrrV4szAsyncServer.stopcCs|jS)N)rQ)rrrrÚisActive9szAsyncServer.isActivecCsFytj|jƒWn0tk r@}z|jtjkr0‚WYdd}~XnXdS)N)r^ÚremoverOÚOSErrorrAÚENOENT)rr0rrrra?s  zAsyncServer._remove_sockcCs0|jƒ}tj|tjƒ}tj|tj|tjBƒdS)N)ÚfilenoÚfcntlZF_GETFDZF_SETFDZ FD_CLOEXEC)rcÚfdÚflagsrrrZ__markCloseOnExecLszAsyncServer.__markCloseOnExec)NF)r#r$r%rrTrXrerrfrVrgraÚ staticmethodrWrrrrrKÁs   rKc@s eZdZdS)rbN)r#r$r%rrrrrbVsrb)NFN)!Ú __author__Z __copyright__Z __license__Úpicklerrrr r;rArlr^rÚsysr[r3ZutilsrZprotocolrZhelpersr r r r#r+rr rJrMrKr*rbrrrrÚs*  X 6server/__pycache__/banmanager.cpython-36.opt-1.pyc000064400000017702152231502150015745 0ustar003 TB(dŸ*ã@sZdZdZdZddlmZddlmZddlmZdd l m Z m Z e e ƒZ Gd d „d ƒZd S) z Cyril Jaquierz Copyright (c) 2004 Cyril JaquierZGPLé)ÚLocké)Ú BanTicket)ÚMyTimeé)Ú getLoggerÚloggingc@sªeZdZdd„Zdd„Zdd„Zdd„Zd d „Zd)d d „Zdd„Z e dd„ƒZ d*dd„Z dd„Z dd„Zdd„Zifdd„Zdd„Zdd „Zd+d"d#„Zd$d%„Zd&d'„Zd(S),Ú BanManagercCs(tƒ|_tƒ|_d|_d|_tj|_dS)NiXr) rÚ_BanManager__lockÚdictÚ_BanManager__banListÚ_BanManager__banTimeÚ_BanManager__banTotalrÚMAX_TIMEÚ_nextUnbanTime)Úself©rú /usr/lib/python3.6/banmanager.pyÚ__init__2s zBanManager.__init__cCst|ƒ|_dS)N)Úintr )rÚvaluerrrÚ setBanTimeDszBanManager.setBanTimecCs|jS)N)r )rrrrÚ getBanTimeMszBanManager.getBanTimecCs ||_dS)N)r)rrrrrÚ setBanTotalUszBanManager.setBanTotalcCs|jS)N)r)rrrrÚ getBanTotal]szBanManager.getBanTotalFc s’|stˆjjƒƒSˆj�8g}x,ˆjjƒD]}|jˆjƒ}|j||fƒq*WWdQRX|jdd„d�t j ‰|r„‡‡fdd„|DƒSdd„|DƒS)NcSs|dS)Nrr)ÚtrrrÚmsz'BanManager.getBanList..)Úkeyc sDg|]<}d|djƒˆ|djƒƒ|djˆjƒˆ|dƒf‘qS)z%s %s + %d = %srr)ÚgetIDÚgetTimerr )Ú.0r)rÚt2srrú psz)BanManager.getBanList..cSsg|]}|djƒ‘qS)r)r)r rrrrr"ts) Úlistr Úkeysr ÚvaluesÚgetEndOfBanTimer ÚappendÚsortrZtime2str)rZorderedZwithTimeZlstÚticketÚeobr)rr!rÚ getBanListes  zBanManager.getBanListcCstt|jjƒƒƒS)N)Úiterr#r r%)rrrrÚ__iter__{szBanManager.__iter__cCs |dkst|ƒdkrdS|SdS)NrÚunknown)Úlen)rrrrÚhandleBlankResult„szBanManager.handleBlankResulté c,Cs`gggdœ}t|dƒs¬y4ddladdlatjjƒ}||_|d|_||_Wn`t k rª}zDt j dƒt |ƒ|d<|dj dƒ|dj dƒ|d j dƒ|Sd}~XnX|j�d d „t|jjƒƒDƒ}WdQRX�y.�x&|D�]}|j|jröd nd ƒ}�yþ|j}|j|dƒ}|�s tdƒ‚tƒ} tƒ} tƒ} x~|D]v} dd „| jƒjdƒDƒ\} }}}}|j| ƒ} |j|ƒ}|j|ƒ}| j|j| ƒƒ| j|j|ƒƒ| j|j|ƒƒ�q8W|dj djt| ƒƒƒ|dj djt| ƒƒƒ|d j djt| ƒƒƒWqâtjjk �r@|dj dƒ|dj dƒ|d j dƒYqâtjjtjj tjj!fk �r¨}z:t j d||ƒt j"t#j$k�rŠt j|ƒt |ƒ|d<PWYdd}~Xqât%k �rþ}z:t j d||ƒt j"t#j$k�ràt j|ƒt |ƒ|d<PWYdd}~XqâXqâWWnTt%k �rZ}z6t j d|ƒt j"t#j$k�r>t j|ƒt |ƒ|d<WYdd}~XnX|S)N)ÚasnÚcountryÚrirÚ dnsResolverrrz7dnspython package is required but could not be importedÚerrorr2r3r4cSsg|] }|jƒ‘qSr)ZgetIP)r ZbanDatarrrr"¤sz:BanManager.getBanListExtendedCymruInfo..zorigin.asn.cymru.comzorigin6.asn.cymru.comZTXTzNo data retrievedcSsg|]}|jdƒ‘qS)z'" )Ústrip)r Zanswerrrrr"·sú|z, Znxdomainz)DNSException %r querying Cymru for %s TXTz0Unhandled Exception %r querying Cymru for %s TXTz*Failure looking up extended Cymru info: %s)&ÚhasattrZ dns.exceptionZdnsZ dns.resolverÚresolverZResolverZlifetimeÚtimeoutr5Ú ImportErrorÚlogSysr6Úreprr'r r#r r%ZgetPTRZisIPv4ZqueryÚ ValueErrorÚsetZto_textÚsplitr0ÚaddÚjoinÚsortedZNXDOMAINÚ exceptionZ DNSExceptionZ NoNameserversZTimeoutÚlevelrÚDEBUGÚ Exception)rr;Z return_dictr:ÚeZbanIPsZipZquestionZanswersZasnsZ countriesZrirsZrdatar2Znetr3r4ZchangedZdnserrrÚgetBanListExtendedCymruInfo�s€       "   "   $      z&BanManager.getBanListExtendedCymruInfocCsLydd„|dDƒStk rF}ztjdƒtj|ƒgSd}~XnXdS)NcSsg|]}|‘qSrr)r r2rrrr"ász3BanManager.geBanListExtendedASN..r2zFailed to lookup ASN)rHr=r6rE)rÚ cymru_inforIrrrÚgeBanListExtendedASNßs   zBanManager.geBanListExtendedASNcCsLydd„|dDƒStk rF}ztjdƒtj|ƒgSd}~XnXdS)NcSsg|]}|‘qSrr)r r3rrrr"ðsz7BanManager.geBanListExtendedCountry..r3zFailed to lookup Country)rHr=r6rE)rrKrIrrrÚgeBanListExtendedCountryîs   z#BanManager.geBanListExtendedCountrycCsLydd„|dDƒStk rF}ztjdƒtj|ƒgSd}~XnXdS)NcSsg|]}|‘qSrr)r r4rrrr"ÿsz3BanManager.geBanListExtendedRIR..r4zFailed to lookup RIR)rHr=r6rE)rrKrIrrrÚgeBanListExtendedRIRýs   zBanManager.geBanListExtendedRIRc Csä|j|jƒ}|tjƒkr$d|d<dS|j�°|jƒ}|jj|ƒ}|r¦||d<||j|jƒkr¢d|d<|j|jƒ}|dkr˜|j ƒ|j ƒ}|dkr˜||7}|j |ƒdS||j|<|j d7_ |j ƒ|j |krÖ||_ dSQRXdS) NrZexpiredFr)ZprolongrTéÿÿÿÿ)r&r rÚtimer rr ÚgetrrrrZ incrBanCountr)rr)Úreasonr*ÚfidZ oldticketZbtmZdiftmrrrÚ addBanTicket s0       zBanManager.addBanTicketcCs t|jƒS)N)r/r )rrrrÚsize1szBanManager.sizecCs|jƒ|jkS)N)rr )rr)rrrÚ _inBanList<szBanManager._inBanListéÿÿÿc sô|j�ä|j}||krtƒSi‰tj}xT|jjƒD]F\}}|j|jƒ}||krl|ˆ|<t ˆƒ|krx|j}Pq2||kr2|}q2W||_t ˆƒrÞt ˆƒdt |jƒdkrÀx8ˆj ƒD] }|j|=q®Wnt ‡fdd„|jjƒDƒƒ|_tˆj ƒƒSQRXdS)Ng@g@c3s"|]\}}|ˆkr||fVqdS)Nr)r rSr))Ú unBanListrrú dsz'BanManager.unBanList..) r rr#rrr Úitemsr&r r/r$r r%)rrPZmaxCountZ nextUnbanTimerSr)r*r)rXrrXFs,  zBanManager.unBanListc Cs,|j�t|jjƒƒ}tƒ|_|SQRXdS)N)r r#r r%r )rZuBListrrrÚ flushBanListpszBanManager.flushBanListcCs8|j�(y |jj|ƒStk r(YnXWdQRXdS)N)r r ÚpopÚKeyError)rrSrrrÚ getTicketByIDzs  zBanManager.getTicketByIDN)FF)r1)rW)Ú__name__Ú __module__Ú __qualname__rrrrrr+r-Ú staticmethodr0rJrLrMrNrTrUrVrXr[r^rrrrr +s$   O %  * r N)Ú __author__Z __copyright__Z __license__Z threadingrr)rZmytimerZhelpersrrr_r=r rrrrÚs    server/__pycache__/banmanager.cpython-36.pyc000064400000017702152231502150015006 0ustar003 TB(dŸ*ã@sZdZdZdZddlmZddlmZddlmZdd l m Z m Z e e ƒZ Gd d „d ƒZd S) z Cyril Jaquierz Copyright (c) 2004 Cyril JaquierZGPLé)ÚLocké)Ú BanTicket)ÚMyTimeé)Ú getLoggerÚloggingc@sªeZdZdd„Zdd„Zdd„Zdd„Zd d „Zd)d d „Zdd„Z e dd„ƒZ d*dd„Z dd„Z dd„Zdd„Zifdd„Zdd„Zdd „Zd+d"d#„Zd$d%„Zd&d'„Zd(S),Ú BanManagercCs(tƒ|_tƒ|_d|_d|_tj|_dS)NiXr) rÚ_BanManager__lockÚdictÚ_BanManager__banListÚ_BanManager__banTimeÚ_BanManager__banTotalrÚMAX_TIMEÚ_nextUnbanTime)Úself©rú /usr/lib/python3.6/banmanager.pyÚ__init__2s zBanManager.__init__cCst|ƒ|_dS)N)Úintr )rÚvaluerrrÚ setBanTimeDszBanManager.setBanTimecCs|jS)N)r )rrrrÚ getBanTimeMszBanManager.getBanTimecCs ||_dS)N)r)rrrrrÚ setBanTotalUszBanManager.setBanTotalcCs|jS)N)r)rrrrÚ getBanTotal]szBanManager.getBanTotalFc s’|stˆjjƒƒSˆj�8g}x,ˆjjƒD]}|jˆjƒ}|j||fƒq*WWdQRX|jdd„d�t j ‰|r„‡‡fdd„|DƒSdd„|DƒS)NcSs|dS)Nrr)ÚtrrrÚmsz'BanManager.getBanList..)Úkeyc sDg|]<}d|djƒˆ|djƒƒ|djˆjƒˆ|dƒf‘qS)z%s %s + %d = %srr)ÚgetIDÚgetTimerr )Ú.0r)rÚt2srrú psz)BanManager.getBanList..cSsg|]}|djƒ‘qS)r)r)r rrrrr"ts) Úlistr Úkeysr ÚvaluesÚgetEndOfBanTimer ÚappendÚsortrZtime2str)rZorderedZwithTimeZlstÚticketÚeobr)rr!rÚ getBanListes  zBanManager.getBanListcCstt|jjƒƒƒS)N)Úiterr#r r%)rrrrÚ__iter__{szBanManager.__iter__cCs |dkst|ƒdkrdS|SdS)NrÚunknown)Úlen)rrrrÚhandleBlankResult„szBanManager.handleBlankResulté c,Cs`gggdœ}t|dƒs¬y4ddladdlatjjƒ}||_|d|_||_Wn`t k rª}zDt j dƒt |ƒ|d<|dj dƒ|dj dƒ|d j dƒ|Sd}~XnX|j�d d „t|jjƒƒDƒ}WdQRX�y.�x&|D�]}|j|jröd nd ƒ}�yþ|j}|j|dƒ}|�s tdƒ‚tƒ} tƒ} tƒ} x~|D]v} dd „| jƒjdƒDƒ\} }}}}|j| ƒ} |j|ƒ}|j|ƒ}| j|j| ƒƒ| j|j|ƒƒ| j|j|ƒƒ�q8W|dj djt| ƒƒƒ|dj djt| ƒƒƒ|d j djt| ƒƒƒWqâtjjk �r@|dj dƒ|dj dƒ|d j dƒYqâtjjtjj tjj!fk �r¨}z:t j d||ƒt j"t#j$k�rŠt j|ƒt |ƒ|d<PWYdd}~Xqât%k �rþ}z:t j d||ƒt j"t#j$k�ràt j|ƒt |ƒ|d<PWYdd}~XqâXqâWWnTt%k �rZ}z6t j d|ƒt j"t#j$k�r>t j|ƒt |ƒ|d<WYdd}~XnX|S)N)ÚasnÚcountryÚrirÚ dnsResolverrrz7dnspython package is required but could not be importedÚerrorr2r3r4cSsg|] }|jƒ‘qSr)ZgetIP)r ZbanDatarrrr"¤sz:BanManager.getBanListExtendedCymruInfo..zorigin.asn.cymru.comzorigin6.asn.cymru.comZTXTzNo data retrievedcSsg|]}|jdƒ‘qS)z'" )Ústrip)r Zanswerrrrr"·sú|z, Znxdomainz)DNSException %r querying Cymru for %s TXTz0Unhandled Exception %r querying Cymru for %s TXTz*Failure looking up extended Cymru info: %s)&ÚhasattrZ dns.exceptionZdnsZ dns.resolverÚresolverZResolverZlifetimeÚtimeoutr5Ú ImportErrorÚlogSysr6Úreprr'r r#r r%ZgetPTRZisIPv4ZqueryÚ ValueErrorÚsetZto_textÚsplitr0ÚaddÚjoinÚsortedZNXDOMAINÚ exceptionZ DNSExceptionZ NoNameserversZTimeoutÚlevelrÚDEBUGÚ Exception)rr;Z return_dictr:ÚeZbanIPsZipZquestionZanswersZasnsZ countriesZrirsZrdatar2Znetr3r4ZchangedZdnserrrÚgetBanListExtendedCymruInfo�s€       "   "   $      z&BanManager.getBanListExtendedCymruInfocCsLydd„|dDƒStk rF}ztjdƒtj|ƒgSd}~XnXdS)NcSsg|]}|‘qSrr)r r2rrrr"ász3BanManager.geBanListExtendedASN..r2zFailed to lookup ASN)rHr=r6rE)rÚ cymru_inforIrrrÚgeBanListExtendedASNßs   zBanManager.geBanListExtendedASNcCsLydd„|dDƒStk rF}ztjdƒtj|ƒgSd}~XnXdS)NcSsg|]}|‘qSrr)r r3rrrr"ðsz7BanManager.geBanListExtendedCountry..r3zFailed to lookup Country)rHr=r6rE)rrKrIrrrÚgeBanListExtendedCountryîs   z#BanManager.geBanListExtendedCountrycCsLydd„|dDƒStk rF}ztjdƒtj|ƒgSd}~XnXdS)NcSsg|]}|‘qSrr)r r4rrrr"ÿsz3BanManager.geBanListExtendedRIR..r4zFailed to lookup RIR)rHr=r6rE)rrKrIrrrÚgeBanListExtendedRIRýs   zBanManager.geBanListExtendedRIRc Csä|j|jƒ}|tjƒkr$d|d<dS|j�°|jƒ}|jj|ƒ}|r¦||d<||j|jƒkr¢d|d<|j|jƒ}|dkr˜|j ƒ|j ƒ}|dkr˜||7}|j |ƒdS||j|<|j d7_ |j ƒ|j |krÖ||_ dSQRXdS) NrZexpiredFr)ZprolongrTéÿÿÿÿ)r&r rÚtimer rr ÚgetrrrrZ incrBanCountr)rr)Úreasonr*ÚfidZ oldticketZbtmZdiftmrrrÚ addBanTicket s0       zBanManager.addBanTicketcCs t|jƒS)N)r/r )rrrrÚsize1szBanManager.sizecCs|jƒ|jkS)N)rr )rr)rrrÚ _inBanList<szBanManager._inBanListéÿÿÿc sô|j�ä|j}||krtƒSi‰tj}xT|jjƒD]F\}}|j|jƒ}||krl|ˆ|<t ˆƒ|krx|j}Pq2||kr2|}q2W||_t ˆƒrÞt ˆƒdt |jƒdkrÀx8ˆj ƒD] }|j|=q®Wnt ‡fdd„|jjƒDƒƒ|_tˆj ƒƒSQRXdS)Ng@g@c3s"|]\}}|ˆkr||fVqdS)Nr)r rSr))Ú unBanListrrú dsz'BanManager.unBanList..) r rr#rrr Úitemsr&r r/r$r r%)rrPZmaxCountZ nextUnbanTimerSr)r*r)rXrrXFs,  zBanManager.unBanListc Cs,|j�t|jjƒƒ}tƒ|_|SQRXdS)N)r r#r r%r )rZuBListrrrÚ flushBanListpszBanManager.flushBanListcCs8|j�(y |jj|ƒStk r(YnXWdQRXdS)N)r r ÚpopÚKeyError)rrSrrrÚ getTicketByIDzs  zBanManager.getTicketByIDN)FF)r1)rW)Ú__name__Ú __module__Ú __qualname__rrrrrr+r-Ú staticmethodr0rJrLrMrNrTrUrVrXr[r^rrrrr +s$   O %  * r N)Ú __author__Z __copyright__Z __license__Z threadingrr)rZmytimerZhelpersrrr_r=r rrrrÚs    server/__init__.py000064400000001716152231502150010164 0ustar00# 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. # Author: Cyril Jaquier # __author__ = "Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" server/action.py000064400000073500152231502150007702 0ustar00# 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. __author__ = "Cyril Jaquier and Fail2Ban Contributors" __copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2012 Yaroslav Halchenko" __license__ = "GPL" import logging import os import re import signal import subprocess import tempfile import threading import time from abc import ABCMeta try: from collections.abc import MutableMapping except ImportError: from collections import MutableMapping from .failregex import mapTag2Opt from .ipdns import DNSUtils from .mytime import MyTime from .utils import Utils from ..helpers import getLogger, _merge_copy_dicts, \ splitwords, substituteRecursiveTags, uni_string, TAG_CRE, MAX_TAG_REPLACE_COUNT # Gets the instance of the logger. logSys = getLogger(__name__) # Create a lock for running system commands _cmd_lock = threading.Lock() # Specifies whether IPv6 subsystem is available: allowed_ipv6 = DNSUtils.IPv6IsAllowed # capture groups from filter for map to ticket data: FCUSTAG_CRE = re.compile(r''); # currently uppercase only COND_FAMILIES = ('inet4', 'inet6') CONDITIONAL_FAM_RE = re.compile(r"^(\w+)\?(family)=(.*)$") # Special tags: DYN_REPL_TAGS = { # System-information: "fq-hostname": lambda: str(DNSUtils.getHostname(fqdn=True)), "sh-hostname": lambda: str(DNSUtils.getHostname(fqdn=False)) } # New line, space ADD_REPL_TAGS = { "br": "\n", "sp": " " } ADD_REPL_TAGS.update(DYN_REPL_TAGS) class CallingMap(MutableMapping, object): """A Mapping type which returns the result of callable values. `CallingMap` behaves similar to a standard python dictionary, with the exception that any values which are callable, are called and the result is returned as the value. No error handling is in place, such that any errors raised in the callable will raised as usual. Actual dictionary is stored in property `data`, and can be accessed to obtain original callable values. Attributes ---------- data : dict The dictionary data which can be accessed to obtain items uncalled """ CM_REPR_ITEMS = () # immutable=True saves content between actions, without interim copying (save original on demand, recoverable via reset) __slots__ = ('data', 'storage', 'immutable', '__org_data') def __init__(self, *args, **kwargs): self.storage = dict() self.immutable = True self.data = dict(*args, **kwargs) def reset(self, immutable=True): self.storage = dict() try: self.data = self.__org_data except AttributeError: pass self.immutable = immutable def _asrepr(self, calculated=False): # be sure it is suitable as string, so use str as checker: return "%s(%r)" % (self.__class__.__name__, self._asdict(calculated, str)) __repr__ = _asrepr def _asdict(self, calculated=False, checker=None): d = dict(self.data, **self.storage) if not calculated: return dict((n,v) for n,v in d.items() \ if not callable(v) or n in self.CM_REPR_ITEMS) for n,v in list(d.items()): if callable(v): try: # calculate: v = self.__getitem__(n) # convert if needed: if checker: checker(v) # store calculated: d[n] = v except: # can't calculate - just ignore it pass return d def getRawItem(self, key): try: value = self.storage[key] except KeyError: value = self.data[key] return value def __getitem__(self, key): try: value = self.storage[key] except KeyError: value = self.data[key] if callable(value): # check arguments can be supplied to callable (for backwards compatibility): value = value(self) if hasattr(value, '__code__') and value.__code__.co_argcount else value() self.storage[key] = value return value def __setitem__(self, key, value): # mutate to copy: if self.immutable: self.storage = self.storage.copy() self.__org_data = self.data self.data = self.data.copy() self.immutable = False self.storage[key] = value def __unavailable(self, key): raise KeyError("Key %r was deleted" % key) def __delitem__(self, key): # mutate to copy: if self.immutable: self.storage = self.storage.copy() self.__org_data = self.data self.data = self.data.copy() self.immutable = False try: del self.storage[key] except KeyError: pass del self.data[key] def __iter__(self): return iter(self.data) def __len__(self): return len(self.data) def copy(self): return self.__class__(_merge_copy_dicts(self.data, self.storage)) class ActionBase(object, metaclass=ABCMeta): """An abstract base class for actions in Fail2Ban. Action Base is a base definition of what methods need to be in place to create a Python based action for Fail2Ban. This class can be inherited from to ease implementation. Required methods: - __init__(jail, name) - start() - stop() - ban(aInfo) - unban(aInfo) Called when action is created, but before the jail/actions is started. This should carry out necessary methods to initialise the action but not "start" the action. Parameters ---------- jail : Jail The jail in which the action belongs to. name : str Name assigned to the action. Notes ----- Any additional arguments specified in `jail.conf` or passed via `fail2ban-client` will be passed as keyword arguments. """ @classmethod def __subclasshook__(cls, C): required = ( "start", "stop", "ban", "reban", "unban", ) for method in required: if not callable(getattr(C, method, None)): return False return True def __init__(self, jail, name): self._jail = jail self._name = name self._logSys = getLogger("fail2ban.%s" % self.__class__.__name__) def start(self): # pragma: no cover - abstract """Executed when the jail/action is started. """ pass def stop(self): # pragma: no cover - abstract """Executed when the jail/action is stopped. """ pass def ban(self, aInfo): # pragma: no cover - abstract """Executed when a ban occurs. Parameters ---------- aInfo : dict Dictionary which includes information in relation to the ban. """ pass def reban(self, aInfo): # pragma: no cover - abstract """Executed when a ban occurs. Parameters ---------- aInfo : dict Dictionary which includes information in relation to the ban. """ return self.ban(aInfo) @property def _prolongable(self): # pragma: no cover - abstract return False def unban(self, aInfo): # pragma: no cover - abstract """Executed when a ban expires. Parameters ---------- aInfo : dict Dictionary which includes information in relation to the ban. """ pass WRAP_CMD_PARAMS = { 'timeout': 'str2seconds', 'bantime': 'ignore', } class CommandAction(ActionBase): """A action which executes OS shell commands. This is the default type of action which Fail2Ban uses. Default sets all commands for actions as empty string, such no command is executed. Parameters ---------- jail : Jail The jail in which the action belongs to. name : str Name assigned to the action. Attributes ---------- actionban actioncheck actionreban actionreload actionrepair actionstart actionstop actionunban timeout """ _escapedTags = set(('matches', 'ipmatches', 'ipjailmatches')) def clearAllParams(self): """ Clear all lists/dicts parameters (used by reloading) """ self.__init = 1 try: self.timeout = 60 ## Command executed in order to initialize the system. self.actionstart = '' ## Command executed when ticket gets banned. self.actionban = '' self.actionreban = '' ## Command executed when ticket gets removed. self.actionunban = '' ## Command executed in order to check requirements. self.actioncheck = '' ## Command executed in order to restore sane environment in error case. self.actionrepair = '' ## Command executed in order to flush all bans at once (e. g. by stop/shutdown the system). self.actionflush = '' ## Command executed in order to stop the system. self.actionstop = '' ## Command executed in case of reloading action. self.actionreload = '' finally: self.__init = 0 def __init__(self, jail, name): super(CommandAction, self).__init__(jail, name) self.__init = 1 self.__properties = None self.__started = {} self.__substCache = {} self.clearAllParams() self._logSys.debug("Created %s" % self.__class__) @classmethod def __subclasshook__(cls, C): return NotImplemented # Standard checks def __setattr__(self, name, value): if not name.startswith('_') and not self.__init and not callable(value): # special case for some parameters: wrp = WRAP_CMD_PARAMS.get(name) if wrp == 'ignore': # ignore (filter) dynamic parameters return elif wrp == 'str2seconds': value = MyTime.str2seconds(value) # parameters changed - clear properties and substitution cache: self.__properties = None self.__substCache.clear() #self._logSys.debug("Set action %r %s = %r", self._name, name, value) self._logSys.debug(" Set %s = %r", name, value) # set: self.__dict__[name] = value __setitem__ = __setattr__ def __delattr__(self, name): if not name.startswith('_'): # parameters changed - clear properties and substitution cache: self.__properties = None self.__substCache.clear() #self._logSys.debug("Unset action %r %s", self._name, name) self._logSys.debug(" Unset %s", name) # del: del self.__dict__[name] @property def _properties(self): """A dictionary of the actions properties. This is used to substitute "tags" in the commands. """ # if we have a properties - return it: if self.__properties is not None: return self.__properties # otherwise retrieve: self.__properties = dict( (key, getattr(self, key)) for key in dir(self) if not key.startswith("_") and not callable(getattr(self, key)) ) return self.__properties @property def _substCache(self): return self.__substCache def _getOperation(self, tag, family): # replace operation tag (interpolate all values), be sure family is enclosed as conditional value # (as lambda in addrepl so only if not overwritten in action): cmd = self.replaceTag(tag, self._properties, conditional=('family='+family if family else ''), cache=self.__substCache) if not family or '<' not in cmd: return cmd # replace family as dynamic tags, important - don't cache, no recursion and auto-escape here: cmd = self.replaceDynamicTags(cmd, {'family':family}) return cmd def _operationExecuted(self, tag, family, *args): """ Get, set or delete command of operation considering family. """ key = ('__eOpCmd',tag) if not len(args): # get if not callable(family): # pragma: no cover return self.__substCache.get(key, {}).get(family) # family as expression - use it to filter values: return [v for f, v in self.__substCache.get(key, {}).items() if family(f)] cmd = args[0] if cmd: # set: try: famd = self.__substCache[key] except KeyError: famd = self.__substCache[key] = {} famd[family] = cmd else: # delete (given family and all other with same command): try: famd = self.__substCache[key] cmd = famd.pop(family) for family, v in list(famd.items()): if v == cmd: del famd[family] except KeyError: # pragma: no cover pass def _executeOperation(self, tag, operation, family=[], afterExec=None): """Executes the operation commands (like "actionstart", "actionstop", etc). Replace the tags in the action command with actions properties and executes the resulting command. """ # check valid tags in properties (raises ValueError if self recursion, etc.): res = True err = 'Script error' if not family: # all started: family = [famoper for (famoper,v) in self.__started.items() if v] for famoper in family: try: cmd = self._getOperation(tag, famoper) ret = True # avoid double execution of same command for both families: if cmd and cmd not in self._operationExecuted(tag, lambda f: f != famoper): realCmd = cmd if self._jail: # simulate action info with "empty" ticket: aInfo = getattr(self._jail.actions, 'actionInfo', None) if not aInfo: aInfo = self._jail.actions._getActionInfo(None) setattr(self._jail.actions, 'actionInfo', aInfo) aInfo['time'] = MyTime.time() aInfo['family'] = famoper # replace dynamical tags, important - don't cache, no recursion and auto-escape here realCmd = self.replaceDynamicTags(cmd, aInfo) ret = self.executeCmd(realCmd, self.timeout) res &= ret if afterExec: afterExec(famoper, ret) self._operationExecuted(tag, famoper, cmd if ret else None) except ValueError as e: res = False err = e if not res: raise RuntimeError("Error %s action %s/%s: %r" % (operation, self._jail, self._name, err)) return res @property def _hasCondSection(self): v = self._properties.get('__hasCondSection') if v is not None: return v v = False for n in self._properties: if CONDITIONAL_FAM_RE.match(n): v = True break self._properties['__hasCondSection'] = v return v @property def _families(self): v = self._properties.get('__families') if v: return v v = self._properties.get('families') if v and not isinstance(v, (list,set)): # pragma: no cover - still unused v = splitwords(v) elif self._hasCondSection: # all conditional families: # todo: check it is needed at all # common (resp. ipv4) + ipv6 if allowed: v = ['inet4', 'inet6'] if allowed_ipv6() else ['inet4'] else: # all action tags seems to be the same v = [''] self._properties['__families'] = v return v @property def _startOnDemand(self): """Checks the action depends on family (conditional)""" v = self._properties.get('actionstart_on_demand') if v is not None: return v # not set - auto-recognize (depending on conditional): v = self._hasCondSection self._properties['actionstart_on_demand'] = v return v def start(self): """Executes the "actionstart" command. Replace the tags in the action command with actions properties and executes the resulting command. """ return self._start() def _start(self, family=None, forceStart=False): """Executes the "actionstart" command. Replace the tags in the action command with actions properties and executes the resulting command. """ # check the action depends on family (conditional): if self._startOnDemand: if not forceStart: return True elif not forceStart and self.__started.get(family): # pragma: no cover - normally unreachable return True family = [family] if family is not None else self._families def _started(family, ret): if ret: self._operationExecuted('', family, None) self.__started[family] = 1 ret = self._executeOperation('', 'starting', family=family, afterExec=_started) return ret def ban(self, aInfo, cmd=''): """Executes the given command ("actionban" or "actionreban"). Replaces the tags in the action command with actions properties and ban information, and executes the resulting command. Parameters ---------- aInfo : dict Dictionary which includes information in relation to the ban. """ # if we should start the action on demand (conditional by family): family = aInfo.get('family', '') if self._startOnDemand: if not self.__started.get(family): self._start(family, forceStart=True) # ban: if not self._processCmd(cmd, aInfo): raise RuntimeError("Error banning %(ip)s" % aInfo) self.__started[family] = self.__started.get(family, 0) | 3; # started and contains items @property def _prolongable(self): return (hasattr(self, 'actionprolong') and self.actionprolong and not str(self.actionprolong).isspace()) def prolong(self, aInfo): """Executes the "actionprolong" command. Replaces the tags in the action command with actions properties and ban information, and executes the resulting command. Parameters ---------- aInfo : dict Dictionary which includes information in relation to the ban. """ if not self._processCmd('', aInfo): raise RuntimeError("Error prolonging %(ip)s" % aInfo) def unban(self, aInfo): """Executes the "actionunban" command. Replaces the tags in the action command with actions properties and ban information, and executes the resulting command. Parameters ---------- aInfo : dict Dictionary which includes information in relation to the ban. """ family = aInfo.get('family', '') if self.__started.get(family, 0) & 2: # contains items if not self._processCmd('', aInfo): raise RuntimeError("Error unbanning %(ip)s" % aInfo) def reban(self, aInfo): """Executes the "actionreban" command if available, otherwise simply repeat "actionban". Replaces the tags in the action command with actions properties and ban information, and executes the resulting command. Parameters ---------- aInfo : dict Dictionary which includes information in relation to the ban. """ # re-ban: return self.ban(aInfo, '' if self.actionreban else '') def flush(self): """Executes the "actionflush" command. Command executed in order to flush all bans at once (e. g. by stop/shutdown the system), instead of unbanning of each single ticket. Replaces the tags in the action command with actions properties and executes the resulting command. """ # collect started families, may be started on demand (conditional): family = [f for (f,v) in self.__started.items() if v & 3 == 3]; # started and contains items # if nothing contains items: if not family: return True # flush: def _afterFlush(family, ret): if ret and self.__started.get(family): self.__started[family] &= ~2; # no items anymore return self._executeOperation('', 'flushing', family=family, afterExec=_afterFlush) def stop(self): """Executes the "actionstop" command. Replaces the tags in the action command with actions properties and executes the resulting command. """ return self._stop() def _stop(self, family=None): """Executes the "actionstop" command. Replaces the tags in the action command with actions properties and executes the resulting command. """ # collect started families, if started on demand (conditional): if family is None: family = [f for (f,v) in self.__started.items() if v] # if no started (on demand) actions: if not family: return True self.__started = {} else: try: self.__started[family] &= 0 family = [family] except KeyError: # pragma: no cover return True def _stopped(family, ret): if ret: self._operationExecuted('', family, None) return self._executeOperation('', 'stopping', family=family, afterExec=_stopped) def reload(self, **kwargs): """Executes the "actionreload" command. Parameters ---------- kwargs : dict Currently unused, because CommandAction do not support initOpts Replaces the tags in the action command with actions properties and executes the resulting command. """ return self._executeOperation('', 'reloading') def consistencyCheck(self, beforeRepair=None): """Executes the invariant check with repair if expected (conditional). """ ret = True # for each started family: if self.actioncheck: for (family, started) in list(self.__started.items()): if started and not self._invariantCheck(family, beforeRepair): # reset started flag and command of executed operation: self.__started[family] = 0 self._operationExecuted('', family, None) ret &= False return ret ESCAPE_CRE = re.compile(r"""[\\#&;`|*?~<>^()\[\]{}$'"\n\r]""") @classmethod def escapeTag(cls, value): """Escape characters which may be used for command injection. Parameters ---------- value : str A string of which characters will be escaped. Returns ------- str `value` with certain characters escaped. Notes ----- The following characters are escaped:: \\#&;`|*?~<>^()[]{}$'"\n\r """ _map2c = {'\n': 'n', '\r': 'r'} def substChar(m): c = m.group() return '\\' + _map2c.get(c, c) value = cls.ESCAPE_CRE.sub(substChar, value) return value @classmethod def replaceTag(cls, query, aInfo, conditional='', addrepl=None, cache=None): """Replaces tags in `query` with property values. Parameters ---------- query : str String with tags. aInfo : dict Tags(keys) and associated values for substitution in query. Returns ------- str `query` string with tags replaced. """ if '<' not in query: return query # use cache if allowed: if cache is not None: ckey = (query, conditional) try: return cache[ckey] except KeyError: pass # **Important**: don't replace if calling map - contains dynamic values only, # no recursive tags, otherwise may be vulnerable on foreign user-input: noRecRepl = isinstance(aInfo, CallingMap) subInfo = aInfo if not noRecRepl: # substitute tags recursive (and cache if possible), # first try get cached tags dictionary: subInfo = csubkey = None if cache is not None: csubkey = ('subst-tags', id(aInfo), conditional) try: subInfo = cache[csubkey] except KeyError: pass # interpolation of dictionary: if subInfo is None: subInfo = substituteRecursiveTags(aInfo, conditional, ignore=cls._escapedTags, addrepl=addrepl) # cache if possible: if csubkey is not None: cache[csubkey] = subInfo # additional replacement as calling map: ADD_REPL_TAGS_CM = CallingMap(ADD_REPL_TAGS) # substitution callable, used by interpolation of each tag def substVal(m): tag = m.group(1) # tagname from match value = None if conditional: value = subInfo.get(tag + '?' + conditional) if value is None: value = subInfo.get(tag) if value is None: # fallback (no or default replacement) return ADD_REPL_TAGS_CM.get(tag, m.group()) value = uni_string(value) # assure string if tag in cls._escapedTags: # That one needs to be escaped since its content is # out of our control value = cls.escapeTag(value) # replacement for tag: return value # interpolation of query: count = MAX_TAG_REPLACE_COUNT + 1 while True: value = TAG_CRE.sub(substVal, query) # **Important**: no recursive replacement for tags from calling map (properties only): if noRecRepl: break # possible recursion ? if value == query or '<' not in value: break query = value count -= 1 if count <= 0: raise ValueError( "unexpected too long replacement interpolation, " "possible self referencing definitions in query: %s" % (query,)) # cache if possible: if cache is not None: cache[ckey] = value # return value ESCAPE_CRE = re.compile(r"""[\\#&;`|*?~<>\^\(\)\[\]{}$'"\n\r]""") ESCAPE_VN_CRE = re.compile(r"\W") @classmethod def replaceDynamicTags(cls, realCmd, aInfo, escapeVal=None): """Replaces dynamical tags in `query` with property values. **Important** ------------- Because this tags are dynamic resp. foreign (user) input: - values should be escaped (using "escape" as shell variable) - no recursive substitution (no interpolation for >) - don't use cache Parameters ---------- query : str String with tags. aInfo : dict Tags(keys) and associated values for substitution in query. Returns ------- str shell script as string or array with tags replaced (direct or as variables). """ # array for escaped vars: varsDict = dict() if not escapeVal: def escapeVal(tag, value): # if the value should be escaped: if cls.ESCAPE_CRE.search(value): # That one needs to be escaped since its content is # out of our control tag = 'f2bV_%s' % cls.ESCAPE_VN_CRE.sub('_', tag) varsDict[tag] = value # add variable value = '$'+tag # replacement as variable # replacement for tag: return value # additional replacement as calling map: ADD_REPL_TAGS_CM = CallingMap(ADD_REPL_TAGS) # substitution callable, used by interpolation of each tag def substVal(m): tag = m.group(1) # tagname from match try: value = aInfo[tag] except KeyError: # fallback (no or default replacement) return ADD_REPL_TAGS_CM.get(tag, m.group()) value = uni_string(value) # assure string # replacement for tag: return escapeVal(tag, value) # Replace normally properties of aInfo non-recursive: realCmd = TAG_CRE.sub(substVal, realCmd) # Replace ticket options (filter capture groups) non-recursive: if '<' in realCmd: tickData = aInfo.get("F-*") if not tickData: tickData = {} def substTag(m): tag = mapTag2Opt(m.group(1)) try: value = uni_string(tickData[tag]) except KeyError: return "" return escapeVal("F_"+tag, value) realCmd = FCUSTAG_CRE.sub(substTag, realCmd) # build command corresponding "escaped" variables: if varsDict: realCmd = Utils.buildShellCmd(realCmd, varsDict) return realCmd @property def banEpoch(self): return getattr(self, '_banEpoch', 0) def invalidateBanEpoch(self): """Increments ban epoch of jail and this action, so already banned tickets would cause a re-ban for all tickets with previous epoch.""" if self._jail is not None: self._banEpoch = self._jail.actions.banEpoch = self._jail.actions.banEpoch + 1 else: self._banEpoch = self.banEpoch + 1 def _invariantCheck(self, family=None, beforeRepair=None, forceStart=True): """Executes a substituted `actioncheck` command. """ # for started action/family only (avoid check not started inet4 if inet6 gets broken): if not forceStart and family is not None and family not in self.__started: return 1 checkCmd = self._getOperation('', family) if not checkCmd or self.executeCmd(checkCmd, self.timeout): return 1 # if don't need repair/restore - just return: if beforeRepair and not beforeRepair(): return -1 self._logSys.error( "Invariant check failed. Trying to restore a sane environment") # increment ban epoch of jail and this action (allows re-ban on already banned): self.invalidateBanEpoch() # try to find repair command, if exists - exec it: repairCmd = self._getOperation('', family) if repairCmd: if not self.executeCmd(repairCmd, self.timeout): self.__started[family] = 0 self._logSys.critical("Unable to restore environment") return 0 self.__started[family] = 1 else: # no repair command, try to restart action... # [WARNING] TODO: be sure all banactions get a repair command, because # otherwise stop/start will theoretically remove all the bans, # but the tickets are still in BanManager, so in case of new failures # it will not be banned, because "already banned" will happen. try: self._stop(family) except RuntimeError: # bypass error in stop (if start/check succeeded hereafter). pass self._start(family, forceStart=forceStart or not self._startOnDemand) if self.__started.get(family) and not self.executeCmd(checkCmd, self.timeout): self._logSys.critical("Unable to restore environment") return 0 return 1 def _processCmd(self, cmd, aInfo=None): """Executes a command with preliminary checks and substitutions. Before executing any commands, executes the "check" command first in order to check if pre-requirements are met. If this check fails, it tries to restore a sane environment before executing the real command. Parameters ---------- cmd : str The command to execute. aInfo : dictionary Dynamic properties. Returns ------- bool True if the command succeeded. """ if cmd == "": self._logSys.debug("Nothing to do") return True # conditional corresponding family of the given ip: try: family = aInfo["family"] except (KeyError, TypeError): family = '' repcnt = 0 while True: # got some error, do invariant check: if repcnt and self.actioncheck: # don't repair/restore if unban (no matter): def _beforeRepair(): if cmd == '' and not self._properties.get('actionrepair_on_unban'): self._logSys.error("Invariant check failed. Unban is impossible.") return False return True # check and repair if broken: ret = self._invariantCheck(family, _beforeRepair, forceStart=(cmd != '')) # if not sane (and not restored) return: if ret != 1: return False # Replace static fields realCmd = self.replaceTag(cmd, self._properties, conditional=('family='+family if family else ''), cache=self.__substCache) # Replace dynamical tags, important - don't cache, no recursion and auto-escape here if aInfo is not None: realCmd = self.replaceDynamicTags(realCmd, aInfo) else: realCmd = cmd # try execute command: ret = self.executeCmd(realCmd, self.timeout) repcnt += 1 if ret or repcnt > 1: return ret @staticmethod def executeCmd(realCmd, timeout=60, **kwargs): """Executes a command. Parameters ---------- realCmd : str The command to execute. timeout : int The time out in seconds for the command. Returns ------- bool True if the command succeeded. Raises ------ OSError If command fails to be executed. RuntimeError If command execution times out. """ if logSys.getEffectiveLevel() < logging.DEBUG: logSys.log(9, realCmd) if not realCmd: logSys.debug("Nothing to do") return True with _cmd_lock: return Utils.executeCmd(realCmd, timeout, shell=True, output=False, **kwargs) server/actions.py000064400000060475152231502150010074 0ustar00# 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. # Author: Cyril Jaquier # __author__ = "Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" import logging import os import sys import time try: from collections.abc import Mapping except ImportError: from collections import Mapping from collections import OrderedDict from .banmanager import BanManager, BanTicket from .ipdns import IPAddr from .jailthread import JailThread from .action import ActionBase, CommandAction, CallingMap from .mytime import MyTime from .observer import Observers from .utils import Utils from ..helpers import getLogger # Gets the instance of the logger. logSys = getLogger(__name__) class Actions(JailThread, Mapping): """Handles jail actions. This class handles the actions of the jail. Creation, deletion or to actions must be done through this class. This class is based on the Mapping type, and the `add` method must be used to add new actions. This class also starts and stops the actions, and fetches bans from the jail executing these bans via the actions. Parameters ---------- jail: Jail The jail of which the actions belongs to. Attributes ---------- daemon ident name status active : bool Control the state of the thread. idle : bool Control the idle state of the thread. sleeptime : int The time the thread sleeps for in the loop. """ def __init__(self, jail): JailThread.__init__(self, name="f2b/a."+jail.name) ## The jail which contains this action. self._jail = jail self._actions = OrderedDict() ## The ban manager. self.banManager = BanManager() self.banEpoch = 0 self.__lastConsistencyCheckTM = 0 ## Precedence of ban (over unban), so max number of tickets banned (to call an unban check): self.banPrecedence = 10 ## Max count of outdated tickets to unban per each __checkUnBan operation: self.unbanMaxCount = self.banPrecedence * 2 @staticmethod def _load_python_module(pythonModule): mod = Utils.load_python_module(pythonModule) if not hasattr(mod, "Action"): # pragma: no cover raise RuntimeError( "%s module does not have 'Action' class" % pythonModule) elif not issubclass(mod.Action, ActionBase): # pragma: no cover raise RuntimeError( "%s module %s does not implement required methods" % ( pythonModule, mod.Action.__name__)) return mod def add(self, name, pythonModule=None, initOpts=None, reload=False): """Adds a new action. Add a new action if not already present, defaulting to standard `CommandAction`, or specified Python module. Parameters ---------- name : str The name of the action. pythonModule : str, optional Path to Python file which must contain `Action` class. Default None, which means `CommandAction` is used. initOpts : dict, optional Options for Python Action, used as keyword arguments for initialisation. Default None. Raises ------ ValueError If action name already exists. RuntimeError If external Python module does not have `Action` class or does not implement necessary methods as per `ActionBase` abstract class. """ # Check is action name already exists if name in self._actions: if not reload: raise ValueError("Action %s already exists" % name) # don't create new action if reload supported: action = self._actions[name] if hasattr(action, 'reload'): # don't execute reload right now, reload after all parameters are actualized if hasattr(action, 'clearAllParams'): action.clearAllParams() self._reload_actions[name] = initOpts return ## Create new action: if pythonModule is None: action = CommandAction(self._jail, name) else: customActionModule = self._load_python_module(pythonModule) action = customActionModule.Action(self._jail, name, **initOpts) self._actions[name] = action def reload(self, begin=True): """ Begin or end of reloading resp. refreshing of all parameters """ if begin: self._reload_actions = dict() else: if hasattr(self, '_reload_actions'): # reload actions after all parameters set via stream: for name, initOpts in self._reload_actions.items(): if name in self._actions: self._actions[name].reload(**(initOpts if initOpts else {})) # remove obsolete actions (untouched by reload process): delacts = OrderedDict((name, action) for name, action in self._actions.items() if name not in self._reload_actions) if len(delacts): # unban all tickets using removed actions only: self.__flushBan(db=False, actions=delacts, stop=True) # stop and remove it: self.stopActions(actions=delacts) delattr(self, '_reload_actions') def __getitem__(self, name): try: return self._actions[name] except KeyError: raise KeyError("Invalid Action name: %s" % name) def __delitem__(self, name): try: del self._actions[name] except KeyError: raise KeyError("Invalid Action name: %s" % name) def __iter__(self): return iter(self._actions) def __len__(self): return len(self._actions) def __eq__(self, other): # Required for Threading return False def __hash__(self): # Required for Threading return id(self) ## # Set the ban time. # # @param value the time def setBanTime(self, value): value = MyTime.str2seconds(value) self.banManager.setBanTime(value) logSys.info(" banTime: %s" % value) ## # Get the ban time. # # @return the time def getBanTime(self): return self.banManager.getBanTime() def getBanned(self, ids): lst = self.banManager.getBanList() if not ids: return lst if len(ids) == 1: return 1 if ids[0] in lst else 0 return [1 if ip in lst else 0 for ip in ids] def getBanList(self, withTime=False): """Returns the list of banned IP addresses. Returns ------- list The list of banned IP addresses. """ return self.banManager.getBanList(ordered=True, withTime=withTime) def addBannedIP(self, ip): """Ban an IP or list of IPs.""" unixTime = MyTime.time() if isinstance(ip, list): # Multiple IPs: tickets = (BanTicket(ip, unixTime) for ip in ip) else: # Single IP: tickets = (BanTicket(ip, unixTime),) return self.__checkBan(tickets) def removeBannedIP(self, ip=None, db=True, ifexists=False): """Removes banned IP calling actions' unban method Remove a banned IP now, rather than waiting for it to expire, even if set to never expire. Parameters ---------- ip : list, str, IPAddr or None The IP address (or multiple IPs as list) to unban or all IPs if None Raises ------ ValueError If `ip` is not banned """ # Unban all? if ip is None: return self.__flushBan(db) # Multiple IPs: if isinstance(ip, (list, tuple)): missed = [] cnt = 0 for i in ip: try: cnt += self.removeBannedIP(i, db, ifexists) except ValueError: if not ifexists: missed.append(i) if missed: raise ValueError("not banned: %r" % missed) return cnt # Single IP: # Always delete ip from database (also if currently not banned) if db and self._jail.database is not None: self._jail.database.delBan(self._jail, ip) # Find the ticket with the IP. ticket = self.banManager.getTicketByID(ip) if ticket is not None: # Unban the IP. self.__unBan(ticket) else: # Multiple IPs by subnet or dns: if not isinstance(ip, IPAddr): ipa = IPAddr(ip) if not ipa.isSingle: # subnet (mask/cidr) or raw (may be dns/hostname): ips = list(filter(ipa.contains, self.banManager.getBanList())) if ips: return self.removeBannedIP(ips, db, ifexists) # not found: msg = "%s is not banned" % ip logSys.log(logging.MSG, msg) if ifexists: return 0 raise ValueError(msg) return 1 def stopActions(self, actions=None): """Stops the actions in reverse sequence (optionally filtered) """ if actions is None: actions = self._actions for name, action in reversed(list(actions.items())): try: action.stop() except Exception as e: logSys.error("Failed to stop jail '%s' action '%s': %s", self._jail.name, name, e, exc_info=logSys.getEffectiveLevel()<=logging.DEBUG) del self._actions[name] logSys.debug("%s: action %s terminated", self._jail.name, name) def run(self): """Main loop for Threading. This function is the main loop of the thread. It checks the jail queue and executes commands when an IP address is banned. Returns ------- bool True when the thread exits nicely. """ cnt = 0 for name, action in self._actions.items(): try: action.start() except Exception as e: logSys.error("Failed to start jail '%s' action '%s': %s", self._jail.name, name, e, exc_info=logSys.getEffectiveLevel()<=logging.DEBUG) while self.active: try: if self.idle: logSys.debug("Actions: enter idle mode") Utils.wait_for(lambda: not self.active or not self.idle, lambda: False, self.sleeptime) logSys.debug("Actions: leave idle mode") continue # wait for ban (stop if gets inactive, pending ban or unban): bancnt = 0 wt = min(self.sleeptime, self.banManager._nextUnbanTime - MyTime.time()) logSys.log(5, "Actions: wait for pending tickets %s (default %s)", wt, self.sleeptime) if Utils.wait_for(lambda: not self.active or self._jail.hasFailTickets, wt): bancnt = self.__checkBan() cnt += bancnt # unban if nothing is banned not later than banned tickets >= banPrecedence if not bancnt or cnt >= self.banPrecedence: if self.active: # let shrink the ban list faster bancnt *= 2 logSys.log(5, "Actions: check-unban %s, bancnt %s, max: %s", bancnt if bancnt and bancnt < self.unbanMaxCount else self.unbanMaxCount, bancnt, self.unbanMaxCount) self.__checkUnBan(bancnt if bancnt and bancnt < self.unbanMaxCount else self.unbanMaxCount) cnt = 0 except Exception as e: # pragma: no cover logSys.error("[%s] unhandled error in actions thread: %s", self._jail.name, e, exc_info=logSys.getEffectiveLevel()<=logging.DEBUG) self.__flushBan(stop=True) self.stopActions() return True class ActionInfo(CallingMap): CM_REPR_ITEMS = ("fid", "raw-ticket") AI_DICT = { "ip": lambda self: self.__ticket.getIP(), "family": lambda self: self['ip'].familyStr, "ip-rev": lambda self: self['ip'].getPTR(''), "ip-host": lambda self: self['ip'].getHost(), "fid": lambda self: self.__ticket.getID(), "failures": lambda self: self.__ticket.getAttempt(), "time": lambda self: self.__ticket.getTime(), "bantime": lambda self: self._getBanTime(), "bancount": lambda self: self.__ticket.getBanCount(), "matches": lambda self: "\n".join(self.__ticket.getMatches()), # to bypass actions, that should not be executed for restored tickets "restored": lambda self: (1 if self.__ticket.restored else 0), # extra-interpolation - all match-tags (captured from the filter): "F-*": lambda self, tag=None: self.__ticket.getData(tag), # merged info: "ipmatches": lambda self: "\n".join(self._mi4ip(True).getMatches()), "ipjailmatches": lambda self: "\n".join(self._mi4ip().getMatches()), "ipfailures": lambda self: self._mi4ip(True).getAttempt(), "ipjailfailures": lambda self: self._mi4ip().getAttempt(), # raw ticket info: "raw-ticket": lambda self: repr(self.__ticket), # jail info: "jail.banned": lambda self: self.__jail.actions.banManager.size(), "jail.banned_total": lambda self: self.__jail.actions.banManager.getBanTotal(), "jail.found": lambda self: self.__jail.filter.failManager.size(), "jail.found_total": lambda self: self.__jail.filter.failManager.getFailTotal() } __slots__ = CallingMap.__slots__ + ('__ticket', '__jail', '__mi4ip') def __init__(self, ticket, jail=None, immutable=True, data=AI_DICT): self.__ticket = ticket self.__jail = jail self.storage = dict() self.immutable = immutable self.data = data def copy(self): # pragma: no cover return self.__class__(self.__ticket, self.__jail, self.immutable, self.data.copy()) def _getBanTime(self): btime = self.__ticket.getBanTime() if btime is None: btime = self.__jail.actions.getBanTime() return int(btime) def _mi4ip(self, overalljails=False): """Gets bans merged once, a helper for lambda(s), prevents stop of executing action by any exception inside. This function never returns None for ainfo lambdas - always a ticket (merged or single one) and prevents any errors through merging (to guarantee ban actions will be executed). [TODO] move merging to observer - here we could wait for merge and read already merged info from a database Parameters ---------- overalljails : bool switch to get a merged bans : False - (default) bans merged for current jail only True - bans merged for all jails of current ip address Returns ------- BanTicket merged or self ticket only """ if not hasattr(self, '__mi4ip'): self.__mi4ip = {} mi = self.__mi4ip idx = 'all' if overalljails else 'jail' if idx in mi: return mi[idx] if mi[idx] is not None else self.__ticket try: jail = self.__jail ip = self['ip'] mi[idx] = None if not jail.database: # pragma: no cover return self.__ticket if overalljails: mi[idx] = jail.database.getBansMerged(ip=ip) else: mi[idx] = jail.database.getBansMerged(ip=ip, jail=jail) except Exception as e: logSys.error( "Failed to get %s bans merged, jail '%s': %s", idx, jail.name, e, exc_info=logSys.getEffectiveLevel()<=logging.DEBUG) return mi[idx] if mi[idx] is not None else self.__ticket def _getActionInfo(self, ticket): if not ticket: ticket = BanTicket("", MyTime.time()) aInfo = Actions.ActionInfo(ticket, self._jail) return aInfo def __getFailTickets(self, count=100): """Generator to get maximal count failure tickets from fail-manager.""" cnt = 0 while cnt < count: ticket = self._jail.getFailTicket() if not ticket: break yield ticket cnt += 1 def __checkBan(self, tickets=None): """Check for IP address to ban. If tickets are not specified look in the jail queue for FailTicket. If a ticket is available, it executes the "ban" command and adds a ticket to the BanManager. Returns ------- bool True if an IP address get banned. """ cnt = 0 if not tickets: tickets = self.__getFailTickets(self.banPrecedence) rebanacts = None for ticket in tickets: bTicket = BanTicket.wrap(ticket) btime = ticket.getBanTime(self.banManager.getBanTime()) ip = bTicket.getID() aInfo = self._getActionInfo(bTicket) reason = {} if self.banManager.addBanTicket(bTicket, reason=reason): cnt += 1 # report ticket to observer, to check time should be increased and hereafter observer writes ban to database (asynchronous) if Observers.Main is not None and not bTicket.restored: Observers.Main.add('banFound', bTicket, self._jail, btime) logSys.notice("[%s] %sBan %s", self._jail.name, ('' if not bTicket.restored else 'Restore '), ip) # do actions : for name, action in self._actions.items(): try: if bTicket.restored and getattr(action, 'norestored', False): continue if not aInfo.immutable: aInfo.reset() action.ban(aInfo) except Exception as e: logSys.error( "Failed to execute ban jail '%s' action '%s' " "info '%r': %s", self._jail.name, name, aInfo, e, exc_info=logSys.getEffectiveLevel()<=logging.DEBUG) # after all actions are processed set banned flag: bTicket.banned = True if self.banEpoch: # be sure tickets always have the same ban epoch (default 0): bTicket.banEpoch = self.banEpoch else: if reason.get('expired', 0): logSys.info('[%s] Ignore %s, expired bantime', self._jail.name, ip) continue bTicket = reason.get('ticket', bTicket) # if already banned (otherwise still process some action) if bTicket.banned: # compare time of failure occurrence with time ticket was really banned: diftm = ticket.getTime() - bTicket.getTime() # log already banned with following level: # DEBUG - before 3 seconds - certain interval for it, because of possible latency by recognizing in backends, etc. # NOTICE - before 60 seconds - may still occur if action is slow, or very high load in backend, # WARNING - after 60 seconds - very long time, something may be wrong ll = logging.DEBUG if diftm < 3 \ else logging.NOTICE if diftm < 60 \ else logging.WARNING logSys.log(ll, "[%s] %s already banned", self._jail.name, ip) # if long time after ban - do consistency check (something is wrong here): if bTicket.banEpoch == self.banEpoch and diftm > 3: # avoid too often checks: if not rebanacts and MyTime.time() > self.__lastConsistencyCheckTM + 3: self.__lastConsistencyCheckTM = MyTime.time() for action in self._actions.values(): if hasattr(action, 'consistencyCheck'): action.consistencyCheck() # check epoch in order to reban it: if bTicket.banEpoch < self.banEpoch: if not rebanacts: rebanacts = dict( (name, action) for name, action in self._actions.items() if action.banEpoch > bTicket.banEpoch) cnt += self.__reBan(bTicket, actions=rebanacts) else: # pragma: no cover - unexpected: ticket is not banned for some reasons - reban using all actions: cnt += self.__reBan(bTicket) # add ban to database moved to observer (should previously check not already banned # and increase ticket time if "bantime.increment" set) if cnt: logSys.debug("Banned %s / %s, %s ticket(s) in %r", cnt, self.banManager.getBanTotal(), self.banManager.size(), self._jail.name) return cnt def __reBan(self, ticket, actions=None, log=True): """Repeat bans for the ticket. Executes the actions in order to reban the host given in the ticket. Parameters ---------- ticket : Ticket Ticket to reban """ actions = actions or self._actions ip = ticket.getID() aInfo = self._getActionInfo(ticket) if log: logSys.notice("[%s] Reban %s%s", self._jail.name, ip, (', action %r' % list(actions.keys())[0] if len(actions) == 1 else '')) for name, action in actions.items(): try: logSys.debug("[%s] action %r: reban %s", self._jail.name, name, ip) if not aInfo.immutable: aInfo.reset() action.reban(aInfo) except Exception as e: logSys.error( "Failed to execute reban jail '%s' action '%s' " "info '%r': %s", self._jail.name, name, aInfo, e, exc_info=logSys.getEffectiveLevel()<=logging.DEBUG) return 0 # after all actions are processed set banned flag: ticket.banned = True if self.banEpoch: # be sure tickets always have the same ban epoch (default 0): ticket.banEpoch = self.banEpoch return 1 def _prolongBan(self, ticket): # prevent to prolong ticket that was removed in-between, # if it in ban list - ban time already prolonged (and it stays there): if not self.banManager._inBanList(ticket): return # do actions : aInfo = None for name, action in self._actions.items(): try: if ticket.restored and getattr(action, 'norestored', False): continue if not action._prolongable: continue if aInfo is None: aInfo = self._getActionInfo(ticket) if not aInfo.immutable: aInfo.reset() action.prolong(aInfo) except Exception as e: logSys.error( "Failed to execute ban jail '%s' action '%s' " "info '%r': %s", self._jail.name, name, aInfo, e, exc_info=logSys.getEffectiveLevel()<=logging.DEBUG) def __checkUnBan(self, maxCount=None): """Check for IP address to unban. Unban IP addresses which are outdated. """ lst = self.banManager.unBanList(MyTime.time(), maxCount) for ticket in lst: self.__unBan(ticket) cnt = len(lst) if cnt: logSys.debug("Unbanned %s, %s ticket(s) in %r", cnt, self.banManager.size(), self._jail.name) return cnt def __flushBan(self, db=False, actions=None, stop=False): """Flush the ban list. Unban all IP address which are still in the banning list. If actions specified, don't flush list - just execute unban for given actions (reload, obsolete resp. removed actions). """ log = True if actions is None: logSys.debug(" Flush ban list") lst = self.banManager.flushBanList() else: log = False # don't log "[jail] Unban ..." if removing actions only. lst = iter(self.banManager) cnt = 0 # first we'll execute flush for actions supporting this operation: unbactions = {} for name, action in (actions if actions is not None else self._actions).items(): try: if hasattr(action, 'flush') and (not isinstance(action, CommandAction) or action.actionflush): logSys.notice("[%s] Flush ticket(s) with %s", self._jail.name, name) if action.flush(): continue except Exception as e: logSys.error("Failed to flush bans in jail '%s' action '%s': %s", self._jail.name, name, e, exc_info=logSys.getEffectiveLevel()<=logging.DEBUG) logSys.info("No flush occurred, do consistency check") if hasattr(action, 'consistencyCheck'): def _beforeRepair(): if stop and not getattr(action, 'actionrepair_on_unban', None): # don't need repair on stop logSys.error("Invariant check failed. Flush is impossible.") return False return True action.consistencyCheck(_beforeRepair) continue # fallback to single unbans: logSys.debug(" Unban tickets each individualy") unbactions[name] = action actions = unbactions # flush the database also: if db and self._jail.database is not None: logSys.debug(" Flush jail in database") self._jail.database.delBan(self._jail) # unban each ticket with non-flusheable actions: for ticket in lst: # unban ip: self.__unBan(ticket, actions=actions, log=log) cnt += 1 logSys.debug(" Unbanned %s, %s ticket(s) in %r", cnt, self.banManager.size(), self._jail.name) return cnt def __unBan(self, ticket, actions=None, log=True): """Unbans host corresponding to the ticket. Executes the actions in order to unban the host given in the ticket. Parameters ---------- ticket : FailTicket Ticket of failures of which to unban """ if actions is None: unbactions = self._actions else: unbactions = actions ip = ticket.getID() aInfo = self._getActionInfo(ticket) if log: logSys.notice("[%s] Unban %s", self._jail.name, ip) for name, action in unbactions.items(): try: logSys.debug("[%s] action %r: unban %s", self._jail.name, name, ip) if not aInfo.immutable: aInfo.reset() action.unban(aInfo) except Exception as e: logSys.error( "Failed to execute unban jail '%s' action '%s' " "info '%r': %s", self._jail.name, name, aInfo, e, exc_info=logSys.getEffectiveLevel()<=logging.DEBUG) def status(self, flavor="basic"): """Status of current and total ban counts and current banned IP list. """ # TODO: Allow this list to be printed as 'status' output supported_flavors = ["short", "basic", "cymru"] if flavor is None or flavor not in supported_flavors: logSys.warning("Unsupported extended jail status flavor %r. Supported: %s" % (flavor, supported_flavors)) # Always print this information (basic) if flavor != "short": banned = self.banManager.getBanList() cnt = len(banned) else: cnt = self.banManager.size() ret = [("Currently banned", cnt), ("Total banned", self.banManager.getBanTotal())] if flavor != "short": ret += [("Banned IP list", banned)] if flavor == "cymru": cymru_info = self.banManager.getBanListExtendedCymruInfo() ret += \ [("Banned ASN list", self.banManager.geBanListExtendedASN(cymru_info)), ("Banned Country list", self.banManager.geBanListExtendedCountry(cymru_info)), ("Banned RIR list", self.banManager.geBanListExtendedRIR(cymru_info))] return ret server/asyncserver.py000064400000023745152231502150010777 0ustar00# 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. # Author: Cyril Jaquier # __author__ = "Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" from pickle import dumps, loads, HIGHEST_PROTOCOL import asynchat import asyncore import errno import fcntl import os import socket import sys import threading import traceback from .utils import Utils from ..protocol import CSPROTO from ..helpers import logging, getLogger, formatExceptionInfo # Gets the instance of the logger. logSys = getLogger(__name__) ## # Request handler class. # # This class extends asynchat in order to provide a request handler for # incoming query. class RequestHandler(asynchat.async_chat): def __init__(self, conn, transmitter): asynchat.async_chat.__init__(self, conn) self.__conn = conn self.__transmitter = transmitter self.__buffer = [] # Sets the terminator. self.set_terminator(CSPROTO.END) def __close(self): if self.__conn: conn = self.__conn self.__conn = None try: conn.shutdown(socket.SHUT_RDWR) conn.close() except socket.error: # pragma: no cover - normally unreachable pass def handle_close(self): self.__close() asynchat.async_chat.handle_close(self) def collect_incoming_data(self, data): #logSys.debug("Received raw data: " + str(data)) self.__buffer.append(data) # exception identifies deserialization errors (exception by load in pickle): class LoadError(Exception): pass ## # Handles a new request. # # This method is called once we have a complete request. def found_terminator(self): try: # Pop whole buffer message = self.__buffer self.__buffer = [] # Joins the buffer items. message = CSPROTO.EMPTY.join(message) # Closes the channel if close was received if message == CSPROTO.CLOSE: self.close_when_done() return # Deserialize try: message = loads(message) except Exception as e: logSys.error('PROTO-error: load message failed: %s', e, exc_info=logSys.getEffectiveLevel() 100: # pragma: no cover - normally unreachable if ( e.args[0] == errno.EMFILE # [Errno 24] Too many open files or sum(err_count.values()) > 1000 ): logSys.critical("Too many errors - critical count reached %r", err_count) break ## # Asynchronous server class. # # This class extends asyncore and dispatches connection requests to # RequestHandler. class AsyncServer(asyncore.dispatcher): def __init__(self, transmitter): asyncore.dispatcher.__init__(self) self.__transmitter = transmitter self.__sock = "/var/run/fail2ban/fail2ban.sock" self.__init = False self.__active = False self.__errCount = {'accept': 0, 'listen': 0} self.onstart = None ## # Returns False as we only read the socket first. def writable(self): return False def handle_accept(self): try: conn, addr = self.accept() except Exception as e: # pragma: no cover self.__errCount['accept'] += 1 if self.__errCount['accept'] < 20: logSys.warning("Accept socket error: %s", e, exc_info=(self.__errCount['accept'] <= 1)) elif self.__errCount['accept'] == 20: logSys.error("Too many acceptor errors - stop logging errors") elif self.__errCount['accept'] > 100: if ( (isinstance(e, socket.error) and e.args[0] == errno.EMFILE) # [Errno 24] Too many open files or sum(self.__errCount.values()) > 1000 ): logSys.critical("Too many errors - critical count reached %r", self.__errCount) self.stop() return if self.__errCount['accept']: self.__errCount['accept'] -= 1; AsyncServer.__markCloseOnExec(conn) # Creates an instance of the handler class to handle the # request/response on the incoming connection. RequestHandler(conn, self.__transmitter) ## # Starts the communication server. # # @param sock: socket file. # @param force: remove the socket file if exists. def start(self, sock, force, timeout=None, use_poll=False): self.__worker = threading.current_thread() self.__sock = sock # Remove socket if os.path.exists(sock): logSys.error("Fail2ban seems to be already running") if force: logSys.warning("Forcing execution of the server") self._remove_sock() else: raise AsyncServerException("Server already running") # Creates the socket. self.create_socket(socket.AF_UNIX, socket.SOCK_STREAM) self.set_reuse_addr() try: self.bind(sock) except Exception: # pragma: no cover raise AsyncServerException("Unable to bind socket %s" % self.__sock) AsyncServer.__markCloseOnExec(self.socket) self.listen(1) # Sets the init flag. self.__init = self.__loop = self.__active = True # Execute on start event (server ready): if self.onstart: self.onstart() # Event loop as long as active: loop(lambda: self.__loop, timeout=timeout, use_poll=use_poll, err_count=self.__errCount) self.__active = False # Cleanup all self.stop() def close(self): stopflg = False if self.__active: self.__loop = False # shutdown socket here: if self.socket: try: self.socket.shutdown(socket.SHUT_RDWR) except socket.error: # pragma: no cover - normally unreachable pass # close connection: asyncore.dispatcher.close(self) # If not the loop thread (stops self in handler), wait (a little bit) # for the server leaves loop, before remove socket if threading.current_thread() != self.__worker: Utils.wait_for(lambda: not self.__active, 1) stopflg = True # Remove socket (file) only if it was created: if self.__init and os.path.exists(self.__sock): self._remove_sock() logSys.debug("Removed socket file " + self.__sock) if stopflg: logSys.debug("Socket shutdown") self.__active = False ## # Stops the communication server. def stop_communication(self): if self.__transmitter: logSys.debug("Stop communication, shutdown") self.__transmitter = None ## # Stops the server. def stop(self): self.stop_communication() self.close() # better remains a method (not a property) since used as a callable for wait_for def isActive(self): return self.__active ## # Safe remove (in multithreaded mode): def _remove_sock(self): try: os.remove(self.__sock) except OSError as e: # pragma: no cover if e.errno != errno.ENOENT: raise ## # Marks socket as close-on-exec to avoid leaking file descriptors when # running actions involving command execution. # @param sock: socket file. @staticmethod def __markCloseOnExec(sock): fd = sock.fileno() flags = fcntl.fcntl(fd, fcntl.F_GETFD) fcntl.fcntl(fd, fcntl.F_SETFD, flags|fcntl.FD_CLOEXEC) ## # AsyncServerException is used to wrap communication exceptions. class AsyncServerException(Exception): pass server/banmanager.py000064400000025237152231502150010524 0ustar00# 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. # Author: Cyril Jaquier # __author__ = "Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" from threading import Lock from .ticket import BanTicket from .mytime import MyTime from ..helpers import getLogger, logging # Gets the instance of the logger. logSys = getLogger(__name__) ## # Banning Manager. # # Manage the banned IP addresses. Convert FailTicket to BanTicket. # This class is mainly used by the Action class. class BanManager: ## # Constructor. # # Initialize members with default values. def __init__(self): ## Mutex used to protect the ban list. self.__lock = Lock() ## The ban list. self.__banList = dict() ## The amount of time an IP address gets banned. self.__banTime = 600 ## Total number of banned IP address self.__banTotal = 0 ## The time for next unban process (for performance and load reasons): self._nextUnbanTime = BanTicket.MAX_TIME ## # Set the ban time. # # Set the amount of time an IP address get banned. # @param value the time def setBanTime(self, value): self.__banTime = int(value) ## # Get the ban time. # # Get the amount of time an IP address get banned. # @return the time def getBanTime(self): return self.__banTime ## # Set the total number of banned address. # # @param value total number def setBanTotal(self, value): self.__banTotal = value ## # Get the total number of banned address. # # @return the total number def getBanTotal(self): return self.__banTotal ## # Returns a copy of the IP list. # # @return IP list def getBanList(self, ordered=False, withTime=False): if not ordered: return list(self.__banList.keys()) with self.__lock: lst = [] for ticket in self.__banList.values(): eob = ticket.getEndOfBanTime(self.__banTime) lst.append((ticket,eob)) lst.sort(key=lambda t: t[1]) t2s = MyTime.time2str if withTime: return ['%s \t%s + %d = %s' % ( t[0].getID(), t2s(t[0].getTime()), t[0].getBanTime(self.__banTime), t2s(t[1]) ) for t in lst] return [t[0].getID() for t in lst] ## # Returns a iterator to ban list (used in reload, so idle). # # @return ban list iterator def __iter__(self): # ensure iterator is safe - traverse over the list in snapshot created within lock (GIL): return iter(list(self.__banList.values())) ## # Returns normalized value # # @return value or "unknown" if value is None or empty string @staticmethod def handleBlankResult(value): if value is None or len(value) == 0: return "unknown" else: return value ## # Returns Cymru DNS query information # # @return {"asn": [], "country": [], "rir": []} dict for self.__banList IPs def getBanListExtendedCymruInfo(self, timeout=10): return_dict = {"asn": [], "country": [], "rir": []} if not hasattr(self, 'dnsResolver'): global dns try: import dns.exception import dns.resolver resolver = dns.resolver.Resolver() resolver.lifetime = timeout resolver.timeout = timeout / 2 self.dnsResolver = resolver except ImportError as e: # pragma: no cover logSys.error("dnspython package is required but could not be imported") return_dict["error"] = repr(e) return_dict["asn"].append("error") return_dict["country"].append("error") return_dict["rir"].append("error") return return_dict # get ips in lock: with self.__lock: banIPs = [banData.getIP() for banData in list(self.__banList.values())] # get cymru info: try: for ip in banIPs: # Reference: https://www.team-cymru.com/IP-ASN-mapping.html#dns question = ip.getPTR( "origin.asn.cymru.com" if ip.isIPv4 else "origin6.asn.cymru.com" ) try: resolver = self.dnsResolver answers = resolver.query(question, "TXT") if not answers: raise ValueError("No data retrieved") asns = set() countries = set() rirs = set() for rdata in answers: asn, net, country, rir, changed =\ [answer.strip("'\" ") for answer in rdata.to_text().split("|")] asn = self.handleBlankResult(asn) country = self.handleBlankResult(country) rir = self.handleBlankResult(rir) asns.add(self.handleBlankResult(asn)) countries.add(self.handleBlankResult(country)) rirs.add(self.handleBlankResult(rir)) return_dict["asn"].append(', '.join(sorted(asns))) return_dict["country"].append(', '.join(sorted(countries))) return_dict["rir"].append(', '.join(sorted(rirs))) except dns.resolver.NXDOMAIN: return_dict["asn"].append("nxdomain") return_dict["country"].append("nxdomain") return_dict["rir"].append("nxdomain") except (dns.exception.DNSException, dns.resolver.NoNameservers, dns.exception.Timeout) as dnse: # pragma: no cover logSys.error("DNSException %r querying Cymru for %s TXT", dnse, question) if logSys.level <= logging.DEBUG: logSys.exception(dnse) return_dict["error"] = repr(dnse) break except Exception as e: # pragma: no cover logSys.error("Unhandled Exception %r querying Cymru for %s TXT", e, question) if logSys.level <= logging.DEBUG: logSys.exception(e) return_dict["error"] = repr(e) break except Exception as e: # pragma: no cover logSys.error("Failure looking up extended Cymru info: %s", e) if logSys.level <= logging.DEBUG: logSys.exception(e) return_dict["error"] = repr(e) return return_dict ## # Returns list of Banned ASNs from Cymru info # # Use getBanListExtendedCymruInfo() to provide cymru_info # # @return list of Banned ASNs def geBanListExtendedASN(self, cymru_info): try: return [asn for asn in cymru_info["asn"]] except Exception as e: logSys.error("Failed to lookup ASN") logSys.exception(e) return [] ## # Returns list of Banned Countries from Cymru info # # Use getBanListExtendedCymruInfo() to provide cymru_info # # @return list of Banned Countries def geBanListExtendedCountry(self, cymru_info): try: return [country for country in cymru_info["country"]] except Exception as e: logSys.error("Failed to lookup Country") logSys.exception(e) return [] ## # Returns list of Banned RIRs from Cymru info # # Use getBanListExtendedCymruInfo() to provide cymru_info # # @return list of Banned RIRs def geBanListExtendedRIR(self, cymru_info): try: return [rir for rir in cymru_info["rir"]] except Exception as e: logSys.error("Failed to lookup RIR") logSys.exception(e) return [] ## # Add a ban ticket. # # Add a BanTicket instance into the ban list. # @param ticket the ticket # @return True if the IP address is not in the ban list def addBanTicket(self, ticket, reason={}): eob = ticket.getEndOfBanTime(self.__banTime) if eob < MyTime.time(): reason['expired'] = 1 return False with self.__lock: # check already banned fid = ticket.getID() oldticket = self.__banList.get(fid) if oldticket: reason['ticket'] = oldticket # if new time for end of ban is larger than already banned end-time: if eob > oldticket.getEndOfBanTime(self.__banTime): # we have longest ban - set new (increment) ban time reason['prolong'] = 1 btm = ticket.getBanTime(self.__banTime) # if not permanent: if btm != -1: diftm = ticket.getTime() - oldticket.getTime() if diftm > 0: btm += diftm oldticket.setBanTime(btm) return False # not yet banned - add new one: self.__banList[fid] = ticket self.__banTotal += 1 ticket.incrBanCount() # correct next unban time: if self._nextUnbanTime > eob: self._nextUnbanTime = eob return True ## # Get the size of the ban list. # # @return the size def size(self): return len(self.__banList) ## # Check if a ticket is in the list. # # Check if a BanTicket with a given IP address is already in the # ban list. # @param ticket the ticket # @return True if a ticket already exists def _inBanList(self, ticket): return ticket.getID() in self.__banList ## # Get the list of IP address to unban. # # Return a list of BanTicket which need to be unbanned. # @param time the time # @return the list of ticket to unban def unBanList(self, time, maxCount=0x7fffffff): with self.__lock: # Check next unban time: nextUnbanTime = self._nextUnbanTime if nextUnbanTime > time: return list() # Gets the list of ticket to remove (thereby correct next unban time). unBanList = {} nextUnbanTime = BanTicket.MAX_TIME for fid,ticket in self.__banList.items(): # current time greater as end of ban - timed out: eob = ticket.getEndOfBanTime(self.__banTime) if time > eob: unBanList[fid] = ticket if len(unBanList) >= maxCount: # stop search cycle, so reset back the next check time nextUnbanTime = self._nextUnbanTime break elif nextUnbanTime > eob: nextUnbanTime = eob self._nextUnbanTime = nextUnbanTime # Removes tickets. if len(unBanList): if len(unBanList) / 2.0 <= len(self.__banList) / 3.0: # few as 2/3 should be removed - remove particular items: for fid in unBanList.keys(): del self.__banList[fid] else: # create new dictionary without items to be deleted: self.__banList = dict((fid,ticket) for fid,ticket in self.__banList.items() \ if fid not in unBanList) # return list of tickets: return list(unBanList.values()) ## # Flush the ban list. # # Get the ban list and initialize it with an empty one. # @return the complete ban list def flushBanList(self): with self.__lock: uBList = list(self.__banList.values()) self.__banList = dict() return uBList ## # Gets the ticket for the specified ID (most of the time it is IP-address). # # @return the ticket or False. def getTicketByID(self, fid): with self.__lock: try: # Return the ticket after removing (popping) # if from the ban list. return self.__banList.pop(fid) except KeyError: pass return None # if none found server/database.py000064400000072010152231502150010164 0ustar00# 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. __author__ = "Steven Hiscocks" __copyright__ = "Copyright (c) 2013 Steven Hiscocks" __license__ = "GPL" import json import os import shutil import sqlite3 import sys import time from functools import wraps from threading import RLock from .mytime import MyTime from .ticket import FailTicket from .utils import Utils from ..helpers import getLogger, uni_string, PREFER_ENC # Gets the instance of the logger. logSys = getLogger(__name__) def _json_default(x): """Avoid errors on types unknown in json-adapters.""" if isinstance(x, set): x = list(x) return uni_string(x) if sys.version_info >= (3,): # pragma: 2.x no cover def _json_dumps_safe(x): try: x = json.dumps(x, ensure_ascii=False, default=_json_default).encode( PREFER_ENC, 'replace') except Exception as e: # adapter handler should be exception-safe logSys.error('json dumps failed: %r', e, exc_info=logSys.getEffectiveLevel() <= 4) x = '{}' return x def _json_loads_safe(x): try: x = json.loads(x.decode(PREFER_ENC, 'replace')) except Exception as e: # converter handler should be exception-safe logSys.error('json loads failed: %r', e, exc_info=logSys.getEffectiveLevel() <= 4) x = {} return x else: # pragma: 3.x no cover def _normalize(x): if isinstance(x, dict): return dict((_normalize(k), _normalize(v)) for k, v in x.items()) elif isinstance(x, (list, set)): return [_normalize(element) for element in x] elif isinstance(x, str): # in 2.x default text_factory is unicode - so return proper unicode here: return x.encode(PREFER_ENC, 'replace').decode(PREFER_ENC) elif isinstance(x, str): return x.decode(PREFER_ENC, 'replace') return x def _json_dumps_safe(x): try: x = json.dumps(_normalize(x), ensure_ascii=False, default=_json_default) except Exception as e: # adapter handler should be exception-safe logSys.error('json dumps failed: %r', e, exc_info=logSys.getEffectiveLevel() <= 4) x = '{}' return x def _json_loads_safe(x): try: x = json.loads(x.decode(PREFER_ENC, 'replace')) except Exception as e: # converter handler should be exception-safe logSys.error('json loads failed: %r', e, exc_info=logSys.getEffectiveLevel() <= 4) x = {} return x sqlite3.register_adapter(dict, _json_dumps_safe) sqlite3.register_converter("JSON", _json_loads_safe) def commitandrollback(f): @wraps(f) def wrapper(self, *args, **kwargs): with self._lock: # Threading lock with self._db: # Auto commit and rollback on exception cur = self._db.cursor() try: return f(self, cur, *args, **kwargs) finally: cur.close() return wrapper class Fail2BanDb(object): """Fail2Ban database for storing persistent data. This allows after Fail2Ban is restarted to reinstated bans and to continue monitoring logs from the same point. This will either create a new Fail2Ban database, connect to an existing, and if applicable upgrade the schema in the process. Parameters ---------- filename : str File name for SQLite3 database, which will be created if doesn't already exist. purgeAge : int Purge age in seconds, used to remove old bans from database during purge. Raises ------ sqlite3.OperationalError Error connecting/creating a SQLite3 database. RuntimeError If exisiting database fails to update to new schema. Attributes ---------- filename purgeage """ __version__ = 4 # Note all SCRIPTS strings must end in ';' for py26 compatibility _CREATE_SCRIPTS = ( ('fail2banDb', "CREATE TABLE IF NOT EXISTS fail2banDb(version INTEGER);") ,('jails', "CREATE TABLE IF NOT EXISTS jails(" \ "name TEXT NOT NULL UNIQUE, " \ "enabled INTEGER NOT NULL DEFAULT 1" \ ");" \ "CREATE INDEX IF NOT EXISTS jails_name ON jails(name);") ,('logs', "CREATE TABLE IF NOT EXISTS logs(" \ "jail TEXT NOT NULL, " \ "path TEXT, " \ "firstlinemd5 TEXT, " \ "lastfilepos INTEGER DEFAULT 0, " \ "FOREIGN KEY(jail) REFERENCES jails(name) ON DELETE CASCADE, " \ "UNIQUE(jail, path)," \ "UNIQUE(jail, path, firstlinemd5)" \ ");" \ "CREATE INDEX IF NOT EXISTS logs_path ON logs(path);" \ "CREATE INDEX IF NOT EXISTS logs_jail_path ON logs(jail, path);") #TODO: systemd journal features \ #"journalmatch TEXT, " \ #"journlcursor TEXT, " \ #"lastfiletime INTEGER DEFAULT 0, " # is this easily available ,('bans', "CREATE TABLE IF NOT EXISTS bans(" \ "jail TEXT NOT NULL, " \ "ip TEXT, " \ "timeofban INTEGER NOT NULL, " \ "bantime INTEGER NOT NULL, " \ "bancount INTEGER NOT NULL default 1, " \ "data JSON, " \ "FOREIGN KEY(jail) REFERENCES jails(name) " \ ");" \ "CREATE INDEX IF NOT EXISTS bans_jail_timeofban_ip ON bans(jail, timeofban);" \ "CREATE INDEX IF NOT EXISTS bans_jail_ip ON bans(jail, ip);" \ "CREATE INDEX IF NOT EXISTS bans_ip ON bans(ip);") ,('bips', "CREATE TABLE IF NOT EXISTS bips(" \ "ip TEXT NOT NULL, " \ "jail TEXT NOT NULL, " \ "timeofban INTEGER NOT NULL, " \ "bantime INTEGER NOT NULL, " \ "bancount INTEGER NOT NULL default 1, " \ "data JSON, " \ "PRIMARY KEY(ip, jail), " \ "FOREIGN KEY(jail) REFERENCES jails(name) " \ ");" \ "CREATE INDEX IF NOT EXISTS bips_timeofban ON bips(timeofban);" \ "CREATE INDEX IF NOT EXISTS bips_ip ON bips(ip);") ) _CREATE_TABS = dict(_CREATE_SCRIPTS) def __init__(self, filename, purgeAge=24*60*60, outDatedFactor=3): self.maxMatches = 10 self._lock = RLock() self._dbFilename = filename self._purgeAge = purgeAge self._outDatedFactor = outDatedFactor; self._connectDB() def _connectDB(self, checkIntegrity=False): filename = self._dbFilename try: self._db = sqlite3.connect( filename, check_same_thread=False, detect_types=sqlite3.PARSE_DECLTYPES) # # to allow use multi-byte utf-8 # self._db.text_factory = str self._bansMergedCache = {} logSys.info( "Connected to fail2ban persistent database '%s'", filename) except sqlite3.OperationalError as e: logSys.error( "Error connecting to fail2ban persistent database '%s': %s", filename, e.args[0]) raise # differentiate pypy: switch journal mode later (save it during the upgrade), # to prevent errors like "database table is locked": try: import __pypy__ pypy = True except ImportError: pypy = False cur = self._db.cursor() try: cur.execute("PRAGMA foreign_keys = ON") # speedup: write data through OS without syncing (no wait): cur.execute("PRAGMA synchronous = OFF") # speedup: transaction log in memory, alternate using OFF (disable, rollback will be impossible): if not pypy: cur.execute("PRAGMA journal_mode = MEMORY") # speedup: temporary tables and indices are kept in memory: cur.execute("PRAGMA temp_store = MEMORY") cur.execute("SELECT version FROM fail2banDb LIMIT 1") except sqlite3.OperationalError: logSys.warning("New database created. Version '%r'", self.createDb()) except sqlite3.Error as e: logSys.error( "Error opening fail2ban persistent database '%s': %s", filename, e.args[0]) # if not a file - raise an error: if not os.path.isfile(filename): raise # try to repair it: cur.close() cur = None self.repairDB() else: version = cur.fetchone()[0] if version != Fail2BanDb.__version__: newversion = self.updateDb(version) if newversion == Fail2BanDb.__version__: logSys.warning( "Database updated from '%r' to '%r'", version, newversion) else: # pragma: no cover logSys.error( "Database update failed to achieve version '%r'" ": updated from '%r' to '%r'", Fail2BanDb.__version__, version, newversion) raise RuntimeError('Failed to fully update') finally: if checkIntegrity: logSys.debug(" Create missing tables/indices ...") self._createDb(cur, incremental=True) logSys.debug(" -> ok") logSys.debug(" Check integrity ...") cur.execute("PRAGMA integrity_check") for s in cur.fetchall(): logSys.debug(" -> %s", ' '.join(s)) self._db.commit() if cur: # pypy: set journal mode after possible upgrade db: if pypy: cur.execute("PRAGMA journal_mode = MEMORY") cur.close() def close(self): logSys.debug("Close connection to database ...") self._db.close() logSys.info("Connection to database closed.") @property def _dbBackupFilename(self): try: return self.__dbBackupFilename except AttributeError: self.__dbBackupFilename = self._dbFilename + '.' + time.strftime('%Y%m%d-%H%M%S', MyTime.gmtime()) return self.__dbBackupFilename def repairDB(self): class RepairException(Exception): pass # avoid endless recursion if reconnect failed again for some reasons: _repairDB = self.repairDB self.repairDB = None try: # backup logSys.info("Trying to repair database %s", self._dbFilename) if not os.path.isfile(self._dbBackupFilename): shutil.move(self._dbFilename, self._dbBackupFilename) logSys.info(" Database backup created: %s", self._dbBackupFilename) elif os.path.isfile(self._dbFilename): os.remove(self._dbFilename) # first try to repair using dump/restore in order Utils.executeCmd((r"""f2b_db=$0; f2b_dbbk=$1; sqlite3 "$f2b_dbbk" ".dump" | sqlite3 "$f2b_db" """, self._dbFilename, self._dbBackupFilename)) dbFileSize = os.stat(self._dbFilename).st_size if dbFileSize: logSys.info(" Repair seems to be successful, restored %d byte(s).", dbFileSize) # succeeded - try to reconnect: self._connectDB(checkIntegrity=True) else: logSys.info(" Repair seems to be failed, restored %d byte(s).", dbFileSize) raise RepairException('Recreate ...') except Exception as e: # if still failed, just recreate database as fallback: logSys.error(" Error repairing of fail2ban database '%s': %s", self._dbFilename, e.args[0], exc_info=(not isinstance(e, RepairException) and logSys.getEffectiveLevel() <= 10)) os.remove(self._dbFilename) self._connectDB(checkIntegrity=True) finally: self.repairDB = _repairDB @property def filename(self): """File name of SQLite3 database file. """ return self._dbFilename @property def purgeage(self): """Purge age in seconds. """ return self._purgeAge @purgeage.setter def purgeage(self, value): self._purgeAge = MyTime.str2seconds(value) def _createDb(self, cur, incremental=False): """Creates a new database, called during initialisation. """ # create all (if not exists): for (n, s) in Fail2BanDb._CREATE_SCRIPTS: cur.executescript(s) # save current database version (if not already set): cur.execute("INSERT INTO fail2banDb(version)" " SELECT ? WHERE NOT EXISTS (SELECT 1 FROM fail2banDb LIMIT 1)", (Fail2BanDb.__version__, )) cur.execute("SELECT version FROM fail2banDb LIMIT 1") return cur.fetchone()[0] @commitandrollback def createDb(self, cur, incremental=False): return self._createDb(cur, incremental); def _tableExists(self, cur, table): cur.execute("select 1 where exists (" "select 1 from sqlite_master WHERE type='table' AND name=?)", (table,)) res = cur.fetchone() return res is not None and res[0] @commitandrollback def updateDb(self, cur, version): """Update an existing database, called during initialisation. A timestamped backup is also created prior to attempting the update. """ if version > Fail2BanDb.__version__: raise NotImplementedError( "Attempt to travel to future version of database ...how did you get here??") try: logSys.info("Upgrade database: %s from version '%r'", self._dbBackupFilename, version) if not os.path.isfile(self._dbBackupFilename): shutil.copyfile(self.filename, self._dbBackupFilename) logSys.info(" Database backup created: %s", self._dbBackupFilename) if version < 2 and self._tableExists(cur, "logs"): cur.executescript("BEGIN TRANSACTION;" "CREATE TEMPORARY TABLE logs_temp AS SELECT * FROM logs;" "DROP TABLE logs;" "%s;" "INSERT INTO logs SELECT * from logs_temp;" "DROP TABLE logs_temp;" "UPDATE fail2banDb SET version = 2;" "COMMIT;" % Fail2BanDb._CREATE_TABS['logs']) if version < 3 and self._tableExists(cur, "bans"): # set ban-time to -2 (note it means rather unknown, as persistent, will be fixed by restore): cur.executescript("BEGIN TRANSACTION;" "CREATE TEMPORARY TABLE bans_temp AS SELECT jail, ip, timeofban, -2 as bantime, 1 as bancount, data FROM bans;" "DROP TABLE bans;" "%s;\n" "INSERT INTO bans SELECT * from bans_temp;" "DROP TABLE bans_temp;" "COMMIT;" % Fail2BanDb._CREATE_TABS['bans']) if version < 4 and not self._tableExists(cur, "bips"): cur.executescript("BEGIN TRANSACTION;" "%s;\n" "UPDATE fail2banDb SET version = 4;" "COMMIT;" % Fail2BanDb._CREATE_TABS['bips']) if self._tableExists(cur, "bans"): cur.execute( "INSERT OR REPLACE INTO bips(ip, jail, timeofban, bantime, bancount, data)" " SELECT ip, jail, timeofban, bantime, bancount, data FROM bans order by timeofban") cur.execute("SELECT version FROM fail2banDb LIMIT 1") return cur.fetchone()[0] except Exception as e: # if still failed, just recreate database as fallback: logSys.error("Failed to upgrade database '%s': %s", self._dbFilename, e.args[0], exc_info=logSys.getEffectiveLevel() <= 10) self.repairDB() @commitandrollback def addJail(self, cur, jail): """Adds a jail to the database. Parameters ---------- jail : Jail Jail to be added to the database. """ cur.execute( "INSERT OR IGNORE INTO jails(name, enabled) VALUES(?, 1)", (jail.name,)) if cur.rowcount <= 0: cur.execute( "UPDATE jails SET enabled = 1 WHERE name = ? AND enabled != 1", (jail.name,)) @commitandrollback def delJail(self, cur, jail): """Deletes a jail from the database. Parameters ---------- jail : Jail Jail to be removed from the database. """ # Will be deleted by purge as appropriate cur.execute( "UPDATE jails SET enabled=0 WHERE name=?", (jail.name, )) @commitandrollback def delAllJails(self, cur): """Deletes all jails from the database. """ # Will be deleted by purge as appropriate cur.execute("UPDATE jails SET enabled=0") @commitandrollback def getJailNames(self, cur, enabled=None): """Get name of jails in database. Currently only used for testing purposes. Returns ------- set Set of jail names. """ if enabled is None: cur.execute("SELECT name FROM jails") else: cur.execute("SELECT name FROM jails WHERE enabled=%s" % (int(enabled),)) return set(row[0] for row in cur.fetchmany()) @commitandrollback def addLog(self, cur, jail, container): """Adds a log to the database. Parameters ---------- jail : Jail Jail that log is being monitored by. container : FileContainer File container of the log file being added. Returns ------- int If log was already present in database, value of last position in the log file; else `None` """ return self._addLog(cur, jail, container.getFileName(), container.getPos(), container.getHash()) def _addLog(self, cur, jail, name, pos=0, md5=None): lastLinePos = None cur.execute( "SELECT firstlinemd5, lastfilepos FROM logs " "WHERE jail=? AND path=?", (jail.name, name)) try: firstLineMD5, lastLinePos = cur.fetchone() except TypeError: firstLineMD5 = None if firstLineMD5 is None and (pos or md5 is not None): cur.execute( "INSERT OR REPLACE INTO logs(jail, path, firstlinemd5, lastfilepos) " "VALUES(?, ?, ?, ?)", (jail.name, name, md5, pos)) if md5 is not None and md5 != firstLineMD5: lastLinePos = None return lastLinePos @commitandrollback def getLogPaths(self, cur, jail=None): """Gets all the log paths from the database. Currently only for testing purposes. Parameters ---------- jail : Jail If specified, will only reutrn logs belonging to the jail. Returns ------- set Set of log paths. """ query = "SELECT path FROM logs" queryArgs = [] if jail is not None: query += " WHERE jail=?" queryArgs.append(jail.name) cur.execute(query, queryArgs) return set(row[0] for row in cur.fetchmany()) @commitandrollback def updateLog(self, cur, jail, container): """Updates hash and last position in log file. Parameters ---------- jail : Jail Jail of which the log file belongs to. container : FileContainer File container of the log file being updated. """ self._updateLog(cur, jail, container.getFileName(), container.getPos(), container.getHash()) def _updateLog(self, cur, jail, name, pos, md5): cur.execute( "UPDATE logs SET firstlinemd5=?, lastfilepos=? " "WHERE jail=? AND path=?", (md5, pos, jail.name, name)) # be sure it is set (if not available): if not cur.rowcount: cur.execute( "INSERT OR REPLACE INTO logs(jail, path, firstlinemd5, lastfilepos) " "VALUES(?, ?, ?, ?)", (jail.name, name, md5, pos)) @commitandrollback def getJournalPos(self, cur, jail, name, time=0, iso=None): """Get journal position from database. Parameters ---------- jail : Jail Jail of which the journal belongs to. name, time, iso : Journal name (typically systemd-journal) and last known time. Returns ------- int (or float) Last position (as time) if it was already present in database; else `None` """ return self._addLog(cur, jail, name, time, iso); # no hash, just time as iso @commitandrollback def updateJournal(self, cur, jail, name, time, iso): """Updates last position (as time) of journal. Parameters ---------- jail : Jail Jail of which the journal belongs to. name, time, iso : Journal name (typically systemd-journal) and last known time. """ self._updateLog(cur, jail, name, time, iso); # no hash, just time as iso @commitandrollback def addBan(self, cur, jail, ticket): """Add a ban to the database. Parameters ---------- jail : Jail Jail in which the ban has occurred. ticket : BanTicket Ticket of the ban to be added. """ ip = str(ticket.getID()) try: del self._bansMergedCache[(ip, jail)] except KeyError: pass try: del self._bansMergedCache[(ip, None)] except KeyError: pass #TODO: Implement data parts once arbitrary match keys completed data = ticket.getData() matches = data.get('matches') if self.maxMatches: if matches and len(matches) > self.maxMatches: data = data.copy() data['matches'] = matches[-self.maxMatches:] elif matches: data = data.copy() del data['matches'] cur.execute( "INSERT INTO bans(jail, ip, timeofban, bantime, bancount, data) VALUES(?, ?, ?, ?, ?, ?)", (jail.name, ip, int(round(ticket.getTime())), ticket.getBanTime(jail.actions.getBanTime()), ticket.getBanCount(), data)) cur.execute( "INSERT OR REPLACE INTO bips(ip, jail, timeofban, bantime, bancount, data) VALUES(?, ?, ?, ?, ?, ?)", (ip, jail.name, int(round(ticket.getTime())), ticket.getBanTime(jail.actions.getBanTime()), ticket.getBanCount(), data)) @commitandrollback def delBan(self, cur, jail, *args): """Delete a single or multiple tickets from the database. Parameters ---------- jail : Jail Jail in which the ticket(s) should be removed. args : list of IP IPs to be removed, if not given all tickets of jail will be removed. """ query1 = "DELETE FROM bips WHERE jail = ?" query2 = "DELETE FROM bans WHERE jail = ?" queryArgs = [jail.name]; if not len(args): cur.execute(query1, queryArgs); cur.execute(query2, queryArgs); return query1 += " AND ip = ?" query2 += " AND ip = ?" queryArgs.append(''); for ip in args: queryArgs[1] = str(ip); cur.execute(query1, queryArgs); cur.execute(query2, queryArgs); @commitandrollback def _getBans(self, cur, jail=None, bantime=None, ip=None): query = "SELECT ip, timeofban, data FROM bans WHERE 1" queryArgs = [] if jail is not None: query += " AND jail=?" queryArgs.append(jail.name) if bantime is not None and bantime >= 0: query += " AND timeofban > ?" queryArgs.append(MyTime.time() - bantime) if ip is not None: query += " AND ip=?" queryArgs.append(str(ip)) query += " ORDER BY ip, timeofban desc" # repack iterator as long as in lock: return list(cur.execute(query, queryArgs)) def getBans(self, **kwargs): """Get bans from the database. Parameters ---------- jail : Jail Jail that the ban belongs to. Default `None`; all jails. bantime : int Ban time in seconds, such that bans returned would still be valid now. Negative values are equivalent to `None`. Default `None`; no limit. ip : str IP Address to filter bans by. Default `None`; all IPs. Returns ------- list List of `Ticket`s for bans stored in database. """ tickets = [] for ip, timeofban, data in self._getBans(**kwargs): #TODO: Implement data parts once arbitrary match keys completed tickets.append(FailTicket(ip, timeofban)) tickets[-1].setData(data) return tickets def getBansMerged(self, ip=None, jail=None, bantime=None): """Get bans from the database, merged into single ticket. This is the same as `getBans`, but bans merged into single ticket. Parameters ---------- jail : Jail Jail that the ban belongs to. Default `None`; all jails. bantime : int Ban time in seconds, such that bans returned would still be valid now. Negative values are equivalent to `None`. Default `None`; no limit. ip : str IP Address to filter bans by. Default `None`; all IPs. Returns ------- list or Ticket Single ticket representing bans stored in database per IP in a list. When `ip` argument passed, a single `Ticket` is returned. """ with self._lock: cacheKey = None if bantime is None or bantime < 0: cacheKey = (ip, jail) if cacheKey in self._bansMergedCache: return self._bansMergedCache[cacheKey] tickets = [] ticket = None results = list(self._getBans(ip=ip, jail=jail, bantime=bantime)) if results: prev_banip = results[0][0] matches = [] failures = 0 tickdata = {} for banip, timeofban, data in results: #TODO: Implement data parts once arbitrary match keys completed if banip != prev_banip: ticket = FailTicket(prev_banip, prev_timeofban, matches) ticket.setAttempt(failures) tickets.append(ticket) # Reset variables prev_banip = banip matches = [] failures = 0 tickdata = {} m = data.get('matches', []) # pre-insert "maxadd" enries (because tickets are ordered desc by time) maxadd = self.maxMatches - len(matches) if maxadd > 0: if len(m) <= maxadd: matches = m + matches else: matches = m[-maxadd:] + matches failures += data.get('failures', 1) data['failures'] = failures data['matches'] = matches tickdata.update(data) prev_timeofban = timeofban ticket = FailTicket(banip, prev_timeofban, data=tickdata) tickets.append(ticket) if cacheKey: self._bansMergedCache[cacheKey] = tickets if ip is None else ticket return tickets if ip is None else ticket @commitandrollback def getBan(self, cur, ip, jail=None, forbantime=None, overalljails=None, fromtime=None): ip = str(ip) if not overalljails: query = "SELECT bancount, timeofban, bantime FROM bips" else: query = "SELECT sum(bancount), max(timeofban), sum(bantime) FROM bips" query += " WHERE ip = ?" queryArgs = [ip] if not overalljails and jail is not None: query += " AND jail=?" queryArgs.append(jail.name) if forbantime is not None: query += " AND timeofban > ?" queryArgs.append(MyTime.time() - forbantime) if fromtime is not None: query += " AND timeofban > ?" queryArgs.append(fromtime) if overalljails or jail is None: query += " GROUP BY ip ORDER BY timeofban DESC LIMIT 1" # repack iterator as long as in lock: return list(cur.execute(query, queryArgs)) def _getCurrentBans(self, cur, jail = None, ip = None, forbantime=None, fromtime=None): queryArgs = [] if jail is not None: query = "SELECT ip, timeofban, bantime, bancount, data FROM bips WHERE jail=?" queryArgs.append(jail.name) else: query = "SELECT ip, max(timeofban), bantime, bancount, data FROM bips WHERE 1" if ip is not None: query += " AND ip=?" queryArgs.append(ip) query += " AND (timeofban + bantime > ? OR bantime <= -1)" queryArgs.append(fromtime) if forbantime not in (None, -1): # not specified or persistent (all) query += " AND timeofban > ?" queryArgs.append(fromtime - forbantime) if ip is None: query += " GROUP BY ip ORDER BY ip, timeofban DESC" else: query += " ORDER BY timeofban DESC LIMIT 1" return cur.execute(query, queryArgs) def getCurrentBans(self, jail=None, ip=None, forbantime=None, fromtime=None, correctBanTime=True, maxmatches=None ): """Reads tickets (with merged info) currently affected from ban from the database. There are all the tickets corresponding parameters jail/ip, forbantime, fromtime (normally now). If correctBanTime specified (default True) it will fix the restored ban-time (and therefore endOfBan) of the ticket (normally it is ban-time of jail as maximum) for all tickets with ban-time greater (or persistent). """ cur = self._db.cursor() try: if fromtime is None: fromtime = MyTime.time() tickets = [] ticket = None if correctBanTime is True: correctBanTime = jail.getMaxBanTime() if jail is not None else None # don't change if persistent allowed: if correctBanTime == -1: correctBanTime = None with self._lock: bans = self._getCurrentBans(cur, jail=jail, ip=ip, forbantime=forbantime, fromtime=fromtime ) for ticket in bans: # can produce unpack error (database may return sporadical wrong-empty row): try: banip, timeofban, bantime, bancount, data = ticket # additionally check for empty values: if banip is None or banip == "": # pragma: no cover raise ValueError('unexpected value %r' % (banip,)) # if bantime unknown (after upgrade-db from earlier version), just use min known ban-time: if bantime == -2: # todo: remove it in future version bantime = jail.actions.getBanTime() if jail is not None else ( correctBanTime if correctBanTime else 600) elif correctBanTime and correctBanTime >= 0: # if persistent ban (or greater as max), use current max-bantime of the jail: if bantime == -1 or bantime > correctBanTime: bantime = correctBanTime # after correction check the end of ban again: if bantime != -1 and timeofban + bantime <= fromtime: # not persistent and too old - ignore it: logSys.debug("ignore ticket (with new max ban-time %r): too old %r <= %r, ticket: %r", bantime, timeofban + bantime, fromtime, ticket) continue except ValueError as e: # pragma: no cover logSys.debug("get current bans: ignore row %r - %s", ticket, e) continue # logSys.debug('restore ticket %r, %r, %r', banip, timeofban, data) ticket = FailTicket(banip, timeofban, data=data) # filter matches if expected (current count > as maxmatches specified): if maxmatches is None: maxmatches = self.maxMatches if maxmatches: matches = ticket.getMatches() if matches and len(matches) > maxmatches: ticket.setMatches(matches[-maxmatches:]) else: ticket.setMatches(None) # logSys.debug('restored ticket: %r', ticket) ticket.setBanTime(bantime) ticket.setBanCount(bancount) if ip is not None: return ticket tickets.append(ticket) finally: cur.close() return tickets def _cleanjails(self, cur): """Remove empty jails jails and log files from database. """ cur.execute( "DELETE FROM jails WHERE enabled = 0 " "AND NOT EXISTS(SELECT * FROM bans WHERE jail = jails.name) " "AND NOT EXISTS(SELECT * FROM bips WHERE jail = jails.name)") def _purge_bips(self, cur): """Purge old bad ips (jails and log files from database). Currently it is timed out IP, whose time since last ban is several times out-dated (outDatedFactor is default 3). Permanent banned ips will be never removed. """ cur.execute( "DELETE FROM bips WHERE timeofban < ? and bantime != -1 and (timeofban + (bantime * ?)) < ?", (int(MyTime.time()) - self._purgeAge, self._outDatedFactor, int(MyTime.time()) - self._purgeAge)) @commitandrollback def purge(self, cur): """Purge old bans, jails and log files from database. """ self._bansMergedCache = {} cur.execute( "DELETE FROM bans WHERE timeofban < ?", (MyTime.time() - self._purgeAge, )) self._purge_bips(cur) self._cleanjails(cur) server/datedetector.py000064400000046034152231502150011076 0ustar00# 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. __author__ = "Cyril Jaquier and Fail2Ban Contributors" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" import copy import time from threading import Lock from .datetemplate import re, DateTemplate, DatePatternRegex, DateTai64n, DateEpoch, \ RE_EPOCH_PATTERN from .strptime import validateTimeZone from .utils import Utils from ..helpers import getLogger # Gets the instance of the logger. logSys = getLogger(__name__) logLevel = 5 RE_DATE_PREMATCH = re.compile(r"(?[-/.])%m(?P=_sep)%d(?:T| ?)%H:%M:%S(?:[.,]%f)?(?:\s*%z)?", # asctime with optional day, subsecond and/or year: # Sun Jan 23 21:59:59.011 2005 r"(?:%a )?%b %d %k:%M:%S(?:\.%f)?(?: %ExY)?", # asctime with optional day, subsecond and/or year coming after day # http://bugs.debian.org/798923 # Sun Jan 23 2005 21:59:59.011 r"(?:%a )?%b %d %ExY %k:%M:%S(?:\.%f)?", # simple date too (from x11vnc): 23/01/2005 21:59:59 # and with optional year given by 2 digits: 23/01/05 21:59:59 # (See http://bugs.debian.org/537610) # 17-07-2008 17:23:25 r"%d(?P<_sep>[-/])%m(?P=_sep)(?:%ExY|%Exy) %k:%M:%S", # Apache format optional time zone: # [31/Oct/2006:09:22:55 -0000] # 26-Jul-2007 15:20:52 # named 26-Jul-2007 15:20:52.252 # roundcube 26-Jul-2007 15:20:52 +0200 r"%d(?P<_sep>[-/])%b(?P=_sep)%ExY[ :]?%H:%M:%S(?:\.%f)?(?: %z)?", # CPanel 05/20/2008:01:57:39 r"%m/%d/%ExY:%H:%M:%S", # 01-27-2012 16:22:44.252 # subseconds explicit to avoid possible %m<->%d confusion # with previous ("%d-%m-%ExY %k:%M:%S" by "%d(?P<_sep>[-/])%m(?P=_sep)(?:%ExY|%Exy) %k:%M:%S") r"%m-%d-%ExY %k:%M:%S(?:\.%f)?", # Epoch r"EPOCH", # Only time information in the log r"{^LN-BEG}%H:%M:%S", # <09/16/08@05:03:30> r"^<%m/%d/%Exy@%H:%M:%S>", # MySQL: 130322 11:46:11 r"%Exy%Exm%Exd ?%H:%M:%S", # Apache Tomcat r"%b %d, %ExY %I:%M:%S %p", # ASSP: Apr-27-13 02:33:06 r"^%b-%d-%Exy %k:%M:%S", # 20050123T215959, 20050123 215959, 20050123 85959 r"%ExY%Exm%Exd(?:T| ?)%ExH%ExM%ExS(?:[.,]%f)?(?:\s*%z)?", # prefixed with optional named time zone (monit): # PDT Apr 16 21:05:29 r"(?:%Z )?(?:%a )?%b %d %k:%M:%S(?:\.%f)?(?: %ExY)?", # +00:00 Jan 23 21:59:59.011 2005 r"(?:%z )?(?:%a )?%b %d %k:%M:%S(?:\.%f)?(?: %ExY)?", # TAI64N r"TAI64N", ] @property def defaultTemplates(self): if isinstance(DateDetectorCache.DEFAULT_TEMPLATES[0], str): for i, dt in enumerate(DateDetectorCache.DEFAULT_TEMPLATES): dt = _getPatternTemplate(dt) DateDetectorCache.DEFAULT_TEMPLATES[i] = dt return DateDetectorCache.DEFAULT_TEMPLATES def _addDefaultTemplate(self): """Add resp. cache Fail2Ban's default set of date templates. """ self.__tmpcache = [], [] # cache default templates: for dt in self.defaultTemplates: self._cacheTemplate(dt) # self.__templates = self.__tmpcache[0] + self.__tmpcache[1] del self.__tmpcache class DateDetectorTemplate(object): """Used for "shallow copy" of the template object. Prevents collectively usage of hits/lastUsed in cached templates """ __slots__ = ('template', 'hits', 'lastUsed', 'distance') def __init__(self, template): self.template = template self.hits = 0 self.lastUsed = 0 # the last distance to date-match within the log file: self.distance = 0x7fffffff @property def weight(self): return self.hits * self.template.weight / max(1, self.distance) def __getattr__(self, name): """ Returns attribute of template (called for parameters not in slots) """ return getattr(self.template, name) class DateDetector(object): """Manages one or more date templates to find a date within a log line. Attributes ---------- templates """ _defCache = DateDetectorCache() def __init__(self): self.__templates = list() self.__known_names = set() # time the template was long unused (currently 300 == 5m): self.__unusedTime = 300 # last known distance (bypass one char collision) and end position: self.__lastPos = 1, None self.__lastEndPos = 0x7fffffff, None self.__lastTemplIdx = 0x7fffffff # first free place: self.__firstUnused = 0 # pre-match pattern: self.__preMatch = None # default TZ (if set, treat log lines without explicit time zone to be in this time zone): self.__default_tz = None def _appendTemplate(self, template, ignoreDup=False): name = template.name if name in self.__known_names: if ignoreDup: return raise ValueError( "There is already a template with name %s" % name) self.__known_names.add(name) self.__templates.append(DateDetectorTemplate(template)) def appendTemplate(self, template): """Add a date template to manage and use in search of dates. Parameters ---------- template : DateTemplate or str Can be either a `DateTemplate` instance, or a string which will be used as the pattern for the `DatePatternRegex` template. The template will then be added to the detector. Raises ------ ValueError If a template already exists with the same name. """ if isinstance(template, str): key = pattern = template if '%' not in pattern: key = pattern.upper() template = DD_patternCache.get(key) if not template: if key in ("{^LN-BEG}", "{DEFAULT}"): flt = \ lambda template: template.flags & DateTemplate.LINE_BEGIN if key == "{^LN-BEG}" else None self.addDefaultTemplate(flt) return elif "{DATE}" in key: self.addDefaultTemplate(preMatch=pattern, allDefaults=False) return elif key == "{NONE}": template = _getPatternTemplate('{UNB}^', key) else: template = _getPatternTemplate(pattern, key) DD_patternCache.set(key, template) self._appendTemplate(template) logSys.info(" date pattern `%r`: `%s`", getattr(template, 'pattern', ''), template.name) logSys.debug(" date pattern regex for %r: %s", getattr(template, 'pattern', ''), template.regex) def addDefaultTemplate(self, filterTemplate=None, preMatch=None, allDefaults=True): """Add Fail2Ban's default set of date templates. """ ignoreDup = len(self.__templates) > 0 for template in ( DateDetector._defCache.templates if allDefaults else DateDetector._defCache.defaultTemplates ): # filter if specified: if filterTemplate is not None and not filterTemplate(template): continue # if exact pattern available - create copy of template, contains replaced {DATE} with default regex: if preMatch is not None: # get cached or create a copy with modified name/pattern, using preMatch replacement for {DATE}: template = _getAnchoredTemplate(template, wrap=lambda s: RE_DATE_PREMATCH.sub(lambda m: DateTemplate.unboundPattern(s), preMatch)) # append date detector template (ignore duplicate if some was added before default): self._appendTemplate(template, ignoreDup=ignoreDup) @property def templates(self): """List of template instances managed by the detector. """ return self.__templates def matchTime(self, line): """Attempts to find date on a log line using templates. This uses the templates' `matchDate` method in an attempt to find a date. It also increments the match hit count for the winning template. Parameters ---------- line : str Line which is searched by the date templates. Returns ------- re.MatchObject, DateTemplate The regex match returned from the first successfully matched template. """ # if no templates specified - default templates should be used: if not len(self.__templates): self.addDefaultTemplate() log = logSys.log if logSys.getEffectiveLevel() <= logLevel else lambda *args: None log(logLevel-1, "try to match time for line: %.120s", line) # first try to use last template with same start/end position: match = None found = None, 0x7fffffff, 0x7fffffff, -1 ignoreBySearch = 0x7fffffff i = self.__lastTemplIdx if i < len(self.__templates): ddtempl = self.__templates[i] template = ddtempl.template if template.flags & (DateTemplate.LINE_BEGIN|DateTemplate.LINE_END): log(logLevel-1, " try to match last anchored template #%02i ...", i) match = template.matchDate(line) ignoreBySearch = i else: distance, endpos = self.__lastPos[0], self.__lastEndPos[0] log(logLevel-1, " try to match last template #%02i (from %r to %r): ...%r==%r %s %r==%r...", i, distance, endpos, line[distance-1:distance], self.__lastPos[1], line[distance:endpos], line[endpos:endpos+1], self.__lastEndPos[2]) # check same boundaries left/right, outside fully equal, inside only if not alnum (e. g. bound RE # with space or some special char), otherwise possible collision/pattern switch: if (( line[distance-1:distance] == self.__lastPos[1] or (line[distance:distance+1] == self.__lastPos[2] and not self.__lastPos[2].isalnum()) ) and ( line[endpos:endpos+1] == self.__lastEndPos[2] or (line[endpos-1:endpos] == self.__lastEndPos[1] and not self.__lastEndPos[1].isalnum()) )): # search in line part only: log(logLevel-1, " boundaries are correct, search in part %r", line[distance:endpos]) match = template.matchDate(line, distance, endpos) else: log(logLevel-1, " boundaries show conflict, try whole search") match = template.matchDate(line) ignoreBySearch = i if match: distance = match.start() endpos = match.end() # if different position, possible collision/pattern switch: if ( len(self.__templates) == 1 or # single template: template.flags & (DateTemplate.LINE_BEGIN|DateTemplate.LINE_END) or (distance == self.__lastPos[0] and endpos == self.__lastEndPos[0]) ): log(logLevel, " matched last time template #%02i", i) else: log(logLevel, " ** last pattern collision - pattern change, reserve & search ...") found = match, distance, endpos, i; # save current best alternative match = None else: log(logLevel, " ** last pattern not found - pattern change, search ...") # search template and better match: if not match: log(logLevel, " search template (%i) ...", len(self.__templates)) i = 0 for ddtempl in self.__templates: if i == ignoreBySearch: i += 1 continue log(logLevel-1, " try template #%02i: %s", i, ddtempl.name) template = ddtempl.template match = template.matchDate(line) if match: distance = match.start() endpos = match.end() log(logLevel, " matched time template #%02i (at %r <= %r, %r) %s", i, distance, ddtempl.distance, self.__lastPos[0], template.name) ## last (or single) template - fast stop: if i+1 >= len(self.__templates): break ## if line-begin/end anchored - stop searching: if template.flags & (DateTemplate.LINE_BEGIN|DateTemplate.LINE_END): break ## stop searching if next template still unused, but we had already hits: if (distance == 0 and ddtempl.hits) and not self.__templates[i+1].template.hits: break ## [grave] if distance changed, possible date-match was found somewhere ## in body of message, so save this template, and search further: if distance > ddtempl.distance or distance > self.__lastPos[0]: log(logLevel, " ** distance collision - pattern change, reserve") ## shortest of both: if distance < found[1]: found = match, distance, endpos, i ## search further: match = None i += 1 continue ## winner - stop search: break i += 1 # check other template was found (use this one with shortest distance): if not match and found[0]: match, distance, endpos, i = found log(logLevel, " use best time template #%02i", i) ddtempl = self.__templates[i] template = ddtempl.template # we've winner, incr hits, set distance, usage, reorder, etc: if match: ddtempl.hits += 1 ddtempl.lastUsed = time.time() ddtempl.distance = distance if self.__firstUnused == i: self.__firstUnused += 1 self.__lastPos = distance, line[distance-1:distance], line[distance] self.__lastEndPos = endpos, line[endpos-1], line[endpos:endpos+1] # if not first - try to reorder current template (bubble up), they will be not sorted anymore: if i and i != self.__lastTemplIdx: i = self._reorderTemplate(i) self.__lastTemplIdx = i # return tuple with match and template reference used for parsing: return (match, template) # not found: log(logLevel, " no template.") return (None, None) @property def default_tz(self): return self.__default_tz @default_tz.setter def default_tz(self, value): self.__default_tz = validateTimeZone(value) def getTime(self, line, timeMatch=None): """Attempts to return the date on a log line using templates. This uses the templates' `getDate` method in an attempt to find a date. For the faster usage, always specify a parameter timeMatch (the previous tuple result of the matchTime), then this will work without locking and without cycle over templates. Parameters ---------- line : str Line which is searched by the date templates. Returns ------- float The Unix timestamp returned from the first successfully matched template or None if not found. """ # search match for all specified templates: if timeMatch is None: timeMatch = self.matchTime(line) # convert: template = timeMatch[1] if template is not None: try: date = template.getDate(line, timeMatch[0], default_tz=self.__default_tz) if date is not None: if logSys.getEffectiveLevel() <= logLevel: # pragma: no cover - heavy debug logSys.log(logLevel, " got time %f for %r using template %s", date[0], date[1].group(1), template.name) return date except ValueError: pass return None def _reorderTemplate(self, num): """Reorder template (bubble up) in template list if hits grows enough. Parameters ---------- num : int Index of template should be moved. """ if num: templates = self.__templates ddtempl = templates[num] if logSys.getEffectiveLevel() <= logLevel: logSys.log(logLevel, " -> reorder template #%02i, hits: %r", num, ddtempl.hits) ## current hits and time the template was long unused: untime = ddtempl.lastUsed - self.__unusedTime weight = ddtempl.weight ## try to move faster (first if unused available, or half of part to current template position): pos = self.__firstUnused if self.__firstUnused < num else num // 2 ## don't move too often (multiline logs resp. log's with different date patterns), ## if template not used too long, replace it also : def _moveable(): pweight = templates[pos].weight if logSys.getEffectiveLevel() <= logLevel: logSys.log(logLevel, " -> compare template #%02i & #%02i, weight %.3f > %.3f, hits %r > %r", num, pos, weight, pweight, ddtempl.hits, templates[pos].hits) return weight > pweight or untime > templates[pos].lastUsed ## ## if not moveable (smaller weight or target position recently used): if not _moveable(): ## try to move slow (exact 1 position): if pos == num-1: return num pos = num-1 ## if still smaller and template at position used, don't move: if not _moveable(): return num ## move: del templates[num] templates[pos:0] = [ddtempl] ## correct first unused: while self.__firstUnused < len(templates) and templates[self.__firstUnused].hits: self.__firstUnused += 1 if logSys.getEffectiveLevel() <= logLevel: logSys.log(logLevel, " -> moved template #%02i -> #%02i", num, pos) return pos return num server/datetemplate.py000064400000030666152231502150011104 0ustar00# emacs: -*- mode: python; coding: utf-8; 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. # Author: Cyril Jaquier # __author__ = "Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" import re, time from abc import abstractmethod from .strptime import reGroupDictStrptime, timeRE, getTimePatternRE from ..helpers import getLogger logSys = getLogger(__name__) # check already grouped contains "(", but ignores char "\(" and conditional "(?(id)...)": RE_GROUPED = re.compile(r'(?(?<=^\[))|(?P(?<=\baudit\()))%s)(?:(?(selinux)(?=:\d+\)))|(?(square)(?=\])))" % epochRE self.setRegex(regex, wordBegin=False) ;# already line begin resp. word begin anchored else: regex = r"((?P(?<=^\[))?%s)(?(square)(?=\]))" % epochRE self.setRegex(regex, wordBegin='start', wordEnd=True) def getDate(self, line, dateMatch=None, default_tz=None): """Method to return the date for a log line. Parameters ---------- line : str Log line, of which the date should be extracted from. default_tz: ignored, Unix timestamps are time zone independent Returns ------- (float, str) Tuple containing a Unix timestamp, and the string of the date which was matched and in turned used to calculated the timestamp. """ if not dateMatch: dateMatch = self.matchDate(line) if dateMatch: v = dateMatch.group(self._grpIdx) # extract part of format which represents seconds since epoch if self._longFrm and len(v) >= 13: if len(v) >= 16 and '.' not in v: v = float(v) / 1000000 else: v = float(v) / 1000 return (float(v), dateMatch) class DatePatternRegex(DateTemplate): """Date template, with regex/pattern Parameters ---------- pattern : str Sets the date templates pattern. Attributes ---------- name regex pattern """ _patternRE, _patternName = getTimePatternRE() _patternRE = re.compile(_patternRE) def __init__(self, pattern=None, **kwargs): super(DatePatternRegex, self).__init__() self._pattern = None if pattern is not None: self.setRegex(pattern, **kwargs) @property def pattern(self): """The pattern used for regex with strptime "%" time fields. This should be a valid regular expression, of which matching string will be extracted from the log line. strptime style "%" fields will be replaced by appropriate regular expressions, or custom regex groups with names as per the strptime fields can also be used instead. """ return self._pattern @pattern.setter def pattern(self, pattern): self.setRegex(pattern) def setRegex(self, pattern, wordBegin=True, wordEnd=True): # original pattern: self._pattern = pattern # if unbound signalled - reset boundaries left and right: if RE_EXLINE_NO_BOUNDS.search(pattern): pattern = RE_EXLINE_NO_BOUNDS.sub('', pattern) wordBegin = wordEnd = False # if explicit given {^LN-BEG} - remove it from pattern and set 'start' in wordBegin: if wordBegin and RE_EXLINE_BOUND_BEG.search(pattern): pattern = RE_EXLINE_BOUND_BEG.sub('', pattern) wordBegin = 'start' try: # wrap to regex: fmt = self._patternRE.sub(r'%(\1)s', pattern) self.name = fmt % self._patternName regex = fmt % timeRE # if expected add (?iu) for "ignore case" and "unicode": if RE_ALPHA_PATTERN.search(pattern): regex = r'(?iu)' + regex super(DatePatternRegex, self).setRegex(regex, wordBegin, wordEnd) except Exception as e: raise TypeError("Failed to set datepattern '%s' (may be an invalid format or unescaped percent char): %s" % (pattern, e)) def getDate(self, line, dateMatch=None, default_tz=None): """Method to return the date for a log line. This uses a custom version of strptime, using the named groups from the instances `pattern` property. Parameters ---------- line : str Log line, of which the date should be extracted from. default_tz: optionally used to correct timezone Returns ------- (float, str) Tuple containing a Unix timestamp, and the string of the date which was matched and in turned used to calculated the timestamp. """ if not dateMatch: dateMatch = self.matchDate(line) if dateMatch: return (reGroupDictStrptime(dateMatch.groupdict(), default_tz=default_tz), dateMatch) class DateTai64n(DateTemplate): """A date template which matches TAI64N formate timestamps. Attributes ---------- name regex """ def __init__(self, wordBegin=False): DateTemplate.__init__(self) self.name = "TAI64N" # We already know the format for TAI64N self.setRegex("@[0-9a-f]{24}", wordBegin=wordBegin) def getDate(self, line, dateMatch=None, default_tz=None): """Method to return the date for a log line. Parameters ---------- line : str Log line, of which the date should be extracted from. default_tz: ignored, since TAI is time zone independent Returns ------- (float, str) Tuple containing a Unix timestamp, and the string of the date which was matched and in turned used to calculated the timestamp. """ if not dateMatch: dateMatch = self.matchDate(line) if dateMatch: # extract part of format which represents seconds since epoch value = dateMatch.group(1) seconds_since_epoch = value[2:17] # convert seconds from HEX into local time stamp return (int(seconds_since_epoch, 16), dateMatch) server/failmanager.py000064400000011745152231502150010676 0ustar00# 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. # Author: Cyril Jaquier # __author__ = "Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" from threading import Lock import logging from .ticket import FailTicket, BanTicket from ..helpers import getLogger, BgService # Gets the instance of the logger. logSys = getLogger(__name__) logLevel = logging.DEBUG class FailManager: def __init__(self): self.__lock = Lock() self.__failList = dict() self.__maxRetry = 3 self.__maxTime = 600 self.__failTotal = 0 self.maxMatches = 5 self.__bgSvc = BgService() def setFailTotal(self, value): self.__failTotal = value def getFailTotal(self): return self.__failTotal def getFailCount(self): # may be slow on large list of failures, should be used for test purposes only... with self.__lock: return len(self.__failList), sum([f.getRetry() for f in list(self.__failList.values())]) def setMaxRetry(self, value): self.__maxRetry = value def getMaxRetry(self): return self.__maxRetry def setMaxTime(self, value): self.__maxTime = value def getMaxTime(self): return self.__maxTime def addFailure(self, ticket, count=1, observed=False): attempts = 1 with self.__lock: fid = ticket.getID() try: fData = self.__failList[fid] # if the same object - the same matches but +1 attempt: if fData is ticket: matches = None attempt = 1 else: # will be incremented / extended (be sure we have at least +1 attempt): matches = ticket.getMatches() if self.maxMatches else None attempt = ticket.getAttempt() if attempt <= 0: attempt += 1 unixTime = ticket.getTime() fData.adjustTime(unixTime, self.__maxTime) fData.inc(matches, attempt, count) # truncate to maxMatches: if self.maxMatches: matches = fData.getMatches() if len(matches) > self.maxMatches: fData.setMatches(matches[-self.maxMatches:]) else: fData.setMatches(None) except KeyError: # not found - already banned - prevent to add failure if comes from observer: if observed or isinstance(ticket, BanTicket): return ticket.getRetry() # if already FailTicket - add it direct, otherwise create (using copy all ticket data): if isinstance(ticket, FailTicket): fData = ticket; else: fData = FailTicket.wrap(ticket) if count > ticket.getAttempt(): fData.setRetry(count) self.__failList[fid] = fData attempts = fData.getRetry() self.__failTotal += 1 if logSys.getEffectiveLevel() <= logLevel: # yoh: Since composing this list might be somewhat time consuming # in case of having many active failures, it should be ran only # if debug level is "low" enough failures_summary = ', '.join(['%s:%d' % (k, v.getRetry()) for k,v in self.__failList.items()]) logSys.log(logLevel, "Total # of detected failures: %d. Current failures from %d IPs (IP:count): %s" % (self.__failTotal, len(self.__failList), failures_summary)) self.__bgSvc.service() return attempts def size(self): return len(self.__failList) def cleanup(self, time): time -= self.__maxTime with self.__lock: todelete = [fid for fid,item in self.__failList.items() \ if item.getTime() <= time] if len(todelete) == len(self.__failList): # remove all: self.__failList = dict() elif not len(todelete): # nothing: return if len(todelete) / 2.0 <= len(self.__failList) / 3.0: # few as 2/3 should be removed - remove particular items: for fid in todelete: del self.__failList[fid] else: # create new dictionary without items to be deleted: self.__failList = dict((fid,item) for fid,item in self.__failList.items() \ if item.getTime() > time) self.__bgSvc.service() def delFailure(self, fid): with self.__lock: try: del self.__failList[fid] except KeyError: pass def toBan(self, fid=None): with self.__lock: for fid in ([fid] if fid is not None and fid in self.__failList else self.__failList): data = self.__failList[fid] if data.getRetry() >= self.__maxRetry: del self.__failList[fid] return data self.__bgSvc.service() raise FailManagerEmpty class FailManagerEmpty(Exception): pass server/failregex.py000064400000032334152231502150010373 0ustar00# 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. __author__ = "Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" import re import sre_constants import sys from .ipdns import IPAddr FTAG_CRE = re.compile(r'') FCUSTNAME_CRE = re.compile(r'^(/?)F-([A-Z0-9_\-]+)$'); # currently uppercase only R_HOST = [ # separated ipv4: r"""(?:::f{4,6}:)?(?P%s)""" % (IPAddr.IP_4_RE,), # separated ipv6: r"""(?P%s)""" % (IPAddr.IP_6_RE,), # separated dns: r"""(?P[\w\-.^_]*\w)""", # place-holder for ADDR tag-replacement (joined): "", # place-holder for HOST tag replacement (joined): "", # CIDR in simplest integer form: r"(?P\d+)", # place-holder for SUBNET tag-replacement "", ] RI_IPV4 = 0 RI_IPV6 = 1 RI_DNS = 2 RI_ADDR = 3 RI_HOST = 4 RI_CIDR = 5 RI_SUBNET = 6 R_HOST[RI_ADDR] = r"\[?(?:%s|%s)\]?" % (R_HOST[RI_IPV4], R_HOST[RI_IPV6],) R_HOST[RI_HOST] = r"(?:%s|%s)" % (R_HOST[RI_ADDR], R_HOST[RI_DNS],) R_HOST[RI_SUBNET] = r"\[?(?:%s|%s)(?:/%s)?\]?" % (R_HOST[RI_IPV4], R_HOST[RI_IPV6], R_HOST[RI_CIDR],) RH4TAG = { # separated ipv4 (self closed, closed): "IP4": R_HOST[RI_IPV4], "F-IP4/": R_HOST[RI_IPV4], # separated ipv6 (self closed, closed): "IP6": R_HOST[RI_IPV6], "F-IP6/": R_HOST[RI_IPV6], # 2 address groups instead of - in opposition to ``, # for separate usage of 2 address groups only (regardless of `usedns`), `ip4` and `ip6` together "ADDR": R_HOST[RI_ADDR], "F-ADDR/": R_HOST[RI_ADDR], # subnet tags for usage as `/` or ``: "CIDR": R_HOST[RI_CIDR], "F-CIDR/": R_HOST[RI_CIDR], "SUBNET": R_HOST[RI_SUBNET], "F-SUBNET/":R_HOST[RI_SUBNET], # separated dns (self closed, closed): "DNS": R_HOST[RI_DNS], "F-DNS/": R_HOST[RI_DNS], # default failure-id as no space tag: "F-ID/": r"""(?P\S+)""", # default failure port, like 80 or http : "F-PORT/": r"""(?P\w+)""", } # default failure groups map for customizable expressions (with different group-id): R_MAP = { "id": "fid", "port": "fport", } # map global flags like ((?i)xxx) or (?:(?i)xxx) to local flags (?i:xxx) if supported by RE-engine in this python version: try: re.search("^re(?i:val)$", "reVAL") R_GLOB2LOCFLAGS = ( re.compile(r"(? user ... ALTNAME_PRE = 'alt_' # TUPLE_ - names of parts to be combined to single value as tuple TUPNAME_PRE = 'tuple_' COMPLNAME_PRE = (ALTNAME_PRE, TUPNAME_PRE) COMPLNAME_CRE = re.compile(r'^(' + '|'.join(COMPLNAME_PRE) + r')(.*?)(?:_\d+)?$') ## # Regular expression class. # # This class represents a regular expression with its compiled version. class Regex: ## # Constructor. # # Creates a new object. This method can throw RegexException in order to # avoid construction of invalid object. # @param value the regular expression def __init__(self, regex, multiline=False, **kwargs): self._matchCache = None # Perform shortcuts expansions. # Replace standard f2b-tags (like "", etc) using default regular expressions: regex = Regex._resolveHostTag(regex, **kwargs) # if regex.lstrip() == '': raise RegexException("Cannot add empty regex") # special handling wrapping global flags to local flags: if R_GLOB2LOCFLAGS: regex = R_GLOB2LOCFLAGS[0].sub(R_GLOB2LOCFLAGS[1], regex) try: self._regexObj = re.compile(regex, re.MULTILINE if multiline else 0) self._regex = regex self._altValues = [] self._tupleValues = [] for k in [k for k in self._regexObj.groupindex if len(k) > len(COMPLNAME_PRE[0])]: n = COMPLNAME_CRE.match(k) if n: g, n = n.group(1), mapTag2Opt(n.group(2)) if g == ALTNAME_PRE: self._altValues.append((k,n)) else: self._tupleValues.append((k,n)) self._altValues.sort() self._tupleValues.sort() self._altValues = self._altValues if len(self._altValues) else None self._tupleValues = self._tupleValues if len(self._tupleValues) else None except sre_constants.error as e: raise RegexException("Unable to compile regular expression '%s':\n%s" % (regex, e)) # set fetch handler depending on presence of alternate (or tuple) tags: self.getGroups = self._getGroupsWithAlt if (self._altValues or self._tupleValues) else self._getGroups def __str__(self): return "%s(%r)" % (self.__class__.__name__, self._regex) ## # Replaces "", "", "", "" with default regular expression for host # # (see gh-1374 for the discussion about other candidates) # @return the replaced regular expression as string @staticmethod def _resolveHostTag(regex, useDns="yes"): openTags = dict() props = { 'nl': 0, # new lines counter by tag; } # tag interpolation callable: def substTag(m): tag = m.group() tn = tag[1:-1] # 3 groups instead of - separated ipv4, ipv6 and host (dns) if tn == "HOST": return R_HOST[RI_HOST if useDns not in ("no",) else RI_ADDR] # replace "" with regular expression for multiple lines (by buffering with maxlines) if tn == "SKIPLINES": nl = props['nl'] props['nl'] = nl + 1 return r"\n(?P(?:(?:.*\n)*?))" % (nl,) # static replacement from RH4TAG: try: return RH4TAG[tn] except KeyError: pass # (begin / end tag) for customizable expressions, additionally used as # user custom tags (match will be stored in ticket data, can be used in actions): m = FCUSTNAME_CRE.match(tn) if m: # match F-... m = m.groups() tn = m[1] # close tag: if m[0]: # check it was already open: if openTags.get(tn): return ")" return tag; # tag not opened, use original # open tag: openTags[tn] = 1 # if should be mapped: tn = mapTag2Opt(tn) return "(?P<%s>" % (tn,) # original, no replacement: return tag # substitute tags: return FTAG_CRE.sub(substTag, regex) ## # Gets the regular expression. # # The effective regular expression used is returned. # @return the regular expression def getRegex(self): return self._regex ## # Returns string buffer using join of the tupleLines. # @staticmethod def _tupleLinesBuf(tupleLines): return "\n".join(["".join(v[::2]) for v in tupleLines]) + "\n" ## # Searches the regular expression. # # Sets an internal cache (match object) in order to avoid searching for # the pattern again. This method must be called before calling any other # method of this object. # @param a list of tupples. The tupples are ( prematch, datematch, postdatematch ) def search(self, tupleLines, orgLines=None): buf = tupleLines if not isinstance(tupleLines, str): buf = Regex._tupleLinesBuf(tupleLines) self._matchCache = self._regexObj.search(buf) if self._matchCache: if orgLines is None: orgLines = tupleLines # if single-line: if len(orgLines) <= 1: self._matchedTupleLines = orgLines self._unmatchedTupleLines = [] else: # Find start of the first line where the match was found try: matchLineStart = self._matchCache.string.rindex( "\n", 0, self._matchCache.start() +1 ) + 1 except ValueError: matchLineStart = 0 # Find end of the last line where the match was found try: matchLineEnd = self._matchCache.string.index( "\n", self._matchCache.end() - 1) + 1 except ValueError: matchLineEnd = len(self._matchCache.string) lineCount1 = self._matchCache.string.count( "\n", 0, matchLineStart) lineCount2 = self._matchCache.string.count( "\n", 0, matchLineEnd) self._matchedTupleLines = orgLines[lineCount1:lineCount2] self._unmatchedTupleLines = orgLines[:lineCount1] n = 0 for skippedLine in self.getSkippedLines(): for m, matchedTupleLine in enumerate( self._matchedTupleLines[n:]): if "".join(matchedTupleLine[::2]) == skippedLine: self._unmatchedTupleLines.append( self._matchedTupleLines.pop(n+m)) n += m break self._unmatchedTupleLines.extend(orgLines[lineCount2:]) # Checks if the previous call to search() matched. # # @return True if a match was found, False otherwise def hasMatched(self): if self._matchCache: return True else: return False ## # Returns all matched groups. # def _getGroups(self): return self._matchCache.groupdict() def _getGroupsWithAlt(self): fail = self._matchCache.groupdict() #fail = fail.copy() # merge alternate values (e. g. 'alt_user_1' -> 'user' or 'alt_host' -> 'host'): if self._altValues: for k,n in self._altValues: v = fail.get(k) if v and not fail.get(n): fail[n] = v # combine tuple values (e. g. 'id', 'tuple_id' ... 'tuple_id_N' -> 'id'): if self._tupleValues: for k,n in self._tupleValues: v = fail.get(k) t = fail.get(n) if isinstance(t, tuple): t += (v,) else: t = (t,v,) fail[n] = t return fail def getGroups(self): # pragma: no cover - abstract function (replaced in __init__) pass ## # Returns skipped lines. # # This returns skipped lines captured by the tag. # @return list of skipped lines def getSkippedLines(self): if not self._matchCache: return [] skippedLines = "" n = 0 while True: try: if self._matchCache.group("skiplines%i" % n) is not None: skippedLines += self._matchCache.group("skiplines%i" % n) n += 1 except IndexError: break # KeyError is because of PyPy issue1665 affecting pypy <= 2.2.1 except KeyError: if 'PyPy' not in sys.version: # pragma: no cover - not sure this is even reachable raise break return skippedLines.splitlines(False) ## # Returns unmatched lines. # # This returns unmatched lines including captured by the tag. # @return list of unmatched lines def getUnmatchedTupleLines(self): if not self.hasMatched(): return [] else: return self._unmatchedTupleLines def getUnmatchedLines(self): if not self.hasMatched(): return [] else: return ["".join(line) for line in self._unmatchedTupleLines] ## # Returns matched lines. # # This returns matched lines by excluding those captured # by the tag. # @return list of matched lines def getMatchedTupleLines(self): if not self.hasMatched(): return [] else: return self._matchedTupleLines def getMatchedLines(self): if not self.hasMatched(): return [] else: return ["".join(line) for line in self._matchedTupleLines] ## # Exception dedicated to the class Regex. class RegexException(Exception): pass ## # Groups used as failure identifier. # # The order of this tuple is important while searching for failure-id # FAILURE_ID_GROPS = ("fid", "ip4", "ip6", "dns") # Additionally allows multi-line failure-id (used for wrapping e. g. conn-id to host) # FAILURE_ID_PRESENTS = FAILURE_ID_GROPS + ("mlfid",) ## # Regular expression class. # # This class represents a regular expression with its compiled version. class FailRegex(Regex): ## # Constructor. # # Creates a new object. This method can throw RegexException in order to # avoid construction of invalid object. # @param value the regular expression def __init__(self, regex, prefRegex=None, **kwargs): # Initializes the parent. Regex.__init__(self, regex, **kwargs) # Check for group "dns", "ip4", "ip6", "fid" if (not [grp for grp in FAILURE_ID_PRESENTS if grp in self._regexObj.groupindex] and (prefRegex is None or not [grp for grp in FAILURE_ID_PRESENTS if grp in prefRegex._regexObj.groupindex]) ): raise RegexException("No failure-id group in '%s'" % self._regex) ## # Returns the matched failure id. # # This corresponds to the pattern matched by the named group from given groups. # @return the matched failure-id def getFailID(self, groups=FAILURE_ID_GROPS): fid = None for grp in groups: try: fid = self._matchCache.group(grp) except (IndexError, KeyError): continue if fid is not None: break if fid is None: # Gets a few information. s = self._matchCache.string r = self._matchCache.re raise RegexException("No group found in '%s' using '%s'" % (s, r)) return str(fid) ## # Returns the matched host. # # This corresponds to the pattern matched by the named group "ip4", "ip6" or "dns". # @return the matched host def getHost(self): return self.getFailID(("ip4", "ip6", "dns")) def getIP(self): fail = self.getGroups() return IPAddr(self.getFailID(("ip4", "ip6")), int(fail.get("cidr") or IPAddr.CIDR_UNSPEC)) server/filter.py000064400000137054152231502160007720 0ustar00# 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. __author__ = "Cyril Jaquier and Fail2Ban Contributors" __copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2013 Yaroslav Halchenko" __license__ = "GPL" import codecs import datetime import fcntl import logging import os import re import sys import time from .actions import Actions from .failmanager import FailManagerEmpty, FailManager from .ipdns import DNSUtils, IPAddr from .observer import Observers from .ticket import FailTicket from .jailthread import JailThread from .datedetector import DateDetector, validateTimeZone from .mytime import MyTime from .failregex import FailRegex, Regex, RegexException from .action import CommandAction from .utils import Utils from ..helpers import getLogger, PREFER_ENC # Gets the instance of the logger. logSys = getLogger(__name__) ## # Log reader class. # # This class reads a log file and detects login failures or anything else # that matches a given regular expression. This class is instantiated by # a Jail object. class Filter(JailThread): ## # Constructor. # # Initialize the filter object with default values. # @param jail the jail object def __init__(self, jail, useDns='warn'): JailThread.__init__(self) ## The jail which contains this filter. self.jail = jail ## The failures manager. self.failManager = FailManager() ## Regular expression pre-filtering matching the failures. self.__prefRegex = None ## The regular expression list matching the failures. self.__failRegex = list() ## The regular expression list with expressions to ignore. self.__ignoreRegex = list() ## Use DNS setting self.setUseDns(useDns) ## The amount of time to look back. self.__findTime = 600 ## Ignore own IPs flag: self.__ignoreSelf = True ## The ignore IP list. self.__ignoreIpSet = set() self.__ignoreIpList = [] ## External command self.__ignoreCommand = False ## Cache for ignoreip: self.__ignoreCache = None ## Size of line buffer self.__lineBufferSize = 1 ## Line buffer self.__lineBuffer = [] ## Store last time stamp, applicable for multi-line self.__lastTimeText = "" self.__lastDate = None ## Next service (cleanup) time self.__nextSvcTime = -(1<<63) ## if set, treat log lines without explicit time zone to be in this time zone self.__logtimezone = None ## Default or preferred encoding (to decode bytes from file or journal): self.__encoding = PREFER_ENC ## Cache temporary holds failures info (used by multi-line for wrapping e. g. conn-id to host): self.__mlfidCache = None ## Error counter (protected, so can be used in filter implementations) ## if it reached 100 (at once), run-cycle will go idle self._errors = 0 ## Next time to update log or journal position in database: self._nextUpdateTM = 0 ## Pending updates (must be executed at next update time or during stop): self._pendDBUpdates = {} ## return raw host (host is not dns): self.returnRawHost = False ## check each regex (used for test purposes): self.checkAllRegex = False ## avoid finding of pending failures (without ID/IP, used in fail2ban-regex): self.ignorePending = True ## callback called on ignoreregex match : self.onIgnoreRegex = None ## if true ignores obsolete failures (failure time < now - findTime): self.checkFindTime = True ## shows that filter is in operation mode (processing new messages): self.inOperation = True ## Ticks counter self.ticks = 0 ## Processed lines counter self.procLines = 0 ## Thread name: self.name="f2b/f."+self.jailName self.dateDetector = DateDetector() logSys.debug("Created %s", self) def __repr__(self): return "%s(%r)" % (self.__class__.__name__, self.jail) @property def jailName(self): return (self.jail is not None and self.jail.name or "~jailless~") def clearAllParams(self): """ Clear all lists/dicts parameters (used by reloading) """ self.delFailRegex() self.delIgnoreRegex() self.delIgnoreIP() def reload(self, begin=True): """ Begin or end of reloading resp. refreshing of all parameters """ if begin: self.clearAllParams() if hasattr(self, 'getLogPaths'): self._reload_logs = dict((k, 1) for k in self.getLogPaths()) else: if hasattr(self, '_reload_logs'): # if it was not reloaded - remove obsolete log file: for path in self._reload_logs: self.delLogPath(path) delattr(self, '_reload_logs') @property def mlfidCache(self): if self.__mlfidCache: return self.__mlfidCache self.__mlfidCache = Utils.Cache(maxCount=100, maxTime=5*60) return self.__mlfidCache @property def prefRegex(self): return self.__prefRegex @prefRegex.setter def prefRegex(self, value): if value: self.__prefRegex = Regex(value, useDns=self.__useDns) else: self.__prefRegex = None ## # Add a regular expression which matches the failure. # # The regular expression can also match any other pattern than failures # and thus can be used for many purporse. # @param value the regular expression def addFailRegex(self, value): multiLine = self.__lineBufferSize > 1 try: regex = FailRegex(value, prefRegex=self.__prefRegex, multiline=multiLine, useDns=self.__useDns) self.__failRegex.append(regex) except RegexException as e: logSys.error(e) raise e def delFailRegex(self, index=None): try: # clear all: if index is None: del self.__failRegex[:] return # delete by index: del self.__failRegex[index] except IndexError: logSys.error("Cannot remove regular expression. Index %d is not " "valid", index) ## # Get the regular expressions as list. # # @return the regular expression list def getFailRegex(self): return [regex.getRegex() for regex in self.__failRegex] ## # Add the regular expression which matches the failure. # # The regular expression can also match any other pattern than failures # and thus can be used for many purpose. # @param value the regular expression def addIgnoreRegex(self, value): try: regex = Regex(value, useDns=self.__useDns) self.__ignoreRegex.append(regex) except RegexException as e: logSys.error(e) raise e def delIgnoreRegex(self, index=None): try: # clear all: if index is None: del self.__ignoreRegex[:] return # delete by index: del self.__ignoreRegex[index] except IndexError: logSys.error("Cannot remove regular expression. Index %d is not " "valid", index) ## # Get the regular expression which matches the failure. # # @return the regular expression def getIgnoreRegex(self): ignoreRegex = list() for regex in self.__ignoreRegex: ignoreRegex.append(regex.getRegex()) return ignoreRegex ## # Set the Use DNS mode # @param value the usedns mode def setUseDns(self, value): if isinstance(value, bool): value = {True: 'yes', False: 'no'}[value] value = value.lower() # must be a string by now if not (value in ('yes', 'warn', 'no', 'raw')): logSys.error("Incorrect value %r specified for usedns. " "Using safe 'no'", value) value = 'no' logSys.debug("Setting usedns = %s for %s", value, self) self.__useDns = value ## # Get the usedns mode # @return the usedns mode def getUseDns(self): return self.__useDns ## # Set the time needed to find a failure. # # This value tells the filter how long it has to take failures into # account. # @param value the time def setFindTime(self, value): value = MyTime.str2seconds(value) self.__findTime = value self.failManager.setMaxTime(value) logSys.info(" findtime: %s", value) ## # Get the time needed to find a failure. # # @return the time def getFindTime(self): return self.__findTime ## # Set the date detector pattern, removing Defaults # # @param pattern the date template pattern def setDatePattern(self, pattern): if pattern is None: self.dateDetector = None return else: dd = DateDetector() dd.default_tz = self.__logtimezone if not isinstance(pattern, (list, tuple)): pattern = list(filter(bool, list(map(str.strip, re.split('\n+', pattern))))) for pattern in pattern: dd.appendTemplate(pattern) self.dateDetector = dd ## # Get the date detector pattern, or Default Detectors if not changed # # @return pattern of the date template pattern def getDatePattern(self): if self.dateDetector is not None: templates = self.dateDetector.templates # lazy template init, by first match if not len(templates) or len(templates) > 2: return None, "Default Detectors" elif len(templates): if hasattr(templates[0], "pattern"): pattern = templates[0].pattern else: pattern = None return pattern, templates[0].name return None ## # Set the log default time zone # # @param tz the symbolic timezone (for now fixed offset only: UTC[+-]HHMM) def setLogTimeZone(self, tz): validateTimeZone(tz); # avoid setting of wrong value, but hold original self.__logtimezone = tz if self.dateDetector: self.dateDetector.default_tz = self.__logtimezone ## # Get the log default timezone # # @return symbolic timezone (a string) def getLogTimeZone(self): return self.__logtimezone ## # Set the maximum retry value. # # @param value the retry value def setMaxRetry(self, value): self.failManager.setMaxRetry(value) logSys.info(" maxRetry: %s", value) ## # Get the maximum retry value. # # @return the retry value def getMaxRetry(self): return self.failManager.getMaxRetry() ## # Set the maximum line buffer size. # # @param value the line buffer size def setMaxLines(self, value): if int(value) <= 0: raise ValueError("maxlines must be integer greater than zero") self.__lineBufferSize = int(value) logSys.info(" maxLines: %i", self.__lineBufferSize) ## # Get the maximum line buffer size. # # @return the line buffer size def getMaxLines(self): return self.__lineBufferSize ## # Set the log file encoding # # @param encoding the encoding used with log files def setLogEncoding(self, encoding): if encoding.lower() == "auto": encoding = PREFER_ENC codecs.lookup(encoding) # Raise LookupError if invalid codec self.__encoding = encoding logSys.info(" encoding: %s", encoding) return encoding ## # Get the log file encoding # # @return log encoding value def getLogEncoding(self): return self.__encoding ## # Main loop. # # This function is the main loop of the thread. It checks if the # file has been modified and looks for failures. # @return True when the thread exits nicely def run(self): # pragma: no cover raise Exception("run() is abstract") ## # External command, for ignoredips # @property def ignoreCommand(self): return self.__ignoreCommand @ignoreCommand.setter def ignoreCommand(self, command): self.__ignoreCommand = command ## # Cache parameters for ignoredips # @property def ignoreCache(self): return [self.__ignoreCache[0], self.__ignoreCache[1].maxCount, self.__ignoreCache[1].maxTime] \ if self.__ignoreCache else None @ignoreCache.setter def ignoreCache(self, command): if command: self.__ignoreCache = command['key'], Utils.Cache( maxCount=int(command.get('max-count', 100)), maxTime=MyTime.str2seconds(command.get('max-time', 5*60)) ) else: self.__ignoreCache = None def performBan(self, ip=None): """Performs a ban for IPs (or given ip) that are reached maxretry of the jail.""" while True: try: ticket = self.failManager.toBan(ip) except FailManagerEmpty: break self.jail.putFailTicket(ticket) if ip: break self.performSvc() def performSvc(self, force=False): """Performs a service tasks (clean failure list).""" tm = MyTime.time() # avoid too early clean up: if force or tm >= self.__nextSvcTime: self.__nextSvcTime = tm + 5 # clean up failure list: self.failManager.cleanup(tm) def addAttempt(self, ip, *matches): """Generate a failed attempt for ip""" if not isinstance(ip, IPAddr): ip = IPAddr(ip) matches = list(matches) # tuple to list # Generate the failure attempt for the IP: unixTime = MyTime.time() ticket = FailTicket(ip, unixTime, matches=matches) logSys.info( "[%s] Attempt %s - %s", self.jailName, ip, datetime.datetime.fromtimestamp(unixTime).strftime("%Y-%m-%d %H:%M:%S") ) attempts = self.failManager.addFailure(ticket, len(matches) or 1) # Perform the ban if this attempt is resulted to: if attempts >= self.failManager.getMaxRetry(): self.performBan(ip) return 1 ## # Ignore own IP/DNS. # @property def ignoreSelf(self): return self.__ignoreSelf @ignoreSelf.setter def ignoreSelf(self, value): self.__ignoreSelf = value ## # Add an IP/DNS to the ignore list. # # IP addresses in the ignore list are not taken into account # when finding failures. CIDR mask and DNS are also accepted. # @param ip IP address to ignore def addIgnoreIP(self, ipstr): # An empty string is always false if ipstr == "": return # Create IP address object ip = IPAddr(ipstr) # Avoid exact duplicates if ip in self.__ignoreIpSet or ip in self.__ignoreIpList: logSys.log(logging.MSG, " Ignore duplicate %r (%r), already in ignore list", ip, ipstr) return # log and append to ignore list logSys.debug(" Add %r to ignore list (%r)", ip, ipstr) # if single IP (not DNS or a subnet) add to set, otherwise to list: if ip.isSingle: self.__ignoreIpSet.add(ip) else: self.__ignoreIpList.append(ip) def delIgnoreIP(self, ip=None): # clear all: if ip is None: self.__ignoreIpSet.clear() del self.__ignoreIpList[:] return # delete by ip: logSys.debug(" Remove %r from ignore list", ip) if ip in self.__ignoreIpSet: self.__ignoreIpSet.remove(ip) else: self.__ignoreIpList.remove(ip) def logIgnoreIp(self, ip, log_ignore, ignore_source="unknown source"): if log_ignore: logSys.info("[%s] Ignore %s by %s", self.jailName, ip, ignore_source) def getIgnoreIP(self): return self.__ignoreIpList + list(self.__ignoreIpSet) ## # Check if IP address/DNS is in the ignore list. # # Check if the given IP address matches an IP address/DNS or a CIDR # mask in the ignore list. # @param ip IP address object or ticket # @return True if IP address is in ignore list def inIgnoreIPList(self, ip, log_ignore=True): ticket = None if isinstance(ip, FailTicket): ticket = ip ip = ticket.getID() elif not isinstance(ip, IPAddr): ip = IPAddr(ip) return self._inIgnoreIPList(ip, ticket, log_ignore) def _inIgnoreIPList(self, ip, ticket, log_ignore=True): aInfo = None # cached ? if self.__ignoreCache: key, c = self.__ignoreCache if ticket: aInfo = Actions.ActionInfo(ticket, self.jail) key = CommandAction.replaceDynamicTags(key, aInfo) else: aInfo = { 'ip': ip } key = CommandAction.replaceTag(key, aInfo) v = c.get(key) if v is not None: return v # check own IPs should be ignored and 'ip' is self IP: if self.__ignoreSelf and ip in DNSUtils.getSelfIPs(): self.logIgnoreIp(ip, log_ignore, ignore_source="ignoreself rule") if self.__ignoreCache: c.set(key, True) return True # check if the IP is covered by ignore IP (in set or in subnet/dns): if ip in self.__ignoreIpSet: self.logIgnoreIp(ip, log_ignore, ignore_source="ip") return True for net in self.__ignoreIpList: if ip.isInNet(net): self.logIgnoreIp(ip, log_ignore, ignore_source=("ip" if net.isValid else "dns")) if self.__ignoreCache: c.set(key, True) return True if self.__ignoreCommand: if ticket: if not aInfo: aInfo = Actions.ActionInfo(ticket, self.jail) command = CommandAction.replaceDynamicTags(self.__ignoreCommand, aInfo) else: if not aInfo: aInfo = { 'ip': ip } command = CommandAction.replaceTag(self.__ignoreCommand, aInfo) logSys.debug('ignore command: %s', command) ret, ret_ignore = CommandAction.executeCmd(command, success_codes=(0, 1)) ret_ignore = ret and ret_ignore == 0 self.logIgnoreIp(ip, log_ignore and ret_ignore, ignore_source="command") if self.__ignoreCache: c.set(key, ret_ignore) return ret_ignore if self.__ignoreCache: c.set(key, False) return False def _logWarnOnce(self, nextLTM, *args): """Log some issue as warning once per day, otherwise level 7""" if MyTime.time() < getattr(self, nextLTM, 0): if logSys.getEffectiveLevel() <= 7: logSys.log(7, *(args[0])) else: setattr(self, nextLTM, MyTime.time() + 24*60*60) for args in args: logSys.warning('[%s] ' + args[0], self.jailName, *args[1:]) def processLine(self, line, date=None): """Split the time portion from log msg and return findFailures on them """ logSys.log(7, "Working on line %r", line) noDate = False if date: tupleLine = line line = "".join(line) self.__lastTimeText = tupleLine[1] self.__lastDate = date else: # try to parse date: timeMatch = self.dateDetector.matchTime(line) m = timeMatch[0] if m: s = m.start(1) e = m.end(1) m = line[s:e] tupleLine = (line[:s], m, line[e:]) if m: # found and not empty - retrive date: date = self.dateDetector.getTime(m, timeMatch) if date is not None: # Lets get the time part date = date[0] self.__lastTimeText = m self.__lastDate = date else: logSys.error("findFailure failed to parse timeText: %s", m) # matched empty value - date is optional or not available - set it to last known or now: elif self.__lastDate and self.__lastDate > MyTime.time() - 60: # set it to last known: tupleLine = ("", self.__lastTimeText, line) date = self.__lastDate else: # set it to now: date = MyTime.time() else: tupleLine = ("", "", line) # still no date - try to use last known: if date is None: noDate = True if self.__lastDate and self.__lastDate > MyTime.time() - 60: tupleLine = ("", self.__lastTimeText, line) date = self.__lastDate elif self.checkFindTime and self.inOperation: date = MyTime.time() if self.checkFindTime and date is not None: # if in operation (modifications have been really found): if self.inOperation: # if weird date - we'd simulate now for timeing issue (too large deviation from now): delta = int(date - MyTime.time()) if abs(delta) > 60: # log timing issue as warning once per day: self._logWarnOnce("_next_simByTimeWarn", ("Detected a log entry %s %s the current time in operation mode. " "This looks like a %s problem. Treating such entries as if they just happened.", MyTime.seconds2str(abs(delta)), "before" if delta < 0 else "after", "latency" if -3300 <= delta < 0 else "timezone" ), ("Please check a jail for a timing issue. Line with odd timestamp: %s", line)) # simulate now as date: date = MyTime.time() self.__lastDate = date else: # in initialization (restore) phase, if too old - ignore: if date < MyTime.time() - self.getFindTime(): # log time zone issue as warning once per day: self._logWarnOnce("_next_ignByTimeWarn", ("Ignoring all log entries older than %ss; these are probably" + " messages generated while fail2ban was not running.", self.getFindTime()), ("Please check a jail for a timing issue. Line with odd timestamp: %s", line)) # ignore - too old (obsolete) entry: return [] # save last line (lazy convert of process line tuple to string on demand): self.processedLine = lambda: "".join(tupleLine[::2]) return self.findFailure(tupleLine, date, noDate=noDate) def processLineAndAdd(self, line, date=None): """Processes the line for failures and populates failManager """ try: for (_, ip, unixTime, fail) in self.processLine(line, date): logSys.debug("Processing line with time:%s and ip:%s", unixTime, ip) # ensure the time is not in the future, e. g. by some estimated (assumed) time: if self.checkFindTime and unixTime > MyTime.time(): unixTime = MyTime.time() tick = FailTicket(ip, unixTime, data=fail) if self._inIgnoreIPList(ip, tick): continue logSys.info( "[%s] Found %s - %s", self.jailName, ip, MyTime.time2str(unixTime) ) attempts = self.failManager.addFailure(tick) # avoid RC on busy filter (too many failures) - if attempts for IP/ID reached maxretry, # we can speedup ban, so do it as soon as possible: if attempts >= self.failManager.getMaxRetry(): self.performBan(ip) # report to observer - failure was found, for possibly increasing of it retry counter (asynchronous) if Observers.Main is not None: Observers.Main.add('failureFound', self.jail, tick) self.procLines += 1 # every 100 lines check need to perform service tasks: if self.procLines % 100 == 0: self.performSvc() # reset (halve) error counter (successfully processed line): if self._errors: self._errors //= 2 except Exception as e: logSys.error("Failed to process line: %r, caught exception: %r", line, e, exc_info=logSys.getEffectiveLevel()<=logging.DEBUG) # incr common error counter: self.commonError() def commonError(self, reason="common", exc=None): # incr error counter, stop processing (going idle) after 100th error : self._errors += 1 # sleep a little bit (to get around time-related errors): time.sleep(self.sleeptime) if self._errors >= 100: logSys.error("Too many errors at once (%s), going idle", self._errors) self._errors //= 2 self.idle = True def _ignoreLine(self, buf, orgBuffer, failRegex=None): # if multi-line buffer - use matched only, otherwise (single line) - original buf: if failRegex and self.__lineBufferSize > 1: orgBuffer = failRegex.getMatchedTupleLines() buf = Regex._tupleLinesBuf(orgBuffer) # search ignored: fnd = None for ignoreRegexIndex, ignoreRegex in enumerate(self.__ignoreRegex): ignoreRegex.search(buf, orgBuffer) if ignoreRegex.hasMatched(): fnd = ignoreRegexIndex logSys.log(7, " Matched ignoreregex %d and was ignored", fnd) if self.onIgnoreRegex: self.onIgnoreRegex(fnd, ignoreRegex) # remove ignored match: if not self.checkAllRegex or self.__lineBufferSize > 1: # todo: check ignoreRegex.getUnmatchedTupleLines() would be better (fix testGetFailuresMultiLineIgnoreRegex): if failRegex: self.__lineBuffer = failRegex.getUnmatchedTupleLines() if not self.checkAllRegex: break return fnd def _updateUsers(self, fail, user=()): users = fail.get('users') # only for regex contains user: if user: if not users: fail['users'] = users = set() users.add(user) return users return users def _mergeFailure(self, mlfid, fail, failRegex): mlfidFail = self.mlfidCache.get(mlfid) if self.__mlfidCache else None users = None nfflgs = 0 if fail.get("mlfgained"): nfflgs |= (8|1) if not fail.get('nofail'): fail['nofail'] = fail["mlfgained"] elif fail.get('nofail'): nfflgs |= 1 if fail.pop('mlfforget', None): nfflgs |= 2 # if multi-line failure id (connection id) known: if mlfidFail: mlfidGroups = mlfidFail[1] # update users set (hold all users of connect): users = self._updateUsers(mlfidGroups, fail.get('user')) # be sure we've correct current state ('nofail' and 'mlfgained' only from last failure) if mlfidGroups.pop('nofail', None): nfflgs |= 4 if mlfidGroups.pop('mlfgained', None): nfflgs |= 4 # gained resets all pending failures (retaining users to check it later) if nfflgs & 8: mlfidGroups.pop('mlfpending', None) # if we had no pending failures then clear the matches (they are already provided): if (nfflgs & 4) == 0 and not mlfidGroups.get('mlfpending', 0): mlfidGroups.pop("matches", None) # overwrite multi-line failure with all values, available in fail: mlfidGroups.update(((k,v) for k,v in fail.items() if v is not None)) # new merged failure data: fail = mlfidGroups # if forget (disconnect/reset) - remove cached entry: if nfflgs & 2: self.mlfidCache.unset(mlfid) elif not (nfflgs & 2): # not mlfforget users = self._updateUsers(fail, fail.get('user')) mlfidFail = [self.__lastDate, fail] self.mlfidCache.set(mlfid, mlfidFail) # check users in order to avoid reset failure by multiple logon-attempts: if fail.pop('mlfpending', 0) or users and len(users) > 1: # we've pending failures or new user, reset 'nofail' because of failures or multiple users attempts: fail.pop('nofail', None) fail.pop('mlfgained', None) nfflgs &= ~(8|1) # reset nofail and gained # merge matches: if (nfflgs & 1) == 0: # current nofail state (corresponding users) m = fail.pop("nofail-matches", []) m += fail.get("matches", []) if (nfflgs & 8) == 0: # no gain signaled m += failRegex.getMatchedTupleLines() fail["matches"] = m elif (nfflgs & 3) == 1: # not mlfforget and nofail: fail["nofail-matches"] = fail.get("nofail-matches", []) + failRegex.getMatchedTupleLines() # return merged: return fail ## # Finds the failure in a line given split into time and log parts. # # Uses the failregex pattern to find it and timeregex in order # to find the logging time. # @return a dict with IP and timestamp. def findFailure(self, tupleLine, date, noDate=False): failList = list() ll = logSys.getEffectiveLevel() defcidr = IPAddr.CIDR_UNSPEC if self.__useDns == "raw" or self.returnRawHost: defcidr = IPAddr.CIDR_RAW if self.__lineBufferSize > 1: self.__lineBuffer.append(tupleLine) orgBuffer = self.__lineBuffer = self.__lineBuffer[-self.__lineBufferSize:] else: orgBuffer = self.__lineBuffer = [tupleLine] if ll <= 5: logSys.log(5, "Looking for match of %r", orgBuffer) buf = Regex._tupleLinesBuf(orgBuffer) # Checks if we must ignore this line (only if fewer ignoreregex than failregex). if self.__ignoreRegex and len(self.__ignoreRegex) < len(self.__failRegex) - 2: if self._ignoreLine(buf, orgBuffer) is not None: # The ignoreregex matched. Return. return failList # Pre-filter fail regex (if available): preGroups = {} if self.__prefRegex: if ll <= 5: logSys.log(5, " Looking for prefregex %r", self.__prefRegex.getRegex()) self.__prefRegex.search(buf, orgBuffer) if not self.__prefRegex.hasMatched(): if ll <= 5: logSys.log(5, " Prefregex not matched") return failList preGroups = self.__prefRegex.getGroups() if ll <= 7: logSys.log(7, " Pre-filter matched %s", preGroups) repl = preGroups.pop('content', None) # Content replacement: if repl: self.__lineBuffer, buf = [('', '', repl)], None # Iterates over all the regular expressions. for failRegexIndex, failRegex in enumerate(self.__failRegex): try: # buffer from tuples if changed: if buf is None: buf = Regex._tupleLinesBuf(self.__lineBuffer) if ll <= 5: logSys.log(5, " Looking for failregex %d - %r", failRegexIndex, failRegex.getRegex()) failRegex.search(buf, orgBuffer) if not failRegex.hasMatched(): continue # current failure data (matched group dict): fail = failRegex.getGroups() # The failregex matched. if ll <= 7: logSys.log(7, " Matched failregex %d: %s", failRegexIndex, fail) # Checks if we must ignore this match. if self.__ignoreRegex and self._ignoreLine(buf, orgBuffer, failRegex) is not None: # The ignoreregex matched. Remove ignored match. buf = None if not self.checkAllRegex: break continue if noDate: self._logWarnOnce("_next_noTimeWarn", ("Found a match but no valid date/time found for %r.", tupleLine[1]), ("Match without a timestamp: %s", "\n".join(failRegex.getMatchedLines())), ("Please try setting a custom date pattern (see man page jail.conf(5)).",) ) if date is None and self.checkFindTime: continue # we should check all regex (bypass on multi-line, otherwise too complex): if not self.checkAllRegex or self.__lineBufferSize > 1: self.__lineBuffer, buf = failRegex.getUnmatchedTupleLines(), None # merge data if multi-line failure: cidr = defcidr raw = (defcidr == IPAddr.CIDR_RAW) if preGroups: currFail, fail = fail, preGroups.copy() fail.update(currFail) # first try to check we have mlfid case (caching of connection id by multi-line): mlfid = fail.get('mlfid') if mlfid is not None: fail = self._mergeFailure(mlfid, fail, failRegex) # bypass if no-failure case: if fail.get('nofail'): if ll <= 7: logSys.log(7, "Nofail by mlfid %r in regex %s: %s", mlfid, failRegexIndex, fail.get('mlfforget', "waiting for failure")) if not self.checkAllRegex: return failList else: # matched lines: fail["matches"] = fail.get("matches", []) + failRegex.getMatchedTupleLines() # failure-id: fid = fail.get('fid') # ip-address or host: ip = fail.get('ip4') if ip is not None: cidr = int(fail.get('cidr') or IPAddr.FAM_IPv4) raw = True else: ip = fail.get('ip6') if ip is not None: cidr = int(fail.get('cidr') or IPAddr.FAM_IPv6) raw = True else: ip = fail.get('dns') if ip is None: # first try to check we have mlfid case (cache connection id): if fid is None and mlfid is None: # if no failure-id also (obscure case, wrong regex), throw error inside getFailID: fid = failRegex.getFailID() ip = fid raw = True # if mlfid case (not failure): if ip is None: if ll <= 7: logSys.log(7, "No failure-id by mlfid %r in regex %s: %s", mlfid, failRegexIndex, fail.get('mlfforget', "waiting for identifier")) fail['mlfpending'] = 1; # mark failure is pending if not self.checkAllRegex and self.ignorePending: return failList fids = [None] # if raw - add single ip or failure-id, # otherwise expand host to multiple ips using dns (or ignore it if not valid): elif raw: # check ip/host equal failure-id, if not - failure with complex id: if fid is None or fid == ip: fid = IPAddr(ip, cidr) else: fail['ip'] = IPAddr(ip, cidr) fid = IPAddr(fid, defcidr) fids = [fid] # otherwise, try to use dns conversion: else: fids = DNSUtils.textToIp(ip, self.__useDns) # if checkAllRegex we must make a copy (to be sure next RE doesn't change merged/cached failure): if self.checkAllRegex and mlfid is not None: fail = fail.copy() # append failure with match to the list: for fid in fids: failList.append([failRegexIndex, fid, date, fail]) if not self.checkAllRegex: break except RegexException as e: # pragma: no cover - unsure if reachable logSys.error(e) return failList def status(self, flavor="basic"): """Status of failures detected by filter. """ ret = [("Currently failed", self.failManager.size()), ("Total failed", self.failManager.getFailTotal())] return ret class FileFilter(Filter): def __init__(self, jail, **kwargs): Filter.__init__(self, jail, **kwargs) ## The log file path. self.__logs = dict() self.__autoSeek = dict() ## # Add a log file path # # @param path log file path def addLogPath(self, path, tail=False, autoSeek=True): if path in self.__logs: if hasattr(self, '_reload_logs') and path in self._reload_logs: del self._reload_logs[path] else: logSys.error(path + " already exists") else: log = FileContainer(path, self.getLogEncoding(), tail) db = self.jail.database if db is not None: lastpos = db.addLog(self.jail, log) if lastpos and not tail: log.setPos(lastpos) self.__logs[path] = log logSys.info("Added logfile: %r (pos = %s, hash = %s)" , path, log.getPos(), log.getHash()) if autoSeek and not tail: self.__autoSeek[path] = autoSeek self._addLogPath(path) # backend specific def _addLogPath(self, path): # nothing to do by default # to be overridden by backends pass ## # Delete a log path # # @param path the log file to delete def delLogPath(self, path): try: log = self.__logs.pop(path) except KeyError: return logSys.info("Removed logfile: %r", path) self._delLogPath(path) return def _delLogPath(self, path): # pragma: no cover - overwritten function # nothing to do by default # to be overridden by backends pass ## # Get the log file names # # @return log paths def getLogPaths(self): return list(self.__logs.keys()) ## # Get the log containers # # @return log containers def getLogs(self): return list(self.__logs.values()) ## # Get the count of log containers # # @return count of log containers def getLogCount(self): return len(self.__logs) ## # Check whether path is already monitored. # # @param path The path # @return True if the path is already monitored else False def containsLogPath(self, path): return path in self.__logs ## # Set the log file encoding # # @param encoding the encoding used with log files def setLogEncoding(self, encoding): encoding = super(FileFilter, self).setLogEncoding(encoding) for log in self.__logs.values(): log.setEncoding(encoding) def getLog(self, path): return self.__logs.get(path, None) ## # Gets all the failure in the log file. # # Gets all the failure in the log file which are newer than # MyTime.time()-self.findTime. When a failure is detected, a FailTicket # is created and is added to the FailManager. def getFailures(self, filename, inOperation=None): if self.idle: return False log = self.getLog(filename) if log is None: logSys.error("Unable to get failures in %s", filename) return False # We should always close log (file), otherwise may be locked (log-rotate, etc.) try: # Try to open log file. try: has_content = log.open() # see http://python.org/dev/peps/pep-3151/ except IOError as e: logSys.error("Unable to open %s", filename) if e.errno != 2: # errno.ENOENT logSys.exception(e) return False except OSError as e: # pragma: no cover - requires race condition to trigger this logSys.error("Error opening %s", filename) logSys.exception(e) return False except Exception as e: # pragma: no cover - Requires implementation error in FileContainer to generate logSys.error("Internal error in FileContainer open method - please report as a bug to https://github.com/fail2ban/fail2ban/issues") logSys.exception(e) return False # seek to find time for first usage only (prevent performance decline with polling of big files) if self.__autoSeek: startTime = self.__autoSeek.pop(filename, None) if startTime: # if default, seek to "current time" - "find time": if isinstance(startTime, bool): startTime = MyTime.time() - self.getFindTime() # prevent completely read of big files first time (after start of service), # initial seek to start time using half-interval search algorithm: try: self.seekToTime(log, startTime) except Exception as e: # pragma: no cover logSys.error("Error during seek to start time in \"%s\"", filename) raise logSys.exception(e) return False if has_content: while not self.idle: line = log.readline() if not self.active: break; # jail has been stopped if line is None: # The jail reached the bottom, simply set in operation for this log # (since we are first time at end of file, growing is only possible after modifications): log.inOperation = True break # acquire in operation from log and process: self.inOperation = inOperation if inOperation is not None else log.inOperation self.processLineAndAdd(line) finally: log.close() if self.jail.database is not None: self._pendDBUpdates[log] = 1 if ( self.ticks % 100 == 0 or MyTime.time() >= self._nextUpdateTM or not self.active ): self._updateDBPending() self._nextUpdateTM = MyTime.time() + Utils.DEFAULT_SLEEP_TIME * 5 return True ## # Seeks to line with date (search using half-interval search algorithm), to start polling from it # def seekToTime(self, container, date, accuracy=3): fs = container.getFileSize() if logSys.getEffectiveLevel() <= logging.DEBUG: logSys.debug("Seek to find time %s (%s), file size %s", date, MyTime.time2str(date), fs) if not fs: return minp = container.getPos() maxp = fs tryPos = minp lastPos = -1 foundPos = 0 foundTime = None cntr = 0 unixTime = None movecntr = accuracy while maxp > minp: if tryPos is None: pos = int(minp + (maxp - minp) / 2) else: pos, tryPos = tryPos, None # because container seek will go to start of next line (minus CRLF): pos = max(0, pos-2) seekpos = pos = container.seek(pos) cntr += 1 # within next 5 lines try to find any legal datetime: lncntr = 5; dateTimeMatch = None nextp = None while True: line = container.readline(False) if line is None: break (timeMatch, template) = self.dateDetector.matchTime(line) if timeMatch: dateTimeMatch = self.dateDetector.getTime( line[timeMatch.start():timeMatch.end()], (timeMatch, template)) else: nextp = container.tell() if nextp > maxp: pos = seekpos break pos = nextp if not dateTimeMatch and lncntr: lncntr -= 1 continue break # not found at this step - stop searching if dateTimeMatch: unixTime = dateTimeMatch[0] if unixTime >= date: if foundTime is None or unixTime <= foundTime: foundPos = pos foundTime = unixTime if pos == maxp: pos = seekpos if pos < maxp: maxp = pos else: if foundTime is None or unixTime >= foundTime: foundPos = pos foundTime = unixTime if nextp is None: nextp = container.tell() pos = nextp if pos > minp: minp = pos # if we can't move (position not changed) if pos == lastPos: movecntr -= 1 if movecntr <= 0: break # we have found large area without any date matched # or end of search - try min position (because can be end of previous line): if minp != lastPos: lastPos = tryPos = minp continue break lastPos = pos # always use smallest pos, that could be found: foundPos = container.seek(minp, False) container.setPos(foundPos) if logSys.getEffectiveLevel() <= logging.DEBUG: logSys.debug("Position %s from %s, found time %s (%s) within %s seeks", lastPos, fs, foundTime, (MyTime.time2str(foundTime) if foundTime is not None else ''), cntr) def status(self, flavor="basic"): """Status of Filter plus files being monitored. """ ret = super(FileFilter, self).status(flavor=flavor) path = list(self.__logs.keys()) ret.append(("File list", path)) return ret def _updateDBPending(self): """Apply pending updates (log position) to database. """ db = self.jail.database while True: try: log, args = self._pendDBUpdates.popitem() except KeyError: break db.updateLog(self.jail, log) def onStop(self): """Stop monitoring of log-file(s). Invoked after run method. """ # ensure positions of pending logs are up-to-date: if self._pendDBUpdates and self.jail.database: self._updateDBPending() # stop files monitoring: for path in list(self.__logs.keys()): self.delLogPath(path) def stop(self): """Stop filter """ # normally onStop will be called automatically in thread after its run ends, # but for backwards compatibilities we'll invoke it in caller of stop method. self.onStop() # stop thread: super(Filter, self).stop() ## # FileContainer class. # # This class manages a file handler and takes care of log rotation detection. # In order to detect log rotation, the hash (MD5) of the first line of the file # is computed and compared to the previous hash of this line. try: import hashlib try: md5sum = hashlib.md5 # try to use it (several standards like FIPS forbid it): md5sum(' ').hexdigest() except: # pragma: no cover md5sum = hashlib.sha1 except ImportError: # pragma: no cover # hashlib was introduced in Python 2.5. For compatibility with those # elderly Pythons, import from md5 import md5 md5sum = md5.new class FileContainer: def __init__(self, filename, encoding, tail=False, doOpen=False): self.__filename = filename self.waitForLineEnd = True self.setEncoding(encoding) self.__tail = tail self.__handler = None self.__pos = 0 self.__pos4hash = 0 self.__hash = '' self.__hashNextTime = time.time() + 30 # Try to open the file. Raises an exception if an error occurred. handler = open(filename, 'rb') if doOpen: # fail2ban-regex only (don't need to reopen it and check for rotation) self.__handler = handler return try: stats = os.fstat(handler.fileno()) self.__ino = stats.st_ino if stats.st_size: firstLine = handler.readline() # first line available and contains new-line: if firstLine != firstLine.rstrip(b'\r\n'): # Computes the MD5 of the first line. self.__hash = md5sum(firstLine).hexdigest() # if tail mode scroll to the end of file if tail: handler.seek(0, 2) self.__pos = handler.tell() finally: handler.close() ## shows that log is in operation mode (expecting new messages only from here): self.inOperation = tail def __hash__(self): return hash(self.__filename) def __eq__(self, other): return (id(self) == id(other) or self.__filename == (other.__filename if isinstance(other, FileContainer) else other) ) def __repr__(self): return 'file-log:'+self.__filename def getFileName(self): return self.__filename def getFileSize(self): h = self.__handler if h is not None: stats = os.fstat(h.fileno()) return stats.st_size return os.path.getsize(self.__filename); def setEncoding(self, encoding): codecs.lookup(encoding) # Raises LookupError if invalid self.__encoding = encoding def getEncoding(self): return self.__encoding def getHash(self): return self.__hash def getPos(self): return self.__pos def setPos(self, value): self.__pos = value def open(self, forcePos=None): h = open(self.__filename, 'rb') try: # Set the file descriptor to be FD_CLOEXEC fd = h.fileno() flags = fcntl.fcntl(fd, fcntl.F_GETFD) fcntl.fcntl(fd, fcntl.F_SETFD, flags | fcntl.FD_CLOEXEC) myHash = self.__hash # Stat the file before even attempting to read it stats = os.fstat(h.fileno()) rotflg = stats.st_size < self.__pos or stats.st_ino != self.__ino if rotflg or not len(myHash) or time.time() > self.__hashNextTime: myHash = '' firstLine = h.readline() # Computes the MD5 of the first line (if it is complete) if firstLine != firstLine.rstrip(b'\r\n'): myHash = md5sum(firstLine).hexdigest() self.__hashNextTime = time.time() + 30 elif stats.st_size == self.__pos: myHash = self.__hash # Compare size, hash and inode if rotflg or myHash != self.__hash: if self.__hash != '': logSys.log(logging.MSG, "Log rotation detected for %s, reason: %r", self.__filename, (stats.st_size, self.__pos, stats.st_ino, self.__ino, myHash, self.__hash)) self.__ino = stats.st_ino self.__pos = 0 self.__hash = myHash # if nothing to read from file yet (empty or no new data): if forcePos is not None: self.__pos = forcePos elif stats.st_size <= self.__pos: return False # Sets the file pointer to the last position. h.seek(self.__pos) # leave file open (to read content): self.__handler = h; h = None finally: # close (no content or error only) if h: h.close(); h = None return True def seek(self, offs, endLine=True): h = self.__handler if h is None: self.open(offs) h = self.__handler # seek to given position h.seek(offs, 0) # goto end of next line if offs and endLine: h.readline() # get current real position return h.tell() def tell(self): # get current real position return self.__handler.tell() @staticmethod def decode_line(filename, enc, line): try: return line.decode(enc, 'strict') except (UnicodeDecodeError, UnicodeEncodeError) as e: # avoid warning if got incomplete end of line (e. g. '\n' in "...[0A" followed by "00]..." for utf-16le: if (e.end == len(line) and line[e.start] in b'\r\n'): return line[0:e.start].decode(enc, 'replace') global _decode_line_warn lev = 7 if not _decode_line_warn.get(filename, 0): lev = logging.WARNING _decode_line_warn.set(filename, 1) logSys.log(lev, "Error decoding line from '%s' with '%s'.", filename, enc) if logSys.getEffectiveLevel() <= lev: logSys.log(lev, "Consider setting logencoding to appropriate encoding for this jail. " "Continuing to process line ignoring invalid characters: %r", line) # decode with replacing error chars: line = line.decode(enc, 'replace') return line def readline(self, complete=True): """Read line from file In opposite to pythons readline it doesn't return new-line, so returns either the line if line is complete (and complete=True) or None if line is not complete (and complete=True) or there is no content to read. If line is complete (and complete is True), it also shift current known position to begin of next line. Also it is safe against interim new-line bytes (e. g. part of multi-byte char) in given encoding. """ if self.__handler is None: return "" # read raw bytes up to \n char: b = self.__handler.readline() if not b: return None bl = len(b) # convert to log-encoding (new-line char could disappear if it is part of multi-byte sequence): r = FileContainer.decode_line( self.getFileName(), self.getEncoding(), b) # trim new-line at end and check the line was written complete (contains a new-line): l = r.rstrip('\r\n') if complete: if l == r: # try to fill buffer in order to find line-end in log encoding: fnd = 0 while 1: r = self.__handler.readline() if not r: break b += r bl += len(r) # convert to log-encoding: r = FileContainer.decode_line( self.getFileName(), self.getEncoding(), b) # ensure new-line is not in the middle (buffered 2 strings, e. g. in utf-16le it is "...[0A"+"00]..."): e = r.find('\n') if e >= 0 and e != len(r)-1: l, r = r[0:e], r[0:e+1] # back to bytes and get offset to seek after NL: r = r.encode(self.getEncoding(), 'replace') self.__handler.seek(-bl+len(r), 1) return l # trim new-line at end and check the line was written complete (contains a new-line): l = r.rstrip('\r\n') if l != r: return l if self.waitForLineEnd: # not fulfilled - seek back and return: self.__handler.seek(-bl, 1) return None return l def close(self): if self.__handler is not None: # Saves the last real position. self.__pos = self.__handler.tell() # Closes the file. self.__handler.close() self.__handler = None def __iter__(self): return self def __next__(self): line = self.readline() if line is None: self.close() raise StopIteration return line _decode_line_warn = Utils.Cache(maxCount=1000, maxTime=24*60*60); ## # JournalFilter class. # # Base interface class for systemd journal filters class JournalFilter(Filter): # pragma: systemd no cover def clearAllParams(self): super(JournalFilter, self).clearAllParams() self.delJournalMatch() def addJournalMatch(self, match): # pragma: no cover - Base class, not used pass def delJournalMatch(self, match=None): # pragma: no cover - Base class, not used pass def getJournalMatch(self, match): # pragma: no cover - Base class, not used return [] server/filtergamin.py000064400000007036152231502160010730 0ustar00# 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. # Author: Cyril Jaquier, Yaroslav Halchenko __author__ = "Cyril Jaquier, Yaroslav Halchenko" __copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2012 Yaroslav Halchenko" __license__ = "GPL" import fcntl import time import gamin from .failmanager import FailManagerEmpty from .filter import FileFilter from .mytime import MyTime from .utils import Utils from ..helpers import getLogger # Gets the instance of the logger. logSys = getLogger(__name__) ## # Log reader class. # # This class reads a log file and detects login failures or anything else # that matches a given regular expression. This class is instanciated by # a Jail object. class FilterGamin(FileFilter): ## # Constructor. # # Initialize the filter object with default values. # @param jail the jail object def __init__(self, jail): FileFilter.__init__(self, jail) # Gamin monitor self.monitor = gamin.WatchMonitor() fd = self.monitor.get_fd() flags = fcntl.fcntl(fd, fcntl.F_GETFD) fcntl.fcntl(fd, fcntl.F_SETFD, flags|fcntl.FD_CLOEXEC) logSys.debug("Created FilterGamin") def callback(self, path, event): logSys.log(4, "Got event: " + repr(event) + " for " + path) if event in (gamin.GAMCreated, gamin.GAMChanged, gamin.GAMExists): logSys.debug("File changed: " + path) self.ticks += 1 self.getFailures(path) ## # Add a log file path # # @param path log file path def _addLogPath(self, path): self.monitor.watch_file(path, self.callback) ## # Delete a log path # # @param path the log file to delete def _delLogPath(self, path): self.monitor.stop_watch(path) def _handleEvents(self): ret = False mon = self.monitor while mon and mon.event_pending() > 0: mon.handle_events() mon = self.monitor ret = True return ret ## # Main loop. # # This function is the main loop of the thread. It checks if the # file has been modified and looks for failures. # @return True when the thread exits nicely def run(self): # Gamin needs a loop to collect and dispatch events while self.active: if self.idle: # wait a little bit here for not idle, to prevent hi-load: if not Utils.wait_for(lambda: not self.active or not self.idle, self.sleeptime * 10, self.sleeptime ): self.ticks += 1 continue Utils.wait_for(lambda: not self.active or self._handleEvents(), self.sleeptime) self.ticks += 1 if self.ticks % 10 == 0: self.performSvc() logSys.debug("[%s] filter terminated", self.jailName) return True def stop(self): super(FilterGamin, self).stop() self.__cleanup() ## # Desallocates the resources used by Gamin. def __cleanup(self): if not self.monitor: return for filename in self.getLogPaths(): self.monitor.stop_watch(filename) self.monitor = None server/filterpoll.py000064400000012410152231502160010573 0ustar00# 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. # Author: Cyril Jaquier, Yaroslav Halchenko # __author__ = "Cyril Jaquier, Yaroslav Halchenko" __copyright__ = "Copyright (c) 2004 Cyril Jaquier; 2012 Yaroslav Halchenko" __license__ = "GPL" import os import time from .filter import FileFilter from .utils import Utils from ..helpers import getLogger, logging # Gets the instance of the logger. logSys = getLogger(__name__) ## # Log reader class. # # This class reads a log file and detects login failures or anything else # that matches a given regular expression. This class is instantiated by # a Jail object. class FilterPoll(FileFilter): ## # Constructor. # # Initialize the filter object with default values. # @param jail the jail object def __init__(self, jail): FileFilter.__init__(self, jail) ## The time of the last modification of the file. self.__prevStats = dict() self.__file404Cnt = dict() logSys.debug("Created FilterPoll") ## # Add a log file path # # @param path log file path def _addLogPath(self, path): self.__prevStats[path] = (0, None, None) # mtime, ino, size self.__file404Cnt[path] = 0 ## # Delete a log path # # @param path the log file to delete def _delLogPath(self, path): del self.__prevStats[path] del self.__file404Cnt[path] ## # Get a modified log path at once # def getModified(self, modlst): for filename in self.getLogPaths(): if self.isModified(filename): modlst.append(filename) return modlst ## # Main loop. # # This function is the main loop of the thread. It checks if the # file has been modified and looks for failures. # @return True when the thread exits nicely def run(self): while self.active: try: if logSys.getEffectiveLevel() <= 4: logSys.log(4, "Woke up idle=%s with %d files monitored", self.idle, self.getLogCount()) if self.idle: if not Utils.wait_for(lambda: not self.active or not self.idle, self.sleeptime * 10, self.sleeptime ): self.ticks += 1 continue # Get file modification modlst = [] Utils.wait_for(lambda: not self.active or self.getModified(modlst), self.sleeptime) if not self.active: # pragma: no cover - timing break for filename in modlst: self.getFailures(filename) self.ticks += 1 if self.ticks % 10 == 0: self.performSvc() except Exception as e: # pragma: no cover if not self.active: # if not active - error by stop... break logSys.error("Caught unhandled exception in main cycle: %r", e, exc_info=logSys.getEffectiveLevel()<=logging.DEBUG) # incr common error counter: self.commonError("unhandled", e) logSys.debug("[%s] filter terminated", self.jailName) return True ## # Checks if the log file has been modified. # # Checks if the log file has been modified using os.stat(). # @return True if log file has been modified def isModified(self, filename): try: logStats = os.stat(filename) stats = logStats.st_mtime, logStats.st_ino, logStats.st_size pstats = self.__prevStats.get(filename, (0,)) if logSys.getEffectiveLevel() <= 4: # we do not want to waste time on strftime etc if not necessary dt = logStats.st_mtime - pstats[0] logSys.log(4, "Checking %s for being modified. Previous/current stats: %s / %s. dt: %s", filename, pstats, stats, dt) # os.system("stat %s | grep Modify" % filename) self.__file404Cnt[filename] = 0 if pstats == stats: return False logSys.debug("%s has been modified", filename) self.__prevStats[filename] = stats return True except Exception as e: # still alive (may be deleted because multi-threaded): if not self.getLog(filename) or self.__prevStats.get(filename) is None: logSys.warning("Log %r seems to be down: %s", filename, e) return False # log error: if self.__file404Cnt[filename] < 2: if e.errno == 2: logSys.debug("Log absence detected (possibly rotation) for %s, reason: %s", filename, e) else: # pragma: no cover logSys.error("Unable to get stat on %s because of: %s", filename, e, exc_info=logSys.getEffectiveLevel()<=logging.DEBUG) # increase file and common error counters: self.__file404Cnt[filename] += 1 self.commonError() if self.__file404Cnt[filename] > 50: logSys.warning("Too many errors. Remove file %r from monitoring process", filename) self.__file404Cnt[filename] = 0 self.delLogPath(filename) return False def getPendingPaths(self): return list(self.__file404Cnt.keys()) server/filterpyinotify.py000064400000031367152231502160011673 0ustar00# 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. # Original author: Cyril Jaquier __author__ = "Cyril Jaquier, Lee Clemens, Yaroslav Halchenko" __copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2012 Lee Clemens, 2012 Yaroslav Halchenko" __license__ = "GPL" import logging from distutils.version import LooseVersion import os from os.path import dirname, sep as pathsep import pyinotify from .failmanager import FailManagerEmpty from .filter import FileFilter from .mytime import MyTime, time from .utils import Utils from ..helpers import getLogger if not hasattr(pyinotify, '__version__') \ or LooseVersion(pyinotify.__version__) < '0.8.3': # pragma: no cover raise ImportError("Fail2Ban requires pyinotify >= 0.8.3") # Verify that pyinotify is functional on this system # Even though imports -- might be dysfunctional, e.g. as on kfreebsd try: manager = pyinotify.WatchManager() del manager except Exception as e: # pragma: no cover raise ImportError("Pyinotify is probably not functional on this system: %s" % str(e)) # Gets the instance of the logger. logSys = getLogger(__name__) # Override pyinotify default logger/init-handler: def _pyinotify_logger_init(): # pragma: no cover return logSys pyinotify._logger_init = _pyinotify_logger_init pyinotify.log = logSys ## # Log reader class. # # This class reads a log file and detects login failures or anything else # that matches a given regular expression. This class is instantiated by # a Jail object. class FilterPyinotify(FileFilter): ## # Constructor. # # Initialize the filter object with default values. # @param jail the jail object def __init__(self, jail): FileFilter.__init__(self, jail) # Pyinotify watch manager self.__monitor = pyinotify.WatchManager() self.__notifier = None self.__watchFiles = dict() self.__watchDirs = dict() self.__pending = dict() self.__pendingChkTime = 0 self.__pendingMinTime = 60 logSys.debug("Created FilterPyinotify") def callback(self, event, origin=''): logSys.log(4, "[%s] %sCallback for Event: %s", self.jailName, origin, event) path = event.pathname # check watching of this path: isWF = False isWD = path in self.__watchDirs if not isWD and path in self.__watchFiles: isWF = True assumeNoDir = False if event.mask & ( pyinotify.IN_CREATE | pyinotify.IN_MOVED_TO ): # skip directories altogether if event.mask & pyinotify.IN_ISDIR: logSys.debug("Ignoring creation of directory %s", path) return # check if that is a file we care about if not isWF: logSys.debug("Ignoring creation of %s we do not monitor", path) return self._refreshWatcher(path) elif event.mask & (pyinotify.IN_IGNORED | pyinotify.IN_MOVE_SELF | pyinotify.IN_DELETE_SELF): assumeNoDir = event.mask & (pyinotify.IN_MOVE_SELF | pyinotify.IN_DELETE_SELF) # fix pyinotify behavior with '-unknown-path' (if target not watched also): if (assumeNoDir and path.endswith('-unknown-path') and not isWF and not isWD ): path = path[:-len('-unknown-path')] isWD = path in self.__watchDirs # watch was removed for some reasons (log-rotate?): if isWD and (assumeNoDir or not os.path.isdir(path)): self._addPending(path, event, isDir=True) elif not isWF: # pragma: no cover (assume too sporadic) for logpath in self.__watchDirs: if logpath.startswith(path + pathsep) and (assumeNoDir or not os.path.isdir(logpath)): self._addPending(logpath, event, isDir=True) if isWF and not os.path.isfile(path): self._addPending(path, event) return # do nothing if idle: if self.idle: # pragma: no cover (too sporadic to get idle in callback) return # be sure we process a file: if not isWF: logSys.debug("Ignoring event (%s) of %s we do not monitor", event.maskname, path) return self._process_file(path) def _process_file(self, path): """Process a given file TODO -- RF: this is a common logic and must be shared/provided by FileFilter """ if not self.idle: self.getFailures(path) def _addPending(self, path, reason, isDir=False): if path not in self.__pending: self.__pending[path] = [Utils.DEFAULT_SLEEP_INTERVAL, isDir]; self.__pendingMinTime = 0 if isinstance(reason, pyinotify.Event): reason = [reason.maskname, reason.pathname] logSys.log(logging.MSG, "Log absence detected (possibly rotation) for %s, reason: %s of %s", path, *reason) def _delPending(self, path): try: del self.__pending[path] except KeyError: pass def getPendingPaths(self): return list(self.__pending.keys()) def _checkPending(self): if not self.__pending: return ntm = time.time() if ntm < self.__pendingChkTime + self.__pendingMinTime: return found = {} minTime = 60 for path, (retardTM, isDir) in list(self.__pending.items()): if ntm - self.__pendingChkTime < retardTM: if minTime > retardTM: minTime = retardTM continue chkpath = os.path.isdir if isDir else os.path.isfile if not chkpath(path): # not found - prolong for next time if retardTM < 60: retardTM *= 2 if minTime > retardTM: minTime = retardTM self.__pending[path][0] = retardTM continue logSys.log(logging.MSG, "Log presence detected for %s %s", "directory" if isDir else "file", path) found[path] = isDir self.__pendingChkTime = time.time() self.__pendingMinTime = minTime # process now because we've missed it in monitoring: for path, isDir in found.items(): self._delPending(path) # refresh monitoring of this: if isDir is not None: self._refreshWatcher(path, isDir=isDir) if isDir: # check all files belong to this dir: for logpath in list(self.__watchFiles): if logpath.startswith(path + pathsep): # if still no file - add to pending, otherwise refresh and process: if not os.path.isfile(logpath): self._addPending(logpath, ('FROM_PARDIR', path)) else: self._refreshWatcher(logpath) self._process_file(logpath) else: # process (possibly no old events for it from watcher): self._process_file(path) def _refreshWatcher(self, oldPath, newPath=None, isDir=False): if not newPath: newPath = oldPath # we need to substitute the watcher with a new one, so first # remove old one and then place a new one if not isDir: self._delFileWatcher(oldPath) self._addFileWatcher(newPath) else: self._delDirWatcher(oldPath) self._addDirWatcher(newPath) def _addFileWatcher(self, path): # we need to watch also the directory for IN_CREATE self._addDirWatcher(dirname(path)) # add file watcher: wd = self.__monitor.add_watch(path, pyinotify.IN_MODIFY) self.__watchFiles.update(wd) logSys.debug("Added file watcher for %s", path) def _delWatch(self, wdInt): m = self.__monitor try: if m.get_path(wdInt) is not None: wd = m.rm_watch(wdInt, quiet=False) return True except pyinotify.WatchManagerError as e: if m.get_path(wdInt) is not None and not str(e).endswith("(EINVAL)"): # prama: no cover logSys.debug("Remove watch causes: %s", e) raise e return False def _delFileWatcher(self, path): try: wdInt = self.__watchFiles.pop(path) if not self._delWatch(wdInt): logSys.debug("Non-existing file watcher %r for file %s", wdInt, path) logSys.debug("Removed file watcher for %s", path) return True except KeyError: # pragma: no cover pass return False def _addDirWatcher(self, path_dir): # Add watch for the directory: if path_dir not in self.__watchDirs: self.__watchDirs.update( self.__monitor.add_watch(path_dir, pyinotify.IN_CREATE | pyinotify.IN_MOVED_TO | pyinotify.IN_MOVE_SELF | pyinotify.IN_DELETE_SELF | pyinotify.IN_ISDIR)) logSys.debug("Added monitor for the parent directory %s", path_dir) def _delDirWatcher(self, path_dir): # Remove watches for the directory: try: wdInt = self.__watchDirs.pop(path_dir) if not self._delWatch(wdInt): # pragma: no cover logSys.debug("Non-existing file watcher %r for directory %s", wdInt, path_dir) logSys.debug("Removed monitor for the parent directory %s", path_dir) except KeyError: # pragma: no cover pass ## # Add a log file path # # @param path log file path def _addLogPath(self, path): self._addFileWatcher(path) # notify (wake up if in waiting): if self.active: self.__pendingMinTime = 0 # retard until filter gets started, isDir=None signals special case: process file only (don't need to refresh monitor): self._addPending(path, ('INITIAL', path), isDir=None) ## # Delete a log path # # @param path the log file to delete def _delLogPath(self, path): self._delPending(path) if not self._delFileWatcher(path): # pragma: no cover logSys.error("Failed to remove watch on path: %s", path) path_dir = dirname(path) for k in list(self.__watchFiles): if k.startswith(path_dir + pathsep): path_dir = None break if path_dir: # Remove watches for the directory # since there is no other monitored file under this directory self._delPending(path_dir) self._delDirWatcher(path_dir) # pyinotify.ProcessEvent default handler: def __process_default(self, event): try: self.callback(event, origin='Default ') except Exception as e: # pragma: no cover logSys.error("Error in FilterPyinotify callback: %s", e, exc_info=logSys.getEffectiveLevel() <= logging.DEBUG) # incr common error counter: self.commonError() self.ticks += 1 @property def __notify_maxtout(self): # timeout for pyinotify must be set in milliseconds (fail2ban time values are # floats contain seconds), max 0.5 sec (additionally regards pending check time) return min(self.sleeptime, 0.5, self.__pendingMinTime) * 1000 ## # Main loop. # # Since all detection is offloaded to pyinotifier -- no manual # loop is necessary def run(self): prcevent = pyinotify.ProcessEvent() prcevent.process_default = self.__process_default self.__notifier = pyinotify.Notifier(self.__monitor, prcevent, timeout=self.__notify_maxtout) logSys.debug("[%s] filter started (pyinotifier)", self.jailName) while self.active: try: # slow check events while idle: if self.idle: if Utils.wait_for(lambda: not self.active or not self.idle, min(self.sleeptime * 10, self.__pendingMinTime), min(self.sleeptime, self.__pendingMinTime) ): if not self.active: break # default pyinotify handling using Notifier: self.__notifier.process_events() # wait for events / timeout: def __check_events(): return ( not self.active or bool(self.__notifier.check_events(timeout=self.__notify_maxtout)) or (self.__pendingMinTime and self.__pending) ) wres = Utils.wait_for(__check_events, min(self.sleeptime, self.__pendingMinTime)) if wres: if not self.active: break if not isinstance(wres, dict): self.__notifier.read_events() self.ticks += 1 # check pending files/dirs (logrotate ready): if self.idle: continue self._checkPending() if self.ticks % 10 == 0: self.performSvc() except Exception as e: # pragma: no cover if not self.active: # if not active - error by stop... break logSys.error("Caught unhandled exception in main cycle: %r", e, exc_info=logSys.getEffectiveLevel()<=logging.DEBUG) # incr common error counter: self.commonError("unhandled", e) logSys.debug("[%s] filter exited (pyinotifier)", self.jailName) self.__notifier = None return True ## # Call super.stop() and then stop the 'Notifier' def stop(self): # stop filter thread: super(FilterPyinotify, self).stop() try: if self.__notifier: # stop the notifier self.__notifier.stop() except AttributeError: # pragma: no cover if self.__notifier: raise ## # Wait for exit with cleanup. def join(self): self.join = lambda *args: 0 self.__cleanup() super(FilterPyinotify, self).join() logSys.debug("[%s] filter terminated (pyinotifier)", self.jailName) ## # Deallocates the resources used by pyinotify. def __cleanup(self): if self.__notifier: if Utils.wait_for(lambda: not self.__notifier, self.sleeptime * 10): self.__notifier = None self.__monitor = None server/filtersystemd.py000064400000034706152231502160011331 0ustar00# 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. __author__ = "Steven Hiscocks" __copyright__ = "Copyright (c) 2013 Steven Hiscocks" __license__ = "GPL" import os import time from distutils.version import LooseVersion from systemd import journal if LooseVersion(getattr(journal, '__version__', "0")) < '204': raise ImportError("Fail2Ban requires systemd >= 204") from .failmanager import FailManagerEmpty from .filter import JournalFilter, Filter from .mytime import MyTime from .utils import Utils from ..helpers import getLogger, logging, splitwords, uni_decode # Gets the instance of the logger. logSys = getLogger(__name__) ## # Journal reader class. # # This class reads from systemd journal and detects login failures or anything # else that matches a given regular expression. This class is instantiated by # a Jail object. class FilterSystemd(JournalFilter): # pragma: systemd no cover ## # Constructor. # # Initialize the filter object with default values. # @param jail the jail object def __init__(self, jail, **kwargs): jrnlargs = FilterSystemd._getJournalArgs(kwargs) JournalFilter.__init__(self, jail, **kwargs) self.__modified = 0 # Initialise systemd-journal connection self.__journal = journal.Reader(**jrnlargs) self.__matches = [] self.setDatePattern(None) logSys.debug("Created FilterSystemd") @staticmethod def _getJournalArgs(kwargs): args = {'converters':{'__CURSOR': lambda x: x}} try: args['path'] = kwargs.pop('journalpath') except KeyError: pass try: args['files'] = kwargs.pop('journalfiles') except KeyError: pass else: import glob p = args['files'] if not isinstance(p, (list, set, tuple)): p = splitwords(p) files = [] for p in p: files.extend(glob.glob(p)) args['files'] = list(set(files)) # Default flags is SYSTEM_ONLY(4). This would lead to ignore user session files, # so can prevent "Too many open files" errors on a lot of user sessions (see gh-2392): try: args['flags'] = int(kwargs.pop('journalflags')) except KeyError: # be sure all journal types will be opened if files/path specified (don't set flags): if ('files' not in args or not len(args['files'])) and ('path' not in args or not args['path']): args['flags'] = int(os.getenv("F2B_SYSTEMD_DEFAULT_FLAGS", 4)) try: args['namespace'] = kwargs.pop('namespace') except KeyError: pass return args ## # Add a journal match filters from list structure # # @param matches list structure with journal matches def _addJournalMatches(self, matches): if self.__matches: self.__journal.add_disjunction() # Add OR newMatches = [] for match in matches: newMatches.append([]) for match_element in match: self.__journal.add_match(match_element) newMatches[-1].append(match_element) self.__journal.add_disjunction() self.__matches.extend(newMatches) ## # Add a journal match filter # # @param match journalctl syntax matches in list structure def addJournalMatch(self, match): newMatches = [[]] for match_element in match: if match_element == "+": newMatches.append([]) else: newMatches[-1].append(match_element) try: self._addJournalMatches(newMatches) except ValueError: logSys.error( "Error adding journal match for: %r", " ".join(match)) self.resetJournalMatches() raise else: logSys.info("[%s] Added journal match for: %r", self.jailName, " ".join(match)) ## # Reset a journal match filter called on removal or failure # # @return None def resetJournalMatches(self): self.__journal.flush_matches() logSys.debug("[%s] Flushed all journal matches", self.jailName) match_copy = self.__matches[:] self.__matches = [] try: self._addJournalMatches(match_copy) except ValueError: logSys.error("Error restoring journal matches") raise else: logSys.debug("Journal matches restored") ## # Delete a journal match filter # # @param match journalctl syntax matches def delJournalMatch(self, match=None): # clear all: if match is None: if not self.__matches: return del self.__matches[:] # delete by index: elif match in self.__matches: del self.__matches[self.__matches.index(match)] else: raise ValueError("Match %r not found" % match) self.resetJournalMatches() logSys.info("[%s] Removed journal match for: %r", self.jailName, match if match else '*') ## # Get current journal match filter # # @return journalctl syntax matches def getJournalMatch(self): return self.__matches ## # Get journal reader # # @return journal reader def getJournalReader(self): return self.__journal def getJrnEntTime(self, logentry): """ Returns time of entry as tuple (ISO-str, Posix).""" date = logentry.get('_SOURCE_REALTIME_TIMESTAMP') if date is None: date = logentry.get('__REALTIME_TIMESTAMP') return (date.isoformat(), time.mktime(date.timetuple()) + date.microsecond/1.0E6) ## # Format journal log entry into syslog style # # @param entry systemd journal entry dict # @return format log line def formatJournalEntry(self, logentry): # Be sure, all argument of line tuple should have the same type: enc = self.getLogEncoding() logelements = [] v = logentry.get('_HOSTNAME') if v: logelements.append(uni_decode(v, enc)) v = logentry.get('SYSLOG_IDENTIFIER') if not v: v = logentry.get('_COMM') if v: logelements.append(uni_decode(v, enc)) v = logentry.get('SYSLOG_PID') if not v: v = logentry.get('_PID') if v: try: # [integer] (if already numeric): v = "[%i]" % v except TypeError: try: # as [integer] (try to convert to int): v = "[%i]" % int(v, 0) except (TypeError, ValueError): # fallback - [string] as it is v = "[%s]" % v logelements[-1] += v logelements[-1] += ":" if logelements[-1] == "kernel:": monotonic = logentry.get('_SOURCE_MONOTONIC_TIMESTAMP') if monotonic is None: monotonic = logentry.get('__MONOTONIC_TIMESTAMP')[0] logelements.append("[%12.6f]" % monotonic.total_seconds()) msg = logentry.get('MESSAGE','') if isinstance(msg, list): logelements.append(" ".join(uni_decode(v, enc) for v in msg)) else: logelements.append(uni_decode(msg, enc)) logline = " ".join(logelements) date = self.getJrnEntTime(logentry) logSys.log(5, "[%s] Read systemd journal entry: %s %s", self.jailName, date[0], logline) ## use the same type for 1st argument: return ((logline[:0], date[0] + ' ', logline.replace('\n', '\\n')), date[1]) def seekToTime(self, date): if isinstance(date, int): date = float(date) self.__journal.seek_realtime(date) def inOperationMode(self): self.inOperation = True logSys.info("[%s] Jail is in operation now (process new journal entries)", self.jailName) ## # Main loop. # # Peridocily check for new journal entries matching the filter and # handover to FailManager def run(self): if not self.getJournalMatch(): logSys.notice( "[%s] Jail started without 'journalmatch' set. " "Jail regexs will be checked against all journal entries, " "which is not advised for performance reasons.", self.jailName) # Save current cursor position (to recognize in operation mode): logentry = None try: self.__journal.seek_tail() logentry = self.__journal.get_previous() if logentry: self.__journal.get_next() except OSError: logentry = None # Reading failure, so safe to ignore if logentry: # Try to obtain the last known time (position of journal) startTime = 0 if self.jail.database is not None: startTime = self.jail.database.getJournalPos(self.jail, 'systemd-journal') or 0 # Seek to max(last_known_time, now - findtime) in journal startTime = max( startTime, MyTime.time() - int(self.getFindTime()) ) self.seekToTime(startTime) # Not in operation while we'll read old messages ... self.inOperation = False # Save current time in order to check time to switch "in operation" mode startTime = (1, MyTime.time(), logentry.get('__CURSOR')) else: # empty journal or no entries for current filter: self.inOperationMode() # seek_tail() seems to have a bug by no entries (could bypass some entries hereafter), so seek to now instead: startTime = MyTime.time() self.seekToTime(startTime) # for possible future switches of in-operation mode: startTime = (0, startTime) # Move back one entry to ensure do not end up in dead space # if start time beyond end of journal try: self.__journal.get_previous() except OSError: pass # Reading failure, so safe to ignore wcode = journal.NOP line = None while self.active: # wait for records (or for timeout in sleeptime seconds): try: ## wait for entries using journal.wait: if wcode == journal.NOP and self.inOperation: ## todo: find better method as wait_for to break (e.g. notify) journal.wait(self.sleeptime), ## don't use `journal.close()` for it, because in some python/systemd implementation it may ## cause abnormal program termination (e. g. segfault) ## ## wait for entries without sleep in intervals, because "sleeping" in journal.wait, ## journal.NOP is 0, so we can wait for non zero (APPEND or INVALIDATE): wcode = Utils.wait_for(lambda: not self.active and journal.APPEND or \ self.__journal.wait(Utils.DEFAULT_SLEEP_INTERVAL), self.sleeptime, 0.00001) ## if invalidate (due to rotation, vacuuming or journal files added/removed etc): if self.active and wcode == journal.INVALIDATE: if self.ticks: logSys.log(logging.DEBUG, "[%s] Invalidate signaled, take a little break (rotation ends)", self.jailName) time.sleep(self.sleeptime * 0.25) Utils.wait_for(lambda: not self.active or \ self.__journal.wait(Utils.DEFAULT_SLEEP_INTERVAL) != journal.INVALIDATE, self.sleeptime * 3, 0.00001) if self.ticks: # move back and forth to ensure do not end up in dead space by rotation or vacuuming, # if position beyond end of journal (gh-3396) try: if self.__journal.get_previous(): self.__journal.get_next() except OSError: pass if self.idle: # because journal.wait will returns immediatelly if we have records in journal, # just wait a little bit here for not idle, to prevent hi-load: if not Utils.wait_for(lambda: not self.active or not self.idle, self.sleeptime * 10, self.sleeptime ): self.ticks += 1 continue self.__modified = 0 while self.active: logentry = None try: logentry = self.__journal.get_next() except OSError as e: logSys.error("Error reading line from systemd journal: %s", e, exc_info=logSys.getEffectiveLevel() <= logging.DEBUG) self.ticks += 1 if logentry: line, tm = self.formatJournalEntry(logentry) # switch "in operation" mode if we'll find start entry (+ some delta): if not self.inOperation: if tm >= MyTime.time() - 1: # reached now (approximated): self.inOperationMode() elif startTime[0] == 1: # if it reached start entry (or get read time larger than start time) if logentry.get('__CURSOR') == startTime[2] or tm > startTime[1]: # give the filter same time it needed to reach the start entry: startTime = (0, MyTime.time()*2 - startTime[1]) elif tm > startTime[1]: # reached start time (approximated): self.inOperationMode() # process line self.processLineAndAdd(line, tm) self.__modified += 1 if self.__modified >= 100: # todo: should be configurable wcode = journal.APPEND; # don't need wait - there are still unprocessed entries break else: # "in operation" mode since we don't have messages anymore (reached end of journal): if not self.inOperation: self.inOperationMode() wcode = journal.NOP; # enter wait - no more entries to process break self.__modified = 0 if self.ticks % 10 == 0: self.performSvc() # update position in log (time and iso string): if self.jail.database: if line: self._pendDBUpdates['systemd-journal'] = (tm, line[1]) line = None if self._pendDBUpdates and ( self.ticks % 100 == 0 or MyTime.time() >= self._nextUpdateTM or not self.active ): self._updateDBPending() self._nextUpdateTM = MyTime.time() + Utils.DEFAULT_SLEEP_TIME * 5 except Exception as e: # pragma: no cover if not self.active: # if not active - error by stop... break wcode = journal.NOP logSys.error("Caught unhandled exception in main cycle: %r", e, exc_info=logSys.getEffectiveLevel()<=logging.DEBUG) # incr common error counter: self.commonError("unhandled", e) logSys.debug("[%s] filter terminated", self.jailName) # close journal: self.closeJournal() logSys.debug("[%s] filter exited (systemd)", self.jailName) return True def closeJournal(self): try: jnl, self.__journal = self.__journal, None if jnl: jnl.close() except Exception as e: # pragma: no cover logSys.error("Close journal failed: %r", e, exc_info=logSys.getEffectiveLevel()<=logging.DEBUG) def status(self, flavor="basic"): ret = super(FilterSystemd, self).status(flavor=flavor) ret.append(("Journal matches", [" + ".join(" ".join(match) for match in self.__matches)])) return ret def _updateDBPending(self): """Apply pending updates (jornal position) to database. """ db = self.jail.database while True: try: log, args = self._pendDBUpdates.popitem() except KeyError: break db.updateJournal(self.jail, log, *args) def onStop(self): """Stop monitoring of journal. Invoked after run method. """ # close journal: self.closeJournal() # ensure positions of pending logs are up-to-date: if self._pendDBUpdates and self.jail.database: self._updateDBPending() server/ipdns.py000064400000041614152231502160007544 0ustar00# 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. __author__ = "Fail2Ban Developers, Alexander Koeppe, Serg G. Brester, Yaroslav Halchenko" __copyright__ = "Copyright (c) 2004-2016 Fail2ban Developers" __license__ = "GPL" import socket import struct import re from .utils import Utils from ..helpers import getLogger # Gets the instance of the logger. logSys = getLogger(__name__) ## # Helper functions # # def asip(ip): """A little helper to guarantee ip being an IPAddr instance""" if isinstance(ip, IPAddr): return ip return IPAddr(ip) def getfqdn(name=''): """Get fully-qualified hostname of given host, thereby resolve of an external IPs and name will be preferred before the local domain (or a loopback), see gh-2438 """ try: name = name or socket.gethostname() names = ( ai[3] for ai in socket.getaddrinfo( name, None, 0, socket.SOCK_DGRAM, 0, socket.AI_CANONNAME ) if ai[3] ) if names: # first try to find a fqdn starting with the host name like www.domain.tld for www: pref = name+'.' first = None for ai in names: if ai.startswith(pref): return ai if not first: first = ai # not found - simply use first known fqdn: return first except socket.error: pass # fallback to python's own getfqdn routine: return socket.getfqdn(name) ## # Utils class for DNS handling. # # This class contains only static methods used to handle DNS # class DNSUtils: # todo: make configurable the expired time and max count of cache entries: CACHE_nameToIp = Utils.Cache(maxCount=1000, maxTime=5*60) CACHE_ipToName = Utils.Cache(maxCount=1000, maxTime=5*60) @staticmethod def dnsToIp(dns): """ Convert a DNS into an IP address using the Python socket module. Thanks to Kevin Drapel. """ # cache, also prevent long wait during retrieving of ip for wrong dns or lazy dns-system: ips = DNSUtils.CACHE_nameToIp.get(dns) if ips is not None: return ips # retrieve ips ips = set() saveerr = None for fam, ipfam in ((socket.AF_INET, IPAddr.FAM_IPv4), (socket.AF_INET6, IPAddr.FAM_IPv6)): try: for result in socket.getaddrinfo(dns, None, fam, 0, socket.IPPROTO_TCP): # if getaddrinfo returns something unexpected: if len(result) < 4 or not len(result[4]): continue # get ip from `(2, 1, 6, '', ('127.0.0.1', 0))`,be sure we've an ip-string # (some python-versions resp. host configurations causes returning of integer there): ip = IPAddr(str(result[4][0]), ipfam) if ip.isValid: ips.add(ip) except Exception as e: saveerr = e if not ips and saveerr: logSys.warning("Unable to find a corresponding IP address for %s: %s", dns, saveerr) DNSUtils.CACHE_nameToIp.set(dns, ips) return ips @staticmethod def ipToName(ip): # cache, also prevent long wait during retrieving of name for wrong addresses, lazy dns: v = DNSUtils.CACHE_ipToName.get(ip, ()) if v != (): return v # retrieve name try: v = socket.gethostbyaddr(ip)[0] except socket.error as e: logSys.debug("Unable to find a name for the IP %s: %s", ip, e) v = None DNSUtils.CACHE_ipToName.set(ip, v) return v @staticmethod def textToIp(text, useDns): """ Return the IP of DNS found in a given text. """ ipList = set() # Search for plain IP plainIP = IPAddr.searchIP(text) if plainIP is not None: ip = IPAddr(plainIP) if ip.isValid: ipList.add(ip) # If we are allowed to resolve -- give it a try if nothing was found if useDns in ("yes", "warn") and not ipList: # Try to get IP from possible DNS ip = DNSUtils.dnsToIp(text) ipList.update(ip) if ip and useDns == "warn": logSys.warning("Determined IP using DNS Lookup: %s = %s", text, ipList) return ipList @staticmethod def getHostname(fqdn=True): """Get short hostname or fully-qualified hostname of host self""" # try find cached own hostnames (this tuple-key cannot be used elsewhere): key = ('self','hostname', fqdn) name = DNSUtils.CACHE_ipToName.get(key) # get it using different ways (hostname, fully-qualified or vice versa): if name is None: name = '' for hostname in ( (getfqdn, socket.gethostname) if fqdn else (socket.gethostname, getfqdn) ): try: name = hostname() break except Exception as e: # pragma: no cover logSys.warning("Retrieving own hostnames failed: %s", e) # cache and return : DNSUtils.CACHE_ipToName.set(key, name) return name # key find cached own hostnames (this tuple-key cannot be used elsewhere): _getSelfNames_key = ('self','dns') @staticmethod def getSelfNames(): """Get own host names of self""" # try find cached own hostnames: names = DNSUtils.CACHE_ipToName.get(DNSUtils._getSelfNames_key) # get it using different ways (a set with names of localhost, hostname, fully qualified): if names is None: names = set([ 'localhost', DNSUtils.getHostname(False), DNSUtils.getHostname(True) ]) - set(['']) # getHostname can return '' # cache and return : DNSUtils.CACHE_ipToName.set(DNSUtils._getSelfNames_key, names) return names # key to find cached own IPs (this tuple-key cannot be used elsewhere): _getSelfIPs_key = ('self','ips') @staticmethod def getSelfIPs(): """Get own IP addresses of self""" # to find cached own IPs: ips = DNSUtils.CACHE_nameToIp.get(DNSUtils._getSelfIPs_key) # get it using different ways (a set with IPs of localhost, hostname, fully qualified): if ips is None: ips = set() for hostname in DNSUtils.getSelfNames(): try: ips |= set(DNSUtils.textToIp(hostname, 'yes')) except Exception as e: # pragma: no cover logSys.warning("Retrieving own IPs of %s failed: %s", hostname, e) # cache and return : DNSUtils.CACHE_nameToIp.set(DNSUtils._getSelfIPs_key, ips) return ips _IPv6IsAllowed = None @staticmethod def setIPv6IsAllowed(value): DNSUtils._IPv6IsAllowed = value logSys.debug("IPv6 is %s", ('on' if value else 'off') if value is not None else 'auto') return value # key to find cached value of IPv6 allowance (this tuple-key cannot be used elsewhere): _IPv6IsAllowed_key = ('self','ipv6-allowed') @staticmethod def IPv6IsAllowed(): if DNSUtils._IPv6IsAllowed is not None: return DNSUtils._IPv6IsAllowed v = DNSUtils.CACHE_nameToIp.get(DNSUtils._IPv6IsAllowed_key) if v is not None: return v v = any((':' in ip.ntoa) for ip in DNSUtils.getSelfIPs()) DNSUtils.CACHE_nameToIp.set(DNSUtils._IPv6IsAllowed_key, v) return v ## # Class for IP address handling. # # This class contains methods for handling IPv4 and IPv6 addresses. # class IPAddr(object): """Encapsulate functionality for IPv4 and IPv6 addresses """ IP_4_RE = r"""(?:\d{1,3}\.){3}\d{1,3}""" IP_6_RE = r"""(?:[0-9a-fA-F]{1,4}::?|::){1,7}(?:[0-9a-fA-F]{1,4}|(?<=:):)""" IP_4_6_CRE = re.compile( r"""^(?:(?P%s)|\[?(?P%s)\]?)$""" % (IP_4_RE, IP_6_RE)) # An IPv4 compatible IPv6 to be reused (see below) IP6_4COMPAT = None # object attributes __slots__ = '_family','_addr','_plen','_maskplen','_raw' # todo: make configurable the expired time and max count of cache entries: CACHE_OBJ = Utils.Cache(maxCount=10000, maxTime=5*60) CIDR_RAW = -2 CIDR_UNSPEC = -1 FAM_IPv4 = CIDR_RAW - socket.AF_INET FAM_IPv6 = CIDR_RAW - socket.AF_INET6 def __new__(cls, ipstr, cidr=CIDR_UNSPEC): if cidr == IPAddr.CIDR_UNSPEC and isinstance(ipstr, (tuple, list)): cidr = IPAddr.CIDR_RAW if cidr == IPAddr.CIDR_RAW: # don't cache raw ip = super(IPAddr, cls).__new__(cls) ip.__init(ipstr, cidr) return ip # check already cached as IPAddr args = (ipstr, cidr) ip = IPAddr.CACHE_OBJ.get(args) if ip is not None: return ip # wrap mask to cidr (correct plen): if cidr == IPAddr.CIDR_UNSPEC: ipstr, cidr = IPAddr.__wrap_ipstr(ipstr) args = (ipstr, cidr) # check cache again: if cidr != IPAddr.CIDR_UNSPEC: ip = IPAddr.CACHE_OBJ.get(args) if ip is not None: return ip ip = super(IPAddr, cls).__new__(cls) ip.__init(ipstr, cidr) if ip._family != IPAddr.CIDR_RAW: IPAddr.CACHE_OBJ.set(args, ip) return ip @staticmethod def __wrap_ipstr(ipstr): # because of standard spelling of IPv6 (with port) enclosed in brackets ([ipv6]:port), # remove they now (be sure the inside failregex uses this for IPv6 (has \[?...\]?) if len(ipstr) > 2 and ipstr[0] == '[' and ipstr[-1] == ']': ipstr = ipstr[1:-1] # test mask: if "/" not in ipstr: return ipstr, IPAddr.CIDR_UNSPEC s = ipstr.split('/', 1) # IP address without CIDR mask if len(s) > 2: raise ValueError("invalid ipstr %r, too many plen representation" % (ipstr,)) if "." in s[1] or ":" in s[1]: # 255.255.255.0 resp. ffff:: style mask s[1] = IPAddr.masktoplen(s[1]) s[1] = int(s[1]) return s def __init(self, ipstr, cidr=CIDR_UNSPEC): """ initialize IP object by converting IP address string to binary to integer """ self._family = socket.AF_UNSPEC self._addr = 0 self._plen = 0 self._maskplen = None # always save raw value (normally used if really raw or not valid only): self._raw = ipstr # if not raw - recognize family, set addr, etc.: if cidr != IPAddr.CIDR_RAW: if cidr is not None and cidr < IPAddr.CIDR_RAW: family = [IPAddr.CIDR_RAW - cidr] else: family = [socket.AF_INET, socket.AF_INET6] for family in family: try: binary = socket.inet_pton(family, ipstr) self._family = family break except socket.error: continue if self._family == socket.AF_INET: # convert host to network byte order self._addr, = struct.unpack("!L", binary) self._plen = 32 # mask out host portion if prefix length is supplied if cidr is not None and cidr >= 0: mask = ~(0xFFFFFFFF >> cidr) self._addr &= mask self._plen = cidr elif self._family == socket.AF_INET6: # convert host to network byte order hi, lo = struct.unpack("!QQ", binary) self._addr = (hi << 64) | lo self._plen = 128 # mask out host portion if prefix length is supplied if cidr is not None and cidr >= 0: mask = ~(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF >> cidr) self._addr &= mask self._plen = cidr # if IPv6 address is a IPv4-compatible, make instance a IPv4 elif self.isInNet(IPAddr.IP6_4COMPAT): self._addr = lo & 0xFFFFFFFF self._family = socket.AF_INET self._plen = 32 else: self._family = IPAddr.CIDR_RAW def __repr__(self): return repr(self.ntoa) def __str__(self): return self.ntoa if isinstance(self.ntoa, str) else str(self.ntoa) def __reduce__(self): """IPAddr pickle-handler, that simply wraps IPAddr to the str Returns a string as instance to be pickled, because fail2ban-client can't unserialize IPAddr objects """ return (str, (self.ntoa,)) @property def addr(self): return self._addr @property def family(self): return self._family FAM2STR = {socket.AF_INET: 'inet4', socket.AF_INET6: 'inet6'} @property def familyStr(self): return IPAddr.FAM2STR.get(self._family) @property def plen(self): return self._plen @property def raw(self): """The raw address Should only be set to a non-empty string if prior address conversion wasn't possible """ return self._raw @property def isValid(self): """Either the object corresponds to a valid IP address """ return self._family != socket.AF_UNSPEC @property def isSingle(self): """Returns whether the object is a single IP address (not DNS and subnet) """ return self._plen == {socket.AF_INET: 32, socket.AF_INET6: 128}.get(self._family, -1000) def __eq__(self, other): if self._family == IPAddr.CIDR_RAW and not isinstance(other, IPAddr): return self._raw == other if not isinstance(other, IPAddr): if other is None: return False other = IPAddr(other) if self._family != other._family: return False if self._family == socket.AF_UNSPEC: return self._raw == other._raw return ( (self._addr == other._addr) and (self._plen == other._plen) ) def __ne__(self, other): return not (self == other) def __lt__(self, other): if self._family == IPAddr.CIDR_RAW and not isinstance(other, IPAddr): return self._raw < other if not isinstance(other, IPAddr): if other is None: return False other = IPAddr(other) return self._family < other._family or self._addr < other._addr def __add__(self, other): if not isinstance(other, IPAddr): other = IPAddr(other) return "%s%s" % (self, other) def __radd__(self, other): if not isinstance(other, IPAddr): other = IPAddr(other) return "%s%s" % (other, self) def __hash__(self): # should be the same as by string (because of possible compare with string): return hash(self.ntoa) #return hash(self._addr)^hash((self._plen<<16)|self._family) @property def hexdump(self): """Hex representation of the IP address (for debug purposes) """ if self._family == socket.AF_INET: return "%08x" % self._addr elif self._family == socket.AF_INET6: return "%032x" % self._addr else: return "" # TODO: could be lazily evaluated @property def ntoa(self): """ represent IP object as text like the deprecated C pendant inet.ntoa but address family independent """ add = '' if self.isIPv4: # convert network to host byte order binary = struct.pack("!L", self._addr) if self._plen and self._plen < 32: add = "/%d" % self._plen elif self.isIPv6: # convert network to host byte order hi = self._addr >> 64 lo = self._addr & 0xFFFFFFFFFFFFFFFF binary = struct.pack("!QQ", hi, lo) if self._plen and self._plen < 128: add = "/%d" % self._plen else: return self._raw return socket.inet_ntop(self._family, binary) + add def getPTR(self, suffix=None): """ return the DNS PTR string of the provided IP address object If "suffix" is provided it will be appended as the second and top level reverse domain. If omitted it is implicitly set to the second and top level reverse domain of the according IP address family """ if self.isIPv4: exploded_ip = self.ntoa.split(".") if suffix is None: suffix = "in-addr.arpa." elif self.isIPv6: exploded_ip = self.hexdump if suffix is None: suffix = "ip6.arpa." else: return "" return "%s.%s" % (".".join(reversed(exploded_ip)), suffix) def getHost(self): """Return the host name (DNS) of the provided IP address object """ return DNSUtils.ipToName(self.ntoa) @property def isIPv4(self): """Either the IP object is of address family AF_INET """ return self.family == socket.AF_INET @property def isIPv6(self): """Either the IP object is of address family AF_INET6 """ return self.family == socket.AF_INET6 def isInNet(self, net): """Return either the IP object is in the provided network """ # if it isn't a valid IP address, try DNS resolution if not net.isValid and net.raw != "": # Check if IP in DNS return self in DNSUtils.dnsToIp(net.raw) if self.family != net.family: return False if self.isIPv4: mask = ~(0xFFFFFFFF >> net.plen) elif self.isIPv6: mask = ~(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF >> net.plen) else: return False return (self.addr & mask) == net.addr def contains(self, ip): """Return whether the object (as network) contains given IP """ return isinstance(ip, IPAddr) and (ip == self or ip.isInNet(self)) # Pre-calculated map: addr to maskplen def __getMaskMap(): m6 = (1 << 128)-1 m4 = (1 << 32)-1 mmap = {m6: 128, m4: 32, 0: 0} m = 0 for i in range(0, 128): m |= 1 << i if i < 32: mmap[m ^ m4] = 32-1-i mmap[m ^ m6] = 128-1-i return mmap MAP_ADDR2MASKPLEN = __getMaskMap() @property def maskplen(self): mplen = 0 if self._maskplen is not None: return self._maskplen mplen = IPAddr.MAP_ADDR2MASKPLEN.get(self._addr) if mplen is None: raise ValueError("invalid mask %r, no plen representation" % (str(self),)) self._maskplen = mplen return mplen @staticmethod def masktoplen(mask): """Convert mask string to prefix length To be used only for IPv4 masks """ return IPAddr(mask).maskplen @staticmethod def searchIP(text): """Search if text is an IP address, and return it if so, else None """ match = IPAddr.IP_4_6_CRE.match(text) if not match: return None ipstr = match.group('IPv4') if ipstr != '': return ipstr return match.group('IPv6') # An IPv4 compatible IPv6 to be reused IPAddr.IP6_4COMPAT = IPAddr("::ffff:0:0", 96) server/jail.py000064400000026461152231502160007351 0ustar00# 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. # Author: Cyril Jaquier __author__ = "Cyril Jaquier, Lee Clemens, Yaroslav Halchenko" __copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2012 Lee Clemens, 2012 Yaroslav Halchenko" __license__ = "GPL" import logging import math import random import queue from .actions import Actions from ..helpers import getLogger, _as_bool, extractOptions, MyTime from .mytime import MyTime # Gets the instance of the logger. logSys = getLogger(__name__) class Jail(object): """Fail2Ban jail, which manages a filter and associated actions. The class handles the initialisation of a filter, and actions. It's role is then to act as an interface between the filter and actions, passing bans detected by the filter, for the actions to then act upon. Parameters ---------- name : str Name assigned to the jail. backend : str Backend to be used for filter. "auto" will attempt to pick the most preferred backend method. Default: "auto" db : Fail2BanDb Fail2Ban persistent database instance. Default: `None` Attributes ---------- name database filter actions idle status """ #Known backends. Each backend should have corresponding __initBackend method # yoh: stored in a list instead of a tuple since only # list had .index until 2.6 _BACKENDS = ['pyinotify', 'gamin', 'polling', 'systemd'] def __init__(self, name, backend = "auto", db=None): self.__db = db # 26 based on iptable chain name limit of 30 less len('f2b-') if len(name) >= 26: logSys.warning("Jail name %r might be too long and some commands " "might not function correctly. Please shorten" % name) self.__name = name self.__queue = queue.Queue() self.__filter = None # Extra parameters for increase ban time self._banExtra = {}; logSys.info("Creating new jail '%s'" % self.name) if backend is not None: self._setBackend(backend) self.backend = backend def __repr__(self): return "%s(%r)" % (self.__class__.__name__, self.name) def _setBackend(self, backend): backend, beArgs = extractOptions(backend) backend = backend.lower() # to assure consistent matching backends = self._BACKENDS if backend != 'auto': # we have got strict specification of the backend to use if not (backend in self._BACKENDS): logSys.error("Unknown backend %s. Must be among %s or 'auto'" % (backend, backends)) raise ValueError("Unknown backend %s. Must be among %s or 'auto'" % (backend, backends)) # so explore starting from it till the 'end' backends = backends[backends.index(backend):] for b in backends: initmethod = getattr(self, '_init%s' % b.capitalize()) try: initmethod(**beArgs) if backend != 'auto' and b != backend: logSys.warning("Could only initiated %r backend whenever " "%r was requested" % (b, backend)) else: logSys.info("Initiated %r backend" % b) self.__actions = Actions(self) return # we are done except ImportError as e: # pragma: no cover # Log debug if auto, but error if specific logSys.log( logging.DEBUG if backend == "auto" else logging.ERROR, "Backend %r failed to initialize due to %s" % (b, e)) # pragma: no cover # log error since runtime error message isn't printed, INVALID COMMAND logSys.error( "Failed to initialize any backend for Jail %r" % self.name) raise RuntimeError( "Failed to initialize any backend for Jail %r" % self.name) def _initPolling(self, **kwargs): from .filterpoll import FilterPoll logSys.info("Jail '%s' uses poller %r" % (self.name, kwargs)) self.__filter = FilterPoll(self, **kwargs) def _initGamin(self, **kwargs): # Try to import gamin from .filtergamin import FilterGamin logSys.info("Jail '%s' uses Gamin %r" % (self.name, kwargs)) self.__filter = FilterGamin(self, **kwargs) def _initPyinotify(self, **kwargs): # Try to import pyinotify from .filterpyinotify import FilterPyinotify logSys.info("Jail '%s' uses pyinotify %r" % (self.name, kwargs)) self.__filter = FilterPyinotify(self, **kwargs) def _initSystemd(self, **kwargs): # pragma: systemd no cover # Try to import systemd from .filtersystemd import FilterSystemd logSys.info("Jail '%s' uses systemd %r" % (self.name, kwargs)) self.__filter = FilterSystemd(self, **kwargs) @property def name(self): """Name of jail. """ return self.__name @property def database(self): """The database used to store persistent data for the jail. """ return self.__db @database.setter def database(self, value): self.__db = value; @property def filter(self): """The filter which the jail is using to monitor log files. """ return self.__filter @property def actions(self): """Actions object used to manage actions for jail. """ return self.__actions @property def idle(self): """A boolean indicating whether jail is idle. """ return self.filter.idle or self.actions.idle @idle.setter def idle(self, value): self.filter.idle = value self.actions.idle = value def status(self, flavor="basic"): """The status of the jail. """ return [ ("Filter", self.filter.status(flavor=flavor)), ("Actions", self.actions.status(flavor=flavor)), ] @property def hasFailTickets(self): """Retrieve whether queue has tickets to ban. """ return not self.__queue.empty() def putFailTicket(self, ticket): """Add a fail ticket to the jail. Used by filter to add a failure for banning. """ self.__queue.put(ticket) # add ban to database moved to observer (should previously check not already banned # and increase ticket time if "bantime.increment" set) def getFailTicket(self): """Get a fail ticket from the jail. Used by actions to get a failure for banning. """ try: ticket = self.__queue.get(False) return ticket except queue.Empty: return False def setBanTimeExtra(self, opt, value): # merge previous extra with new option: be = self._banExtra; if value == '': value = None if value is not None: be[opt] = value; elif opt in be: del be[opt] logSys.info('Set banTime.%s = %s', opt, value) if opt == 'increment': be[opt] = _as_bool(value) if be.get(opt) and self.database is None: logSys.warning("ban time increment is not available as long jail database is not set") if opt in ['maxtime', 'rndtime']: if not value is None: be[opt] = MyTime.str2seconds(value) # prepare formula lambda: if opt in ['formula', 'factor', 'maxtime', 'rndtime', 'multipliers'] or be.get('evformula', None) is None: # split multifiers to an array begins with 0 (or empty if not set): if opt == 'multipliers': be['evmultipliers'] = [int(i) for i in (value.split(' ') if value is not None and value != '' else [])] # if we have multifiers - use it in lambda, otherwise compile and use formula within lambda multipliers = be.get('evmultipliers', []) banFactor = eval(be.get('factor', "1")) if len(multipliers): evformula = lambda ban, banFactor=banFactor: ( ban.Time * banFactor * multipliers[ban.Count if ban.Count < len(multipliers) else -1] ) else: formula = be.get('formula', 'ban.Time * (1<<(ban.Count if ban.Count<20 else 20)) * banFactor') formula = compile(formula, '~inline-conf-expr~', 'eval') evformula = lambda ban, banFactor=banFactor, formula=formula: max(ban.Time, eval(formula)) # extend lambda with max time : if not be.get('maxtime', None) is None: maxtime = be['maxtime'] evformula = lambda ban, evformula=evformula: min(evformula(ban), maxtime) # mix lambda with random time (to prevent bot-nets to calculate exact time IP can be unbanned): if not be.get('rndtime', None) is None: rndtime = be['rndtime'] evformula = lambda ban, evformula=evformula: (evformula(ban) + random.random() * rndtime) # set to extra dict: be['evformula'] = evformula #logSys.info('banTimeExtra : %s' % json.dumps(be)) def getBanTimeExtra(self, opt=None): if opt is not None: return self._banExtra.get(opt, None) return self._banExtra def getMaxBanTime(self): """Returns max possible ban-time of jail. """ return self._banExtra.get("maxtime", -1) \ if self._banExtra.get('increment') else self.actions.getBanTime() def restoreCurrentBans(self, correctBanTime=True): """Restore any previous valid bans from the database. """ try: if self.database is not None: if self._banExtra.get('increment'): forbantime = None; if correctBanTime: correctBanTime = self.getMaxBanTime() else: # use ban time as search time if we have not enabled a increasing: forbantime = self.actions.getBanTime() for ticket in self.database.getCurrentBans(jail=self, forbantime=forbantime, correctBanTime=correctBanTime, maxmatches=self.filter.failManager.maxMatches ): try: #logSys.debug('restored ticket: %s', ticket) if self.filter.inIgnoreIPList(ticket.getID(), log_ignore=True): continue # mark ticked was restored from database - does not put it again into db: ticket.restored = True # correct start time / ban time (by the same end of ban): btm = ticket.getBanTime(forbantime) diftm = MyTime.time() - ticket.getTime() if btm != -1 and diftm > 0: btm -= diftm # ignore obsolete tickets: if btm != -1 and btm <= 0: continue self.putFailTicket(ticket) except Exception as e: # pragma: no cover logSys.error('Restore ticket failed: %s', e, exc_info=logSys.getEffectiveLevel()<=logging.DEBUG) except Exception as e: # pragma: no cover logSys.error('Restore bans failed: %s', e, exc_info=logSys.getEffectiveLevel()<=logging.DEBUG) def start(self): """Start the jail, by starting filter and actions threads. Once stated, also queries the persistent database to reinstate any valid bans. """ logSys.debug("Starting jail %r", self.name) self.filter.start() self.actions.start() self.restoreCurrentBans() logSys.info("Jail %r started", self.name) def stop(self, stop=True, join=True): """Stop the jail, by stopping filter and actions threads. """ if stop: logSys.debug("Stopping jail %r", self.name) for obj in (self.filter, self.actions): try: ## signal to stop filter / actions: if stop: obj.stop() ## wait for end of threads: if join: obj.join() except Exception as e: logSys.error("Stop %r of jail %r failed: %s", obj, self.name, e, exc_info=logSys.getEffectiveLevel()<=logging.DEBUG) if join: logSys.info("Jail %r stopped", self.name) def isAlive(self): """Check jail "isAlive" by checking filter and actions threads. """ return self.filter.isAlive() or self.actions.isAlive() server/jails.py000064400000005425152231502160007531 0ustar00# 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. __author__ = "Cyril Jaquier, Yaroslav Halchenko" __copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2013- Yaroslav Halchenko" __license__ = "GPL" from threading import Lock try: from collections.abc import Mapping except ImportError: from collections import Mapping from ..exceptions import DuplicateJailException, UnknownJailException from .jail import Jail class Jails(Mapping): """Handles the jails. This class handles the jails. Creation, deletion or access to a jail must be done through this class. This class is thread-safe which is not the case of the jail itself, including filter and actions. This class is based on Mapping type, and the `add` method must be used to add additional jails. """ def __init__(self): self.__lock = Lock() self._jails = dict() def add(self, name, backend, db=None): """Adds a jail. Adds a new jail if not already present which should use the given backend. Parameters ---------- name : str The name of the jail. backend : str The backend to use. db : Fail2BanDb Fail2Ban's persistent database instance. Raises ------ DuplicateJailException If jail name is already present. """ with self.__lock: if name in self._jails: if noduplicates: raise DuplicateJailException(name) else: self._jails[name] = Jail(name, backend, db) def exists(self, name): return name in self._jails def __getitem__(self, name): try: self.__lock.acquire() return self._jails[name] except KeyError: raise UnknownJailException(name) finally: self.__lock.release() def __delitem__(self, name): try: self.__lock.acquire() del self._jails[name] except KeyError: raise UnknownJailException(name) finally: self.__lock.release() def __len__(self): try: self.__lock.acquire() return len(self._jails) finally: self.__lock.release() def __iter__(self): try: self.__lock.acquire() return iter(self._jails) finally: self.__lock.release() server/jailthread.py000064400000007723152231502160010541 0ustar00# 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. # Author: Cyril Jaquier # __author__ = "Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" import sys from threading import Thread from abc import abstractmethod from .utils import Utils from ..helpers import excepthook, prctl_set_th_name class JailThread(Thread): """Abstract class for threading elements in Fail2Ban. Attributes ---------- daemon ident name status active : bool Control the state of the thread. idle : bool Control the idle state of the thread. sleeptime : int The time the thread sleeps for in the loop. """ def __init__(self, name=None): super(JailThread, self).__init__(name=name) ## Should going with main thread also: self.daemon = True ## Control the state of the thread (None - was not started, True - active, False - stopped). self.active = None ## Control the idle state of the thread. self.idle = False ## The time the thread sleeps in the loop. self.sleeptime = Utils.DEFAULT_SLEEP_TIME # excepthook workaround for threads, derived from: # http://bugs.python.org/issue1230540#msg91244 run = self.run def run_with_except_hook(*args, **kwargs): try: run(*args, **kwargs) # call on stop callback to do some finalizations: self.onStop() except Exception as e: # avoid very sporadic error "'NoneType' object has no attribute 'exc_info'" (https://bugs.python.org/issue7336) # only extremely fast systems are affected ATM (2.7 / 3.x), if thread ends nothing is available here. if sys is not None: excepthook(*sys.exc_info()) else: print(e) self.run = run_with_except_hook if sys.version_info >= (3,): # pragma: 2.x no cover def _bootstrap(self): prctl_set_th_name(self.name) return super(JailThread, self)._bootstrap(); else: # pragma: 3.x no cover def __bootstrap(self): prctl_set_th_name(self.name) return Thread._Thread__bootstrap(self) @abstractmethod def status(self, flavor="basic"): # pragma: no cover - abstract """Abstract - Should provide status information. """ pass def start(self): """Sets active flag and starts thread. """ self.active = True super(JailThread, self).start() @abstractmethod def onStop(self): # pragma: no cover - absract """Abstract - Called when thread ends (after run). """ pass def stop(self): """Sets `active` property to False, to flag run method to return. """ self.active = False @abstractmethod def run(self): # pragma: no cover - absract """Abstract - Called when thread starts, thread stops when returns. """ pass def join(self): """ Safer join, that could be called also for not started (or ended) threads (used for cleanup). """ ## if cleanup needed - create derivate and call it before join... ## if was really started - should call join: if self.active is not None: super(JailThread, self).join() ## python 2.x replace binding of private __bootstrap method: if sys.version_info < (3,): # pragma: 3.x no cover JailThread._Thread__bootstrap = JailThread._JailThread__bootstrap ## python 3.9, restore isAlive method: elif not hasattr(JailThread, 'isAlive'): # pragma: 2.x no cover JailThread.isAlive = JailThread.is_alive server/mytime.py000064400000016777152231502160007747 0ustar00# 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. __author__ = "Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" import datetime import re import time ## # MyTime class. # class MyTime: """A wrapper around time module primarily for testing purposes This class is a wrapper around time.time() and time.gmtime(). When performing unit test, it is very useful to get a fixed value from these functions. Thus, time.time() and time.gmtime() should never be called directly. This wrapper should be called instead. The API are equivalent. """ myTime = None alternateNowTime = None alternateNow = None @staticmethod def setAlternateNow(t): """Set current time. Use None in order to always get the real current time. @param t the time to set or None """ MyTime.alternateNowTime = t MyTime.alternateNow = \ datetime.datetime.fromtimestamp(t) if t is not None else None @staticmethod def setTime(t): """Set current time. Use None in order to always get the real current time. @param t the time to set or None """ MyTime.myTime = t @staticmethod def time(): """Decorate time.time() for the purpose of testing mocking @return time.time() if setTime was called with None """ if MyTime.myTime is None: return time.time() else: return MyTime.myTime @staticmethod def gmtime(): """Decorate time.gmtime() for the purpose of testing mocking @return time.gmtime() if setTime was called with None """ if MyTime.myTime is None: return time.gmtime() else: return time.gmtime(MyTime.myTime) @staticmethod def now(): """Decorate datetime.now() for the purpose of testing mocking @return datetime.now() if setTime was called with None """ if MyTime.myTime is None: return datetime.datetime.now() if MyTime.myTime == MyTime.alternateNowTime: return MyTime.alternateNow return datetime.datetime.fromtimestamp(MyTime.myTime) @staticmethod def localtime(x=None): """Decorate time.localtime() for the purpose of testing mocking @return time.localtime() if setTime was called with None """ if MyTime.myTime is None or x is not None: return time.localtime(x) else: return time.localtime(MyTime.myTime) @staticmethod def time2str(unixTime, format="%Y-%m-%d %H:%M:%S"): """Convert time to a string representing as date and time using given format. Default format is ISO 8601, YYYY-MM-DD HH:MM:SS without microseconds. @return ISO-capable string representation of given unixTime """ # consider end of 9999th year (in GMT+23 to avoid year overflow in other TZ) dt = datetime.datetime.fromtimestamp( unixTime).replace(microsecond=0 ) if unixTime < 253402214400 else datetime.datetime(9999, 12, 31, 23, 59, 59) return dt.strftime(format) ## precreate/precompile primitives used in str2seconds: ## preparing expression: _str2sec_prep = re.compile(r"(?i)(?<=[a-z])(\d)") ## finally expression: _str2sec_fini = re.compile(r"(\d)\s+(\d)") ## wrapper for each sub part: _str2sec_subpart = r"(?i)(?<=[\d\s])(%s)\b" ## parts to be replaced - pair of (regexp x replacement): _str2sec_parts = ( (re.compile(_str2sec_subpart % r"days?|da|dd?"), "*"+str(24*60*60)), (re.compile(_str2sec_subpart % r"weeks?|wee?|ww?"), "*"+str(7*24*60*60)), (re.compile(_str2sec_subpart % r"months?|mon?"), "*"+str((365*3+366)*24*60*60/4/12)), (re.compile(_str2sec_subpart % r"years?|yea?|yy?"), "*"+str((365*3+366)*24*60*60/4)), (re.compile(_str2sec_subpart % r"seconds?|sec?|ss?"), "*"+str(1)), (re.compile(_str2sec_subpart % r"minutes?|min?|mm?"), "*"+str(60)), (re.compile(_str2sec_subpart % r"hours?|hou?|hh?"), "*"+str(60*60)), ) @staticmethod def str2seconds(val): """Wraps string expression like "1h 2m 3s" into number contains seconds (3723). The string expression will be evaluated as mathematical expression, spaces between each groups will be wrapped to "+" operand (only if any operand does not specified between). Because of case insensitivity and overwriting with minutes ("m" or "mm"), the short replacement for month are "mo" or "mon". Ex: 1hour+30min = 5400 0d 1h 30m = 5400 1year-6mo = 15778800 6 months = 15778800 warn: month is not 30 days, it is a year in seconds / 12, the leap years will be respected also: >>>> float(str2seconds("1month")) / 60 / 60 / 24 30.4375 >>>> float(str2seconds("1year")) / 60 / 60 / 24 365.25 @returns number (calculated seconds from expression "val") """ if isinstance(val, (int, float, complex)): return val # replace together standing abbreviations, example '1d12h' -> '1d 12h': val = MyTime._str2sec_prep.sub(r" \1", val) # replace abbreviation with expression: for rexp, rpl in MyTime._str2sec_parts: val = rexp.sub(rpl, val) val = MyTime._str2sec_fini.sub(r"\1+\2", val) return eval(val) class seconds2str(): """Converts seconds to string on demand (if string representation needed). Ex: seconds2str(86400*390) = 1y 3w 4d seconds2str(86400*368) = 1y 3d seconds2str(86400*365.5) = 1y seconds2str(86400*2+3600*7+60*15) = 2d 7h 15m seconds2str(86400*2+3599) = 2d 1h seconds2str(3600-5) = 1h seconds2str(3600-10) = 59m 50s seconds2str(59) = 59s """ def __init__(self, sec): self.sec = sec def __str__(self): # s = str(datetime.timedelta(seconds=int(self.sec))) # return s if s[-3:] != ":00" else s[:-3] s = self.sec; c = 3 # automatic accuracy: round by large values (and maximally 3 groups) if s >= 31536000: # a year as 365*24*60*60 (don't need to consider leap year by this accuracy) s = int(round(float(s)/86400)) # round by a day r = str(s//365) + 'y '; s %= 365 if s >= 7: r += str(s//7) + 'w '; s %= 7 if s: r += str(s) + 'd ' return r[:-1] if s >= 604800: # a week as 24*60*60*7 s = int(round(float(s)/3600)) # round by a hour r = str(s//168) + 'w '; s %= 168 if s >= 24: r += str(s//24) + 'd '; s %= 24 if s: r += str(s) + 'h ' return r[:-1] if s >= 86400: # a day as 24*60*60 s = int(round(float(s)/60)) # round by a minute r = str(s//1440) + 'd '; s %= 1440 if s >= 60: r += str(s//60) + 'h '; s %= 60 if s: r += str(s) + 'm ' return r[:-1] if s >= 3595: # a hour as 60*60 (- 5 seconds) s = int(round(float(s)/10)) # round by 10 seconds r = str(s//360) + 'h '; s %= 360 if s >= 6: # a minute r += str(s//6) + 'm '; s %= 6 return r[:-1] r = '' if s >= 60: # a minute r += str(s//60) + 'm '; s %= 60 if s: # remaining seconds r += str(s) + 's ' elif not self.sec: # 0s r = '0 ' return r[:-1] def __repr__(self): return self.__str__() server/observer.py000064400000042256152231502160010261 0ustar00# 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. # Author: Serg G. Brester (sebres) # # This module was written as part of ban time increment feature. __author__ = "Serg G. Brester (sebres)" __copyright__ = "Copyright (c) 2014 Serg G. Brester" __license__ = "GPL" import threading from .jailthread import JailThread from .failmanager import FailManagerEmpty import os, logging, time, datetime, math, json, random import sys from ..helpers import getLogger from .mytime import MyTime from .utils import Utils # Gets the instance of the logger. logSys = getLogger(__name__) class ObserverThread(JailThread): """Handles observing a database, managing bad ips and ban increment. Parameters ---------- Attributes ---------- daemon ident name status active : bool Control the state of the thread. idle : bool Control the idle state of the thread. sleeptime : int The time the thread sleeps for in the loop. """ # observer is event driven and it sleep organized incremental, so sleep intervals can be shortly: DEFAULT_SLEEP_INTERVAL = Utils.DEFAULT_SLEEP_INTERVAL / 10 def __init__(self): # init thread super(ObserverThread, self).__init__(name='f2b/observer') # before started - idle: self.idle = True ## Event queue self._queue_lock = threading.RLock() self._queue = [] ## Event, be notified if anything added to event queue self._notify = threading.Event() ## Sleep for max 60 seconds, it possible to specify infinite to always sleep up to notifying via event, ## but so we can later do some service "events" occurred infrequently directly in main loop of observer (not using queue) self.sleeptime = 60 # self._timers = {} self._paused = False self.__db = None self.__db_purge_interval = 60*60 # observer is a not main thread: self.daemon = True def __getitem__(self, i): try: return self._queue[i] except KeyError: raise KeyError("Invalid event index : %s" % i) def __delitem__(self, i): try: del self._queue[i] except KeyError: raise KeyError("Invalid event index: %s" % i) def __iter__(self): return iter(self._queue) def __len__(self): return len(self._queue) def __eq__(self, other): # Required for Threading return False def __hash__(self): # Required for Threading return id(self) def add_named_timer(self, name, starttime, *event): """Add a named timer event to queue will start (and wake) in 'starttime' seconds Previous timer event with same name will be canceled and trigger self into queue after new 'starttime' value """ t = self._timers.get(name, None) if t is not None: t.cancel() t = threading.Timer(starttime, self.add, event) self._timers[name] = t t.start() def add_timer(self, starttime, *event): """Add a timer event to queue will start (and wake) in 'starttime' seconds """ # in testing we should wait (looping) for the possible time drifts: if MyTime.myTime is not None and starttime: # test time after short sleep: t = threading.Timer(Utils.DEFAULT_SLEEP_INTERVAL, self._delayedEvent, (MyTime.time() + starttime, time.time() + starttime, event) ) t.start() return # add timer event: t = threading.Timer(starttime, self.add, event) t.start() def _delayedEvent(self, endMyTime, endTime, event): if MyTime.time() >= endMyTime or time.time() >= endTime: self.add_timer(0, *event) return # repeat after short sleep: t = threading.Timer(Utils.DEFAULT_SLEEP_INTERVAL, self._delayedEvent, (endMyTime, endTime, event) ) t.start() def pulse_notify(self): """Notify wakeup (sets /and resets/ notify event) """ if not self._paused: n = self._notify if n: n.set() #n.clear() def add(self, *event): """Add a event to queue and notify thread to wake up. """ ## lock and add new event to queue: with self._queue_lock: self._queue.append(event) self.pulse_notify() def add_wn(self, *event): """Add a event to queue withouth notifying thread to wake up. """ ## lock and add new event to queue: with self._queue_lock: self._queue.append(event) def call_lambda(self, l, *args): l(*args) def run(self): """Main loop for Threading. This function is the main loop of the thread. Returns ------- bool True when the thread exits nicely. """ logSys.info("Observer start...") ## first time create named timer to purge database each hour (clean old entries) ... self.add_named_timer('DB_PURGE', self.__db_purge_interval, 'db_purge') ## Mapping of all possible event types of observer: __meth = { # universal lambda: 'call': self.call_lambda, # system and service events: 'db_set': self.db_set, 'db_purge': self.db_purge, # service events of observer self: 'is_alive' : self.isAlive, 'is_active': self.isActive, 'start': self.start, 'stop': self.stop, 'nop': lambda:(), 'shutdown': lambda:() } try: ## check it self with sending is_alive event self.add('is_alive') ## if we should stop - break a main loop while self.active: self.idle = False ## check events available and execute all events from queue while not self._paused: ## lock, check and pop one from begin of queue: try: ev = None with self._queue_lock: if len(self._queue): ev = self._queue.pop(0) if ev is None: break ## retrieve method by name meth = ev[0] if not callable(ev[0]): meth = __meth.get(meth) or getattr(self, meth) ## execute it with rest of event as variable arguments meth(*ev[1:]) except Exception as e: #logSys.error('%s', e, exc_info=logSys.getEffectiveLevel()<=logging.DEBUG) logSys.error('%s', e, exc_info=True) ## going sleep, wait for events (in queue) n = self._notify if n: self.idle = True n.wait(self.sleeptime) ## wake up - reset signal now (we don't need it so long as we reed from queue) n.clear() if self._paused: continue else: ## notify event deleted (shutdown) - just sleep a little bit (waiting for shutdown events, prevent high cpu usage) time.sleep(ObserverThread.DEFAULT_SLEEP_INTERVAL) ## stop by shutdown and empty queue : if not self.is_full: break ## end of main loop - exit logSys.info("Observer stopped, %s events remaining.", len(self._queue)) self._notify = None #print("Observer stopped, %s events remaining." % len(self._queue)) except Exception as e: logSys.error('Observer stopped after error: %s', e, exc_info=True) #print("Observer stopped with error: %s" % str(e)) # clear all events - exit, for possible calls of wait_empty: with self._queue_lock: self._queue = [] self.idle = True return True def isAlive(self): #logSys.debug("Observer alive...") return True def isActive(self, fromStr=None): # logSys.info("Observer alive, %s%s", # 'active' if self.active else 'inactive', # '' if fromStr is None else (", called from '%s'" % fromStr)) return self.active def start(self): with self._queue_lock: if not self.active: super(ObserverThread, self).start() def stop(self, wtime=5, forceQuit=True): if self.active and self._notify: logSys.info("Observer stop ... try to end queue %s seconds", wtime) #print("Observer stop ....") # just add shutdown job to make possible wait later until full (events remaining) with self._queue_lock: self.add_wn('shutdown') #don't pulse - just set, because we will delete it hereafter (sometimes not wakeup) n = self._notify self._notify.set() #self.pulse_notify() self._notify = None # wait max wtime seconds until full (events remaining) if self.wait_empty(wtime) or forceQuit: n.clear() self.active = False; # leave outer (active) loop self._paused = True; # leave inner (queue) loop self.__db = None else: self._notify = n return self.wait_idle(min(wtime, 0.5)) and not self.is_full return True @property def is_full(self): with self._queue_lock: return True if len(self._queue) else False def wait_empty(self, sleeptime=None): """Wait observer is running and returns if observer has no more events (queue is empty) """ time.sleep(ObserverThread.DEFAULT_SLEEP_INTERVAL) if sleeptime is not None: e = MyTime.time() + sleeptime # block queue with not operation to be sure all really jobs are executed if nop goes from queue : if self._notify is not None: self.add_wn('nop') if self.is_full and self.idle: self.pulse_notify() while self.is_full: if sleeptime is not None and MyTime.time() > e: break time.sleep(ObserverThread.DEFAULT_SLEEP_INTERVAL) # wait idle to be sure the last queue element is processed (because pop event before processing it) : self.wait_idle(0.001) return not self.is_full def wait_idle(self, sleeptime=None): """Wait observer is running and returns if observer idle (observer sleeps) """ time.sleep(ObserverThread.DEFAULT_SLEEP_INTERVAL) if self.idle: return True if sleeptime is not None: e = MyTime.time() + sleeptime while not self.idle: if sleeptime is not None and MyTime.time() > e: break time.sleep(ObserverThread.DEFAULT_SLEEP_INTERVAL) return self.idle @property def paused(self): return self._paused; @paused.setter def paused(self, pause): if self._paused == pause: return self._paused = pause # wake after pause ended self.pulse_notify() @property def status(self): """Status of observer to be implemented. [TODO] """ return ('', '') ## ----------------------------------------- ## [Async] database service functionality ... ## ----------------------------------------- def db_set(self, db): self.__db = db def db_purge(self): logSys.debug("Purge database event occurred") if self.__db is not None: self.__db.purge() # trigger timer again ... self.add_named_timer('DB_PURGE', self.__db_purge_interval, 'db_purge') ## ----------------------------------------- ## [Async] ban time increment functionality ... ## ----------------------------------------- def failureFound(self, jail, ticket): """ Notify observer a failure for ip was found Observer will check ip was known (bad) and possibly increase an retry count """ # check jail active : if not jail.isAlive() or not jail.getBanTimeExtra("increment"): return ip = ticket.getID() unixTime = ticket.getTime() logSys.debug("[%s] Observer: failure found %s", jail.name, ip) # increase retry count for known (bad) ip, corresponding banCount of it (one try will count than 2, 3, 5, 9 ...) : banCount = 0 retryCount = 1 timeOfBan = None try: maxRetry = jail.filter.failManager.getMaxRetry() db = jail.database if db is not None: for banCount, timeOfBan, lastBanTime in db.getBan(ip, jail): banCount = max(banCount, ticket.getBanCount()) retryCount = ((1 << (banCount if banCount < 20 else 20))/2 + 1) # if lastBanTime == -1 or timeOfBan + lastBanTime * 2 > MyTime.time(): # retryCount = maxRetry break retryCount = min(retryCount, maxRetry) # check this ticket already known (line was already processed and in the database and will be restored from there): if timeOfBan is not None and unixTime <= timeOfBan: logSys.debug("[%s] Ignore failure %s before last ban %s < %s, restored", jail.name, ip, unixTime, timeOfBan) return # for not increased failures observer should not add it to fail manager, because was already added by filter self if retryCount <= 1: return # retry counter was increased - add it again: logSys.info("[%s] Found %s, bad - %s, %s # -> %s%s", jail.name, ip, MyTime.time2str(unixTime), banCount, retryCount, (', Ban' if retryCount >= maxRetry else '')) # retryCount-1, because a ticket was already once incremented by filter self retryCount = jail.filter.failManager.addFailure(ticket, retryCount - 1, True) ticket.setBanCount(banCount) # after observe we have increased attempt count, compare it >= maxretry ... if retryCount >= maxRetry: # perform the banning of the IP now (again) jail.filter.performBan(ip) except Exception as e: logSys.error('%s', e, exc_info=logSys.getEffectiveLevel()<=logging.DEBUG) class BanTimeIncr: def __init__(self, banTime, banCount): self.Time = banTime self.Count = banCount def calcBanTime(self, jail, banTime, banCount): be = jail.getBanTimeExtra() return be['evformula'](self.BanTimeIncr(banTime, banCount)) def incrBanTime(self, jail, banTime, ticket): """Check for IP address to increment ban time (if was already banned). Returns ------- float new ban time. """ # check jail active : if not jail.isAlive() or not jail.database: return banTime be = jail.getBanTimeExtra() ip = ticket.getID() orgBanTime = banTime # check ip was already banned (increment time of ban): try: if banTime > 0 and be.get('increment', False): # search IP in database and increase time if found: for banCount, timeOfBan, lastBanTime in \ jail.database.getBan(ip, jail, overalljails=be.get('overalljails', False)) \ : # increment count in ticket (if still not increased from banmanager, test-cases?): if banCount >= ticket.getBanCount(): ticket.setBanCount(banCount+1) logSys.debug('IP %s was already banned: %s #, %s', ip, banCount, timeOfBan); # calculate new ban time if banCount > 0: banTime = be['evformula'](self.BanTimeIncr(banTime, banCount)) ticket.setBanTime(banTime) # check current ticket time to prevent increasing for twice read tickets (restored from log file besides database after restart) if ticket.getTime() > timeOfBan: logSys.info('[%s] IP %s is bad: %s # last %s - incr %s to %s' % (jail.name, ip, banCount, MyTime.time2str(timeOfBan), MyTime.seconds2str(orgBanTime), MyTime.seconds2str(banTime))) else: ticket.restored = True break except Exception as e: logSys.error('%s', e, exc_info=logSys.getEffectiveLevel()<=logging.DEBUG) return banTime def banFound(self, ticket, jail, btime): """ Notify observer a ban occured for ip Observer will check ip was known (bad) and possibly increase/prolong a ban time Secondary we will actualize the bans and bips (bad ip) in database """ if ticket.restored: # pragma: no cover (normally not resored tickets only) return try: oldbtime = btime ip = ticket.getID() logSys.debug("[%s] Observer: ban found %s, %s", jail.name, ip, btime) # if not permanent and ban time was not set - check time should be increased: if btime != -1 and ticket.getBanTime() is None: btime = self.incrBanTime(jail, btime, ticket) # if we should prolong ban time: if btime == -1 or btime > oldbtime: ticket.setBanTime(btime) # if not permanent if btime != -1: bendtime = ticket.getTime() + btime logtime = (MyTime.seconds2str(btime), MyTime.time2str(bendtime)) # check ban is not too old : if bendtime < MyTime.time(): logSys.debug('Ignore old bantime %s', logtime[1]) return False else: logtime = ('permanent', 'infinite') # if ban time was prolonged - log again with new ban time: if btime != oldbtime: logSys.notice("[%s] Increase Ban %s (%d # %s -> %s)", jail.name, ip, ticket.getBanCount(), *logtime) # delayed prolonging ticket via actions that expected this (not later than 10 sec): logSys.log(5, "[%s] Observer: prolong %s in %s", jail.name, ip, (btime, oldbtime)) self.add_timer(min(10, max(0, btime - oldbtime - 5)), self.prolongBan, ticket, jail) # add ticket to database, but only if was not restored (not already read from database): if jail.database is not None and not ticket.restored: # add to database always only after ban time was calculated an not yet already banned: jail.database.addBan(jail, ticket) except Exception as e: logSys.error('%s', e, exc_info=logSys.getEffectiveLevel()<=logging.DEBUG) def prolongBan(self, ticket, jail): """ Notify observer a ban occured for ip Observer will check ip was known (bad) and possibly increase/prolong a ban time Secondary we will actualize the bans and bips (bad ip) in database """ try: btime = ticket.getBanTime() ip = ticket.getID() logSys.debug("[%s] Observer: prolong %s, %s", jail.name, ip, btime) # prolong ticket via actions that expected this: jail.actions._prolongBan(ticket) except Exception as e: logSys.error('%s', e, exc_info=logSys.getEffectiveLevel()<=logging.DEBUG) # Global observer initial created in server (could be later rewriten via singleton) class _Observers: def __init__(self): self.Main = None Observers = _Observers() server/server.py000064400000073401152231502160007734 0ustar00# 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. # Author: Cyril Jaquier # __author__ = "Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" import threading from threading import Lock, RLock import logging import os import signal import stat import sys from .observer import Observers, ObserverThread from .jails import Jails from .filter import DNSUtils, FileFilter, JournalFilter from .transmitter import Transmitter from .asyncserver import AsyncServer, AsyncServerException from .. import version from ..helpers import getLogger, _as_bool, extractOptions, str2LogLevel, \ getVerbosityFormat, excepthook, prctl_set_th_name # Gets the instance of the logger. logSys = getLogger(__name__) DEF_SYSLOGSOCKET = "auto" DEF_LOGLEVEL = "INFO" DEF_LOGTARGET = "STDOUT" try: from .database import Fail2BanDb except ImportError: # pragma: no cover # Dont print error here, as database may not even be used Fail2BanDb = None def _thread_name(): return threading.current_thread().__class__.__name__ try: FileExistsError except NameError: # pragma: 3.x no cover FileExistsError = OSError def _make_file_path(name): """Creates path of file (last level only) on demand""" name = os.path.dirname(name) # only if it is absolute (e. g. important for socket, so if unix path): if os.path.isabs(name): # be sure path exists (create last level of directory on demand): try: os.mkdir(name) except (OSError, FileExistsError) as e: if e.errno != 17: # pragma: no cover - not EEXIST is not covered raise class Server: def __init__(self, daemon=False): self.__loggingLock = Lock() self.__lock = RLock() self.__jails = Jails() self.__db = None self.__daemon = daemon self.__transm = Transmitter(self) self.__reload_state = {} #self.__asyncServer = AsyncServer(self.__transm) self.__asyncServer = None self.__logLevel = None self.__logTarget = None self.__verbose = None self.__syslogSocket = None self.__autoSyslogSocketPaths = { 'Darwin': '/var/run/syslog', 'FreeBSD': '/var/run/log', 'Linux': '/dev/log', } self.__prev_signals = {} def __sigTERMhandler(self, signum, frame): # pragma: no cover - indirect tested logSys.debug("Caught signal %d. Exiting", signum) self.quit() def __sigUSR1handler(self, signum, fname): # pragma: no cover - indirect tested logSys.debug("Caught signal %d. Flushing logs", signum) self.flushLogs() def _rebindSignal(self, s, new): """Bind new signal handler while storing old one in _prev_signals""" self.__prev_signals[s] = signal.getsignal(s) signal.signal(s, new) def start(self, sock, pidfile, force=False, observer=True, conf={}): # First set the mask to only allow access to owner os.umask(0o077) # Second daemonize before logging etc, because it will close all handles: if self.__daemon: # pragma: no cover logSys.info("Starting in daemon mode") ret = self.__createDaemon() # If forked parent - return here (parent process will configure server later): if ret is None: return False # If error: if not ret[0]: err = "Could not create daemon %s", ret[1:] logSys.error(err) raise ServerInitializationError(err) # We are daemon. # replace main thread (and process) name to identify server (for top/ps/pstree or diagnostic): prctl_set_th_name(conf.get("pname", "fail2ban-server")) # Set all logging parameters (or use default if not specified): self.__verbose = conf.get("verbose", None) self.setSyslogSocket(conf.get("syslogsocket", self.__syslogSocket if self.__syslogSocket is not None else DEF_SYSLOGSOCKET)) self.setLogLevel(conf.get("loglevel", self.__logLevel if self.__logLevel is not None else DEF_LOGLEVEL)) self.setLogTarget(conf.get("logtarget", self.__logTarget if self.__logTarget is not None else DEF_LOGTARGET)) logSys.info("-"*50) logSys.info("Starting Fail2ban v%s", version.version) if self.__daemon: # pragma: no cover logSys.info("Daemon started") # Install signal handlers if _thread_name() == '_MainThread': for s in (signal.SIGTERM, signal.SIGINT): self._rebindSignal(s, self.__sigTERMhandler) self._rebindSignal(signal.SIGUSR1, self.__sigUSR1handler) # Ensure unhandled exceptions are logged sys.excepthook = excepthook # Creates a PID file. try: logSys.debug("Creating PID file %s", pidfile) _make_file_path(pidfile) pidFile = open(pidfile, 'w') pidFile.write("%s\n" % os.getpid()) pidFile.close() except (OSError, IOError) as e: # pragma: no cover logSys.error("Unable to create PID file: %s", e) # Create observers and start it: if observer: if Observers.Main is None: Observers.Main = ObserverThread() Observers.Main.start() # Start the communication logSys.debug("Starting communication") try: _make_file_path(sock) self.__asyncServer = AsyncServer(self.__transm) self.__asyncServer.onstart = conf.get('onstart') self.__asyncServer.start(sock, force) except AsyncServerException as e: logSys.error("Could not start server: %s", e) # Stop (if not yet already executed): self.quit() # Removes the PID file. try: logSys.debug("Remove PID file %s", pidfile) os.remove(pidfile) except (OSError, IOError) as e: # pragma: no cover logSys.error("Unable to remove PID file: %s", e) def quit(self): # Prevent to call quit twice: self.quit = lambda: False logSys.info("Shutdown in progress...") # Stop communication first because if jail's unban action # tries to communicate via fail2ban-client we get a lockup # among threads. So the simplest resolution is to stop all # communications first (which should be ok anyways since we # are exiting) # See https://github.com/fail2ban/fail2ban/issues/7 if self.__asyncServer is not None: self.__asyncServer.stop_communication() # Restore default signal handlers: if _thread_name() == '_MainThread': for s, sh in self.__prev_signals.items(): signal.signal(s, sh) # Give observer a small chance to complete its work before exit obsMain = Observers.Main if obsMain is not None: if obsMain.stop(forceQuit=False): obsMain = None Observers.Main = None # Now stop all the jails self.stopAllJail() # Stop observer ultimately if obsMain is not None: obsMain.stop() # Explicit close database (server can leave in a thread, # so delayed GC can prevent commiting changes) if self.__db: self.__db.close() self.__db = None # Stop async and exit if self.__asyncServer is not None: self.__asyncServer.stop() self.__asyncServer = None logSys.info("Exiting Fail2ban") def addJail(self, name, backend): addflg = True if self.__reload_state.get(name) and self.__jails.exists(name): jail = self.__jails[name] # if backend switch - restart instead of reload: if jail.backend == backend: addflg = False logSys.info("Reload jail %r", name) # prevent to reload the same jail twice (temporary keep it in state, needed to commit reload): self.__reload_state[name] = None else: logSys.info("Restart jail %r (reason: %r != %r)", name, jail.backend, backend) self.delJail(name, stop=True) # prevent to start the same jail twice (no reload more - restart): del self.__reload_state[name] if addflg: self.__jails.add(name, backend, self.__db) if self.__db is not None: self.__db.addJail(self.__jails[name]) def delJail(self, name, stop=True, join=True): jail = self.__jails[name] if join or jail.isAlive(): jail.stop(stop=stop, join=join) if join: if self.__db is not None: self.__db.delJail(jail) del self.__jails[name] def startJail(self, name): with self.__lock: jail = self.__jails[name] if not jail.isAlive(): jail.start() elif name in self.__reload_state: logSys.info("Jail %r reloaded", name) del self.__reload_state[name] if jail.idle: jail.idle = False def stopJail(self, name): with self.__lock: self.delJail(name, stop=True) def stopAllJail(self): logSys.info("Stopping all jails") with self.__lock: # 1st stop all jails (signal and stop actions/filter thread): for name in list(self.__jails.keys()): self.delJail(name, stop=True, join=False) # 2nd wait for end and delete jails: for name in list(self.__jails.keys()): self.delJail(name, stop=False, join=True) def clearCaches(self): # we need to clear caches, to be able to recognize new IPs/families etc: DNSUtils.CACHE_nameToIp.clear() DNSUtils.CACHE_ipToName.clear() def reloadJails(self, name, opts, begin): if begin: # begin reload: if self.__reload_state and (name == '--all' or self.__reload_state.get(name)): # pragma: no cover raise ValueError('Reload already in progress') logSys.info("Reload " + (("jail %s" % name) if name != '--all' else "all jails")) with self.__lock: # if single jail: if name != '--all': jail = None # test jail exists (throws exception if not): if "--if-exists" not in opts or self.__jails.exists(name): jail = self.__jails[name] if jail: # first unban all ips (will be not restored after (re)start): if "--unban" in opts: self.setUnbanIP(name) # stop if expected: if "--restart" in opts: self.stopJail(name) else: # invalidate caches by reload self.clearCaches() # first unban all ips (will be not restored after (re)start): if "--unban" in opts: self.setUnbanIP() # stop if expected: if "--restart" in opts: self.stopAllJail() # first set all affected jail(s) to idle and reset filter regex and other lists/dicts: for jn, jail in self.__jails.items(): if name == '--all' or jn == name: jail.idle = True self.__reload_state[jn] = jail jail.filter.reload(begin=True) jail.actions.reload(begin=True) pass else: # end reload, all affected (or new) jails have already all new parameters (via stream) and (re)started: with self.__lock: deljails = [] for jn, jail in self.__jails.items(): # still in reload state: if jn in self.__reload_state: # remove jails that are not reloaded (untouched, so not in new configuration) deljails.append(jn) else: # commit (reload was finished): jail.filter.reload(begin=False) jail.actions.reload(begin=False) for jn in deljails: self.delJail(jn) self.__reload_state = {} logSys.info("Reload finished.") def setIdleJail(self, name, value): self.__jails[name].idle = value return True def getIdleJail(self, name): return self.__jails[name].idle # Filter def setIgnoreSelf(self, name, value): self.__jails[name].filter.ignoreSelf = _as_bool(value) def getIgnoreSelf(self, name): return self.__jails[name].filter.ignoreSelf def addIgnoreIP(self, name, ip): self.__jails[name].filter.addIgnoreIP(ip) def delIgnoreIP(self, name, ip): self.__jails[name].filter.delIgnoreIP(ip) def getIgnoreIP(self, name): return self.__jails[name].filter.getIgnoreIP() def addLogPath(self, name, fileName, tail=False): filter_ = self.__jails[name].filter if isinstance(filter_, FileFilter): filter_.addLogPath(fileName, tail) def delLogPath(self, name, fileName): filter_ = self.__jails[name].filter if isinstance(filter_, FileFilter): filter_.delLogPath(fileName) def getLogPath(self, name): filter_ = self.__jails[name].filter if isinstance(filter_, FileFilter): return filter_.getLogPaths() else: # pragma: systemd no cover logSys.debug("Jail %s is not a FileFilter instance" % name) return [] def addJournalMatch(self, name, match): # pragma: systemd no cover filter_ = self.__jails[name].filter if isinstance(filter_, JournalFilter): filter_.addJournalMatch(match) def delJournalMatch(self, name, match): # pragma: systemd no cover filter_ = self.__jails[name].filter if isinstance(filter_, JournalFilter): filter_.delJournalMatch(match) def getJournalMatch(self, name): # pragma: systemd no cover filter_ = self.__jails[name].filter if isinstance(filter_, JournalFilter): return filter_.getJournalMatch() else: logSys.debug("Jail %s is not a JournalFilter instance" % name) return [] def setLogEncoding(self, name, encoding): filter_ = self.__jails[name].filter filter_.setLogEncoding(encoding) def getLogEncoding(self, name): filter_ = self.__jails[name].filter return filter_.getLogEncoding() def setFindTime(self, name, value): self.__jails[name].filter.setFindTime(value) def getFindTime(self, name): return self.__jails[name].filter.getFindTime() def setDatePattern(self, name, pattern): self.__jails[name].filter.setDatePattern(pattern) def getDatePattern(self, name): return self.__jails[name].filter.getDatePattern() def setLogTimeZone(self, name, tz): self.__jails[name].filter.setLogTimeZone(tz) def getLogTimeZone(self, name): return self.__jails[name].filter.getLogTimeZone() def setIgnoreCommand(self, name, value): self.__jails[name].filter.ignoreCommand = value def getIgnoreCommand(self, name): return self.__jails[name].filter.ignoreCommand def setIgnoreCache(self, name, value): value, options = extractOptions("cache["+value+"]") self.__jails[name].filter.ignoreCache = options def getIgnoreCache(self, name): return self.__jails[name].filter.ignoreCache def setPrefRegex(self, name, value): flt = self.__jails[name].filter logSys.debug(" prefregex: %r", value) flt.prefRegex = value def getPrefRegex(self, name): return self.__jails[name].filter.prefRegex def addFailRegex(self, name, value, multiple=False): flt = self.__jails[name].filter if not multiple: value = (value,) for value in value: logSys.debug(" failregex: %r", value) flt.addFailRegex(value) def delFailRegex(self, name, index=None): self.__jails[name].filter.delFailRegex(index) def getFailRegex(self, name): return self.__jails[name].filter.getFailRegex() def addIgnoreRegex(self, name, value, multiple=False): flt = self.__jails[name].filter if not multiple: value = (value,) for value in value: logSys.debug(" ignoreregex: %r", value) flt.addIgnoreRegex(value) def delIgnoreRegex(self, name, index): self.__jails[name].filter.delIgnoreRegex(index) def getIgnoreRegex(self, name): return self.__jails[name].filter.getIgnoreRegex() def setUseDns(self, name, value): self.__jails[name].filter.setUseDns(value) def getUseDns(self, name): return self.__jails[name].filter.getUseDns() def setMaxMatches(self, name, value): self.__jails[name].filter.failManager.maxMatches = value def getMaxMatches(self, name): return self.__jails[name].filter.failManager.maxMatches def setMaxRetry(self, name, value): self.__jails[name].filter.setMaxRetry(value) def getMaxRetry(self, name): return self.__jails[name].filter.getMaxRetry() def setMaxLines(self, name, value): self.__jails[name].filter.setMaxLines(value) def getMaxLines(self, name): return self.__jails[name].filter.getMaxLines() # Action def addAction(self, name, value, *args): ## create (or reload) jail action: self.__jails[name].actions.add(value, *args, reload=name in self.__reload_state) def getActions(self, name): return self.__jails[name].actions def delAction(self, name, value): del self.__jails[name].actions[value] def getAction(self, name, value): return self.__jails[name].actions[value] def setBanTime(self, name, value): self.__jails[name].actions.setBanTime(value) def addAttemptIP(self, name, *args): return self.__jails[name].filter.addAttempt(*args) def setBanIP(self, name, value): return self.__jails[name].actions.addBannedIP(value) def setUnbanIP(self, name=None, value=None, ifexists=True): if name is not None: # single jail: jails = [self.__jails[name]] else: # in all jails: jails = list(self.__jails.values()) # unban given or all (if value is None): cnt = 0 ifexists |= (name is None) for jail in jails: cnt += jail.actions.removeBannedIP(value, ifexists=ifexists) return cnt def banned(self, name=None, ids=None): if name is not None: # single jail: jails = [self.__jails[name]] else: # in all jails: jails = list(self.__jails.values()) # check banned ids: res = [] if name is None and ids: for ip in ids: ret = [] for jail in jails: if jail.actions.getBanned([ip]): ret.append(jail.name) res.append(ret) else: for jail in jails: ret = jail.actions.getBanned(ids) if name is not None: return ret res.append(ret) else: res.append({jail.name: ret}) return res def getBanTime(self, name): return self.__jails[name].actions.getBanTime() def getBanList(self, name, withTime=False): """Returns the list of banned IP addresses for a jail. Parameters ---------- name : str The name of a jail. Returns ------- list The list of banned IP addresses. """ return self.__jails[name].actions.getBanList(withTime) def setBanTimeExtra(self, name, opt, value): self.__jails[name].setBanTimeExtra(opt, value) def getBanTimeExtra(self, name, opt): return self.__jails[name].getBanTimeExtra(opt) def isStarted(self): return self.__asyncServer is not None and self.__asyncServer.isActive() def isAlive(self, jailnum=None): if jailnum is not None and len(self.__jails) != jailnum: return 0 for jail in list(self.__jails.values()): if not jail.isAlive(): return 0 return 1 # Status def status(self): try: self.__lock.acquire() jails = list(self.__jails) jails.sort() jailList = ", ".join(jails) ret = [("Number of jail", len(self.__jails)), ("Jail list", jailList)] return ret finally: self.__lock.release() def statusJail(self, name, flavor="basic"): return self.__jails[name].status(flavor=flavor) # Logging ## # Set the logging level. # # CRITICAL # ERROR # WARNING # NOTICE # INFO # DEBUG # @param value the level def setLogLevel(self, value): value = value.upper() with self.__loggingLock: if self.__logLevel == value: return ll = str2LogLevel(value) # don't change real log-level if running from the test cases: getLogger("fail2ban").setLevel( ll if DEF_LOGTARGET != "INHERITED" or ll < logging.DEBUG else DEF_LOGLEVEL) self.__logLevel = value ## # Get the logging level. # # @see setLogLevel # @return the log level def getLogLevel(self): with self.__loggingLock: return self.__logLevel ## # Sets the logging target. # # target can be a file, SYSLOG, STDOUT or STDERR. # @param target the logging target def setLogTarget(self, target): # check reserved targets in uppercase, don't change target, because it can be file: target, logOptions = extractOptions(target) systarget = target.upper() with self.__loggingLock: # don't set new handlers if already the same # or if "INHERITED" (foreground worker of the test cases, to prevent stop logging): if self.__logTarget == target: return True if systarget == "INHERITED": self.__logTarget = target return True padding = logOptions.get('padding') # set a format which is simpler for console use if systarget == "SYSTEMD-JOURNAL": from systemd.journal import JournalHandler hdlr = JournalHandler(SYSLOG_IDENTIFIER='fail2ban') elif systarget == "SYSLOG": facility = logOptions.get('facility', 'DAEMON').upper() # backwards compatibility - default no padding for syslog handler: if padding is None: padding = '0' try: facility = getattr(logging.handlers.SysLogHandler, 'LOG_' + facility) except AttributeError: # pragma: no cover logSys.error("Unable to set facility %r, using 'DAEMON'", logOptions.get('facility')) facility = logging.handlers.SysLogHandler.LOG_DAEMON if self.__syslogSocket == "auto": import platform self.__syslogSocket = self.__autoSyslogSocketPaths.get( platform.system()) if self.__syslogSocket is not None\ and os.path.exists(self.__syslogSocket)\ and stat.S_ISSOCK(os.stat( self.__syslogSocket).st_mode): hdlr = logging.handlers.SysLogHandler( self.__syslogSocket, facility=facility) else: logSys.error( "Syslog socket file: %s does not exists" " or is not a socket" % self.__syslogSocket) return False elif systarget in ("STDOUT", "SYSOUT"): hdlr = logging.StreamHandler(sys.stdout) elif systarget == "STDERR": hdlr = logging.StreamHandler(sys.stderr) else: # Target should be a file try: open(target, "a").close() hdlr = logging.handlers.RotatingFileHandler(target) except IOError: logSys.error("Unable to log to %r", target) logSys.info("Logging to previous target %r", self.__logTarget) return False # Removes previous handlers -- in reverse order since removeHandler # alter the list in-place and that can confuses the iterable logger = getLogger("fail2ban") for handler in logger.handlers[::-1]: # Remove the handler. logger.removeHandler(handler) # And try to close -- it might be closed already try: handler.flush() handler.close() except (ValueError, KeyError): # pragma: no cover # Is known to be thrown after logging was shutdown once # with older Pythons -- seems to be safe to ignore there if sys.version_info < (3,) or sys.version_info >= (3, 2): raise # detailed format by deep log levels (as DEBUG=10): if logger.getEffectiveLevel() <= logging.DEBUG: # pragma: no cover if self.__verbose is None: self.__verbose = logging.DEBUG - logger.getEffectiveLevel() + 1 # If handler don't already add date to the message: addtime = logOptions.get('datetime') if addtime is not None: addtime = _as_bool(addtime) else: addtime = systarget not in ("SYSLOG", "SYSOUT") if padding is not None: padding = _as_bool(padding) else: padding = True # If log-format is redefined in options: if logOptions.get('format', '') != '': fmt = logOptions.get('format') else: # verbose log-format: verbose = 0 if self.__verbose is not None and self.__verbose > 2: # pragma: no cover verbose = self.__verbose-1 fmt = getVerbosityFormat(verbose, addtime=addtime, padding=padding) # tell the handler to use this format if target != "SYSTEMD-JOURNAL": hdlr.setFormatter(logging.Formatter(fmt)) logger.addHandler(hdlr) # Does not display this message at startup. if self.__logTarget is not None: logSys.info("Start Fail2ban v%s", version.version) logSys.info( "Changed logging target to %s for Fail2ban v%s" % ((target if target != "SYSLOG" else "%s (%s)" % (target, self.__syslogSocket)), version.version)) # Sets the logging target. self.__logTarget = target return True ## # Sets the syslog socket. # # syslogsocket is the full path to the syslog socket # @param syslogsocket the syslog socket path def setSyslogSocket(self, syslogsocket): with self.__loggingLock: if self.__syslogSocket == syslogsocket: return True self.__syslogSocket = syslogsocket # Conditionally reload, logtarget depends on socket path when SYSLOG return self.__logTarget != "SYSLOG"\ or self.setLogTarget(self.__logTarget) def getLogTarget(self): with self.__loggingLock: return self.__logTarget def getSyslogSocket(self): with self.__loggingLock: return self.__syslogSocket def flushLogs(self): if self.__logTarget not in ['STDERR', 'STDOUT', 'SYSLOG', 'SYSTEMD-JOURNAL']: for handler in getLogger("fail2ban").handlers: try: handler.doRollover() logSys.info("rollover performed on %s" % self.__logTarget) except AttributeError: handler.flush() logSys.info("flush performed on %s" % self.__logTarget) return "rolled over" else: for handler in getLogger("fail2ban").handlers: handler.flush() logSys.info("flush performed on %s" % self.__logTarget) return "flushed" @staticmethod def setIPv6IsAllowed(value): value = _as_bool(value) if value != 'auto' else None return DNSUtils.setIPv6IsAllowed(value) def setThreadOptions(self, value): for o, v in value.items(): if o == 'stacksize': threading.stack_size(int(v)*1024) else: # pragma: no cover raise KeyError("unknown option %r" % o) def getThreadOptions(self): return {'stacksize': threading.stack_size() // 1024} def setDatabase(self, filename): # if not changed - nothing to do if self.__db and self.__db.filename == filename: return if not self.__db and filename.lower() == 'none': return if len(self.__jails) != 0: raise RuntimeError( "Cannot change database when there are jails present") if filename.lower() == "none": self.__db = None else: if Fail2BanDb is not None: _make_file_path(filename) self.__db = Fail2BanDb(filename) self.__db.delAllJails() else: # pragma: no cover logSys.error( "Unable to import fail2ban database module as sqlite " "is not available.") if Observers.Main is not None: Observers.Main.db_set(self.__db) def getDatabase(self): return self.__db @staticmethod def __get_fdlist(): """Generate a list of open file descriptors. This wouldn't work on some platforms, or if proc/fdescfs not mounted, or a chroot environment, then it'd raise a FileExistsError. """ for path in ( '/proc/self/fd', # Linux, Cygwin and NetBSD '/proc/fd', # MacOS and FreeBSD ): if os.path.exists(path): def fdlist(): for name in os.listdir(path): if name.isdigit(): yield int(name) return fdlist() # other platform or unmounted, chroot etc: raise FileExistsError("fd-list not found") def __createDaemon(self): # pragma: no cover """ Detach a process from the controlling terminal and run it in the background as a daemon. http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/278731 """ # When the first child terminates, all processes in the second child # are sent a SIGHUP, so it's ignored. # We need to set this in the parent process, so it gets inherited by the # child process, and this makes sure that it is effect even if the parent # terminates quickly. self._rebindSignal(signal.SIGHUP, signal.SIG_IGN) try: # Fork a child process so the parent can exit. This will return control # to the command line or shell. This is required so that the new process # is guaranteed not to be a process group leader. We have this guarantee # because the process GID of the parent is inherited by the child, but # the child gets a new PID, making it impossible for its PID to equal its # PGID. pid = os.fork() except OSError as e: return (False, (e.errno, e.strerror)) # ERROR (return a tuple) if pid == 0: # The first child. # Next we call os.setsid() to become the session leader of this new # session. The process also becomes the process group leader of the # new process group. Since a controlling terminal is associated with a # session, and this new session has not yet acquired a controlling # terminal our process now has no controlling terminal. This shouldn't # fail, since we're guaranteed that the child is not a process group # leader. os.setsid() try: # Fork a second child to prevent zombies. Since the first child is # a session leader without a controlling terminal, it's possible for # it to acquire one by opening a terminal in the future. This second # fork guarantees that the child is no longer a session leader, thus # preventing the daemon from ever acquiring a controlling terminal. pid = os.fork() # Fork a second child. except OSError as e: return (False, (e.errno, e.strerror)) # ERROR (return a tuple) if (pid == 0): # The second child. # Ensure that the daemon doesn't keep any directory in use. Failure # to do this could make a filesystem unmountable. os.chdir("/") else: os._exit(0) # Exit parent (the first child) of the second child. else: # Signal to exit, parent of the first child. return None # Close all open files. Try to obtain the range of open descriptors directly. # As a fallback try the system configuration variable, SC_OPEN_MAX, # for the maximum number of open files to close. If it doesn't exist, use # the default value (configurable). try: fdlist = self.__get_fdlist() maxfd = -1 except: try: maxfd = os.sysconf("SC_OPEN_MAX") except (AttributeError, ValueError): maxfd = 256 # default maximum fdlist = range(maxfd+1) # urandom should not be closed in Python 3.4.0. Fixed in 3.4.1 # http://bugs.python.org/issue21207 if sys.version_info[0:3] == (3, 4, 0): # pragma: no cover urandom_fd = os.open("/dev/urandom", os.O_RDONLY) for fd in fdlist: try: if not os.path.sameopenfile(urandom_fd, fd): os.close(fd) except OSError: # ERROR (ignore) pass os.close(urandom_fd) elif maxfd == -1: for fd in fdlist: try: os.close(fd) except OSError: # ERROR (ignore) pass else: os.closerange(0, maxfd) # Redirect the standard file descriptors to /dev/null. os.open("/dev/null", os.O_RDONLY) # standard input (0) os.open("/dev/null", os.O_RDWR) # standard output (1) os.open("/dev/null", os.O_RDWR) # standard error (2) return (True,) class ServerInitializationError(Exception): pass server/strptime.py000064400000032146152231502160010276 0ustar00# emacs: -*- mode: python; coding: utf-8; 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 re import time import calendar import datetime from _strptime import LocaleTime, TimeRE, _calc_julian_from_U_or_W from .mytime import MyTime locale_time = LocaleTime() TZ_ABBR_RE = r"[A-Z](?:[A-Z]{2,4})?" FIXED_OFFSET_TZ_RE = re.compile(r"(%s)?([+-][01]\d(?::?\d{2})?)?$" % (TZ_ABBR_RE,)) timeRE = TimeRE() # %k - one- or two-digit number giving the hour of the day (0-23) on a 24-hour clock, # (corresponds %H, but allows space if not zero-padded). # %l - one- or two-digit number giving the hour of the day (12-11) on a 12-hour clock, # (corresponds %I, but allows space if not zero-padded). timeRE['k'] = r" ?(?P[0-2]?\d)" timeRE['l'] = r" ?(?P1?\d)" # TODO: because python currently does not support mixing of case-sensitive with case-insensitive matching, # check how TZ (in uppercase) can be combined with %a/%b etc. (that are currently case-insensitive), # to avoid invalid date-time recognition in strings like '11-Aug-2013 03:36:11.372 error ...' # with wrong TZ "error", which is at least not backwards compatible. # Hence %z currently match literal Z|UTC|GMT only (and offset-based), and %Exz - all zone abbreviations. timeRE['Z'] = r"(?PZ|[A-Z]{3,5})" timeRE['z'] = r"(?PZ|UTC|GMT|[+-][01]\d(?::?\d{2})?)" # Note: this extended tokens supported zone abbreviations, but it can parse 1 or 3-5 char(s) in lowercase, # see todo above. Don't use them in default date-patterns (if not anchored, few precise resp. optional). timeRE['ExZ'] = r"(?P%s)" % (TZ_ABBR_RE,) timeRE['Exz'] = r"(?P(?:%s)?[+-][01]\d(?::?\d{2})?|%s)" % (TZ_ABBR_RE, TZ_ABBR_RE) # overwrite default patterns, since they can be non-optimal: timeRE['d'] = r"(?P[1-2]\d|[0 ]?[1-9]|3[0-1])" timeRE['m'] = r"(?P0?[1-9]|1[0-2])" timeRE['Y'] = r"(?P\d{4})" timeRE['H'] = r"(?P[0-1]?\d|2[0-3])" timeRE['M'] = r"(?P[0-5]?\d)" timeRE['S'] = r"(?P[0-5]?\d|6[0-1])" # Extend build-in TimeRE with some exact patterns # exact two-digit patterns: timeRE['Exd'] = r"(?P[1-2]\d|0[1-9]|3[0-1])" timeRE['Exm'] = r"(?P0[1-9]|1[0-2])" timeRE['ExH'] = r"(?P[0-1]\d|2[0-3])" timeRE['Exk'] = r" ?(?P[0-1]?\d|2[0-3])" timeRE['Exl'] = r" ?(?P1[0-2]|\d)" timeRE['ExM'] = r"(?P[0-5]\d)" timeRE['ExS'] = r"(?P[0-5]\d|6[0-1])" def _updateTimeRE(): def _getYearCentRE(cent=(0,3), distance=3, now=(MyTime.now(), MyTime.alternateNow)): """ Build century regex for last year and the next years (distance). Thereby respect possible run in the test-cases (alternate date used there) """ cent = lambda year, f=cent[0], t=cent[1]: str(year)[f:t] def grp(exprset): c = None if len(exprset) > 1: for i in exprset: if c is None or i[0:-1] == c: c = i[0:-1] else: c = None break if not c: for i in exprset: if c is None or i[0] == c: c = i[0] else: c = None break if c: return "%s%s" % (c, grp([i[len(c):] for i in exprset])) return ("(?:%s)" % "|".join(exprset) if len(exprset[0]) > 1 else "[%s]" % "".join(exprset)) \ if len(exprset) > 1 else "".join(exprset) exprset = set( cent(now[0].year + i) for i in (-1, distance) ) if len(now) > 1 and now[1]: exprset |= set( cent(now[1].year + i) for i in range(-1, now[0].year-now[1].year+1, distance) ) return grp(sorted(list(exprset))) # more precise year patterns, within same century of last year and # the next 3 years (for possible long uptime of fail2ban); thereby # consider possible run in the test-cases (alternate date used there), # so accept years: 20xx (from test-date or 2001 up to current century) timeRE['ExY'] = r"(?P%s\d)" % _getYearCentRE(cent=(0,3), distance=3, now=(datetime.datetime.now(), datetime.datetime.fromtimestamp( min(MyTime.alternateNowTime or 978393600, 978393600)) ) ) timeRE['Exy'] = r"(?P\d{2})" _updateTimeRE() def getTimePatternRE(): keys = list(timeRE.keys()) patt = (r"%%(%%|%s|[%s])" % ( "|".join([k for k in keys if len(k) > 1]), "".join([k for k in keys if len(k) == 1]), )) names = { 'a': "DAY", 'A': "DAYNAME", 'b': "MON", 'B': "MONTH", 'd': "Day", 'H': "24hour", 'I': "12hour", 'j': "Yearday", 'm': "Month", 'M': "Minute", 'p': "AMPM", 'S': "Second", 'U': "Yearweek", 'w': "Weekday", 'W': "Yearweek", 'y': 'Year2', 'Y': "Year", '%': "%", 'z': "Zone offset", 'f': "Microseconds", 'Z': "Zone name", } for key in set(keys) - set(names): # may not have them all... if key.startswith('Ex'): kn = names.get(key[2:]) if kn: names[key] = "Ex" + kn continue names[key] = "%%%s" % key return (patt, names) def validateTimeZone(tz): """Validate a timezone and convert it to offset if it can (offset-based TZ). For now this accepts the UTC[+-]hhmm format (UTC has aliases GMT/Z and optional). Additionally it accepts all zone abbreviations mentioned below in TZ_STR. Note that currently this zone abbreviations are offset-based and used fixed offset without automatically DST-switch (if CET used then no automatically CEST-switch). In the future, it may be extended for named time zones (such as Europe/Paris) present on the system, if a suitable tz library is present (pytz). """ if tz is None: return None m = FIXED_OFFSET_TZ_RE.match(tz) if m is None: raise ValueError("Unknown or unsupported time zone: %r" % tz) tz = m.groups() return zone2offset(tz, 0) def zone2offset(tz, dt): """Return the proper offset, in minutes according to given timezone at a given time. Parameters ---------- tz: symbolic timezone or offset (for now only TZA?([+-]hh:?mm?)? is supported, as value are accepted: int offset; string in form like 'CET+0100' or 'UTC' or '-0400'; tuple (or list) in form (zone name, zone offset); dt: datetime instance for offset computation (currently unused) """ if isinstance(tz, int): return tz if isinstance(tz, str): return validateTimeZone(tz) tz, tzo = tz if tzo is None or tzo == '': # without offset return TZ_ABBR_OFFS[tz] if len(tzo) <= 3: # short tzo (hh only) # [+-]hh --> [+-]hh*60 return TZ_ABBR_OFFS[tz] + int(tzo)*60 if tzo[3] != ':': # [+-]hhmm --> [+-]1 * (hh*60 + mm) return TZ_ABBR_OFFS[tz] + (-1 if tzo[0] == '-' else 1) * (int(tzo[1:3])*60 + int(tzo[3:5])) else: # [+-]hh:mm --> [+-]1 * (hh*60 + mm) return TZ_ABBR_OFFS[tz] + (-1 if tzo[0] == '-' else 1) * (int(tzo[1:3])*60 + int(tzo[4:6])) def reGroupDictStrptime(found_dict, msec=False, default_tz=None): """Return time from dictionary of strptime fields This is tweaked from python built-in _strptime. Parameters ---------- found_dict : dict Dictionary where keys represent the strptime fields, and values the respective value. default_tz : default timezone to apply if nothing relevant is in found_dict (may be a non-fixed one in the future) Returns ------- float Unix time stamp. """ now = \ year = month = day = tzoffset = \ weekday = julian = week_of_year = None hour = minute = second = fraction = 0 for key, val in found_dict.items(): if val is None: continue # Directives not explicitly handled below: # c, x, X # handled by making out of other directives # U, W # worthless without day of the week if key == 'y': year = int(val) # Fail2ban year should be always in the current century (>= 2000) if year <= 2000: year += 2000 elif key == 'Y': year = int(val) elif key == 'm': month = int(val) elif key == 'B': month = locale_time.f_month.index(val.lower()) elif key == 'b': month = locale_time.a_month.index(val.lower()) elif key == 'd': day = int(val) elif key == 'H': hour = int(val) elif key == 'I': hour = int(val) ampm = found_dict.get('p', '').lower() # If there was no AM/PM indicator, we'll treat this like AM if ampm in ('', locale_time.am_pm[0]): # We're in AM so the hour is correct unless we're # looking at 12 midnight. # 12 midnight == 12 AM == hour 0 if hour == 12: hour = 0 elif ampm == locale_time.am_pm[1]: # We're in PM so we need to add 12 to the hour unless # we're looking at 12 noon. # 12 noon == 12 PM == hour 12 if hour != 12: hour += 12 elif key == 'M': minute = int(val) elif key == 'S': second = int(val) elif key == 'f': if msec: # pragma: no cover - currently unused s = val # Pad to always return microseconds. s += "0" * (6 - len(s)) fraction = int(s) elif key == 'A': weekday = locale_time.f_weekday.index(val.lower()) elif key == 'a': weekday = locale_time.a_weekday.index(val.lower()) elif key == 'w': weekday = int(val) - 1 if weekday < 0: weekday = 6 elif key == 'j': julian = int(val) elif key in ('U', 'W'): week_of_year = int(val) # U starts week on Sunday, W - on Monday week_of_year_start = 6 if key == 'U' else 0 elif key in ('z', 'Z'): z = val if z in ("Z", "UTC", "GMT"): tzoffset = 0 else: tzoffset = zone2offset(z, 0); # currently offset-based only # Fail2Ban will assume it's this year assume_year = False if year is None: if not now: now = MyTime.now() year = now.year assume_year = True if month is None or day is None: # If we know the week of the year and what day of that week, we can figure # out the Julian day of the year. if julian is None and week_of_year is not None and weekday is not None: julian = _calc_julian_from_U_or_W(year, week_of_year, weekday, (week_of_year_start == 0)) # Cannot pre-calculate datetime.datetime() since can change in Julian # calculation and thus could have different value for the day of the week # calculation. if julian is not None: datetime_result = datetime.datetime.fromordinal((julian - 1) + datetime.datetime(year, 1, 1).toordinal()) year = datetime_result.year month = datetime_result.month day = datetime_result.day # Fail2Ban assume today assume_today = False if month is None and day is None: if not now: now = MyTime.now() month = now.month day = now.day assume_today = True # Actully create date date_result = datetime.datetime( year, month, day, hour, minute, second, fraction) # Correct timezone if not supplied in the log linge if tzoffset is None and default_tz is not None: tzoffset = zone2offset(default_tz, date_result) # Add timezone info if tzoffset is not None: date_result -= datetime.timedelta(seconds=tzoffset * 60) if assume_today: if not now: now = MyTime.now() if date_result > now: # Rollover at midnight, could mean it's yesterday... date_result -= datetime.timedelta(days=1) if assume_year: if not now: now = MyTime.now() if date_result > now + datetime.timedelta(days=1): # ignore by timezone issues (+24h) # assume last year - also reset month and day as it's not yesterday... date_result = date_result.replace( year=year-1, month=month, day=day) # make time: if tzoffset is not None: tm = calendar.timegm(date_result.utctimetuple()) else: tm = time.mktime(date_result.timetuple()) if msec: # pragma: no cover - currently unused tm += fraction/1000000.0 return tm TZ_ABBR_OFFS = {'':0, None:0} TZ_STR = ''' -12 Y -11 X NUT SST -10 W CKT HAST HST TAHT TKT -9 V AKST GAMT GIT HADT HNY -8 U AKDT CIST HAY HNP PST PT -7 T HAP HNR MST PDT -6 S CST EAST GALT HAR HNC MDT -5 R CDT COT EASST ECT EST ET HAC HNE PET -4 Q AST BOT CLT COST EDT FKT GYT HAE HNA PYT -3 P ADT ART BRT CLST FKST GFT HAA PMST PYST SRT UYT WGT -2 O BRST FNT PMDT UYST WGST -1 N AZOT CVT EGT 0 Z EGST GMT UTC WET WT 1 A CET DFT WAT WEDT WEST 2 B CAT CEDT CEST EET SAST WAST 3 C EAT EEDT EEST IDT MSK 4 D AMT AZT GET GST KUYT MSD MUT RET SAMT SCT 5 E AMST AQTT AZST HMT MAWT MVT PKT TFT TJT TMT UZT YEKT 6 F ALMT BIOT BTT IOT KGT NOVT OMST YEKST 7 G CXT DAVT HOVT ICT KRAT NOVST OMSST THA WIB 8 H ACT AWST BDT BNT CAST HKT IRKT KRAST MYT PHT SGT ULAT WITA WST 9 I AWDT IRKST JST KST PWT TLT WDT WIT YAKT 10 K AEST ChST PGT VLAT YAKST YAPT 11 L AEDT LHDT MAGT NCT PONT SBT VLAST VUT 12 M ANAST ANAT FJT GILT MAGST MHT NZST PETST PETT TVT WFT 13 FJST NZDT 11.5 NFT 10.5 ACDT LHST 9.5 ACST 6.5 CCT MMT 5.75 NPT 5.5 SLT 4.5 AFT IRDT 3.5 IRST -2.5 HAT NDT -3.5 HNT NST NT -4.5 HLV VET -9.5 MART MIT ''' def _init_TZ_ABBR(): """Initialized TZ_ABBR_OFFS dictionary (TZ -> offset in minutes)""" for tzline in map(str.split, TZ_STR.split('\n')): if not len(tzline): continue tzoffset = int(float(tzline[0]) * 60) for tz in tzline[1:]: TZ_ABBR_OFFS[tz] = tzoffset _init_TZ_ABBR() server/ticket.py000064400000020407152231502160007707 0ustar00# 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. # Author: Cyril Jaquier # __author__ = "Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" from ..helpers import getLogger from .ipdns import IPAddr from .mytime import MyTime # Gets the instance of the logger. logSys = getLogger(__name__) class Ticket(object): __slots__ = ('_id', '_flags', '_banCount', '_banTime', '_time', '_data', '_retry', '_lastReset') MAX_TIME = 0X7FFFFFFFFFFF ;# 4461763-th year RESTORED = 0x01 BANNED = 0x08 def __init__(self, ip=None, time=None, matches=None, data={}, ticket=None): """Ticket constructor @param ip the IP address @param time the ban time @param matches (log) lines caused the ticket """ self.setID(ip) self._flags = 0; self._banCount = 0; self._banTime = None; self._time = time if time is not None else MyTime.time() self._data = {'matches': matches or [], 'failures': 0} if data is not None: for k,v in data.items(): if v is not None: self._data[k] = v if ticket: # ticket available - copy whole information from ticket: self.update(ticket) #self.__dict__.update(i for i in ticket.__dict__.iteritems() if i[0] in self.__dict__) def __str__(self): return "%s: ip=%s time=%s bantime=%s bancount=%s #attempts=%d matches=%r" % \ (self.__class__.__name__.split('.')[-1], self._id, self._time, self._banTime, self._banCount, self._data['failures'], self._data.get('matches', [])) def __repr__(self): return str(self) def __eq__(self, other): try: return self._id == other._id and \ round(self._time, 2) == round(other._time, 2) and \ self._data == other._data except AttributeError: return False def update(self, ticket): for n in ticket.__slots__: v = getattr(ticket, n, None) if v is not None: setattr(self, n, v) def setID(self, value): # guarantee using IPAddr instead of unicode, str for the IP if isinstance(value, str): value = IPAddr(value) self._id = value def getID(self): return self._id def getIP(self): return self._data.get('ip', self._id) def setTime(self, value): self._time = value def getTime(self): return self._time def setBanTime(self, value): self._banTime = value def getBanTime(self, defaultBT=None): return (self._banTime if self._banTime is not None else defaultBT) def setBanCount(self, value, always=False): if always or value > self._banCount: self._banCount = value def incrBanCount(self, value=1): self._banCount += value def getBanCount(self): return self._banCount; def getEndOfBanTime(self, defaultBT=None): bantime = (self._banTime if self._banTime is not None else defaultBT) # permanent if bantime == -1: return Ticket.MAX_TIME # unban time (end of ban): return self._time + bantime def isTimedOut(self, time, defaultBT=None): bantime = (self._banTime if self._banTime is not None else defaultBT) # permanent if bantime == -1: return False # timed out return (time > self._time + bantime) def setAttempt(self, value): self._data['failures'] = value def getAttempt(self): return self._data['failures'] def setMatches(self, matches): if matches: self._data['matches'] = matches else: try: del self._data['matches'] except KeyError: pass def getMatches(self): return [(line if not isinstance(line, (list, tuple)) else "".join(line)) \ for line in self._data.get('matches', ())] @property def restored(self): return self._flags & Ticket.RESTORED @restored.setter def restored(self, value): if value: self._flags |= Ticket.RESTORED else: self._flags &= ~(Ticket.RESTORED) @property def banned(self): return self._flags & Ticket.BANNED @banned.setter def banned(self, value): if value: self._flags |= Ticket.BANNED else: self._flags &= ~(Ticket.BANNED) def setData(self, *args, **argv): # if overwrite - set data and filter None values: if len(args) == 1: # todo: if support >= 2.7 only: # self._data = {k:v for k,v in args[0].iteritems() if v is not None} self._data = dict([(k,v) for k,v in args[0].items() if v is not None]) # add k,v list or dict (merge): elif len(args) == 2: self._data.update((args,)) elif len(args) > 2: self._data.update((k,v) for k,v in zip(*[iter(args)]*2)) if len(argv): self._data.update(argv) # filter (delete) None values: # todo: if support >= 2.7 only: # self._data = {k:v for k,v in self._data.iteritems() if v is not None} self._data = dict([(k,v) for k,v in self._data.items() if v is not None]) def getData(self, key=None, default=None): # return whole data dict: if key is None: return self._data # return default if not exists: if not self._data: return default if not isinstance(key,(str,type(None),int,float,bool,complex)): # return filtered by lambda/function: if callable(key): # todo: if support >= 2.7 only: # return {k:v for k,v in self._data.iteritems() if key(k)} return dict([(k,v) for k,v in self._data.items() if key(k)]) # return filtered by keys: if hasattr(key, '__iter__'): # todo: if support >= 2.7 only: # return {k:v for k,v in self._data.iteritems() if k in key} return dict([(k,v) for k,v in self._data.items() if k in key]) # return single value of data: return self._data.get(key, default) @property def banEpoch(self): return getattr(self, '_banEpoch', 0) @banEpoch.setter def banEpoch(self, value): self._banEpoch = value class FailTicket(Ticket): def __init__(self, ip=None, time=None, matches=None, data={}, ticket=None): # this class variables: self._firstTime = None self._retry = 1 # create/copy using default ticket constructor: Ticket.__init__(self, ip, time, matches, data, ticket) # init: if not isinstance(ticket, FailTicket): self._firstTime = time if time is not None else self.getTime() self._retry = self._data.get('failures', 1) def setRetry(self, value): """ Set artificial retry count, normally equal failures / attempt, used in incremental features (BanTimeIncr) to increase retry count for bad IPs """ self._retry = value if not self._data['failures']: self._data['failures'] = 1 if not value: self._data['failures'] = 0 self._data['matches'] = [] def getRetry(self): """ Returns failures / attempt count or artificial retry count increased for bad IPs """ return self._retry def adjustTime(self, time, maxTime): """ Adjust time of ticket and current attempts count considering given maxTime as estimation from rate by previous known interval (if it exceeds the findTime) """ if time > self._time: # expand current interval and attemps count (considering maxTime): if self._firstTime < time - maxTime: # adjust retry calculated as estimation from rate by previous known interval: self._retry = int(round(self._retry / float(time - self._firstTime) * maxTime)) self._firstTime = time - maxTime # last time of failure: self._time = time def inc(self, matches=None, attempt=1, count=1): self._retry += count self._data['failures'] += attempt if matches: # we should duplicate "matches", because possibly referenced to multiple tickets: if self._data['matches']: self._data['matches'] = self._data['matches'] + matches else: self._data['matches'] = matches @staticmethod def wrap(o): o.__class__ = FailTicket return o ## # Ban Ticket. # # This class extends the Ticket class. It is mainly used by the BanManager. class BanTicket(FailTicket): @staticmethod def wrap(o): o.__class__ = BanTicket return o server/transmitter.py000064400000037507152231502160011011 0ustar00# 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. # Author: Cyril Jaquier # __author__ = "Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" import time import json from ..helpers import getLogger, logging from .. import version # Gets the instance of the logger. logSys = getLogger(__name__) class Transmitter: ## # Constructor. # # @param The server reference def __init__(self, server): self.__server = server self.__quiet = 0 ## # Proceeds a command. # # Proceeds an incoming command. # @param command The incoming command def proceed(self, command): # Deserialize object logSys.log(5, "Command: %r", command) try: ret = self.__commandHandler(command) ack = 0, ret except Exception as e: logSys.error("Command %r has failed. Received %r", command, e, exc_info=logSys.getEffectiveLevel()<=logging.DEBUG) ack = 1, e return ack ## # Handle an command. # # def __commandHandler(self, command): name = command[0] if name == "ping": return "pong" elif name == "add": name = command[1] if name == "--all": raise Exception("Reserved name %r" % (name,)) try: backend = command[2] except IndexError: backend = "auto" self.__server.addJail(name, backend) return name elif name == "multi-set": return self.__commandSet(command[1:], True) elif name == "set": return self.__commandSet(command[1:]) elif name == "start": name = command[1] self.__server.startJail(name) return None elif name == "stop": if len(command) == 1: self.__server.quit() elif command[1] == "--all": self.__server.stopAllJail() else: name = command[1] self.__server.stopJail(name) return None elif name == "reload": opts = command[1:3] self.__quiet = 1 try: self.__server.reloadJails(*opts, begin=True) for cmd in command[3]: self.__commandHandler(cmd) finally: self.__quiet = 0 self.__server.reloadJails(*opts, begin=False) return 'OK' elif name == "unban" and len(command) >= 2: # unban in all jails: value = command[1:] # if all ips: if len(value) == 1 and value[0] == "--all": return self.__server.setUnbanIP() return self.__server.setUnbanIP(None, value) elif name == "banned": # check IP is banned in all jails: return self.__server.banned(None, command[1:]) elif name == "echo": return command[1:] elif name == "server-status": logSys.debug("Status: ready") return "Server ready" elif name == "server-stream": self.__quiet = 1 try: for cmd in command[1]: self.__commandHandler(cmd) finally: self.__quiet = 0 return None elif name == "sleep": value = command[1] time.sleep(float(value)) return None elif name == "flushlogs": return self.__server.flushLogs() elif name == "get": return self.__commandGet(command[1:]) elif name == "status": return self.status(command[1:]) elif name == "version": return version.version elif name == "config-error": logSys.error(command[1]) return None raise Exception("Invalid command") def __commandSet(self, command, multiple=False): name = command[0] # Logging if name == "loglevel": value = command[1] self.__server.setLogLevel(value) if self.__quiet: return return self.__server.getLogLevel() elif name == "logtarget": value = command[1] if self.__server.setLogTarget(value): if self.__quiet: return return self.__server.getLogTarget() else: raise Exception("Failed to change log target") elif name == "syslogsocket": value = command[1] if self.__server.setSyslogSocket(value): if self.__quiet: return return self.__server.getSyslogSocket() else: raise Exception("Failed to change syslog socket") elif name == "allowipv6": value = command[1] self.__server.setIPv6IsAllowed(value) if self.__quiet: return return value #Thread elif name == "thread": value = command[1] return self.__server.setThreadOptions(value) #Database elif name == "dbfile": self.__server.setDatabase(command[1]) db = self.__server.getDatabase() if db is None: return None else: if self.__quiet: return return db.filename elif name == "dbmaxmatches": db = self.__server.getDatabase() if db is None: logSys.log(logging.MSG, "dbmaxmatches setting was not in effect since no db yet") return None else: db.maxMatches = int(command[1]) if self.__quiet: return return db.maxMatches elif name == "dbpurgeage": db = self.__server.getDatabase() if db is None: logSys.log(logging.MSG, "dbpurgeage setting was not in effect since no db yet") return None else: db.purgeage = command[1] if self.__quiet: return return db.purgeage # Jail elif command[1] == "idle": if command[2] == "on": self.__server.setIdleJail(name, True) elif command[2] == "off": self.__server.setIdleJail(name, False) else: raise Exception("Invalid idle option, must be 'on' or 'off'") if self.__quiet: return return self.__server.getIdleJail(name) # Filter elif command[1] == "ignoreself": value = command[2] self.__server.setIgnoreSelf(name, value) if self.__quiet: return return self.__server.getIgnoreSelf(name) elif command[1] == "addignoreip": for value in command[2:]: self.__server.addIgnoreIP(name, value) if self.__quiet: return return self.__server.getIgnoreIP(name) elif command[1] == "delignoreip": value = command[2] self.__server.delIgnoreIP(name, value) if self.__quiet: return return self.__server.getIgnoreIP(name) elif command[1] == "ignorecommand": value = command[2] self.__server.setIgnoreCommand(name, value) if self.__quiet: return return self.__server.getIgnoreCommand(name) elif command[1] == "ignorecache": value = command[2] self.__server.setIgnoreCache(name, value) if self.__quiet: return return self.__server.getIgnoreCache(name) elif command[1] == "addlogpath": value = command[2] tail = False if len(command) == 4: if command[3].lower() == "tail": tail = True elif command[3].lower() != "head": raise ValueError("File option must be 'head' or 'tail'") elif len(command) > 4: raise ValueError("Only one file can be added at a time") self.__server.addLogPath(name, value, tail) if self.__quiet: return return self.__server.getLogPath(name) elif command[1] == "dellogpath": value = command[2] self.__server.delLogPath(name, value) if self.__quiet: return return self.__server.getLogPath(name) elif command[1] == "logencoding": value = command[2] self.__server.setLogEncoding(name, value) if self.__quiet: return return self.__server.getLogEncoding(name) elif command[1] == "addjournalmatch": # pragma: systemd no cover value = command[2:] self.__server.addJournalMatch(name, value) if self.__quiet: return return self.__server.getJournalMatch(name) elif command[1] == "deljournalmatch": # pragma: systemd no cover value = command[2:] self.__server.delJournalMatch(name, value) if self.__quiet: return return self.__server.getJournalMatch(name) elif command[1] == "prefregex": value = command[2] self.__server.setPrefRegex(name, value) if self.__quiet: return v = self.__server.getPrefRegex(name) return v.getRegex() if v else "" elif command[1] == "addfailregex": value = command[2] self.__server.addFailRegex(name, value, multiple=multiple) if multiple: return True if self.__quiet: return return self.__server.getFailRegex(name) elif command[1] == "delfailregex": value = int(command[2]) self.__server.delFailRegex(name, value) if self.__quiet: return return self.__server.getFailRegex(name) elif command[1] == "addignoreregex": value = command[2] self.__server.addIgnoreRegex(name, value, multiple=multiple) if multiple: return True if self.__quiet: return return self.__server.getIgnoreRegex(name) elif command[1] == "delignoreregex": value = int(command[2]) self.__server.delIgnoreRegex(name, value) if self.__quiet: return return self.__server.getIgnoreRegex(name) elif command[1] == "usedns": value = command[2] self.__server.setUseDns(name, value) if self.__quiet: return return self.__server.getUseDns(name) elif command[1] == "findtime": value = command[2] self.__server.setFindTime(name, value) if self.__quiet: return return self.__server.getFindTime(name) elif command[1] == "datepattern": value = command[2] self.__server.setDatePattern(name, value) if self.__quiet: return return self.__server.getDatePattern(name) elif command[1] == "logtimezone": value = command[2] self.__server.setLogTimeZone(name, value) if self.__quiet: return return self.__server.getLogTimeZone(name) elif command[1] == "maxmatches": value = command[2] self.__server.setMaxMatches(name, int(value)) if self.__quiet: return return self.__server.getMaxMatches(name) elif command[1] == "maxretry": value = command[2] self.__server.setMaxRetry(name, int(value)) if self.__quiet: return return self.__server.getMaxRetry(name) elif command[1] == "maxlines": value = command[2] self.__server.setMaxLines(name, int(value)) if self.__quiet: return return self.__server.getMaxLines(name) # command elif command[1] == "bantime": value = command[2] self.__server.setBanTime(name, value) if self.__quiet: return return self.__server.getBanTime(name) elif command[1] == "attempt": value = command[2:] if self.__quiet: return return self.__server.addAttemptIP(name, *value) elif command[1].startswith("bantime."): value = command[2] opt = command[1][len("bantime."):] self.__server.setBanTimeExtra(name, opt, value) if self.__quiet: return return self.__server.getBanTimeExtra(name, opt) elif command[1] == "banip": value = command[2:] return self.__server.setBanIP(name,value) elif command[1] == "unbanip": ifexists = True if command[2] != "--report-absent": value = command[2:] else: ifexists = False value = command[3:] return self.__server.setUnbanIP(name, value, ifexists=ifexists) elif command[1] == "addaction": args = [command[2]] if len(command) > 3: args.extend([command[3], json.loads(command[4])]) self.__server.addAction(name, *args) if self.__quiet: return return args[0] elif command[1] == "delaction": value = command[2] self.__server.delAction(name, value) return None elif command[1] == "action": actionname = command[2] action = self.__server.getAction(name, actionname) if multiple: for cmd in command[3]: logSys.log(5, " %r", cmd) actionkey = cmd[0] if callable(getattr(action, actionkey, None)): actionvalue = json.loads(cmd[1]) if len(cmd)>1 else {} getattr(action, actionkey)(**actionvalue) else: actionvalue = cmd[1] setattr(action, actionkey, actionvalue) return True else: actionkey = command[3] if callable(getattr(action, actionkey, None)): actionvalue = json.loads(command[4]) if len(command)>4 else {} if self.__quiet: return return getattr(action, actionkey)(**actionvalue) else: actionvalue = command[4] setattr(action, actionkey, actionvalue) if self.__quiet: return return getattr(action, actionkey) raise Exception("Invalid command %r (no set action or not yet implemented)" % (command[1],)) def __commandGet(self, command): name = command[0] # Logging if name == "loglevel": return self.__server.getLogLevel() elif name == "logtarget": return self.__server.getLogTarget() elif name == "syslogsocket": return self.__server.getSyslogSocket() #Thread elif name == "thread": return self.__server.getThreadOptions() #Database elif name == "dbfile": db = self.__server.getDatabase() if db is None: return None else: return db.filename elif name == "dbmaxmatches": db = self.__server.getDatabase() if db is None: return None else: return db.maxMatches elif name == "dbpurgeage": db = self.__server.getDatabase() if db is None: return None else: return db.purgeage # Jail, Filter elif command[1] == "banned": # check IP is banned in all jails: return self.__server.banned(name, command[2:]) elif command[1] == "logpath": return self.__server.getLogPath(name) elif command[1] == "logencoding": return self.__server.getLogEncoding(name) elif command[1] == "journalmatch": # pragma: systemd no cover return self.__server.getJournalMatch(name) elif command[1] == "ignoreself": return self.__server.getIgnoreSelf(name) elif command[1] == "ignoreip": return self.__server.getIgnoreIP(name) elif command[1] == "ignorecommand": return self.__server.getIgnoreCommand(name) elif command[1] == "ignorecache": return self.__server.getIgnoreCache(name) elif command[1] == "prefregex": v = self.__server.getPrefRegex(name) return v.getRegex() if v else "" elif command[1] == "failregex": return self.__server.getFailRegex(name) elif command[1] == "ignoreregex": return self.__server.getIgnoreRegex(name) elif command[1] == "usedns": return self.__server.getUseDns(name) elif command[1] == "findtime": return self.__server.getFindTime(name) elif command[1] == "datepattern": return self.__server.getDatePattern(name) elif command[1] == "logtimezone": return self.__server.getLogTimeZone(name) elif command[1] == "maxmatches": return self.__server.getMaxMatches(name) elif command[1] == "maxretry": return self.__server.getMaxRetry(name) elif command[1] == "maxlines": return self.__server.getMaxLines(name) # Action elif command[1] == "bantime": return self.__server.getBanTime(name) elif command[1] == "banip": return self.__server.getBanList(name, withTime=len(command) > 2 and command[2] == "--with-time") elif command[1].startswith("bantime."): opt = command[1][len("bantime."):] return self.__server.getBanTimeExtra(name, opt) elif command[1] == "actions": return list(self.__server.getActions(name).keys()) elif command[1] == "action": actionname = command[2] actionvalue = command[3] action = self.__server.getAction(name, actionname) return getattr(action, actionvalue) elif command[1] == "actionproperties": actionname = command[2] action = self.__server.getAction(name, actionname) return [ key for key in dir(action) if not key.startswith("_") and not callable(getattr(action, key))] elif command[1] == "actionmethods": actionname = command[2] action = self.__server.getAction(name, actionname) return [ key for key in dir(action) if not key.startswith("_") and callable(getattr(action, key))] raise Exception("Invalid command (no get action or not yet implemented)") def status(self, command): if len(command) == 0: return self.__server.status() elif len(command) == 1: name = command[0] return self.__server.statusJail(name) elif len(command) == 2: name = command[0] flavor = command[1] return self.__server.statusJail(name, flavor=flavor) raise Exception("Invalid command (no status)") server/utils.py000064400000027330152231502160007566 0ustar00# 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. __author__ = "Serg G. Brester (sebres) and Fail2Ban Contributors" __copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2012 Yaroslav Halchenko, 2012-2015 Serg G. Brester" __license__ = "GPL" import fcntl import logging import os import signal import subprocess import sys from threading import Lock import time from ..helpers import getLogger, _merge_dicts, uni_decode from collections import OrderedDict if sys.version_info >= (3, 3): import importlib.machinery else: import imp # Gets the instance of the logger. logSys = getLogger(__name__) # Some hints on common abnormal exit codes _RETCODE_HINTS = { 127: '"Command not found". Make sure that all commands in %(realCmd)r ' 'are in the PATH of fail2ban-server process ' '(grep -a PATH= /proc/`pidof -x fail2ban-server`/environ). ' 'You may want to start ' '"fail2ban-server -f" separately, initiate it with ' '"fail2ban-client reload" in another shell session and observe if ' 'additional informative error messages appear in the terminals.' } # Dictionary to lookup signal name from number signame = dict((num, name) for name, num in signal.__dict__.items() if name.startswith("SIG")) class Utils(): """Utilities provide diverse static methods like executes OS shell commands, etc. """ DEFAULT_SLEEP_TIME = 2 DEFAULT_SLEEP_INTERVAL = 0.2 DEFAULT_SHORT_INTERVAL = 0.001 DEFAULT_SHORTEST_INTERVAL = DEFAULT_SHORT_INTERVAL / 100 class Cache(object): """A simple cache with a TTL and limit on size """ def __init__(self, *args, **kwargs): self.setOptions(*args, **kwargs) self._cache = OrderedDict() self.__lock = Lock() def setOptions(self, maxCount=1000, maxTime=60): self.maxCount = maxCount self.maxTime = maxTime def __len__(self): return len(self._cache) def get(self, k, defv=None): v = self._cache.get(k) if v: if v[1] > time.time(): return v[0] self.unset(k) return defv def set(self, k, v): t = time.time() # avoid multiple modification of dict multi-threaded: cache = self._cache with self.__lock: # clean cache if max count reached: if len(cache) >= self.maxCount: # ordered (so remove some from ahead, FIFO) while cache: (ck, cv) = cache.popitem(last=False) # if not yet expired (but has free slot for new entry): if cv[1] > t and len(cache) < self.maxCount: break # set now: cache[k] = (v, t + self.maxTime) def unset(self, k): with self.__lock: self._cache.pop(k, None) def clear(self): with self.__lock: self._cache.clear() @staticmethod def setFBlockMode(fhandle, value): flags = fcntl.fcntl(fhandle, fcntl.F_GETFL) if not value: flags |= os.O_NONBLOCK else: flags &= ~os.O_NONBLOCK fcntl.fcntl(fhandle, fcntl.F_SETFL, flags) return flags @staticmethod def buildShellCmd(realCmd, varsDict): """Generates new shell command as array, contains map as variables to arguments statement (varsStat), the command (realCmd) used this variables and the list of the arguments, mapped from varsDict Example: buildShellCmd('echo "V2: $v2, V1: $v1"', {"v1": "val 1", "v2": "val 2", "vUnused": "unused var"}) returns: ['v1=$0 v2=$1 vUnused=$2 \necho "V2: $v2, V1: $v1"', 'val 1', 'val 2', 'unused var'] """ # build map as array of vars and command line array: varsStat = "" if not isinstance(realCmd, list): realCmd = [realCmd] i = len(realCmd)-1 for k, v in varsDict.items(): varsStat += "%s=$%s " % (k, i) realCmd.append(v) i += 1 realCmd[0] = varsStat + "\n" + realCmd[0] return realCmd @staticmethod def executeCmd(realCmd, timeout=60, shell=True, output=False, tout_kill_tree=True, success_codes=(0,), varsDict=None): """Executes a command. Parameters ---------- realCmd : str The command to execute. timeout : int The time out in seconds for the command. shell : bool If shell is True (default), the specified command (may be a string) will be executed through the shell. output : bool If output is True, the function returns tuple (success, stdoutdata, stderrdata, returncode). If False, just indication of success is returned varsDict: dict variables supplied to the command (or to the shell script) Returns ------- bool or (bool, str, str, int) True if the command succeeded and with stdout, stderr, returncode if output was set to True Raises ------ OSError If command fails to be executed. RuntimeError If command execution times out. """ stdout = stderr = None retcode = None popen = env = None if varsDict: if shell: # build map as array of vars and command line array: realCmd = Utils.buildShellCmd(realCmd, varsDict) else: # pragma: no cover - currently unused env = _merge_dicts(os.environ, varsDict) realCmdId = id(realCmd) logCmd = lambda level: logSys.log(level, "%x -- exec: %s", realCmdId, realCmd) try: popen = subprocess.Popen( realCmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=shell, env=env, preexec_fn=os.setsid # so that killpg does not kill our process ) # wait with timeout for process has terminated: retcode = popen.poll() if retcode is None: def _popen_wait_end(): retcode = popen.poll() return (True, retcode) if retcode is not None else None # popen.poll is fast operation so we can use the shortest sleep interval: retcode = Utils.wait_for(_popen_wait_end, timeout, Utils.DEFAULT_SHORTEST_INTERVAL) if retcode: retcode = retcode[1] # if timeout: if retcode is None: if logCmd: logCmd(logging.ERROR); logCmd = None logSys.error("%x -- timed out after %s seconds." % (realCmdId, timeout)) pgid = os.getpgid(popen.pid) # if not tree - first try to terminate and then kill, otherwise - kill (-9) only: os.killpg(pgid, signal.SIGTERM) # Terminate the process time.sleep(Utils.DEFAULT_SLEEP_INTERVAL) retcode = popen.poll() #logSys.debug("%s -- terminated %s ", realCmd, retcode) if retcode is None or tout_kill_tree: # Still going... os.killpg(pgid, signal.SIGKILL) # Kill the process time.sleep(Utils.DEFAULT_SLEEP_INTERVAL) if retcode is None: # pragma: no cover - too sporadic retcode = popen.poll() #logSys.debug("%s -- killed %s ", realCmd, retcode) if retcode is None and not Utils.pid_exists(pgid): # pragma: no cover retcode = signal.SIGKILL except OSError as e: if logCmd: logCmd(logging.ERROR); logCmd = None stderr = "%s -- failed with %s" % (realCmd, e) logSys.error(stderr) if not popen: return False if not output else (False, stdout, stderr, retcode) std_level = logging.DEBUG if retcode in success_codes else logging.ERROR if std_level >= logSys.getEffectiveLevel(): if logCmd: logCmd(std_level-1 if std_level == logging.DEBUG else logging.ERROR); logCmd = None # if we need output (to return or to log it): if output or std_level >= logSys.getEffectiveLevel(): # if was timeouted (killed/terminated) - to prevent waiting, set std handles to non-blocking mode. if popen.stdout: try: if retcode is None or retcode < 0: Utils.setFBlockMode(popen.stdout, False) stdout = popen.stdout.read() except IOError as e: # pragma: no cover logSys.error(" ... -- failed to read stdout %s", e) if stdout is not None and stdout != '' and std_level >= logSys.getEffectiveLevel(): for l in stdout.splitlines(): logSys.log(std_level, "%x -- stdout: %r", realCmdId, uni_decode(l)) if popen.stderr: try: if retcode is None or retcode < 0: Utils.setFBlockMode(popen.stderr, False) stderr = popen.stderr.read() except IOError as e: # pragma: no cover logSys.error(" ... -- failed to read stderr %s", e) if stderr is not None and stderr != '' and std_level >= logSys.getEffectiveLevel(): for l in stderr.splitlines(): logSys.log(std_level, "%x -- stderr: %r", realCmdId, uni_decode(l)) if popen.stdout: popen.stdout.close() if popen.stderr: popen.stderr.close() success = False if retcode in success_codes: logSys.debug("%x -- returned successfully %i", realCmdId, retcode) success = True elif retcode is None: logSys.error("%x -- unable to kill PID %i", realCmdId, popen.pid) elif retcode < 0 or retcode > 128: # dash would return negative while bash 128 + n sigcode = -retcode if retcode < 0 else retcode - 128 logSys.error("%x -- killed with %s (return code: %s)", realCmdId, signame.get(sigcode, "signal %i" % sigcode), retcode) else: msg = _RETCODE_HINTS.get(retcode, None) logSys.error("%x -- returned %i", realCmdId, retcode) if msg: logSys.info("HINT on %i: %s", retcode, msg % locals()) if output: return success, stdout, stderr, retcode return success if len(success_codes) == 1 else (success, retcode) @staticmethod def wait_for(cond, timeout, interval=None): """Wait until condition expression `cond` is True, up to `timeout` sec Parameters ---------- cond : callable The expression to check condition (should return equivalent to bool True if wait successful). timeout : float or callable The time out for end of wait (in seconds or callable that returns True if timeout occurred). interval : float (optional) Polling start interval for wait cycle in seconds. Returns ------- variable The return value of the last call of `cond`, logical False (or None, 0, etc) if timeout occurred. """ #logSys.log(5, " wait for %r, tout: %r / %r", cond, timeout, interval) ini = 1 # to delay initializations until/when necessary while True: ret = cond() if ret: return ret if ini: ini = stm = 0 if not callable(timeout): time0 = time.time() + timeout timeout_expr = lambda: time.time() > time0 else: timeout_expr = timeout if timeout_expr(): break stm = min(stm + (interval or Utils.DEFAULT_SLEEP_INTERVAL), Utils.DEFAULT_SLEEP_TIME) time.sleep(stm) return ret # Solution from http://stackoverflow.com/questions/568271/how-to-check-if-there-exists-a-process-with-a-given-pid # under cc by-sa 3.0 if os.name == 'posix': @staticmethod def pid_exists(pid): """Check whether pid exists in the current process table.""" import errno if pid < 0: return False try: os.kill(pid, 0) except OSError as e: return e.errno == errno.EPERM else: return True else: # pragma: no cover (no windows currently supported) @staticmethod def pid_exists(pid): import ctypes kernel32 = ctypes.windll.kernel32 SYNCHRONIZE = 0x100000 process = kernel32.OpenProcess(SYNCHRONIZE, 0, pid) if process != 0: kernel32.CloseHandle(process) return True else: return False @staticmethod def load_python_module(pythonModule): pythonModuleName = os.path.splitext( os.path.basename(pythonModule))[0] if sys.version_info >= (3, 3): mod = importlib.machinery.SourceFileLoader( pythonModuleName, pythonModule).load_module() else: mod = imp.load_source( pythonModuleName, pythonModule) return mod __init__.py000064400000005322152231502160006654 0ustar00# 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. # Author: Cyril Jaquier # __author__ = "Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" import logging.handlers # Custom debug levels logging.MSG = logging.INFO - 2 logging.TRACEDEBUG = 7 logging.HEAVYDEBUG = 5 logging.addLevelName(logging.MSG, 'MSG') logging.addLevelName(logging.TRACEDEBUG, 'TRACE') logging.addLevelName(logging.HEAVYDEBUG, 'HEAVY') """ Below derived from: https://mail.python.org/pipermail/tutor/2007-August/056243.html """ logging.NOTICE = logging.INFO + 5 logging.addLevelName(logging.NOTICE, 'NOTICE') # define a new logger function for notice # this is exactly like existing info, critical, debug...etc def _Logger_notice(self, msg, *args, **kwargs): """ Log 'msg % args' with severity 'NOTICE'. To pass exception information, use the keyword argument exc_info with a true value, e.g. logger.notice("Houston, we have a %s", "major disaster", exc_info=1) """ if self.isEnabledFor(logging.NOTICE): self._log(logging.NOTICE, msg, args, **kwargs) logging.Logger.notice = _Logger_notice # define a new root level notice function # this is exactly like existing info, critical, debug...etc def _root_notice(msg, *args, **kwargs): """ Log a message with severity 'NOTICE' on the root logger. """ if len(logging.root.handlers) == 0: logging.basicConfig() logging.root.notice(msg, *args, **kwargs) # make the notice root level function known logging.notice = _root_notice # add NOTICE to the priority map of all the levels logging.handlers.SysLogHandler.priority_map['NOTICE'] = 'notice' from time import strptime # strptime thread safety hack-around - http://bugs.python.org/issue7980 strptime("2012", "%Y") # short names for pure numeric log-level ("Level 25" could be truncated by short formats): def _init(): for i in range(50): if logging.getLevelName(i).startswith('Level'): logging.addLevelName(i, '#%02d-Lev.' % i) _init() exceptions.py000064400000002214152231502160007273 0ustar00# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- # vi: set ft=python sts=4 ts=4 sw=4 noet : """Fail2Ban exceptions used by both client and server """ # 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. __author__ = "Cyril Jaquier, Yaroslav Halchenko" __copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2012 Yaroslav Halchenko" __license__ = "GPL" # # Jails # class DuplicateJailException(Exception): pass class UnknownJailException(KeyError): pass helpers.py000064400000043543152231502160006566 0ustar00# 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. __author__ = "Cyril Jaquier, Arturo 'Buanzo' Busleiman, Yaroslav Halchenko" __license__ = "GPL" import gc import locale import logging import os import re import sys import traceback from threading import Lock from .server.mytime import MyTime import imp try: import ctypes _libcap = ctypes.CDLL('libcap.so.2') except: _libcap = None PREFER_ENC = locale.getpreferredencoding() # correct preferred encoding if lang not set in environment: if PREFER_ENC.startswith('ANSI_'): # pragma: no cover if sys.stdout and sys.stdout.encoding is not None and not sys.stdout.encoding.startswith('ANSI_'): PREFER_ENC = sys.stdout.encoding elif all((os.getenv(v) in (None, "") for v in ('LANGUAGE', 'LC_ALL', 'LC_CTYPE', 'LANG'))): PREFER_ENC = 'UTF-8'; # py-2.x: try to minimize influence of sporadic conversion errors on python 2.x, # caused by implicit converting of string/unicode (e. g. `str(u"\uFFFD")` produces an error # if default encoding is 'ascii'); if sys.version_info < (3,): # pragma: 3.x no cover # correct default (global system) encoding (mostly UTF-8): def __resetDefaultEncoding(encoding): global PREFER_ENC ode = sys.getdefaultencoding().upper() if ode == 'ASCII' and ode != PREFER_ENC.upper(): # setdefaultencoding is normally deleted after site initialized, so hack-in using load of sys-module: _sys = sys if not hasattr(_sys, "setdefaultencoding"): try: from imp import load_dynamic as __ldm _sys = __ldm('_sys', 'sys') except ImportError: # pragma: no cover - only if load_dynamic fails imp.reload(sys) _sys = sys if hasattr(_sys, "setdefaultencoding"): _sys.setdefaultencoding(encoding) # override to PREFER_ENC: __resetDefaultEncoding(PREFER_ENC) del __resetDefaultEncoding # todo: rewrite explicit (and implicit) str-conversions via encode/decode with IO-encoding (sys.stdout.encoding), # e. g. inside tags-replacement by command-actions, etc. # # Following "uni_decode", "uni_string" functions unified python independent any # to string converting. # # Typical example resp. work-case for understanding the coding/decoding issues: # # [isinstance('', str), isinstance(b'', str), isinstance(u'', str)] # [True, True, False]; # -- python2 # [True, False, True]; # -- python3 # if sys.version_info >= (3,): # pragma: 2.x no cover def uni_decode(x, enc=PREFER_ENC, errors='strict'): try: if isinstance(x, bytes): return x.decode(enc, errors) return x except (UnicodeDecodeError, UnicodeEncodeError): # pragma: no cover - unsure if reachable if errors != 'strict': raise return x.decode(enc, 'replace') def uni_string(x): if not isinstance(x, bytes): return str(x) return x.decode(PREFER_ENC, 'replace') else: # pragma: 3.x no cover def uni_decode(x, enc=PREFER_ENC, errors='strict'): try: if isinstance(x, str): return x.encode(enc, errors) return x except (UnicodeDecodeError, UnicodeEncodeError): # pragma: no cover - unsure if reachable if errors != 'strict': raise return x.encode(enc, 'replace') if sys.getdefaultencoding().upper() != 'UTF-8': # pragma: no cover - utf-8 is default encoding now def uni_string(x): if not isinstance(x, str): return str(x) return x.encode(PREFER_ENC, 'replace') else: uni_string = str def _as_bool(val): return bool(val) if not isinstance(val, str) \ else val.lower() in ('1', 'on', 'true', 'yes') def formatExceptionInfo(): """ Consistently format exception information """ cla, exc = sys.exc_info()[:2] return (cla.__name__, uni_string(exc)) # # Following "traceback" functions are adopted from PyMVPA distributed # under MIT/Expat and copyright by PyMVPA developers (i.e. me and # Michael). Hereby I re-license derivative work on these pieces under GPL # to stay in line with the main Fail2Ban license # def mbasename(s): """Custom function to include directory name if filename is too common Also strip .py at the end """ base = os.path.basename(s) if base.endswith('.py'): base = base[:-3] if base in set(['base', '__init__']): base = os.path.basename(os.path.dirname(s)) + '.' + base return base class TraceBack(object): """Customized traceback to be included in debug messages """ def __init__(self, compress=False): """Initialize TrackBack metric Parameters ---------- compress : bool if True then prefix common with previous invocation gets replaced with ... """ self.__prev = "" self.__compress = compress def __call__(self): ftb = traceback.extract_stack(limit=100)[:-2] entries = [ [mbasename(x[0]), os.path.dirname(x[0]), str(x[1])] for x in ftb] entries = [ [e[0], e[2]] for e in entries if not (e[0] in ['unittest', 'logging.__init__'] or e[1].endswith('/unittest'))] # lets make it more concise entries_out = [entries[0]] for entry in entries[1:]: if entry[0] == entries_out[-1][0]: entries_out[-1][1] += ',%s' % entry[1] else: entries_out.append(entry) sftb = '>'.join(['%s:%s' % (mbasename(x[0]), x[1]) for x in entries_out]) if self.__compress: # lets remove part which is common with previous invocation prev_next = sftb common_prefix = os.path.commonprefix((self.__prev, sftb)) common_prefix2 = re.sub('>[^>]*$', '', common_prefix) if common_prefix2 != "": sftb = '...' + sftb[len(common_prefix2):] self.__prev = prev_next return sftb class FormatterWithTraceBack(logging.Formatter): """Custom formatter which expands %(tb) and %(tbc) with tracebacks TODO: might need locking in case of compressed tracebacks """ def __init__(self, fmt, *args, **kwargs): logging.Formatter.__init__(self, fmt=fmt, *args, **kwargs) compress = '%(tbc)s' in fmt self._tb = TraceBack(compress=compress) def format(self, record): record.tbc = record.tb = self._tb() return logging.Formatter.format(self, record) logging.exitOnIOError = False def __stopOnIOError(logSys=None, logHndlr=None): # pragma: no cover if logSys and len(logSys.handlers): logSys.removeHandler(logSys.handlers[0]) if logHndlr: logHndlr.close = lambda: None logging.StreamHandler.flush = lambda self: None #sys.excepthook = lambda *args: None if logging.exitOnIOError: try: sys.stderr.close() except: pass sys.exit(0) try: BrokenPipeError = BrokenPipeError except NameError: # pragma: 3.x no cover BrokenPipeError = IOError __origLog = logging.Logger._log def __safeLog(self, level, msg, args, **kwargs): """Safe log inject to avoid possible errors by unsafe log-handlers, concat, str. conversion, representation fails, etc. Used to intrude exception-safe _log-method instead of _log-method of Logger class to be always safe by logging and to get more-info about. See testSafeLogging test-case for more information. At least the errors covered in phase 3 seems to affected in all known pypy/python versions until now. """ try: # if isEnabledFor(level) already called... __origLog(self, level, msg, args, **kwargs) except (BrokenPipeError, IOError) as e: # pragma: no cover if e.errno == 32: # closed / broken pipe __stopOnIOError(self) raise except Exception as e: # pragma: no cover - unreachable if log-handler safe in this python-version try: for args in ( ("logging failed: %r on %s", (e, uni_string(msg))), (" args: %r", ([uni_string(a) for a in args],)) ): try: __origLog(self, level, *args) except: # pragma: no cover pass except: # pragma: no cover pass logging.Logger._log = __safeLog __origLogFlush = logging.StreamHandler.flush def __safeLogFlush(self): """Safe flush inject stopping endless logging on closed streams (redirected pipe). """ try: __origLogFlush(self) except (BrokenPipeError, IOError) as e: # pragma: no cover if e.errno == 32: # closed / broken pipe __stopOnIOError(None, self) raise logging.StreamHandler.flush = __safeLogFlush def getLogger(name): """Get logging.Logger instance with Fail2Ban logger name convention """ if "." in name: name = "fail2ban.%s" % name.rpartition(".")[-1] return logging.getLogger(name) def str2LogLevel(value): try: if isinstance(value, int) or value.isdigit(): ll = int(value) else: ll = getattr(logging, value.upper()) except AttributeError: raise ValueError("Invalid log level %r" % value) return ll def getVerbosityFormat(verbosity, fmt=' %(message)s', addtime=True, padding=True): """Custom log format for the verbose runs """ if verbosity > 1: # pragma: no cover if verbosity > 3: fmt = ' | %(module)15.15s-%(levelno)-2d: %(funcName)-20.20s |' + fmt if verbosity > 2: fmt = ' +%(relativeCreated)5d %(thread)X %(name)-25.25s %(levelname)-5.5s' + fmt else: fmt = ' %(thread)X %(levelname)-5.5s' + fmt if addtime: fmt = ' %(asctime)-15s' + fmt else: # default (not verbose): fmt = "%(name)-24s[%(process)d]: %(levelname)-7s" + fmt if addtime: fmt = "%(asctime)s " + fmt # remove padding if not needed: if not padding: fmt = re.sub(r'(?<=\))-?\d+(?:\.\d+)?s', lambda m: 's', fmt) return fmt def excepthook(exctype, value, traceback): """Except hook used to log unhandled exceptions to Fail2Ban log """ getLogger("fail2ban").critical( "Unhandled exception in Fail2Ban:", exc_info=True) return sys.__excepthook__(exctype, value, traceback) def splitwords(s): """Helper to split words on any comma, space, or a new line Returns empty list if input is empty (or None) and filters out empty entries """ if not s: return [] return list(filter(bool, [v.strip() for v in re.split('[ ,\n]+', s)])) if sys.version_info >= (3,5): eval(compile(r'''if 1: def _merge_dicts(x, y): """Helper to merge dicts. """ if y: return {**x, **y} return x def _merge_copy_dicts(x, y): """Helper to merge dicts to guarantee a copy result (r is never x). """ return {**x, **y} ''', __file__, 'exec')) else: def _merge_dicts(x, y): """Helper to merge dicts. """ r = x if y: r = x.copy() r.update(y) return r def _merge_copy_dicts(x, y): """Helper to merge dicts to guarantee a copy result (r is never x). """ r = x.copy() if y: r.update(y) return r # # Following function used for parse options from parameter (e.g. `name[p1=0, p2="..."][p3='...']`). # # regex, to extract list of options: OPTION_CRE = re.compile(r"^([^\[]+)(?:\[(.*)\])?\s*$", re.DOTALL) # regex, to iterate over single option in option list, syntax: # `action = act[p1="...", p2='...', p3=...]`, where the p3=... not contains `,` or ']' # since v0.10 separator extended with `]\s*[` for support of multiple option groups, syntax # `action = act[p1=...][p2=...]` OPTION_EXTRACT_CRE = re.compile( r'\s*([\w\-_\.]+)=(?:"([^"]*)"|\'([^\']*)\'|([^,\]]*))(?:,|\]\s*\[|$|(?P.+))|,?\s*$|(?P.+)', re.DOTALL) # split by new-line considering possible new-lines within options [...]: OPTION_SPLIT_CRE = re.compile( r'(?:[^\[\s]+(?:\s*\[\s*(?:[\w\-_\.]+=(?:"[^"]*"|\'[^\']*\'|[^,\]]*)\s*(?:,|\]\s*\[)?\s*)*\])?\s*|\S+)(?=\n\s*|\s+|$)', re.DOTALL) def extractOptions(option): match = OPTION_CRE.match(option) if not match: raise ValueError("unexpected option syntax") option_name, optstr = match.groups() option_opts = dict() if optstr: for optmatch in OPTION_EXTRACT_CRE.finditer(optstr): if optmatch.group("wrngA"): raise ValueError("unexpected syntax at %d after option %r: %s" % ( optmatch.start("wrngA"), optmatch.group(1), optmatch.group("wrngA")[0:25])) if optmatch.group("wrngB"): raise ValueError("expected option, wrong syntax at %d: %s" % ( optmatch.start("wrngB"), optmatch.group("wrngB")[0:25])) opt = optmatch.group(1) if not opt: continue value = [ val for val in optmatch.group(2,3,4) if val is not None][0] option_opts[opt.strip()] = value.strip() return option_name, option_opts def splitWithOptions(option): return OPTION_SPLIT_CRE.findall(option) # # Following facilities used for safe recursive interpolation of # tags () in tagged options. # # max tag replacement count (considering tag X in tag Y repeat): MAX_TAG_REPLACE_COUNT = 25 # compiled RE for tag name (replacement name) TAG_CRE = re.compile(r'<([^ <>]+)>') def substituteRecursiveTags(inptags, conditional='', ignore=(), addrepl=None ): """Sort out tag definitions within other tags. Since v.0.9.2 supports embedded interpolation (see test cases for examples). so: becomes: a = 3 a = 3 b = _3 b = 3_3 Parameters ---------- inptags : dict Dictionary of tags(keys) and their values. Returns ------- dict Dictionary of tags(keys) and their values, with tags within the values recursively replaced. """ #logSys = getLogger("fail2ban") tre_search = TAG_CRE.search tags = inptags # init: ignore = set(ignore) done = set() noRecRepl = hasattr(tags, "getRawItem") # repeat substitution while embedded-recursive (repFlag is True) repCounts = {} while True: repFlag = False # substitute each value: for tag in tags.keys(): # ignore escaped or already done (or in ignore list): if tag in ignore or tag in done: continue # ignore replacing callable items from calling map - should be converted on demand only (by get): if noRecRepl and callable(tags.getRawItem(tag)): continue value = orgval = uni_string(tags[tag]) # search and replace all tags within value, that can be interpolated using other tags: m = tre_search(value) rplc = repCounts.get(tag, {}) #logSys.log(5, 'TAG: %s, value: %s' % (tag, value)) while m: # found replacement tag: rtag = m.group(1) # don't replace tags that should be currently ignored (pre-replacement): if rtag in ignore: m = tre_search(value, m.end()) continue #logSys.log(5, 'found: %s' % rtag) if rtag == tag or rplc.get(rtag, 1) > MAX_TAG_REPLACE_COUNT: # recursive definitions are bad #logSys.log(5, 'recursion fail tag: %s value: %s' % (tag, value) ) raise ValueError( "properties contain self referencing definitions " "and cannot be resolved, fail tag: %s, found: %s in %s, value: %s" % (tag, rtag, rplc, value)) repl = None if conditional: repl = tags.get(rtag + '?' + conditional) if repl is None: repl = tags.get(rtag) # try to find tag using additional replacement (callable): if repl is None and addrepl is not None: repl = addrepl(rtag) if repl is None: # Missing tags - just continue on searching after end of match # Missing tags are ok - cInfo can contain aInfo elements like and valid shell # constructs like . m = tre_search(value, m.end()) continue # if calling map - be sure we've string: if not isinstance(repl, str): repl = uni_string(repl) value = value.replace('<%s>' % rtag, repl) #logSys.log(5, 'value now: %s' % value) # increment reference count: rplc[rtag] = rplc.get(rtag, 0) + 1 # the next match for replace: m = tre_search(value, m.start()) #logSys.log(5, 'TAG: %s, newvalue: %s' % (tag, value)) # was substituted? if orgval != value: # check still contains any tag - should be repeated (possible embedded-recursive substitution): if tre_search(value): repCounts[tag] = rplc repFlag = True # copy return tags dict to prevent modifying of inptags: if id(tags) == id(inptags): tags = inptags.copy() tags[tag] = value # no more sub tags (and no possible composite), add this tag to done set (just to be faster): if '<' not in value: done.add(tag) # stop interpolation, if no replacements anymore: if not repFlag: break return tags if _libcap: def prctl_set_th_name(name): """Helper to set real thread name (used for identification and diagnostic purposes). Side effect: name can be silently truncated to 15 bytes (16 bytes with NTS zero) """ try: if sys.version_info >= (3,): # pragma: 2.x no cover name = name.encode() else: # pragma: 3.x no cover name = bytes(name) _libcap.prctl(15, name) # PR_SET_NAME = 15 except: # pragma: no cover pass else: # pragma: no cover def prctl_set_th_name(name): pass class BgService(object): """Background servicing Prevents memory leak on some platforms/python versions, using forced GC in periodical intervals. """ _mutex = Lock() _instance = None def __new__(cls): if not cls._instance: cls._instance = \ super(BgService, cls).__new__(cls) return cls._instance def __init__(self): self.__serviceTime = -0x7fffffff self.__periodTime = 30 self.__threshold = 100; self.__count = self.__threshold; if hasattr(gc, 'set_threshold'): gc.set_threshold(0) # don't disable auto garbage, because of non-reference-counting python's (like pypy), # otherwise it may leak there on objects like unix-socket, etc. #gc.disable() def service(self, force=False, wait=False): self.__count -= 1 # avoid locking if next service time don't reached if not force and (self.__count > 0 or MyTime.time() < self.__serviceTime): return False # return immediately if mutex already locked (other thread in servicing): if not BgService._mutex.acquire(wait): return False try: # check again in lock: if MyTime.time() < self.__serviceTime: return False gc.collect() self.__serviceTime = MyTime.time() + self.__periodTime self.__count = self.__threshold return True finally: BgService._mutex.release() return False protocol.py000064400000026536152231502160006770 0ustar00# 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. # Author: Cyril Jaquier # __author__ = "Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" import textwrap def output(s): """Default output handler for printing protocol. Used to ease mocking in the test cases. """ print(s) ## # Describes the protocol used to communicate with the server. class dotdict(dict): def __getattr__(self, name): return self[name] CSPROTO = dotdict({ "EMPTY": b"", "END": b"", "CLOSE": b"" }) protocol = [ ['', "BASIC", ""], ["start", "starts the server and the jails"], ["restart", "restarts the server"], ["restart [--unban] [--if-exists] ", "restarts the jail (alias for 'reload --restart ... ')"], ["reload [--restart] [--unban] [--all]", "reloads the configuration without restarting of the server, the option '--restart' activates completely restarting of affected jails, thereby can unban IP addresses (if option '--unban' specified)"], ["reload [--restart] [--unban] [--if-exists] ", "reloads the jail , or restarts it (if option '--restart' specified)"], ["stop", "stops all jails and terminate the server"], ["unban --all", "unbans all IP addresses (in all jails and database)"], ["unban ... ", "unbans (in all jails and database)"], ["banned", "return jails with banned IPs as dictionary"], ["banned ... ]", "return list(s) of jails where given IP(s) are banned"], ["status", "gets the current status of the server"], ["ping", "tests if the server is alive"], ["echo", "for internal usage, returns back and outputs a given string"], ["help", "return this output"], ["version", "return the server version"], ['', "LOGGING", ""], ["set loglevel ", "sets logging level to . Levels: CRITICAL, ERROR, WARNING, NOTICE, INFO, " "DEBUG, TRACEDEBUG, HEAVYDEBUG or corresponding numeric value (50-5)"], ["get loglevel", "gets the logging level"], ["set logtarget ", "sets logging target to . Can be STDOUT, STDERR, SYSLOG, SYSTEMD-JOURNAL or a file"], ["get logtarget", "gets logging target"], ["set syslogsocket auto|", "sets the syslog socket path to auto or . Only used if logtarget is SYSLOG"], ["get syslogsocket", "gets syslog socket path"], ["flushlogs", "flushes the logtarget if a file and reopens it. For log rotation."], ['', "DATABASE", ""], ["set dbfile ", "set the location of fail2ban persistent datastore. Set to \"None\" to disable"], ["get dbfile", "get the location of fail2ban persistent datastore"], ["set dbmaxmatches ", "sets the max number of matches stored in database per ticket"], ["get dbmaxmatches", "gets the max number of matches stored in database per ticket"], ["set dbpurgeage ", "sets the max age in that history of bans will be kept"], ["get dbpurgeage", "gets the max age in seconds that history of bans will be kept"], ['', "JAIL CONTROL", ""], ["add ", "creates using "], ["start ", "starts the jail "], ["stop ", "stops the jail . The jail is removed"], ["status [FLAVOR]", "gets the current status of , with optional flavor or extended info"], ['', "JAIL CONFIGURATION", ""], ["set idle on|off", "sets the idle state of "], ["set ignoreself true|false", "allows the ignoring of own IP addresses"], ["set addignoreip ", "adds to the ignore list of "], ["set delignoreip ", "removes from the ignore list of "], ["set ignorecommand ", "sets ignorecommand of "], ["set ignorecache ", "sets ignorecache of "], ["set addlogpath ['tail']", "adds to the monitoring list of , optionally starting at the 'tail' of the file (default 'head')."], ["set dellogpath ", "removes from the monitoring list of "], ["set logencoding ", "sets the of the log files for "], ["set addjournalmatch ", "adds to the journal filter of "], ["set deljournalmatch ", "removes from the journal filter of "], ["set addfailregex ", "adds the regular expression which must match failures for "], ["set delfailregex ", "removes the regular expression at for failregex"], ["set addignoreregex ", "adds the regular expression which should match pattern to exclude for "], ["set delignoreregex ", "removes the regular expression at for ignoreregex"], ["set findtime