diff --git a/README.md b/README.md
index 2fb4002..1aed365 100644
--- a/README.md
+++ b/README.md
@@ -47,12 +47,12 @@ Manual Installation:
License:
--------
-fail2ban-digest is free software; you can redistribute it and/or modify it under the
+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-digest is distributed in the hope that it will be useful, but WITHOUT ANY
+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.
diff --git a/bin/fail2ban_digest b/bin/fail2ban_digest
index 2f7af0e..588077e 100755
--- a/bin/fail2ban_digest
+++ b/bin/fail2ban_digest
@@ -17,7 +17,7 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-from datetime import datetime, timedelta, timezone
+from datetime import datetime, timedelta
from dbm import gnu as dbm
from email.message import EmailMessage
from smtplib import SMTP
@@ -34,83 +34,17 @@ import re
import sys
db_location = '/var/lib/fail2ban/digest'
-db_creation_date_key = u'db_creation_date'
+db_creation_date_key = 'db_creation_date'
db_date_format = '%Y-%m-%d %H:%M:%S'
default_mail_template = Template('''Hi,\n
-This is a digest email of the ${count} banned IPs between ${creation_date} and ${date_now}:
+ this is a digest email of banned IPs since ${creation_date} UTC and ${date_now}
${digest}
Regards,
-Fail2ban Digest
+Fail2ban digest
''')
-default_html_template = Template('''
-
-
-
-
-
- Hi,
- This is a digest email of the ${count} banned IPs between ${creation_date} and ${date_now}:
-
-
- # |
- IPs |
- When |
-
-${digest}
-
- Regards,
- Fail2Ban Digest
-
-
-''')
-html_tr_template = Template('''
- ${count} |
- ${ip} |
- ${events} |
-
-''')
-html_error_template = Template('''
- ${error_msg} |
-
-''')
-
-class Ban:
- def __init__(self, ip, events):
- self.ip = ip
- self.events = []
- for event in events:
- self.events.append(utc_to_local(event))
- self.events.sort
class store_yesno(argparse.Action):
def __init__(self, option_strings, dest, nargs = None, **kwargs):
@@ -146,12 +80,6 @@ def ip_address(string):
else:
raise argparse.ArgumentTypeError('%s is not a valid IPv4 or IPv6 address' % string)
-def utc_to_local(date_string):
- try:
- return datetime.strptime(date_string, db_date_format).replace(tzinfo=timezone.utc).astimezone().strftime(db_date_format)
- except ValueError:
- return date_string
-
def close_db(db_fd):
db_fd.close()
atexit.unregister(close_db)
@@ -178,7 +106,7 @@ def add(db, ip):
db = db_busy_open(db_location + '/' + db + '.dbm', 'c', 30)
if db_creation_date_key not in db.keys():
db[db_creation_date_key] = datetime.utcnow().strftime(db_date_format).encode('UTF-8')
- event_date = ('%s, ' % datetime.utcnow().strftime(db_date_format)).encode('UTF-8')
+ event_date = ('%s, ' % datetime.now().strftime(db_date_format)).encode('UTF-8')
try:
db[ip] += event_date
except KeyError:
@@ -186,7 +114,7 @@ def add(db, ip):
close_db(db)
return
-def digest(db, delete, sort):
+def digest(db, delete):
db_file = db_location + '/' + db + '.dbm'
new_db_file = db_location + '/.' + db + '.dbm'
try:
@@ -202,48 +130,32 @@ def digest(db, delete, sort):
os.rename(new_db_file, db_file)
try:
- db_creation_date = utc_to_local(db[db_creation_date_key].decode('UTF-8'))
+ db_creation_date = db[db_creation_date_key].decode('UTF-8')
except KeyError as e:
db_creation_date = 'not found'
- events_list = []
+ event_list = []
for ip in db.keys():
if ip.decode('UTF-8') == db_creation_date_key:
continue
- events_list.append(Ban(ip.decode('UTF-8'), db[ip].decode('UTF-8').split(', ')[:-1]))
+ event_list.append((ip.decode('UTF-8'), db[ip].decode('UTF-8').split(', ')[:-1]))
close_db(db)
- events_list.sort(key=lambda x: x.events[0]) # sort by date
- if sort:
- events_list.sort(key=lambda x: len(x.events), reverse=True)
+ event_list.sort(key = lambda x: len(x[1]), reverse = True)
msg = ''
- msg_html = ''
- for ban in events_list:
- msg_html += html_tr_template.substitute(count = len(ban.events), ip = ban.ip, events = '
'.join(ban.events))
- msg += '%3d event(s) for IP %-42s: %s\n' %(len(ban.events), ban.ip, ', '.join(ban.events))
- return (len(events_list), db_creation_date, msg, msg_html)
+ for ip, events in event_list:
+ msg += '%3d event(s) for IP %-42s: %s\n' %(len(events), ip, ', '.join(events))
+ return (db_creation_date, msg)
-def mail_digest(db, mail_to, mail_from, delete, html, quiet, sort):
+def mail_digest(db, mail_to, mail_from, delete):
msg = EmailMessage()
- date_now = datetime.now().strftime(db_date_format)
- count, creation_date, dgst, dgst_html = digest(db, delete, sort)
+ date_now = datetime.utcnow().strftime(db_date_format)
+ creation_date, dgst = digest(db, delete)
if dgst == '':
- if quiet:
- return
- else:
- dgst = ' No ban event recorded for the named time frame.'
- dgst_html = html_error_template.substitute(error_msg = dgst)
+ dgst = 'no ban event recorded for the named time frame'
msg.set_content(default_mail_template.substitute(
- count = count,
- creation_date = creation_date,
- date_now = date_now,
- digest = dgst
- ))
- if html:
- msg.add_alternative(default_html_template.substitute(
- count = count,
creation_date = creation_date,
date_now = date_now,
- digest = dgst_html
- ), subtype = 'html')
+ digest = dgst,
+ ))
msg['To'] = mail_to
msg['From'] = mail_from
msg['Subject'] = '[Fail2Ban] %s: digest for %s %s' % (db, socket.gethostname(), date_now)
@@ -256,9 +168,9 @@ def main(args):
if args.cmd == 'add':
add(args.database, args.ip)
elif args.cmd == 'digest':
- print(digest(args.database, args.delete, args.sort)[2])
+ print(digest(args.database, args.delete)[1])
elif args.cmd == 'maildigest':
- mail_digest(args.database, args.to, args.mail_from, args.delete, args.html, args.quiet, args.sort)
+ mail_digest(args.database, args.to, args.mail_from, args.delete)
elif args.cmd is None:
print('No action specified')
return
@@ -307,12 +219,6 @@ if __name__ == '__main__':
default = False,
help = 'do / don\'t delete current database, next call to add will create a new empty one'
)
- subcommands[sc].add_argument(
- '--sort', '--no-sort',
- action = store_yesno,
- default = True,
- help = 'do / don\'t sort the digest by repeat event occurrences.'
- )
sc = 'maildigest'
subcommands[sc] = subparsers.add_parser(
@@ -329,30 +235,12 @@ if __name__ == '__main__':
default = True,
help = 'do / don\'t delete current database, next call to add will create a new empty one'
)
- subcommands[sc].add_argument(
- '--html', '--no-html',
- action = store_yesno,
- default = False,
- help = 'do / don\'t send the digest in HTML format.'
- )
subcommands[sc].add_argument(
'--mail-from',
action = 'store',
default = 'Fail2ban at {0} '.format(socket.gethostname()),
help = 'Use FROM address for the email From header. Default: Fail2ban at {0} (automatically detected system hostname)'.format(socket.gethostname())
)
- subcommands[sc].add_argument(
- '--quiet', '--no-quiet',
- action = store_yesno,
- default = False,
- help = 'do / don\'t send digest if there are no ban events recorded for the named time frame'
- )
- subcommands[sc].add_argument(
- '--sort', '--no-sort',
- action = store_yesno,
- default = True,
- help = 'do / don\'t sort the digest by repeat event occurrences.'
- )
subcommands[sc].add_argument(
'--to',
action = 'store',