Merge branch '0.10' into f2b-perfom-prepare-716-cs-0.10

pull/1483/head
sebres 2016-07-11 11:28:34 +02:00
commit 4c1bcac0c7
55 changed files with 1029 additions and 365 deletions

View File

@ -72,14 +72,33 @@ ver. 0.9.5 (2016/XX/XXX) - wanna-be-released
- failregex of previous monit version merged as single expression.
* filter.d/postfix.conf, filter.d/postfix-sasl.conf
- extended failregex daemon part, matching also `postfix/smtps/smtpd` now (gh-1391)
* fixed a grave bug within tags substitutions because of incorrect detection of recursion
in case of multiple inline substitutions of the same tag (affected actions: `bsd-ipfw`, etc).
Now tracks the actual list of the already substituted tags (per tag instead of single list)
* filter.d/common.conf
- unexpected extra regex-space in generic `__prefix_line` (gh-1405)
- all optional spaces normalized in `common.conf`, test covered now
- generic `__prefix_line` extended with optional brackets for the date ambit (gh-1421),
added new parameter `__date_ambit`
* gentoo-initd fixed --pidfile bug: `--pidfile` is option of start-stop-daemon,
not argument of fail2ban (see gh-1434)
* filter.d/asterisk.conf - fix security log support for PJSIP and Asterisk 13+
- New Features:
* New Actions:
- action.d/firewallcmd-rich-rules and action.d/firewallcmd-rich-logging (gh-1367)
- Enhancements:
* Extreme speedup of all sqlite database operations (gh-1436),
by using of following sqlite options:
- (synchronous = OFF) write data through OS without syncing
- (journal_mode = MEMORY) use memory for the transaction logging
- (temp_store = MEMORY) temporary tables and indices are kept in memory
* journald journalmatch for pure-ftpd (gh-1362)
* Add additional regex filter for dovecot ldap authentication failures (gh-1370)
* added additional regex filters for exim (gh-1371)
* filter.d/exim*conf
- added additional regexes (gh-1371)
- made port entry optional
ver. 0.9.4 (2016/03/08) - for-you-ladies

View File

@ -167,7 +167,6 @@ fail2ban/client/jailreader.py
fail2ban/client/jailsreader.py
fail2ban/exceptions.py
fail2ban/helpers.py
fail2ban/ipdns.py
fail2ban/__init__.py
fail2ban/protocol.py
fail2ban/server/action.py
@ -185,6 +184,7 @@ fail2ban/server/filter.py
fail2ban/server/filterpyinotify.py
fail2ban/server/filtersystemd.py
fail2ban/server/__init__.py
fail2ban/server/ipdns.py
fail2ban/server/iso8601.py
fail2ban/server/jail.py
fail2ban/server/jails.py

View File

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*-
# vi: set ft=python sts=4 ts=4 sw=4 noet :

View File

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*-
# vi: set ft=python sts=4 ts=4 sw=4 noet :
#

View File

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*-
# vi: set ft=python sts=4 ts=4 sw=4 noet :

