summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--ChangeLog9
-rw-r--r--MANIFEST1
-rw-r--r--THANKS1
-rw-r--r--config/action.d/firewallcmd-allports.conf52
-rw-r--r--config/action.d/firewallcmd-multiport.conf64
-rw-r--r--config/filter.d/postfix-sasl.conf2
-rw-r--r--config/filter.d/postfix.conf1
-rw-r--r--fail2ban/tests/files/logs/postfix3
-rw-r--r--fail2ban/tests/files/logs/postfix-sasl4
-rw-r--r--fail2ban/tests/files/testcase-usedns.log2
-rw-r--r--fail2ban/tests/filtertestcase.py14
-rwxr-xr-xfiles/debian-initd248
-rw-r--r--files/monit/fail2ban9
14 files changed, 401 insertions, 10 deletions
diff --git a/.gitignore b/.gitignore
index a8942050..780ecfb5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,4 @@ htmlcov
*.bak
__pycache__
.vagrant/
+.idea/
diff --git a/ChangeLog b/ChangeLog
index f644da1e..d0589248 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -24,6 +24,8 @@ ver. 0.9.2 (2014/xx/xx) - increment ban time
multiple expressions was not possible).
* filters.d/exim.conf - cover different settings of exim logs
details. Thanks bes.internal
+ * filter.d/postfix-sasl.conf - failregex is now case insensitive
+ * filters.d/postfix.conf - add 'Client host rejected error message' failregex
- New Features:
* increment ban time (+ observer) functionality introduced.
@@ -33,9 +35,14 @@ ver. 0.9.2 (2014/xx/xx) - increment ban time
possible to extend a stock filter or jail regexp in .local file
(opposite to simply set failregex/ignoreregex that overwrites it),
see gh-867.
-
+ - Monit config for fail2ban in /files/monit
+ - New actions:
+ - action.d/firewallcmd-multiport and action.d/firewallcmd-allports Thanks Donald Yandt
+
- Enhancements:
* Enable multiport for firewallcmd-new action. Closes gh-834
+ * files/debian-initd migrated from the debian branch and should be
+ suitable for manual installations now (thanks Juan Karlo de Guzman)
ver. 0.9.1 (2014/10/29) - better, faster, stronger
----------
diff --git a/MANIFEST b/MANIFEST
index eeb13eef..c6de80fd 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -328,6 +328,7 @@ man/fail2ban-server.h2m
man/fail2ban-regex.1
man/fail2ban-regex.h2m
man/generate-man
+files/debian-initd
files/gentoo-initd
files/gentoo-confd
files/redhat-initd
diff --git a/THANKS b/THANKS
index 33f7ea16..67f4f894 100644
--- a/THANKS
+++ b/THANKS
@@ -33,6 +33,7 @@ Daniel B.
Daniel Black
David Nutter
Derek Atkins
+Donald Yandt
Eric Gerbier
Enrico Labedzki
Eugene Hopkinson (SlowRiot)
diff --git a/config/action.d/firewallcmd-allports.conf b/config/action.d/firewallcmd-allports.conf
new file mode 100644
index 00000000..c0c378a4
--- /dev/null
+++ b/config/action.d/firewallcmd-allports.conf
@@ -0,0 +1,52 @@
+# Fail2Ban configuration file
+#
+# Author: Donald Yandt
+# Because of the --remove-rules in stop this action requires firewalld-0.3.8+
+
+
+[INCLUDES]
+
+before = iptables-blocktype.conf
+
+actionstart = firewall-cmd --direct --add-chain ipv4 filter f2b-<name>
+ firewall-cmd --direct --add-rule ipv4 filter f2b-<name> 1000 -j RETURN
+ firewall-cmd --direct --add-rule ipv4 filter <chain> 0 -j f2b-<name>
+
+actionstop = firewall-cmd --direct --remove-rule ipv4 filter <chain> 0 -j f2b-<name>
+ firewall-cmd --direct --remove-rules ipv4 filter f2b-<name>
+ firewall-cmd --direct --remove-chain ipv4 filter f2b-<name>
+
+
+# Note: uses regular expression whitespaces '\s' & end of line '$'
+# Example actioncheck: firewall-cmd --direct --get-chains ipv4 filter | grep -q '\sf2b-recidive$'
+
+actioncheck = firewall-cmd --direct --get-chains ipv4 filter | grep -q '\sf2b-<name>$'
+
+actionban = firewall-cmd --direct --add-rule ipv4 filter f2b-<name> 0 -s <ip> -j <blocktype>
+
+actionunban = firewall-cmd --direct --remove-rule ipv4 filter f2b-<name> 0 -s <ip> -j <blocktype>
+
+[Init]
+
+# Default name of the chain
+#
+name = default
+
+chain = INPUT_direct
+
+# DEV NOTES:
+#
+# Author: Donald Yandt
+# Uses "FirewallD" instead of the "iptables daemon".
+#
+#
+# Output:
+
+# actionstart:
+# $ firewall-cmd --direct --add-chain ipv4 filter f2b-recidive
+# success
+# $ firewall-cmd --direct --add-rule ipv4 filter f2b-recidive 1000 -j RETURN
+# success
+# $ sudo firewall-cmd --direct --add-rule ipv4 filter INPUT_direct 0 -j f2b-recidive
+# success
+
diff --git a/config/action.d/firewallcmd-multiport.conf b/config/action.d/firewallcmd-multiport.conf
new file mode 100644
index 00000000..da73f9f1
--- /dev/null
+++ b/config/action.d/firewallcmd-multiport.conf
@@ -0,0 +1,64 @@
+# Fail2Ban configuration file
+#
+# Author: Donald Yandt
+# Because of the --remove-rules in stop this action requires firewalld-0.3.8+
+
+[INCLUDES]
+
+before = iptables-blocktype.conf
+
+[Definition]
+
+actionstart = firewall-cmd --direct --add-chain ipv4 filter f2b-<name>
+ firewall-cmd --direct --add-rule ipv4 filter f2b-<name> 1000 -j RETURN
+ firewall-cmd --direct --add-rule ipv4 filter <chain> 0 -m state --state NEW -p <protocol> -m multiport --dports <port> -j f2b-<name>
+
+actionstop = firewall-cmd --direct --remove-rule ipv4 filter <chain> 0 -m state --state NEW -p <protocol> -m multiport --dports <port> -j f2b-<name>
+ firewall-cmd --direct --remove-rules ipv4 filter f2b-<name>
+ firewall-cmd --direct --remove-chain ipv4 filter f2b-<name>
+
+# Note: uses regular expression whitespaces '\s' & end of line '$'
+# Example actioncheck: firewall-cmd --direct --get-chains ipv4 filter | grep -q '\sf2b-apache-modsecurity$'
+
+actioncheck = firewall-cmd --direct --get-chains ipv4 filter | grep -q '\sf2b-<name>$'
+
+actionban = firewall-cmd --direct --add-rule ipv4 filter f2b-<name> 0 -s <ip> -j <blocktype>
+
+actionunban = firewall-cmd --direct --remove-rule ipv4 filter f2b-<name> 0 -s <ip> -j <blocktype>
+
+[Init]
+
+# Default name of the chain
+name = default
+
+chain = INPUT_direct
+
+# Could also use port numbers separated by a comma.
+port = 1:65535
+
+
+# Option: protocol
+# Values: [ tcp | udp | icmp | all ]
+
+protocol = tcp
+
+
+
+# DEV NOTES:
+#
+# Author: Donald Yandt
+# Uses "FirewallD" instead of the "iptables daemon".
+#
+#
+# Output:
+# actionstart:
+# $ firewall-cmd --direct --add-chain ipv4 filter f2b-apache-modsecurity
+# success
+# $ firewall-cmd --direct --add-rule ipv4 filter f2b-apache-modsecurity 1000 -j RETURN
+# success
+# $ sudo firewall-cmd --direct --add-rule ipv4 filter INPUT_direct 0 -m state --state NEW -p tcp -m multiport --dports 80,443 -j f2b-apache-modsecurity
+# success
+# actioncheck:
+# $ firewall-cmd --direct --get-chains ipv4 filter f2b-apache-modsecurity | grep -q '\sf2b-apache-modsecurity$'
+# f2b-apache-modsecurity
+
diff --git a/config/filter.d/postfix-sasl.conf b/config/filter.d/postfix-sasl.conf
index 35b064d3..e038b695 100644
--- a/config/filter.d/postfix-sasl.conf
+++ b/config/filter.d/postfix-sasl.conf
@@ -9,7 +9,7 @@ before = common.conf
_daemon = postfix/(submission/)?smtp(d|s)
-failregex = ^%(__prefix_line)swarning: [-._\w]+\[<HOST>\]: SASL (?:LOGIN|PLAIN|(?:CRAM|DIGEST)-MD5) authentication failed(: [ A-Za-z0-9+/]*={0,2})?\s*$
+failregex = ^%(__prefix_line)swarning: [-._\w]+\[<HOST>\]: SASL ((?i)LOGIN|PLAIN|(?:CRAM|DIGEST)-MD5) authentication failed(: [ A-Za-z0-9+/]*={0,2})?\s*$
ignoreregex =
diff --git a/config/filter.d/postfix.conf b/config/filter.d/postfix.conf
index a7a05e47..a994d772 100644
--- a/config/filter.d/postfix.conf
+++ b/config/filter.d/postfix.conf
@@ -13,6 +13,7 @@ before = common.conf
_daemon = postfix/(submission/)?smtp(d|s)
failregex = ^%(__prefix_line)sNOQUEUE: reject: RCPT from \S+\[<HOST>\]: 554 5\.7\.1 .*$
+ ^%(__prefix_line)sNOQUEUE: reject: RCPT from \S+\[<HOST>\]: 450 4\.7\.1 Client host rejected: cannot find your hostname, (\[\S*\]); from=<\S*> to=<\S+> proto=ESMTP helo=<\S*>$
^%(__prefix_line)sNOQUEUE: reject: RCPT from \S+\[<HOST>\]: 450 4\.7\.1 : Helo command rejected: Host not found; from=<> to=<> proto=ESMTP helo= *$
^%(__prefix_line)sNOQUEUE: reject: VRFY from \S+\[<HOST>\]: 550 5\.1\.1 .*$
^%(__prefix_line)simproper command pipelining after \S+ from [^[]*\[<HOST>\]:?$
diff --git a/fail2ban/tests/files/logs/postfix b/fail2ban/tests/files/logs/postfix
index ccf2f8bc..ee8720f8 100644
--- a/fail2ban/tests/files/logs/postfix
+++ b/fail2ban/tests/files/logs/postfix
@@ -20,3 +20,6 @@ Dec 25 02:35:54 platypus postfix/smtpd[9144]: improper command pipelining after
# failJSON: { "time": "2004-12-18T02:05:46", "match": true , "host": "216.245.198.245" }
Dec 18 02:05:46 platypus postfix/smtpd[16349]: improper command pipelining after NOOP from unknown[216.245.198.245]
+
+# failJSON: { "time": "2004-12-21T21:17:29", "match": true , "host": "93.184.216.34" }
+Dec 21 21:17:29 xxx postfix/smtpd[7150]: NOQUEUE: reject: RCPT from badserver.example.com[93.184.216.34]: 450 4.7.1 Client host rejected: cannot find your hostname, [93.184.216.34]; from=<badactor@example.com> to=<goodguy@example.com> proto=ESMTP helo=<badserver.example.com>
diff --git a/fail2ban/tests/files/logs/postfix-sasl b/fail2ban/tests/files/logs/postfix-sasl
index 697ac424..46c1e9da 100644
--- a/fail2ban/tests/files/logs/postfix-sasl
+++ b/fail2ban/tests/files/logs/postfix-sasl
@@ -8,3 +8,7 @@ Mar 10 13:33:30 gandalf postfix/smtpd[3937]: warning: HOSTNAME[1.1.1.1]: SASL LO
#3 Example from postfix post-debian changes to rename to add "submission" to syslog name
# failJSON: { "time": "2004-09-06T00:44:56", "match": true , "host": "82.221.106.233" }
Sep 6 00:44:56 trianon postfix/submission/smtpd[11538]: warning: unknown[82.221.106.233]: SASL LOGIN authentication failed: UGFzc3dvcmQ6
+
+#4 Example from postfix post-debian changes to rename to add "submission" to syslog name + downcase
+# failJSON: { "time": "2004-09-06T00:44:57", "match": true , "host": "82.221.106.233" }
+Sep 6 00:44:57 trianon postfix/submission/smtpd[11538]: warning: unknown[82.221.106.233]: SASL login authentication failed: UGFzc3dvcmQ6
diff --git a/fail2ban/tests/files/testcase-usedns.log b/fail2ban/tests/files/testcase-usedns.log
index a91fd7ac..de484127 100644
--- a/fail2ban/tests/files/testcase-usedns.log
+++ b/fail2ban/tests/files/testcase-usedns.log
@@ -1,2 +1,2 @@
Aug 14 11:54:59 i60p295 sshd[12365]: Failed publickey for roehl from example.com port 51332 ssh2
-Aug 14 11:58:59 i60p295 sshd[12365]: Failed publickey for roehl from ::ffff:93.184.216.119 port 51332 ssh2
+Aug 14 11:58:59 i60p295 sshd[12365]: Failed publickey for roehl from ::ffff:93.184.216.34 port 51332 ssh2
diff --git a/fail2ban/tests/filtertestcase.py b/fail2ban/tests/filtertestcase.py
index 26f1801a..50103ae0 100644
--- a/fail2ban/tests/filtertestcase.py
+++ b/fail2ban/tests/filtertestcase.py
@@ -951,12 +951,12 @@ class GetFailures(unittest.TestCase):
def testGetFailuresUseDNS(self):
# We should still catch failures with usedns = no ;-)
- output_yes = ('93.184.216.119', 2, 1124013539.0,
+ output_yes = ('93.184.216.34', 2, 1124013539.0,
[u'Aug 14 11:54:59 i60p295 sshd[12365]: Failed publickey for roehl from example.com port 51332 ssh2',
- u'Aug 14 11:58:59 i60p295 sshd[12365]: Failed publickey for roehl from ::ffff:93.184.216.119 port 51332 ssh2'])
+ u'Aug 14 11:58:59 i60p295 sshd[12365]: Failed publickey for roehl from ::ffff:93.184.216.34 port 51332 ssh2'])
- output_no = ('93.184.216.119', 1, 1124013539.0,
- [u'Aug 14 11:58:59 i60p295 sshd[12365]: Failed publickey for roehl from ::ffff:93.184.216.119 port 51332 ssh2'])
+ output_no = ('93.184.216.34', 1, 1124013539.0,
+ [u'Aug 14 11:58:59 i60p295 sshd[12365]: Failed publickey for roehl from ::ffff:93.184.216.34 port 51332 ssh2'])
# Actually no exception would be raised -- it will be just set to 'no'
#self.assertRaises(ValueError,
@@ -1056,9 +1056,9 @@ class DNSUtilsTests(unittest.TestCase):
res = DNSUtils.textToIp('www.example.com', 'no')
self.assertEqual(res, [])
res = DNSUtils.textToIp('www.example.com', 'warn')
- self.assertEqual(res, ['93.184.216.119'])
+ self.assertEqual(res, ['93.184.216.34'])
res = DNSUtils.textToIp('www.example.com', 'yes')
- self.assertEqual(res, ['93.184.216.119'])
+ self.assertEqual(res, ['93.184.216.34'])
def testTextToIp(self):
# Test hostnames
@@ -1070,7 +1070,7 @@ class DNSUtilsTests(unittest.TestCase):
for s in hostnames:
res = DNSUtils.textToIp(s, 'yes')
if s == 'www.example.com':
- self.assertEqual(res, ['93.184.216.119'])
+ self.assertEqual(res, ['93.184.216.34'])
else:
self.assertEqual(res, [])
diff --git a/files/debian-initd b/files/debian-initd
new file mode 100755
index 00000000..d9bb3f50
--- /dev/null
+++ b/files/debian-initd
@@ -0,0 +1,248 @@
+#! /bin/sh
+### BEGIN INIT INFO
+# Provides: fail2ban
+# Required-Start: $local_fs $remote_fs
+# Required-Stop: $local_fs $remote_fs
+# Should-Start: $time $network $syslog iptables firehol shorewall ipmasq arno-iptables-firewall iptables-persistent ferm
+# Should-Stop: $network $syslog iptables firehol shorewall ipmasq arno-iptables-firewall iptables-persistent ferm
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: Start/stop fail2ban
+# Description: Start/stop fail2ban, a daemon scanning the log files and
+# banning potential attackers.
+### END INIT INFO
+
+# Author: Aaron Isotton <aaron@isotton.com>
+# Modified: by Yaroslav Halchenko <debian@onerussian.com>
+# reindented + minor corrections + to work on sarge without modifications
+# Modified: by Glenn Aaldering <glenn@openvideo.nl>
+# added exit codes for status command
+# Modified: by Juan Karlo de Guzman <jkarlodg@gmail.com>
+# corrected the DAEMON's path and the SOCKFILE
+# rename this file: (sudo) mv /etc/init.d/fail2ban.init /etc/init.d/fail2ban
+# same with the logrotate file: (sudo) mv /etc/logrotate.d/fail2ban.logrotate /etc/logrotate.d/fail2ban
+#
+PATH=/usr/sbin:/usr/bin:/sbin:/bin
+DESC="authentication failure monitor"
+NAME=fail2ban
+
+# fail2ban-client is not a daemon itself but starts a daemon and
+# loads its with configuration
+DAEMON=/usr/local/bin/$NAME-client
+SCRIPTNAME=/etc/init.d/$NAME
+
+# Ad-hoc way to parse out socket file name
+SOCKFILE=`grep -h '^[^#]*socket *=' /etc/$NAME/$NAME.conf /etc/$NAME/$NAME.local 2>/dev/null \
+ | tail -n 1 | sed -e 's/.*socket *= *//g' -e 's/ *$//g'`
+[ -z "$SOCKFILE" ] && SOCKFILE='/var/run/fail2ban.sock'
+
+# Exit if the package is not installed
+[ -x "$DAEMON" ] || exit 0
+
+# Run as root by default.
+FAIL2BAN_USER=root
+
+# Read configuration variable file if it is present
+[ -r /etc/default/$NAME ] && . /etc/default/$NAME
+DAEMON_ARGS="$FAIL2BAN_OPTS"
+
+# Load the VERBOSE setting and other rcS variables
+[ -f /etc/default/rcS ] && . /etc/default/rcS
+
+# Predefine what can be missing from lsb source later on -- necessary to run
+# on sarge. Just present it in a bit more compact way from what was shipped
+log_daemon_msg () {
+ [ -z "$1" ] && return 1
+ echo -n "$1:"
+ [ -z "$2" ] || echo -n " $2"
+}
+
+# Define LSB log_* functions.
+# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
+# Actually has to (>=2.0-7) present in sarge. log_daemon_msg is predefined
+# so we must be ok
+. /lib/lsb/init-functions
+
+#
+# Shortcut function for abnormal init script interruption
+#
+report_bug()
+{
+ echo $*
+ echo "Please submit a bug report to Debian BTS (reportbug fail2ban)"
+ exit 1
+}
+
+#
+# Helper function to check if socket is present, which is often left after
+# abnormal exit of fail2ban and needs to be removed
+#
+check_socket()
+{
+ # Return
+ # 0 if socket is present and readable
+ # 1 if socket file is not present
+ # 2 if socket file is present but not readable
+ # 3 if socket file is present but is not a socket
+ [ -e "$SOCKFILE" ] || return 1
+ [ -r "$SOCKFILE" ] || return 2
+ [ -S "$SOCKFILE" ] || return 3
+ return 0
+}
+
+#
+# Function that starts the daemon/service
+#
+do_start()
+{
+ # Return
+ # 0 if daemon has been started
+ # 1 if daemon was already running
+ # 2 if daemon could not be started
+ do_status && return 1
+
+ if [ -e "$SOCKFILE" ]; then
+ log_failure_msg "Socket file $SOCKFILE is present"
+ [ "$1" = "force-start" ] \
+ && log_success_msg "Starting anyway as requested" \
+ || return 2
+ DAEMON_ARGS="$DAEMON_ARGS -x"
+ fi
+
+ # Assure that /var/run/fail2ban exists
+ [ -d /var/run/fail2ban ] || mkdir -p /var/run/fail2ban
+
+ if [ "$FAIL2BAN_USER" != "root" ]; then
+ # Make the socket directory, IP lists and fail2ban log
+ # files writable by fail2ban
+ chown "$FAIL2BAN_USER" /var/run/fail2ban
+ # Create the logfile if it doesn't exist
+ touch /var/log/fail2ban.log
+ chown "$FAIL2BAN_USER" /var/log/fail2ban.log
+ find /proc/net/xt_recent -name 'fail2ban-*' -exec chown "$FAIL2BAN_USER" {} \;
+ fi
+
+ start-stop-daemon --start --quiet --chuid "$FAIL2BAN_USER" --exec $DAEMON -- \
+ $DAEMON_ARGS start > /dev/null\
+ || return 2
+
+ return 0
+}
+
+
+#
+# Function that checks the status of fail2ban and returns
+# corresponding code
+#
+do_status()
+{
+ $DAEMON ping > /dev/null 2>&1
+ return $?
+}
+
+#
+# Function that stops the daemon/service
+#
+do_stop()
+{
+ # Return
+ # 0 if daemon has been stopped
+ # 1 if daemon was already stopped
+ # 2 if daemon could not be stopped
+ # other if a failure occurred
+ $DAEMON status > /dev/null 2>&1 || return 1
+ $DAEMON stop > /dev/null || return 2
+
+ # now we need actually to wait a bit since it might take time
+ # for server to react on client's stop request. Especially
+ # important for restart command on slow boxes
+ count=1
+ while do_status && [ $count -lt 60 ]; do
+ sleep 1
+ count=$(($count+1))
+ done
+ [ $count -lt 60 ] || return 3 # failed to stop
+
+ return 0
+}
+
+#
+# Function to reload configuration
+#
+do_reload() {
+ $DAEMON reload > /dev/null && return 0 || return 1
+ return 0
+}
+
+# yoh:
+# shortcut function to don't duplicate case statements and to don't use
+# bashisms (arrays). Fixes #368218
+#
+log_end_msg_wrapper()
+{
+ if [ "$3" != "no" ]; then
+ [ $1 -lt $2 ] && value=0 || value=1
+ log_end_msg $value
+ fi
+}
+
+command="$1"
+case "$command" in
+ start|force-start)
+ [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
+ do_start "$command"
+ log_end_msg_wrapper $? 2 "$VERBOSE"
+ ;;
+
+ stop)
+ [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
+ do_stop
+ log_end_msg_wrapper $? 2 "$VERBOSE"
+ ;;
+
+ restart|force-reload)
+ log_daemon_msg "Restarting $DESC" "$NAME"
+ do_stop
+ case "$?" in
+ 0|1)
+ do_start
+ log_end_msg_wrapper $? 1 "always"
+ ;;
+ *)
+ # Failed to stop
+ log_end_msg 1
+ ;;
+ esac
+ ;;
+
+ reload|force-reload)
+ log_daemon_msg "Reloading $DESC" "$NAME"
+ do_reload
+ log_end_msg $?
+ ;;
+
+ status)
+ log_daemon_msg "Status of $DESC"
+ do_status
+ case $? in
+ 0) log_success_msg " $NAME is running" ;;
+ 255)
+ check_socket
+ case $? in
+ 1) log_failure_msg " $NAME is not running" && exit 3 ;;
+ 0) log_failure_msg " $NAME is not running but $SOCKFILE exists" && exit 3 ;;
+ 2) log_failure_msg " $SOCKFILE not readable, status of $NAME is unknown" && exit 3 ;;
+ 3) log_failure_msg " $SOCKFILE exists but not a socket, status of $NAME is unknown" && exit 3 ;;
+ *) report_bug "Unknown return code from $NAME:check_socket." && exit 4 ;;
+ esac
+ ;;
+ *) report_bug "Unknown $NAME status code" && exit 4
+ esac
+ ;;
+ *)
+ echo "Usage: $SCRIPTNAME {start|force-start|stop|restart|force-reload|status}" >&2
+ exit 3
+ ;;
+esac
+
+:
diff --git a/files/monit/fail2ban b/files/monit/fail2ban
new file mode 100644
index 00000000..8e6c9419
--- /dev/null
+++ b/files/monit/fail2ban
@@ -0,0 +1,9 @@
+check process fail2ban with pidfile /var/run/fail2ban/fail2ban.pid
+ group services
+ start program = "/etc/init.d/fail2ban force-start"
+ stop program = "/etc/init.d/fail2ban stop || :"
+ if failed unixsocket /var/run/fail2ban/fail2ban.sock then restart
+ if 5 restarts within 5 cycles then timeout
+
+check file fail2ban_log with path /var/log/fail2ban.log
+ if match "ERROR|WARNING" then alert