diff options
Diffstat (limited to 'straper/db/public/fail2ban/etc-fail2ban/action.d/smtp.py')
| -rw-r--r-- | straper/db/public/fail2ban/etc-fail2ban/action.d/smtp.py | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/straper/db/public/fail2ban/etc-fail2ban/action.d/smtp.py b/straper/db/public/fail2ban/etc-fail2ban/action.d/smtp.py new file mode 100644 index 0000000..40eda1b --- /dev/null +++ b/straper/db/public/fail2ban/etc-fail2ban/action.d/smtp.py @@ -0,0 +1,240 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : + +# This file is part of Fail2Ban. +# +# Fail2Ban is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# Fail2Ban is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Fail2Ban; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +import socket +import smtplib +from email.mime.text import MIMEText +from email.utils import formatdate, formataddr + +from fail2ban.server.actions import ActionBase, CallingMap + +messages = {} +messages['start'] = \ +"""Hi, + +The jail %(jailname)s has been started successfully. + +Regards, +Fail2Ban""" + +messages['stop'] = \ +"""Hi, + +The jail %(jailname)s has been stopped. + +Regards, +Fail2Ban""" + +messages['ban'] = {} +messages['ban']['head'] = \ +"""Hi, + +The IP %(ip)s has just been banned for %(bantime)i seconds +by Fail2Ban after %(failures)i attempts against %(jailname)s. +""" +messages['ban']['tail'] = \ +""" +Regards, +Fail2Ban""" +messages['ban']['matches'] = \ +""" +Matches for this ban: +%(matches)s +""" +messages['ban']['ipmatches'] = \ +""" +Matches for %(ip)s: +%(ipmatches)s +""" +messages['ban']['ipjailmatches'] = \ +""" +Matches for %(ip)s for jail %(jailname)s: +%(ipjailmatches)s +""" + + +class SMTPAction(ActionBase): + """Fail2Ban action which sends emails to inform on jail starting, + stopping and bans. + """ + + def __init__( + self, jail, name, host="localhost", ssl=False, user=None, password=None, + sendername="Fail2Ban", sender="fail2ban", dest="root", matches=None): + """Initialise action. + + Parameters + ---------- + jail : Jail + The jail which the action belongs to. + name : str + Named assigned to the action. + host : str, optional + SMTP host, of host:port format. Default host "localhost" and + port "25" + ssl : bool, optional + Whether to use TLS for the SMTP connection or not. Default False. + user : str, optional + Username used for authentication with SMTP server. + password : str, optional + Password used for authentication with SMTP server. + sendername : str, optional + Name to use for from address in email. Default "Fail2Ban". + sender : str, optional + Email address to use for from address in email. + Default "fail2ban". + dest : str, optional + Email addresses of intended recipient(s) in comma space ", " + delimited format. Default "root". + matches : str, optional + Type of matches to be included from ban in email. Can be one + of "matches", "ipmatches" or "ipjailmatches". Default None + (see man jail.conf.5). + """ + + super(SMTPAction, self).__init__(jail, name) + + self.host = host + self.ssl = ssl + + self.user = user + self.password =password + + self.fromname = sendername + self.fromaddr = sender + self.toaddr = dest + + self.matches = matches + + self.message_values = CallingMap( + jailname = self._jail.name, + hostname = socket.gethostname, + bantime = lambda: self._jail.actions.getBanTime(), + ) + + # bypass ban/unban for restored tickets + self.norestored = 1 + + def _sendMessage(self, subject, text): + """Sends message based on arguments and instance's properties. + + Parameters + ---------- + subject : str + Subject of the email. + text : str + Body of the email. + + Raises + ------ + SMTPConnectionError + Error on connecting to host. + SMTPAuthenticationError + Error authenticating with SMTP server. + SMTPException + See Python `smtplib` for full list of other possible + exceptions. + """ + msg = MIMEText(text) + msg['Subject'] = subject + msg['From'] = formataddr((self.fromname, self.fromaddr)) + msg['To'] = self.toaddr + msg['Date'] = formatdate() + + smtp_host, smtp_port = self.host.split(':') + smtp = smtplib.SMTP(host=smtp_host, port=smtp_port) + try: + r = smtp.connect(host=smtp_host, port=smtp_port) + self._logSys.debug("Connected to SMTP '%s', response: %i: %s", + self.host, *r) + + if self.ssl: # pragma: no cover + r = smtp.starttls()[0]; + if r != 220: # pragma: no cover + raise Exception("Failed to starttls() on '%s': %s" % (self.host, r)) + + if self.user and self.password: # pragma: no cover (ATM no tests covering that) + smtp.login(self.user, self.password) + failed_recipients = smtp.sendmail( + self.fromaddr, self.toaddr.split(", "), msg.as_string()) + except smtplib.SMTPConnectError: # pragma: no cover + self._logSys.error("Error connecting to host '%s'", self.host) + raise + except smtplib.SMTPAuthenticationError: # pragma: no cover + self._logSys.error( + "Failed to authenticate with host '%s' user '%s'", + self.host, self.user) + raise + except smtplib.SMTPException: # pragma: no cover + self._logSys.error( + "Error sending mail to host '%s' from '%s' to '%s'", + self.host, self.fromaddr, self.toaddr) + raise + else: + if failed_recipients: # pragma: no cover + self._logSys.warning( + "Email to '%s' failed to following recipients: %r", + self.toaddr, failed_recipients) + self._logSys.debug("Email '%s' successfully sent", subject) + finally: + try: + self._logSys.debug("Disconnected from '%s', response %i: %s", + self.host, *smtp.quit()) + except smtplib.SMTPServerDisconnected: # pragma: no cover + pass # Not connected + + def start(self): + """Sends email to recipients informing that the jail has started. + """ + self._sendMessage( + "[Fail2Ban] %(jailname)s: started on %(hostname)s" % + self.message_values, + messages['start'] % self.message_values) + + def stop(self): + """Sends email to recipients informing that the jail has stopped. + """ + self._sendMessage( + "[Fail2Ban] %(jailname)s: stopped on %(hostname)s" % + self.message_values, + messages['stop'] % self.message_values) + + def ban(self, aInfo): + """Sends email to recipients informing that ban has occurred. + + Parameters + ---------- + aInfo : dict + Dictionary which includes information in relation to + the ban. + """ + if aInfo.get('restored'): + return + aInfo.update(self.message_values) + message = "".join([ + messages['ban']['head'], + messages['ban'].get(self.matches, ""), + messages['ban']['tail'] + ]) + self._sendMessage( + "[Fail2Ban] %(jailname)s: banned %(ip)s from %(hostname)s" % + aInfo, + message % aInfo) + +Action = SMTPAction |