View File

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*-
# vi: set ft=python sts=4 ts=4 sw=4 noet :
"""Script to run Fail2Ban tests battery

View File

@ -80,14 +80,17 @@ class BadIPsAction(ActionBase):
If invalid `category`, `score`, `banaction` or `updateperiod`.
"""
TIMEOUT = 10
_badips = "http://www.badips.com"
def _Request(self, url, **argv):
return Request(url, headers={'User-Agent': self.agent}, **argv)
def __init__(self, jail, name, category, score=3, age="24h", key=None,
banaction=None, bancategory=None, bankey=None, updateperiod=900, agent="Fail2Ban"):
banaction=None, bancategory=None, bankey=None, updateperiod=900, agent="Fail2Ban",
timeout=TIMEOUT):
super(BadIPsAction, self).__init__(jail, name)
self.timeout = timeout
self.agent = agent
self.category = category
self.score = score
@ -119,7 +122,7 @@ class BadIPsAction(ActionBase):
"""
try:
response = urlopen(
self._Request("/".join([self._badips, "get", "categories"])), None, 3)
self._Request("/".join([self._badips, "get", "categories"])), timeout=self.timeout)
except HTTPError as response:
messages = json.loads(response.read().decode('utf-8'))
self._logSys.error(
@ -173,7 +176,7 @@ class BadIPsAction(ActionBase):
urlencode({'age': age})])
if key:
url = "&".join([url, urlencode({'key': key})])
response = urlopen(self._Request(url))
response = urlopen(self._Request(url), timeout=self.timeout)
except HTTPError as response:
messages = json.loads(response.read().decode('utf-8'))
self._logSys.error(
@ -358,7 +361,7 @@ class BadIPsAction(ActionBase):
url = "/".join([self._badips, "add", self.category, aInfo['ip']])
if self.key:
url = "?".join([url, urlencode({'key': self.key})])
response = urlopen(self._Request(url))
response = urlopen(self._Request(url), timeout=self.timeout)
except HTTPError as response:
messages = json.loads(response.read().decode('utf-8'))
self._logSys.error(

View File

@ -6,34 +6,26 @@
[INCLUDES]
before = iptables-common.conf
before = firewallcmd-common.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 -j f2b-<name>
actionstart = firewall-cmd --direct --add-chain <family> filter f2b-<name>
firewall-cmd --direct --add-rule <family> filter f2b-<name> 1000 -j RETURN
firewall-cmd --direct --add-rule <family> 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>
actionstop = firewall-cmd --direct --remove-rule <family> filter <chain> 0 -j f2b-<name>
firewall-cmd --direct --remove-rules <family> filter f2b-<name>
firewall-cmd --direct --remove-chain <family> filter f2b-<name>
# Example actioncheck: firewall-cmd --direct --get-chains ipv4 filter | sed -e 's, ,\n,g' | grep -q '^f2b-recidive$'
actioncheck = firewall-cmd --direct --get-chains ipv4 filter | sed -e 's, ,\n,g' | grep -q '^f2b-<name>$'
actioncheck = firewall-cmd --direct --get-chains <family> filter | sed -e 's, ,\n,g' | grep -q '^f2b-<name>$'
actionban = firewall-cmd --direct --add-rule ipv4 filter f2b-<name> 0 -s <ip> -j <blocktype>
actionban = firewall-cmd --direct --add-rule <family> 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
actionunban = firewall-cmd --direct --remove-rule <family> filter f2b-<name> 0 -s <ip> -j <blocktype>
# DEV NOTES:
#

View File

@ -0,0 +1,76 @@
# Fail2Ban configuration file
#
# Author: Donald Yandt
#
[Init]
# Option: name
# Notes Default name of the chain
# Values: STRING
name = default
# Option port
# Notes Can also use port numbers separated by a comma and in rich-rules comma and/or space.
# Value STRING Default: 1:65535
port = 1:65535
# Option: protocol
# Notes [ tcp | udp | icmp | all ]
# Values: STRING Default: tcp
protocol = tcp
# Option: family(ipv4)
# Notes specifies the socket address family type
# Values: STRING
family = ipv4
# Option: chain
# Notes specifies the firewalld chain to which the Fail2Ban rules should be
# added
# Values: STRING Default: INPUT_direct
chain = INPUT_direct
# Option: zone
# Notes use command firewall-cmd --get-active-zones to see a list of all active zones. See firewalld man pages for more information on zones
# Values: STRING Default: public
zone = public
# Option: service
# Notes use command firewall-cmd --get-services to see a list of services available
# Examples services: amanda-client amanda-k5-client bacula bacula-client dhcp dhcpv6 dhcpv6-client dns freeipa-ldap freeipa-ldaps
# freeipa-replication ftp high-availability http https imaps ipp ipp-client ipsec iscsi-target kadmin kerberos
# kpasswd ldap ldaps libvirt libvirt-tls mdns mosh mountd ms-wbt mysql nfs ntp openvpn pmcd pmproxy pmwebapi pmwebapis pop3s
# postgresql privoxy proxy-dhcp puppetmaster radius rpc-bind rsyncd samba samba-client sane smtp squid ssh synergy
# telnet tftp tftp-client tinc tor-socks transmission-client vdsm vnc-server wbem-https xmpp-bosh xmpp-client xmpp-local xmpp-server
# Values: STRING Default: ssh
service = ssh
# Option: rejecttype (ipv4)
# Notes See iptables/firewalld man pages for ipv4 reject types.
# Values: STRING
rejecttype = icmp-port-unreachable
# Option: blocktype (ipv4/ipv6)
# Notes See iptables/firewalld man pages for jump targets. Common values are REJECT,
# REJECT --reject-with icmp-port-unreachable, DROP
# Values: STRING
blocktype = REJECT --reject-with <rejecttype>
# Option: rich-blocktype (ipv4/ipv6)
# Notes See firewalld man pages for jump targets. Common values are reject,
# reject type="icmp-port-unreachable", drop
# Values: STRING
rich-blocktype = reject type='<rejecttype>'
[Init?family=inet6]
# Option: family(ipv6)
# Notes specifies the socket address family type
# Values: STRING
family = ipv6
# Option: rejecttype (ipv6)
# Note: See iptables/firewalld man pages for ipv6 reject types.
# Values: STRING
rejecttype = icmp6-port-unreachable

View File

@ -14,20 +14,20 @@
[INCLUDES]
before = iptables-common.conf
before = firewallcmd-common.conf
[Definition]
actionstart = ipset create fail2ban-<name> hash:ip timeout <bantime>
firewall-cmd --direct --add-rule ipv4 filter <chain> 0 -p <protocol> -m multiport --dports <port> -m set --match-set fail2ban-<name> src -j <blocktype>
actionstart = ipset create <ipmset> hash:ip timeout <bantime>
firewall-cmd --direct --add-rule <family> filter <chain> 0 -p <protocol> -m multiport --dports <port> -m set --match-set <ipmset> src -j <blocktype>
actionstop = firewall-cmd --direct --remove-rule ipv4 filter <chain> 0 -p <protocol> -m multiport --dports <port> -m set --match-set fail2ban-<name> src -j <blocktype>
ipset flush fail2ban-<name>
ipset destroy fail2ban-<name>
actionstop = firewall-cmd --direct --remove-rule <family> filter <chain> 0 -p <protocol> -m multiport --dports <port> -m set --match-set <ipmset> src -j <blocktype>
ipset flush <ipmset>
ipset destroy <ipmset>
actionban = ipset add fail2ban-<name> <ip> timeout <bantime> -exist
actionban = ipset add <ipmset> <ip> timeout <bantime> -exist
actionunban = ipset del fail2ban-<name> <ip> -exist
actionunban = ipset del <ipmset> <ip> -exist
[Init]
@ -44,6 +44,12 @@ chain = INPUT_direct
bantime = 600
ipmset = f2b-<name>
[Init?family=inet6]
ipmset = f2b-<name>6
# DEV NOTES:
#

View File

@ -5,59 +5,22 @@
[INCLUDES]
before = iptables-common.conf
before = firewallcmd-common.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 conntrack --ctstate NEW -p <protocol> -m multiport --dports <port> -j f2b-<name>
actionstart = firewall-cmd --direct --add-chain <family> filter f2b-<name>
firewall-cmd --direct --add-rule <family> filter f2b-<name> 1000 -j RETURN
firewall-cmd --direct --add-rule <family> filter <chain> 0 -m conntrack --ctstate NEW -p <protocol> -m multiport --dports <port> -j f2b-<name>
actionstop = firewall-cmd --direct --remove-rule ipv4 filter <chain> 0 -m conntrack --ctstate 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>
actionstop = firewall-cmd --direct --remove-rule <family> filter <chain> 0 -m conntrack --ctstate NEW -p <protocol> -m multiport --dports <port> -j f2b-<name>
firewall-cmd --direct --remove-rules <family> filter f2b-<name>
firewall-cmd --direct --remove-chain <family> filter f2b-<name>
# Example actioncheck: firewall-cmd --direct --get-chains ipv4 filter | sed -e 's, ,\n,g' | grep -q '^f2b-apache-modsecurity$'
actioncheck = firewall-cmd --direct --get-chains ipv4 filter | sed -e 's, ,\n,g' | grep -q '^f2b-<name>$'
actioncheck = firewall-cmd --direct --get-chains <family> filter | sed -e 's, ,\n,g' | grep -q '^f2b-<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 | sed -e 's, ,\n,g' | grep -q '^f2b-apache-modsecurity$'
# f2b-apache-modsecurity
actionban = firewall-cmd --direct --add-rule <family> filter f2b-<name> 0 -s <ip> -j <blocktype>
actionunban = firewall-cmd --direct --remove-rule <family> filter f2b-<name> 0 -s <ip> -j <blocktype>

View File

@ -4,32 +4,23 @@
[INCLUDES]
before = iptables-common.conf
before = firewallcmd-common.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>
actionstart = firewall-cmd --direct --add-chain <family> filter f2b-<name>
firewall-cmd --direct --add-rule <family> filter f2b-<name> 1000 -j RETURN
firewall-cmd --direct --add-rule <family> 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>
actionstop = firewall-cmd --direct --remove-rule <family> filter <chain> 0 -m state --state NEW -p <protocol> -m multiport --dports <port> -j f2b-<name>
firewall-cmd --direct --remove-rules <family> filter f2b-<name>
firewall-cmd --direct --remove-chain <family> filter f2b-<name>
actioncheck = firewall-cmd --direct --get-chains ipv4 filter | grep -q 'f2b-<name>$'
actioncheck = firewall-cmd --direct --get-chains <family> filter | grep -q 'f2b-<name>$'
actionban = firewall-cmd --direct --add-rule ipv4 filter f2b-<name> 0 -s <ip> -j <blocktype>
actionban = firewall-cmd --direct --add-rule <family> filter f2b-<name> 0 -s <ip> -j <blocktype>
actionunban = firewall-cmd --direct --remove-rule ipv4 filter f2b-<name> 0 -s <ip> -j <blocktype>
[Init]
# Option: chain
# Notes specifies the iptables chain to which the fail2ban rules should be
# added
# Values: [ STRING ]
#
chain = INPUT_direct
actionunban = firewall-cmd --direct --remove-rule <family> filter f2b-<name> 0 -s <ip> -j <blocktype>
# DEV NOTES:
#

View File

@ -15,6 +15,10 @@
# firewall-cmd [--zone=<zone>] --list-all
# firewall-cmd [--zone=zone] --query-rich-rule='rule'
[INCLUDES]
before = firewallcmd-common.conf
[Definition]
actionstart =
@ -26,40 +30,22 @@ actioncheck =
# you can also use zones and/or service names.
#
# zone example:
# firewall-cmd --zone=<zone> --add-rich-rule="rule family='ipv4' source address='<ip>' port port='<port>' protocol='<protocol>' log prefix='f2b-<name>' level='<level>' limit value='<rate>/m' <blocktype>"
# firewall-cmd --zone=<zone> --add-rich-rule="rule family='<family>' source address='<ip>' port port='<port>' protocol='<protocol>' log prefix='f2b-<name>' level='<level>' limit value='<rate>/m' <rich-blocktype>"
#
# service name example:
# firewall-cmd --zone=<zone> --add-rich-rule="rule family='ipv4' source address='<ip>' service name='<service>' log prefix='f2b-<name>' level='<level>' limit value='<rate>/m' <blocktype>"
# firewall-cmd --zone=<zone> --add-rich-rule="rule family='<family>' source address='<ip>' service name='<service>' log prefix='f2b-<name>' level='<level>' limit value='<rate>/m' <rich-blocktype>"
#
# Because rich rules can only handle single or a range of ports we must split ports and execute the command for each port. Ports can be single and ranges seperated by a comma or space for an example: http, https, 22-60, 18 smtp
actionban = ports="<port>"; for p in $(echo $ports | tr ", " " "); do firewall-cmd --add-rich-rule="rule family='ipv4' source address='<ip>' port port='$p' protocol='<protocol>' log prefix='f2b-<name>' level='<level>' limit value='<rate>/m' <blocktype>"; done
actionban = ports="<port>"; for p in $(echo $ports | tr ", " " "); do firewall-cmd --add-rich-rule="rule family='<family>' source address='<ip>' port port='$p' protocol='<protocol>' log prefix='f2b-<name>' level='<level>' limit value='<rate>/m' <rich-blocktype>"; done
actionunban = ports="<port>"; for p in $(echo $ports | tr ", " " "); do firewall-cmd --remove-rich-rule="rule family='ipv4' source address='<ip>' port port='$p' protocol='<protocol>' log prefix='f2b-<name>' level='<level>' limit value='<rate>/m' <blocktype>"; done
actionunban = ports="<port>"; for p in $(echo $ports | tr ", " " "); do firewall-cmd --remove-rich-rule="rule family='<family>' source address='<ip>' port port='$p' protocol='<protocol>' log prefix='f2b-<name>' level='<level>' limit value='<rate>/m' <rich-blocktype>"; done
[Init]
name = default
# log levels are "emerg", "alert", "crit", "error", "warning", "notice", "info" or "debug"
level = info
# log rate per minute
rate = 1
zone = public
# use command firewall-cmd --get-services to see a list of services available
#
# Examples:
#
# amanda-client amanda-k5-client bacula bacula-client dhcp dhcpv6 dhcpv6-client dns freeipa-ldap freeipa-ldaps
# freeipa-replication ftp high-availability http https imaps ipp ipp-client ipsec iscsi-target kadmin kerberos
# kpasswd ldap ldaps libvirt libvirt-tls mdns mosh mountd ms-wbt mysql nfs ntp openvpn pmcd pmproxy pmwebapi pmwebapis pop3s
# postgresql privoxy proxy-dhcp puppetmaster radius rpc-bind rsyncd samba samba-client sane smtp squid ssh synergy
# telnet tftp tftp-client tinc tor-socks transmission-client vdsm vnc-server wbem-https xmpp-bosh xmpp-client xmpp-local xmpp-server
service = ssh
# reject types: 'icmp-net-unreachable', 'icmp-host-unreachable', 'icmp-port-unreachable', 'icmp-proto-unreachable',
# 'icmp-net-prohibited', 'icmp-host-prohibited', 'icmp-admin-prohibited' or 'tcp-reset'
blocktype = reject type='icmp-port-unreachable'

View File

@ -13,6 +13,10 @@
# firewall-cmd [--zone=<zone>] --list-all
# firewall-cmd [--zone=zone] --query-rich-rule='rule'
[INCLUDES]
before = firewallcmd-common.conf
[Definition]
actionstart =
@ -24,34 +28,15 @@ actioncheck =
#you can also use zones and/or service names.
#
# zone example:
# firewall-cmd --zone=<zone> --add-rich-rule="rule family='ipv4' source address='<ip>' port port='<port>' protocol='<protocol>' <blocktype>"
# firewall-cmd --zone=<zone> --add-rich-rule="rule family='ipv4' source address='<ip>' port port='<port>' protocol='<protocol>' <rich-blocktype>"
#
# service name example:
# firewall-cmd --zone=<zone> --add-rich-rule="rule family='ipv4' source address='<ip>' service name='<service>' <blocktype>"
# firewall-cmd --zone=<zone> --add-rich-rule="rule family='ipv4' source address='<ip>' service name='<service>' <rich-blocktype>"
#
# Because rich rules can only handle single or a range of ports we must split ports and execute the command for each port. Ports can be single and ranges seperated by a comma or space for an example: http, https, 22-60, 18 smtp
actionban = ports="<port>"; for p in $(echo $ports | tr ", " " "); do firewall-cmd --add-rich-rule="rule family='ipv4' source address='<ip>' port port='$p' protocol='<protocol>' <blocktype>"; done
actionban = ports="<port>"; for p in $(echo $ports | tr ", " " "); do firewall-cmd --add-rich-rule="rule family='<family>' source address='<ip>' port port='$p' protocol='<protocol>' <rich-blocktype>"; done
actionunban = ports="<port>"; for p in $(echo $ports | tr ", " " "); do firewall-cmd --remove-rich-rule="rule family='ipv4' source address='<ip>' port port='$p' protocol='<protocol>' <blocktype>"; done
actionunban = ports="<port>"; for p in $(echo $ports | tr ", " " "); do firewall-cmd --remove-rich-rule="rule family='<family>' source address='<ip>' port port='$p' protocol='<protocol>' <rich-blocktype>"; done
[Init]
name = default
zone = public
# use command firewall-cmd --get-services to see a list of services available
#
# Examples:
#
# amanda-client amanda-k5-client bacula bacula-client dhcp dhcpv6 dhcpv6-client dns freeipa-ldap freeipa-ldaps
# freeipa-replication ftp high-availability http https imaps ipp ipp-client ipsec iscsi-target kadmin kerberos
# kpasswd ldap ldaps libvirt libvirt-tls mdns mosh mountd ms-wbt mysql nfs ntp openvpn pmcd pmproxy pmwebapi pmwebapis pop3s
# postgresql privoxy proxy-dhcp puppetmaster radius rpc-bind rsyncd samba samba-client sane smtp squid ssh synergy
# telnet tftp tftp-client tinc tor-socks transmission-client vdsm vnc-server wbem-https xmpp-bosh xmpp-client xmpp-local xmpp-server
service = ssh
# reject types: 'icmp-net-unreachable', 'icmp-host-unreachable', 'icmp-port-unreachable', 'icmp-proto-unreachable',
# 'icmp-net-prohibited', 'icmp-host-prohibited', 'icmp-admin-prohibited' or 'tcp-reset'
blocktype = reject type='icmp-port-unreachable'

View File

@ -16,7 +16,7 @@
# we don't enable PF automatically; to enable run pfctl -e
# or add `pf_enable="YES"` to /etc/rc.conf (tested on FreeBSD)
actionstart = echo "table <<tablename>-<name>> persist counters" | pfctl -f-
echo "block proto <protocol> from <<tablename>-<name>> to any port <port>" | pfctl -f-
echo "block proto <protocol> from <<tablename>-<name>> to <actiontype>" | pfctl -f-
# Option: actionstop
@ -71,9 +71,20 @@ tablename = f2b
#
protocol = tcp
# Option: port
# Notes.: the port to block, defaults to any
# Values: [ STRING ]
#
port = any
# Option: actiontype
# Notes.: defines additions to the blocking rule
# Values: leave empty to block all attempts from the host
# Default: Value of the multiport
actiontype = <multiport>
# Option: allports
# Notes.: default addition to block all ports
# Usage.: use in jail config: "banaction = pf[actiontype=<allports>]"
allports = any
# Option: multiport
# Notes.: addition to block access only to specific ports
# Usage.: use in jail config: "banaction = pf[actiontype=<multiport>]"
multiport = any port <port>

View File

@ -16,17 +16,18 @@ __pid_re = (?:\[\d+\])
iso8601 = \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+[+-]\d{4}
# All Asterisk log messages begin like this:
log_prefix= (?:NOTICE|SECURITY)%(__pid_re)s:?(?:\[C-[\da-f]*\])? \S+:\d*( in \w+:)?
log_prefix= (?:NOTICE|SECURITY|WARNING)%(__pid_re)s:?(?:\[C-[\da-f]*\])? [^:]+:\d*( in \w+:)?
failregex = ^(%(__prefix_line)s|\[\]\s*)%(log_prefix)s Registration from '[^']*' failed for '<HOST>(:\d+)?' - (Wrong password|Username/auth name mismatch|No matching peer found|Not a local domain|Device does not match ACL|Peer is not supposed to register|ACL error \(permit/deny\)|Not a local domain)$
^(%(__prefix_line)s|\[\]\s*)%(log_prefix)s Call from '[^']*' \(<HOST>:\d+\) to extension '[^']*' rejected because extension not found in context
^(%(__prefix_line)s|\[\]\s*)%(log_prefix)s Host <HOST> failed to authenticate as '[^']*'$
^(%(__prefix_line)s|\[\]\s*)%(log_prefix)s No registration for peer '[^']*' \(from <HOST>\)$
^(%(__prefix_line)s|\[\]\s*)%(log_prefix)s Host <HOST> failed MD5 authentication for '[^']*' \([^)]+\)$
^(%(__prefix_line)s|\[\]\s*)%(log_prefix)s Failed to authenticate (user|device) [^@]+@<HOST>\S*$
^(%(__prefix_line)s|\[\]\s*)%(log_prefix)s hacking attempt detected '<HOST>'$
^(%(__prefix_line)s|\[\]\s*)%(log_prefix)s SecurityEvent="(FailedACL|InvalidAccountID|ChallengeResponseFailed|InvalidPassword)",EventTV="([\d-]+|%(iso8601)s)",Severity="[\w]+",Service="[\w]+",EventVersion="\d+",AccountID="(\d*|<unknown>)",SessionID=".+",LocalAddress="IPV[46]/(UDP|TCP|WS)/[\da-fA-F:.]+/\d+",RemoteAddress="IPV[46]/(UDP|TCP|WS)/<HOST>/\d+"(,Challenge="[\w/]+")?(,ReceivedChallenge="\w+")?(,Response="\w+",ExpectedResponse="\w*")?(,ReceivedHash="[\da-f]+")?(,ACLName="\w+")?$
^(%(__prefix_line)s|\[\]\s*WARNING%(__pid_re)s:?(?:\[C-[\da-f]*\])? )Ext\. s: "Rejecting unknown SIP connection from <HOST>"$
failregex = ^%(__prefix_line)s%(log_prefix)s Registration from '[^']*' failed for '<HOST>(:\d+)?' - (Wrong password|Username/auth name mismatch|No matching peer found|Not a local domain|Device does not match ACL|Peer is not supposed to register|ACL error \(permit/deny\)|Not a local domain)$
^%(__prefix_line)s%(log_prefix)s Call from '[^']*' \(<HOST>:\d+\) to extension '[^']*' rejected because extension not found in context
^%(__prefix_line)s%(log_prefix)s Host <HOST> failed to authenticate as '[^']*'$
^%(__prefix_line)s%(log_prefix)s No registration for peer '[^']*' \(from <HOST>\)$
^%(__prefix_line)s%(log_prefix)s Host <HOST> failed MD5 authentication for '[^']*' \([^)]+\)$
^%(__prefix_line)s%(log_prefix)s Failed to authenticate (user|device) [^@]+@<HOST>\S*$
^%(__prefix_line)s%(log_prefix)s hacking attempt detected '<HOST>'$
^%(__prefix_line)s%(log_prefix)s SecurityEvent="(FailedACL|InvalidAccountID|ChallengeResponseFailed|InvalidPassword)",EventTV="([\d-]+|%(iso8601)s)",Severity="[\w]+",Service="[\w]+",EventVersion="\d+",AccountID="(\d*|<unknown>)",SessionID=".+",LocalAddress="IPV[46]/(UDP|TCP|WS)/[\da-fA-F:.]+/\d+",RemoteAddress="IPV[46]/(UDP|TCP|WS)/<HOST>/\d+"(,Challenge="[\w/]+")?(,ReceivedChallenge="\w+")?(,Response="\w+",ExpectedResponse="\w*")?(,ReceivedHash="[\da-f]+")?(,ACLName="\w+")?$
^%(__prefix_line)s%(log_prefix)s "Rejecting unknown SIP connection from <HOST>"$
^%(__prefix_line)s%(log_prefix)s Request from '[^']*' failed for '<HOST>(?::\d+)?' \(callid: \w*\) - No matching endpoint found$
ignoreregex =

View File

@ -26,7 +26,7 @@ __daemon_re = [\[\(]?%(_daemon)s(?:\(\S+\))?[\]\)]?:?
# extra daemon info
# EXAMPLE: [ID 800047 auth.info]
__daemon_extra_re = (?:\[ID \d+ \S+\])
__daemon_extra_re = \[ID \d+ \S+\]
# Combinations of daemon name and PID
# EXAMPLES: sshd[31607], pop(pam_unix)[4920]
@ -44,14 +44,18 @@ __md5hex = (?:[\da-f]{2}:){15}[\da-f]{2}
# bsdverbose is where syslogd is started with -v or -vv and results in <4.3> or
# <auth.info> appearing before the host as per testcases/files/logs/bsd/*.
__bsd_syslog_verbose = (<[^.]+\.[^.]+>)
__bsd_syslog_verbose = <[^.]+\.[^.]+>
__vserver = @vserver_\S+
__date_ambit = (?:\[\])
# Common line prefixes (beginnings) which could be used in filters
#
# [bsdverbose]? [hostname] [vserver tag] daemon_id spaces
#
# This can be optional (for instance if we match named native log files)
__prefix_line = \s*%(__bsd_syslog_verbose)s?\s*(?:%(__hostname)s )?(?:%(__kernel_prefix)s )?(?:@vserver_\S+ )?%(__daemon_combs_re)s?\s%(__daemon_extra_re)s?\s*
__prefix_line = %(__date_ambit)s?\s*(?:%(__bsd_syslog_verbose)s\s+)?(?:%(__hostname)s\s+)?(?:%(__kernel_prefix)s\s+)?(?:%(__vserver)s\s+)?(?:%(__daemon_combs_re)s\s+)?(?:%(__daemon_extra_re)s\s+)?
# PAM authentication mechanism check for failures, e.g.: pam_unix, pam_sss,
# pam_ldap

View File

@ -13,7 +13,7 @@ before = common.conf
_daemon = courieresmtpd
failregex = ^%(__prefix_line)serror,relay=<HOST>,.*: 550 User (<.*> )?unknown\.?$
^%(__prefix_line)serror,relay=<HOST>,msg="535 Authentication failed\.",cmd:( AUTH \S+)?( [0-9a-zA-Z\+/=]+)?$
^%(__prefix_line)serror,relay=<HOST>,msg="535 Authentication failed\.",cmd:( AUTH \S+)?( [0-9a-zA-Z\+/=]+)?(?: \S+)$
ignoreregex =

View File

@ -9,8 +9,8 @@ after = exim-common.local
[Definition]
host_info = H=([\w.-]+ )?(\(\S+\) )?\[<HOST>\](:\d+)? (I=\[\S+\]:\d+ )?(U=\S+ )?(P=e?smtp )?
pid = ( \[\d+\])?
host_info = (?:H=([\w.-]+ )?(?:\(\S+\) )?)?\[<HOST>\](?::\d+)? (?:I=\[\S+\](:\d+)? )?(?:U=\S+ )?(?:P=e?smtp )?
pid = (?: \[\d+\])?
# DEV Notes:
# From exim source code: ./src/receive.c:add_host_info_for_log

View File

@ -14,13 +14,13 @@ before = exim-common.conf
[Definition]
failregex = ^%(pid)s %(host_info)ssender verify fail for <\S+>: (?:Unknown user|Unrouteable address|all relevant MX records point to non-existent hosts)\s*$
^%(pid)s \w+ authenticator failed for (\S+ )?\(\S+\) \[<HOST>\](:\d+)?( I=\[\S+\](:\d+)?)?: 535 Incorrect authentication data( \(set_id=.*\)|: \d+ Time\(s\))?\s*$
^%(pid)s %(host_info)sF=(<>|[^@]+@\S+) rejected RCPT [^@]+@\S+: (relay not permitted|Sender verify failed|Unknown user)\s*$
^%(pid)s SMTP protocol synchronization error \([^)]*\): rejected (connection from|"\S+") %(host_info)s(next )?input=".*"\s*$
^%(pid)s SMTP call from \S+ \[<HOST>\](:\d+)? (I=\[\S+\](:\d+)? )?dropped: too many nonmail commands \(last was "\S+"\)\s*$
^%(pid)s SMTP protocol error in "AUTH \S*(| \S*)" H=(|\S* )(|\(\S*\) )\[<HOST>\]\:\d+ I=\[\S*\]\:\d+ AUTH command used when not advertised\s*$
^%(pid)s no MAIL in SMTP connection from (|\S* )(|\(\S*\) )\[<HOST>\]\:\d+ I=\[\S*\]\:\d+ D=\d+s(| C=\S*)\s*$
^%(pid)s \S+ SMTP connection from (|\S* )(|\(\S*\) )\[<HOST>\]\:\d+ I=\[\S*\]\:\d+ closed by DROP in ACL\s*$
^%(pid)s \w+ authenticator failed for (\S+ )?\(\S+\) \[<HOST>\](?::\d+)?(?: I=\[\S+\](:\d+)?)?: 535 Incorrect authentication data( \(set_id=.*\)|: \d+ Time\(s\))?\s*$
^%(pid)s %(host_info)sF=(?:<>|[^@]+@\S+) rejected RCPT [^@]+@\S+: (?:relay not permitted|Sender verify failed|Unknown user)\s*$
^%(pid)s SMTP protocol synchronization error \([^)]*\): rejected (?:connection from|"\S+") %(host_info)s(?:next )?input=".*"\s*$
^%(pid)s SMTP call from \S+ %(host_info)sdropped: too many nonmail commands \(last was "\S+"\)\s*$
^%(pid)s SMTP protocol error in "AUTH \S*(?: \S*)?" %(host_info)sAUTH command used when not advertised\s*$
^%(pid)s no MAIL in SMTP connection from (?:\S* )?(?:\(\S*\) )?%(host_info)sD=\d+s(?: C=\S*)?\s*$
^%(pid)s \S+ SMTP connection from (?:\S* )?(?:\(\S*\) )?%(host_info)sclosed by DROP in ACL\s*$
ignoreregex =

View File

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
# Inspired by https://isc.sans.edu/forums/diary/When+Google+isnt+Google/15968/
#
# Written in Python to reuse built-in Python batteries and not depend on

View File

@ -22,7 +22,7 @@ _daemon = nsd
# (?:::f{4,6}:)?(?P<host>[\w\-.^_]+)
# Values: TEXT
failregex = ^\[\]%(__prefix_line)sinfo: ratelimit block .* query <HOST> TYPE255$
^\[\]%(__prefix_line)sinfo: .* <HOST> refused, no acl matches\.$
failregex = ^%(__prefix_line)sinfo: ratelimit block .* query <HOST> TYPE255$
^%(__prefix_line)sinfo: .* <HOST> refused, no acl matches\.$
ignoreregex =

View File

@ -94,6 +94,7 @@ backend = auto
# but it will be logged as a warning.
# no: if a hostname is encountered, will not be used for banning,
# but it will be logged as info.
# raw: use raw value (no hostname), allow use it for no-host filters/actions (example user)
usedns = warn
# "logencoding" specifies the encoding of the log files handled by the jail

View File

@ -44,7 +44,9 @@ class CSocket:
def send(self, msg):
# Convert every list member to string
obj = dumps([str(m) for m in msg], HIGHEST_PROTOCOL)
obj = dumps(map(
lambda m: str(m) if not isinstance(m, (list, dict, set)) else m, msg),
HIGHEST_PROTOCOL)
self.__csock.send(obj + CSPROTO.END)
return self.receive(self.__csock)

6
fail2ban/client/fail2banregex.py Executable file → Normal file
View File

@ -1,4 +1,3 @@
#!/usr/bin/python
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*-
# vi: set ft=python sts=4 ts=4 sw=4 noet :
#
@ -126,6 +125,8 @@ Report bugs to https://github.com/fail2ban/fail2ban/issues
help="set custom pattern used to match date/times"),
Option("-e", "--encoding",
help="File encoding. Default: system locale"),
Option("-r", "--raw", action='store_true',
help="Raw hosts, don't resolve dns"),
Option("-L", "--maxlines", type=int, default=0,
help="maxlines for multi-line regex"),
Option("-m", "--journalmatch",
@ -239,6 +240,7 @@ class Fail2banRegex(object):
self.encoding = opts.encoding
else:
self.encoding = locale.getpreferredencoding()
self.raw = True if opts.raw else False
def decode_line(self, line):
return FileContainer.decode_line('<LOG>', self.encoding, line)
@ -342,7 +344,7 @@ class Fail2banRegex(object):
orgLineBuffer = self._filter._Filter__lineBuffer
fullBuffer = len(orgLineBuffer) >= self._filter.getMaxLines()
try:
line, ret = self._filter.processLine(line, date, checkAllRegex=True)
line, ret = self._filter.processLine(line, date, checkAllRegex=True, returnRawHost=self.raw)
for match in ret:
# Append True/False flag depending if line was matched by
# more than one regex

View File

@ -34,7 +34,7 @@ from .filterreader import FilterReader
from .actionreader import ActionReader
from ..version import version
from ..helpers import getLogger
from ..helpers import splitcommaspace
from ..helpers import splitwords
# Gets the instance of the logger.
logSys = getLogger(__name__)
@ -42,9 +42,14 @@ logSys = getLogger(__name__)
class JailReader(ConfigReader):
# regex, to extract list of options:
optionCRE = re.compile("^((?:\w|-|_|\.)+)(?:\[(.*)\])?$")
# regex, to iterate over single option in option list, syntax:
# `action = act[p1="...", p2='...', p3=...]`, where the p3=... not contains `,` or ']'
# since v0.10 separator extended with `]\s*[` for support of multiple option groups, syntax
# `action = act[p1=...][p2=...]`
optionExtractRE = re.compile(
r'([\w\-_\.]+)=(?:"([^"]*)"|\'([^\']*)\'|([^,]*))(?:,|$)')
r'([\w\-_\.]+)=(?:"([^"]*)"|\'([^\']*)\'|([^,\]]*))(?:,|\]\s*\[|$)')
def __init__(self, name, force_enable=False, **kwargs):
ConfigReader.__init__(self, **kwargs)
@ -214,7 +219,7 @@ class JailReader(ConfigReader):
elif opt == "maxretry":
stream.append(["set", self.__name, "maxretry", value])
elif opt == "ignoreip":
for ip in splitcommaspace(value):
for ip in splitwords(value):
stream.append(["set", self.__name, "addignoreip", ip])
elif opt == "findtime":
stream.append(["set", self.__name, "findtime", value])

View File

@ -133,15 +133,15 @@ def excepthook(exctype, value, traceback):
"Unhandled exception in Fail2Ban:", exc_info=True)
return sys.__excepthook__(exctype, value, traceback)
def splitcommaspace(s):
"""Helper to split on any comma or space
def splitwords(s):
"""Helper to split words on any comma, space, or a new line
Returns empty list if input is empty (or None) and filters
out empty entries
"""
if not s:
return []
return filter(bool, re.split('[ ,]', s))
return filter(bool, map(str.strip, re.split('[ ,\n]+', s)))
class BgService(object):

