Subversion Repositories configs

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
192 - 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
 
71
 
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 = lambda: self._jail.actions.getBanTime(),
127
			)
128
 
129
		# bypass ban/unban for restored tickets
130
		self.norestored = 1
131
 
132
	def _sendMessage(self, subject, text):
133
		"""Sends message based on arguments and instance's properties.
134
 
135
		Parameters
136
		----------
137
		subject : str
138
			Subject of the email.
139
		text : str
140
			Body of the email.
141
 
142
		Raises
143
		------
144
		SMTPConnectionError
145
			Error on connecting to host.
146
		SMTPAuthenticationError
147
			Error authenticating with SMTP server.
148
		SMTPException
149
			See Python `smtplib` for full list of other possible
150
			exceptions.
151
		"""
152
		msg = MIMEText(text)
153
		msg['Subject'] = subject
154
		msg['From'] = formataddr((self.fromname, self.fromaddr))
155
		msg['To'] = self.toaddr
156
		msg['Date'] = formatdate()
157
 
158
		smtp = smtplib.SMTP()
159
		try:
160
			self._logSys.debug("Connected to SMTP '%s', response: %i: %s",
161
				self.host, *smtp.connect(self.host))
162
			if self.user and self.password: # pragma: no cover (ATM no tests covering that)
163
				smtp.login(self.user, self.password)
164
			failed_recipients = smtp.sendmail(
165
				self.fromaddr, self.toaddr.split(", "), msg.as_string())
166
		except smtplib.SMTPConnectError: # pragma: no cover
167
			self._logSys.error("Error connecting to host '%s'", self.host)
168
			raise
169
		except smtplib.SMTPAuthenticationError: # pragma: no cover
170
			self._logSys.error(
171
				"Failed to authenticate with host '%s' user '%s'",
172
				self.host, self.user)
173
			raise
174
		except smtplib.SMTPException: # pragma: no cover
175
			self._logSys.error(
176
				"Error sending mail to host '%s' from '%s' to '%s'",
177
				self.host, self.fromaddr, self.toaddr)
178
			raise
179
		else:
180
			if failed_recipients: # pragma: no cover
181
				self._logSys.warning(
182
					"Email to '%s' failed to following recipients: %r",
183
					self.toaddr, failed_recipients)
184
			self._logSys.debug("Email '%s' successfully sent", subject)
185
		finally:
186
			try:
187
				self._logSys.debug("Disconnected from '%s', response %i: %s",
188
					self.host, *smtp.quit())
189
			except smtplib.SMTPServerDisconnected: # pragma: no cover
190
				pass # Not connected
191
 
192
	def start(self):
193
		"""Sends email to recipients informing that the jail has started.
194
		"""
195
		self._sendMessage(
196
			"[Fail2Ban] %(jailname)s: started on %(hostname)s" %
197
				self.message_values,
198
			messages['start'] % self.message_values)
199
 
200
	def stop(self):
201
		"""Sends email to recipients informing that the jail has stopped.
202
		"""
203
		self._sendMessage(
204
			"[Fail2Ban] %(jailname)s: stopped on %(hostname)s" %
205
				self.message_values,
206
			messages['stop'] % self.message_values)
207
 
208
	def ban(self, aInfo):
209
		"""Sends email to recipients informing that ban has occurred.
210
 
211
		Parameters
212
		----------
213
		aInfo : dict
214
			Dictionary which includes information in relation to
215
			the ban.
216
		"""
217
		if aInfo.get('restored'):
218
			return
219
		aInfo.update(self.message_values)
220
		message = "".join([
221
			messages['ban']['head'],
222
			messages['ban'].get(self.matches, ""),
223
			messages['ban']['tail']
224
			])
225
		self._sendMessage(
226
			"[Fail2Ban] %(jailname)s: banned %(ip)s from %(hostname)s" %
227
				aInfo,
228
			message % aInfo)
229
 
230
Action = SMTPAction