a =Æ*f¾oã@sàdZdZdZddlZddlZddlZddlZddlZddlZddl m Z ddl m Z ddl mZdd lmZdd lmZd d lmZmZmZeeƒZd d„Zdd„Zdd„Ze ee¡e de¡dd„ZGdd„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úˆ||g|¢Ri|¤ŽW| ¡WdƒWdƒS| ¡0Wdƒn1sx0YWdƒn1s–0YdS©N)Ú_lockÚ_dbÚcursorÚclose)ÚselfÚargsÚkwargsÚcur©ÚfrrÚwrapperHs þz"commitandrollback..wrapperr)r0r1rr/rÚcommitandrollbackGsr2c@sœeZdZdZdZdZeeƒZdMdd„ZdNd d „Z d d „Z e d d„ƒZ dd„Z e dd„ƒZe dd„ƒZejdd„ƒZdOdd„ZedPdd„ƒZdd„Zedd„ƒZedd„ƒZed d!„ƒZed"d#„ƒZedQd%d&„ƒZed'd(„ƒZdRd*d+„ZedSd,d-„ƒZed.d/„ƒZd0d1„ZedTd2d3„ƒZed4d5„ƒZ ed6d7„ƒZ!ed8d9„ƒZ"edUd:d;„ƒZ#dd?„Z%edWd@dA„ƒZ&dXdBdC„Z'dYdEdF„Z(dGdH„Z)dIdJ„Z*edKdL„ƒZ+d$S)ZÚ 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 existing database fails to update to new schema. Attributes ---------- filename purgeage r))Z fail2banDbz7CREATE TABLE IF NOT EXISTS fail2banDb(version INTEGER);)Zjailsz•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);)ÚlogsaSCREATE 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);)Úbansa‚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);)ÚbipsaZCREATE 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);é€QécCs,d|_tƒ|_||_||_||_| ¡dS)Né )Ú maxMatchesrr'Ú _dbFilenameÚ _purgeAgeÚ_outDatedFactorÚ _connectDB)r+ÚfilenameZpurgeAgeZoutDatedFactorrrrÚ__init__¦s zFail2BanDb.__init__Fc CsÒ|j}z*tj|dtjd|_i|_t d|¡Wn<tjyl}z"t  d||j d¡‚WYd}~n d}~00zddl }d}Wnt y”d}Yn0|j  ¡}z¢z:| d¡| d¡|sÈ| d ¡| d ¡| d ¡Wn„tjyt d | ¡¡Yn¸tjyb}zDt  d ||j d¡tj |¡s:‚| ¡d}| ¡WYd}~nbd}~00| ¡d}|tjkr¼| |¡}|tjkr¢t d||¡nt  dtj||¡tdƒ‚W|r&t d¡|j|ddt d¡t d¡| d¡| ¡D]} t dd | ¡¡q|j  ¡|rÎ|r<| d ¡| ¡nˆ|r®t d¡|j|ddt d¡t d¡| d¡| ¡D]} t dd | ¡¡qŠ|j  ¡|rÌ|rÄ| d ¡| ¡0dS)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 = MEMORYú&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ú )!r;Úsqlite3ZconnectZPARSE_DECLTYPESr(Ú_bansMergedCacherÚinfoZOperationalErrorrr,Ú__pypy__Ú ImportErrorr)ÚexecuteZwarningÚcreateDbÚErrorÚosÚpathÚisfiler*ÚrepairDBÚfetchoner3Ú __version__ÚupdateDbÚ RuntimeErrorÚdebugÚ _createDbZfetchallÚjoinZcommit) r+ÚcheckIntegrityr?r!rGZpypyr.ÚversionZ newversionÚsrrrr>®s þÿ þ       ÿ þ    ÿþ         ó       zFail2BanDb._connectDBcCs"t d¡|j ¡t d¡dS)Nz Close connection to database ...zConnection to database closed.)rrTr(r*rF©r+rrrr*ÿs  zFail2BanDb.closec CsBz|jWSty<|jdt dt ¡¡|_|jYS0dS)NÚ.z %Y%m%d-%H%M%S)Z_Fail2BanDb__dbBackupFilenameÚAttributeErrorr;ÚtimeÚstrftimerÚgmtimerZrrrÚ_dbBackupFilenames  zFail2BanDb._dbBackupFilenamec CsTGdd„dtƒ}|j}d|_z(z®t d|j¡tj |j¡s^t   |j|j¡t d|j¡ntj |j¡rxt  |j¡t   d|j|jf¡t |j¡j}|r¸t d|¡|jddnt d |¡|d ƒ‚Wnnty<}zTtjd |j|jd t||ƒ o t ¡d kdt  |j¡|jddWYd}~n d}~00W||_n||_0dS)Nc@s eZdZdS)z,Fail2BanDb.repairDB..RepairExceptionN)Ú__name__Ú __module__Ú __qualname__rrrrÚRepairException srdzTrying to repair database %sú 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)rWz1 Repair seems to be failed, restored %d byte(s).z Recreate ...z/ Error repairing of fail2ban database '%s': %srr9r)rrOrrFr;rLrMrNr`ÚshutilÚmoveÚremoverZ executeCmdÚstatÚst_sizer>rr,r r)r+rdZ _repairDBZ dbFileSizer!rrrrO s6 ÿ    þ $zFail2BanDb.repairDBcCs|jS)z&File name of SQLite3 database file. )r;rZrrrr?/szFail2BanDb.filenamecCs|jS)zPurge age in seconds. )r<rZrrrÚpurgeage5szFail2BanDb.purgeagecCst |¡|_dSr&)rZ str2secondsr<)r+Úvaluerrrrk;scCs@tjD]\}}| |¡q| dtjf¡| d¡| ¡dS)z8Creates a new database, called during initialisation. z\INSERT INTO fail2banDb(version) SELECT ? WHERE NOT EXISTS (SELECT 1 FROM fail2banDb LIMIT 1)rAr)r3Ú_CREATE_SCRIPTSÚ executescriptrIrQrP)r+r.rBÚnrYrrrrU?s þ zFail2BanDb._createDbcCs | ||¡Sr&)rU)r+r.rBrrrrJLszFail2BanDb.createDbcCs&| d|f¡| ¡}|duo$|dS)NzQselect 1 where exists (select 1 from sqlite_master WHERE type='table' AND name=?)r)rIrP)r+r.ÚtableÚresrrrÚ _tableExistsPs ÿzFail2BanDb._tableExistsc CsJ|tjkrtdƒ‚zât d|j|¡tj |j¡sPt   |j |j¡t d|j¡|dkrx|  |d¡rx|  dtjd¡|dkr |  |d¡r |  d tjd¡|d krÞ|  |d ¡sÞ|  d tjd ¡|  |d¡rÞ| d ¡| d¡| ¡dWStyD}z6tjd|j|jdt ¡dkd| ¡WYd}~n d}~00dS)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'rer r4zÃ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;r8r5zØ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;rr6z?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 timeofbanrArz#Failed to upgrade database '%s': %sr9rN)r3rQÚNotImplementedErrorrrFr`rLrMrNrfÚcopyfiler?rrrnÚ _CREATE_TABSrIrPrrr;r,rrO)r+r.rXr!rrrrRVsB ÿù úý ÿ   þzFail2BanDb.updateDbcCs.| d|jf¡|jdkr*| d|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|dur| d¡n| dt|ƒf¡tdd„| ¡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©Ú.0ÚrowrrrÚ Àóz*Fail2BanDb.getJailNames..)rIÚintrÚ fetchmany)r+r.ZenabledrrrÚ getJailNames°s  ÿzFail2BanDb.getJailNamescCs| ||| ¡| ¡| ¡¡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©r+r.ryÚ containerrrrÚaddLogÂszFail2BanDb.addLogrcCs€d}| d|j|f¡z| ¡\}}Wnty<d}Yn0|durh|sR|durh| d|j|||f¡|dur|||kr|d}|S)NzBSELECT firstlinemd5, lastfilepos FROM logs WHERE jail=? AND path=?úUINSERT OR REPLACE INTO logs(jail, path, firstlinemd5, lastfilepos) VALUES(?, ?, ?, ?))rIrwrPÚ TypeError)r+r.ryrwÚposÚmd5Z lastLinePosZ firstLineMD5rrrrˆÕs"ý   þzFail2BanDb._addLogcCsFd}g}|dur$|d7}| |j¡| ||¡tdd„| ¡DƒƒS)zëGets all the log paths from the database. Currently only for testing purposes. Parameters ---------- jail : Jail If specified, will only return logs belonging to the jail. Returns ------- set Set of log paths. zSELECT path FROM logsNz WHERE jail=?css|]}|dVqdSrrr€rrrrƒþr„z)Fail2BanDb.getLogPaths..)ÚappendrwrIrr†)r+r.ryÚqueryÚ queryArgsrrrÚ getLogPathsès  zFail2BanDb.getLogPathscCs"| ||| ¡| ¡| ¡¡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‹rŒrrrÚ updateLogs zFail2BanDb.updateLogcCs6| d|||j|f¡|js2| d|j|||f¡dS)NzEUPDATE logs SET firstlinemd5=?, lastfilepos=? WHERE jail=? AND path=?rrv)r+r.ryrwr‘r’rrrr— s þ þzFail2BanDb._updateLogcCs| |||||¡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ˆ©r+r.ryrwr]ZisorrrÚ getJournalPosszFail2BanDb.getJournalPoscCs| |||||¡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—r™rrrÚ updateJournal)s zFail2BanDb.updateJournalc Cst| ¡ƒ}z|j||f=Wnty.Yn0z|j|df=WntyRYn0| ¡}| d¡}|jrœ|r®t|ƒ|jkr®| ¡}||j d…|d<n|r®| ¡}|d=|  d|j |t t |  ¡ƒƒ| |j ¡¡| ¡|f¡|  d||j t t |  ¡ƒƒ| |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(?, ?, ?, ?, ?, ?))ÚstrZgetIDrEÚKeyErrorZgetDataÚgetr:ÚlenÚcopyrIrwr…ÚroundZgetTimeÚ getBanTimeÚactionsZ getBanCount)r+r.ryÚticketÚipÚdatarœrrrÚaddBan6s<    (ÿþ(ÿþzFail2BanDb.addBancGs€d}d}|jg}t|ƒs4| ||¡| ||¡dS|d7}|d7}| d¡|D](}t|ƒ|d<| ||¡| ||¡qRdS)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)rwr rIr“r)r+r.ryr,Zquery1Zquery2r•r¦rrrÚdelBan]s      zFail2BanDb.delBancCs„d}g}|dur$|d7}| |j¡|durN|dkrN|d7}| t ¡|¡|durl|d7}| t|ƒ¡|d7}t| ||¡ƒS)Nz,SELECT ip, timeofban, data FROM bans WHERE 1ú AND jail=?rú AND timeofban > ?ú AND ip=?z ORDER BY ip, timeofban desc)r“rwrr]rrrI)r+r.ryÚbantimer¦r”r•rrrÚ_getBansws zFail2BanDb._getBanscKsBg}|jfi|¤ŽD](\}}}| t||ƒ¡|d |¡q|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“rZsetData)r+r-Úticketsr¦Ú timeofbanr§rrrÚgetBansŠs zFail2BanDb.getBanscCs²|j”d}|dus|dkrH||f}||jvrH|j|WdƒSg}d}t|j|||dƒ}|r^|dd}g} d} i} |D]¾\} } }| |krÈt||| ƒ}| | ¡| |¡| }g} d} i} | dg¡}|jt | ƒ}|dkrt |ƒ|kr|| } n|| d…| } | | dd¡7} | |d<| |d<|   |¡| }q†t| || d}| |¡|r||durr|n||j|<|durŠ|n|WdƒS1s¤0YdS)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¦ryr®rœÚfailuresr©r§) r'rErr¯rZ setAttemptr“rŸr:r Úupdate)r+r¦ryr®ZcacheKeyr±r¥ÚresultsZ prev_baniprœr´ZtickdataÚbanipr²r§Zprev_timeofbanÚmZmaxaddrrrÚ getBansMerged¤sN           zFail2BanDb.getBansMergedc Cs¤t|ƒ}|sd}nd}|d7}|g}|sD|durD|d7}| |j¡|durf|d7}| t ¡|¡|dur€|d7}| |¡|sŒ|dur”|d7}t| ||¡ƒS)Nz-SELECT bancount, timeofban, bantime FROM bipsz