View File

@ -46,9 +46,13 @@ _cmd_lock = threading.Lock()
# Todo: make it configurable resp. automatically set, ex.: `[ -f /proc/net/if_inet6 ] && echo 'yes' || echo 'no'`:
allowed_ipv6 = True
# max tag replacement count:
MAX_TAG_REPLACE_COUNT = 10
# compiled RE for tag name (replacement name)
TAG_CRE = re.compile(r'<([^ <>]+)>')
class CallingMap(MutableMapping):
"""A Mapping type which returns the result of callable values.
@ -229,8 +233,8 @@ class CommandAction(ActionBase):
def __setattr__(self, name, value):
if not name.startswith('_') and not callable(value):
# special case for some pasrameters:
if name == 'timeout':
value = MyTime.str2seconds(value)
if name in ('timeout', 'bantime'):
value = str(MyTime.str2seconds(value))
# parameters changed - clear properties and substitution cache:
self.__properties = None
self.__substCache.clear()
@ -356,27 +360,28 @@ class CommandAction(ActionBase):
tags = inptags.copy()
t = TAG_CRE
# repeat substitution while embedded-recursive (repFlag is True)
done = cls._escapedTags.copy()
while True:
repFlag = False
# substitute each value:
for tag in tags.iterkeys():
if tag in cls._escapedTags:
# Escaped so won't match
continue
# ignore escaped or already done:
if tag in done: continue
value = str(tags[tag])
# search and replace all tags within value, that can be interpolated using other tags:
m = t.search(value)
done = []
refCounts = {}
#logSys.log(5, 'TAG: %s, value: %s' % (tag, value))
while m:
found_tag = m.group(1)
#logSys.log(5, 'found: %s' % found_tag)
if found_tag == tag or found_tag in done:
if found_tag == tag or refCounts.get(found_tag, 1) > MAX_TAG_REPLACE_COUNT:
# recursive definitions are bad
#logSys.log(5, 'recursion fail tag: %s value: %s' % (tag, value) )
raise ValueError(
"properties contain self referencing definitions "
"and cannot be resolved, fail tag: %s value: %s" % (tag, value))
"and cannot be resolved, fail tag: %s, found: %s in %s, value: %s" %
(tag, found_tag, refCounts, value))
repl = None
if found_tag not in cls._escapedTags:
repl = tags.get(found_tag + '?' + conditional)
@ -390,7 +395,9 @@ class CommandAction(ActionBase):
continue
value = value.replace('<%s>' % found_tag, repl)
#logSys.log(5, 'value now: %s' % value)
done.append(found_tag)
# increment reference count:
refCounts[found_tag] = refCounts.get(found_tag, 0) + 1
# the next match for replace:
m = t.search(value, m.start())
#logSys.log(5, 'TAG: %s, newvalue: %s' % (tag, value))
# was substituted?
@ -399,6 +406,9 @@ class CommandAction(ActionBase):
if t.search(value):
repFlag = True
tags[tag] = value
# no more sub tags (and no possible composite), add this tag to done set (just to be faster):
if '<' not in value: done.add(tag)
# stop interpolation, if no replacements anymore:
if not repFlag:
break
return tags

View File

@ -42,7 +42,6 @@ from .banmanager import BanManager
from .jailthread import JailThread
from .action import ActionBase, CommandAction, CallingMap
from .mytime import MyTime
from .filter import IPAddr
from .utils import Utils
from ..helpers import getLogger

View File

@ -182,8 +182,23 @@ class Fail2BanDb(object):
filename, e.args[0])
raise
# differentiate pypy: switch journal mode later (save it during the upgrade),
# to prevent errors like "database table is locked":
try:
import __pypy__
pypy = True
except ImportError:
pypy = False
cur = self._db.cursor()
cur.execute("PRAGMA foreign_keys = ON;")
cur.execute("PRAGMA foreign_keys = ON")
# speedup: write data through OS without syncing (no wait):
cur.execute("PRAGMA synchronous = OFF")
# speedup: transaction log in memory, alternate using OFF (disable, rollback will be impossible):
if not pypy:
cur.execute("PRAGMA journal_mode = MEMORY")
# speedup: temporary tables and indices are kept in memory:
cur.execute("PRAGMA temp_store = MEMORY")
try:
cur.execute("SELECT version FROM fail2banDb LIMIT 1")
@ -203,6 +218,9 @@ class Fail2BanDb(object):
Fail2BanDb.__version__, version, newversion)
raise RuntimeError('Failed to fully update')
finally:
# pypy: set journal mode after possible upgrade db:
if pypy:
cur.execute("PRAGMA journal_mode = MEMORY")
cur.close()
@property
@ -245,13 +263,14 @@ class Fail2BanDb(object):
A timestamped backup is also created prior to attempting the update.
"""
self._dbBackupFilename = self.filename + '.' + time.strftime('%Y%m%d-%H%M%S', MyTime.gmtime())
shutil.copyfile(self.filename, self._dbBackupFilename)
logSys.info("Database backup created: %s", self._dbBackupFilename)
if version > Fail2BanDb.__version__:
raise NotImplementedError(
"Attempt to travel to future version of database ...how did you get here??")
self._dbBackupFilename = self.filename + '.' + time.strftime('%Y%m%d-%H%M%S', MyTime.gmtime())
shutil.copyfile(self.filename, self._dbBackupFilename)
logSys.info("Database backup created: %s", self._dbBackupFilename)
if version < 2:
cur.executescript("BEGIN TRANSACTION;"
"CREATE TEMPORARY TABLE logs_temp AS SELECT * FROM logs;"
@ -455,7 +474,7 @@ class Fail2BanDb(object):
queryArgs.append(MyTime.time() - bantime)
if ip is not None:
query += " AND ip=?"
queryArgs.append(ip)
queryArgs.append(str(ip))
query += " ORDER BY ip, timeofban desc"
return cur.execute(query, queryArgs)

View File

@ -78,9 +78,9 @@ class FailManager:
def addFailure(self, ticket, count=1):
attempts = 1
with self.__lock:
ip = ticket.getIP()
fid = ticket.getID()
try:
fData = self.__failList[ip]
fData = self.__failList[fid]
# if the same object - the same matches but +1 attempt:
if fData is ticket:
matches = None
@ -109,7 +109,7 @@ class FailManager:
fData = FailTicket(ticket=ticket)
if count > ticket.getAttempt():
fData.setRetry(count)
self.__failList[ip] = fData
self.__failList[fid] = fData
attempts = fData.getRetry()
self.__failTotal += 1
@ -132,7 +132,7 @@ class FailManager:
def cleanup(self, time):
with self.__lock:
todelete = [ip for ip,item in self.__failList.iteritems() \
todelete = [fid for fid,item in self.__failList.iteritems() \
if item.getLastTime() + self.__maxTime <= time]
if len(todelete) == len(self.__failList):
# remove all:
@ -142,27 +142,27 @@ class FailManager:
return
if len(todelete) / 2.0 <= len(self.__failList) / 3.0:
# few as 2/3 should be removed - remove particular items:
for ip in todelete:
del self.__failList[ip]
for fid in todelete:
del self.__failList[fid]
else:
# create new dictionary without items to be deleted:
self.__failList = dict((ip,item) for ip,item in self.__failList.iteritems() \
self.__failList = dict((fid,item) for fid,item in self.__failList.iteritems() \
if item.getLastTime() + self.__maxTime > time)
self.__bgSvc.service()
def delFailure(self, ip):
def delFailure(self, fid):
with self.__lock:
try:
del self.__failList[ip]
del self.__failList[fid]
except KeyError:
pass
def toBan(self, ip=None):
def toBan(self, fid=None):
with self.__lock:
for ip in ([ip] if ip != None and ip in self.__failList else self.__failList):
data = self.__failList[ip]
for fid in ([fid] if fid != None and fid in self.__failList else self.__failList):
data = self.__failList[fid]
if data.getRetry() >= self.__maxRetry:
del self.__failList[ip]
del self.__failList[fid]
return data
self.__bgSvc.service()
raise FailManagerEmpty

View File

@ -62,18 +62,39 @@ class Regex:
def __str__(self):
return "%s(%r)" % (self.__class__.__name__, self._regex)
##
# Replaces "<HOST>", "<IP4>", "<IP6>", "<FID>" with default regular expression for host
#
# (see gh-1374 for the discussion about other candidates)
# @return the replaced regular expression as string
@staticmethod
def _resolveHostTag(regex):
# Replace "<HOST>" with default regular expression for host:
# Other candidates (see gh-1374 for the discussion about):
# differentiate: r"""(?:(?:::f{4,6}:)?(?P<IPv4>(?:\d{1,3}\.){3}\d{1,3})|\[?(?P<IPv6>(?:[0-9a-fA-F]{1,4}::?|::){1,7}(?:[0-9a-fA-F]{1,4}|(?<=:):))\]?|(?P<HOST>[\w\-.^_]*\w))"""
# expected many changes in filter, failregex, etc...
# simple: r"""(?:::f{4,6}:)?(?P<host>[\w\-.^_:]*\w)"""
# not good enough, if not precise expressions around <HOST>, because for example will match '1.2.3.4:23930' as ip-address;
# Todo: move this functionality to filter reader, as default <HOST> replacement,
# make it configurable (via jail/filter configs)
return regex.replace("<HOST>",
r"""(?:::f{4,6}:)?(?P<host>(?:\d{1,3}\.){3}\d{1,3}|\[?(?:[0-9a-fA-F]{1,4}::?|::){1,7}(?:[0-9a-fA-F]{1,4}\]?|(?<=:):)|[\w\-.^_]*\w)""")
# 3 groups instead of <HOST> - separated ipv4, ipv6 and host
regex = regex.replace("<HOST>",
r"""(?:(?:::f{4,6}:)?(?P<ip4>(?:\d{1,3}\.){3}\d{1,3})|\[?(?P<ip6>(?:[0-9a-fA-F]{1,4}::?|::){1,7}(?:[0-9a-fA-F]{1,4}|(?<=:):))\]?|(?P<dns>[\w\-.^_]*\w))""")
# separated ipv4:
r = r"""(?:::f{4,6}:)?(?P<ip4>(?:\d{1,3}\.){3}\d{1,3})"""
regex = regex.replace("<IP4>", r); # self closed
regex = regex.replace("<F-IP4/>", r); # closed
# separated ipv6:
r = r"""(?P<ip6>(?:[0-9a-fA-F]{1,4}::?|::){1,7}(?:[0-9a-fA-F]{1,4}?|(?<=:):))"""
regex = regex.replace("<IP6>", r); # self closed
regex = regex.replace("<F-IP6/>", r); # closed
# separated dns:
r = r"""(?P<dns>[\w\-.^_]*\w)"""
regex = regex.replace("<DNS>", r); # self closed
regex = regex.replace("<F-DNS/>", r); # closed
# default failure-id as no space tag:
regex = regex.replace("<F-ID/>", r"""(?P<fid>\S+)"""); # closed
# default failure port, like 80 or http :
regex = regex.replace("<F-PORT/>", r"""(?P<port>\w+)"""); # closed
# default failure groups (begin / end tag) for customizable expressions:
for o,r in (('IP4', 'ip4'), ('IP6', 'ip6'), ('DNS', 'dns'), ('ID', 'fid'), ('PORT', 'fport')):
regex = regex.replace("<F-%s>" % o, "(?P<%s>" % r); # open tag
regex = regex.replace("</F-%s>" % o, ")"); # close tag
return regex
##
# Gets the regular expression.
@ -207,6 +228,13 @@ class RegexException(Exception):
pass
##
# Groups used as failure identifier.
#
# The order of this tuple is important while searching for failure-id
#
FAILURE_ID_GROPS = ("fid", "ip4", "ip6", "dns")
##
# Regular expression class.
#
@ -220,25 +248,48 @@ class FailRegex(Regex):
# Creates a new object. This method can throw RegexException in order to
# avoid construction of invalid object.
# @param value the regular expression
def __init__(self, regex):
# Initializes the parent.
Regex.__init__(self, regex)
# Check for group "host"
if "host" not in self._regexObj.groupindex:
raise RegexException("No 'host' group in '%s'" % self._regex)
# Check for group "dns", "ip4", "ip6", "fid"
if not [grp for grp in FAILURE_ID_GROPS if grp in self._regexObj.groupindex]:
raise RegexException("No failure-id group in '%s'" % self._regex)
##
# Returns the matched host.
# Returns all matched groups.
#
# This corresponds to the pattern matched by the named group "host".
# @return the matched host
def getGroups(self):
return self._matchCache.groupdict()
##
# Returns the matched failure id.
#
# This corresponds to the pattern matched by the named group from given groups.
# @return the matched failure-id
def getHost(self):
host = self._matchCache.group("host")
if host is None:
def getFailID(self, groups=FAILURE_ID_GROPS):
fid = None
for grp in groups:
try:
fid = self._matchCache.group(grp)
except (IndexError, KeyError):
continue
if fid is not None:
break
if fid is None:
# Gets a few information.
s = self._matchCache.string
r = self._matchCache.re
raise RegexException("No 'host' found in '%s' using '%s'" % (s, r))
return str(host)
raise RegexException("No group found in '%s' using '%s'" % (s, r))
return str(fid)
##
# Returns the matched host.
#
# This corresponds to the pattern matched by the named group "ip4", "ip6" or "dns".
# @return the matched host
def getHost(self):
return self.getFailID(("ip4", "ip6", "dns"))

View File

@ -171,7 +171,7 @@ class Filter(JailThread):
if isinstance(value, bool):
value = {True: 'yes', False: 'no'}[value]
value = value.lower() # must be a string by now
if not (value in ('yes', 'no', 'warn')):
if not (value in ('yes', 'warn', 'no', 'raw')):
logSys.error("Incorrect value %r specified for usedns. "
"Using safe 'no'" % (value,))
value = 'no'
@ -418,6 +418,9 @@ class Filter(JailThread):
ip = element[1]
unixTime = element[2]
lines = element[3]
fail = {}
if len(element) > 4:
fail = element[4]
logSys.debug("Processing line with time:%s and ip:%s",
unixTime, ip)
if unixTime < MyTime.time() - self.getFindTime():
@ -429,7 +432,7 @@ class Filter(JailThread):
logSys.info(
"[%s] Found %s - %s", self.jail.name, ip, datetime.datetime.fromtimestamp(unixTime).strftime("%Y-%m-%d %H:%M:%S")
)
tick = FailTicket(ip, unixTime, lines)
tick = FailTicket(ip, unixTime, lines, data=fail)
self.failManager.addFailure(tick)
##
@ -457,7 +460,12 @@ class Filter(JailThread):
checkAllRegex=False):
failList = list()
# Checks if we must ignore this line.
cidr = IPAddr.CIDR_UNSPEC
if self.__useDns == "raw":
returnRawHost = True
cidr = IPAddr.CIDR_RAW
# Checks if we mut ignore this line.
if self.ignoreLine([tupleLine[::2]]) is not None:
# The ignoreregex matched. Return.
logSys.log(7, "Matched ignoreregex and was \"%s\" ignored",
@ -518,19 +526,41 @@ class Filter(JailThread):
% ("\n".join(failRegex.getMatchedLines()), timeText))
else:
self.__lineBuffer = failRegex.getUnmatchedTupleLines()
# retrieve failure-id, host, etc from failure match:
raw = returnRawHost
try:
host = failRegex.getHost()
if returnRawHost:
failList.append([failRegexIndex, IPAddr(host), date,
failRegex.getMatchedLines()])
fail = failRegex.getGroups()
# failure-id:
fid = fail.get('fid')
# ip-address or host:
host = fail.get('ip4') or fail.get('ip6')
if host is not None:
raw = True
else:
host = fail.get('dns')
if host is None:
# if no failure-id also (obscure case, wrong regex), throw error inside getFailID:
if fid is None:
fid = failRegex.getFailID()
host = fid
cidr = IPAddr.CIDR_RAW
# if raw - add single ip or failure-id,
# otherwise expand host to multiple ips using dns (or ignore it if not valid):
if raw:
ip = IPAddr(host, cidr)
# check host equal failure-id, if not - failure with complex id:
if fid is not None and fid != host:
ip = IPAddr(fid, IPAddr.CIDR_RAW)
failList.append([failRegexIndex, ip, date,
failRegex.getMatchedLines(), fail])
if not checkAllRegex:
break
else:
ips = DNSUtils.textToIp(host, self.__useDns)
if ips:
for ip in ips:
failList.append([failRegexIndex, ip,
date, failRegex.getMatchedLines()])
failList.append([failRegexIndex, ip, date,
failRegex.getMatchedLines(), fail])
if not checkAllRegex:
break
except RegexException, e: # pragma: no cover - unsure if reachable

View File

@ -85,10 +85,7 @@ class DNSUtils:
return v
# retrieve name
try:
if not isinstance(ip, IPAddr):
v = socket.gethostbyaddr(ip)[0]
else:
v = socket.gethostbyaddr(ip.ntoa)[0]
v = socket.gethostbyaddr(ip)[0]
except socket.error, e:
logSys.debug("Unable to find a name for the IP %s: %s", ip, e)
v = None
@ -138,18 +135,21 @@ class IPAddr(object):
# todo: make configurable the expired time and max count of cache entries:
CACHE_OBJ = Utils.Cache(maxCount=1000, maxTime=5*60)
def __new__(cls, ipstr, cidr=-1):
CIDR_RAW = -2
CIDR_UNSPEC = -1
def __new__(cls, ipstr, cidr=CIDR_UNSPEC):
# check already cached as IPAddr
args = (ipstr, cidr)
ip = IPAddr.CACHE_OBJ.get(args)
if ip is not None:
return ip
# wrap mask to cidr (correct plen):
if cidr == -1:
if cidr == IPAddr.CIDR_UNSPEC:
ipstr, cidr = IPAddr.__wrap_ipstr(ipstr)
args = (ipstr, cidr)
# check cache again:
if cidr != -1:
if cidr != IPAddr.CIDR_UNSPEC:
ip = IPAddr.CACHE_OBJ.get(args)
if ip is not None:
return ip
@ -166,17 +166,17 @@ class IPAddr(object):
ipstr = ipstr[1:-1]
# test mask:
if "/" not in ipstr:
return ipstr, -1
return ipstr, IPAddr.CIDR_UNSPEC
s = ipstr.split('/', 1)
# IP address without CIDR mask
if len(s) > 2:
raise ValueError("invalid ipstr %r, too many plen representation" % (ipstr,))
if "." in s[1]: # 255.255.255.0 style mask
if "." in s[1] or ":" in s[1]: # 255.255.255.0 resp. ffff:: style mask
s[1] = IPAddr.masktoplen(s[1])
s[1] = long(s[1])
return s
def __init(self, ipstr, cidr=-1):
def __init(self, ipstr, cidr=CIDR_UNSPEC):
""" initialize IP object by converting IP address string
to binary to integer
"""
@ -184,55 +184,62 @@ class IPAddr(object):
self._addr = 0
self._plen = 0
self._maskplen = None
self._raw = ""
# always save raw value (normally used if really raw or not valid only):
self._raw = ipstr
# if not raw - recognize family, set addr, etc.:
if cidr != IPAddr.CIDR_RAW:
for family in [socket.AF_INET, socket.AF_INET6]:
try:
binary = socket.inet_pton(family, ipstr)
self._family = family
break
except socket.error:
continue
for family in [socket.AF_INET, socket.AF_INET6]:
try:
binary = socket.inet_pton(family, ipstr)
self._family = family
break
except socket.error:
continue
if self._family == socket.AF_INET:
# convert host to network byte order
self._addr, = struct.unpack("!L", binary)
self._plen = 32
# mask out host portion if prefix length is supplied
if cidr is not None and cidr >= 0:
mask = ~(0xFFFFFFFFL >> cidr)
self._addr &= mask
self._plen = cidr
elif self._family == socket.AF_INET6:
# convert host to network byte order
hi, lo = struct.unpack("!QQ", binary)
self._addr = (hi << 64) | lo
self._plen = 128
# mask out host portion if prefix length is supplied
if cidr is not None and cidr >= 0:
mask = ~(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFL >> cidr)
self._addr &= mask
self._plen = cidr
# if IPv6 address is a IPv4-compatible, make instance a IPv4
elif self.isInNet(IPAddr.IP6_4COMPAT):
self._addr = lo & 0xFFFFFFFFL
self._family = socket.AF_INET
if self._family == socket.AF_INET:
# convert host to network byte order
self._addr, = struct.unpack("!L", binary)
self._plen = 32
# mask out host portion if prefix length is supplied
if cidr is not None and cidr >= 0:
mask = ~(0xFFFFFFFFL >> cidr)
self._addr &= mask
self._plen = cidr
elif self._family == socket.AF_INET6:
# convert host to network byte order
hi, lo = struct.unpack("!QQ", binary)
self._addr = (hi << 64) | lo
self._plen = 128
# mask out host portion if prefix length is supplied
if cidr is not None and cidr >= 0:
mask = ~(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFL >> cidr)
self._addr &= mask
self._plen = cidr
# if IPv6 address is a IPv4-compatible, make instance a IPv4
elif self.isInNet(IPAddr.IP6_4COMPAT):
self._addr = lo & 0xFFFFFFFFL
self._family = socket.AF_INET
self._plen = 32
else:
# string couldn't be converted neither to a IPv4 nor
# to a IPv6 address - retain raw input for later use
# (e.g. DNS resolution)
self._raw = ipstr
self._family = IPAddr.CIDR_RAW
def __repr__(self):
return self.ntoa
def __str__(self):
return self.ntoa
def __reduce__(self):
"""IPAddr pickle-handler, that simply wraps IPAddr to the str
Returns a string as instance to be pickled, because fail2ban-client can't
unserialize IPAddr objects
"""
return (str, (self.ntoa,))
@property
def addr(self):
@ -262,6 +269,8 @@ class IPAddr(object):
return self._family != socket.AF_UNSPEC
def __eq__(self, other):
if self._family == IPAddr.CIDR_RAW and not isinstance(other, IPAddr):
return self._raw == other
if not isinstance(other, IPAddr):
if other is None: return False
other = IPAddr(other)
@ -277,6 +286,8 @@ class IPAddr(object):
return not (self == other)
def __lt__(self, other):
if self._family == IPAddr.CIDR_RAW and not isinstance(other, IPAddr):
return self._raw < other
if not isinstance(other, IPAddr):
if other is None: return False
other = IPAddr(other)
@ -345,7 +356,7 @@ class IPAddr(object):
if not suffix:
suffix = "in-addr.arpa."
elif self.isIPv6:
exploded_ip = self.hexdump()
exploded_ip = self.hexdump
if not suffix:
suffix = "ip6.arpa."
else:
@ -384,17 +395,29 @@ class IPAddr(object):
return (self.addr & mask) == net.addr
# Pre-calculated map: addr to maskplen
def __getMaskMap():
m6 = (1 << 128)-1
m4 = (1 << 32)-1
mmap = {m6: 128, m4: 32, 0: 0}
m = 0
for i in xrange(0, 128):
m |= 1 << i
if i < 32:
mmap[m ^ m4] = 32-1-i
mmap[m ^ m6] = 128-1-i
return mmap
MAP_ADDR2MASKPLEN = __getMaskMap()
@property
def maskplen(self):
mplen = 0
if self._maskplen is not None:
return self._maskplen
maddr = self._addr
while maddr:
if not (maddr & 0x80000000):
raise ValueError("invalid mask %r, no plen representation" % (str(self),))
maddr = (maddr << 1) & 0xFFFFFFFFL
mplen += 1
mplen = IPAddr.MAP_ADDR2MASKPLEN.get(self._addr)
if mplen is None:
raise ValueError("invalid mask %r, no plen representation" % (str(self),))
self._maskplen = mplen
return mplen

View File

@ -299,8 +299,10 @@ class Server:
flt = self.__jails[name].filter
if multiple:
for value in value:
logSys.debug(" failregex: %r", value)
flt.addFailRegex(value)
else:
logSys.debug(" failregex: %r", value)
flt.addFailRegex(value)
def delFailRegex(self, name, index):
@ -313,8 +315,10 @@ class Server:
flt = self.__jails[name].filter
if multiple:
for value in value:
logSys.debug(" ignoreregex: %r", value)
flt.addIgnoreRegex(value)
else:
logSys.debug(" ignoreregex: %r", value)
flt.addIgnoreRegex(value)
def delIgnoreRegex(self, name, index):

View File

@ -36,7 +36,7 @@ logSys = getLogger(__name__)
class Ticket:
def __init__(self, ip=None, time=None, matches=None, ticket=None):
def __init__(self, ip=None, time=None, matches=None, data={}, ticket=None):
"""Ticket constructor
@param ip the IP address
@ -50,6 +50,7 @@ class Ticket:
self._banTime = None;
self._time = time if time is not None else MyTime.time()
self._data = {'matches': [], 'failures': 0}
self._data.update(data)
if ticket:
# ticket available - copy whole information from ticket:
self.__dict__.update(i for i in ticket.__dict__.iteritems() if i[0] in self.__dict__)
@ -78,6 +79,9 @@ class Ticket:
value = IPAddr(value)
self.__ip = value
def getID(self):
return self._data.get('fid', self.__ip)
def getIP(self):
return self.__ip
@ -164,12 +168,12 @@ class Ticket:
class FailTicket(Ticket):
def __init__(self, ip=None, time=None, matches=None, ticket=None):
def __init__(self, ip=None, time=None, matches=None, data={}, ticket=None):
# this class variables:
self.__retry = 0
self.__lastReset = None
# create/copy using default ticket constructor:
Ticket.__init__(self, ip, time, matches, ticket)
Ticket.__init__(self, ip, time, matches, data, ticket)
# init:
if ticket is None:
self.__lastReset = time if time is not None else self.getTime()

View File

@ -52,7 +52,7 @@ class Transmitter:
def proceed(self, command):
# Deserialize object
logSys.debug("Command: " + repr(command))
logSys.debug("Command: %r", command)
try:
ret = self.__commandHandler(command)
ack = 0, ret
@ -265,6 +265,7 @@ class Transmitter:
action = self.__server.getAction(name, actionname)
if multiple:
for cmd in command[3]:
logSys.debug(" %r", cmd)
actionkey = cmd[0]
if callable(getattr(action, actionkey, None)):
actionvalue = json.loads(cmd[1]) if len(cmd)>1 else {}

View File

@ -39,6 +39,7 @@ if sys.version_info >= (2,7):
self.jail.actions.add("badips", pythonModule, initOpts={
'category': "ssh",
'banaction': "test",
'timeout': (3 if unittest.F2B.fast else 30),
})
self.action = self.jail.actions["badips"]

View File

@ -30,6 +30,7 @@ import time
import unittest
from ..server.action import CommandAction, CallingMap
from ..server.actions import OrderedDict
from ..server.utils import Utils
from .utils import LogCaptureTestCase
@ -65,6 +66,62 @@ class CommandActionTest(LogCaptureTestCase):
lambda: CommandAction.substituteRecursiveTags({'A': 'to=<B> fromip=<IP>', 'C': '<B>', 'B': '<C>', 'D': ''}))
self.assertRaises(ValueError,
lambda: CommandAction.substituteRecursiveTags({'failregex': 'to=<honeypot> fromip=<IP>', 'sweet': '<honeypot>', 'honeypot': '<sweet>', 'ignoreregex': ''}))
# We need here an ordered, because the sequence of iteration is very important for this test
if OrderedDict:
# No cyclic recursion, just multiple replacement of tag <T>, should be successful:
self.assertEqual(CommandAction.substituteRecursiveTags( OrderedDict(
(('X', 'x=x<T>'), ('T', '1'), ('Z', '<X> <T> <Y>'), ('Y', 'y=y<T>')))
), {'X': 'x=x1', 'T': '1', 'Y': 'y=y1', 'Z': 'x=x1 1 y=y1'}
)
# No cyclic recursion, just multiple replacement of tag <T> in composite tags, should be successful:
self.assertEqual(CommandAction.substituteRecursiveTags( OrderedDict(
(('X', 'x=x<T> <Z> <<R1>> <<R2>>'), ('R1', 'Z'), ('R2', 'Y'), ('T', '1'), ('Z', '<T> <Y>'), ('Y', 'y=y<T>')))
), {'X': 'x=x1 1 y=y1 1 y=y1 y=y1', 'R1': 'Z', 'R2': 'Y', 'T': '1', 'Z': '1 y=y1', 'Y': 'y=y1'}
)
# No cyclic recursion, just multiple replacement of same tags, should be successful:
self.assertEqual(CommandAction.substituteRecursiveTags( OrderedDict((
('actionstart', 'ipset create <ipmset> hash:ip timeout <bantime> family <ipsetfamily>\n<iptables> -I <chain> <actiontype>'),
('ipmset', 'f2b-<name>'),
('name', 'any'),
('bantime', '600'),
('ipsetfamily', 'inet'),
('iptables', 'iptables <lockingopt>'),
('lockingopt', '-w'),
('chain', 'INPUT'),
('actiontype', '<multiport>'),
('multiport', '-p <protocol> -m multiport --dports <port> -m set --match-set <ipmset> src -j <blocktype>'),
('protocol', 'tcp'),
('port', 'ssh'),
('blocktype', 'REJECT',),
))
), OrderedDict((
('actionstart', 'ipset create f2b-any hash:ip timeout 600 family inet\niptables -w -I INPUT -p tcp -m multiport --dports ssh -m set --match-set f2b-any src -j REJECT'),
('ipmset', 'f2b-any'),
('name', 'any'),
('bantime', '600'),
('ipsetfamily', 'inet'),
('iptables', 'iptables -w'),
('lockingopt', '-w'),
('chain', 'INPUT'),
('actiontype', '-p tcp -m multiport --dports ssh -m set --match-set f2b-any src -j REJECT'),
('multiport', '-p tcp -m multiport --dports ssh -m set --match-set f2b-any src -j REJECT'),
('protocol', 'tcp'),
('port', 'ssh'),
('blocktype', 'REJECT')
))
)
# Cyclic recursion by composite tag creation, tags "create" another tag, that closes cycle:
self.assertRaises(ValueError, lambda: CommandAction.substituteRecursiveTags( OrderedDict((
('A', '<<B><C>>'),
('B', 'D'), ('C', 'E'),
('DE', 'cycle <A>'),
)) ))
self.assertRaises(ValueError, lambda: CommandAction.substituteRecursiveTags( OrderedDict((
('DE', 'cycle <A>'),
('A', '<<B><C>>'),
('B', 'D'), ('C', 'E'),
)) ))
# missing tags are ok
self.assertEqual(CommandAction.substituteRecursiveTags({'A': '<C>'}), {'A': '<C>'})
self.assertEqual(CommandAction.substituteRecursiveTags({'A': '<C> <D> <X>','X':'fun'}), {'A': '<C> <D> fun', 'X':'fun'})

View File

@ -45,7 +45,7 @@ TEST_FILES_DIR_SHARE_CFG = {}
from .utils import CONFIG_DIR
CONFIG_DIR_SHARE_CFG = unittest.F2B.share_config
STOCK = os.path.exists(os.path.join('config','fail2ban.conf'))
STOCK = os.path.exists(os.path.join('config', 'fail2ban.conf'))
IMPERFECT_CONFIG = os.path.join(os.path.dirname(__file__), 'config')
IMPERFECT_CONFIG_SHARE_CFG = {}
@ -255,6 +255,13 @@ class JailReaderTest(LogCaptureTestCase):
result = JailReader.extractOptions(option)
self.assertEqual(expected, result)
# And multiple groups (`][` instead of `,`)
result = JailReader.extractOptions(option.replace(',', ']['))
expected2 = (expected[0],
dict((k, v.replace(',', '][')) for k, v in expected[1].iteritems())
)
self.assertEqual(expected2, result)
def testVersionAgent(self):
jail = JailReader('blocklisttest', force_enable=True, basedir=CONFIG_DIR)
# emulate jail.read(), because such jail not exists:

View File

@ -0,0 +1,17 @@
# Fail2Ban generic example resp. test filter
#
# Author: Serg G. Brester (sebres)
#
[INCLUDES]
# Read common prefixes. If any customizations available -- read them from
# common.local
before = ../../../../config/filter.d/common.conf
[Definition]
_daemon = test-demo
failregex = ^%(__prefix_line)sF2B: failure from <HOST>$
ignoreregex =

View File

@ -120,6 +120,15 @@ class Fail2banRegexTest(LogCaptureTestCase):
self.assertLogged('Dez 31 11:59:59 [sshd] error: PAM: Authentication failure for kevin from 193.168.0.128')
self.assertLogged('Dec 31 11:59:59 [sshd] error: PAM: Authentication failure for kevin from 87.142.124.10')
def testDirectRE_1raw(self):
(opts, args, fail2banRegex) = _Fail2banRegex(
"--print-all-matched", "--raw",
Fail2banRegexTest.FILENAME_01,
Fail2banRegexTest.RE_00
)
self.assertTrue(fail2banRegex.start(opts, args))
self.assertLogged('Lines: 19 lines, 0 ignored, 16 matched, 3 missed')
def testDirectRE_2(self):
(opts, args, fail2banRegex) = _Fail2banRegex(
"--print-all-matched",

View File

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
import requests
try:

View File

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
import sys
if sys.argv[1] == "10.0.0.1":
exit(0)

View File

@ -67,3 +67,7 @@ Nov 4 18:30:40 localhost asterisk[32229]: NOTICE[32257]: chan_sip.c:23417 in han
[2016-01-28 10:34:31] NOTICE[3477][C-000003c3] chan_sip.c: Call from '' (1.2.3.4:10836) to extension '0+441772285407' rejected because extension not found in context 'default'.
# failJSON: { "time": "2016-01-28T10:34:33", "match": true , "host": "1.2.3.4" }
[2016-01-28 10:34:33] NOTICE[3477][C-000003c3] chan_sip.c: Call from '' (1.2.3.4:10836) to extension '' rejected because extension not found in context 'my-context'.
# Failed authentication with pjsip on Asterisk 13+
# failJSON: { "time": "2016-05-23T10:18:16", "match": true , "host": "1.2.3.4" }
[2016-05-23 10:18:16] NOTICE[19388] res_pjsip/pjsip_distributor.c: Request from '"1000" <sip:1000@10.0.0.1>' failed for '1.2.3.4:48336' (callid: 276666022) - No matching endpoint found

View File

@ -10,3 +10,5 @@ Jul 6 03:42:28 whistler courieresmtpd: error,relay=::ffff:1.2.3.4,from=<>,to=<a
Nov 21 23:16:17 server courieresmtpd: error,relay=::ffff:1.2.3.4,from=<>,to=<>: 550 User unknown.
# failJSON: { "time": "2004-08-14T12:51:04", "match": true , "host": "1.2.3.4" }
Aug 14 12:51:04 HOSTNAME courieresmtpd: error,relay=::ffff:1.2.3.4,from=<firozquarl@aclunc.org>,to=<BOGUSUSER@HOSTEDDOMAIN.org>: 550 User unknown.
# failJSON: { "time": "2004-08-14T12:51:04", "match": true , "host": "1.2.3.4" }
Aug 14 12:51:04 mail.server courieresmtpd[26762]: error,relay=::ffff:1.2.3.4,msg="535 Authentication failed.",cmd: AUTH PLAIN AAAAABBBBCCCCWxlZA== admin

View File

@ -48,10 +48,14 @@
2016-03-18 00:34:06 [7513] SMTP protocol error in "AUTH LOGIN" H=(ylmf-pc) [45.32.34.167]:60723 I=[172.89.0.6]:587 AUTH command used when not advertised
# failJSON: { "time": "2016-03-19T18:40:44", "match": true , "host": "92.45.204.170" }
2016-03-19 18:40:44 [26221] SMTP protocol error in "AUTH LOGIN aW5mb0BtYW5iYXQub3Jn" H=([127.0.0.1]) [92.45.204.170]:14243 I=[172.89.0.6]:587 AUTH command used when not advertised
# failJSON: { "time": "2016-05-17T06:25:27", "match": true , "host": "69.10.61.61", "desc": "from gh-1430" }
2016-05-17 06:25:27 SMTP protocol error in "AUTH LOGIN" H=(ylmf-pc) [69.10.61.61] AUTH command used when not advertised
# failJSON: { "time": "2016-03-21T06:38:05", "match": true , "host": "49.212.207.15" }
2016-03-21 06:38:05 [5718] no MAIL in SMTP connection from www3005.sakura.ne.jp [49.212.207.15]:28890 I=[172.89.0.6]:25 D=21s C=EHLO,STARTTLS
# failJSON: { "time": "2016-03-21T06:57:36", "match": true , "host": "122.165.71.116" }
2016-03-21 06:57:36 [5908] no MAIL in SMTP connection from [122.165.71.116]:2056 I=[172.89.0.6]:25 D=10s
# failJSON: { "time": "2016-03-21T06:57:36", "match": true , "host": "122.165.71.116" }
2016-03-21 06:57:36 [5908] no MAIL in SMTP connection from [122.165.71.116] I=[172.89.0.6]:25 D=10s
# failJSON: { "time": "2016-03-21T04:07:49", "match": true , "host": "174.137.147.204" }
2016-03-21 04:07:49 [25874] 1ahr79-0006jK-G9 SMTP connection from (voyeur.webair.com) [174.137.147.204]:44884 I=[172.89.0.6]:25 closed by DROP in ACL
# failJSON: { "time": "2016-03-21T04:33:13", "match": true , "host": "206.214.71.53" }

View File

@ -0,0 +1,31 @@
# -- _daemon with __pid_re, without __hostname --
# failJSON: { "time": "2005-06-21T16:47:46", "match": true , "host": "192.0.2.1" }
Jun 21 16:47:46 machine test-demo[13709]: F2B: failure from 192.0.2.1
# -- _daemon with __pid_re --
# failJSON: { "time": "2005-06-21T16:47:48", "match": true , "host": "192.0.2.1" }
Jun 21 16:47:48 test-demo[13709]: F2B: failure from 192.0.2.1
# -- __kernel_prefix --
# failJSON: { "time": "2005-06-21T16:47:50", "match": true , "host": "192.0.2.2" }
Jun 21 16:47:50 machine kernel: [ 970.699396] F2B: failure from 192.0.2.2
# -- _daemon_re with and without __pid_re --
# failJSON: { "time": "2005-06-21T16:47:52", "match": true , "host": "192.0.2.3" }
Jun 21 16:47:52 machine [test-demo] F2B: failure from 192.0.2.3
# failJSON: { "time": "2005-06-21T16:47:53", "match": true , "host": "192.0.2.3" }
Jun 21 16:47:53 machine [test-demo][13709] F2B: failure from 192.0.2.3
# failJSON: { "time": "2005-06-21T16:50:00", "match": true , "host": "192.0.2.3" }
Jun 21 16:50:00 machine test-demo(pam_unix) F2B: failure from 192.0.2.3
# failJSON: { "time": "2005-06-21T16:50:02", "match": true , "host": "192.0.2.3" }
Jun 21 16:50:02 machine test-demo(pam_unix)[13709] F2B: failure from 192.0.2.3
# -- all common definitions together (bsdverbose hostname kernel_prefix vserver tag daemon_id space) --
# failJSON: { "time": "2005-06-21T16:55:01", "match": true , "host": "192.0.2.3" }
Jun 21 16:55:01 <auth.info> machine kernel: [ 970.699396] @vserver_demo test-demo(pam_unix)[13709] [ID 255 test] F2B: failure from 192.0.2.3
# -- the same as above with additional spaces around --
# failJSON: { "time": "2005-06-21T16:55:02", "match": true , "host": "192.0.2.3" }
Jun 21 16:55:02 <auth.info> machine kernel: [ 970.699396] @vserver_demo test-demo(pam_unix)[13709] [ID 255 test] F2B: failure from 192.0.2.3
# -- the same as above with brackets as date ambit --
# failJSON: { "time": "2005-06-21T16:55:03", "match": true , "host": "192.0.2.3" }
[Jun 21 16:55:03] <auth.info> machine kernel: [ 970.699396] @vserver_demo test-demo(pam_unix)[13709] [ID 255 test] F2B: failure from 192.0.2.3

View File

@ -1353,13 +1353,51 @@ class DNSUtilsNetworkTests(unittest.TestCase):
"""Call before every test case."""
unittest.F2B.SkipIfNoNetwork()
def test_IPAddr(self):
self.assertTrue(IPAddr('192.0.2.1').isIPv4)
self.assertTrue(IPAddr('2001:DB8::').isIPv6)
def test_IPAddr_Raw(self):
# raw string:
r = IPAddr('xxx', IPAddr.CIDR_RAW)
self.assertFalse(r.isIPv4)
self.assertFalse(r.isIPv6)
self.assertTrue(r.isValid)
self.assertEqual(r, 'xxx')
self.assertEqual('xxx', str(r))
self.assertNotEqual(r, IPAddr('xxx'))
# raw (not IP, for example host:port as string):
r = IPAddr('1:2', IPAddr.CIDR_RAW)
self.assertFalse(r.isIPv4)
self.assertFalse(r.isIPv6)
self.assertTrue(r.isValid)
self.assertEqual(r, '1:2')
self.assertEqual('1:2', str(r))
self.assertNotEqual(r, IPAddr('1:2'))
# raw vs ip4 (raw is not an ip):
r = IPAddr('93.184.0.1', IPAddr.CIDR_RAW)
ip4 = IPAddr('93.184.0.1')
self.assertNotEqual(ip4, r)
self.assertNotEqual(r, ip4)
self.assertTrue(r < ip4)
self.assertTrue(r < ip4)
# raw vs ip6 (raw is not an ip):
r = IPAddr('1::2', IPAddr.CIDR_RAW)
ip6 = IPAddr('1::2')
self.assertNotEqual(ip6, r)
self.assertNotEqual(r, ip6)
self.assertTrue(r < ip6)
self.assertTrue(r < ip6)
def testUseDns(self):
res = DNSUtils.textToIp('www.example.com', 'no')
self.assertEqual(res, [])
res = DNSUtils.textToIp('www.example.com', 'warn')
self.assertEqual(res, ['93.184.216.34', '2606:2800:220:1:248:1893:25c8:1946'])
# sort ipaddr, IPv4 is always smaller as IPv6
self.assertEqual(sorted(res), ['93.184.216.34', '2606:2800:220:1:248:1893:25c8:1946'])
res = DNSUtils.textToIp('www.example.com', 'yes')
self.assertEqual(res, ['93.184.216.34', '2606:2800:220:1:248:1893:25c8:1946'])
# sort ipaddr, IPv4 is always smaller as IPv6
self.assertEqual(sorted(res), ['93.184.216.34', '2606:2800:220:1:248:1893:25c8:1946'])
def testTextToIp(self):
# Test hostnames
@ -1371,17 +1409,29 @@ class DNSUtilsNetworkTests(unittest.TestCase):
for s in hostnames:
res = DNSUtils.textToIp(s, 'yes')
if s == 'www.example.com':
self.assertEqual(res, ['93.184.216.34', '2606:2800:220:1:248:1893:25c8:1946'])
# sort ipaddr, IPv4 is always smaller as IPv6
self.assertEqual(sorted(res), ['93.184.216.34', '2606:2800:220:1:248:1893:25c8:1946'])
else:
self.assertEqual(res, [])
# pure ips:
for s in ('93.184.216.34', '2606:2800:220:1:248:1893:25c8:1946'):
ips = DNSUtils.textToIp(s, 'yes')
self.assertEqual(ips, [s])
self.assertTrue(isinstance(ips[0], IPAddr))
def testIpToName(self):
unittest.F2B.SkipIfNoNetwork()
res = DNSUtils.ipToName('8.8.4.4')
self.assertEqual(res, 'google-public-dns-b.google.com')
# same as above, but with IPAddr:
res = DNSUtils.ipToName(IPAddr('8.8.4.4'))
self.assertEqual(res, 'google-public-dns-b.google.com')
# invalid ip (TEST-NET-1 according to RFC 5737)
res = DNSUtils.ipToName('192.0.2.0')
self.assertEqual(res, None)
# invalid ip:
res = DNSUtils.ipToName('192.0.2.888')
self.assertEqual(res, None)
def testAddr2bin(self):
res = IPAddr('10.0.0.0')
@ -1395,11 +1445,44 @@ class DNSUtilsNetworkTests(unittest.TestCase):
res = IPAddr('10.0.0.1', cidr=31L)
self.assertEqual(res.addr, 167772160L)
self.assertEqual(IPAddr('10.0.0.0').hexdump, '0a000000')
self.assertEqual(IPAddr('1::2').hexdump, '00010000000000000000000000000002')
self.assertEqual(IPAddr('xxx').hexdump, '')
self.assertEqual(IPAddr('192.0.2.0').getPTR(), '0.2.0.192.in-addr.arpa.')
self.assertEqual(IPAddr('192.0.2.1').getPTR(), '1.2.0.192.in-addr.arpa.')
self.assertEqual(IPAddr('2606:2800:220:1:248:1893:25c8:1946').getPTR(),
'6.4.9.1.8.c.5.2.3.9.8.1.8.4.2.0.1.0.0.0.0.2.2.0.0.0.8.2.6.0.6.2.ip6.arpa.')
def testIPAddr_Equal6(self):
self.assertEqual(
IPAddr('2606:2800:220:1:248:1893::'),
IPAddr('2606:2800:220:1:248:1893:0:0')
)
# special case IPv6 in brackets:
self.assertEqual(
IPAddr('[2606:2800:220:1:248:1893::]'),
IPAddr('2606:2800:220:1:248:1893:0:0')
)
def testIPAddr_InInet(self):
ip4net = IPAddr('93.184.0.1/24')
ip6net = IPAddr('2606:2800:220:1:248:1893:25c8:0/120')
# ip4:
self.assertTrue(IPAddr('93.184.0.1').isInNet(ip4net))
self.assertTrue(IPAddr('93.184.0.255').isInNet(ip4net))
self.assertFalse(IPAddr('93.184.1.0').isInNet(ip4net))
self.assertFalse(IPAddr('93.184.0.1').isInNet(ip6net))
# ip6:
self.assertTrue(IPAddr('2606:2800:220:1:248:1893:25c8:1').isInNet(ip6net))
self.assertTrue(IPAddr('2606:2800:220:1:248:1893:25c8:ff').isInNet(ip6net))
self.assertFalse(IPAddr('2606:2800:220:1:248:1893:25c8:100').isInNet(ip6net))
self.assertFalse(IPAddr('2606:2800:220:1:248:1893:25c8:100').isInNet(ip4net))
# raw not in net:
self.assertFalse(IPAddr('93.184.0.1', IPAddr.CIDR_RAW).isInNet(ip4net))
self.assertFalse(IPAddr('2606:2800:220:1:248:1893:25c8:1', IPAddr.CIDR_RAW).isInNet(ip6net))
# invalid not in net:
self.assertFalse(IPAddr('xxx').isInNet(ip4net))
def testIPAddr_Compare(self):
ip4 = [
@ -1452,11 +1535,28 @@ class DNSUtilsNetworkTests(unittest.TestCase):
self.assertEqual(IPAddr('93.184.0.1', 24).ntoa, '93.184.0.0/24')
self.assertEqual(IPAddr('192.168.1.0/255.255.255.128').ntoa, '192.168.1.0/25')
self.assertEqual(IPAddr('93.184.0.1/32').ntoa, '93.184.0.1')
self.assertEqual(IPAddr('93.184.0.1/255.255.255.255').ntoa, '93.184.0.1')
self.assertEqual(str(IPAddr('2606:2800:220:1:248:1893:25c8::', 120)), '2606:2800:220:1:248:1893:25c8:0/120')
self.assertEqual(IPAddr('2606:2800:220:1:248:1893:25c8::', 120).ntoa, '2606:2800:220:1:248:1893:25c8:0/120')
self.assertEqual(str(IPAddr('2606:2800:220:1:248:1893:25c8:0/120')), '2606:2800:220:1:248:1893:25c8:0/120')
self.assertEqual(IPAddr('2606:2800:220:1:248:1893:25c8:0/120').ntoa, '2606:2800:220:1:248:1893:25c8:0/120')
self.assertEqual(str(IPAddr('2606:28ff:220:1:248:1893:25c8::', 25)), '2606:2880::/25')
self.assertEqual(str(IPAddr('2606:28ff:220:1:248:1893:25c8::/ffff:ff80::')), '2606:2880::/25')
self.assertEqual(str(IPAddr('2606:28ff:220:1:248:1893:25c8::/ffff:ffff:ffff:ffff:ffff:ffff:ffff::')),
'2606:28ff:220:1:248:1893:25c8:0/112')
self.assertEqual(str(IPAddr('2606:28ff:220:1:248:1893:25c8::/128')),
'2606:28ff:220:1:248:1893:25c8:0')
self.assertEqual(str(IPAddr('2606:28ff:220:1:248:1893:25c8::/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff')),
'2606:28ff:220:1:248:1893:25c8:0')
def testIPAddr_CIDR_Wrong(self):
# too many plen representations:
self.assertRaises(ValueError, IPAddr, '2606:28ff:220:1:248:1893:25c8::/ffff::/::1')
def testIPAddr_CIDR_Repr(self):
self.assertEqual(["127.0.0.0/8", "::/32", "2001:db8::/32"],
[IPAddr("127.0.0.0", 8), IPAddr("::1", 32), IPAddr("2001:db8::", 32)]

View File

@ -33,7 +33,7 @@ from glob import glob
from StringIO import StringIO
from ..helpers import formatExceptionInfo, mbasename, TraceBack, FormatterWithTraceBack, getLogger
from ..helpers import splitcommaspace
from ..helpers import splitwords
from ..server.datetemplate import DatePatternRegex
from ..server.mytime import MyTime
@ -57,13 +57,15 @@ class HelpersTest(unittest.TestCase):
# might be fragile due to ' vs "
self.assertEqual(args, "('Very bad', None)")
def testsplitcommaspace(self):
self.assertEqual(splitcommaspace(None), [])
self.assertEqual(splitcommaspace(''), [])
self.assertEqual(splitcommaspace(' '), [])
self.assertEqual(splitcommaspace('1'), ['1'])
self.assertEqual(splitcommaspace(' 1 2 '), ['1', '2'])
self.assertEqual(splitcommaspace(' 1, 2 , '), ['1', '2'])
def testsplitwords(self):
self.assertEqual(splitwords(None), [])
self.assertEqual(splitwords(''), [])
self.assertEqual(splitwords(' '), [])
self.assertEqual(splitwords('1'), ['1'])
self.assertEqual(splitwords(' 1 2 '), ['1', '2'])
self.assertEqual(splitwords(' 1, 2 , '), ['1', '2'])
self.assertEqual(splitwords(' 1\n 2'), ['1', '2'])
self.assertEqual(splitwords(' 1\n 2, 3'), ['1', '2', '3'])
def _getSysPythonVersion():

View File

@ -35,6 +35,7 @@ from ..server.filter import Filter
from ..client.filterreader import FilterReader
from .utils import setUpMyTime, tearDownMyTime, CONFIG_DIR
TEST_CONFIG_DIR = os.path.join(os.path.dirname(__file__), "config")
TEST_FILES_DIR = os.path.join(os.path.dirname(__file__), "files")
@ -60,12 +61,12 @@ class FilterSamplesRegex(unittest.TestCase):
"Expected more FilterSampleRegexs tests")
def testSampleRegexsFactory(name):
def testSampleRegexsFactory(name, basedir):
def testFilter(self):
# Check filter exists
filterConf = FilterReader(name, "jail", {},
basedir=CONFIG_DIR, share_config=unittest.F2B.share_config)
basedir=basedir, share_config=unittest.F2B.share_config)
self.assertEqual(filterConf.getFile(), name)
self.assertEqual(filterConf.getJailName(), "jail")
filterConf.read()
@ -126,7 +127,7 @@ def testSampleRegexsFactory(name):
(map(lambda x: x[0], ret),logFile.filename(), logFile.filelineno()))
# Verify timestamp and host as expected
failregex, host, fail2banTime, lines = ret[0]
failregex, host, fail2banTime, lines, fail = ret[0]
self.assertEqual(host, faildata.get("host", None))
t = faildata.get("time", None)
@ -155,11 +156,15 @@ def testSampleRegexsFactory(name):
return testFilter
for filter_ in filter(lambda x: not x.endswith('common.conf') and x.endswith('.conf'),
os.listdir(os.path.join(CONFIG_DIR, "filter.d"))):
filterName = filter_.rpartition(".")[0]
if not filterName.startswith('.'):
setattr(
FilterSamplesRegex,
"testSampleRegexs%s" % filterName.upper(),
testSampleRegexsFactory(filterName))
for basedir_, filter_ in (
(CONFIG_DIR, lambda x: not x.endswith('common.conf') and x.endswith('.conf')),
(TEST_CONFIG_DIR, lambda x: x.startswith('zzz-') and x.endswith('.conf')),
):
for filter_ in filter(filter_,
os.listdir(os.path.join(basedir_, "filter.d"))):
filterName = filter_.rpartition(".")[0]
if not filterName.startswith('.'):
setattr(
FilterSamplesRegex,
"testSampleRegexs%s" % filterName.upper(),
testSampleRegexsFactory(filterName, basedir_))

View File

@ -631,11 +631,11 @@ class Transmitter(TransmitterBase):
self.assertEqual(
self.transm.proceed(
["set", self.jailName, "action", action, "timeout", "10"]),
(0, 10))
(0, "10"))
self.assertEqual(
self.transm.proceed(
["get", self.jailName, "action", action, "timeout"]),
(0, 10))
(0, "10"))
self.assertEqual(
self.transm.proceed(["set", self.jailName, "delaction", action]),
(0, None))
@ -837,7 +837,7 @@ class TransmitterLogging(TransmitterBase):
outCode=1,
outValue=Exception('Failed to change log target'),
repr_=True # Exceptions are not comparable apparently
)
)
}[platform.system() in ('Linux',) and os.path.exists('/dev/log')]
)
@ -926,6 +926,14 @@ class RegexTests(unittest.TestCase):
def testHost(self):
self.assertRaises(RegexException, FailRegex, '')
self.assertRaises(RegexException, FailRegex, '^test no group$')
self.assertTrue(FailRegex('^test <HOST> group$'))
self.assertTrue(FailRegex('^test <IP4> group$'))
self.assertTrue(FailRegex('^test <IP6> group$'))
self.assertTrue(FailRegex('^test <DNS> group$'))
self.assertTrue(FailRegex('^test id group: ip:port = <F-ID><IP4>(?::<F-PORT/>)?</F-ID>$'))
self.assertTrue(FailRegex('^test id group: user:\(<F-ID>[^\)]+</F-ID>\)$'))
self.assertTrue(FailRegex('^test id group: anything = <F-ID/>$'))
# Testing obscure case when host group might be missing in the matched pattern,
# e.g. if we made it optional.
fr = FailRegex('%%<HOST>?')
@ -933,6 +941,30 @@ class RegexTests(unittest.TestCase):
fr.search([('%%',"","")])
self.assertTrue(fr.hasMatched())
self.assertRaises(RegexException, fr.getHost)
# The same as above but using separated IPv4/IPv6 expressions
fr = FailRegex('%%inet(?:=<F-IP4/>|inet6=<F-IP6/>)?')
self.assertFalse(fr.hasMatched())
fr.search([('%%inet=test',"","")])
self.assertTrue(fr.hasMatched())
self.assertRaises(RegexException, fr.getHost)
# Success case: using separated IPv4/IPv6 expressions (no HOST)
fr = FailRegex('%%(?:inet(?:=<IP4>|6=<IP6>)?|dns=<DNS>?)')
self.assertFalse(fr.hasMatched())
fr.search([('%%inet=192.0.2.1',"","")])
self.assertTrue(fr.hasMatched())
self.assertEqual(fr.getHost(), '192.0.2.1')
fr.search([('%%inet6=2001:DB8::',"","")])
self.assertTrue(fr.hasMatched())
self.assertEqual(fr.getHost(), '2001:DB8::')
fr.search([('%%dns=example.com',"","")])
self.assertTrue(fr.hasMatched())
self.assertEqual(fr.getHost(), 'example.com')
# Success case: using user as failure-id
fr = FailRegex('^test id group: user:\(<F-ID>[^\)]+</F-ID>\)$')
self.assertFalse(fr.hasMatched())
fr.search([('test id group: user:(test login name)',"","")])
self.assertTrue(fr.hasMatched())
self.assertEqual(fr.getFailID(), 'test login name')
class _BadThread(JailThread):
@ -1005,14 +1037,43 @@ class ServerConfigReaderTests(LogCaptureTestCase):
logSys.debug(l)
return True
def test_IPAddr(self):
self.assertTrue(IPAddr('192.0.2.1').isIPv4)
self.assertTrue(IPAddr('2001:DB8::').isIPv6)
def _testExecActions(self, server):
jails = server._Server__jails
for jail in jails:
# print(jail, jails[jail])
for a in jails[jail].actions:
action = jails[jail].actions[a]
logSys.debug('# ' + ('=' * 50))
logSys.debug('# == %-44s ==', jail + ' - ' + action._name)
logSys.debug('# ' + ('=' * 50))
# we can currently test only command actions:
if not isinstance(action, _actions.CommandAction): continue
# wrap default command processor, just log if (heavy)debug:
action.executeCmd = self._executeCmd
# test start :
logSys.debug('# === start ==='); self.pruneLog()
action.start()
# test ban ip4 :
logSys.debug('# === ban-ipv4 ==='); self.pruneLog()
action.ban({'ip': IPAddr('192.0.2.1')})
# test unban ip4 :
logSys.debug('# === unban ipv4 ==='); self.pruneLog()
action.unban({'ip': IPAddr('192.0.2.1')})
# test ban ip6 :
logSys.debug('# === ban ipv6 ==='); self.pruneLog()
action.ban({'ip': IPAddr('2001:DB8::')})
# test unban ip6 :
logSys.debug('# === unban ipv6 ==='); self.pruneLog()
action.unban({'ip': IPAddr('2001:DB8::')})
# test stop :
logSys.debug('# === stop ==='); self.pruneLog()
action.stop()
if STOCK:
def testCheckStockJailActions(self):
jails = JailsReader(basedir=CONFIG_DIR, force_enable=True, share_config=self.__share_cfg) # we are running tests from root project dir atm
# we are running tests from root project dir atm
jails = JailsReader(basedir=CONFIG_DIR, force_enable=True, share_config=self.__share_cfg)
self.assertTrue(jails.read()) # opens fine
self.assertTrue(jails.getOptions()) # reads fine
stream = jails.convert(allow_no_files=True)
@ -1030,10 +1091,17 @@ class ServerConfigReaderTests(LogCaptureTestCase):
# change to the fast init backend:
if cmd[0] == 'add':
cmd[2] = 'polling'
# change log path to test log of jail (to prevent "Permission denied" on /var/logs/ for test-user):
# change log path to test log of the jail
# (to prevent "Permission denied" on /var/logs/ for test-user):
elif len(cmd) > 3 and cmd[0] == 'set' and cmd[2] == 'addlogpath':
cmd[3] = os.path.join(TEST_FILES_DIR, 'logs', cmd[1])
# if fast add dummy regex to prevent too long compile of all regexp (we don't use it in this test at all):
fn = os.path.join(TEST_FILES_DIR, 'logs', cmd[1])
# fallback to testcase01 if jail has no its own test log-file
# (should not matter really):
if not os.path.exists(fn): # pragma: no cover
fn = os.path.join(TEST_FILES_DIR, 'testcase01.log')
cmd[3] = fn
# if fast add dummy regex to prevent too long compile of all regexp
# (we don't use it in this test at all):
elif unittest.F2B.fast and (
len(cmd) > 3 and cmd[0] in ('set', 'multi-set') and cmd[2] == 'addfailregex'
):
@ -1042,13 +1110,17 @@ class ServerConfigReaderTests(LogCaptureTestCase):
# command to server, use cmdHandler direct instead of `transm.proceed(cmd)`:
try:
cmdHandler(cmd)
except Exception, e: # pragma: no cover
except Exception, e: # pragma: no cover
self.fail("Command %r has failed. Received %r" % (cmd, e))
# jails = server._Server__jails
# for j in jails:
# print(j, jails[j])
# test default stock actions sepecified in all stock jails:
if not unittest.F2B.fast:
self._testExecActions(server)
def getDefaultJailStream(self, jail, act):
act = act.replace('%(__name__)s', jail)
actName, actOpt = JailReader.extractOptions(act)
@ -1064,6 +1136,25 @@ class ServerConfigReaderTests(LogCaptureTestCase):
stream.extend(action.convert())
return stream
def testCheckStockAllActions(self):
unittest.F2B.SkipIfFast()
import glob
server = TestServer()
transm = server._Server__transm
for actCfg in glob.glob(os.path.join(CONFIG_DIR, 'action.d', '*.conf')):
act = os.path.basename(actCfg).replace('.conf', '')
# transmit artifical jail with each action to the server:
stream = self.getDefaultJailStream('j-'+act, act)
for cmd in stream:
# command to server:
ret, res = transm.proceed(cmd)
self.assertEqual(ret, 0)
# test executing action commands:
self._testExecActions(server)
def testCheckStockCommandActions(self):
# test cases to check valid ipv4/ipv6 action definition, tuple with (('jail', 'action[params]', 'tests', ...)
# where tests is a dictionary contains:
@ -1073,7 +1164,7 @@ class ServerConfigReaderTests(LogCaptureTestCase):
# etc.
testJailsActions = (
# iptables-multiport --
('j-w-iptables-mp', 'iptables-multiport[name=%(__name__)s, bantime="600", port="http,https", protocol="tcp", chain="INPUT"]', {
('j-w-iptables-mp', 'iptables-multiport[name=%(__name__)s, bantime="10m", port="http,https", protocol="tcp", chain="INPUT"]', {
'ip4': ('`iptables ', 'icmp-port-unreachable'), 'ip6': ('`ip6tables ', 'icmp6-port-unreachable'),
'start': (
"`iptables -w -N f2b-j-w-iptables-mp`",
@ -1111,7 +1202,7 @@ class ServerConfigReaderTests(LogCaptureTestCase):
),
}),
# iptables-allports --
('j-w-iptables-ap', 'iptables-allports[name=%(__name__)s, bantime="600", protocol="tcp", chain="INPUT"]', {
('j-w-iptables-ap', 'iptables-allports[name=%(__name__)s, bantime="10m", protocol="tcp", chain="INPUT"]', {
'ip4': ('`iptables ', 'icmp-port-unreachable'), 'ip6': ('`ip6tables ', 'icmp6-port-unreachable'),
'start': (
"`iptables -w -N f2b-j-w-iptables-ap`",
@ -1149,7 +1240,7 @@ class ServerConfigReaderTests(LogCaptureTestCase):
),
}),
# iptables-ipset-proto6 --
('j-w-iptables-ipset', 'iptables-ipset-proto6[name=%(__name__)s, bantime="600", port="http", protocol="tcp", chain="INPUT"]', {
('j-w-iptables-ipset', 'iptables-ipset-proto6[name=%(__name__)s, bantime="10m", port="http", protocol="tcp", chain="INPUT"]', {
'ip4': (' f2b-j-w-iptables-ipset ',), 'ip6': (' f2b-j-w-iptables-ipset6 ',),
'start': (
"`ipset create f2b-j-w-iptables-ipset hash:ip timeout 600`",
@ -1181,7 +1272,7 @@ class ServerConfigReaderTests(LogCaptureTestCase):
),
}),
# iptables-ipset-proto6-allports --
('j-w-iptables-ipset-ap', 'iptables-ipset-proto6-allports[name=%(__name__)s, bantime="600", chain="INPUT"]', {
('j-w-iptables-ipset-ap', 'iptables-ipset-proto6-allports[name=%(__name__)s, bantime="10m", chain="INPUT"]', {
'ip4': (' f2b-j-w-iptables-ipset-ap ',), 'ip6': (' f2b-j-w-iptables-ipset-ap6 ',),
'start': (
"`ipset create f2b-j-w-iptables-ipset-ap hash:ip timeout 600`",
@ -1213,7 +1304,7 @@ class ServerConfigReaderTests(LogCaptureTestCase):
),
}),
# iptables --
('j-w-iptables', 'iptables[name=%(__name__)s, bantime="600", port="http", protocol="tcp", chain="INPUT"]', {
('j-w-iptables', 'iptables[name=%(__name__)s, bantime="10m", port="http", protocol="tcp", chain="INPUT"]', {
'ip4': ('`iptables ', 'icmp-port-unreachable'), 'ip6': ('`ip6tables ', 'icmp6-port-unreachable'),
'start': (
"`iptables -w -N f2b-j-w-iptables`",
@ -1251,7 +1342,7 @@ class ServerConfigReaderTests(LogCaptureTestCase):
),
}),
# iptables-new --
('j-w-iptables-new', 'iptables-new[name=%(__name__)s, bantime="600", port="http", protocol="tcp", chain="INPUT"]', {
('j-w-iptables-new', 'iptables-new[name=%(__name__)s, bantime="10m", port="http", protocol="tcp", chain="INPUT"]', {
'ip4': ('`iptables ', 'icmp-port-unreachable'), 'ip6': ('`ip6tables ', 'icmp6-port-unreachable'),
'start': (
"`iptables -w -N f2b-j-w-iptables-new`",
@ -1289,7 +1380,7 @@ class ServerConfigReaderTests(LogCaptureTestCase):
),
}),
# iptables-xt_recent-echo --
('j-w-iptables-xtre', 'iptables-xt_recent-echo[name=%(__name__)s, bantime="600", chain="INPUT"]', {
('j-w-iptables-xtre', 'iptables-xt_recent-echo[name=%(__name__)s, bantime="10m", chain="INPUT"]', {
'ip4': ('`iptables ', '/f2b-j-w-iptables-xtre`'), 'ip6': ('`ip6tables ', '/f2b-j-w-iptables-xtre6`'),
'start': (
"`if [ `id -u` -eq 0 ];then iptables -w -I INPUT -m recent --update --seconds 3600 --name f2b-j-w-iptables-xtre -j REJECT --reject-with icmp-port-unreachable;fi`",
@ -1318,14 +1409,14 @@ class ServerConfigReaderTests(LogCaptureTestCase):
),
'ip6-unban': (
r"`echo -2001:db8:: > /proc/net/xt_recent/f2b-j-w-iptables-xtre6`",
),
),
}),
# pf --
# pf default -- multiport on default port (tag <port> set in jail.conf, but not in this test case)
('j-w-pf', 'pf[name=%(__name__)s]', {
'ip4': (), 'ip6': (),
'start': (
'`echo "table <f2b-j-w-pf> persist counters" | pfctl -f-`',
'`echo "block proto tcp from <f2b-j-w-pf> to any port any" | pfctl -f-`',
'`echo "block proto tcp from <f2b-j-w-pf> to any port <port>" | pfctl -f-`',
),
'stop': (
'`pfctl -sr 2>/dev/null | grep -v f2b-j-w-pf | pfctl -f-`',
@ -1339,6 +1430,152 @@ class ServerConfigReaderTests(LogCaptureTestCase):
'ip6-ban': ("`pfctl -t f2b-j-w-pf -T add 2001:db8::`",),
'ip6-unban': ("`pfctl -t f2b-j-w-pf -T delete 2001:db8::`",),
}),
# pf multiport with custom port --
('j-w-pf-mp', 'pf[actiontype=<multiport>][name=%(__name__)s, port=http]', {
'ip4': (), 'ip6': (),
'start': (
'`echo "table <f2b-j-w-pf-mp> persist counters" | pfctl -f-`',
'`echo "block proto tcp from <f2b-j-w-pf-mp> to any port http" | pfctl -f-`',
),
'stop': (
'`pfctl -sr 2>/dev/null | grep -v f2b-j-w-pf-mp | pfctl -f-`',
'`pfctl -t f2b-j-w-pf-mp -T flush`',
'`pfctl -t f2b-j-w-pf-mp -T kill`',
),
'ip4-check': ("`pfctl -sr | grep -q f2b-j-w-pf-mp`",),
'ip6-check': ("`pfctl -sr | grep -q f2b-j-w-pf-mp`",),
'ip4-ban': ("`pfctl -t f2b-j-w-pf-mp -T add 192.0.2.1`",),
'ip4-unban': ("`pfctl -t f2b-j-w-pf-mp -T delete 192.0.2.1`",),
'ip6-ban': ("`pfctl -t f2b-j-w-pf-mp -T add 2001:db8::`",),
'ip6-unban': ("`pfctl -t f2b-j-w-pf-mp -T delete 2001:db8::`",),
}),
# pf allports --
('j-w-pf-ap', 'pf[actiontype=<allports>][name=%(__name__)s]', {
'ip4': (), 'ip6': (),
'start': (
'`echo "table <f2b-j-w-pf-ap> persist counters" | pfctl -f-`',
'`echo "block proto tcp from <f2b-j-w-pf-ap> to any" | pfctl -f-`',
),
'stop': (
'`pfctl -sr 2>/dev/null | grep -v f2b-j-w-pf-ap | pfctl -f-`',
'`pfctl -t f2b-j-w-pf-ap -T flush`',
'`pfctl -t f2b-j-w-pf-ap -T kill`',
),
'ip4-check': ("`pfctl -sr | grep -q f2b-j-w-pf-ap`",),
'ip6-check': ("`pfctl -sr | grep -q f2b-j-w-pf-ap`",),
'ip4-ban': ("`pfctl -t f2b-j-w-pf-ap -T add 192.0.2.1`",),
'ip4-unban': ("`pfctl -t f2b-j-w-pf-ap -T delete 192.0.2.1`",),
'ip6-ban': ("`pfctl -t f2b-j-w-pf-ap -T add 2001:db8::`",),
'ip6-unban': ("`pfctl -t f2b-j-w-pf-ap -T delete 2001:db8::`",),
}),
# firewallcmd-multiport --
('j-w-fwcmd-mp', 'firewallcmd-multiport[name=%(__name__)s, bantime="10m", port="http,https", protocol="tcp", chain="INPUT"]', {
'ip4': (' ipv4 ', 'icmp-port-unreachable'), 'ip6': (' ipv6 ', 'icmp6-port-unreachable'),
'start': (
"`firewall-cmd --direct --add-chain ipv4 filter f2b-j-w-fwcmd-mp`",
"`firewall-cmd --direct --add-rule ipv4 filter f2b-j-w-fwcmd-mp 1000 -j RETURN`",
"`firewall-cmd --direct --add-rule ipv4 filter INPUT 0 -m conntrack --ctstate NEW -p tcp -m multiport --dports http,https -j f2b-j-w-fwcmd-mp`",
"`firewall-cmd --direct --add-chain ipv6 filter f2b-j-w-fwcmd-mp`",
"`firewall-cmd --direct --add-rule ipv6 filter f2b-j-w-fwcmd-mp 1000 -j RETURN`",
"`firewall-cmd --direct --add-rule ipv6 filter INPUT 0 -m conntrack --ctstate NEW -p tcp -m multiport --dports http,https -j f2b-j-w-fwcmd-mp`",
),
'stop': (
"`firewall-cmd --direct --remove-rule ipv4 filter INPUT 0 -m conntrack --ctstate NEW -p tcp -m multiport --dports http,https -j f2b-j-w-fwcmd-mp`",
"`firewall-cmd --direct --remove-rules ipv4 filter f2b-j-w-fwcmd-mp`",
"`firewall-cmd --direct --remove-chain ipv4 filter f2b-j-w-fwcmd-mp`",
"`firewall-cmd --direct --remove-rule ipv6 filter INPUT 0 -m conntrack --ctstate NEW -p tcp -m multiport --dports http,https -j f2b-j-w-fwcmd-mp`",
"`firewall-cmd --direct --remove-rules ipv6 filter f2b-j-w-fwcmd-mp`",
"`firewall-cmd --direct --remove-chain ipv6 filter f2b-j-w-fwcmd-mp`",
),
'ip4-check': (
r"`firewall-cmd --direct --get-chains ipv4 filter | sed -e 's, ,\n,g' | grep -q '^f2b-j-w-fwcmd-mp$'`",
),
'ip6-check': (
r"`firewall-cmd --direct --get-chains ipv6 filter | sed -e 's, ,\n,g' | grep -q '^f2b-j-w-fwcmd-mp$'`",
),
'ip4-ban': (
r"`firewall-cmd --direct --add-rule ipv4 filter f2b-j-w-fwcmd-mp 0 -s 192.0.2.1 -j REJECT --reject-with icmp-port-unreachable`",
),
'ip4-unban': (
r"`firewall-cmd --direct --remove-rule ipv4 filter f2b-j-w-fwcmd-mp 0 -s 192.0.2.1 -j REJECT --reject-with icmp-port-unreachable`",
),
'ip6-ban': (
r"`firewall-cmd --direct --add-rule ipv6 filter f2b-j-w-fwcmd-mp 0 -s 2001:db8:: -j REJECT --reject-with icmp6-port-unreachable`",
),
'ip6-unban': (
r"`firewall-cmd --direct --remove-rule ipv6 filter f2b-j-w-fwcmd-mp 0 -s 2001:db8:: -j REJECT --reject-with icmp6-port-unreachable`",
),
}),
# firewallcmd-allports --
('j-w-fwcmd-ap', 'firewallcmd-allports[name=%(__name__)s, bantime="10m", protocol="tcp", chain="INPUT"]', {
'ip4': (' ipv4 ', 'icmp-port-unreachable'), 'ip6': (' ipv6 ', 'icmp6-port-unreachable'),
'start': (
"`firewall-cmd --direct --add-chain ipv4 filter f2b-j-w-fwcmd-ap`",
"`firewall-cmd --direct --add-rule ipv4 filter f2b-j-w-fwcmd-ap 1000 -j RETURN`",
"`firewall-cmd --direct --add-rule ipv4 filter INPUT 0 -j f2b-j-w-fwcmd-ap`",
"`firewall-cmd --direct --add-chain ipv6 filter f2b-j-w-fwcmd-ap`",
"`firewall-cmd --direct --add-rule ipv6 filter f2b-j-w-fwcmd-ap 1000 -j RETURN`",
"`firewall-cmd --direct --add-rule ipv6 filter INPUT 0 -j f2b-j-w-fwcmd-ap`",
),
'stop': (
"`firewall-cmd --direct --remove-rule ipv4 filter INPUT 0 -j f2b-j-w-fwcmd-ap`",
"`firewall-cmd --direct --remove-rules ipv4 filter f2b-j-w-fwcmd-ap`",
"`firewall-cmd --direct --remove-chain ipv4 filter f2b-j-w-fwcmd-ap`",
"`firewall-cmd --direct --remove-rule ipv6 filter INPUT 0 -j f2b-j-w-fwcmd-ap`",
"`firewall-cmd --direct --remove-rules ipv6 filter f2b-j-w-fwcmd-ap`",
"`firewall-cmd --direct --remove-chain ipv6 filter f2b-j-w-fwcmd-ap`",
),
'ip4-check': (
r"`firewall-cmd --direct --get-chains ipv4 filter | sed -e 's, ,\n,g' | grep -q '^f2b-j-w-fwcmd-ap$'`",
),
'ip6-check': (
r"`firewall-cmd --direct --get-chains ipv6 filter | sed -e 's, ,\n,g' | grep -q '^f2b-j-w-fwcmd-ap$'`",
),
'ip4-ban': (
r"`firewall-cmd --direct --add-rule ipv4 filter f2b-j-w-fwcmd-ap 0 -s 192.0.2.1 -j REJECT --reject-with icmp-port-unreachable`",
),
'ip4-unban': (
r"`firewall-cmd --direct --remove-rule ipv4 filter f2b-j-w-fwcmd-ap 0 -s 192.0.2.1 -j REJECT --reject-with icmp-port-unreachable`",
),
'ip6-ban': (
r"`firewall-cmd --direct --add-rule ipv6 filter f2b-j-w-fwcmd-ap 0 -s 2001:db8:: -j REJECT --reject-with icmp6-port-unreachable`",
),
'ip6-unban': (
r"`firewall-cmd --direct --remove-rule ipv6 filter f2b-j-w-fwcmd-ap 0 -s 2001:db8:: -j REJECT --reject-with icmp6-port-unreachable`",
),
}),
# firewallcmd-ipset --
('j-w-fwcmd-ipset', 'firewallcmd-ipset[name=%(__name__)s, bantime="10m", port="http", protocol="tcp", chain="INPUT"]', {
'ip4': (' f2b-j-w-fwcmd-ipset ',), 'ip6': (' f2b-j-w-fwcmd-ipset6 ',),
'start': (
"`ipset create f2b-j-w-fwcmd-ipset hash:ip timeout 600`",
"`firewall-cmd --direct --add-rule ipv4 filter INPUT 0 -p tcp -m multiport --dports http -m set --match-set f2b-j-w-fwcmd-ipset src -j REJECT --reject-with icmp-port-unreachable`",
"`ipset create f2b-j-w-fwcmd-ipset6 hash:ip timeout 600`",
"`firewall-cmd --direct --add-rule ipv6 filter INPUT 0 -p tcp -m multiport --dports http -m set --match-set f2b-j-w-fwcmd-ipset6 src -j REJECT --reject-with icmp6-port-unreachable`",
),
'stop': (
"`firewall-cmd --direct --remove-rule ipv4 filter INPUT 0 -p tcp -m multiport --dports http -m set --match-set f2b-j-w-fwcmd-ipset src -j REJECT --reject-with icmp-port-unreachable`",
"`ipset flush f2b-j-w-fwcmd-ipset`",
"`ipset destroy f2b-j-w-fwcmd-ipset`",
"`firewall-cmd --direct --remove-rule ipv6 filter INPUT 0 -p tcp -m multiport --dports http -m set --match-set f2b-j-w-fwcmd-ipset6 src -j REJECT --reject-with icmp6-port-unreachable`",
"`ipset flush f2b-j-w-fwcmd-ipset6`",
"`ipset destroy f2b-j-w-fwcmd-ipset6`",
),
'ip4-check': (),
'ip6-check': (),
'ip4-ban': (
r"`ipset add f2b-j-w-fwcmd-ipset 192.0.2.1 timeout 600 -exist`",
),
'ip4-unban': (
r"`ipset del f2b-j-w-fwcmd-ipset 192.0.2.1 -exist`",
),
'ip6-ban': (
r"`ipset add f2b-j-w-fwcmd-ipset6 2001:db8:: timeout 600 -exist`",
),
'ip6-unban': (
r"`ipset del f2b-j-w-fwcmd-ipset6 2001:db8:: -exist`",
),
}),
)
server = TestServer()
transm = server._Server__transm
@ -1350,7 +1587,7 @@ class ServerConfigReaderTests(LogCaptureTestCase):
# for cmd in stream:
# print(cmd)
# filter all start commands (we want not start all jails):
# transmit jail to the server:
for cmd in stream:
# command to server:
ret, res = transm.proceed(cmd)

View File

@ -24,4 +24,4 @@ __author__ = "Cyril Jaquier, Yaroslav Halchenko, Steven Hiscocks, Daniel Black"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2005-2016 Yaroslav Halchenko, 2013-2014 Steven Hiscocks, Daniel Black"
__license__ = "GPL-v2+"
version = "0.9.4.dev0"
version = "0.10.0a1"

View File

@ -34,15 +34,15 @@ start() {
# remove stalled sock file after system crash
# bug 347477
rm -f /var/run/fail2ban/fail2ban.sock || return 1
start-stop-daemon --start --exec ${FAIL2BAN} start \
--pidfile /var/run/fail2ban/fail2ban.pid
start-stop-daemon --start --pidfile /var/run/fail2ban/fail2ban.pid \
-- ${FAIL2BAN} start
eend $? "Failed to start fail2ban"
}
stop() {
ebegin "Stopping fail2ban"
start-stop-daemon --stop --exec ${FAIL2BAN} stop \
--pidfile /var/run/fail2ban/fail2ban.pid
start-stop-daemon --stop --pidfile /var/run/fail2ban/fail2ban.pid \
-- ${FAIL2BAN} stop
eend $? "Failed to stop fail2ban"
}

View File

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*-
# vi: set ft=python sts=4 ts=4 sw=4 noet :