34 |
- |
1 |
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*-
|
|
|
2 |
# vi: set ft=python sts=4 ts=4 sw=4 noet :
|
|
|
3 |
|
|
|
4 |
# This file is part of Fail2Ban.
|
|
|
5 |
#
|
|
|
6 |
# Fail2Ban is free software; you can redistribute it and/or modify
|
|
|
7 |
# it under the terms of the GNU General Public License as published by
|
|
|
8 |
# the Free Software Foundation; either version 2 of the License, or
|
|
|
9 |
# (at your option) any later version.
|
|
|
10 |
#
|
|
|
11 |
# Fail2Ban is distributed in the hope that it will be useful,
|
|
|
12 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
13 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
14 |
# GNU General Public License for more details.
|
|
|
15 |
#
|
|
|
16 |
# You should have received a copy of the GNU General Public License
|
|
|
17 |
# along with Fail2Ban; if not, write to the Free Software
|
|
|
18 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
19 |
|
|
|
20 |
import socket
|
|
|
21 |
import smtplib
|
|
|
22 |
from email.mime.text import MIMEText
|
|
|
23 |
from email.utils import formatdate, formataddr
|
|
|
24 |
|
|
|
25 |
from fail2ban.server.actions import ActionBase, CallingMap
|
|
|
26 |
|
|
|
27 |
messages = {}
|
|
|
28 |
messages['start'] = \
|
|
|
29 |
"""Hi,
|
|
|
30 |
|
|
|
31 |
The jail %(jailname)s has been started successfully.
|
|
|
32 |
|
|
|
33 |
Regards,
|
|
|
34 |
Fail2Ban"""
|
|
|
35 |
|
|
|
36 |
messages['stop'] = \
|
|
|
37 |
"""Hi,
|
|
|
38 |
|
|
|
39 |
The jail %(jailname)s has been stopped.
|
|
|
40 |
|
|
|
41 |
Regards,
|
|
|
42 |
Fail2Ban"""
|
|
|
43 |
|
|
|
44 |
messages['ban'] = {}
|
|
|
45 |
messages['ban']['head'] = \
|
|
|
46 |
"""Hi,
|
|
|
47 |
|
|
|
48 |
The IP %(ip)s has just been banned for %(bantime)i seconds
|
|
|
49 |
by Fail2Ban after %(failures)i attempts against %(jailname)s.
|
|
|
50 |
"""
|
|
|
51 |
messages['ban']['tail'] = \
|
|
|
52 |
"""
|
|
|
53 |
Regards,
|
|
|
54 |
Fail2Ban"""
|
|
|
55 |
messages['ban']['matches'] = \
|
|
|
56 |
"""
|
|
|
57 |
Matches for this ban:
|
|
|
58 |
%(matches)s
|
|
|
59 |
"""
|
|
|
60 |
messages['ban']['ipmatches'] = \
|
|
|
61 |
"""
|
|
|
62 |
Matches for %(ip)s:
|
|
|
63 |
%(ipmatches)s
|
|
|
64 |
"""
|
|
|
65 |
messages['ban']['ipjailmatches'] = \
|
|
|
66 |
"""
|
|
|
67 |
Matches for %(ip)s for jail %(jailname)s:
|
|
|
68 |
%(ipjailmatches)s
|
|
|
69 |
"""
|
|
|
70 |
|
39 |
- |
71 |
|
34 |
- |
72 |
class SMTPAction(ActionBase):
|
|
|
73 |
"""Fail2Ban action which sends emails to inform on jail starting,
|
|
|
74 |
stopping and bans.
|
|
|
75 |
"""
|
|
|
76 |
|
|
|
77 |
def __init__(
|
|
|
78 |
self, jail, name, host="localhost", user=None, password=None,
|
|
|
79 |
sendername="Fail2Ban", sender="fail2ban", dest="root", matches=None):
|
|
|
80 |
"""Initialise action.
|
|
|
81 |
|
|
|
82 |
Parameters
|
|
|
83 |
----------
|
|
|
84 |
jail : Jail
|
|
|
85 |
The jail which the action belongs to.
|
|
|
86 |
name : str
|
|
|
87 |
Named assigned to the action.
|
|
|
88 |
host : str, optional
|
|
|
89 |
SMTP host, of host:port format. Default host "localhost" and
|
|
|
90 |
port "25"
|
|
|
91 |
user : str, optional
|
|
|
92 |
Username used for authentication with SMTP server.
|
|
|
93 |
password : str, optional
|
|
|
94 |
Password used for authentication with SMTP server.
|
|
|
95 |
sendername : str, optional
|
|
|
96 |
Name to use for from address in email. Default "Fail2Ban".
|
|
|
97 |
sender : str, optional
|
|
|
98 |
Email address to use for from address in email.
|
|
|
99 |
Default "fail2ban".
|
|
|
100 |
dest : str, optional
|
|
|
101 |
Email addresses of intended recipient(s) in comma space ", "
|
|
|
102 |
delimited format. Default "root".
|
|
|
103 |
matches : str, optional
|
|
|
104 |
Type of matches to be included from ban in email. Can be one
|
|
|
105 |
of "matches", "ipmatches" or "ipjailmatches". Default None
|
|
|
106 |
(see man jail.conf.5).
|
|
|
107 |
"""
|
|
|
108 |
|
|
|
109 |
super(SMTPAction, self).__init__(jail, name)
|
|
|
110 |
|
|
|
111 |
self.host = host
|
|
|
112 |
#TODO: self.ssl = ssl
|
|
|
113 |
|
|
|
114 |
self.user = user
|
|
|
115 |
self.password =password
|
|
|
116 |
|
|
|
117 |
self.fromname = sendername
|
|
|
118 |
self.fromaddr = sender
|
|
|
119 |
self.toaddr = dest
|
|
|
120 |
|
|
|
121 |
self.matches = matches
|
|
|
122 |
|
|
|
123 |
self.message_values = CallingMap(
|
|
|
124 |
jailname = self._jail.name,
|
|
|
125 |
hostname = socket.gethostname,
|
|
|
126 |
bantime = self._jail.actions.getBanTime,
|
|
|
127 |
)
|
|
|
128 |
|
|
|
129 |
def _sendMessage(self, subject, text):
|
|
|
130 |
"""Sends message based on arguments and instance's properties.
|
|
|
131 |
|
|
|
132 |
Parameters
|
|
|
133 |
----------
|
|
|
134 |
subject : str
|
|
|
135 |
Subject of the email.
|
|
|
136 |
text : str
|
|
|
137 |
Body of the email.
|
|
|
138 |
|
|
|
139 |
Raises
|
|
|
140 |
------
|
|
|
141 |
SMTPConnectionError
|
|
|
142 |
Error on connecting to host.
|
|
|
143 |
SMTPAuthenticationError
|
|
|
144 |
Error authenticating with SMTP server.
|
|
|
145 |
SMTPException
|
|
|
146 |
See Python `smtplib` for full list of other possible
|
|
|
147 |
exceptions.
|
|
|
148 |
"""
|
|
|
149 |
msg = MIMEText(text)
|
|
|
150 |
msg['Subject'] = subject
|
|
|
151 |
msg['From'] = formataddr((self.fromname, self.fromaddr))
|
|
|
152 |
msg['To'] = self.toaddr
|
|
|
153 |
msg['Date'] = formatdate()
|
|
|
154 |
|
|
|
155 |
smtp = smtplib.SMTP()
|
|
|
156 |
try:
|
|
|
157 |
self._logSys.debug("Connected to SMTP '%s', response: %i: %s",
|
|
|
158 |
self.host, *smtp.connect(self.host))
|
|
|
159 |
if self.user and self.password:
|
|
|
160 |
smtp.login(self.user, self.password)
|
|
|
161 |
failed_recipients = smtp.sendmail(
|
|
|
162 |
self.fromaddr, self.toaddr.split(", "), msg.as_string())
|
|
|
163 |
except smtplib.SMTPConnectError:
|
|
|
164 |
self._logSys.error("Error connecting to host '%s'", self.host)
|
|
|
165 |
raise
|
|
|
166 |
except smtplib.SMTPAuthenticationError:
|
|
|
167 |
self._logSys.error(
|
|
|
168 |
"Failed to authenticate with host '%s' user '%s'",
|
|
|
169 |
self.host, self.user)
|
|
|
170 |
raise
|
|
|
171 |
except smtplib.SMTPException:
|
|
|
172 |
self._logSys.error(
|
|
|
173 |
"Error sending mail to host '%s' from '%s' to '%s'",
|
|
|
174 |
self.host, self.fromaddr, self.toaddr)
|
|
|
175 |
raise
|
|
|
176 |
else:
|
|
|
177 |
if failed_recipients:
|
|
|
178 |
self._logSys.warning(
|
|
|
179 |
"Email to '%s' failed to following recipients: %r",
|
|
|
180 |
self.toaddr, failed_recipients)
|
|
|
181 |
self._logSys.debug("Email '%s' successfully sent", subject)
|
|
|
182 |
finally:
|
|
|
183 |
try:
|
|
|
184 |
self._logSys.debug("Disconnected from '%s', response %i: %s",
|
|
|
185 |
self.host, *smtp.quit())
|
|
|
186 |
except smtplib.SMTPServerDisconnected:
|
|
|
187 |
pass # Not connected
|
|
|
188 |
|
|
|
189 |
def start(self):
|
|
|
190 |
"""Sends email to recipients informing that the jail has started.
|
|
|
191 |
"""
|
|
|
192 |
self._sendMessage(
|
|
|
193 |
"[Fail2Ban] %(jailname)s: started on %(hostname)s" %
|
|
|
194 |
self.message_values,
|
|
|
195 |
messages['start'] % self.message_values)
|
|
|
196 |
|
|
|
197 |
def stop(self):
|
|
|
198 |
"""Sends email to recipients informing that the jail has stopped.
|
|
|
199 |
"""
|
|
|
200 |
self._sendMessage(
|
|
|
201 |
"[Fail2Ban] %(jailname)s: stopped on %(hostname)s" %
|
|
|
202 |
self.message_values,
|
|
|
203 |
messages['stop'] % self.message_values)
|
|
|
204 |
|
|
|
205 |
def ban(self, aInfo):
|
|
|
206 |
"""Sends email to recipients informing that ban has occurred.
|
|
|
207 |
|
|
|
208 |
Parameters
|
|
|
209 |
----------
|
|
|
210 |
aInfo : dict
|
|
|
211 |
Dictionary which includes information in relation to
|
|
|
212 |
the ban.
|
|
|
213 |
"""
|
|
|
214 |
aInfo.update(self.message_values)
|
|
|
215 |
message = "".join([
|
|
|
216 |
messages['ban']['head'],
|
|
|
217 |
messages['ban'].get(self.matches, ""),
|
|
|
218 |
messages['ban']['tail']
|
|
|
219 |
])
|
|
|
220 |
self._sendMessage(
|
|
|
221 |
"[Fail2Ban] %(jailname)s: banned %(ip)s from %(hostname)s" %
|
|
|
222 |
aInfo,
|
|
|
223 |
message % aInfo)
|
|
|
224 |
|
|
|
225 |
Action = SMTPAction
|