summaryrefslogtreecommitdiff
path: root/config
diff options
context:
space:
mode:
authorSergey G. Brester <serg.brester@sebres.de>2023-03-23 12:01:50 +0100
committerGitHub <noreply@github.com>2023-03-23 12:01:50 +0100
commit2c0360d1788d6569c6274ac690799d2a92c459df (patch)
treece0742af14e84f84ba8cf4c500d8ea9b75e175b2 /config
parent7e88ae0ee66628893a283d6fed06a347f9f6673e (diff)
parentd1d1730de01de45820db062d811d9b91f261ea83 (diff)
downloadfail2ban-2c0360d1788d6569c6274ac690799d2a92c459df.tar.gz
Merge branch 'master' into nginx-forbidden
Diffstat (limited to 'config')
-rw-r--r--config/action.d/abuseipdb.conf17
-rw-r--r--config/action.d/apprise.conf49
-rw-r--r--config/action.d/badips.conf19
-rw-r--r--config/action.d/badips.py389
-rw-r--r--config/action.d/bsd-ipfw.conf5
-rw-r--r--config/action.d/cloudflare-token.conf93
-rw-r--r--config/action.d/cloudflare.conf24
-rw-r--r--config/action.d/complain.conf2
-rw-r--r--config/action.d/dshield.conf2
-rw-r--r--config/action.d/firewallcmd-ipset.conf63
-rw-r--r--config/action.d/firewallcmd-rich-logging.conf30
-rw-r--r--config/action.d/firewallcmd-rich-rules.conf8
-rw-r--r--config/action.d/helpers-common.conf5
-rw-r--r--config/action.d/iptables-allports.conf46
-rw-r--r--config/action.d/iptables-common.conf92
-rw-r--r--config/action.d/iptables-ipset-proto4.conf9
-rw-r--r--config/action.d/iptables-ipset-proto6-allports.conf60
-rw-r--r--config/action.d/iptables-ipset-proto6.conf60
-rw-r--r--config/action.d/iptables-ipset.conf90
-rw-r--r--config/action.d/iptables-multiport-log.conf2
-rw-r--r--config/action.d/iptables-multiport.conf44
-rw-r--r--config/action.d/iptables-new.conf45
-rw-r--r--config/action.d/iptables-xt_recent-echo.conf20
-rw-r--r--config/action.d/iptables.conf130
-rw-r--r--config/action.d/ipthreat.conf107
-rw-r--r--config/action.d/mail-buffered.conf8
-rw-r--r--config/action.d/mail-whois-common.conf2
-rw-r--r--config/action.d/mail-whois-lines.conf2
-rw-r--r--config/action.d/mail-whois.conf6
-rw-r--r--config/action.d/mail.conf6
-rw-r--r--config/action.d/nftables-allports.conf11
-rw-r--r--config/action.d/nftables-common.conf135
-rw-r--r--config/action.d/nftables-multiport.conf11
-rw-r--r--config/action.d/nftables.conf203
-rw-r--r--config/action.d/nginx-block-map.conf17
-rw-r--r--config/action.d/sendmail-buffered.conf8
-rw-r--r--config/action.d/sendmail-common.conf8
-rw-r--r--config/action.d/sendmail-geoip-lines.conf4
-rw-r--r--config/action.d/sendmail-whois-ipjailmatches.conf5
-rw-r--r--config/action.d/sendmail-whois-ipmatches.conf5
-rw-r--r--config/action.d/sendmail-whois-lines.conf9
-rw-r--r--config/action.d/sendmail-whois-matches.conf5
-rw-r--r--config/action.d/sendmail-whois.conf5
-rw-r--r--config/action.d/sendmail.conf2
-rw-r--r--config/action.d/shorewall-ipset-proto6.conf20
-rw-r--r--config/action.d/shorewall.conf2
-rw-r--r--config/action.d/smtp.py12
-rw-r--r--config/action.d/symbiosis-blacklist-allports.conf7
-rw-r--r--config/action.d/ufw.conf47
-rw-r--r--config/action.d/xarf-login-attack.conf28
-rw-r--r--config/fail2ban.conf33
-rw-r--r--config/filter.d/apache-auth.conf10
-rw-r--r--config/filter.d/apache-common.conf4
-rw-r--r--config/filter.d/apache-fakegooglebot.conf4
-rw-r--r--config/filter.d/apache-modsecurity.conf2
-rw-r--r--config/filter.d/apache-noscript.conf6
-rw-r--r--config/filter.d/apache-overflows.conf2
-rw-r--r--config/filter.d/asterisk.conf15
-rw-r--r--config/filter.d/bitwarden.conf13
-rw-r--r--config/filter.d/centreon.conf9
-rw-r--r--config/filter.d/common.conf32
-rw-r--r--config/filter.d/courier-auth.conf2
-rw-r--r--config/filter.d/courier-smtp.conf2
-rw-r--r--config/filter.d/dante.conf16
-rw-r--r--config/filter.d/domino-smtp.conf9
-rw-r--r--config/filter.d/dovecot.conf15
-rw-r--r--config/filter.d/drupal-auth.conf2
-rw-r--r--config/filter.d/exim-common.conf2
-rw-r--r--config/filter.d/gitlab.conf6
-rw-r--r--config/filter.d/grafana.conf9
-rw-r--r--config/filter.d/guacamole.conf50
-rwxr-xr-xconfig/filter.d/ignorecommands/apache-fakegooglebot27
-rw-r--r--config/filter.d/lighttpd-auth.conf2
-rw-r--r--config/filter.d/monit.conf8
-rw-r--r--config/filter.d/monitorix.conf25
-rw-r--r--config/filter.d/mssql-auth.conf15
-rw-r--r--config/filter.d/mysqld-auth.conf4
-rw-r--r--config/filter.d/named-refused.conf15
-rw-r--r--config/filter.d/nginx-bad-request.conf16
-rw-r--r--config/filter.d/nginx-botsearch.conf4
-rw-r--r--config/filter.d/nginx-http-auth.conf19
-rw-r--r--config/filter.d/nginx-limit-req.conf3
-rw-r--r--config/filter.d/nsd.conf6
-rw-r--r--config/filter.d/phpmyadmin-syslog.conf2
-rw-r--r--config/filter.d/postfix.conf28
-rw-r--r--config/filter.d/proftpd.conf13
-rw-r--r--config/filter.d/scanlogd.conf17
-rw-r--r--config/filter.d/selinux-common.conf2
-rw-r--r--config/filter.d/selinux-ssh.conf4
-rw-r--r--config/filter.d/sendmail-auth.conf7
-rw-r--r--config/filter.d/sendmail-reject.conf18
-rw-r--r--config/filter.d/softethervpn.conf9
-rw-r--r--config/filter.d/sogo-auth.conf2
-rw-r--r--config/filter.d/sshd.conf65
-rw-r--r--config/filter.d/traefik-auth.conf76
-rw-r--r--config/filter.d/znc-adminlog.conf34
-rw-r--r--config/filter.d/zoneminder.conf16
-rw-r--r--config/jail.conf153
-rw-r--r--config/paths-common.conf3
-rw-r--r--config/paths-debian.conf2
100 files changed, 1605 insertions, 1212 deletions
diff --git a/config/action.d/abuseipdb.conf b/config/action.d/abuseipdb.conf
index c53ed489..ed958c86 100644
--- a/config/action.d/abuseipdb.conf
+++ b/config/action.d/abuseipdb.conf
@@ -21,14 +21,13 @@
#
# Example, for ssh bruteforce (in section [sshd] of `jail.local`):
# action = %(known/action)s
-# %(action_abuseipdb)s[abuseipdb_apikey="my-api-key", abuseipdb_category="18,22"]
+# abuseipdb[abuseipdb_apikey="my-api-key", abuseipdb_category="18,22"]
#
-# See below for catagories.
+# See below for categories.
#
-# Original Ref: https://wiki.shaunc.com/wikka.php?wakka=ReportingToAbuseIPDBWithFail2Ban
# Added to fail2ban by Andrew James Collett (ajcollett)
-## abuseIPDB Catagories, `the abuseipdb_category` MUST be set in the jail.conf action call.
+## abuseIPDB Categories, `the abuseipdb_category` MUST be set in the jail.conf action call.
# Example, for ssh bruteforce: action = %(action_abuseipdb)s[abuseipdb_category="18,22"]
# ID Title Description
# 3 Fraud Orders
@@ -47,6 +46,9 @@
[Definition]
+# bypass action for restored tickets
+norestored = 1
+
# Option: actionstart
# Notes.: command executed on demand at the first ban (or at the start of Fail2Ban if actionstart_on_demand is set to false).
# Values: CMD
@@ -80,13 +82,10 @@ actioncheck =
# wherever you install the helper script. For the PHP helper script, see
# <https://wiki.shaunc.com/wikka.php?wakka=ReportingToAbuseIPDBWithFail2Ban>
#
-# --ciphers ecdhe_ecdsa_aes_256_sha is used to workaround a
-# "NSS error -12286" from curl as it attempts to connect using
-# SSLv3. See https://www.centos.org/forums/viewtopic.php?t=52732
# Tags: See jail.conf(5) man page
# Values: CMD
#
-actionban = lgm=$(printf '%%s\n...' "<matches>"); curl --fail --tlsv1.1 --data "key=<abuseipdb_apikey>" --data-urlencode "comment=$lgm" --data "ip=<ip>" --data "category=<abuseipdb_category>" "https://www.abuseipdb.com/report/json"
+actionban = lgm=$(printf '%%.1000s\n...' "<matches>"); curl -sSf "https://api.abuseipdb.com/api/v2/report" -H "Accept: application/json" -H "Key: <abuseipdb_apikey>" --data-urlencode "comment=$lgm" --data-urlencode "ip=<ip>" --data "categories=<abuseipdb_category>"
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
@@ -101,5 +100,5 @@ actionunban =
# Notes Your API key from abuseipdb.com
# Values: STRING Default: None
# Register for abuseipdb [https://www.abuseipdb.com], get api key and set below.
-# You will need to set the catagory in the action call.
+# You will need to set the category in the action call.
abuseipdb_apikey =
diff --git a/config/action.d/apprise.conf b/config/action.d/apprise.conf
new file mode 100644
index 00000000..37c42ea2
--- /dev/null
+++ b/config/action.d/apprise.conf
@@ -0,0 +1,49 @@
+# Fail2Ban configuration file
+#
+# Author: Chris Caron <lead2gold@gmail.com>
+#
+#
+
+[Definition]
+
+# Option: actionstart
+# Notes.: command executed once at the start of Fail2Ban.
+# Values: CMD
+#
+actionstart = printf %%b "The jail <name> as been started successfully." | <apprise> -t "[Fail2Ban] <name>: started on `uname -n`"
+
+# Option: actionstop
+# Notes.: command executed once at the end of Fail2Ban
+# Values: CMD
+#
+actionstop = printf %%b "The jail <name> has been stopped." | <apprise> -t "[Fail2Ban] <name>: stopped on `uname -n`"
+
+# Option: actioncheck
+# Notes.: command executed once before each actionban command
+# Values: CMD
+#
+actioncheck =
+
+# Option: actionban
+# Notes.: command executed when banning an IP. Take care that the
+# command is executed with Fail2Ban user rights.
+# Tags: See jail.conf(5) man page
+# Values: CMD
+#
+actionban = printf %%b "The IP <ip> has just been banned by Fail2Ban after <failures> attempts against <name>" | <apprise> -n "warning" -t "[Fail2Ban] <name>: banned <ip> from `uname -n`"
+
+# Option: actionunban
+# Notes.: command executed when unbanning an IP. Take care that the
+# command is executed with Fail2Ban user rights.
+# Tags: See jail.conf(5) man page
+# Values: CMD
+#
+actionunban =
+
+[Init]
+
+# Define location of the default apprise configuration file to use
+#
+config = /etc/fail2ban/apprise.conf
+#
+apprise = apprise -c "<config>"
diff --git a/config/action.d/badips.conf b/config/action.d/badips.conf
deleted file mode 100644
index 6f9513f6..00000000
--- a/config/action.d/badips.conf
+++ /dev/null
@@ -1,19 +0,0 @@
-# Fail2ban reporting to badips.com
-#
-# Note: This reports an IP only and does not actually ban traffic. Use
-# another action in the same jail if you want bans to occur.
-#
-# Set the category to the appropriate value before use.
-#
-# To get see register and optional key to get personalised graphs see:
-# http://www.badips.com/blog/personalized-statistics-track-the-attackers-of-all-your-servers-with-one-key
-
-[Definition]
-
-actionban = curl --fail --user-agent "<agent>" http://www.badips.com/add/<category>/<ip>
-
-[Init]
-
-# Option: category
-# Notes.: Values are from the list here: http://www.badips.com/get/categories
-category =
diff --git a/config/action.d/badips.py b/config/action.d/badips.py
deleted file mode 100644
index 4e50890c..00000000
--- a/config/action.d/badips.py
+++ /dev/null
@@ -1,389 +0,0 @@
-# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*-
-# vi: set ft=python sts=4 ts=4 sw=4 noet :
-
-# This file is part of Fail2Ban.
-#
-# Fail2Ban is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# Fail2Ban is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Fail2Ban; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-import sys
-if sys.version_info < (2, 7): # pragma: no cover
- raise ImportError("badips.py action requires Python >= 2.7")
-import json
-import threading
-import logging
-if sys.version_info >= (3, ): # pragma: 2.x no cover
- from urllib.request import Request, urlopen
- from urllib.parse import urlencode
- from urllib.error import HTTPError
-else: # pragma: 3.x no cover
- from urllib2 import Request, urlopen, HTTPError
- from urllib import urlencode
-
-from fail2ban.server.actions import ActionBase
-
-
-class BadIPsAction(ActionBase): # pragma: no cover - may be unavailable
- """Fail2Ban action which reports bans to badips.com, and also
- blacklist bad IPs listed on badips.com by using another action's
- ban method.
-
- Parameters
- ----------
- jail : Jail
- The jail which the action belongs to.
- name : str
- Name assigned to the action.
- category : str
- Valid badips.com category for reporting failures.
- score : int, optional
- Minimum score for bad IPs. Default 3.
- age : str, optional
- Age of last report for bad IPs, per badips.com syntax.
- Default "24h" (24 hours)
- key : str, optional
- Key issued by badips.com to report bans, for later retrieval
- of personalised content.
- banaction : str, optional
- Name of banaction to use for blacklisting bad IPs. If `None`,
- no blacklist of IPs will take place.
- Default `None`.
- bancategory : str, optional
- Name of category to use for blacklisting, which can differ
- from category used for reporting. e.g. may want to report
- "postfix", but want to use whole "mail" category for blacklist.
- Default `category`.
- bankey : str, optional
- Key issued by badips.com to blacklist IPs reported with the
- associated key.
- updateperiod : int, optional
- Time in seconds between updating bad IPs blacklist.
- Default 900 (15 minutes)
- agent : str, optional
- User agent transmitted to server.
- Default `Fail2Ban/ver.`
-
- Raises
- ------
- ValueError
- If invalid `category`, `score`, `banaction` or `updateperiod`.
- """
-
- TIMEOUT = 10
- _badips = "https://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",
- timeout=TIMEOUT):
- super(BadIPsAction, self).__init__(jail, name)
-
- self.timeout = timeout
- self.agent = agent
- self.category = category
- self.score = score
- self.age = age
- self.key = key
- self.banaction = banaction
- self.bancategory = bancategory or category
- self.bankey = bankey
- self.updateperiod = updateperiod
-
- self._bannedips = set()
- # Used later for threading.Timer for updating badips
- self._timer = None
-
- @staticmethod
- def isAvailable(timeout=1):
- try:
- response = urlopen(Request("/".join([BadIPsAction._badips]),
- headers={'User-Agent': "Fail2Ban"}), timeout=timeout)
- return True, ''
- except Exception as e: # pragma: no cover
- return False, e
-
- def logError(self, response, what=''): # pragma: no cover - sporadical (502: Bad Gateway, etc)
- messages = {}
- try:
- messages = json.loads(response.read().decode('utf-8'))
- except:
- pass
- self._logSys.error(
- "%s. badips.com response: '%s'", what,
- messages.get('err', 'Unknown'))
-
- def getCategories(self, incParents=False):
- """Get badips.com categories.
-
- Returns
- -------
- set
- Set of categories.
-
- Raises
- ------
- HTTPError
- Any issues with badips.com request.
- ValueError
- If badips.com response didn't contain necessary information
- """
- try:
- response = urlopen(
- self._Request("/".join([self._badips, "get", "categories"])), timeout=self.timeout)
- except HTTPError as response: # pragma: no cover
- self.logError(response, "Failed to fetch categories")
- raise
- else:
- response_json = json.loads(response.read().decode('utf-8'))
- if not 'categories' in response_json:
- err = "badips.com response lacked categories specification. Response was: %s" \
- % (response_json,)
- self._logSys.error(err)
- raise ValueError(err)
- categories = response_json['categories']
- categories_names = set(
- value['Name'] for value in categories)
- if incParents:
- categories_names.update(set(
- value['Parent'] for value in categories
- if "Parent" in value))
- return categories_names
-
- def getList(self, category, score, age, key=None):
- """Get badips.com list of bad IPs.
-
- Parameters
- ----------
- category : str
- Valid badips.com category.
- score : int
- Minimum score for bad IPs.
- age : str
- Age of last report for bad IPs, per badips.com syntax.
- key : str, optional
- Key issued by badips.com to fetch IPs reported with the
- associated key.
-
- Returns
- -------
- set
- Set of bad IPs.
-
- Raises
- ------
- HTTPError
- Any issues with badips.com request.
- """
- try:
- url = "?".join([
- "/".join([self._badips, "get", "list", category, str(score)]),
- urlencode({'age': age})])
- if key:
- url = "&".join([url, urlencode({'key': key})])
- self._logSys.debug('badips.com: get list, url: %r', url)
- response = urlopen(self._Request(url), timeout=self.timeout)
- except HTTPError as response: # pragma: no cover
- self.logError(response, "Failed to fetch bad IP list")
- raise
- else:
- return set(response.read().decode('utf-8').split())
-
- @property
- def category(self):
- """badips.com category for reporting IPs.
- """
- return self._category
-
- @category.setter
- def category(self, category):
- if category not in self.getCategories():
- self._logSys.error("Category name '%s' not valid. "
- "see badips.com for list of valid categories",
- category)
- raise ValueError("Invalid category: %s" % category)
- self._category = category
-
- @property
- def bancategory(self):
- """badips.com bancategory for fetching IPs.
- """
- return self._bancategory
-
- @bancategory.setter
- def bancategory(self, bancategory):
- if bancategory != "any" and bancategory not in self.getCategories(incParents=True):
- self._logSys.error("Category name '%s' not valid. "
- "see badips.com for list of valid categories",
- bancategory)
- raise ValueError("Invalid bancategory: %s" % bancategory)
- self._bancategory = bancategory
-
- @property
- def score(self):
- """badips.com minimum score for fetching IPs.
- """
- return self._score
-
- @score.setter
- def score(self, score):
- score = int(score)
- if 0 <= score <= 5:
- self._score = score
- else:
- raise ValueError("Score must be 0-5")
-
- @property
- def banaction(self):
- """Jail action to use for banning/unbanning.
- """
- return self._banaction
-
- @banaction.setter
- def banaction(self, banaction):
- if banaction is not None and banaction not in self._jail.actions:
- self._logSys.error("Action name '%s' not in jail '%s'",
- banaction, self._jail.name)
- raise ValueError("Invalid banaction")
- self._banaction = banaction
-
- @property
- def updateperiod(self):
- """Period in seconds between banned bad IPs will be updated.
- """
- return self._updateperiod
-
- @updateperiod.setter
- def updateperiod(self, updateperiod):
- updateperiod = int(updateperiod)
- if updateperiod > 0:
- self._updateperiod = updateperiod
- else:
- raise ValueError("Update period must be integer greater than 0")
-
- def _banIPs(self, ips):
- for ip in ips:
- try:
- self._jail.actions[self.banaction].ban({
- 'ip': ip,
- 'failures': 0,
- 'matches': "",
- 'ipmatches': "",
- 'ipjailmatches': "",
- })
- except Exception as e:
- self._logSys.error(
- "Error banning IP %s for jail '%s' with action '%s': %s",
- ip, self._jail.name, self.banaction, e,
- exc_info=self._logSys.getEffectiveLevel()<=logging.DEBUG)
- else:
- self._bannedips.add(ip)
- self._logSys.debug(
- "Banned IP %s for jail '%s' with action '%s'",
- ip, self._jail.name, self.banaction)
-
- def _unbanIPs(self, ips):
- for ip in ips:
- try:
- self._jail.actions[self.banaction].unban({
- 'ip': ip,
- 'failures': 0,
- 'matches': "",
- 'ipmatches': "",
- 'ipjailmatches': "",
- })
- except Exception as e:
- self._logSys.info(
- "Error unbanning IP %s for jail '%s' with action '%s': %s",
- ip, self._jail.name, self.banaction, e,
- exc_info=self._logSys.getEffectiveLevel()<=logging.DEBUG)
- else:
- self._logSys.debug(
- "Unbanned IP %s for jail '%s' with action '%s'",
- ip, self._jail.name, self.banaction)
- finally:
- self._bannedips.remove(ip)
-
- def start(self):
- """If `banaction` set, blacklists bad IPs.
- """
- if self.banaction is not None:
- self.update()
-
- def update(self):
- """If `banaction` set, updates blacklisted IPs.
-
- Queries badips.com for list of bad IPs, removing IPs from the
- blacklist if no longer present, and adds new bad IPs to the
- blacklist.
- """
- if self.banaction is not None:
- if self._timer:
- self._timer.cancel()
- self._timer = None
-
- try:
- ips = self.getList(
- self.bancategory, self.score, self.age, self.bankey)
- # Remove old IPs no longer listed
- self._unbanIPs(self._bannedips - ips)
- # Add new IPs which are now listed
- self._banIPs(ips - self._bannedips)
-
- self._logSys.debug(
- "Updated IPs for jail '%s'. Update again in %i seconds",
- self._jail.name, self.updateperiod)
- finally:
- self._timer = threading.Timer(self.updateperiod, self.update)
- self._timer.start()
-
- def stop(self):
- """If `banaction` set, clears blacklisted IPs.
- """
- if self.banaction is not None:
- if self._timer:
- self._timer.cancel()
- self._timer = None
- self._unbanIPs(self._bannedips.copy())
-
- def ban(self, aInfo):
- """Reports banned IP to badips.com.
-
- Parameters
- ----------
- aInfo : dict
- Dictionary which includes information in relation to
- the ban.
-
- Raises
- ------
- HTTPError
- Any issues with badips.com request.
- """
- try:
- url = "/".join([self._badips, "add", self.category, str(aInfo['ip'])])
- if self.key:
- url = "?".join([url, urlencode({'key': self.key})])
- self._logSys.debug('badips.com: ban, url: %r', url)
- response = urlopen(self._Request(url), timeout=self.timeout)
- except HTTPError as response: # pragma: no cover
- self.logError(response, "Failed to ban")
- raise
- else:
- messages = json.loads(response.read().decode('utf-8'))
- self._logSys.debug(
- "Response from badips.com report: '%s'",
- messages['suc'])
-
-Action = BadIPsAction
diff --git a/config/action.d/bsd-ipfw.conf b/config/action.d/bsd-ipfw.conf
index 5116b0d8..444192d3 100644
--- a/config/action.d/bsd-ipfw.conf
+++ b/config/action.d/bsd-ipfw.conf
@@ -14,7 +14,10 @@
# Notes.: command executed on demand at the first ban (or at the start of Fail2Ban if actionstart_on_demand is set to false).
# Values: CMD
#
-actionstart = ipfw show | fgrep -c -m 1 -s 'table(<table>)' > /dev/null 2>&1 || ( ipfw show | awk 'BEGIN { b = <lowest_rule_num> } { if ($1 < b) {} else if ($1 == b) { b = $1 + 1 } else { e = b } } END { if (e) exit e <br> else exit b }'; num=$?; ipfw -q add $num <blocktype> <block> from table\(<table>\) to me <port>; echo $num > "<startstatefile>" )
+actionstart = ipfw show | fgrep -c -m 1 -s 'table(<table>)' > /dev/null 2>&1 || (
+ num=$(ipfw show | awk 'BEGIN { b = <lowest_rule_num> } { if ($1 == b) { b = $1 + 1 } } END { print b }');
+ ipfw -q add "$num" <blocktype> <block> from table\(<table>\) to me <port>; echo "$num" > "<startstatefile>"
+ )
# Option: actionstop
diff --git a/config/action.d/cloudflare-token.conf b/config/action.d/cloudflare-token.conf
new file mode 100644
index 00000000..287621eb
--- /dev/null
+++ b/config/action.d/cloudflare-token.conf
@@ -0,0 +1,93 @@
+#
+# Author: Logic-32
+#
+# IMPORTANT
+#
+# Please set jail.local's permission to 640 because it contains your CF API token.
+#
+# This action depends on curl.
+#
+# To get your Cloudflare API token: https://developers.cloudflare.com/api/tokens/create/
+#
+# Cloudflare Firewall API: https://developers.cloudflare.com/firewall/api/cf-firewall-rules/endpoints/
+
+[Definition]
+
+# Option: actionstart
+# Notes.: command executed on demand at the first ban (or at the start of Fail2Ban if actionstart_on_demand is set to false).
+# Values: CMD
+#
+actionstart =
+
+# Option: actionstop
+# Notes.: command executed at the stop of jail (or at the end of Fail2Ban)
+# Values: CMD
+#
+actionstop =
+
+# Option: actioncheck
+# Notes.: command executed once before each actionban command
+# Values: CMD
+#
+actioncheck =
+
+# Option: actionban
+# Notes.: command executed when banning an IP. Take care that the
+# command is executed with Fail2Ban user rights.
+# Tags: <ip> IP address
+# <failures> number of failures
+# <time> unix timestamp of the ban time
+# Values: CMD
+actionban = curl -s -X POST "<_cf_api_url>" \
+ <_cf_api_prms> \
+ --data '{"mode":"<cfmode>","configuration":{"target":"<cftarget>","value":"<ip>"},"notes":"<notes>"}'
+
+# Option: actionunban
+# Notes.: command executed when unbanning an IP. Take care that the
+# command is executed with Fail2Ban user rights.
+# Tags: <ip> IP address
+# <failures> number of failures
+# <time> unix timestamp of the ban time
+# Values: CMD
+#
+actionunban = id=$(curl -s -X GET "<_cf_api_url>" \
+ --data-urlencode "mode=<cfmode>" --data-urlencode "notes=<notes>" --data-urlencode "configuration.target=<cftarget>" --data-urlencode "configuration.value=<ip>" \
+ <_cf_api_prms> \
+ | awk -F"[,:}]" '{for(i=1;i<=NF;i++){if($i~/'id'\042/){print $(i+1)}}}' \
+ | tr -d ' "' \
+ | head -n 1)
+ if [ -z "$id" ]; then echo "<name>: id for <ip> cannot be found using target <cftarget>"; exit 0; fi; \
+ curl -s -X DELETE "<_cf_api_url>/$id" \
+ <_cf_api_prms> \
+ --data '{"cascade": "none"}'
+
+_cf_api_url = https://api.cloudflare.com/client/v4/zones/<cfzone>/firewall/access_rules/rules
+_cf_api_prms = -H "Authorization: Bearer <cftoken>" -H "Content-Type: application/json"
+
+[Init]
+
+# Declare your Cloudflare Authorization Bearer Token in the [DEFAULT] section of your jail.local file.
+
+# The Cloudflare <ZONE_ID> of hte domain you want to manage.
+#
+# cfzone =
+
+# Your personal Cloudflare token. Ideally restricted to just have "Zone.Firewall Services" permissions.
+#
+# cftoken =
+
+# Target of the firewall rule. Default is "ip" (v4).
+#
+cftarget = ip
+
+# The firewall mode Cloudflare should use. Default is "block" (deny access).
+# Consider also "js_challenge" or other "allowed_modes" if you want.
+#
+cfmode = block
+
+# The message to include in the firewall IP banning rule.
+#
+notes = Fail2Ban <name>
+
+[Init?family=inet6]
+cftarget = ip6
diff --git a/config/action.d/cloudflare.conf b/config/action.d/cloudflare.conf
index 1c48a37f..4af87080 100644
--- a/config/action.d/cloudflare.conf
+++ b/config/action.d/cloudflare.conf
@@ -5,7 +5,7 @@
#
# Please set jail.local's permission to 640 because it contains your CF API key.
#
-# This action depends on curl.
+# This action depends on curl (and optionally jq).
# Referenced from http://www.normyee.net/blog/2012/02/02/adding-cloudflare-support-to-fail2ban by NORM YEE
#
# To get your CloudFlare API Key: https://www.cloudflare.com/a/account/my-account
@@ -43,9 +43,9 @@ actioncheck =
# API v1
#actionban = curl -s -o /dev/null https://www.cloudflare.com/api_json.html -d 'a=ban' -d 'tkn=<cftoken>' -d 'email=<cfuser>' -d 'key=<ip>'
# API v4
-actionban = curl -s -o /dev/null -X POST -H 'X-Auth-Email: <cfuser>' -H 'X-Auth-Key: <cftoken>' \
- -H 'Content-Type: application/json' -d '{ "mode": "block", "configuration": { "target": "ip", "value": "<ip>" } }' \
- https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules
+actionban = curl -s -o /dev/null -X POST <_cf_api_prms> \
+ -d '{"mode":"block","configuration":{"target":"<cftarget>","value":"<ip>"},"notes":"Fail2Ban <name>"}' \
+ <_cf_api_url>
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
@@ -58,9 +58,14 @@ actionban = curl -s -o /dev/null -X POST -H 'X-Auth-Email: <cfuser>' -H 'X-Auth-
# API v1
#actionunban = curl -s -o /dev/null https://www.cloudflare.com/api_json.html -d 'a=nul' -d 'tkn=<cftoken>' -d 'email=<cfuser>' -d 'key=<ip>'
# API v4
-actionunban = curl -s -o /dev/null -X DELETE -H 'X-Auth-Email: <cfuser>' -H 'X-Auth-Key: <cftoken>' \
- https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules/$(curl -s -X GET -H 'X-Auth-Email: <cfuser>' -H 'X-Auth-Key: <cftoken>' \
- 'https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules?mode=block&configuration_target=ip&configuration_value=<ip>&page=1&per_page=1' | cut -d'"' -f6)
+actionunban = id=$(curl -s -X GET <_cf_api_prms> \
+ "<_cf_api_url>?mode=block&configuration_target=<cftarget>&configuration_value=<ip>&page=1&per_page=1&notes=Fail2Ban%%20<name>" \
+ | { jq -r '.result[0].id' 2>/dev/null || tr -d '\n' | sed -nE 's/^.*"result"\s*:\s*\[\s*\{\s*"id"\s*:\s*"([^"]+)".*$/\1/p'; })
+ if [ -z "$id" ]; then echo "<name>: id for <ip> cannot be found"; exit 0; fi;
+ curl -s -o /dev/null -X DELETE <_cf_api_prms> "<_cf_api_url>/$id"
+
+_cf_api_url = https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules
+_cf_api_prms = -H 'X-Auth-Email: <cfuser>' -H 'X-Auth-Key: <cftoken>' -H 'Content-Type: application/json'
[Init]
@@ -76,3 +81,8 @@ actionunban = curl -s -o /dev/null -X DELETE -H 'X-Auth-Email: <cfuser>' -H 'X-A
cftoken =
cfuser =
+
+cftarget = ip
+
+[Init?family=inet6]
+cftarget = ip6
diff --git a/config/action.d/complain.conf b/config/action.d/complain.conf
index 3a5f882c..4d73b058 100644
--- a/config/action.d/complain.conf
+++ b/config/action.d/complain.conf
@@ -102,7 +102,7 @@ logpath = /dev/null
# Notes.: Your system mail command. Is passed 2 args: subject and recipient
# Values: CMD
#
-mailcmd = mail -s
+mailcmd = mail -E 'set escape' -s
# Option: mailargs
# Notes.: Additional arguments to mail command. e.g. for standard Unix mail:
diff --git a/config/action.d/dshield.conf b/config/action.d/dshield.conf
index c128bef3..3d5a7a53 100644
--- a/config/action.d/dshield.conf
+++ b/config/action.d/dshield.conf
@@ -179,7 +179,7 @@ tcpflags =
# Notes.: Your system mail command. Is passed 2 args: subject and recipient
# Values: CMD
#
-mailcmd = mail -s
+mailcmd = mail -E 'set escape' -s
# Option: mailargs
# Notes.: Additional arguments to mail command. e.g. for standard Unix mail:
diff --git a/config/action.d/firewallcmd-ipset.conf b/config/action.d/firewallcmd-ipset.conf
index a1065224..c36ba694 100644
--- a/config/action.d/firewallcmd-ipset.conf
+++ b/config/action.d/firewallcmd-ipset.conf
@@ -18,20 +18,45 @@ before = firewallcmd-common.conf
[Definition]
-actionstart = ipset create <ipmset> hash:ip timeout <default-timeout><familyopt>
+actionstart = <ipstype_<ipsettype>/actionstart>
firewall-cmd --direct --add-rule <family> filter <chain> 0 <actiontype> -m set --match-set <ipmset> src -j <blocktype>
-actionflush = ipset flush <ipmset>
+actionflush = <ipstype_<ipsettype>/actionflush>
actionstop = firewall-cmd --direct --remove-rule <family> filter <chain> 0 <actiontype> -m set --match-set <ipmset> src -j <blocktype>
<actionflush>
- ipset destroy <ipmset>
+ <ipstype_<ipsettype>/actionstop>
+
+actionban = <ipstype_<ipsettype>/actionban>
+
+# actionprolong = %(actionban)s
+
+actionunban = <ipstype_<ipsettype>/actionunban>
+
+[ipstype_ipset]
+
+actionstart = ipset -exist create <ipmset> hash:ip timeout <default-ipsettime> <familyopt>
+
+actionflush = ipset flush <ipmset>
-actionban = ipset add <ipmset> <ip> timeout <bantime> -exist
+actionstop = ipset destroy <ipmset>
-actionprolong = %(actionban)s
+actionban = ipset -exist add <ipmset> <ip> timeout <ipsettime>
-actionunban = ipset del <ipmset> <ip> -exist
+actionunban = ipset -exist del <ipmset> <ip>
+
+[ipstype_firewalld]
+
+actionstart = firewall-cmd --direct --new-ipset=<ipmset> --type=hash:ip --option=timeout=<default-ipsettime> <firewalld_familyopt>
+
+# TODO: there doesn't seem to be an explicit way to invoke the ipset flush function using firewall-cmd
+actionflush =
+
+actionstop = firewall-cmd --direct --delete-ipset=<ipmset>
+
+actionban = firewall-cmd --ipset=<ipmset> --add-entry=<ip>
+
+actionunban = firewall-cmd --ipset=<ipmset> --remove-entry=<ip>
[Init]
@@ -42,11 +67,25 @@ actionunban = ipset del <ipmset> <ip> -exist
#
chain = INPUT_direct
-# Option: default-timeout
+# Option: default-ipsettime
# Notes: specifies default timeout in seconds (handled default ipset timeout only)
-# Values: [ NUM ] Default: 600
+# Values: [ NUM ] Default: 0 (no timeout, managed by fail2ban by unban)
+default-ipsettime = 0
+
+# Option: ipsettime
+# Notes: specifies ticket timeout (handled ipset timeout only)
+# Values: [ NUM ] Default: 0 (managed by fail2ban by unban)
+ipsettime = 0
+
+# expresion to caclulate timeout from bantime, example:
+# banaction = %(known/banaction)s[ipsettime='<timeout-bantime>']
+timeout-bantime = $([ "<bantime>" -le 2147483 ] && echo "<bantime>" || echo 0)
-default-timeout = 600
+# Option: ipsettype
+# Notes.: defines type of ipset used for match-set (firewalld or ipset)
+# Values: firewalld or ipset
+# Default: ipset
+ipsettype = ipset
# Option: actiontype
# Notes.: defines additions to the blocking rule
@@ -67,14 +106,16 @@ multiport = -p <protocol> -m multiport --dports <port>
ipmset = f2b-<name>
familyopt =
+firewalld_familyopt =
[Init?family=inet6]
ipmset = f2b-<name>6
-familyopt = <sp>family inet6
+familyopt = family inet6
+firewalld_familyopt = --option=family=inet6
# DEV NOTES:
#
-# Author: Edgar Hoch and Daniel Black
+# Author: Edgar Hoch, Daniel Black, Sergey Brester and Mihail Politaev
# firewallcmd-new / iptables-ipset-proto6 combined for maximium goodness
diff --git a/config/action.d/firewallcmd-rich-logging.conf b/config/action.d/firewallcmd-rich-logging.conf
index badfee83..21e45087 100644
--- a/config/action.d/firewallcmd-rich-logging.conf
+++ b/config/action.d/firewallcmd-rich-logging.conf
@@ -1,6 +1,6 @@
# Fail2Ban configuration file
#
-# Author: Donald Yandt
+# Authors: Donald Yandt, Sergey G. Brester
#
# Because of the rich rule commands requires firewalld-0.3.1+
# This action uses firewalld rich-rules which gives you a cleaner iptables since it stores rules according to zones and not
@@ -10,36 +10,15 @@
#
# If you use the --permanent rule you get a xml file in /etc/firewalld/zones/<zone>.xml that can be shared and parsed easliy
#
-# Example commands to view rules:
-# firewall-cmd [--zone=<zone>] --list-rich-rules
-# firewall-cmd [--zone=<zone>] --list-all
-# firewall-cmd [--zone=zone] --query-rich-rule='rule'
+# This is an derivative of firewallcmd-rich-rules.conf, see there for details and other parameters.
[INCLUDES]
-before = firewallcmd-common.conf
+before = firewallcmd-rich-rules.conf
[Definition]
-actionstart =
-
-actionstop =
-
-actioncheck =
-
-# you can also use zones and/or service names.
-#
-# zone example:
-# 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='<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 separated 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='<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='<family>' source address='<ip>' port port='$p' protocol='<protocol>' log prefix='f2b-<name>' level='<level>' limit value='<rate>/m' <rich-blocktype>"; done
+rich-suffix = log prefix='f2b-<name>' level='<level>' limit value='<rate>/m' <rich-blocktype>
[Init]
@@ -48,4 +27,3 @@ level = info
# log rate per minute
rate = 1
-
diff --git a/config/action.d/firewallcmd-rich-rules.conf b/config/action.d/firewallcmd-rich-rules.conf
index bed71797..75a27d88 100644
--- a/config/action.d/firewallcmd-rich-rules.conf
+++ b/config/action.d/firewallcmd-rich-rules.conf
@@ -35,8 +35,10 @@ actioncheck =
#
# 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 separated 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='<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='<family>' source address='<ip>' port port='$p' protocol='<protocol>' <rich-blocktype>"; done
+fwcmd_rich_rule = rule family='<family>' source address='<ip>' port port='$p' protocol='<protocol>' %(rich-suffix)s
+actionban = ports="<port>"; for p in $(echo $ports | tr ", " " "); do firewall-cmd --add-rich-rule="%(fwcmd_rich_rule)s"; done
+
+actionunban = ports="<port>"; for p in $(echo $ports | tr ", " " "); do firewall-cmd --remove-rich-rule="%(fwcmd_rich_rule)s"; done
+rich-suffix = <rich-blocktype> \ No newline at end of file
diff --git a/config/action.d/helpers-common.conf b/config/action.d/helpers-common.conf
index b036f68f..03422a87 100644
--- a/config/action.d/helpers-common.conf
+++ b/config/action.d/helpers-common.conf
@@ -4,8 +4,9 @@
# _grep_logs_args = 'test'
# (printf %%b "Log-excerpt contains 'test':\n"; %(_grep_logs)s; printf %%b "Log-excerpt contains 'test':\n") | mail ...
#
-_grep_logs = logpath="<logpath>"; grep <grepopts> -E %(_grep_logs_args)s $logpath | <greplimit>
-_grep_logs_args = "(^|[^0-9a-fA-F:])$(echo '<ip>' | sed 's/\./\\./g')([^0-9a-fA-F:]|$)"
+_grep_logs = logpath="<logpath>"; grep <grepopts> %(_grep_logs_args)s $logpath | <greplimit>
+# options `-wF` used to match only whole words and fixed string (not as pattern)
+_grep_logs_args = -wF "<ip>"
# Used for actions, that should not by executed if ticket was restored:
_bypass_if_restored = if [ '<restored>' = '1' ]; then exit 0; fi;
diff --git a/config/action.d/iptables-allports.conf b/config/action.d/iptables-allports.conf
index caf9ab81..51c4694d 100644
--- a/config/action.d/iptables-allports.conf
+++ b/config/action.d/iptables-allports.conf
@@ -4,52 +4,12 @@
# Modified: Yaroslav O. Halchenko <debian@onerussian.com>
# made active on all ports from original iptables.conf
#
-#
+# Obsolete: superseded by iptables[type=allports]
[INCLUDES]
-before = iptables-common.conf
-
+before = iptables.conf
[Definition]
-# Option: actionstart
-# Notes.: command executed on demand at the first ban (or at the start of Fail2Ban if actionstart_on_demand is set to false).
-# Values: CMD
-#
-actionstart = <iptables> -N f2b-<name>
- <iptables> -A f2b-<name> -j <returntype>
- <iptables> -I <chain> -p <protocol> -j f2b-<name>
-
-# Option: actionstop
-# Notes.: command executed at the stop of jail (or at the end of Fail2Ban)
-# Values: CMD
-#
-actionstop = <iptables> -D <chain> -p <protocol> -j f2b-<name>
- <actionflush>
- <iptables> -X f2b-<name>
-
-# Option: actioncheck
-# Notes.: command executed once before each actionban command
-# Values: CMD
-#
-actioncheck = <iptables> -n -L <chain> | grep -q 'f2b-<name>[ \t]'
-
-# Option: actionban
-# Notes.: command executed when banning an IP. Take care that the
-# command is executed with Fail2Ban user rights.
-# Tags: See jail.conf(5) man page
-# Values: CMD
-#
-actionban = <iptables> -I f2b-<name> 1 -s <ip> -j <blocktype>
-
-# Option: actionunban
-# Notes.: command executed when unbanning an IP. Take care that the
-# command is executed with Fail2Ban user rights.
-# Tags: See jail.conf(5) man page
-# Values: CMD
-#
-actionunban = <iptables> -D f2b-<name> -s <ip> -j <blocktype>
-
-[Init]
-
+type = allports
diff --git a/config/action.d/iptables-common.conf b/config/action.d/iptables-common.conf
deleted file mode 100644
index e016ef2f..00000000
--- a/config/action.d/iptables-common.conf
+++ /dev/null
@@ -1,92 +0,0 @@
-# Fail2Ban configuration file
-#
-# Author: Daniel Black
-#
-# This is a included configuration file and includes the definitions for the iptables
-# used in all iptables based actions by default.
-#
-# The user can override the defaults in iptables-common.local
-#
-# Modified: Alexander Koeppe <format_c@online.de>, Serg G. Brester <serg.brester@sebres.de>
-# made config file IPv6 capable (see new section Init?family=inet6)
-
-[INCLUDES]
-
-after = iptables-blocktype.local
- iptables-common.local
-# iptables-blocktype.local is obsolete
-
-[Definition]
-
-# Option: actionflush
-# Notes.: command executed once to flush IPS, by shutdown (resp. by stop of the jail or this action)
-# Values: CMD
-#
-actionflush = <iptables> -F f2b-<name>
-
-
-[Init]
-
-# Option: chain
-# Notes specifies the iptables chain to which the Fail2Ban rules should be
-# added
-# Values: STRING Default: INPUT
-chain = INPUT
-
-# Default name of the chain
-#
-name = default
-
-# Option: port
-# Notes.: specifies port to monitor
-# Values: [ NUM | STRING ] Default:
-#
-port = ssh
-
-# Option: protocol
-# Notes.: internally used by config reader for interpolations.
-# Values: [ tcp | udp | icmp | all ] Default: tcp
-#
-protocol = tcp
-
-# Option: blocktype
-# Note: This is what the action does with rules. This can be any jump target
-# as per the iptables man page (section 8). Common values are DROP
-# REJECT, REJECT --reject-with icmp-port-unreachable
-# Values: STRING
-blocktype = REJECT --reject-with icmp-port-unreachable
-
-# Option: returntype
-# Note: This is the default rule on "actionstart". This should be RETURN
-# in all (blocking) actions, except REJECT in allowing actions.
-# Values: STRING
-returntype = RETURN
-
-# Option: lockingopt
-# Notes.: Option was introduced to iptables to prevent multiple instances from
-# running concurrently and causing irratic behavior. -w was introduced
-# in iptables 1.4.20, so might be absent on older systems
-# See https://github.com/fail2ban/fail2ban/issues/1122
-# Values: STRING
-lockingopt = -w
-
-# Option: iptables
-# Notes.: Actual command to be executed, including common to all calls options
-# Values: STRING
-iptables = iptables <lockingopt>
-
-
-[Init?family=inet6]
-
-# Option: blocktype (ipv6)
-# Note: This is what the action does with rules. This can be any jump target
-# as per the iptables man page (section 8). Common values are DROP
-# REJECT, REJECT --reject-with icmp6-port-unreachable
-# Values: STRING
-blocktype = REJECT --reject-with icmp6-port-unreachable
-
-# Option: iptables (ipv6)
-# Notes.: Actual command to be executed, including common to all calls options
-# Values: STRING
-iptables = ip6tables <lockingopt>
-
diff --git a/config/action.d/iptables-ipset-proto4.conf b/config/action.d/iptables-ipset-proto4.conf
index 99ebbf8c..37624284 100644
--- a/config/action.d/iptables-ipset-proto4.conf
+++ b/config/action.d/iptables-ipset-proto4.conf
@@ -19,7 +19,7 @@
[INCLUDES]
-before = iptables-common.conf
+before = iptables.conf
[Definition]
@@ -28,7 +28,7 @@ before = iptables-common.conf
# Values: CMD
#
actionstart = ipset --create f2b-<name> iphash
- <iptables> -I <chain> -p <protocol> -m multiport --dports <port> -m set --match-set f2b-<name> src -j <blocktype>
+ <_ipt_add_rules>
# Option: actionflush
@@ -41,7 +41,7 @@ actionflush = ipset --flush f2b-<name>
# Notes.: command executed at the stop of jail (or at the end of Fail2Ban)
# Values: CMD
#
-actionstop = <iptables> -D <chain> -p <protocol> -m multiport --dports <port> -m set --match-set f2b-<name> src -j <blocktype>
+actionstop = <_ipt_del_rules>
<actionflush>
ipset --destroy f2b-<name>
@@ -61,5 +61,6 @@ actionban = ipset --test f2b-<name> <ip> || ipset --add f2b-<name> <ip>
#
actionunban = ipset --test f2b-<name> <ip> && ipset --del f2b-<name> <ip>
-[Init]
+# Several capabilities used internaly:
+rule-jump = -m set --match-set f2b-<name> src -j <blocktype>
diff --git a/config/action.d/iptables-ipset-proto6-allports.conf b/config/action.d/iptables-ipset-proto6-allports.conf
index c851233c..1aa7fd6f 100644
--- a/config/action.d/iptables-ipset-proto6-allports.conf
+++ b/config/action.d/iptables-ipset-proto6-allports.conf
@@ -15,65 +15,13 @@
#
# Modified: Alexander Koeppe <format_c@online.de>, Serg G. Brester <serg.brester@sebres.de>
# made config file IPv6 capable (see new section Init?family=inet6)
+#
+# Obsolete: superseded by iptables-ipset[type=allports]
[INCLUDES]
-before = iptables-common.conf
+before = iptables-ipset.conf
[Definition]
-# Option: actionstart
-# Notes.: command executed on demand at the first ban (or at the start of Fail2Ban if actionstart_on_demand is set to false).
-# Values: CMD
-#
-actionstart = ipset create <ipmset> hash:ip timeout <default-timeout><familyopt>
- <iptables> -I <chain> -m set --match-set <ipmset> src -j <blocktype>
-
-# Option: actionflush
-# Notes.: command executed once to flush IPS, by shutdown (resp. by stop of the jail or this action)
-# Values: CMD
-#
-actionflush = ipset flush <ipmset>
-
-# Option: actionstop
-# Notes.: command executed at the stop of jail (or at the end of Fail2Ban)
-# Values: CMD
-#
-actionstop = <iptables> -D <chain> -m set --match-set <ipmset> src -j <blocktype>
- <actionflush>
- ipset destroy <ipmset>
-
-# Option: actionban
-# Notes.: command executed when banning an IP. Take care that the
-# command is executed with Fail2Ban user rights.
-# Tags: See jail.conf(5) man page
-# Values: CMD
-#
-actionban = ipset add <ipmset> <ip> timeout <bantime> -exist
-
-actionprolong = %(actionban)s
-
-# Option: actionunban
-# Notes.: command executed when unbanning an IP. Take care that the
-# command is executed with Fail2Ban user rights.
-# Tags: See jail.conf(5) man page
-# Values: CMD
-#
-actionunban = ipset del <ipmset> <ip> -exist
-
-[Init]
-
-# Option: default-timeout
-# Notes: specifies default timeout in seconds (handled default ipset timeout only)
-# Values: [ NUM ] Default: 600
-
-default-timeout = 600
-
-ipmset = f2b-<name>
-familyopt =
-
-
-[Init?family=inet6]
-
-ipmset = f2b-<name>6
-familyopt = <sp>family inet6
+type = allports
diff --git a/config/action.d/iptables-ipset-proto6.conf b/config/action.d/iptables-ipset-proto6.conf
index 12c3ddd6..ef744984 100644
--- a/config/action.d/iptables-ipset-proto6.conf
+++ b/config/action.d/iptables-ipset-proto6.conf
@@ -15,65 +15,13 @@
#
# Modified: Alexander Koeppe <format_c@online.de>, Serg G. Brester <serg.brester@sebres.de>
# made config file IPv6 capable (see new section Init?family=inet6)
+#
+# Obsolete: superseded by iptables-ipset[type=multiport]
[INCLUDES]
-before = iptables-common.conf
+before = iptables-ipset.conf
[Definition]
-# Option: actionstart
-# Notes.: command executed on demand at the first ban (or at the start of Fail2Ban if actionstart_on_demand is set to false).
-# Values: CMD
-#
-actionstart = ipset create <ipmset> hash:ip timeout <default-timeout><familyopt>
- <iptables> -I <chain> -p <protocol> -m multiport --dports <port> -m set --match-set <ipmset> src -j <blocktype>
-
-# Option: actionflush
-# Notes.: command executed once to flush IPS, by shutdown (resp. by stop of the jail or this action)
-# Values: CMD
-#
-actionflush = ipset flush <ipmset>
-
-# Option: actionstop
-# Notes.: command executed at the stop of jail (or at the end of Fail2Ban)
-# Values: CMD
-#
-actionstop = <iptables> -D <chain> -p <protocol> -m multiport --dports <port> -m set --match-set <ipmset> src -j <blocktype>
- <actionflush>
- ipset destroy <ipmset>
-
-# Option: actionban
-# Notes.: command executed when banning an IP. Take care that the
-# command is executed with Fail2Ban user rights.
-# Tags: See jail.conf(5) man page
-# Values: CMD
-#
-actionban = ipset add <ipmset> <ip> timeout <bantime> -exist
-
-actionprolong = %(actionban)s
-
-# Option: actionunban
-# Notes.: command executed when unbanning an IP. Take care that the
-# command is executed with Fail2Ban user rights.
-# Tags: See jail.conf(5) man page
-# Values: CMD
-#
-actionunban = ipset del <ipmset> <ip> -exist
-
-[Init]
-
-# Option: default-timeout
-# Notes: specifies default timeout in seconds (handled default ipset timeout only)
-# Values: [ NUM ] Default: 600
-
-default-timeout = 600
-
-ipmset = f2b-<name>
-familyopt =
-
-
-[Init?family=inet6]
-
-ipmset = f2b-<name>6
-familyopt = <sp>family inet6
+type = multiport
diff --git a/config/action.d/iptables-ipset.conf b/config/action.d/iptables-ipset.conf
new file mode 100644
index 00000000..b44e6ec4
--- /dev/null
+++ b/config/action.d/iptables-ipset.conf
@@ -0,0 +1,90 @@
+# Fail2Ban configuration file
+#
+# Authors: Sergey G Brester (sebres), Daniel Black, Alexander Koeppe
+#
+# This is for ipset protocol 6 (and hopefully later) (ipset v6.14).
+# Use ipset -V to see the protocol and version. Version 4 should use
+# iptables-ipset-proto4.conf.
+#
+# This requires the program ipset which is normally in package called ipset.
+#
+# IPset was a feature introduced in the linux kernel 2.6.39 and 3.0.0 kernels.
+#
+# If you are running on an older kernel you make need to patch in external
+# modules.
+#
+
+[INCLUDES]
+
+before = iptables.conf
+
+[Definition]
+
+# Option: actionstart
+# Notes.: command executed on demand at the first ban (or at the start of Fail2Ban if actionstart_on_demand is set to false).
+# Values: CMD
+#
+actionstart = ipset -exist create <ipmset> hash:ip timeout <default-ipsettime> <familyopt>
+ <_ipt_add_rules>
+
+# Option: actionflush
+# Notes.: command executed once to flush IPS, by shutdown (resp. by stop of the jail or this action)
+# Values: CMD
+#
+actionflush = ipset flush <ipmset>
+
+# Option: actionstop
+# Notes.: command executed at the stop of jail (or at the end of Fail2Ban)
+# Values: CMD
+#
+actionstop = <_ipt_del_rules>
+ <actionflush>
+ ipset destroy <ipmset>
+
+# Option: actionban
+# Notes.: command executed when banning an IP. Take care that the
+# command is executed with Fail2Ban user rights.
+# Tags: See jail.conf(5) man page
+# Values: CMD
+#
+actionban = ipset -exist add <ipmset> <ip> timeout <ipsettime>
+
+# actionprolong = %(actionban)s
+
+# Option: actionunban
+# Notes.: command executed when unbanning an IP. Take care that the
+# command is executed with Fail2Ban user rights.
+# Tags: See jail.conf(5) man page
+# Values: CMD
+#
+actionunban = ipset -exist del <ipmset> <ip>
+
+# Several capabilities used internaly:
+
+rule-jump = -m set --match-set <ipmset> src -j <blocktype>
+
+
+[Init]
+
+# Option: default-ipsettime
+# Notes: specifies default timeout in seconds (handled default ipset timeout only)
+# Values: [ NUM ] Default: 0 (no timeout, managed by fail2ban by unban)
+default-ipsettime = 0
+
+# Option: ipsettime
+# Notes: specifies ticket timeout (handled ipset timeout only)
+# Values: [ NUM ] Default: 0 (managed by fail2ban by unban)
+ipsettime = 0
+
+# expresion to caclulate timeout from bantime, example:
+# banaction = %(known/banaction)s[ipsettime='<timeout-bantime>']
+timeout-bantime = $([ "<bantime>" -le 2147483 ] && echo "<bantime>" || echo 0)
+
+ipmset = f2b-<name>
+familyopt =
+
+
+[Init?family=inet6]
+
+ipmset = f2b-<name>6
+familyopt = family inet6
diff --git a/config/action.d/iptables-multiport-log.conf b/config/action.d/iptables-multiport-log.conf
index df126dbf..322a7491 100644
--- a/config/action.d/iptables-multiport-log.conf
+++ b/config/action.d/iptables-multiport-log.conf
@@ -11,7 +11,7 @@
[INCLUDES]
-before = iptables-common.conf
+before = iptables.conf
[Definition]
diff --git a/config/action.d/iptables-multiport.conf b/config/action.d/iptables-multiport.conf
index 41b00c54..008208e0 100644
--- a/config/action.d/iptables-multiport.conf
+++ b/config/action.d/iptables-multiport.conf
@@ -3,50 +3,12 @@
# Author: Cyril Jaquier
# Modified by Yaroslav Halchenko for multiport banning
#
+# Obsolete: superseded by iptables[type=multiport]
[INCLUDES]
-before = iptables-common.conf
+before = iptables.conf
[Definition]
-# Option: actionstart
-# Notes.: command executed on demand at the first ban (or at the start of Fail2Ban if actionstart_on_demand is set to false).
-# Values: CMD
-#
-actionstart = <iptables> -N f2b-<name>
- <iptables> -A f2b-<name> -j <returntype>
- <iptables> -I <chain> -p <protocol> -m multiport --dports <port> -j f2b-<name>
-
-# Option: actionstop
-# Notes.: command executed at the stop of jail (or at the end of Fail2Ban)
-# Values: CMD
-#
-actionstop = <iptables> -D <chain> -p <protocol> -m multiport --dports <port> -j f2b-<name>
- <actionflush>
- <iptables> -X f2b-<name>
-
-# Option: actioncheck
-# Notes.: command executed once before each actionban command
-# Values: CMD
-#
-actioncheck = <iptables> -n -L <chain> | grep -q 'f2b-<name>[ \t]'
-
-# Option: actionban
-# Notes.: command executed when banning an IP. Take care that the
-# command is executed with Fail2Ban user rights.
-# Tags: See jail.conf(5) man page
-# Values: CMD
-#
-actionban = <iptables> -I f2b-<name> 1 -s <ip> -j <blocktype>
-
-# Option: actionunban
-# Notes.: command executed when unbanning an IP. Take care that the
-# command is executed with Fail2Ban user rights.
-# Tags: See jail.conf(5) man page
-# Values: CMD
-#
-actionunban = <iptables> -D f2b-<name> -s <ip> -j <blocktype>
-
-[Init]
-
+type = multiport
diff --git a/config/action.d/iptables-new.conf b/config/action.d/iptables-new.conf
index 39a17099..170cb934 100644
--- a/config/action.d/iptables-new.conf
+++ b/config/action.d/iptables-new.conf
@@ -4,51 +4,12 @@
# Copied from iptables.conf and modified by Yaroslav Halchenko
# to fulfill the needs of bugreporter dbts#350746.
#
-#
+# Obsolete: superseded by iptables[pre-rule='-m state --state NEW<sp>']
[INCLUDES]
-before = iptables-common.conf
+before = iptables.conf
[Definition]
-# Option: actionstart
-# Notes.: command executed on demand at the first ban (or at the start of Fail2Ban if actionstart_on_demand is set to false).
-# Values: CMD
-#
-actionstart = <iptables> -N f2b-<name>
- <iptables> -A f2b-<name> -j <returntype>
- <iptables> -I <chain> -m state --state NEW -p <protocol> --dport <port> -j f2b-<name>
-
-# Option: actionstop
-# Notes.: command executed at the stop of jail (or at the end of Fail2Ban)
-# Values: CMD
-#
-actionstop = <iptables> -D <chain> -m state --state NEW -p <protocol> --dport <port> -j f2b-<name>
- <actionflush>
- <iptables> -X f2b-<name>
-
-# Option: actioncheck
-# Notes.: command executed once before each actionban command
-# Values: CMD
-#
-actioncheck = <iptables> -n -L <chain> | grep -q 'f2b-<name>[ \t]'
-
-# Option: actionban
-# Notes.: command executed when banning an IP. Take care that the
-# command is executed with Fail2Ban user rights.
-# Tags: See jail.conf(5) man page
-# Values: CMD
-#
-actionban = <iptables> -I f2b-<name> 1 -s <ip> -j <blocktype>
-
-# Option: actionunban
-# Notes.: command executed when unbanning an IP. Take care that the
-# command is executed with Fail2Ban user rights.
-# Tags: See jail.conf(5) man page
-# Values: CMD
-#
-actionunban = <iptables> -D f2b-<name> -s <ip> -j <blocktype>
-
-[Init]
-
+pre-rule = -m state --state NEW<sp> \ No newline at end of file
diff --git a/config/action.d/iptables-xt_recent-echo.conf b/config/action.d/iptables-xt_recent-echo.conf
index 97449222..c3c175b3 100644
--- a/config/action.d/iptables-xt_recent-echo.conf
+++ b/config/action.d/iptables-xt_recent-echo.conf
@@ -7,10 +7,14 @@
[INCLUDES]
-before = iptables-common.conf
+before = iptables.conf
[Definition]
+_ipt_chain_rule = -m recent --update --seconds 3600 --name <iptname> -j <blocktype>
+_ipt_for_proto-iter =
+_ipt_for_proto-done =
+
# Option: actionstart
# Notes.: command executed on demand at the first ban (or at the start of Fail2Ban if actionstart_on_demand is set to false).
# Values: CMD
@@ -33,7 +37,9 @@ before = iptables-common.conf
# own rules. The 3600 second timeout is independent and acts as a
# safeguard in case the fail2ban process dies unexpectedly. The
# shorter of the two timeouts actually matters.
-actionstart = if [ `id -u` -eq 0 ];then <iptables> -I <chain> -m recent --update --seconds 3600 --name <iptname> -j <blocktype>;fi
+actionstart = if [ `id -u` -eq 0 ];then
+ { %(_ipt_check_rule)s >/dev/null 2>&1; } || { <iptables> -I <chain> %(_ipt_chain_rule)s; }
+ fi
# Option: actionflush
#
@@ -46,13 +52,15 @@ actionflush =
# Values: CMD
#
actionstop = echo / > /proc/net/xt_recent/<iptname>
- if [ `id -u` -eq 0 ];then <iptables> -D <chain> -m recent --update --seconds 3600 --name <iptname> -j <blocktype>;fi
+ if [ `id -u` -eq 0 ];then
+ <iptables> -D <chain> %(_ipt_chain_rule)s;
+ fi
# Option: actioncheck
-# Notes.: command executed once before each actionban command
+# Notes.: command executed as invariant check (error by ban)
# Values: CMD
#
-actioncheck = test -e /proc/net/xt_recent/<iptname>
+actioncheck = { <iptables> -C <chain> %(_ipt_chain_rule)s; } && test -e /proc/net/xt_recent/<iptname>
# Option: actionban
# Notes.: command executed when banning an IP. Take care that the
@@ -72,7 +80,7 @@ actionunban = echo -<ip> > /proc/net/xt_recent/<iptname>
[Init]
-iptname = f2b-<name>
+iptname = f2b-<name>
[Init?family=inet6]
diff --git a/config/action.d/iptables.conf b/config/action.d/iptables.conf
index 8ed5fdad..67d496f5 100644
--- a/config/action.d/iptables.conf
+++ b/config/action.d/iptables.conf
@@ -1,28 +1,35 @@
# Fail2Ban configuration file
#
-# Author: Cyril Jaquier
-#
+# Authors: Sergey G. Brester (sebres), Cyril Jaquier, Daniel Black,
+# Yaroslav O. Halchenko, Alexander Koeppe et al.
#
-[INCLUDES]
+[Definition]
-before = iptables-common.conf
+# Option: type
+# Notes.: type of the action.
+# Values: [ oneport | multiport | allports ] Default: oneport
+#
+type = oneport
-[Definition]
+# Option: actionflush
+# Notes.: command executed once to flush IPS, by shutdown (resp. by stop of the jail or this action)
+# Values: CMD
+#
+actionflush = <iptables> -F f2b-<name>
# Option: actionstart
# Notes.: command executed on demand at the first ban (or at the start of Fail2Ban if actionstart_on_demand is set to false).
# Values: CMD
#
-actionstart = <iptables> -N f2b-<name>
- <iptables> -A f2b-<name> -j <returntype>
- <iptables> -I <chain> -p <protocol> --dport <port> -j f2b-<name>
+actionstart = { <iptables> -C f2b-<name> -j <returntype> >/dev/null 2>&1; } || { <iptables> -N f2b-<name> || true; <iptables> -A f2b-<name> -j <returntype>; }
+ <_ipt_add_rules>
# Option: actionstop
# Notes.: command executed at the stop of jail (or at the end of Fail2Ban)
# Values: CMD
#
-actionstop = <iptables> -D <chain> -p <protocol> --dport <port> -j f2b-<name>
+actionstop = <_ipt_del_rules>
<actionflush>
<iptables> -X f2b-<name>
@@ -30,7 +37,7 @@ actionstop = <iptables> -D <chain> -p <protocol> --dport <port> -j f2b-<name>
# Notes.: command executed once before each actionban command
# Values: CMD
#
-actioncheck = <iptables> -n -L <chain> | grep -q 'f2b-<name>[ \t]'
+actioncheck = <_ipt_check_rules>
# Option: actionban
# Notes.: command executed when banning an IP. Take care that the
@@ -48,5 +55,108 @@ actionban = <iptables> -I f2b-<name> 1 -s <ip> -j <blocktype>
#
actionunban = <iptables> -D f2b-<name> -s <ip> -j <blocktype>
+# Option: pre-rule
+# Notes.: prefix parameter(s) inserted to the begin of rule. No default (empty)
+#
+pre-rule =
+
+rule-jump = -j <_ipt_rule_target>
+
+# Several capabilities used internaly:
+
+_ipt_for_proto-iter = for proto in $(echo '<protocol>' | sed 's/,/ /g'); do
+_ipt_for_proto-done = done
+
+_ipt_add_rules = <_ipt_for_proto-iter>
+ { %(_ipt_check_rule)s >/dev/null 2>&1; } || { <iptables> -I <chain> %(_ipt_chain_rule)s; }
+ <_ipt_for_proto-done>
+
+_ipt_del_rules = <_ipt_for_proto-iter>
+ <iptables> -D <chain> %(_ipt_chain_rule)s
+ <_ipt_for_proto-done>
+
+_ipt_check_rules = <_ipt_for_proto-iter>
+ %(_ipt_check_rule)s
+ <_ipt_for_proto-done>
+
+_ipt_chain_rule = <pre-rule><ipt_<type>/_chain_rule>
+_ipt_check_rule = <iptables> -C <chain> %(_ipt_chain_rule)s
+_ipt_rule_target = f2b-<name>
+
+[ipt_oneport]
+
+_chain_rule = -p $proto --dport <port> <rule-jump>
+
+[ipt_multiport]
+
+_chain_rule = -p $proto -m multiport --dports <port> <rule-jump>
+
+[ipt_allports]
+
+_chain_rule = -p $proto <rule-jump>
+
+
[Init]
+# Option: chain
+# Notes specifies the iptables chain to which the Fail2Ban rules should be
+# added
+# Values: STRING Default: INPUT
+chain = INPUT
+
+# Default name of the chain
+#
+name = default
+
+# Option: port
+# Notes.: specifies port to monitor
+# Values: [ NUM | STRING ] Default:
+#
+port = ssh
+
+# Option: protocol
+# Notes.: internally used by config reader for interpolations.
+# Values: [ tcp | udp | icmp | all ] Default: tcp
+#
+protocol = tcp
+
+# Option: blocktype
+# Note: This is what the action does with rules. This can be any jump target
+# as per the iptables man page (section 8). Common values are DROP
+# REJECT, REJECT --reject-with icmp-port-unreachable
+# Values: STRING
+blocktype = REJECT --reject-with icmp-port-unreachable
+
+# Option: returntype
+# Note: This is the default rule on "actionstart". This should be RETURN
+# in all (blocking) actions, except REJECT in allowing actions.
+# Values: STRING
+returntype = RETURN
+
+# Option: lockingopt
+# Notes.: Option was introduced to iptables to prevent multiple instances from
+# running concurrently and causing irratic behavior. -w was introduced
+# in iptables 1.4.20, so might be absent on older systems
+# See https://github.com/fail2ban/fail2ban/issues/1122
+# Values: STRING
+lockingopt = -w
+
+# Option: iptables
+# Notes.: Actual command to be executed, including common to all calls options
+# Values: STRING
+iptables = iptables <lockingopt>
+
+
+[Init?family=inet6]
+
+# Option: blocktype (ipv6)
+# Note: This is what the action does with rules. This can be any jump target
+# as per the iptables man page (section 8). Common values are DROP
+# REJECT, REJECT --reject-with icmp6-port-unreachable
+# Values: STRING
+blocktype = REJECT --reject-with icmp6-port-unreachable
+
+# Option: iptables (ipv6)
+# Notes.: Actual command to be executed, including common to all calls options
+# Values: STRING
+iptables = ip6tables <lockingopt>
diff --git a/config/action.d/ipthreat.conf b/config/action.d/ipthreat.conf
new file mode 100644
index 00000000..193a60f2
--- /dev/null
+++ b/config/action.d/ipthreat.conf
@@ -0,0 +1,107 @@
+# IPThreat configuration file
+#
+# Added to fail2ban by Jeff Johnson (jjxtra)
+#
+# Action to report IP address to ipthreat.net
+#
+# You must sign up to obtain an API key from ipthreat.net and request bulk report permissions
+# https://ipthreat.net/integrations
+#
+# IPThreat is a 100% free site and service, all data is licensed under a creative commons by attribution license
+# Please do not integrate if you do not agree to the license
+#
+# IMPORTANT:
+#
+# Reporting an IP is a serious action. Make sure that it is legit.
+# Consider using this action only for:
+# * IP that has been banned more than once
+# * High max retry to avoid user mis-typing password
+# * Filters that are unlikely to be human error
+#
+# Example:
+# ```
+# action = %(known/action)s
+# ipthreat[]
+# ```
+#
+# The action accepts the following arguments: ipthreat[ipthreat_flags="8",ipthreat_system="SSH", ipthreat_apikey=...]
+# In most cases your action could be as simple as: ipthreat[], since the default flags and system are set to the most correct default values.
+# You can optionally override ipthreat_system and ipthreat_flags if desired.
+# The ipthreat_apikey must be set at the bottom of this configuration file.
+#
+# `ipthreat_system` is a short name of the system attacked, i.e. SSH, SMTP, MYSQL, PHP, etc.
+#
+# For `ipthreat_flags`, most cases will use 8 (BruteForce) which is the default, but you could use others.
+# You can use the name or the ordinal.
+# Multiple values are comma separated.
+# ```
+# Name Ordinal Description
+# Dns 1 Abuse/attack of dns (domain name server)
+# Fraud 2 General fraud, whether orders, misuse of payment info, etc
+# DDos 4 Distributed denial of service attack, whether through http requests, large ping attack, etc
+# BruteForce 8 Brute force login attack
+# Proxy 16 IP is a proxy like TOR or other proxy server
+# Spam 32 Email, comment or other type of spam
+# Vpn 64 IP is part of a VPN
+# Hacking 128 General hacking outside of brute force attack (includes vulnerability scans, sql injection, etc.). Use port scan flag instead if it's just probe on ports.
+# BadBot 256 Bad bot that is not honoring robots.txt or just flooding with too many requests, etc
+# Compromised 512 The ip has been taken over by malware or botnet
+# Phishing 1024 The ip is involved in phishing or spoofing
+# Iot 2048 The ip has targetted an iot (Internet of Things) device
+# PortScan 4096 Port scan
+# See https://ipthreat.net/bulkreportformat for more information
+# ```
+
+[Definition]
+
+# bypass action for restored tickets
+norestored = 1
+
+# Option: actionstart
+# Notes.: command executed on demand at the first ban (or at the start of Fail2Ban if actionstart_on_demand is set to false).
+# Values: CMD
+#
+actionstart =
+
+# Option: actionstop
+# Notes.: command executed at the stop of jail (or at the end of Fail2Ban)
+# Values: CMD
+#
+actionstop =
+
+# Option: actioncheck
+# Notes.: command executed once before each actionban command
+# Values: CMD
+#
+actioncheck =
+
+# Option: actionban
+# Notes.: command executed when banning an IP. Take care that the
+# command is executed with Fail2Ban user rights.
+#
+# Tags: See jail.conf(5) man page
+# Values: CMD
+#
+actionban = curl -sSf "https://api.ipthreat.net/api/report" -X POST -H "Content-Type: application/json" -H "X-API-KEY: <ipthreat_apikey>" -d "{\"ip\":\"<ip>\",\"flags\":\"<ipthreat_flags>\",\"system\":\"<ipthreat_system>\",\"notes\":\"fail2ban\"}"
+
+# Option: actionunban
+# Notes.: command executed when unbanning an IP. Take care that the
+# command is executed with Fail2Ban user rights.
+# Tags: See jail.conf(5) man page
+# Values: CMD
+#
+actionunban =
+
+[Init]
+# Option: ipthreat_apikey
+# Notes Your API key from ipthreat.net
+# Values: STRING Default: None
+# Register for ipthreat [https://ipthreat.net], get api key and set below.
+# You will need to set the flags and system in the action call in jail.conf
+ipthreat_apikey =
+
+# By default, the ipthreat system is the name of the fail2ban jail
+ipthreat_system = <name>
+
+# By default the ip threat flags is 8 (brute force), but you can override this per jail if desired
+ipthreat_flags = 8 \ No newline at end of file
diff --git a/config/action.d/mail-buffered.conf b/config/action.d/mail-buffered.conf
index 325f185b..79b84104 100644
--- a/config/action.d/mail-buffered.conf
+++ b/config/action.d/mail-buffered.conf
@@ -17,7 +17,7 @@ actionstart = printf %%b "Hi,\n
The jail <name> has been started successfully.\n
Output will be buffered until <lines> lines are available.\n
Regards,\n
- Fail2Ban"|mail -s "[Fail2Ban] <name>: started on <fq-hostname>" <dest>
+ Fail2Ban"|mail -E 'set escape' -s "[Fail2Ban] <name>: started on <fq-hostname>" <dest>
# Option: actionstop
# Notes.: command executed at the stop of jail (or at the end of Fail2Ban)
@@ -28,13 +28,13 @@ actionstop = if [ -f <tmpfile> ]; then
These hosts have been banned by Fail2Ban.\n
`cat <tmpfile>`
Regards,\n
- Fail2Ban"|mail -s "[Fail2Ban] <name>: Summary from <fq-hostname>" <dest>
+ Fail2Ban"|mail -E 'set escape' -s "[Fail2Ban] <name>: Summary from <fq-hostname>" <dest>
rm <tmpfile>
fi
printf %%b "Hi,\n
The jail <name> has been stopped.\n
Regards,\n
- Fail2Ban"|mail -s "[Fail2Ban] <name>: stopped on <fq-hostname>" <dest>
+ Fail2Ban"|mail -E 'set escape' -s "[Fail2Ban] <name>: stopped on <fq-hostname>" <dest>
# Option: actioncheck
# Notes.: command executed once before each actionban command
@@ -55,7 +55,7 @@ actionban = printf %%b "`date`: <ip> (<failures> failures)\n" >> <tmpfile>
These hosts have been banned by Fail2Ban.\n
`cat <tmpfile>`
\nRegards,\n
- Fail2Ban"|mail -s "[Fail2Ban] <name>: Summary" <dest>
+ Fail2Ban"|mail -E 'set escape' -s "[Fail2Ban] <name>: Summary" <dest>
rm <tmpfile>
fi
diff --git a/config/action.d/mail-whois-common.conf b/config/action.d/mail-whois-common.conf
index b0d27afc..ecf3a5d9 100644
--- a/config/action.d/mail-whois-common.conf
+++ b/config/action.d/mail-whois-common.conf
@@ -17,7 +17,7 @@ _whois = whois <ip> || echo "missing whois program"
# character set before sending it to a mail program
# make sure you have 'file' and 'iconv' commands installed when opting for that
_whois_target_charset = UTF-8
-_whois_convert_charset = whois <ip> |
+_whois_convert_charset = (%(_whois)s) |
{ WHOIS_OUTPUT=$(cat) ; WHOIS_CHARSET=$(printf %%b "$WHOIS_OUTPUT" | file -b --mime-encoding -) ; printf %%b "$WHOIS_OUTPUT" | iconv -f $WHOIS_CHARSET -t %(_whois_target_charset)s//TRANSLIT - ; }
# choose between _whois and _whois_convert_charset in mail-whois-common.local
diff --git a/config/action.d/mail-whois-lines.conf b/config/action.d/mail-whois-lines.conf
index 3a3e56b2..d2818cb9 100644
--- a/config/action.d/mail-whois-lines.conf
+++ b/config/action.d/mail-whois-lines.conf
@@ -72,7 +72,7 @@ actionunban =
# Notes.: Your system mail command. Is passed 2 args: subject and recipient
# Values: CMD
#
-mailcmd = mail -s
+mailcmd = mail -E 'set escape' -s
# Default name of the chain
#
diff --git a/config/action.d/mail-whois.conf b/config/action.d/mail-whois.conf
index 7fea34c4..ab33b616 100644
--- a/config/action.d/mail-whois.conf
+++ b/config/action.d/mail-whois.conf
@@ -20,7 +20,7 @@ norestored = 1
actionstart = printf %%b "Hi,\n
The jail <name> has been started successfully.\n
Regards,\n
- Fail2Ban"|mail -s "[Fail2Ban] <name>: started on <fq-hostname>" <dest>
+ Fail2Ban"|mail -E 'set escape' -s "[Fail2Ban] <name>: started on <fq-hostname>" <dest>
# Option: actionstop
# Notes.: command executed at the stop of jail (or at the end of Fail2Ban)
@@ -29,7 +29,7 @@ actionstart = printf %%b "Hi,\n
actionstop = printf %%b "Hi,\n
The jail <name> has been stopped.\n
Regards,\n
- Fail2Ban"|mail -s "[Fail2Ban] <name>: stopped on <fq-hostname>" <dest>
+ Fail2Ban"|mail -E 'set escape' -s "[Fail2Ban] <name>: stopped on <fq-hostname>" <dest>
# Option: actioncheck
# Notes.: command executed once before each actionban command
@@ -49,7 +49,7 @@ actionban = printf %%b "Hi,\n
Here is more information about <ip> :\n
`%(_whois_command)s`\n
Regards,\n
- Fail2Ban"|mail -s "[Fail2Ban] <name>: banned <ip> from <fq-hostname>" <dest>
+ Fail2Ban"|mail -E 'set escape' -s "[Fail2Ban] <name>: banned <ip> from <fq-hostname>" <dest>
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
diff --git a/config/action.d/mail.conf b/config/action.d/mail.conf
index 5d8c0e15..f4838ddc 100644
--- a/config/action.d/mail.conf
+++ b/config/action.d/mail.conf
@@ -16,7 +16,7 @@ norestored = 1
actionstart = printf %%b "Hi,\n
The jail <name> has been started successfully.\n
Regards,\n
- Fail2Ban"|mail -s "[Fail2Ban] <name>: started on <fq-hostname>" <dest>
+ Fail2Ban"|mail -E 'set escape' -s "[Fail2Ban] <name>: started on <fq-hostname>" <dest>
# Option: actionstop
# Notes.: command executed at the stop of jail (or at the end of Fail2Ban)
@@ -25,7 +25,7 @@ actionstart = printf %%b "Hi,\n
actionstop = printf %%b "Hi,\n
The jail <name> has been stopped.\n
Regards,\n
- Fail2Ban"|mail -s "[Fail2Ban] <name>: stopped on <fq-hostname>" <dest>
+ Fail2Ban"|mail -E 'set escape' -s "[Fail2Ban] <name>: stopped on <fq-hostname>" <dest>
# Option: actioncheck
# Notes.: command executed once before each actionban command
@@ -43,7 +43,7 @@ actionban = printf %%b "Hi,\n
The IP <ip> has just been banned by Fail2Ban after
<failures> attempts against <name>.\n
Regards,\n
- Fail2Ban"|mail -s "[Fail2Ban] <name>: banned <ip> from <fq-hostname>" <dest>
+ Fail2Ban"|mail -E 'set escape' -s "[Fail2Ban] <name>: banned <ip> from <fq-hostname>" <dest>
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
diff --git a/config/action.d/nftables-allports.conf b/config/action.d/nftables-allports.conf
index 6c69da39..908abe40 100644
--- a/config/action.d/nftables-allports.conf
+++ b/config/action.d/nftables-allports.conf
@@ -6,17 +6,12 @@
# Modified: Alexander Belykh <albel727@ngs.ru>
# adapted for nftables
#
+# Obsolete: superseded by nftables[type=allports]
[INCLUDES]
-before = nftables-common.conf
+before = nftables.conf
[Definition]
-# Option: nftables_mode
-# Notes.: additional expressions for nftables filter rule
-# Values: nftables expressions
-#
-nftables_mode = meta l4proto <protocol>
-
-[Init]
+type = allports
diff --git a/config/action.d/nftables-common.conf b/config/action.d/nftables-common.conf
deleted file mode 100644
index 37045712..00000000
--- a/config/action.d/nftables-common.conf
+++ /dev/null
@@ -1,135 +0,0 @@
-# Fail2Ban configuration file
-#
-# Author: Daniel Black
-# Author: Cyril Jaquier
-# Modified: Yaroslav O. Halchenko <debian@onerussian.com>
-# made active on all ports from original iptables.conf
-# Modified: Alexander Belykh <albel727@ngs.ru>
-# adapted for nftables
-#
-# This is a included configuration file and includes the definitions for the nftables
-# used in all nftables based actions by default.
-#
-# The user can override the defaults in nftables-common.local
-
-[INCLUDES]
-
-after = nftables-common.local
-
-[Definition]
-
-# Option: nftables_mode
-# Notes.: additional expressions for nftables filter rule
-# Values: nftables expressions
-#
-nftables_mode = <protocol> dport \{ <port> \}
-
-# Option: actionstart
-# Notes.: command executed on demand at the first ban (or at the start of Fail2Ban if actionstart_on_demand is set to false).
-# Values: CMD
-#
-actionstart = <nftables> add set <nftables_family> <nftables_table> <set_name> \{ type <nftables_type>\; \}
- <nftables> insert rule <nftables_family> <nftables_table> <chain> %(nftables_mode)s <address_family> saddr @<set_name> <blocktype>
-
-_nft_list = <nftables> --handle --numeric list chain <nftables_family> <nftables_table> <chain>
-_nft_get_handle_id = grep -m1 '<address_family> saddr @<set_name> <blocktype> # handle' | grep -oe ' handle [0-9]*'
-
-# Option: actionstop
-# Notes.: command executed at the stop of jail (or at the end of Fail2Ban)
-# Values: CMD
-#
-actionstop = HANDLE_ID=$(%(_nft_list)s | %(_nft_get_handle_id)s)
- <nftables> delete rule <nftables_family> <nftables_table> <chain> $HANDLE_ID
- <nftables> delete set <nftables_family> <nftables_table> <set_name>
-
-# Option: actioncheck
-# Notes.: command executed once before each actionban command
-# Values: CMD
-#
-actioncheck = <nftables> list chain <nftables_family> <nftables_table> <chain> | grep -q '@<set_name>[ \t]'
-
-# Option: actionban
-# Notes.: command executed when banning an IP. Take care that the
-# command is executed with Fail2Ban user rights.
-# Tags: See jail.conf(5) man page
-# Values: CMD
-#
-actionban = <nftables> add element <nftables_family> <nftables_table> <set_name> \{ <ip> \}
-
-# Option: actionunban
-# Notes.: command executed when unbanning an IP. Take care that the
-# command is executed with Fail2Ban user rights.
-# Tags: See jail.conf(5) man page
-# Values: CMD
-#
-actionunban = <nftables> delete element <nftables_family> <nftables_table> <set_name> \{ <ip> \}
-
-[Init]
-
-# Option: nftables_type
-# Notes.: address type to work with
-# Values: [ipv4_addr | ipv6_addr] Default: ipv4_addr
-#
-nftables_type = ipv4_addr
-
-# Option: nftables_family
-# Notes.: address family to work in
-# Values: [ip | ip6 | inet] Default: inet
-#
-nftables_family = inet
-
-# Option: nftables_table
-# Notes.: table in the address family to work in
-# Values: STRING Default: filter
-#
-nftables_table = filter
-
-# Option: chain
-# Notes specifies the nftables chain to which the Fail2Ban rules should be
-# added
-# Values: STRING Default: input
-chain = input
-
-# Default name of the filtering set
-#
-name = default
-
-# Option: port
-# Notes.: specifies port to monitor
-# Values: [ NUM | STRING ] Default:
-#
-port = ssh
-
-# Option: protocol
-# Notes.: internally used by config reader for interpolations.
-# Values: [ tcp | udp ] Default: tcp
-#
-protocol = tcp
-
-# Option: blocktype
-# Note: This is what the action does with rules. This can be any jump target
-# as per the nftables man page (section 8). Common values are drop
-# reject, reject with icmp type host-unreachable
-# Values: STRING
-blocktype = reject
-
-# Option: nftables
-# Notes.: Actual command to be executed, including common to all calls options
-# Values: STRING
-nftables = nft
-
-# Option: set_name
-# Notes.: The name of the nft set used to store banned addresses
-# Values: STRING
-set_name = f2b-<name>
-
-# Option: address_family
-# Notes.: The family of the banned addresses
-# Values: [ ip | ip6 ]
-address_family = ip
-
-[Init?family=inet6]
-
-nftables_type = ipv6_addr
-set_name = f2b-<name>6
-address_family = ip6
diff --git a/config/action.d/nftables-multiport.conf b/config/action.d/nftables-multiport.conf
index d1afafb3..ba3ec92c 100644
--- a/config/action.d/nftables-multiport.conf
+++ b/config/action.d/nftables-multiport.conf
@@ -6,17 +6,12 @@
# Modified: Alexander Belykh <albel727@ngs.ru>
# adapted for nftables
#
+# Obsolete: superseded by nftables[type=multiport]
[INCLUDES]
-before = nftables-common.conf
+before = nftables.conf
[Definition]
-# Option: nftables_mode
-# Notes.: additional expressions for nftables filter rule
-# Values: nftables expressions
-#
-nftables_mode = <protocol> dport \{ <port> \}
-
-[Init]
+type = multiport \ No newline at end of file
diff --git a/config/action.d/nftables.conf b/config/action.d/nftables.conf
new file mode 100644
index 00000000..77cf3661
--- /dev/null
+++ b/config/action.d/nftables.conf
@@ -0,0 +1,203 @@
+# Fail2Ban configuration file
+#
+# Author: Daniel Black
+# Author: Cyril Jaquier
+# Modified: Yaroslav O. Halchenko <debian@onerussian.com>
+# made active on all ports from original iptables.conf
+# Modified: Alexander Belykh <albel727@ngs.ru>
+# adapted for nftables
+#
+# This is a included configuration file and includes the definitions for the nftables
+# used in all nftables based actions by default.
+#
+# The user can override the defaults in nftables-common.local
+# Example: redirect flow to honeypot
+#
+# [Init]
+# table_family = ip
+# chain_type = nat
+# chain_hook = prerouting
+# chain_priority = -50
+# blocktype = counter redirect to 2222
+
+[INCLUDES]
+
+after = nftables-common.local
+
+[Definition]
+
+# Option: type
+# Notes.: type of the action.
+# Values: [ multiport | allports ] Default: multiport
+#
+type = multiport
+
+rule_match-custom =
+rule_match-allports = meta l4proto \{ <protocol> \}
+rule_match-multiport = $proto dport \{ $(echo '<port>' | sed s/:/-/g) \}
+match = <rule_match-<type>>
+
+# Option: rule_stat
+# Notes.: statement for nftables filter rule.
+# leaving it empty will block all (include udp and icmp)
+# Values: nftables statement
+#
+rule_stat = %(match)s <addr_family> saddr @<addr_set> <blocktype>
+
+# optional interator over protocol's:
+_nft_for_proto-custom-iter =
+_nft_for_proto-custom-done =
+_nft_for_proto-allports-iter =
+_nft_for_proto-allports-done =
+_nft_for_proto-multiport-iter = for proto in $(echo '<protocol>' | sed 's/,/ /g'); do
+_nft_for_proto-multiport-done = done
+
+_nft_list = <nftables> -a list chain <table_family> <table> <chain>
+_nft_get_handle_id = grep -oP '@<addr_set>\s+.*\s+\Khandle\s+(\d+)$'
+
+_nft_add_set = <nftables> add set <table_family> <table> <addr_set> \{ type <addr_type>\; \}
+ <_nft_for_proto-<type>-iter>
+ <nftables> add rule <table_family> <table> <chain> %(rule_stat)s
+ <_nft_for_proto-<type>-done>
+_nft_del_set = { %(_nft_list)s | %(_nft_get_handle_id)s; } | while read -r hdl; do
+ <nftables> delete rule <table_family> <table> <chain> $hdl; done
+ <nftables> delete set <table_family> <table> <addr_set>
+
+# Option: _nft_shutdown_table
+# Notes.: command executed after the stop in order to delete table (it checks that no sets are available):
+# Values: CMD
+#
+_nft_shutdown_table = { <nftables> list table <table_family> <table> | grep -qP '^\s+set\s+'; } || {
+ <nftables> delete table <table_family> <table>
+ }
+
+# Option: actionstart
+# Notes.: command executed on demand at the first ban (or at the start of Fail2Ban if actionstart_on_demand is set to false).
+# Values: CMD
+#
+actionstart = <nftables> add table <table_family> <table>
+ <nftables> -- add chain <table_family> <table> <chain> \{ type <chain_type> hook <chain_hook> priority <chain_priority> \; \}
+ %(_nft_add_set)s
+
+# Option: actionflush
+# Notes.: command executed once to flush IPS, by shutdown (resp. by stop of the jail or this action);
+# uses `nft flush set ...` and as fallback (e. g. unsupported) recreates the set (with references)
+# Values: CMD
+#
+actionflush = { <nftables> flush set <table_family> <table> <addr_set> 2> /dev/null; } || {
+ %(_nft_del_set)s
+ %(_nft_add_set)s
+ }
+
+# Option: actionstop
+# Notes.: command executed at the stop of jail (or at the end of Fail2Ban)
+# Values: CMD
+#
+actionstop = %(_nft_del_set)s
+ <_nft_shutdown_table>
+
+# Option: actioncheck
+# Notes.: command executed once before each actionban command
+# Values: CMD
+#
+actioncheck = <nftables> list chain <table_family> <table> <chain> | grep -q '@<addr_set>[ \t]'
+
+# Option: actionban
+# Notes.: command executed when banning an IP. Take care that the
+# command is executed with Fail2Ban user rights.
+# Tags: See jail.conf(5) man page
+# Values: CMD
+#
+actionban = <nftables> add element <table_family> <table> <addr_set> \{ <ip> \}
+
+# Option: actionunban
+# Notes.: command executed when unbanning an IP. Take care that the
+# command is executed with Fail2Ban user rights.
+# Tags: See jail.conf(5) man page
+# Values: CMD
+#
+actionunban = <nftables> delete element <table_family> <table> <addr_set> \{ <ip> \}
+
+[Init]
+
+# Option: table
+# Notes.: main table to store chain and sets (automatically created on demand)
+# Values: STRING Default: f2b-table
+table = f2b-table
+
+# Option: table_family
+# Notes.: address family to work in
+# Values: [ip | ip6 | inet] Default: inet
+table_family = inet
+
+# Option: chain
+# Notes.: main chain to store rules
+# Values: STRING Default: f2b-chain
+chain = f2b-chain
+
+# Option: chain_type
+# Notes.: refers to the kind of chain to be created
+# Values: [filter | route | nat] Default: filter
+#
+chain_type = filter
+
+# Option: chain_hook
+# Notes.: refers to the kind of chain to be created
+# Values: [ prerouting | input | forward | output | postrouting ] Default: input
+#
+chain_hook = input
+
+# Option: chain_priority
+# Notes.: priority in the chain.
+# Values: NUMBER Default: -1
+#
+chain_priority = -1
+
+# Option: addr_type
+# Notes.: address type to work with
+# Values: [ipv4_addr | ipv6_addr] Default: ipv4_addr
+#
+addr_type = ipv4_addr
+
+# Default name of the filtering set
+#
+name = default
+
+# Option: port
+# Notes.: specifies port to monitor
+# Values: [ NUM | STRING ] Default:
+#
+port = ssh
+
+# Option: protocol
+# Notes.: internally used by config reader for interpolations.
+# Values: [ tcp | udp ] Default: tcp
+#
+protocol = tcp
+
+# Option: blocktype
+# Note: This is what the action does with rules. This can be any jump target
+# as per the nftables man page (section 8). Common values are drop,
+# reject, reject with icmpx type host-unreachable, redirect to 2222
+# Values: STRING
+blocktype = reject
+
+# Option: nftables
+# Notes.: Actual command to be executed, including common to all calls options
+# Values: STRING
+nftables = nft
+
+# Option: addr_set
+# Notes.: The name of the nft set used to store banned addresses
+# Values: STRING
+addr_set = addr-set-<name>
+
+# Option: addr_family
+# Notes.: The family of the banned addresses
+# Values: [ ip | ip6 ]
+addr_family = ip
+
+[Init?family=inet6]
+addr_family = ip6
+addr_type = ipv6_addr
+addr_set = addr6-set-<name>
diff --git a/config/action.d/nginx-block-map.conf b/config/action.d/nginx-block-map.conf
index 33c15f9c..0de382bd 100644
--- a/config/action.d/nginx-block-map.conf
+++ b/config/action.d/nginx-block-map.conf
@@ -84,8 +84,15 @@ srv_cfg_path = /etc/nginx/
#srv_cmd = nginx -c %(srv_cfg_path)s/nginx.conf
srv_cmd = nginx
-# first test configuration is correct, hereafter send reload signal:
-blck_lst_reload = %(srv_cmd)s -qt; if [ $? -eq 0 ]; then
+# pid file (used to check nginx is running):
+srv_pid = /run/nginx.pid
+
+# command used to check whether nginx is running and configuration is valid:
+srv_is_running = [ -f "%(srv_pid)s" ]
+srv_check_cmd = %(srv_is_running)s && %(srv_cmd)s -qt
+
+# first test nginx is running and configuration is correct, hereafter send reload signal:
+blck_lst_reload = %(srv_check_cmd)s; if [ $? -eq 0 ]; then
%(srv_cmd)s -s reload; if [ $? -ne 0 ]; then echo 'reload failed.'; fi;
fi;
@@ -103,6 +110,8 @@ actionstop = %(actionflush)s
actioncheck =
-actionban = echo "\\\\<fid> 1;" >> '%(blck_lst_file)s'; %(blck_lst_reload)s
+_echo_blck_row = printf '\%%s 1;\n' "<fid>"
+
+actionban = %(_echo_blck_row)s >> '%(blck_lst_file)s'; %(blck_lst_reload)s
-actionunban = id=$(echo "<fid>" | sed -e 's/[]\/$*.^|[]/\\&/g'); sed -i "/$id 1;/d" %(blck_lst_file)s; %(blck_lst_reload)s
+actionunban = id=$(%(_echo_blck_row)s | sed -e 's/[]\/$*.^|[]/\\&/g'); sed -i "/^$id$/d" %(blck_lst_file)s; %(blck_lst_reload)s
diff --git a/config/action.d/sendmail-buffered.conf b/config/action.d/sendmail-buffered.conf
index 199c6ce5..13803f8b 100644
--- a/config/action.d/sendmail-buffered.conf
+++ b/config/action.d/sendmail-buffered.conf
@@ -24,7 +24,7 @@ actionstart = printf %%b "Subject: [Fail2Ban] <name>: started on <fq-hostname>
The jail <name> has been started successfully.\n
Output will be buffered until <lines> lines are available.\n
Regards,\n
- Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
+ Fail2Ban" | <mailcmd>
# Option: actionstop
# Notes.: command executed at the stop of jail (or at the end of Fail2Ban)
@@ -38,7 +38,7 @@ actionstop = if [ -f <tmpfile> ]; then
These hosts have been banned by Fail2Ban.\n
`cat <tmpfile>`
Regards,\n
- Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
+ Fail2Ban" | <mailcmd>
rm <tmpfile>
fi
printf %%b "Subject: [Fail2Ban] <name>: stopped on <fq-hostname>
@@ -47,7 +47,7 @@ actionstop = if [ -f <tmpfile> ]; then
Hi,\n
The jail <name> has been stopped.\n
Regards,\n
- Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
+ Fail2Ban" | <mailcmd>
# Option: actioncheck
# Notes.: command executed once before each actionban command
@@ -71,7 +71,7 @@ actionban = printf %%b "`date`: <ip> (<failures> failures)\n" >> <tmpfile>
These hosts have been banned by Fail2Ban.\n
`cat <tmpfile>`
Regards,\n
- Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
+ Fail2Ban" | <mailcmd>
rm <tmpfile>
fi
diff --git a/config/action.d/sendmail-common.conf b/config/action.d/sendmail-common.conf
index 9bf15054..1e31fadf 100644
--- a/config/action.d/sendmail-common.conf
+++ b/config/action.d/sendmail-common.conf
@@ -21,7 +21,7 @@ actionstart = printf %%b "Subject: [Fail2Ban] <name>: started on <fq-hostname>
Hi,\n
The jail <name> has been started successfully.\n
Regards,\n
- Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
+ Fail2Ban" | <mailcmd>
# Option: actionstop
# Notes.: command executed at the stop of jail (or at the end of Fail2Ban)
@@ -34,7 +34,7 @@ actionstop = printf %%b "Subject: [Fail2Ban] <name>: stopped on <fq-hostname>
Hi,\n
The jail <name> has been stopped.\n
Regards,\n
- Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
+ Fail2Ban" | <mailcmd>
# Option: actioncheck
# Notes.: command executed once before each actionban command
@@ -60,6 +60,10 @@ actionunban =
[Init]
+# Your system mail command
+#
+mailcmd = /usr/sbin/sendmail -f "<sender>" "<dest>"
+
# Recipient mail address
#
dest = root
diff --git a/config/action.d/sendmail-geoip-lines.conf b/config/action.d/sendmail-geoip-lines.conf
index b7c1bf36..b36e49a7 100644
--- a/config/action.d/sendmail-geoip-lines.conf
+++ b/config/action.d/sendmail-geoip-lines.conf
@@ -37,11 +37,11 @@ actionban = ( printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from <fq-hostn
Country:`geoiplookup -f /usr/share/GeoIP/GeoIP.dat "<ip>" | cut -d':' -f2-`
AS:`geoiplookup -f /usr/share/GeoIP/GeoIPASNum.dat "<ip>" | cut -d':' -f2-`
hostname: <ip-host>\n\n
- Lines containing failures of <ip>\n";
+ Lines containing failures of <ip> (max <grepmax>)\n";
%(_grep_logs)s;
printf %%b "\n
Regards,\n
- Fail2Ban" ) | /usr/sbin/sendmail -f <sender> <dest>
+ Fail2Ban" ) | <mailcmd>
[Init]
diff --git a/config/action.d/sendmail-whois-ipjailmatches.conf b/config/action.d/sendmail-whois-ipjailmatches.conf
index 06ea3a3e..7790ec53 100644
--- a/config/action.d/sendmail-whois-ipjailmatches.conf
+++ b/config/action.d/sendmail-whois-ipjailmatches.conf
@@ -7,6 +7,7 @@
[INCLUDES]
before = sendmail-common.conf
+ mail-whois-common.conf
[Definition]
@@ -27,11 +28,11 @@ actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from <fq-hostnam
The IP <ip> has just been banned by Fail2Ban after
<failures> attempts against <name>.\n\n
Here is more information about <ip> :\n
- `/usr/bin/whois <ip>`\n\n
+ `%(_whois_command)s`\n\n
Matches for <name> with <ipjailfailures> failures IP:<ip>\n
<ipjailmatches>\n\n
Regards,\n
- Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
+ Fail2Ban" | <mailcmd>
[Init]
diff --git a/config/action.d/sendmail-whois-ipmatches.conf b/config/action.d/sendmail-whois-ipmatches.conf
index 83bff1b4..e4717ca1 100644
--- a/config/action.d/sendmail-whois-ipmatches.conf
+++ b/config/action.d/sendmail-whois-ipmatches.conf
@@ -7,6 +7,7 @@
[INCLUDES]
before = sendmail-common.conf
+ mail-whois-common.conf
[Definition]
@@ -27,11 +28,11 @@ actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from <fq-hostnam
The IP <ip> has just been banned by Fail2Ban after
<failures> attempts against <name>.\n\n
Here is more information about <ip> :\n
- `/usr/bin/whois <ip>`\n\n
+ `%(_whois_command)s`\n\n
Matches with <ipfailures> failures IP:<ip>\n
<ipmatches>\n\n
Regards,\n
- Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
+ Fail2Ban" | <mailcmd>
[Init]
diff --git a/config/action.d/sendmail-whois-lines.conf b/config/action.d/sendmail-whois-lines.conf
index 4b947cb2..47ec6ed5 100644
--- a/config/action.d/sendmail-whois-lines.conf
+++ b/config/action.d/sendmail-whois-lines.conf
@@ -7,6 +7,7 @@
[INCLUDES]
before = sendmail-common.conf
+ mail-whois-common.conf
helpers-common.conf
[Definition]
@@ -27,13 +28,13 @@ actionban = ( printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from <fq-hostn
Hi,\n
The IP <ip> has just been banned by Fail2Ban after
<failures> attempts against <name>.\n\n
- Here is more information about <ip> :\n
- `/usr/bin/whois <ip> || echo missing whois program`\n\n
- Lines containing failures of <ip>\n";
+ Here is more information about <ip> :\n"
+ %(_whois_command)s;
+ printf %%b "\nLines containing failures of <ip> (max <grepmax>)\n";
%(_grep_logs)s;
printf %%b "\n
Regards,\n
- Fail2Ban" ) | /usr/sbin/sendmail -f <sender> <dest>
+ Fail2Ban" ) | <mailcmd>
[Init]
diff --git a/config/action.d/sendmail-whois-matches.conf b/config/action.d/sendmail-whois-matches.conf
index 01520135..08215ea7 100644
--- a/config/action.d/sendmail-whois-matches.conf
+++ b/config/action.d/sendmail-whois-matches.conf
@@ -7,6 +7,7 @@
[INCLUDES]
before = sendmail-common.conf
+ mail-whois-common.conf
[Definition]
@@ -27,11 +28,11 @@ actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from <fq-hostnam
The IP <ip> has just been banned by Fail2Ban after
<failures> attempts against <name>.\n\n
Here is more information about <ip> :\n
- `/usr/bin/whois <ip>`\n\n
+ `%(_whois_command)s`\n\n
Matches:\n
<matches>\n\n
Regards,\n
- Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
+ Fail2Ban" | <mailcmd>
[Init]
diff --git a/config/action.d/sendmail-whois.conf b/config/action.d/sendmail-whois.conf
index 2fb01ed3..9e93cd32 100644
--- a/config/action.d/sendmail-whois.conf
+++ b/config/action.d/sendmail-whois.conf
@@ -7,6 +7,7 @@
[INCLUDES]
before = sendmail-common.conf
+ mail-whois-common.conf
[Definition]
@@ -27,9 +28,9 @@ actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from <fq-hostnam
The IP <ip> has just been banned by Fail2Ban after
<failures> attempts against <name>.\n\n
Here is more information about <ip> :\n
- `/usr/bin/whois <ip> || echo missing whois program`\n
+ `%(_whois_command)s`\n
Regards,\n
- Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
+ Fail2Ban" | <mailcmd>
[Init]
diff --git a/config/action.d/sendmail.conf b/config/action.d/sendmail.conf
index cf420915..ad9e8d79 100644
--- a/config/action.d/sendmail.conf
+++ b/config/action.d/sendmail.conf
@@ -27,7 +27,7 @@ actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from <fq-hostnam
The IP <ip> has just been banned by Fail2Ban after
<failures> attempts against <name>.\n
Regards,\n
- Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
+ Fail2Ban" | <mailcmd>
[Init]
diff --git a/config/action.d/shorewall-ipset-proto6.conf b/config/action.d/shorewall-ipset-proto6.conf
index 45be0c0a..eacb53d9 100644
--- a/config/action.d/shorewall-ipset-proto6.conf
+++ b/config/action.d/shorewall-ipset-proto6.conf
@@ -51,7 +51,7 @@
# Values: CMD
#
actionstart = if ! ipset -quiet -name list f2b-<name> >/dev/null;
- then ipset -quiet -exist create f2b-<name> hash:ip timeout <default-timeout>;
+ then ipset -quiet -exist create f2b-<name> hash:ip timeout <default-ipsettime>;
fi
# Option: actionstop
@@ -66,9 +66,9 @@ actionstop = ipset flush f2b-<name>
# Tags: See jail.conf(5) man page
# Values: CMD
#
-actionban = ipset add f2b-<name> <ip> timeout <bantime> -exist
+actionban = ipset add f2b-<name> <ip> timeout <ipsettime> -exist
-actionprolong = %(actionban)s
+# actionprolong = %(actionban)s
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
@@ -78,8 +78,16 @@ actionprolong = %(actionban)s
#
actionunban = ipset del f2b-<name> <ip> -exist
-# Option: default-timeout
+# Option: default-ipsettime
# Notes: specifies default timeout in seconds (handled default ipset timeout only)
-# Values: [ NUM ] Default: 600
+# Values: [ NUM ] Default: 0 (no timeout, managed by fail2ban by unban)
+default-ipsettime = 0
-default-timeout = 600
+# Option: ipsettime
+# Notes: specifies ticket timeout (handled ipset timeout only)
+# Values: [ NUM ] Default: 0 (managed by fail2ban by unban)
+ipsettime = 0
+
+# expresion to caclulate timeout from bantime, example:
+# banaction = %(known/banaction)s[ipsettime='<timeout-bantime>']
+timeout-bantime = $([ "<bantime>" -le 2147483 ] && echo "<bantime>" || echo 0)
diff --git a/config/action.d/shorewall.conf b/config/action.d/shorewall.conf
index dcef8829..83d08d99 100644
--- a/config/action.d/shorewall.conf
+++ b/config/action.d/shorewall.conf
@@ -9,7 +9,7 @@
# connections. So if the attempter goes on trying using the same connection
# he could even log in. In order to get the same behavior of the iptable
# action (so that the ban is immediate) the /etc/shorewall/shorewall.conf
-# file should me modified with "BLACKLISTNEWONLY=No". Note that as of
+# file should be modified with "BLACKLISTNEWONLY=No". Note that as of
# Shorewall 4.5.13 BLACKLISTNEWONLY is deprecated; however the equivalent
# of BLACKLISTNEWONLY=No can now be achieved by setting BLACKLIST="ALL".
#
diff --git a/config/action.d/smtp.py b/config/action.d/smtp.py
index 9cdfe327..5c27d0ff 100644
--- a/config/action.d/smtp.py
+++ b/config/action.d/smtp.py
@@ -159,25 +159,25 @@ class SMTPAction(ActionBase):
try:
self._logSys.debug("Connected to SMTP '%s', response: %i: %s",
self.host, *smtp.connect(self.host))
- if self.user and self.password:
+ if self.user and self.password: # pragma: no cover (ATM no tests covering that)
smtp.login(self.user, self.password)
failed_recipients = smtp.sendmail(
self.fromaddr, self.toaddr.split(", "), msg.as_string())
- except smtplib.SMTPConnectError:
+ except smtplib.SMTPConnectError: # pragma: no cover
self._logSys.error("Error connecting to host '%s'", self.host)
raise
- except smtplib.SMTPAuthenticationError:
+ except smtplib.SMTPAuthenticationError: # pragma: no cover
self._logSys.error(
"Failed to authenticate with host '%s' user '%s'",
self.host, self.user)
raise
- except smtplib.SMTPException:
+ except smtplib.SMTPException: # pragma: no cover
self._logSys.error(
"Error sending mail to host '%s' from '%s' to '%s'",
self.host, self.fromaddr, self.toaddr)
raise
else:
- if failed_recipients:
+ if failed_recipients: # pragma: no cover
self._logSys.warning(
"Email to '%s' failed to following recipients: %r",
self.toaddr, failed_recipients)
@@ -186,7 +186,7 @@ class SMTPAction(ActionBase):
try:
self._logSys.debug("Disconnected from '%s', response %i: %s",
self.host, *smtp.quit())
- except smtplib.SMTPServerDisconnected:
+ except smtplib.SMTPServerDisconnected: # pragma: no cover
pass # Not connected
def start(self):
diff --git a/config/action.d/symbiosis-blacklist-allports.conf b/config/action.d/symbiosis-blacklist-allports.conf
index 6fb7d0af..7208b293 100644
--- a/config/action.d/symbiosis-blacklist-allports.conf
+++ b/config/action.d/symbiosis-blacklist-allports.conf
@@ -5,7 +5,7 @@
[INCLUDES]
-before = iptables-common.conf
+before = iptables.conf
[Definition]
@@ -41,6 +41,11 @@ actionban = echo 'all' >| /etc/symbiosis/firewall/blacklist.d/<ip>.auto
actionunban = rm -f /etc/symbiosis/firewall/blacklist.d/<ip>.auto
<iptables> -D <chain> -s <ip> -j <blocktype> || :
+# [TODO] Flushing is currently not implemented for symbiosis blacklist.d
+#
+actionflush =
+
+
[Init]
# Option: chain
diff --git a/config/action.d/ufw.conf b/config/action.d/ufw.conf
index d2f731f2..c9ff7f37 100644
--- a/config/action.d/ufw.conf
+++ b/config/action.d/ufw.conf
@@ -13,16 +13,45 @@ actionstop =
actioncheck =
-actionban = [ -n "<application>" ] && app="app <application>"
- ufw insert <insertpos> <blocktype> from <ip> to <destination> $app
+# ufw does "quickly process packets for which we already have a connection" in before.rules,
+# therefore all related sockets should be closed
+# actionban is using `ss` to do so, this only handles IPv4 and IPv6.
-actionunban = [ -n "<application>" ] && app="app <application>"
- ufw delete <blocktype> from <ip> to <destination> $app
+actionban = if [ -n "<application>" ] && ufw app info "<application>"
+ then
+ ufw <add> <blocktype> from <ip> to <destination> app "<application>" comment "<comment>"
+ else
+ ufw <add> <blocktype> from <ip> to <destination> comment "<comment>"
+ fi
+ <kill>
+
+actionunban = if [ -n "<application>" ] && ufw app info "<application>"
+ then
+ ufw delete <blocktype> from <ip> to <destination> app "<application>"
+ else
+ ufw delete <blocktype> from <ip> to <destination>
+ fi
+
+# Option: kill-mode
+# Notes.: can be set to ss or conntrack (may be extended later with other modes) to immediately drop all connections from banned IP, default empty (no kill)
+# Example: banaction = ufw[kill-mode=ss]
+kill-mode =
+
+# intern conditional parameter used to provide killing mode after ban:
+_kill_ =
+_kill_ss = ss -K dst "[<ip>]"
+_kill_conntrack = conntrack -D -s "<ip>"
+
+# Option: kill
+# Notes.: can be used to specify custom killing feature, by default depending on option kill-mode
+# Examples: banaction = ufw[kill='ss -K "( sport = :http || sport = :https )" dst "[<ip>]"']
+# banaction = ufw[kill='cutter "<ip>"']
+kill = <_kill_<kill-mode>>
[Init]
-# Option: insertpos
-# Notes.: The position number in the firewall list to insert the block rule
-insertpos = 1
+# Option: add
+# Notes.: can be set to "insert 1" to insert a rule at certain position (here 1):
+add = prepend
# Option: blocktype
# Notes.: reject or deny
@@ -36,6 +65,10 @@ destination = any
# Notes.: application from sudo ufw app list
application =
+# Option: comment
+# Notes.: comment for rule added by fail2ban
+comment = by Fail2Ban after <failures> attempts against <name>
+
# DEV NOTES:
#
# Author: Guilhem Lettron
diff --git a/config/action.d/xarf-login-attack.conf b/config/action.d/xarf-login-attack.conf
index 2b135c43..f348b2c4 100644
--- a/config/action.d/xarf-login-attack.conf
+++ b/config/action.d/xarf-login-attack.conf
@@ -41,7 +41,12 @@ actionstop =
actioncheck =
-actionban = oifs=${IFS}; IFS=.;SEP_IP=( <ip> ); set -- ${SEP_IP}; ADDRESSES=$(dig +short -t txt -q $4.$3.$2.$1.abuse-contacts.abusix.org); IFS=${oifs}
+actionban = oifs=${IFS};
+ RESOLVER_ADDR="%(addr_resolver)s"
+ if [ "<debug>" -gt 0 ]; then echo "try to resolve $RESOLVER_ADDR"; fi
+ ADDRESSES=$(dig +short -t txt -q $RESOLVER_ADDR | tr -d '"')
+ IFS=,; ADDRESSES=$(echo $ADDRESSES)
+ IFS=${oifs}
IP=<ip>
FROM=<sender>
SERVICE=<service>
@@ -51,26 +56,37 @@ actionban = oifs=${IFS}; IFS=.;SEP_IP=( <ip> ); set -- ${SEP_IP}; ADDRESSES=$(di
PORT=<port>
DATE=`LC_ALL=C date --date=@<time> +"%%a, %%d %%h %%Y %%T %%z"`
if [ ! -z "$ADDRESSES" ]; then
+ oifs=${IFS}; IFS=,; ADDRESSES=$(echo $ADDRESSES)
+ IFS=${oifs}
(printf -- %%b "<header>\n<message>\n<report>\n\n";
date '+Note: Local timezone is %%z (%%Z)';
- printf -- %%b "\n<ipmatches>\n\n<footer>") | <mailcmd> <mailargs> ${ADDRESSES//,/\" \"}
+ printf -- %%b "\n<ipmatches>\n\n<footer>") | <mailcmd> <mailargs> $ADDRESSES
fi
actionunban =
-[Init]
+# Server as resolver used in dig command
+#
+addr_resolver = <ip-rev>abuse-contacts.abusix.org
+
+# Option: boundary
+# Notes: This can be overwritten to be safe for possible predictions
+boundary = bfbb0f920793ac03cb8634bde14d8a1e
+
+_boundary = Abuse<time>-<boundary>
+
# Option: header
# Notes: This is really a fixed value
-header = Subject: abuse report about $IP - $DATE\nAuto-Submitted: auto-generated\nX-XARF: PLAIN\nContent-Transfer-Encoding: 7bit\nContent-Type: multipart/mixed; charset=utf8;\n boundary=Abuse-bfbb0f920793ac03cb8634bde14d8a1e;\n\n--Abuse-bfbb0f920793ac03cb8634bde14d8a1e\nMIME-Version: 1.0\nContent-Transfer-Encoding: 7bit\nContent-Type: text/plain; charset=utf-8;\n
+header = Subject: abuse report about $IP - $DATE\nAuto-Submitted: auto-generated\nX-XARF: PLAIN\nContent-Transfer-Encoding: 7bit\nContent-Type: multipart/mixed; charset=utf8;\n boundary=%(_boundary)s;\n\n--%(_boundary)s\nMIME-Version: 1.0\nContent-Transfer-Encoding: 7bit\nContent-Type: text/plain; charset=utf-8;\n
# Option: footer
# Notes: This is really a fixed value and needs to match the report and header
# mime delimiters
-footer = \n\n--Abuse-bfbb0f920793ac03cb8634bde14d8a1e--
+footer = \n\n--%(_boundary)s--
# Option: report
# Notes: Intended to be fixed
-report = --Abuse-bfbb0f920793ac03cb8634bde14d8a1e\nMIME-Version: 1.0\nContent-Transfer-Encoding: 7bit\nContent-Type: text/plain; charset=utf-8; name=\"report.txt\";\n\n---\nReported-From: $FROM\nCategory: abuse\nReport-ID: $REPORTID\nReport-Type: login-attack\nService: $SERVICE\nVersion: 0.2\nUser-Agent: Fail2ban v0.9\nDate: $DATE\nSource-Type: ip-address\nSource: $IP\nPort: $PORT\nSchema-URL: http://www.x-arf.org/schema/abuse_login-attack_0.1.2.json\nAttachment: text/plain\nOccurances: $FAILURES\nTLP: $TLP\n\n\n--Abuse-bfbb0f920793ac03cb8634bde14d8a1e\nMIME-Version: 1.0\nContent-Transfer-Encoding: 7bit\nContent-Type: text/plain; charset=utf8; name=\"logfile.log\";
+report = --%(_boundary)s\nMIME-Version: 1.0\nContent-Transfer-Encoding: 7bit\nContent-Type: text/plain; charset=utf-8; name=\"report.txt\";\n\n---\nReported-From: $FROM\nCategory: abuse\nReport-ID: $REPORTID\nReport-Type: login-attack\nService: $SERVICE\nVersion: 0.2\nUser-Agent: Fail2ban v0.9\nDate: $DATE\nSource-Type: ip-address\nSource: $IP\nPort: $PORT\nSchema-URL: http://www.x-arf.org/schema/abuse_login-attack_0.1.2.json\nAttachment: text/plain\nOccurances: $FAILURES\nTLP: $TLP\n\n\n--%(_boundary)s\nMIME-Version: 1.0\nContent-Transfer-Encoding: 7bit\nContent-Type: text/plain; charset=utf8; name=\"logfile.log\";
# Option: Message
# Notes: This can be modified by the users
diff --git a/config/fail2ban.conf b/config/fail2ban.conf
index 52e47187..fd6baebf 100644
--- a/config/fail2ban.conf
+++ b/config/fail2ban.conf
@@ -5,11 +5,11 @@
# Changes: in most of the cases you should not modify this
# file, but provide customizations in fail2ban.local file, e.g.:
#
-# [Definition]
+# [DEFAULT]
# loglevel = DEBUG
#
-[Definition]
+[DEFAULT]
# Option: loglevel
# Notes.: Set the log level output.
@@ -19,18 +19,18 @@
# NOTICE
# INFO
# DEBUG
-# Values: [ LEVEL ] Default: ERROR
+# Values: [ LEVEL ] Default: INFO
#
loglevel = INFO
# Option: logtarget
-# Notes.: Set the log target. This could be a file, SYSLOG, STDERR or STDOUT.
+# Notes.: Set the log target. This could be a file, SYSTEMD-JOURNAL, SYSLOG, STDERR or STDOUT.
# Only one log target can be specified.
# If you change logtarget from the default value and you are
# using logrotate -- also adjust or disable rotation in the
# corresponding configuration file
# (e.g. /etc/logrotate.d/fail2ban on Debian systems)
-# Values: [ STDOUT | STDERR | SYSLOG | SYSOUT | FILE ] Default: STDERR
+# Values: [ STDOUT | STDERR | SYSLOG | SYSOUT | SYSTEMD-JOURNAL | FILE ] Default: STDERR
#
logtarget = /var/log/fail2ban.log
@@ -55,6 +55,12 @@ socket = /var/run/fail2ban/fail2ban.sock
#
pidfile = /var/run/fail2ban/fail2ban.pid
+# Option: allowipv6
+# Notes.: Allows IPv6 interface:
+# Default: auto
+# Values: [ auto yes (on, true, 1) no (off, false, 0) ] Default: auto
+#allowipv6 = auto
+
# Options: dbfile
# Notes.: Set the file for the fail2ban persistent data to be stored.
# A value of ":memory:" means database is only stored in memory
@@ -67,3 +73,20 @@ dbfile = /var/lib/fail2ban/fail2ban.sqlite3
# Notes.: Sets age at which bans should be purged from the database
# Values: [ SECONDS ] Default: 86400 (24hours)
dbpurgeage = 1d
+
+# Options: dbmaxmatches
+# Notes.: Number of matches stored in database per ticket (resolvable via
+# tags <ipmatches>/<ipjailmatches> in actions)
+# Values: [ INT ] Default: 10
+dbmaxmatches = 10
+
+[Definition]
+
+
+[Thread]
+
+# Options: stacksize
+# Notes.: Specifies the stack size (in KiB) to be used for subsequently created threads,
+# and must be 0 or a positive integer value of at least 32.
+# Values: [ SIZE ] Default: 0 (use platform or configured default)
+#stacksize = 0
diff --git a/config/filter.d/apache-auth.conf b/config/filter.d/apache-auth.conf
index 91c89b26..40f6d6e3 100644
--- a/config/filter.d/apache-auth.conf
+++ b/config/filter.d/apache-auth.conf
@@ -9,6 +9,16 @@ before = apache-common.conf
[Definition]
+# Mode for filter: normal (default) and aggressive (allows DDoS & brute force detection of mod_evasive)
+mode = normal
+
+# ignore messages of mod_evasive module:
+apache-pref-ign-normal = (?!evasive)
+# allow "denied by server configuration" from all modules:
+apache-pref-ign-aggressive =
+# mode related ignore prefix for common _apache_error_client substitution:
+apache-pref-ignore = <apache-pref-ign-<mode>>
+
prefregex = ^%(_apache_error_client)s (?:AH\d+: )?<F-CONTENT>.+</F-CONTENT>$
# auth_type = ((?:Digest|Basic): )?
diff --git a/config/filter.d/apache-common.conf b/config/filter.d/apache-common.conf
index 3eec83d0..6577fe7d 100644
--- a/config/filter.d/apache-common.conf
+++ b/config/filter.d/apache-common.conf
@@ -27,7 +27,9 @@ _daemon = (?:apache\d*|httpd(?:/\w+)?)
apache-prefix = <apache-prefix-<logging>>
-_apache_error_client = <apache-prefix>\[(:?error|\S+:\S+)\]( \[pid \d+(:\S+ \d+)?\])? \[client <HOST>(:\d{1,5})?\]
+apache-pref-ignore =
+
+_apache_error_client = <apache-prefix>\[(:?error|<apache-pref-ignore>\S+:\S+)\]( \[pid \d+(:\S+ \d+)?\])? \[client <HOST>(:\d{1,5})?\]
datepattern = {^LN-BEG}
diff --git a/config/filter.d/apache-fakegooglebot.conf b/config/filter.d/apache-fakegooglebot.conf
index 729410ad..ee23656a 100644
--- a/config/filter.d/apache-fakegooglebot.conf
+++ b/config/filter.d/apache-fakegooglebot.conf
@@ -2,11 +2,11 @@
[Definition]
-failregex = ^<HOST> .*Googlebot.*$
+failregex = ^\s*<HOST> \S+ \S+(?: \S+)?\s+\S+ "[A-Z]+ /\S* [^"]*" \d+ \d+ \"[^"]*\" "[^"]*\bGooglebot/[^"]*"
ignoreregex =
-datepattern = ^[^\[]*\[({DATE})
+datepattern = ^[^\[]*(\[{DATE}\s*\])
{^LN-BEG}
# DEV Notes:
diff --git a/config/filter.d/apache-modsecurity.conf b/config/filter.d/apache-modsecurity.conf
index e296227a..f7600acf 100644
--- a/config/filter.d/apache-modsecurity.conf
+++ b/config/filter.d/apache-modsecurity.conf
@@ -10,7 +10,7 @@ before = apache-common.conf
[Definition]
-failregex = ^%(_apache_error_client)s ModSecurity:\s+(?:\[(?:\w+ \"[^\"]*\"|[^\]]*)\]\s*)*Access denied with code [45]\d\d
+failregex = ^%(_apache_error_client)s(?: \[client [^\]]+\])? ModSecurity:\s+(?:\[(?:\w+ \"[^\"]*\"|[^\]]*)\]\s*)*Access denied with code [45]\d\d
ignoreregex =
diff --git a/config/filter.d/apache-noscript.conf b/config/filter.d/apache-noscript.conf
index 1963d1dd..dd9452a9 100644
--- a/config/filter.d/apache-noscript.conf
+++ b/config/filter.d/apache-noscript.conf
@@ -17,13 +17,13 @@ before = apache-common.conf
[Definition]
-script = /\S*(?:php(?:[45]|[.-]cgi)?|\.asp|\.exe|\.pl)
+script = /\S*(?:php(?:[45]|[.-]cgi)?|\.asp|\.exe|\.pl|\bcgi-bin/)
-prefregex = ^%(_apache_error_client)s (?:AH0(?:01(?:28|30)|1(?:264|071)): )?(?:(?:[Ff]ile|script|[Gg]ot) )<F-CONTENT>.+</F-CONTENT>$
+prefregex = ^%(_apache_error_client)s (?:AH0(?:01(?:28|30)|1(?:264|071)|2811): )?(?:(?:[Ff]ile|script|[Gg]ot) )<F-CONTENT>.+</F-CONTENT>$
failregex = ^(?:does not exist|not found or unable to stat): <script>\b
^'<script>\S*' not found or unable to stat
- ^error '[Pp]rimary script unknown\\n'
+ ^error '[Pp]rimary script unknown(?:\\n)?'
ignoreregex =
diff --git a/config/filter.d/apache-overflows.conf b/config/filter.d/apache-overflows.conf
index 02a2ef20..0f54da11 100644
--- a/config/filter.d/apache-overflows.conf
+++ b/config/filter.d/apache-overflows.conf
@@ -8,7 +8,7 @@ before = apache-common.conf
[Definition]
-failregex = ^%(_apache_error_client)s (?:(?:AH0013[456]: )?Invalid (method|URI) in request\b|(?:AH00565: )?request failed: URI too long \(longer than \d+\)|request failed: erroneous characters after protocol string:|(?:AH00566: )?request failed: invalid characters in URI\b)
+failregex = ^%(_apache_error_client)s (?:(?:AH001[23][456]: )?Invalid (method|URI) in request\b|(?:AH00565: )?request failed: URI too long \(longer than \d+\)|request failed: erroneous characters after protocol string:|(?:AH00566: )?request failed: invalid characters in URI\b)
ignoreregex =
diff --git a/config/filter.d/asterisk.conf b/config/filter.d/asterisk.conf
index 6f7ae5d5..e15d7bfe 100644
--- a/config/filter.d/asterisk.conf
+++ b/config/filter.d/asterisk.conf
@@ -21,12 +21,12 @@ log_prefix= (?:NOTICE|SECURITY|WARNING)%(__pid_re)s:?(?:\[C-[\da-f]*\])?:? [^:]+
prefregex = ^%(__prefix_line)s%(log_prefix)s <F-CONTENT>.+</F-CONTENT>$
failregex = ^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)$
- ^Call from '[^']*' \(<HOST>:\d+\) to extension '[^']*' rejected because extension not found in context
+ ^Call from '[^']*' \((?:(?:TCP|UDP):)?<HOST>:\d+\) to extension '[^']*' rejected because extension not found in context
^(?:Host )?<HOST> (?:failed (?:to authenticate\b|MD5 authentication\b)|tried to authenticate with nonexistent user\b)
^No registration for peer '[^']*' \(from <HOST>\)$
^hacking attempt detected '<HOST>'$
- ^SecurityEvent="(?:FailedACL|InvalidAccountID|ChallengeResponseFailed|InvalidPassword)"(?:(?:,(?!RemoteAddress=)\w+="[^"]*")*|.*?),RemoteAddress="IPV[46]/(UDP|TCP|WS)/<HOST>/\d+"(?:,(?!RemoteAddress=)\w+="[^"]*")*$
- ^"Rejecting unknown SIP connection from <HOST>"$
+ ^SecurityEvent="(?:FailedACL|InvalidAccountID|ChallengeResponseFailed|InvalidPassword)"(?:(?:,(?!RemoteAddress=)\w+="[^"]*")*|.*?),RemoteAddress="IPV[46]/[^/"]+/<HOST>/\d+"(?:,(?!RemoteAddress=)\w+="[^"]*")*$
+ ^"Rejecting unknown SIP connection from <HOST>(?::\d+)?"$
^Request (?:'[^']*' )?from '(?:[^']*|.*?)' failed for '<HOST>(?::\d+)?'\s\(callid: [^\)]*\) - (?:No matching endpoint found|Not match Endpoint(?: Contact)? ACL|(?:Failed|Error) to authenticate)\s*$
# FreePBX (todo: make optional in v.0.10):
@@ -44,3 +44,12 @@ datepattern = {^LN-BEG}
# First regex: channels/chan_sip.c
#
# main/logger.c:ast_log_vsyslog - "in {functionname}:" only occurs in syslog
+
+journalmatch = _SYSTEMD_UNIT=asterisk.service
+
+
+[lt_journal]
+
+# asterisk can log timestamp if logs into systemd-journal (optional part matching this timestamp, gh-2383):
+__extra_timestamp = (?:\[[^\]]+\]\s+)?
+__prefix_line = %(known/__prefix_line)s%(__extra_timestamp)s
diff --git a/config/filter.d/bitwarden.conf b/config/filter.d/bitwarden.conf
new file mode 100644
index 00000000..b0651c8e
--- /dev/null
+++ b/config/filter.d/bitwarden.conf
@@ -0,0 +1,13 @@
+# Fail2Ban filter for Bitwarden
+# Detecting failed login attempts
+# Logged in bwdata/logs/identity/Identity/log.txt
+
+[INCLUDES]
+before = common.conf
+
+[Definition]
+_daemon = Bitwarden-Identity
+failregex = ^%(__prefix_line)s\s*\[(?:W(?:RN|arning)|Bit\.Core\.[^\]]+)\]\s+Failed login attempt(?:, 2FA invalid)?\. <ADDR>$
+
+# DEV Notes:
+# __prefix_line can result to an empty string, so it can support syslog and non-syslog at once.
diff --git a/config/filter.d/centreon.conf b/config/filter.d/centreon.conf
new file mode 100644
index 00000000..fd3c8482
--- /dev/null
+++ b/config/filter.d/centreon.conf
@@ -0,0 +1,9 @@
+# Fail2Ban filter for Centreon Web
+# Detecting unauthorized access to the Centreon Web portal
+# typically logged in /var/log/centreon/login.log
+
+[Init]
+datepattern = ^%%Y-%%m-%%d %%H:%%M:%%S
+
+[Definition]
+failregex = ^(?:\|-?\d+){3}\|\[[^\]]*\] \[<HOST>\] Authentication failed for '<F-USER>[^']+</F-USER>'
diff --git a/config/filter.d/common.conf b/config/filter.d/common.conf
index a8cba188..e6b3c641 100644
--- a/config/filter.d/common.conf
+++ b/config/filter.d/common.conf
@@ -10,6 +10,9 @@ after = common.local
[DEFAULT]
+# Type of log-file resp. log-format (file, short, journal, rfc5424):
+logtype = file
+
# Daemon definition is to be specialized (if needed) in .conf file
_daemon = \S*
@@ -22,7 +25,7 @@ __pid_re = (?:\[\d+\])
# Daemon name (with optional source_file:line or whatever)
# EXAMPLES: pam_rhosts_auth, [sshd], pop(pam_unix)
-__daemon_re = [\[\(]?%(_daemon)s(?:\(\S+\))?[\]\)]?:?
+__daemon_re = [\[\(]?<_daemon>(?:\(\S+\))?[\]\)]?:?
# extra daemon info
# EXAMPLE: [ID 800047 auth.info]
@@ -30,11 +33,11 @@ __daemon_extra_re = \[ID \d+ \S+\]
# Combinations of daemon name and PID
# EXAMPLES: sshd[31607], pop(pam_unix)[4920]
-__daemon_combs_re = (?:%(__pid_re)s?:\s+%(__daemon_re)s|%(__daemon_re)s%(__pid_re)s?:?)
+__daemon_combs_re = (?:<__pid_re>?:\s+<__daemon_re>|<__daemon_re><__pid_re>?:?)
# Some messages have a kernel prefix with a timestamp
# EXAMPLES: kernel: [769570.846956]
-__kernel_prefix = kernel: \[ *\d+\.\d+\]
+__kernel_prefix = kernel:\s?\[ *\d+\.\d+\]:?
__hostname = \S+
@@ -55,13 +58,32 @@ __date_ambit = (?:\[\])
# [bsdverbose]? [hostname] [vserver tag] daemon_id spaces
#
# This can be optional (for instance if we match named native log files)
-__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+)?
+__prefix_line = <lt_<logtype>/__prefix_line>
# PAM authentication mechanism check for failures, e.g.: pam_unix, pam_sss,
# pam_ldap
__pam_auth = pam_unix
# standardly all formats using prefix have line-begin anchored date:
+datepattern = <lt_<logtype>/datepattern>
+
+[lt_file]
+# Common line prefixes for logtype "file":
+__prefix_line = <__date_ambit>?\s*(?:<__bsd_syslog_verbose>\s+)?(?:<__hostname>\s+)?(?:<__kernel_prefix>\s+)?(?:<__vserver>\s+)?(?:<__daemon_combs_re>\s+)?(?:<__daemon_extra_re>\s+)?
datepattern = {^LN-BEG}
-# Author: Yaroslav Halchenko
+[lt_short]
+# Common (short) line prefix for logtype "journal" (corresponds output of formatJournalEntry):
+__prefix_line = \s*(?:<__hostname>\s+)?(?:<_daemon><__pid_re>?:?\s+)?(?:<__kernel_prefix>\s+)?
+datepattern = %(lt_file/datepattern)s
+[lt_journal]
+__prefix_line = %(lt_short/__prefix_line)s
+datepattern = %(lt_short/datepattern)s
+
+[lt_rfc5424]
+# RFC 5424 log-format, see gh-2309:
+#__prefix_line = \s*<__hostname> <__daemon_re> \d+ \S+ \S+\s+
+__prefix_line = \s*<__hostname> <__daemon_re> \d+ \S+ (?:[^\[\]\s]+|(?:\[(?:[^\]"]*|"[^"]*")*\])+)\s+
+datepattern = ^<\d+>\d+\s+{DATE}
+
+# Author: Yaroslav Halchenko, Sergey G. Brester (aka sebres)
diff --git a/config/filter.d/courier-auth.conf b/config/filter.d/courier-auth.conf
index 1ac33736..d5ba9c50 100644
--- a/config/filter.d/courier-auth.conf
+++ b/config/filter.d/courier-auth.conf
@@ -11,7 +11,7 @@ before = common.conf
_daemon = (?:courier)?(?:imapd?|pop3d?)(?:login)?(?:-ssl)?
-failregex = ^%(__prefix_line)sLOGIN FAILED, (?:user|method)=.*, ip=\[<HOST>\]$
+failregex = ^%(__prefix_line)sLOGIN FAILED, (?:(?!ip=)(?:user=<F-USER>[^,]*</F-USER>|\w+=[^,]*), )*ip=\[<HOST>\]
ignoreregex =
diff --git a/config/filter.d/courier-smtp.conf b/config/filter.d/courier-smtp.conf
index 888753c4..4b2b8d87 100644
--- a/config/filter.d/courier-smtp.conf
+++ b/config/filter.d/courier-smtp.conf
@@ -12,7 +12,7 @@ before = common.conf
_daemon = courieresmtpd
-prefregex = ^%(__prefix_line)serror,relay=<HOST>,<F-CONTENT>.+</F-CONTENT>$
+prefregex = ^%(__prefix_line)serror,relay=<HOST>,(?:port=\d+,)?<F-CONTENT>.+</F-CONTENT>$
failregex = ^[^:]*: 550 User (<.*> )?unknown\.?$
^msg="535 Authentication failed\.",cmd:( AUTH \S+)?( [0-9a-zA-Z\+/=]+)?(?: \S+)$
diff --git a/config/filter.d/dante.conf b/config/filter.d/dante.conf
new file mode 100644
index 00000000..e3f6f7b2
--- /dev/null
+++ b/config/filter.d/dante.conf
@@ -0,0 +1,16 @@
+# Fail2Ban filter for dante
+#
+# Make sure you have "log: error" set in your "client pass" directive
+#
+
+[INCLUDES]
+before = common.conf
+
+[Definition]
+_daemon = danted
+
+failregex = ^%(__prefix_line)sinfo: block\(1\): tcp/accept \]: <HOST>\.\d+ [\d.]+: error after reading \d+ bytes? in \d+ seconds?: (?:could not access |system password authentication failed for )user "<F-USER>[^"]+</F-USER>"
+
+[Init]
+journalmatch = _SYSTEMD_UNIT=danted.service
+
diff --git a/config/filter.d/domino-smtp.conf b/config/filter.d/domino-smtp.conf
index cdc17736..638cd7c5 100644
--- a/config/filter.d/domino-smtp.conf
+++ b/config/filter.d/domino-smtp.conf
@@ -35,9 +35,12 @@
# 08-09-2014 06:14:27 smtp: postmaster [1.2.3.4] authentication failure using internet password
# 08-09-2014 06:14:27 SMTP Server: Authentication failed for user postmaster ; connecting host 1.2.3.4
-__prefix = (?:\[[^\]]+\])?\s+
-failregex = ^%(__prefix)sSMTP Server: Authentication failed for user .*? \; connecting host <HOST>$
- ^%(__prefix)ssmtp: (?:[^\[]+ )*\[<HOST>\] authentication failure using internet password\s*$
+__prefix = (?:\[[^\]]+\])?\s*
+__opt_data = (?::|\s+\[[^\]]+\])
+failregex = ^%(__prefix)sSMTP Server%(__opt_data)s Authentication failed for user .*? \; connecting host \[?<HOST>\]?$
+ ^%(__prefix)ssmtp: (?:[^\[]+ )*\[?<HOST>\]? authentication failure using internet password\s*$
+ ^%(__prefix)sSMTP Server%(__opt_data)s Connection from \[?<HOST>\]? rejected for policy reasons\.
+
# Option: ignoreregex
# Notes.: regex to ignore. If this regex matches, the line is ignored.
# Values: TEXT
diff --git a/config/filter.d/dovecot.conf b/config/filter.d/dovecot.conf
index f0481e06..dc3ebbcd 100644
--- a/config/filter.d/dovecot.conf
+++ b/config/filter.d/dovecot.conf
@@ -7,18 +7,21 @@ before = common.conf
[Definition]
-_auth_worker = (?:dovecot: )?auth(?:-worker)?
_daemon = (?:dovecot(?:-auth)?|auth)
-prefregex = ^%(__prefix_line)s(?:%(_auth_worker)s(?:\([^\)]+\))?: )?(?:%(__pam_auth)s(?:\(dovecot:auth\))?: |(?:pop3|imap)-login: )?(?:Info: )?<F-CONTENT>.+</F-CONTENT>$
+_auth_worker = (?:dovecot: )?auth(?:-worker)?
+_auth_worker_info = (?:conn \w+:auth(?:-worker)? \([^\)]+\): auth(?:-worker)?<\d+>: )?
+_bypass_reject_reason = (?:: (?:\w+\([^\):]*\) \w+|[^\(]+))*
+
+prefregex = ^%(__prefix_line)s(?:%(_auth_worker)s(?:\([^\)]+\))?: )?(?:%(__pam_auth)s(?:\(dovecot:auth\))?: |(?:pop3|imap|managesieve|submission)-login: )?(?:Info: )?%(_auth_worker_info)s<F-CONTENT>.+</F-CONTENT>$
failregex = ^authentication failure; logname=<F-ALT_USER1>\S*</F-ALT_USER1> uid=\S* euid=\S* tty=dovecot ruser=<F-USER>\S*</F-USER> rhost=<HOST>(?:\s+user=<F-ALT_USER>\S*</F-ALT_USER>)?\s*$
- ^(?:Aborted login|Disconnected)(?::(?: [^ \(]+)+)? \((?:auth failed, \d+ attempts(?: in \d+ secs)?|tried to use (?:disabled|disallowed) \S+ auth|proxy dest auth failed)\):(?: user=<<F-USER>[^>]*</F-USER>>,)?(?: method=\S+,)? rip=<HOST>(?:[^>]*(?:, session=<\S+>)?)\s*$
- ^pam\(\S+,<HOST>(?:,\S*)?\): pam_authenticate\(\) failed: (?:User not known to the underlying authentication module: \d+ Time\(s\)|Authentication failure \(password mismatch\?\)|Permission denied)\s*$
- ^[a-z\-]{3,15}\(\S*,<HOST>(?:,\S*)?\): (?:unknown user|invalid credentials|Password mismatch)\s*$
+ ^(?:Aborted login|Disconnected|Remote closed connection|Client has quit the connection)%(_bypass_reject_reason)s \((?:auth failed, \d+ attempts(?: in \d+ secs)?|tried to use (?:disabled|disallowed) \S+ auth|proxy dest auth failed)\):(?: user=<<F-USER>[^>]*</F-USER>>,)?(?: method=\S+,)? rip=<HOST>(?:[^>]*(?:, session=<\S+>)?)\s*$
+ ^pam\(\S+,<HOST>(?:,\S*)?\): pam_authenticate\(\) failed: (?:User not known to the underlying authentication module: \d+ Time\(s\)|Authentication failure \([Pp]assword mismatch\?\)|Permission denied)\s*$
+ ^[a-z\-]{3,15}\(\S*,<HOST>(?:,\S*)?\): (?:[Uu]nknown user|[Ii]nvalid credentials|[Pp]assword mismatch)
<mdre-<mode>>
-mdre-aggressive = ^(?:Aborted login|Disconnected)(?::(?: [^ \(]+)+)? \((?:no auth attempts|disconnected before auth was ready,|client didn't finish \S+ auth,)(?: (?:in|waited) \d+ secs)?\):(?: user=<[^>]*>,)?(?: method=\S+,)? rip=<HOST>(?:[^>]*(?:, session=<\S+>)?)\s*$
+mdre-aggressive = ^(?:Aborted login|Disconnected|Remote closed connection|Client has quit the connection)%(_bypass_reject_reason)s \((?:no auth attempts|disconnected before auth was ready,|client didn't finish \S+ auth,)(?: (?:in|waited) \d+ secs)?\):(?: user=<[^>]*>,)?(?: method=\S+,)? rip=<HOST>(?:[^>]*(?:, session=<\S+>)?)\s*$
mdre-normal =
diff --git a/config/filter.d/drupal-auth.conf b/config/filter.d/drupal-auth.conf
index b60abe3e..2404cc6d 100644
--- a/config/filter.d/drupal-auth.conf
+++ b/config/filter.d/drupal-auth.conf
@@ -14,7 +14,7 @@ before = common.conf
[Definition]
-failregex = ^%(__prefix_line)s(https?:\/\/)([\da-z\.-]+)\.([a-z\.]{2,6})(\/[\w\.-]+)*\|\d{10}\|user\|<HOST>\|.+\|.+\|\d\|.*\|Login attempt failed for .+\.$
+failregex = ^%(__prefix_line)s(?:https?:\/\/)[^|]+\|[^|]+\|[^|]+\|<ADDR>\|(?:[^|]*\|)*Login attempt failed (?:for|from) <F-USER>[^|]+</F-USER>\.$
ignoreregex =
diff --git a/config/filter.d/exim-common.conf b/config/filter.d/exim-common.conf
index b3b25750..36644e94 100644
--- a/config/filter.d/exim-common.conf
+++ b/config/filter.d/exim-common.conf
@@ -12,7 +12,7 @@ after = exim-common.local
host_info_pre = (?:H=([\w.-]+ )?(?:\(\S+\) )?)?
host_info_suf = (?::\d+)?(?: I=\[\S+\](:\d+)?)?(?: U=\S+)?(?: P=e?smtp)?(?: F=(?:<>|[^@]+@\S+))?\s
host_info = %(host_info_pre)s\[<HOST>\]%(host_info_suf)s
-pid = (?: \[\d+\])?
+pid = (?: \[\d+\]| \w+ exim\[\d+\]:)?
# DEV Notes:
# From exim source code: ./src/receive.c:add_host_info_for_log
diff --git a/config/filter.d/gitlab.conf b/config/filter.d/gitlab.conf
new file mode 100644
index 00000000..0c614ae5
--- /dev/null
+++ b/config/filter.d/gitlab.conf
@@ -0,0 +1,6 @@
+# Fail2Ban filter for Gitlab
+# Detecting unauthorized access to the Gitlab Web portal
+# typically logged in /var/log/gitlab/gitlab-rails/application.log
+
+[Definition]
+failregex = ^: Failed Login: username=<F-USER>.+</F-USER> ip=<HOST>$
diff --git a/config/filter.d/grafana.conf b/config/filter.d/grafana.conf
new file mode 100644
index 00000000..e7f0f420
--- /dev/null
+++ b/config/filter.d/grafana.conf
@@ -0,0 +1,9 @@
+# Fail2Ban filter for Grafana
+# Detecting unauthorized access
+# Typically logged in /var/log/grafana/grafana.log
+
+[Init]
+datepattern = ^t=%%Y-%%m-%%dT%%H:%%M:%%S%%z
+
+[Definition]
+failregex = ^(?: lvl=err?or)? msg="Invalid username or password"(?: uname=(?:"<F-ALT_USER>[^"]+</F-ALT_USER>"|<F-USER>\S+</F-USER>)| error="<F-ERROR>[^"]+</F-ERROR>"| \S+=(?:\S*|"[^"]+"))* remote_addr=<ADDR>$
diff --git a/config/filter.d/guacamole.conf b/config/filter.d/guacamole.conf
index 09b4e7b0..bc6dbea9 100644
--- a/config/filter.d/guacamole.conf
+++ b/config/filter.d/guacamole.conf
@@ -5,21 +5,47 @@
[Definition]
-# Option: failregex
-# Notes.: regex to match the password failures messages in the logfile.
-# Values: TEXT
-#
-failregex = ^.*\nWARNING: Authentication attempt from <HOST> for user "[^"]*" failed\.$
+logging = catalina
+failregex = <L_<logging>/failregex>
+maxlines = <L_<logging>/maxlines>
+datepattern = <L_<logging>/datepattern>
-# Option: ignoreregex
-# Notes.: regex to ignore. If this regex matches, the line is ignored.
-# Values: TEXT
-#
-ignoreregex =
+[L_catalina]
+
+failregex = ^.*\nWARNING: Authentication attempt from <HOST> for user "[^"]*" failed\.$
-# "maxlines" is number of log lines to buffer for multi-line regex searches
maxlines = 2
datepattern = ^%%b %%d, %%ExY %%I:%%M:%%S %%p
^WARNING:()**
- {^LN-BEG} \ No newline at end of file
+ {^LN-BEG}
+
+[L_webapp]
+
+failregex = ^ \[\S+\] WARN \S+ - Authentication attempt from <HOST> for user "<F-USER>[^"]+</F-USER>" failed.
+
+maxlines = 1
+
+datepattern = ^%%H:%%M:%%S.%%f
+
+# DEV Notes:
+#
+# failregex is based on the default pattern given in Guacamole documentation :
+# https://guacamole.apache.org/doc/gug/configuring-guacamole.html#webapp-logging
+#
+# The following logback.xml Guacamole configuration file can then be used accordingly :
+# <configuration>
+# <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+# <file>/var/log/guacamole.log</file>
+# <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+# <fileNamePattern>/var/log/guacamole.%d.log.gz</fileNamePattern>
+# <maxHistory>32</maxHistory>
+# </rollingPolicy>
+# <encoder>
+# <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
+# </encoder>
+# </appender>
+# <root level="info">
+# <appender-ref ref="FILE" />
+# </root>
+# </configuration>
diff --git a/config/filter.d/ignorecommands/apache-fakegooglebot b/config/filter.d/ignorecommands/apache-fakegooglebot
index 3c443251..8351efa2 100755
--- a/config/filter.d/ignorecommands/apache-fakegooglebot
+++ b/config/filter.d/ignorecommands/apache-fakegooglebot
@@ -6,32 +6,43 @@
#
import sys
from fail2ban.server.ipdns import DNSUtils, IPAddr
+from threading import Thread
def process_args(argv):
- if len(argv) != 2:
- raise ValueError("Please provide a single IP as an argument. Got: %s\n"
- % (argv[1:]))
+ if len(argv) - 1 not in (1, 2):
+ raise ValueError("Usage %s ip ?timeout?. Got: %s\n"
+ % (argv[0], argv[1:]))
ip = argv[1]
if not IPAddr(ip).isValid:
raise ValueError("Argument must be a single valid IP. Got: %s\n"
% ip)
- return ip
+ return argv[1:]
google_ips = None
-def is_googlebot(ip):
+def is_googlebot(ip, timeout=55):
import re
- host = DNSUtils.ipToName(ip)
- if not host or not re.match('.*\.google(bot)?\.com$', host):
+ timeout = float(timeout or 0)
+ if timeout:
+ def ipToNameTO(host, ip, timeout):
+ host[0] = DNSUtils.ipToName(ip)
+ host = [None]
+ th = Thread(target=ipToNameTO, args=(host, ip, timeout)); th.daemon=True; th.start()
+ th.join(timeout)
+ host = host[0]
+ else:
+ host = DNSUtils.ipToName(ip)
+
+ if not host or not re.match(r'.*\.google(bot)?\.com$', host):
return False
host_ips = DNSUtils.dnsToIp(host)
return (ip in host_ips)
if __name__ == '__main__': # pragma: no cover
try:
- ret = is_googlebot(process_args(sys.argv))
+ ret = is_googlebot(*process_args(sys.argv))
except ValueError as e:
sys.stderr.write(str(e))
sys.exit(2)
diff --git a/config/filter.d/lighttpd-auth.conf b/config/filter.d/lighttpd-auth.conf
index a68f4f4d..dcf19d3e 100644
--- a/config/filter.d/lighttpd-auth.conf
+++ b/config/filter.d/lighttpd-auth.conf
@@ -3,7 +3,7 @@
[Definition]
-failregex = ^: \((?:http|mod)_auth\.c\.\d+\) (?:password doesn\'t match .* username: .*|digest: auth failed for .*: wrong password|get_password failed), IP: <HOST>\s*$
+failregex = ^\s*(?:: )?\(?(?:http|mod)_auth\.c\.\d+\) (?:password doesn\'t match for (?:\S+|.*?) username:\s+<F-USER>(?:\S+|.*?)</F-USER>\s*|digest: auth failed(?: for\s+<F-ALT_USER>(?:\S+|.*?)</F-ALT_USER>\s*)?: (?:wrong password|uri mismatch \([^\)]*\))|get_password failed),? IP: <HOST>\s*$
ignoreregex =
diff --git a/config/filter.d/monit.conf b/config/filter.d/monit.conf
index b652a1f4..fdaee9c3 100644
--- a/config/filter.d/monit.conf
+++ b/config/filter.d/monit.conf
@@ -8,13 +8,17 @@
# common.local
before = common.conf
+# [DEFAULT]
+# logtype = short
+
[Definition]
_daemon = monit
+_prefix = Warning|HttpRequest
+
# Regexp for previous (accessing monit httpd) and new (access denied) versions
-failregex = ^\[\s*\]\s*error\s*:\s*Warning:\s+Client '<HOST>' supplied (?:unknown user '[^']+'|wrong password for user '[^']*') accessing monit httpd$
- ^%(__prefix_line)s\w+: access denied -- client <HOST>: (?:unknown user '[^']+'|wrong password for user '[^']*'|empty password)$
+failregex = ^%(__prefix_line)s(?:error\s*:\s+)?(?:%(_prefix)s):\s+(?:access denied\s+--\s+)?[Cc]lient '?<HOST>'?(?:\s+supplied|\s*:)\s+(?:unknown user '<F-ALT_USER>[^']+</F-ALT_USER>'|wrong password for user '<F-USER>[^']*</F-USER>'|empty password)
# Ignore login with empty user (first connect, no user specified)
# ignoreregex = %(__prefix_line)s\w+: access denied -- client <HOST>: (?:unknown user '')
diff --git a/config/filter.d/monitorix.conf b/config/filter.d/monitorix.conf
new file mode 100644
index 00000000..ff69f1bc
--- /dev/null
+++ b/config/filter.d/monitorix.conf
@@ -0,0 +1,25 @@
+# Fail2Ban filter for Monitorix (HTTP built-in server)
+#
+
+[INCLUDES]
+
+before = common.conf
+
+[Definition]
+
+_daemon = monitorix-httpd
+
+# Option: failregex
+# Notes.: regex to match the password failures messages in the logfile. The
+# host must be matched by a group named "host". The tag "<HOST>" can
+# be used for standard IP/hostname matching and is only an alias for
+# (?:::f{4,6}:)?(?P<host>\S+)
+# Values: TEXT
+#
+failregex = ^(?:\s+-)?\s*(?:NOTEXIST|AUTHERR|NOTALLOWED) - <ADDR>\b
+
+# Option: ignoreregex
+# Notes.: regex to ignore. If this regex matches, the line is ignored.
+# Values: TEXT
+#
+ignoreregex =
diff --git a/config/filter.d/mssql-auth.conf b/config/filter.d/mssql-auth.conf
new file mode 100644
index 00000000..65bbd917
--- /dev/null
+++ b/config/filter.d/mssql-auth.conf
@@ -0,0 +1,15 @@
+# Fail2Ban filter for failed MSSQL Server authentication attempts
+
+[Definition]
+
+failregex = ^\s*Logon\s+Login failed for user '<F-USER>(?:[^']*|.*)</F-USER>'\. [^'\[]+\[CLIENT: <ADDR>\]$
+
+
+# DEV Notes:
+# Tested with SQL Server 2019 on Ubuntu 18.04
+#
+# Example:
+# 2020-02-24 14:48:55.12 Logon Login failed for user 'root'. Reason: Could not find a login matching the name provided. [CLIENT: 127.0.0.1]
+#
+# Author: Rüdiger Olschewsky
+# \ No newline at end of file
diff --git a/config/filter.d/mysqld-auth.conf b/config/filter.d/mysqld-auth.conf
index 31bd2056..930c9b5a 100644
--- a/config/filter.d/mysqld-auth.conf
+++ b/config/filter.d/mysqld-auth.conf
@@ -3,7 +3,7 @@
#
# To log wrong MySQL access attempts add to /etc/my.cnf in [mysqld]:
# log-error=/var/log/mysqld.log
-# log-warning = 2
+# log-warnings = 2
#
# If using mysql syslog [mysql_safe] has syslog in /etc/my.cnf
@@ -17,7 +17,7 @@ before = common.conf
_daemon = mysqld
-failregex = ^%(__prefix_line)s(?:\d+ |\d{6} \s?\d{1,2}:\d{2}:\d{2} )?\[\w+\] Access denied for user '[^']+'@'<HOST>' (to database '[^']*'|\(using password: (YES|NO)\))*\s*$
+failregex = ^%(__prefix_line)s(?:(?:\d{6}|\d{4}-\d{2}-\d{2})[ T]\s?\d{1,2}:\d{2}:\d{2} )?(?:\d+ )?\[\w+\] (?:\[[^\]]+\] )*Access denied for user '<F-USER>[^']+</F-USER>'@'<HOST>' (to database '[^']*'|\(using password: (YES|NO)\))*\s*$
ignoreregex =
diff --git a/config/filter.d/named-refused.conf b/config/filter.d/named-refused.conf
index 2e14d442..798f66e6 100644
--- a/config/filter.d/named-refused.conf
+++ b/config/filter.d/named-refused.conf
@@ -22,7 +22,7 @@
[Definition]
# Daemon name
-_daemon=named
+_daemon=named(?:-\w+)?
# Shortcuts for easier comprehension of the failregex
@@ -30,15 +30,18 @@ __pid_re=(?:\[\d+\])
__daemon_re=\(?%(_daemon)s(?:\(\S+\))?\)?:?
__daemon_combs_re=(?:%(__pid_re)s?:\s+%(__daemon_re)s|%(__daemon_re)s%(__pid_re)s?:)
+_category = (?!error|info)[\w-]+
+_category_re = (?:%(_category)s: )?
+
# hostname daemon_id spaces
# this can be optional (for instance if we match named native log files)
-__line_prefix=(?:\s\S+ %(__daemon_combs_re)s\s+)?
+__line_prefix=\s*(?:\S+ %(__daemon_combs_re)s\s+)?%(_category_re)s
-prefregex = ^%(__line_prefix)s( error:)?\s*client <HOST>#\S+( \([\S.]+\))?: <F-CONTENT>.+</F-CONTENT>$
+prefregex = ^%(__line_prefix)s(?:(?:error|info):\s*)?client(?: @\S*)? <HOST>#\S+(?: \([\S.]+\))?: <F-CONTENT>.+</F-CONTENT>\s(?:denied|\(NOTAUTH\))\s*$
-failregex = ^(view (internal|external): )?query(?: \(cache\))? '.*' denied\s*$
- ^zone transfer '\S+/AXFR/\w+' denied\s*$
- ^bad zone transfer request: '\S+/IN': non-authoritative zone \(NOTAUTH\)\s*$
+failregex = ^(?:view (?:internal|external): )?query(?: \(cache\))?
+ ^zone transfer
+ ^bad zone transfer request: '\S+/IN': non-authoritative zone
ignoreregex =
diff --git a/config/filter.d/nginx-bad-request.conf b/config/filter.d/nginx-bad-request.conf
new file mode 100644
index 00000000..12c14ab7
--- /dev/null
+++ b/config/filter.d/nginx-bad-request.conf
@@ -0,0 +1,16 @@
+# Fail2Ban filter to match bad requests to nginx
+#
+
+[Definition]
+
+# The request often doesn't contain a method, only some encoded garbage
+# This will also match requests that are entirely empty
+failregex = ^<HOST> - \S+ \[\] "[^"]*" 400
+
+datepattern = {^LN-BEG}%%ExY(?P<_sep>[-/.])%%m(?P=_sep)%%d[T ]%%H:%%M:%%S(?:[.,]%%f)?(?:\s*%%z)?
+ ^[^\[]*\[({DATE})
+ {^LN-BEG}
+
+journalmatch = _SYSTEMD_UNIT=nginx.service + _COMM=nginx
+
+# Author: Jan Przybylak
diff --git a/config/filter.d/nginx-botsearch.conf b/config/filter.d/nginx-botsearch.conf
index 0be895b2..2bd23072 100644
--- a/config/filter.d/nginx-botsearch.conf
+++ b/config/filter.d/nginx-botsearch.conf
@@ -17,7 +17,9 @@ datepattern = {^LN-BEG}%%ExY(?P<_sep>[-/.])%%m(?P=_sep)%%d[T ]%%H:%%M:%%S(?:[.,]
^[^\[]*\[({DATE})
{^LN-BEG}
+journalmatch = _SYSTEMD_UNIT=nginx.service + _COMM=nginx
+
# DEV Notes:
# Based on apache-botsearch filter
#
-# Author: Frantisek Sumsal \ No newline at end of file
+# Author: Frantisek Sumsal
diff --git a/config/filter.d/nginx-http-auth.conf b/config/filter.d/nginx-http-auth.conf
index 93341cd2..71806e85 100644
--- a/config/filter.d/nginx-http-auth.conf
+++ b/config/filter.d/nginx-http-auth.conf
@@ -3,15 +3,32 @@
[Definition]
+mode = normal
-failregex = ^ \[error\] \d+#\d+: \*\d+ user "(?:[^"]+|.*?)":? (?:password mismatch|was not found in "[^\"]*"), client: <HOST>, server: \S*, request: "\S+ \S+ HTTP/\d+\.\d+", host: "\S+"(?:, referrer: "\S+")?\s*$
+mdre-auth = ^\s*\[error\] \d+#\d+: \*\d+ user "(?:[^"]+|.*?)":? (?:password mismatch|was not found in "[^\"]*"), client: <HOST>, server: \S*, request: "\S+ \S+ HTTP/\d+\.\d+", host: "\S+"(?:, referrer: "\S+")?\s*$
+mdre-fallback = ^\s*\[crit\] \d+#\d+: \*\d+ SSL_do_handshake\(\) failed \(SSL: error:\S+(?: \S+){1,3} too (?:long|short)\)[^,]*, client: <HOST>
+
+mdre-normal = %(mdre-auth)s
+mdre-aggressive = %(mdre-auth)s
+ %(mdre-fallback)s
+
+failregex = <mdre-<mode>>
ignoreregex =
datepattern = {^LN-BEG}
+journalmatch = _SYSTEMD_UNIT=nginx.service + _COMM=nginx
+
# DEV NOTES:
+# mdre-auth:
# Based on samples in https://github.com/fail2ban/fail2ban/pull/43/files
# Extensive search of all nginx auth failures not done yet.
#
# Author: Daniel Black
+
+# mdre-fallback:
+# Ban people checking for TLS_FALLBACK_SCSV repeatedly
+# https://stackoverflow.com/questions/28010492/nginx-critical-error-with-ssl-handshaking/28010608#28010608
+# Author: Stephan Orlowsky
+
diff --git a/config/filter.d/nginx-limit-req.conf b/config/filter.d/nginx-limit-req.conf
index e23548ab..2f45e831 100644
--- a/config/filter.d/nginx-limit-req.conf
+++ b/config/filter.d/nginx-limit-req.conf
@@ -44,3 +44,6 @@ failregex = ^\s*\[[a-z]+\] \d+#\d+: \*\d+ limiting requests, excess: [\d\.]+ by
ignoreregex =
datepattern = {^LN-BEG}
+
+journalmatch = _SYSTEMD_UNIT=nginx.service + _COMM=nginx
+
diff --git a/config/filter.d/nsd.conf b/config/filter.d/nsd.conf
index bfd99544..0589c16c 100644
--- a/config/filter.d/nsd.conf
+++ b/config/filter.d/nsd.conf
@@ -22,10 +22,10 @@ _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 <ADDR> TYPE255$
+ ^%(__prefix_line)sinfo: .* from(?: client)? <ADDR> refused, no acl matches\.?$
ignoreregex =
datepattern = {^LN-BEG}Epoch
- {^LN-BEG} \ No newline at end of file
+ {^LN-BEG}
diff --git a/config/filter.d/phpmyadmin-syslog.conf b/config/filter.d/phpmyadmin-syslog.conf
index 5b0862bb..4378bedb 100644
--- a/config/filter.d/phpmyadmin-syslog.conf
+++ b/config/filter.d/phpmyadmin-syslog.conf
@@ -1,4 +1,4 @@
-# Fail2Ban fitler for the phpMyAdmin-syslog
+# Fail2Ban filter for the phpMyAdmin-syslog
#
[INCLUDES]
diff --git a/config/filter.d/postfix.conf b/config/filter.d/postfix.conf
index d1505e32..b374f472 100644
--- a/config/filter.d/postfix.conf
+++ b/config/filter.d/postfix.conf
@@ -12,16 +12,15 @@ before = common.conf
_daemon = postfix(-\w+)?/\w+(?:/smtp[ds])?
_port = (?::\d+)?
+_pref = [A-Z]{4}
prefregex = ^%(__prefix_line)s<mdpr-<mode>> <F-CONTENT>.+</F-CONTENT>$
-mdpr-normal = (?:NOQUEUE: reject:|improper command pipelining after \S+)
-mdre-normal=^RCPT from [^[]*\[<HOST>\]%(_port)s: 55[04] 5\.7\.1\s
- ^RCPT from [^[]*\[<HOST>\]%(_port)s: 45[04] 4\.7\.1 (?:Service unavailable\b|Client host rejected: cannot find your (reverse )?hostname\b)
- ^RCPT from [^[]*\[<HOST>\]%(_port)s: 450 4\.7\.1 (<[^>]*>)?: Helo command rejected: Host not found\b
- ^EHLO from [^[]*\[<HOST>\]%(_port)s: 504 5\.5\.2 (<[^>]*>)?: Helo command rejected: need fully-qualified hostname\b
- ^VRFY from [^[]*\[<HOST>\]%(_port)s: 550 5\.1\.1\s
- ^RCPT from [^[]*\[<HOST>\]%(_port)s: 450 4\.1\.8 (<[^>]*>)?: Sender address rejected: Domain not found\b
+# Extended RE for normal mode to match reject by unknown users or undeliverable address, can be set to empty to avoid this:
+exre-user = |[Uu](?:ser unknown|ndeliverable address)
+
+mdpr-normal = (?:\w+: (?:milter-)?reject:|(?:improper command pipelining|too many errors) after \S+)
+mdre-normal=^%(_pref)s from [^[]*\[<HOST>\]%(_port)s: [45][50][04] [45]\.\d\.\d+ (?:(?:<[^>]*>)?: )?(?:(?:Helo command|(?:Sender|Recipient) address) rejected: )?(?:Service unavailable|(?:Client host|Command|Data command) rejected|Relay access denied|(?:Host|Domain) not found|need fully-qualified hostname|match%(exre-user)s)\b
^from [^[]*\[<HOST>\]%(_port)s:?
mdpr-auth = warning:
@@ -31,13 +30,15 @@ mdre-auth2= ^[^[]*\[<HOST>\]%(_port)s: SASL ((?i)LOGIN|PLAIN|(?:CRAM|DIGEST)-MD5
# Mode "rbl" currently included in mode "normal", but if needed for jail "postfix-rbl" only:
mdpr-rbl = %(mdpr-normal)s
-mdre-rbl = ^RCPT from [^[]*\[<HOST>\]%(_port)s: [45]54 [45]\.7\.1 Service unavailable; Client host \[\S+\] blocked\b
+mdre-rbl = ^%(_pref)s from [^[]*\[<HOST>\]%(_port)s: [45]54 [45]\.7\.1 Service unavailable; Client host \[\S+\] blocked\b
# Mode "rbl" currently included in mode "normal" (within 1st rule)
mdpr-more = %(mdpr-normal)s
mdre-more = %(mdre-normal)s
-mdpr-ddos = lost connection after(?! DATA) [A-Z]+
+# Includes some of the log messages described in
+# <http://www.postfix.org/POSTSCREEN_README.html>.
+mdpr-ddos = (?:lost connection after(?! DATA) [A-Z]+|disconnect(?= from \S+(?: \S+=\d+)* auth=0/(?:[1-9]|\d\d+))|(?:PREGREET \d+|HANGUP) after \S+|COMMAND (?:TIME|COUNT|LENGTH) LIMIT)
mdre-ddos = ^from [^[]*\[<HOST>\]%(_port)s:?
mdpr-extra = (?:%(mdpr-auth)s|%(mdpr-normal)s)
@@ -48,6 +49,8 @@ mdpr-aggressive = (?:%(mdpr-auth)s|%(mdpr-normal)s|%(mdpr-ddos)s)
mdre-aggressive = %(mdre-auth2)s
%(mdre-normal)s
+mdpr-errors = too many errors after \S+
+mdre-errors = ^from [^[]*\[<HOST>\]%(_port)s$
failregex = <mdre-<mode>>
@@ -56,10 +59,17 @@ failregex = <mdre-<mode>>
# Usage example (for jail.local):
# [postfix]
# mode = aggressive
+#
# # or another jail (rewrite filter parameters of jail):
# [postfix-rbl]
# filter = postfix[mode=rbl]
#
+# # jail to match "too many errors", related postconf `smtpd_hard_error_limit`:
+# # (normally included in other modes (normal, more, extra, aggressive), but this jail'd allow to ban on the first message)
+# [postfix-many-errors]
+# filter = postfix[mode=errors]
+# maxretry = 1
+#
mode = more
ignoreregex =
diff --git a/config/filter.d/proftpd.conf b/config/filter.d/proftpd.conf
index a7bd2837..71f2ba73 100644
--- a/config/filter.d/proftpd.conf
+++ b/config/filter.d/proftpd.conf
@@ -1,4 +1,4 @@
-# Fail2Ban fitler for the Proftpd FTP daemon
+# Fail2Ban filter for the Proftpd FTP daemon
#
# Set "UseReverseDNS off" in proftpd.conf to avoid the need for DNS.
# See: http://www.proftpd.org/docs/howto/DNS.html
@@ -14,16 +14,15 @@ before = common.conf
_daemon = proftpd
-__suffix_failed_login = (User not authorized for login|No such user found|Incorrect password|Password expired|Account disabled|Invalid shell: '\S+'|User in \S+|Limit (access|configuration) denies login|Not a UserAlias|maximum login length exceeded).?
+__suffix_failed_login = ([uU]ser not authorized for login|[nN]o such user found|[iI]ncorrect password|[pP]assword expired|[aA]ccount disabled|[iI]nvalid shell: '\S+'|[uU]ser in \S+|[lL]imit (access|configuration) denies login|[nN]ot a UserAlias|[mM]aximum login length exceeded)
-prefregex = ^%(__prefix_line)s%(__hostname)s \(\S+\[<HOST>\]\)[: -]+ <F-CONTENT>(?:USER|SECURITY|Maximum).+</F-CONTENT>$
+prefregex = ^%(__prefix_line)s%(__hostname)s \(\S+\[<HOST>\]\)[: -]+ <F-CONTENT>(?:USER|SECURITY|Maximum) .+</F-CONTENT>$
-failregex = ^USER .*: no such user found from \S+ \[\S+\] to \S+:\S+ *$
- ^USER .* \(Login failed\): %(__suffix_failed_login)s\s*$
- ^SECURITY VIOLATION: .* login attempted\. *$
- ^Maximum login attempts \(\d+\) exceeded *$
+failregex = ^USER <F-USER>\S+|.*?</F-USER>(?: \(Login failed\))?: %(__suffix_failed_login)s
+ ^SECURITY VIOLATION: <F-USER>\S+|.*?</F-USER> login attempted
+ ^Maximum login attempts \(\d+\) exceeded
ignoreregex =
diff --git a/config/filter.d/scanlogd.conf b/config/filter.d/scanlogd.conf
new file mode 100644
index 00000000..d3fe78b0
--- /dev/null
+++ b/config/filter.d/scanlogd.conf
@@ -0,0 +1,17 @@
+# Fail2Ban filter for port scans detected by scanlogd
+
+[INCLUDES]
+
+# Read common prefixes. If any customizations available -- read them from
+# common.local
+before = common.conf
+
+[Definition]
+
+_daemon = scanlogd
+
+failregex = ^%(__prefix_line)s<ADDR>(?::<F-PORT/>)? to \S+ ports\b
+
+ignoreregex =
+
+# Author: Mike Gabriel <mike.gabriel@das-netzwerkteam.de>
diff --git a/config/filter.d/selinux-common.conf b/config/filter.d/selinux-common.conf
index b3e0ae4f..dc9616d2 100644
--- a/config/filter.d/selinux-common.conf
+++ b/config/filter.d/selinux-common.conf
@@ -14,7 +14,7 @@
[Definition]
-failregex = ^type=%(_type)s msg=audit\(:\d+\): (user )?pid=\d+ uid=%(_uid)s auid=%(_auid)s ses=\d+ subj=%(_subj)s msg='%(_msg)s'$
+failregex = ^type=%(_type)s msg=audit\(:\d+\): (?:user )?pid=\d+ uid=%(_uid)s auid=%(_auid)s ses=\d+ subj=%(_subj)s msg='%(_msg)s'(?:\x1D|$)
ignoreregex =
diff --git a/config/filter.d/selinux-ssh.conf b/config/filter.d/selinux-ssh.conf
index 6955094f..0e38eb11 100644
--- a/config/filter.d/selinux-ssh.conf
+++ b/config/filter.d/selinux-ssh.conf
@@ -15,7 +15,9 @@ _subj = (?:unconfined_u|system_u):system_r:sshd_t:s0-s0:c0\.c1023
_exe =/usr/sbin/sshd
_terminal = ssh
-_msg = op=\S+ acct=(?P<_quote_acct>"?)\S+(?P=_quote_acct) exe="%(_exe)s" hostname=(\?|(\d+\.){3}\d+) addr=<HOST> terminal=%(_terminal)s res=failed
+_anygrp = (?!acct=|exe=|addr=|terminal=|res=)\w+=(?:"[^"]+"|\S*)
+
+_msg = (?:%(_anygrp)s )*acct=(?:"<F-USER>[^"]+</F-USER>"|<F-ALT_USER>\S+</F-ALT_USER>) exe="%(_exe)s" (?:%(_anygrp)s )*addr=<ADDR> terminal=%(_terminal)s res=failed
# DEV Notes:
#
diff --git a/config/filter.d/sendmail-auth.conf b/config/filter.d/sendmail-auth.conf
index a370eea2..3fa3c701 100644
--- a/config/filter.d/sendmail-auth.conf
+++ b/config/filter.d/sendmail-auth.conf
@@ -8,9 +8,14 @@ before = common.conf
[Definition]
_daemon = (?:sendmail|sm-(?:mta|acceptingconnections))
+# "\w{14,20}" will give support for IDs from 14 up to 20 characters long
+__prefix_line = %(known/__prefix_line)s(?:\w{14,20}: )?
+addr = (?:IPv6:<IP6>|<IP4>)
-failregex = ^%(__prefix_line)s\w{14}: (\S+ )?\[(?:IPv6:<IP6>|<IP4>)\]( \(may be forged\))?: possible SMTP attack: command=AUTH, count=\d+$
+prefregex = ^<F-MLFID>%(__prefix_line)s</F-MLFID><F-CONTENT>.+</F-CONTENT>$
+failregex = ^(\S+ )?\[%(addr)s\]( \(may be forged\))?: possible SMTP attack: command=AUTH, count=\d+$
+ ^AUTH failure \([^\)]+\):(?: [^:]+:)? (?:authentication failure|user not found): [^,]*, (?:user=<F-USER>(?:\S+|.*?)</F-USER>, )?relay=(?:\S+ )?\[%(addr)s\](?: \(may be forged\))?$
ignoreregex =
journalmatch = _SYSTEMD_UNIT=sendmail.service
diff --git a/config/filter.d/sendmail-reject.conf b/config/filter.d/sendmail-reject.conf
index 985eac8b..41035e5f 100644
--- a/config/filter.d/sendmail-reject.conf
+++ b/config/filter.d/sendmail-reject.conf
@@ -20,19 +20,21 @@ before = common.conf
[Definition]
_daemon = (?:(sm-(mta|acceptingconnections)|sendmail))
+__prefix_line = %(known/__prefix_line)s(?:\w{14,20}: )?
+addr = (?:(?:IPv6:)?<IP6>|<IP4>)
-prefregex = ^<F-MLFID>%(__prefix_line)s(?:\w{14}: )?</F-MLFID><F-CONTENT>.+</F-CONTENT>$
+prefregex = ^<F-MLFID>%(__prefix_line)s</F-MLFID><F-CONTENT>.+</F-CONTENT>$
-cmnfailre = ^ruleset=check_rcpt, arg1=(?P<email><\S+@\S+>), relay=(\S+ )?\[(?:IPv6:<IP6>|<IP4>)\](?: \(may be forged\))?, reject=(550 5\.7\.1 (?P=email)\.\.\. Relaying denied\. (IP name possibly forged \[(\d+\.){3}\d+\]|Proper authentication required\.|IP name lookup failed \[(\d+\.){3}\d+\])|553 5\.1\.8 (?P=email)\.\.\. Domain of sender address \S+ does not exist|550 5\.[71]\.1 (?P=email)\.\.\. (Rejected: .*|User unknown))$
- ^ruleset=check_relay, arg1=(?P<dom>\S+), arg2=(?:IPv6:<IP6>|<IP4>), relay=((?P=dom) )?\[(\d+\.){3}\d+\](?: \(may be forged\))?, reject=421 4\.3\.2 (Connection rate limit exceeded\.|Too many open connections\.)$
- ^rejecting commands from (\S* )?\[(?:IPv6:<IP6>|<IP4>)\] due to pre-greeting traffic after \d+ seconds$
- ^(?:\S+ )?\[(?:IPv6:<IP6>|<IP4>)\]: (?:(?i)expn|vrfy) \S+ \[rejected\]$
+cmnfailre = ^ruleset=check_rcpt, arg1=(?P<email><\S+@\S+>), relay=(\S+ )?\[%(addr)s\](?: \(may be forged\))?, reject=(?:550 5\.7\.1(?: (?P=email)\.\.\.)?(?: Relaying denied\.)? (?:IP name possibly forged \[(\d+\.){3}\d+\]|Proper authentication required\.|IP name lookup failed \[(\d+\.){3}\d+\]|Fix reverse DNS for \S+)|553 5\.1\.8(?: (?P=email)\.\.\.)? Domain of sender address \S+ does not exist|550 5\.[71]\.1 (?P=email)\.\.\. (Rejected: .*|User unknown))$
+ ^ruleset=check_relay(?:, arg\d+=\S*)*, relay=(\S+ )?\[%(addr)s\](?: \(may be forged\))?, reject=421 4\.3\.2 (Connection rate limit exceeded\.|Too many open connections\.)$
+ ^rejecting commands from (\S* )?\[%(addr)s\] due to pre-greeting traffic after \d+ seconds$
+ ^(?:\S+ )?\[%(addr)s\]: (?:(?i)expn|vrfy) \S+ \[rejected\]$
^<[^@]+@[^>]+>\.\.\. No such user here$
- ^<F-NOFAIL>from=<[^@]+@[^>]+></F-NOFAIL>, size=\d+, class=\d+, nrcpts=\d+, bodytype=\w+, proto=E?SMTP, daemon=MTA, relay=\S+ \[(?:IPv6:<IP6>|<IP4>)\]$
+ ^<F-NOFAIL>from=<[^@]+@[^>]+></F-NOFAIL>, size=\d+, class=\d+, nrcpts=\d+, bodytype=\w+, proto=E?SMTP, daemon=MTA, relay=\S+ \[%(addr)s\]$
mdre-normal =
-mdre-extra = ^(?:\S+ )?\[(?:IPv6:<IP6>|<IP4>)\](?: \(may be forged\))? did not issue (?:[A-Z]{4}[/ ]?)+during connection to M(?:TA|SP)(?:-\w+)?$
+mdre-extra = ^(?:\S+ )?\[%(addr)s\](?: \(may be forged\))? did not issue \S+ during connection
mdre-aggressive = %(mdre-extra)s
@@ -48,7 +50,7 @@ mode = normal
ignoreregex =
-journalmatch = _SYSTEMD_UNIT=sendmail.service
+journalmatch = SYSLOG_IDENTIFIER=sm-mta + _SYSTEMD_UNIT=sendmail.service
# DEV NOTES:
#
diff --git a/config/filter.d/softethervpn.conf b/config/filter.d/softethervpn.conf
new file mode 100644
index 00000000..f7e7c0c3
--- /dev/null
+++ b/config/filter.d/softethervpn.conf
@@ -0,0 +1,9 @@
+# Fail2Ban filter for SoftEtherVPN
+# Detecting unauthorized access to SoftEtherVPN
+# typically logged in /usr/local/vpnserver/security_log/*/sec.log, or in syslog, depending on configuration
+
+[INCLUDES]
+before = common.conf
+
+[Definition]
+failregex = ^%(__prefix_line)s(?:(?:\([\d\-]+ [\d:.]+\) )?<SECURITY_LOG>: )?Connection "[^"]+": User authentication failed. The user name that has been provided was "<F-USER>(?:[^"]+|.+)</F-USER>", from <ADDR>\.$
diff --git a/config/filter.d/sogo-auth.conf b/config/filter.d/sogo-auth.conf
index 48221dc0..4155f89e 100644
--- a/config/filter.d/sogo-auth.conf
+++ b/config/filter.d/sogo-auth.conf
@@ -4,7 +4,7 @@
[Definition]
-failregex = ^ sogod \[\d+\]: SOGoRootPage Login from '<HOST>' for user '.*' might not have worked( - password policy: \d* grace: -?\d* expire: -?\d* bound: -?\d*)?\s*$
+failregex = ^ sogod \[\d+\]: SOGoRootPage Login from '<HOST>(?:,[^']*)?' for user '[^']*' might not have worked( - password policy: \d* grace: -?\d* expire: -?\d* bound: -?\d*)?\s*$
ignoreregex = "^<ADDR>"
diff --git a/config/filter.d/sshd.conf b/config/filter.d/sshd.conf
index 60efead7..d5d189b0 100644
--- a/config/filter.d/sshd.conf
+++ b/config/filter.d/sshd.conf
@@ -25,7 +25,7 @@ __pref = (?:(?:error|fatal): (?:PAM: )?)?
__suff = (?: (?:port \d+|on \S+|\[preauth\])){0,3}\s*
__on_port_opt = (?: (?:port \d+|on \S+)){0,2}
# close by authenticating user:
-__authng_user = (?: authenticating user <F-USER>\S+|.+?</F-USER>)?
+__authng_user = (?: (?:invalid|authenticating) user <F-USER>\S+|.*?</F-USER>)?
# for all possible (also future) forms of "no matching (cipher|mac|MAC|compression method|key exchange method|host key type) found",
# see ssherr.c for all possible SSH_ERR_..._ALG_MATCH errors.
@@ -40,45 +40,68 @@ prefregex = ^<F-MLFID>%(__prefix_line)s</F-MLFID>%(__pref)s<F-CONTENT>.+</F-CONT
cmnfailre = ^[aA]uthentication (?:failure|error|failed) for <F-USER>.*</F-USER> from <HOST>( via \S+)?%(__suff)s$
^User not known to the underlying authentication module for <F-USER>.*</F-USER> from <HOST>%(__suff)s$
- ^Failed publickey for invalid user <F-USER>(?P<cond_user>\S+)|(?:(?! from ).)*?</F-USER> from <HOST>%(__on_port_opt)s(?: ssh\d*)?(?(cond_user): |(?:(?:(?! from ).)*)$)
- ^Failed \b(?!publickey)\S+ for (?P<cond_inv>invalid user )?<F-USER>(?P<cond_user>\S+)|(?(cond_inv)(?:(?! from ).)*?|[^:]+)</F-USER> from <HOST>%(__on_port_opt)s(?: ssh\d*)?(?(cond_user): |(?:(?:(?! from ).)*)$)
+ <cmnfailre-failed-pub-<publickey>>
+ ^Failed <cmnfailed> for (?P<cond_inv>invalid user )?<F-USER>(?P<cond_user>\S+)|(?(cond_inv)(?:(?! from ).)*?|[^:]+)</F-USER> from <HOST>%(__on_port_opt)s(?: ssh\d*)?(?(cond_user): |(?:(?:(?! from ).)*)$)
^<F-USER>ROOT</F-USER> LOGIN REFUSED FROM <HOST>
^[iI](?:llegal|nvalid) user <F-USER>.*?</F-USER> from <HOST>%(__suff)s$
- ^User <F-USER>.+</F-USER> from <HOST> not allowed because not listed in AllowUsers%(__suff)s$
- ^User <F-USER>.+</F-USER> from <HOST> not allowed because listed in DenyUsers%(__suff)s$
- ^User <F-USER>.+</F-USER> from <HOST> not allowed because not in any group%(__suff)s$
+ ^User <F-USER>\S+|.*?</F-USER> from <HOST> not allowed because not listed in AllowUsers%(__suff)s$
+ ^User <F-USER>\S+|.*?</F-USER> from <HOST> not allowed because listed in DenyUsers%(__suff)s$
+ ^User <F-USER>\S+|.*?</F-USER> from <HOST> not allowed because not in any group%(__suff)s$
^refused connect from \S+ \(<HOST>\)
^Received <F-MLFFORGET>disconnect</F-MLFFORGET> from <HOST>%(__on_port_opt)s:\s*3: .*: Auth fail%(__suff)s$
- ^User <F-USER>.+</F-USER> from <HOST> not allowed because a group is listed in DenyGroups%(__suff)s$
- ^User <F-USER>.+</F-USER> from <HOST> not allowed because none of user's groups are listed in AllowGroups%(__suff)s$
+ ^User <F-USER>\S+|.*?</F-USER> from <HOST> not allowed because a group is listed in DenyGroups%(__suff)s$
+ ^User <F-USER>\S+|.*?</F-USER> from <HOST> not allowed because none of user's groups are listed in AllowGroups%(__suff)s$
^<F-NOFAIL>%(__pam_auth)s\(sshd:auth\):\s+authentication failure;</F-NOFAIL>(?:\s+(?:(?:logname|e?uid|tty)=\S*)){0,4}\s+ruser=<F-ALT_USER>\S*</F-ALT_USER>\s+rhost=<HOST>(?:\s+user=<F-USER>\S*</F-USER>)?%(__suff)s$
- ^(error: )?maximum authentication attempts exceeded for <F-USER>.*</F-USER> from <HOST>%(__on_port_opt)s(?: ssh\d*)?%(__suff)s$
- ^User <F-USER>.+</F-USER> not allowed because account is locked%(__suff)s
- ^<F-MLFFORGET>Disconnecting</F-MLFFORGET>: Too many authentication failures(?: for <F-USER>.+?</F-USER>)?%(__suff)s$
+ ^maximum authentication attempts exceeded for <F-USER>.*</F-USER> from <HOST>%(__on_port_opt)s(?: ssh\d*)?%(__suff)s$
+ ^User <F-USER>\S+|.*?</F-USER> not allowed because account is locked%(__suff)s
+ ^<F-MLFFORGET>Disconnecting</F-MLFFORGET>(?: from)?(?: (?:invalid|authenticating)) user <F-USER>\S+</F-USER> <HOST>%(__on_port_opt)s:\s*Change of username or service not allowed:\s*.*\[preauth\]\s*$
+ ^Disconnecting: Too many authentication failures(?: for <F-USER>\S+|.*?</F-USER>)?%(__suff)s$
^<F-NOFAIL>Received <F-MLFFORGET>disconnect</F-MLFFORGET></F-NOFAIL> from <HOST>%(__on_port_opt)s:\s*11:
- ^<F-NOFAIL>Connection <F-MLFFORGET>closed</F-MLFFORGET></F-NOFAIL> by%(__authng_user)s <HOST><mdrp-<mode>-suff-onclosed>
- ^<F-MLFFORGET><F-NOFAIL>Accepted \w+</F-NOFAIL></F-MLFFORGET> for <F-USER>\S+</F-USER> from <HOST>(?:\s|$)
+ <mdre-<mode>-other>
+ ^<F-MLFFORGET><F-MLFGAINED>Accepted \w+</F-MLFGAINED></F-MLFFORGET> for <F-USER>\S+</F-USER> from <HOST>(?:\s|$)
+
+cmnfailed-any = \S+
+cmnfailed-ignore = \b(?!publickey)\S+
+cmnfailed-invalid = <cmnfailed-ignore>
+cmnfailed-nofail = (?:<F-NOFAIL>publickey</F-NOFAIL>|\S+)
+cmnfailed = <cmnfailed-<publickey>>
mdre-normal =
# used to differentiate "connection closed" with and without `[preauth]` (fail/nofail cases in ddos mode)
-mdrp-normal-suff-onclosed = (?:%(__suff)s|\s*)$
+mdre-normal-other = ^<F-NOFAIL><F-MLFFORGET>(Connection (?:closed|reset)|Disconnected)</F-MLFFORGET></F-NOFAIL> (?:by|from)%(__authng_user)s <HOST>(?:%(__suff)s|\s*)$
mdre-ddos = ^Did not receive identification string from <HOST>
- ^Connection <F-MLFFORGET>reset</F-MLFFORGET> by <HOST>
- ^Connection <F-MLFFORGET>closed</F-MLFFORGET> by%(__authng_user)s <HOST>%(__on_port_opt)s\s+\[preauth\]\s*$
+ ^kex_exchange_identification: (?:read: )?(?:[Cc]lient sent invalid protocol identifier|[Cc]onnection (?:closed by remote host|reset by peer))
+ ^Bad protocol version identification '.*' from <HOST>
^<F-NOFAIL>SSH: Server;Ltype:</F-NOFAIL> (?:Authname|Version|Kex);Remote: <HOST>-\d+;[A-Z]\w+:
^Read from socket failed: Connection <F-MLFFORGET>reset</F-MLFFORGET> by peer
-mdrp-ddos-suff-onclosed = %(__on_port_opt)s\s*$
+ ^banner exchange: Connection from <HOST><__on_port_opt>: invalid format
+# same as mdre-normal-other, but as failure (without <F-NOFAIL> with [preauth] and with <F-NOFAIL> on no preauth phase as helper to identify address):
+mdre-ddos-other = ^<F-MLFFORGET>(Connection (?:closed|reset)|Disconnected)</F-MLFFORGET> (?:by|from)%(__authng_user)s <HOST>%(__on_port_opt)s\s+\[preauth\]\s*$
+ ^<F-NOFAIL><F-MLFFORGET>(Connection (?:closed|reset)|Disconnected)</F-MLFFORGET></F-NOFAIL> (?:by|from)%(__authng_user)s <HOST>(?:%(__on_port_opt)s|\s*)$
-mdre-extra = ^Received <F-MLFFORGET>disconnect</F-MLFFORGET> from <HOST>%(__on_port_opt)s:\s*14: No supported authentication methods available
+mdre-extra = ^Received <F-MLFFORGET>disconnect</F-MLFFORGET> from <HOST>%(__on_port_opt)s:\s*14: No(?: supported)? authentication methods available
^Unable to negotiate with <HOST>%(__on_port_opt)s: no matching <__alg_match> found.
^Unable to negotiate a <__alg_match>
^no matching <__alg_match> found:
-mdrp-extra-suff-onclosed = %(mdrp-normal-suff-onclosed)s
+# part of mdre-ddos-other, but user name is supplied (invalid/authenticating) on [preauth] phase only:
+mdre-extra-other = ^<F-MLFFORGET>Disconnected</F-MLFFORGET>(?: from)?(?: (?:invalid|authenticating)) user <F-USER>\S+|.*?</F-USER> <HOST>%(__on_port_opt)s \[preauth\]\s*$
mdre-aggressive = %(mdre-ddos)s
%(mdre-extra)s
-mdrp-aggressive-suff-onclosed = %(mdrp-ddos-suff-onclosed)s
+# mdre-extra-other is fully included within mdre-ddos-other:
+mdre-aggressive-other = %(mdre-ddos-other)s
+
+# Parameter "publickey": nofail (default), invalid, any, ignore
+publickey = nofail
+# consider failed publickey for invalid users only:
+cmnfailre-failed-pub-invalid = ^Failed publickey for invalid user <F-USER>(?P<cond_user>\S+)|(?:(?! from ).)*?</F-USER> from <HOST>%(__on_port_opt)s(?: ssh\d*)?(?(cond_user): |(?:(?:(?! from ).)*)$)
+# consider failed publickey for valid users too (don't need RE, see cmnfailed):
+cmnfailre-failed-pub-any =
+# same as invalid, but consider failed publickey for valid users too, just as no failure (helper to get IP and user-name only, see cmnfailed):
+cmnfailre-failed-pub-nofail = <cmnfailre-failed-pub-invalid>
+# don't consider failed publickey as failures (don't need RE, see cmnfailed):
+cmnfailre-failed-pub-ignore =
cfooterre = ^<F-NOFAIL>Connection from</F-NOFAIL> <HOST>
@@ -104,8 +127,6 @@ maxlines = 1
journalmatch = _SYSTEMD_UNIT=sshd.service + _COMM=sshd
-datepattern = {^LN-BEG}
-
# DEV Notes:
#
# "Failed \S+ for .*? from <HOST>..." failregex uses non-greedy catch-all because
diff --git a/config/filter.d/traefik-auth.conf b/config/filter.d/traefik-auth.conf
new file mode 100644
index 00000000..8022fee1
--- /dev/null
+++ b/config/filter.d/traefik-auth.conf
@@ -0,0 +1,76 @@
+# Fail2ban filter configuration for traefik :: auth
+# used to ban hosts, that were failed through traefik
+#
+# Author: CrazyMax
+#
+# To use 'traefik-auth' filter you have to configure your Traefik instance to write
+# the access logs as describe in https://docs.traefik.io/configuration/logs/#access-logs
+# into a log file on host and specifiy users for Basic Authentication
+# https://docs.traefik.io/configuration/entrypoints/#basic-authentication
+#
+# Example:
+#
+# version: "3.2"
+#
+# services:
+# traefik:
+# image: traefik:latest
+# command:
+# - "--loglevel=INFO"
+# - "--accesslog=true"
+# - "--accessLog.filePath=/var/log/access.log"
+# # - "--accessLog.filters.statusCodes=400-499"
+# - "--defaultentrypoints=http,https"
+# - "--entryPoints=Name:http Address::80"
+# - "--entryPoints=Name:https Address::443 TLS"
+# - "--docker.domain=example.com"
+# - "--docker.watch=true"
+# - "--docker.exposedbydefault=false"
+# - "--api=true"
+# - "--api.dashboard=true"
+# ports:
+# - target: 80
+# published: 80
+# protocol: tcp
+# mode: host
+# - target: 443
+# published: 443
+# protocol: tcp
+# mode: host
+# labels:
+# - "traefik.enable=true"
+# - "traefik.port=8080"
+# - "traefik.backend=traefik"
+# - "traefik.frontend.rule=Host:traefik.example.com"
+# - "traefik.frontend.auth.basic.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/"
+# volumes:
+# - "/var/log/traefik:/var/log"
+# - "/var/run/docker.sock:/var/run/docker.sock"
+# restart: always
+#
+
+[Definition]
+
+# Parameter "method" can be used to specifiy request method
+req-method = \S+
+# Usage example (for jail.local):
+# filter = traefik-auth[req-method="GET|POST|HEAD"]
+
+failregex = ^<HOST> \- <usrre-<mode>> \[\] \"(?:<req-method>) [^\"]+\" 401\b
+
+ignoreregex =
+
+# Parameter "mode": normal (default), ddos or aggressive
+# Usage example (for jail.local):
+# [traefik-auth]
+# mode = aggressive
+# # or another jail (rewrite filter parameters of jail):
+# [traefik-auth-ddos]
+# filter = traefik-auth[mode=ddos]
+#
+mode = normal
+
+# part of failregex matches user name (must be available in normal mode, must be empty in ddos mode, and both for aggressive mode):
+usrre-normal = (?!- )<F-USER>\S+</F-USER>
+usrre-ddos = -
+usrre-aggressive = <F-USER>\S+</F-USER> \ No newline at end of file
diff --git a/config/filter.d/znc-adminlog.conf b/config/filter.d/znc-adminlog.conf
new file mode 100644
index 00000000..8faa25e3
--- /dev/null
+++ b/config/filter.d/znc-adminlog.conf
@@ -0,0 +1,34 @@
+# Fail2Ban filter for ZNC (requires adminlog module)
+#
+# to use this module, enable the adminlog module from within ZNC and point
+# logpath to its logfile (e.g. /var/lib/znc/moddata/adminlog/znc.log).
+
+[DEFAULT]
+
+logtype = file
+
+[Definition]
+
+_daemon = znc
+
+# Prefix for different logtype (file, journal):
+#
+__prefix_file = (?:\[\]\s+)?
+__prefix_short = (?:\S+\s+%(_daemon)s\[\d+\]:)\s+
+__prefix_journal = %(__prefix_short)s
+
+__prefix_line = <__prefix_<logtype>>
+
+failregex = ^%(__prefix_line)s\[[^]]+\] failed to login from <ADDR>
+
+ignoreregex =
+
+journalmatch = _SYSTEMD_UNIT=znc.service + _COMM=znc
+
+# DEV Notes:
+# Log format is: [<DATE+TIME>] [<USERNAME>] <ACTION> from <ADDR>
+# [2018-10-27 01:40:17] [girst] connected to ZNC from 1.2.3.4
+# [2018-10-27 01:40:21] [girst] disconnected from ZNC from 1.2.3.4
+# [2018-10-27 01:40:55] [girst] failed to login from 1.2.3.4
+#
+# Author: Tobias Girstmair (//gir.st/)
diff --git a/config/filter.d/zoneminder.conf b/config/filter.d/zoneminder.conf
index cc82755a..8e8ed432 100644
--- a/config/filter.d/zoneminder.conf
+++ b/config/filter.d/zoneminder.conf
@@ -5,17 +5,23 @@ before = apache-common.conf
[Definition]
-# pattern: [Wed Apr 27 23:12:07.736196 2016] [:error] [pid 2460] [client 10.1.1.1:47296] WAR [Login denied for user "test"], referer: https://zoneminderurl/index.php
-#
+# patterns: [Mon Mar 28 16:50:49.522240 2016] [:error] [pid 1795] [client 10.1.1.1:50700] WAR [Login denied for user "username1"], referer: https://zoneminder/
+# [Sun Mar 28 16:53:00.472693 2021] [php7:notice] [pid 11328] [client 10.1.1.1:39568] ERR [Could not retrieve user test details], referer: https://zm/
+# [Sun Mar 28 16:59:14.150625 2021] [php7:notice] [pid 11336] [client 10.1.1.1:39654] ERR [Login denied for user "john"], referer: https://zm/
#
# Option: failregex
-# Notes.: regex to match the password failure messages in the logfile.
+# Notes.: regex to match the login failure and non-existent user error messages in the logfile.
+
+prefregex = ^%(_apache_error_client)s (?:ERR|WAR) <F-CONTENT>\[(?:Login denied|Could not retrieve).*</F-CONTENT>$
-failregex = ^%(_apache_error_client)s WAR \[Login denied for user "[^"]*"\]
+failregex = ^\[Login denied for user "<F-USER>[^"]*</F-USER>"\]
+ ^\[Could not retrieve user <F-USER>\S*</F-USER>
ignoreregex =
# Notes:
-# Tested on Zoneminder 1.29.0
+# Tested on Zoneminder 1.29 and 1.35.21
+#
+# Zoneminder versions > 1.3x use "ERR" and < 1.3x use "WAR" level logs, so i've kept both for compatibility reasons
#
# Author: John Marzella
diff --git a/config/jail.conf b/config/jail.conf
index a6f2ac5a..b2fb7ec0 100644
--- a/config/jail.conf
+++ b/config/jail.conf
@@ -52,7 +52,7 @@ before = paths-debian.conf
# to prevent "clever" botnets calculate exact time IP can be unbanned again:
#bantime.rndtime =
-# "bantime.maxtime" is the max number of seconds using the ban time can reach (don't grows further)
+# "bantime.maxtime" is the max number of seconds using the ban time can reach (doesn't grow further)
#bantime.maxtime =
# "bantime.factor" is a coefficient to calculate exponent growing of the formula or common multiplier,
@@ -60,14 +60,14 @@ before = paths-debian.conf
# grows by 1, 2, 4, 8, 16 ...
#bantime.factor = 1
-# "bantime.formula" used by default to calculate next value of ban time, default value bellow,
+# "bantime.formula" used by default to calculate next value of ban time, default value below,
# the same ban time growing will be reached by multipliers 1, 2, 4, 8, 16, 32...
#bantime.formula = ban.Time * (1<<(ban.Count if ban.Count<20 else 20)) * banFactor
#
# more aggressive example of formula has the same values only for factor "2.0 / 2.885385" :
#bantime.formula = ban.Time * math.exp(float(ban.Count+1)*banFactor)/math.exp(1*banFactor)
-# "bantime.multipliers" used to calculate next value of ban time instead of formula, coresponding
+# "bantime.multipliers" used to calculate next value of ban time instead of formula, corresponding
# previously ban count and given "bantime.factor" (for multipliers default is 1);
# following example grows ban time by 1, 2, 4, 8, 16 ... and if last ban count greater as multipliers count,
# always used last multiplier (64 in example), for factor '1' and original ban time 600 - 10.6 hours
@@ -77,7 +77,7 @@ before = paths-debian.conf
#bantime.multipliers = 1 5 30 60 300 720 1440 2880
# "bantime.overalljails" (if true) specifies the search of IP in the database will be executed
-# cross over all jails, if false (dafault), only current jail of the ban IP will be searched
+# cross over all jails, if false (default), only current jail of the ban IP will be searched
#bantime.overalljails = false
# --------------------
@@ -85,6 +85,7 @@ before = paths-debian.conf
# "ignoreself" specifies whether the local resp. own IP addresses should be ignored
# (default is true). Fail2ban will not ban a host which matches such addresses.
#ignoreself = true
+
# "ignoreip" can be a list of IP addresses, CIDR masks or DNS hosts. Fail2ban
# will not ban a host which matches an address in this list. Several addresses
# can be defined using space (and/or comma) separator.
@@ -106,6 +107,9 @@ findtime = 10m
# "maxretry" is the number of failures before a host get banned.
maxretry = 5
+# "maxmatches" is the number of matches stored in ticket (resolvable via tag <matches> in actions).
+maxmatches = %(maxretry)s
+
# "backend" specifies the backend used to get files modification.
# Available options are "pyinotify", "gamin", "polling", "systemd" and "auto".
# This option can be overridden in each jail as well.
@@ -205,28 +209,37 @@ banaction = iptables-multiport
banaction_allports = iptables-allports
# The simplest action to take: ban only
-action_ = %(banaction)s[name=%(__name__)s, port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
+action_ = %(banaction)s[port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
# ban & send an e-mail with whois report to the destemail.
-action_mw = %(banaction)s[name=%(__name__)s, port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
- %(mta)s-whois[name=%(__name__)s, sender="%(sender)s", dest="%(destemail)s", protocol="%(protocol)s", chain="%(chain)s"]
+action_mw = %(action_)s
+ %(mta)s-whois[sender="%(sender)s", dest="%(destemail)s", protocol="%(protocol)s", chain="%(chain)s"]
# ban & send an e-mail with whois report and relevant log lines
# to the destemail.
-action_mwl = %(banaction)s[name=%(__name__)s, port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
- %(mta)s-whois-lines[name=%(__name__)s, sender="%(sender)s", dest="%(destemail)s", logpath=%(logpath)s, chain="%(chain)s"]
+action_mwl = %(action_)s
+ %(mta)s-whois-lines[sender="%(sender)s", dest="%(destemail)s", logpath="%(logpath)s", chain="%(chain)s"]
# See the IMPORTANT note in action.d/xarf-login-attack for when to use this action
#
# ban & send a xarf e-mail to abuse contact of IP address and include relevant log lines
# to the destemail.
-action_xarf = %(banaction)s[name=%(__name__)s, port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
- xarf-login-attack[service=%(__name__)s, sender="%(sender)s", logpath=%(logpath)s, port="%(port)s"]
+action_xarf = %(action_)s
+ xarf-login-attack[service=%(__name__)s, sender="%(sender)s", logpath="%(logpath)s", port="%(port)s"]
+
+# ban & send a notification to one or more of the 50+ services supported by Apprise.
+# See https://github.com/caronc/apprise/wiki for details on what is supported.
+#
+# You may optionally over-ride the default configuration line (containing the Apprise URLs)
+# by using 'apprise[config="/alternate/path/to/apprise.cfg"]' otherwise
+# /etc/fail2ban/apprise.conf is sourced for your supported notification configuration.
+# action = %(action_)s
+# apprise
# ban IP on CloudFlare & send an e-mail with whois report and relevant log lines
# to the destemail.
action_cf_mwl = cloudflare[cfuser="%(cfemail)s", cftoken="%(cfapikey)s"]
- %(mta)s-whois-lines[name=%(__name__)s, sender="%(sender)s", dest="%(destemail)s", logpath=%(logpath)s, chain="%(chain)s"]
+ %(mta)s-whois-lines[sender="%(sender)s", dest="%(destemail)s", logpath="%(logpath)s", chain="%(chain)s"]
# Report block via blocklist.de fail2ban reporting service API
#
@@ -236,21 +249,7 @@ action_cf_mwl = cloudflare[cfuser="%(cfemail)s", cftoken="%(cfapikey)s"]
# in your `jail.local` globally (section [DEFAULT]) or per specific jail section (resp. in
# corresponding jail.d/my-jail.local file).
#
-action_blocklist_de = blocklist_de[email="%(sender)s", service=%(filter)s, apikey="%(blocklist_de_apikey)s", agent="%(fail2ban_agent)s"]
-
-# Report ban via badips.com, and use as blacklist
-#
-# See BadIPsAction docstring in config/action.d/badips.py for
-# documentation for this action.
-#
-# NOTE: This action relies on banaction being present on start and therefore
-# should be last action defined for a jail.
-#
-action_badips = badips.py[category="%(__name__)s", banaction="%(banaction)s", agent="%(fail2ban_agent)s"]
-#
-# Report ban via badips.com (uses action.d/badips.conf for reporting only)
-#
-action_badips_report = badips[category="%(__name__)s", agent="%(fail2ban_agent)s"]
+action_blocklist_de = blocklist_de[email="%(sender)s", service="%(__name__)s", apikey="%(blocklist_de_apikey)s", agent="%(fail2ban_agent)s"]
# Report ban via abuseipdb.com.
#
@@ -347,7 +346,7 @@ maxretry = 2
port = http,https
logpath = %(apache_access_log)s
maxretry = 1
-ignorecommand = %(ignorecommands_dir)s/apache-fakegooglebot <ip>
+ignorecommand = %(fail2ban_confpath)s/filter.d/ignorecommands/apache-fakegooglebot <ip>
[apache-modsecurity]
@@ -367,12 +366,15 @@ maxretry = 1
[openhab-auth]
filter = openhab
-action = iptables-allports[name=NoAuthFailures]
+banaction = %(banaction_allports)s
logpath = /opt/openhab/logs/request.log
+# To use more aggressive http-auth modes set filter parameter "mode" in jail.local:
+# normal (default), aggressive (combines all), auth or fallback
+# See "tests/files/logs/nginx-http-auth" or "filter.d/nginx-http-auth.conf" for usage example and details.
[nginx-http-auth]
-
+# mode = normal
port = http,https
logpath = %(nginx_error_log)s
@@ -388,13 +390,14 @@ logpath = %(nginx_error_log)s
port = http,https
logpath = %(nginx_error_log)s
-maxretry = 2
-[nginx-forbidden]
+[nginx-bad-request]
+port = http,https
+logpath = %(nginx_access_log)s
+[nginx-forbidden]
port = http,https
logpath = %(nginx_error_log)s
-maxretry = 10
# Ban attackers that try to use PHP's URL-fopen() functionality
# through GET/POST variables. - Experimental, with more than a year
@@ -479,11 +482,13 @@ backend = %(syslog_backend)s
port = http,https
logpath = /var/log/tomcat*/catalina.out
+#logpath = /var/log/guacamole.log
[monit]
#Ban clients brute-forcing the monit gui login
port = 2812
logpath = /var/log/monit
+ /var/log/monit.log
[webmin-auth]
@@ -744,8 +749,8 @@ logpath = /var/log/named/security.log
[nsd]
port = 53
-action = %(banaction)s[name=%(__name__)s-tcp, port="%(port)s", protocol="tcp", chain="%(chain)s", actname=%(banaction)s-tcp]
- %(banaction)s[name=%(__name__)s-udp, port="%(port)s", protocol="udp", chain="%(chain)s", actname=%(banaction)s-udp]
+action_ = %(default/action_)s[name=%(__name__)s-tcp, protocol="tcp"]
+ %(default/action_)s[name=%(__name__)s-udp, protocol="udp"]
logpath = /var/log/nsd.log
@@ -756,9 +761,8 @@ logpath = /var/log/nsd.log
[asterisk]
port = 5060,5061
-action = %(banaction)s[name=%(__name__)s-tcp, port="%(port)s", protocol="tcp", chain="%(chain)s", actname=%(banaction)s-tcp]
- %(banaction)s[name=%(__name__)s-udp, port="%(port)s", protocol="udp", chain="%(chain)s", actname=%(banaction)s-udp]
- %(mta)s-whois[name=%(__name__)s, dest="%(destemail)s"]
+action_ = %(default/action_)s[name=%(__name__)s-tcp, protocol="tcp"]
+ %(default/action_)s[name=%(__name__)s-udp, protocol="udp"]
logpath = /var/log/asterisk/messages
maxretry = 10
@@ -766,16 +770,22 @@ maxretry = 10
[freeswitch]
port = 5060,5061
-action = %(banaction)s[name=%(__name__)s-tcp, port="%(port)s", protocol="tcp", chain="%(chain)s", actname=%(banaction)s-tcp]
- %(banaction)s[name=%(__name__)s-udp, port="%(port)s", protocol="udp", chain="%(chain)s", actname=%(banaction)s-udp]
- %(mta)s-whois[name=%(__name__)s, dest="%(destemail)s"]
+action_ = %(default/action_)s[name=%(__name__)s-tcp, protocol="tcp"]
+ %(default/action_)s[name=%(__name__)s-udp, protocol="udp"]
logpath = /var/log/freeswitch.log
maxretry = 10
+# enable adminlog; it will log to a file inside znc's directory by default.
+[znc-adminlog]
+
+port = 6667
+logpath = /var/lib/znc/moddata/adminlog/znc.log
+
+
# To log wrong MySQL access attempts add to /etc/my.cnf in [mysqld] or
# equivalent section:
-# log-warning = 2
+# log-warnings = 2
#
# for syslog (daemon facility)
# [mysqld_safe]
@@ -791,6 +801,14 @@ logpath = %(mysql_log)s
backend = %(mysql_backend)s
+[mssql-auth]
+# Default configuration for Microsoft SQL Server for Linux
+# See the 'mssql-conf' manpage how to change logpath or port
+logpath = /var/opt/mssql/log/errorlog
+port = 1433
+filter = mssql-auth
+
+
# Log wrong MongoDB auth (for details see filter 'filter.d/mongodb-auth.conf')
[mongodb-auth]
# change port when running with "--shardsvr" or "--configsvr" runtime operation
@@ -846,11 +864,31 @@ logpath = /var/log/ejabberd/ejabberd.log
[counter-strike]
logpath = /opt/cstrike/logs/L[0-9]*.log
-# Firewall: http://www.cstrike-planet.com/faq/6
tcpport = 27030,27031,27032,27033,27034,27035,27036,27037,27038,27039
udpport = 1200,27000,27001,27002,27003,27004,27005,27006,27007,27008,27009,27010,27011,27012,27013,27014,27015
-action = %(banaction)s[name=%(__name__)s-tcp, port="%(tcpport)s", protocol="tcp", chain="%(chain)s", actname=%(banaction)s-tcp]
- %(banaction)s[name=%(__name__)s-udp, port="%(udpport)s", protocol="udp", chain="%(chain)s", actname=%(banaction)s-udp]
+action_ = %(default/action_)s[name=%(__name__)s-tcp, port="%(tcpport)s", protocol="tcp"]
+ %(default/action_)s[name=%(__name__)s-udp, port="%(udpport)s", protocol="udp"]
+
+[softethervpn]
+port = 500,4500
+protocol = udp
+logpath = /usr/local/vpnserver/security_log/*/sec.log
+
+[gitlab]
+port = http,https
+logpath = /var/log/gitlab/gitlab-rails/application.log
+
+[grafana]
+port = http,https
+logpath = /var/log/grafana/grafana.log
+
+[bitwarden]
+port = http,https
+logpath = /home/*/bwdata/logs/identity/Identity/log.txt
+
+[centreon]
+port = http,https
+logpath = /var/log/centreon/login.log
# consider low maxretry and a long bantime
# nobody except your own Nagios server should ever probe nrpe
@@ -884,7 +922,8 @@ filter = apache-pass[knocking_url="%(knocking_url)s"]
logpath = %(apache_access_log)s
blocktype = RETURN
returntype = DROP
-action = %(action_)s[blocktype=%(blocktype)s, returntype=%(returntype)s]
+action = %(action_)s[blocktype=%(blocktype)s, returntype=%(returntype)s,
+ actionstart_on_demand=false, actionrepair_on_unban=true]
bantime = 1h
maxretry = 1
findtime = 1
@@ -893,8 +932,8 @@ findtime = 1
[murmur]
# AKA mumble-server
port = 64738
-action = %(banaction)s[name=%(__name__)s-tcp, port="%(port)s", protocol=tcp, chain="%(chain)s", actname=%(banaction)s-tcp]
- %(banaction)s[name=%(__name__)s-udp, port="%(port)s", protocol=udp, chain="%(chain)s", actname=%(banaction)s-udp]
+action_ = %(default/action_)s[name=%(__name__)s-tcp, protocol="tcp"]
+ %(default/action_)s[name=%(__name__)s-udp, protocol="udp"]
logpath = /var/log/mumble-server/mumble-server.log
@@ -930,3 +969,21 @@ backend = %(syslog_backend)s
port = http,https
logpath = %(apache_error_log)s
+[traefik-auth]
+# to use 'traefik-auth' filter you have to configure your Traefik instance,
+# see `filter.d/traefik-auth.conf` for details and service example.
+port = http,https
+logpath = /var/log/traefik/access.log
+
+[scanlogd]
+logpath = %(syslog_local0)s
+banaction = %(banaction_allports)s
+
+[monitorix]
+port = 8080
+logpath = /var/log/monitorix-httpd
+
+[dante]
+port = 1080
+logpath = %(syslog_daemon)s
+
diff --git a/config/paths-common.conf b/config/paths-common.conf
index 7383cafe..4f6a5f71 100644
--- a/config/paths-common.conf
+++ b/config/paths-common.conf
@@ -91,6 +91,3 @@ mysql_log = %(syslog_daemon)s
mysql_backend = %(default_backend)s
roundcube_errors_log = /var/log/roundcube/errors
-
-# Directory with ignorecommand scripts
-ignorecommands_dir = /etc/fail2ban/filter.d/ignorecommands
diff --git a/config/paths-debian.conf b/config/paths-debian.conf
index e096f972..1f5ea37d 100644
--- a/config/paths-debian.conf
+++ b/config/paths-debian.conf
@@ -26,3 +26,5 @@ exim_main_log = /var/log/exim4/mainlog
# was in debian squeezy but not in wheezy
# /etc/proftpd/proftpd.conf (SystemLog)
proftpd_log = /var/log/proftpd/proftpd.log
+
+roundcube_errors_log = /var/log/roundcube/errors.log