Merge branch 'master' into 'sebres:ban-time-incr'

pull/716/head
sebres 2015-01-05 19:14:51 +01:00
commit d5ebe542f9
14 changed files with 401 additions and 10 deletions

1
.gitignore vendored
View File

@ -9,3 +9,4 @@ htmlcov
*.bak *.bak
__pycache__ __pycache__
.vagrant/ .vagrant/
.idea/

View File

@ -24,6 +24,8 @@ ver. 0.9.2 (2014/xx/xx) - increment ban time
multiple expressions was not possible). multiple expressions was not possible).
* filters.d/exim.conf - cover different settings of exim logs * filters.d/exim.conf - cover different settings of exim logs
details. Thanks bes.internal 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: - New Features:
* increment ban time (+ observer) functionality introduced. * 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 possible to extend a stock filter or jail regexp in .local file
(opposite to simply set failregex/ignoreregex that overwrites it), (opposite to simply set failregex/ignoreregex that overwrites it),
see gh-867. 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: - Enhancements:
* Enable multiport for firewallcmd-new action. Closes gh-834 * 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 ver. 0.9.1 (2014/10/29) - better, faster, stronger
---------- ----------

View File

@ -328,6 +328,7 @@ man/fail2ban-server.h2m
man/fail2ban-regex.1 man/fail2ban-regex.1
man/fail2ban-regex.h2m man/fail2ban-regex.h2m
man/generate-man man/generate-man
files/debian-initd
files/gentoo-initd files/gentoo-initd
files/gentoo-confd files/gentoo-confd
files/redhat-initd files/redhat-initd

1
THANKS
View File

@ -33,6 +33,7 @@ Daniel B.
Daniel Black Daniel Black
David Nutter David Nutter
Derek Atkins Derek Atkins
Donald Yandt
Eric Gerbier Eric Gerbier
Enrico Labedzki Enrico Labedzki
Eugene Hopkinson (SlowRiot) Eugene Hopkinson (SlowRiot)

View File

@ -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

View File

@ -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

View File

@ -9,7 +9,7 @@ before = common.conf
_daemon = postfix/(submission/)?smtp(d|s) _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 = ignoreregex =

View File

@ -13,6 +13,7 @@ before = common.conf
_daemon = postfix/(submission/)?smtp(d|s) _daemon = postfix/(submission/)?smtp(d|s)
failregex = ^%(__prefix_line)sNOQUEUE: reject: RCPT from \S+\[<HOST>\]: 554 5\.7\.1 .*$ 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: 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)sNOQUEUE: reject: VRFY from \S+\[<HOST>\]: 550 5\.1\.1 .*$
^%(__prefix_line)simproper command pipelining after \S+ from [^[]*\[<HOST>\]:?$ ^%(__prefix_line)simproper command pipelining after \S+ from [^[]*\[<HOST>\]:?$

View File

@ -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" } # 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] 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>

View File

@ -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 #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" } # 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 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

View File

@ -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: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

View File

@ -951,12 +951,12 @@ class GetFailures(unittest.TestCase):
def testGetFailuresUseDNS(self): def testGetFailuresUseDNS(self):
# We should still catch failures with usedns = no ;-) # 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: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, 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.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'])
# Actually no exception would be raised -- it will be just set to 'no' # Actually no exception would be raised -- it will be just set to 'no'
#self.assertRaises(ValueError, #self.assertRaises(ValueError,
@ -1056,9 +1056,9 @@ class DNSUtilsTests(unittest.TestCase):
res = DNSUtils.textToIp('www.example.com', 'no') res = DNSUtils.textToIp('www.example.com', 'no')
self.assertEqual(res, []) self.assertEqual(res, [])
res = DNSUtils.textToIp('www.example.com', 'warn') 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') res = DNSUtils.textToIp('www.example.com', 'yes')
self.assertEqual(res, ['93.184.216.119']) self.assertEqual(res, ['93.184.216.34'])
def testTextToIp(self): def testTextToIp(self):
# Test hostnames # Test hostnames
@ -1070,7 +1070,7 @@ class DNSUtilsTests(unittest.TestCase):
for s in hostnames: for s in hostnames:
res = DNSUtils.textToIp(s, 'yes') res = DNSUtils.textToIp(s, 'yes')
if s == 'www.example.com': if s == 'www.example.com':
self.assertEqual(res, ['93.184.216.119']) self.assertEqual(res, ['93.184.216.34'])
else: else:
self.assertEqual(res, []) self.assertEqual(res, [])

248
files/debian-initd Executable file
View File

@ -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
:

9
files/monit/fail2ban Normal file
View File

@ -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