Compare commits

...

108 commits

Author SHA1 Message Date
Sergey G. Brester
557e7eecf9
Merge pull request #4133 from B1gG/master
Update jail.conf.5 documentation for action tags
2026-05-11 10:29:15 +02:00
sebres
adfffe5267 merge gh-4164: new tag <jail.name> available in ignorecommand or actions (same as <name> there) 2026-04-11 14:49:13 +02:00
sebres
8d3f5048ef filter.d/postfix.conf - extended prefregex to capture username in postfix SASL failures;
closes gh-4165
2026-04-11 14:42:57 +02:00
sebres
507d0468cd implements RFE #4164: new tag <jail.name> available in ignorecommand or actions (same as <name> there) 2026-04-11 14:25:12 +02:00
Sergey G. Brester
732dc86ef3
simplify timestamp conversion in systemd journal reader;
also handle DST flag correct, so doesn't mistakenly consider the UTC timestamp as local timestamp, see #2882
2026-04-10 16:44:43 +02:00
Sergey G. Brester
0a8356dd31
jail.conf: removed dead link in warning comment
removed dead URL (reference to 690-fail2ban-+-dns-fail.html in blog) from the warning comment about UDP spoofing;
closes gh-4162.
2026-03-26 21:03:29 +01:00
sebres
8be17b0981 Merge branch 'gh-4142--nginx-ssl-aggressive': extends filter.d/nginx-http-auth.conf - modes fallback and aggressive match more SSL failures by SSL_do_handshake or SSL_read (gh-4142, gh-2881) 2026-02-12 14:04:55 +01:00
sebres
c03a6204c1 ChangeLog update 2026-02-12 14:03:07 +01:00
sebres
eb7ed973ef filter.d/nginx-http-auth.conf: modes fallback and aggressive extended to match more SSL failures, see gh-4142 (amend to gh-2881) 2026-02-12 13:53:57 +01:00
Sergey G. Brester
3b8033b337
Merge pull request #2537 from viiru-/improve-systemd-service
Improve systemd service
2026-02-11 15:08:35 +01:00
Sergey G. Brester
243876e60a
Merge pull request #4143 from caronc/apprise-tag-support-v2
Small amend to #4141, adjust Apprise URL doc source and action comments
2026-02-02 03:07:01 +01:00
Sergey G. Brester
3bead7c011
Update comments in action
jail.conf shall be unmodified (jails are ideally in jail.local or jail.d/*.conf)
2026-02-02 03:04:25 +01:00
Chris Caron
05f6ad4fcc small fix to url for Apprise doc source 2026-02-01 20:52:06 -05:00
Sergey G. Brester
81b906303c
Merge pull request #4141 from caronc/apprise-tag-support
Improved Apprise integration (support tagging)
2026-02-02 02:44:46 +01:00
Sergey G. Brester
025adbf485
fixes apprise action configuration examples 2026-02-02 02:37:26 +01:00
Sergey G. Brester
f457cf8131
ChangeLog adjusted
move from compat to enhancement section
2026-02-02 02:31:19 +01:00
Chris Caron
1a802bee93 further feedback from PR 2026-02-01 20:18:07 -05:00
Chris Caron
36e28359ed fixed spelling 2026-02-01 19:51:26 -05:00
Chris Caron
8a8afefd70 applied updates based on PR feedback 2026-02-01 19:45:44 -05:00
Chris Caron
8afd0c8956 updated ChangeLog to reflect Apprise updates 2026-01-28 21:55:04 -05:00
Chris Caron
6cdb5738ec improved apprise fail2ban integration (support tagging) 2026-01-28 21:49:42 -05:00
sebres
9887ee4412 CI: bump python version (3.15.0-alpha.5) 2026-01-23 21:28:52 +01:00
sebres
8506e4a41d action.d/nftables.conf - fixed for SELinux without execmem permission, rewrite capturing with grep -P using grep -E or sed;
(PCRE-JIT by `grep -P` may cause SELinux denial for execmem), closes gh-4137
2026-01-23 21:23:58 +01:00
Gerardo Gonzalez
481be8790a
Update jail.conf.5 documentation for action tags
Missing tags added:
family - IP address family string
ip-rev - Reverse DNS PTR record
ip-host - Hostname of the IP
fid - Failure/ticket ID
bantime - Effective ban duration
bancount - Number of times IP has been banned
restored - Flag for restored tickets (0 or 1)
F-* - Free-form filter match tags
raw-ticket - Raw ticket representation
jail.banned - Currently banned count
jail.banned_total - Total ban count
jail.found - Current failure count
jail.found_total - Total failure count
2026-01-07 19:31:21 +01:00
sebres
948e923589 Merge fix for #4126 (branch 'gh-4126--py-3.15')
refactor module loading to use exec_module: deprecated load_module is removed in py-3.15;
closes gh-4126
2026-01-01 21:56:15 +01:00
Sergey G. Brester
247667c9c2
refactor loading of SMTP action module in tests (deprecated load_module removed in v.3.15) 2026-01-01 21:49:12 +01:00
Sergey G. Brester
7528fce11b
refactor module loading to use exec_module: load_module is deprecated;
closes gh-4126
2026-01-01 21:43:27 +01:00
Sergey G. Brester
edaf8ef19f
GHA-CI: update python 3.14 + added 3.15 2026-01-01 21:26:22 +01:00
Sergey G. Brester
74981e4c13
Merge pull request #3254 from evanlinde/master
New filter for XRDP
2025-12-07 01:19:01 +01:00
Sergey G. Brester
45453826a3
small amend with missing newline 2025-12-07 01:18:04 +01:00
Sergey G. Brester
2f0e05a0d7
Merge branch 'master' into master 2025-12-07 01:14:39 +01:00
sebres
ef65652671 filter.d/apache-badbots.conf, filter.d/apache-fakegooglebot.conf - regexs fixed to match lines with vhost in accesslog;
closes gh-1594
2025-11-28 22:27:06 +01:00
sebres
bfafd12c59 filter.d/apache-badbots.conf, filter.d/apache-fakegooglebot.conf - rewrite apache access-log REs more strict (remove catch-alls) 2025-11-28 22:16:23 +01:00
Sergey G. Brester
7c2bda4977
Fix image size for IPv6 logo in README
Updated image tag in README to use 'style' attribute.
2025-11-24 23:14:47 +01:00
sebres
3f78f1520b fixed typo in comparison by build of stream from filter options (see #4066) 2025-10-28 21:34:00 +01:00
Sergey G. Brester
7bac839603
Merge pull request #4069 from sebres/init-param-to-cond-section
Setting of blocktype="DROP" via jail doesn't apply for IPv6 chain
2025-09-24 18:23:44 +02:00
Sergey G. Brester
d0b94c147e
Update ChangeLog 2025-09-24 18:22:06 +02:00
Sergey G. Brester
070d49e09c
man/jail.conf.5 - update docu 2025-09-24 18:18:38 +02:00
Sergey G. Brester
dda4aa7d2d
Merge pull request #4075 from para-do-x/froxlor-auth
Froxlor auth update
2025-09-24 16:58:27 +02:00
para-do-x
ad9aba5871
Update ChangeLog gh4075 2025-09-24 18:43:39 +04:00
sebres
13563fd09b combine both REs to single RE, no prefregex needed here 2025-09-24 16:23:05 +02:00
sebres
a9401233dd code review, make it backwards compatible to logging type=1 (as suggested in https://github.com/fail2ban/fail2ban/issues/2926#issuecomment-774780120); use by default type=2 2025-09-24 16:09:42 +02:00
para-do-x
1379a262f6 Update froxlor-auth testfile 2025-09-24 15:59:19 +02:00
para-do-x
abdd0d4b25 Update jail.conf for froxlor-auth
Changed logpath to syslog_user for froxlor-auth
2025-09-24 15:59:18 +02:00
para-do-x
897b21a4c5 Update froxlor-auth.conf
updated the regex to the new logging situation for froxlor.
2025-09-24 15:59:17 +02:00
sebres
65668b8ed8 filter.d/postfix.conf - modes ddos and aggressive extended to match rate limit exceeded for connection or message delivery request rates;
closes gh-3265;
closes gh-4073;
2025-09-23 12:18:45 +02:00
sebres
2856092709 filter.d/postfix.conf - use common prefix instead of NOQUEUE for all modes, outside of mdpr-<mode> in prefregex (amend to gh-4072) 2025-09-18 15:01:05 +02:00
Sergey G. Brester
2ac7e1284f
Merge pull request #4072 from ulm/postfix-ddos
filter.d/postfix.conf: Add optional "NOQUEUE:" to mdpr-ddos
2025-09-18 14:35:35 +02:00
Ulrich Müller
0fee8dbe92 filter.d/postfix.conf: Add optional "NOQUEUE:" to mdpr-ddos
The current regex doesn't match the following log entry, seen with
Postfix 3.10.2:

Sep 17 18:19:20 mxhost postfix/smtpd[12345]: NOQUEUE: lost connection after CONNECT from unknown[192.0.2.25]
Sep 17 18:19:20 mxhost postfix/smtpd[12345]: disconnect from unknown[192.0.2.25] commands=0/0
2025-09-18 08:23:45 +02:00
Sergey G. Brester
6c47bf6461
Merge pull request #4068 from billfor/xarf
fix `dig` to filter out warnings and prevent them from being injected as emails
2025-09-15 17:23:32 +02:00
sebres
9534bdac37 filter.d/nginx-http-auth.conf: filter rewritten and extended:
- with `prefregex` to capture content of error only (bypass common prefix and suffix, like server, request, host, referrer);
  - to match PAM authentication failures (gh-4071)
2025-09-15 16:14:22 +02:00
Sergey G. Brester
a8875c36b8
Merge pull request #4070 from yizhao1/fix
clientreadertestcase.py: set correct config dir for testReadStockJailFilterComplete
2025-09-12 14:51:14 +02:00
Yi Zhao
9f26da3cf8 clientreadertestcase.py: set correct config dir for testReadStockJailFilterComplete
In test case testReadStockJailFilterComplete, set configuration
directory to CONFIG_DIR (/etc/fail2ban/filter.d on the target) instead
of the hardcoded "config" directory. Otherwise, the config files will
not be found during runtime testing.

Signed-off-by: Yi Zhao <yi.zhao@windriver.com>
2025-09-12 12:53:45 +08:00
sebres
5beee494a3 allow to overwrite conditional parameters only direct from jail, for example
`banaction = iptables-ipset[blocktype="...", blocktype?family=inet6="..."]`
2025-09-11 23:11:45 +02:00
sebres
3fd3454146 if parameter supplied to the config, overwrite also conditional init options (from init?... section) 2025-09-11 19:39:06 +02:00
sebres
ce8cc5d261 test illustrating the issue with blocktype="DROP" for IPv6 chain (supplying init parameter to action doesn't overwrite the value in conditional section) 2025-09-11 16:44:06 +02:00
Sergey G. Brester
4539e6719c
Update ChangeLog 2025-09-10 20:19:34 +02:00
Sergey G. Brester
85cfb81782
lets see an error (with debug messages) in debug case 2025-09-10 20:04:10 +02:00
bill
3d23a44bb1 fix dig to filter out warnings from email address capture 2025-09-10 13:27:30 -04:00
Sergey G. Brester
77efe3b40c
Merge pull request #4020 from billfor/sendmail
Update sendmail-reject.conf
2025-09-02 19:46:57 +02:00
sebres
26b91862fc introduces a parameter mta_dname (default \S+) to allow more complex REs to match custom MTA daemon names (e.g. with spaces etc) 2025-09-02 19:41:40 +02:00
sebres
10b12e8c57 reorder 2 tests belonging together 2025-09-02 19:11:05 +02:00
sebres
13876e93ad fixes the inconsistency with F-MLFID ("ID" matched by (?:\w{14,20}: )? is optional in message); simplify PR 2025-09-02 19:11:04 +02:00
bill
70d7fd0fdd update the test for lost input channel with real ip 2025-09-02 12:54:42 -04:00
bill
9e72e78f34 filter.d/sendmail-reject.conf: support BSD log format. match user unknown messages. add aggressive mode for lost input channel and relaying denied messages 2025-09-01 22:34:53 -04:00
sebres
912e3c81a2 removes mistaken return in quiet case for set jail attempt command 2025-09-01 20:12:07 +02:00
sebres
c54d505dea small amend (info with date pattern before debug message with regex) 2025-09-01 18:10:43 +02:00
sebres
6ac181f559 improve logging of date pattern (count of default templates added, info if it's filtered or used pre-match) 2025-09-01 18:03:09 +02:00
sebres
52399e6ef1 amend to #2351: providing the attempt via fail2bans protocol (Pickle, client command, etc) must follow ignore facilities (shall be ignored if matches ignoreip, ignoreself, ignorecommand etc) 2025-08-26 18:03:46 +02:00
sebres
c9e1a1b087 silence warning "Unknown distribution option: 'test_suite'", seems not work anymore (2.x only?) - test suite shall be invoked using bin/fail2ban-testcases 2025-08-23 22:22:20 +02:00
sebres
a055568500 GHA: update python 3.14.0-rc.2 2025-08-23 22:10:55 +02:00
sebres
0265df854e silence skipping tests output for python versions that basically can not have the modules 2025-08-23 22:00:03 +02:00
sebres
a3d181c973 filter.d/dovecot.conf: new matches in aggressive mode:
- new variant for `no auth attempts in X secs` with `Login aborted` and `(no_auth_attempts)`;
- covered `disconnected during TLS handshake` with `no application protocol` and `no shared cipher`.
2025-08-23 20:22:08 +02:00
sebres
002719dca4 ChangeLog update 2025-08-23 20:18:59 +02:00
sebres
c26fda9dbb filter.d/dovecot.conf: new matches in aggressive mode:
- new variant for `no auth attempts in X secs` with `Login aborted` and `(no_auth_attempts)`;
- covered `disconnected during TLS handshake` with `no application protocol` and `no shared cipher`.
2025-08-23 20:16:40 +02:00
sebres
bdb5d99906 Log Repeal Ban instead of Unban on stop action, jail or fail2ban, because the tickets are "unbanned" temporary (till restart);
closes gh-4057
2025-08-19 11:37:01 +02:00
sebres
4e22c20559 fixes ignoreip prefix file:// - it shall resolve absolute file name (starting with /) unless it starts with ./;
relative paths are based relative the working dir;
to use it relative current config root (normally `/etc/fail2ban`), one can use interpolation `%(fail2ban_confpath)s`, e.g.:
  file://%(fail2ban_confpath)s/ignore-ipaddr-file
2025-08-12 23:46:10 +02:00
sebres
3ce6f344e3 fixes beautifier get ignoreip (explicit convert to string) 2025-08-12 23:26:42 +02:00
Sergey G. Brester
bf4903538d
update ChangeLog (enhancement from #3291) 2025-08-08 10:29:02 +02:00
Sergey G. Brester
77ba28bae1
Merge pull request #3291 from ttyS4/patch-1
nftables.conf - add support for cidr notation and address ranges
2025-08-08 10:23:08 +02:00
Sergey G. Brester
dc3268ce5d
servertestcase.py: adjust test coverage 2025-08-08 10:16:01 +02:00
Sergey G. Brester
eb80b895d1
provides flags interval as addr_options now 2025-08-08 10:10:40 +02:00
Bill
6120a731d9
update nginx limit-req filter again (#4048)
amend to #4047 - removes unused ngx_limit_con_zones parameter.
2025-08-04 21:16:26 +02:00
Sergey G. Brester
e16e982a45
Merge pull request #4047 from billfor/nginx
Update nginx-limit-req filter (extended to ban hosts failed by limit connection in ngx_http_limit_conn_module);
closes gh-3674
2025-08-04 11:34:35 +02:00
Sergey G. Brester
dd58d440bc
Update ChangeLog 2025-08-04 11:32:10 +02:00
Sergey G. Brester
e6516fd2b3
combine 2 REs to single regex
closes gh-3674
2025-08-04 11:24:51 +02:00
bill
0a91bf69a5 add filter for delayed requests and connection limiting 2025-08-04 00:27:45 -04:00
sebres
d86a7aecca amend to #3979: removed mistaken double pipes in group matches 2025-07-31 17:38:28 +02:00
sebres
ff3eca1d61 * Merge pull request #3527 from vafgoettlich/master
(partial merge, only postfix-backend)
2025-07-24 11:17:05 +02:00
sebres
0b255a8723 Merge pull request #3527 from vafgoettlich/master
(partial merge, only postfix-backend)
2025-07-24 11:14:03 +02:00
Sergey G. Brester
793d0c6555
Merge pull request #4037 from kusaka-0107/fix/asterisk-conf-regex
filter.d/asterisk: fix regex to match "No matching endpoint found" with retry info (like `after X tries in Y ms`)
2025-07-20 15:17:17 +02:00
Sergey G. Brester
7bb86822d0
Update ChangeLog 2025-07-20 15:15:38 +02:00
Sergey G. Brester
6d3bfa8781
revert RE back, but relive the end-anchor a bit (ignore any text without single quote, so also preventing false match by injection on foreign data) 2025-07-20 15:04:15 +02:00
177ac
b309cf6b3c Add test line 2025-07-20 18:06:33 +09:00
177ac
e97df4672a filter.d/asterisk: fix regex to match "No matching endpoint found" with retry info 2025-07-20 18:05:35 +09:00
sebres
1c2ace2958 GHA: update python 3.14.0-beta.4 2025-07-13 01:08:50 +02:00
sebres
b710d5b6c7 filter.d/sendmail-reject.conf - also recognize "Domain of sender address ... does not resolve";
closes gh-4035
2025-07-13 01:03:53 +02:00
sebres
dc899e438f avoid error "Unable to get failures" by stop (if file gets removed from filter, but filter already entered getFailures for the file);
closes gh-4032
2025-07-07 01:04:35 +02:00
Ulrich Goettlich
7d6b1a4c3b Fixed startup on debian bookworm (using systemd as sshd_backend) 2023-06-13 09:00:38 +02:00
Csillag Tamas
05575de1f1
nftables.conf - add support for cidr notation
Currently when trying to add an address like: 141.98.11.0/24 it fails with:

fail2ban.utils          [720]: ERROR   7fe8c36f6630 -- exec: nft add element inet f2b-table addr-set-custom \{ 141.98.11.0/24 \}
fail2ban.utils          [720]: ERROR   7fe8c36f6630 -- stderr: "Error: You must add 'flags interval' to your set declaration if you want to add prefix elements"

After adding 'flags interval' one can ban ranges now as expected.
2022-05-30 14:05:18 +02:00
evanlinde
5a0224ff0e
Use potentially faster regex for username match 2022-04-08 09:52:52 -05:00
evanlinde
64983ecc29
Make added date pattern specific to xrdp's format 2022-04-08 09:48:35 -05:00
Sergey G. Brester
6a2d2aa97a
capture user, add datepattern (anchored exact main format and fail2ban defaults) 2022-04-08 09:55:44 +02:00
Evan Linde
11768a97e9 add filter for xrdp 2022-04-07 21:34:33 -05:00
Evan Linde
2224b3db4a extend date regex to include xrdp's %Y%m%d-%H:%M:%S format 2022-04-07 20:51:20 -05:00
Arto Jantunen
f376da4bec Set StateDirectory
This automatically handles permissions for /var/lib/fail2ban when run as a
non-root user.
2020-10-02 16:18:24 +03:00
Arto Jantunen
60b136333e Use RuntimeDirectory to create /run/fail2ban
Instead of the duplicated tmpfiles + ExecStartPre. This way the lifetime of
that directory becomes fixed, and also User is automatically respected for
the ownership of the directory (making it easy to run fail2ban as a
non-root user, like it was with at least the Debian init script).
2020-10-02 16:18:22 +03:00
Arto Jantunen
3359845242 Stop setting PIDFile, useless when Type=simple
This has been unneeded since commit 528a7a5ab which converted this from
Type=forking to Type=simple.
2020-10-02 16:17:34 +03:00
51 changed files with 602 additions and 153 deletions

View file

@ -22,7 +22,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.8, 3.9, '3.10', '3.11', '3.12', '3.13', '3.14.0-alpha.6', pypy3.11]
python-version: [3.8, 3.9, '3.10', '3.11', '3.12', '3.13', '3.14', '3.15.0-alpha.5', pypy3.11]
fail-fast: false
# Steps represent a sequence of tasks that will be executed as part of the job
steps:

View file

@ -24,6 +24,10 @@ ver. 1.1.1-dev-1 (20??/??/??) - development nightly edition
in systemd module (see https://github.com/systemd/python-systemd/issues/143)
* fixes `systemd` causing "too many open files" error for a lot of journal files and large amount of systemd jails
(see new parameter `rotated` below, gh-3391);
* passing of arguments from jails to action or filter will affect conditional section too (gh-4069),
e. g. setting `blocktype="DROP"` via jail for action would now apply for IPv4 and IPv6 chains,
to submit different `blocktype` for IPv4 and IPv6 from jail, one can pass them like in this example:
`banaction = iptables-ipset[blocktype="...", blocktype?family=inet6="..."]`
* `jail.conf`:
- default banactions need to be specified in `paths-*.conf` (maintainer level) now
- since stock fail2ban includes `paths-debian.conf` by default, banactions are `nftables`
@ -33,10 +37,20 @@ ver. 1.1.1-dev-1 (20??/??/??) - development nightly edition
* `paths-debian.conf`:
- default banactions are `nftables`
- sshd backend switched to `systemd` (gh-3292)
- postfix backend switched to `systemd` (gh-3527)
* `action.d/firewallcmd-ipset.conf`:
- rename `ipsettype` to `ipsetbackend` (gh-2620), parameter `ipsettype` will be used now to the real set type (gh-3760)
* `action.d/nftables.conf`:
- action fixed for SELinux without execmem permission, rewrite capturing with `grep -P` using `grep -E` or `sed`
(PCRE-JIT by `grep -P` may cause SELinux denial for execmem, see gh-4137)
* `action.d/xarf-login-attack.conf` - ignore errors or warnings in output of `dig` provided as comment (gh-4068)
* `filter.d/apache-badbots.conf`, `filter.d/apache-fakegooglebot.conf`:
- regexs rewritten more strict (removed catch-alls, etc);
- regexs fixed to match lines with vhost in accesslog (gh-1594)
* `filter.d/apache-noscript.conf` - consider new log-format with "AH02811: stderr from /..." (gh-3900)
* `filter.d/apache-overflows.conf` - consider AH10244: invalid URI path (gh-3778, gh-3900)
* `filter.d/asterisk.conf` - fixed RE for "no matching endpoint" with retry info (like `after X tries in Y ms`) at end,
loosening of end anchor (ignore any simple text tokens at end if no single quote found), gh-4037
* `filter.d/exim.conf`:
- several rules of mode `normal` moved to new mode `more`, because of too risky handling (gh-3940),
thereby mode `aggressive` is not affected, because it fully includes mode `more` now;
@ -45,18 +59,28 @@ ver. 1.1.1-dev-1 (20??/??/??) - development nightly edition
FreeSWITCH log line prefix has changed in newer versions (gh-3143)
* `filter.d/lighttpd-auth.conf` - fixed regex (if failures generated by systemd-journal), bypass several prefixes now (gh-3955)
* `filter.d/postfix.conf`:
- extended `prefregex` to capture username in postfix SASL failures (gh-4165)
- consider CONNECT and other rejected commands as a valid `_pref` (gh-3800)
- default `_daemon` in prefix-line is loosened - can match everything starting with word postfix, like `postfix-example.com/smtpd` (gh-3297)
- add optional `NOQUEUE:` prefix to ddos regex (gh-4072)
- internal parameter `_pref` is renamed to `_cmd`, `_pref` matches now optional prefix like `NOQUEUE: ` etc
- modes `ddos` and `aggressive` extended to match `rate limit exceeded` for connection or message delivery request rates (gh-3265, gh-4073)
* `filter.d/dropbear.conf`:
- recognizes extra pid/timestamp if logged into stdout/journal, added `journalmatch` (gh-3597)
- failregex extended to match different format of "Exit before auth" message (gh-3791)
* `filter.d/recidive.conf` - restore possibility to set jail name in the filter, _jailname is positive now (gh-3769)
* `filter.d/roundcube-auth.conf` - improved RE better matching log format of roundcube version 1.4+ (gh-3816)
* `filter.d/sendmail-reject.conf`: (gh-4020)
- support `<F-MLFID>` for BSD-style logfiles
- add match for `User unknown` to default
- the relay field may not always have a hostname before the ip address
- mode `aggressive` enables match for `lost input channel` and `Cannot resolve PTR record`
* `filter.d/sshd.conf`:
- adapted to conform possible new daemon name sshd-session, since OpenSSH 9.8
several log messages will be tagged with as originating from a process named "sshd-session" rather than "sshd" (gh-3782)
- `ddos` and `aggressive` modes: regex extended for timeout before authentication (optional connection from part, gh-3907)
* `filter.d/vsftpd.conf` - fixed regex (if failures generated by systemd-journal, gh-3954)
* `filter.d/froxlor-auth.conf` - updated the regex to the new logging situation for froxlor and changed logpath in jail.conf (gh-4075).
### New Features and Enhancements
* backend `systemd` extended with new parameter `rotated` (default `false`, as prevention against "too many open files"),
@ -75,16 +99,30 @@ ver. 1.1.1-dev-1 (20??/??/??) - development nightly edition
by first ban (and automatically reloaded by update after small latency to avoid expensive stats check on every compare);
the entries inside the file can be separated by comma, space or new line with optional comments (text following chars
`#` or `;` after space or newline would be ignored up to next newline)
* `action.d/apprise.conf` - updated to support tagging and other command line args (gh-4141)
* `action.d/*-ipset.conf`:
- parameter `ipsettype` to set type of ipset, e. g. hash:ip, hash:net, etc (gh-3760)
* `action.d/iptables.conf` - action and few derivatives of it extended to handle multiple chains,
e. g. would also accept `chain = INPUT,FORWARD` (gh-3909)
* `action.d/nftables.conf` (gh-3291):
- new parameter `addr_options` for addr-set (default `flags interval\;`, allows to store CIDR or address ranges);
can be set to empty value to create simple addresses set (restore previous behavior).
* `action.d/firewallcmd-rich-*.conf` - fixed incorrect quoting, disabling port variable expansion
by substitution of rich rule (gh-3815)
* `filter.d/dovecot.conf` - add support for latest Dovecot 2.4 release (gh-4016)
* `filter.d/dovecot.conf`:
- add support for latest Dovecot 2.4 release (gh-4016)
- mode `aggressive` covered new variant for `no auth attempts in X secs` with `Login aborted` and `(no_auth_attempts)`
- mode `aggressive` extended to match `disconnected during TLS handshake` with `no application protocol` and `no shared cipher`
* `filter.d/nginx-http-auth.conf`:
- extended with `prefregex` to capture content of error only (bypass common prefix and suffix, like server, request, host, referrer);
- extended to match PAM authentication failures (gh-4071)
- modes `fallback` and `aggressive` extended to match more SSL failures by SSL_do_handshake or SSL_read (gh-4142, gh-2881)
* `filter.d/nginx-limit-req.conf` - extended to ban hosts failed by limit connection in ngx_http_limit_conn_module (gh-3674, gh-4047)
* `filter.d/proxmox.conf` - add support to Proxmox Web GUI (gh-2966)
* `filter.d/openvpn.conf` - new filter and jail for openvpn recognizing failed TLS handshakes (gh-2702)
* `filter.d/sendmail-reject.conf` - also recognize "Domain of sender address ... does not resolve" (gh-4035)
* `filter.d/vaultwarden.conf` - new filter and jail for Vaultwarden (gh-3979)
* `filter.d/xrdp.conf` - new filter for XRDP, an open source RDP server (gh-3254)
* `fail2ban-regex` extended with new option `-i` or `--invert` to output not-matched lines by `-o` or `--out` (gh-4001)

View file

@ -18,7 +18,7 @@ attempts, it cannot eliminate the risk presented by weak authentication.
Set up services to use only two factor, or public/private authentication
mechanisms if you really want to protect services.
<img src="http://www.worldipv6launch.org/wp-content/themes/ipv6/downloads/World_IPv6_launch_logo.svg" height="52pt"/> | Since v0.10 fail2ban supports the matching of IPv6 addresses.
<img src="http://www.worldipv6launch.org/wp-content/themes/ipv6/downloads/World_IPv6_launch_logo.svg" style="height:52pt;"/> | Since v0.10 fail2ban supports the matching of IPv6 addresses.
------|------
This README is a quick introduction to Fail2Ban. More documentation, FAQ, and HOWTOs

View file

@ -2,7 +2,61 @@
#
# Author: Chris Caron <lead2gold@gmail.com>
#
# ban & send a notification to one or more of the 120+ services supported by
# Apprise.
# - See https://appriseit.com/services/ for details on what is supported.
# - See https://appriseit.com/getting-started/configuration/ for information
# on how to prepare an Apprise configuration file.
#
# This plugin requires that Apprise is installed on your system:
#
# pip install apprise
#
# Breakdown:
# config provide a path to an Apprise Config file
# The default is /etc/fail2ban/apprise.conf if not provided.
# Both YAML and TEXT formats are supported.
# You can even point your configuration to an Apprise API
# endpoint.
#
# args Provide additional arguments to support the Apprise CLI.
# See https://appriseit.com/cli/usage/ for additional options.
# the --tag (-g) is incredibly useful for integrating with
# fail2ban as you can exclusively have it target specific
# notifications this way.
#
# Config Example #1: Simple
# 1. Create a /etc/fail2ban/apprise.conf
# ```
# # /etc/fail2ban/apprise.conf
# fail2ban=mailto://user:pass@example.com
# ```
# 2 In jail:
# ```
# action = %(action_)s
# apprise[args='--tag fail2ban']
# ```
#
# Config Example #2: YAML an Custom path
# 1. Create a /etc/fail2ban/apprise.conf
# ```
# # /etc/fail2ban/apprise.yaml
# urls:
# - mailto://user:pass@example.com:
# tags: f2b
# ```
# 2. In jail:
# ```
# action = %(action_)s
# apprise[config='/etc/fail2ban/apprise.yaml',args='--tag f2b']
# ```
#
# Config Example #3: Apprise API
# 1. In jail:
# ```
# action = %(action_)s
# apprise[config='http://apprise.example.ca/get/mykey',args='-g f2b']
# ```
[Definition]
@ -45,5 +99,9 @@ actionunban =
# Define location of the default apprise configuration file to use
#
config = /etc/fail2ban/apprise.conf
# Support passing in arguments for example: "-g fail2ban"
#
apprise = apprise -c "<config>"
args =
#
apprise = apprise -c "<config>" <args>

View file

@ -53,9 +53,9 @@ _nft_for_proto-multiport-iter = for proto in $(echo '<protocol>' | sed 's/,/ /g'
_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_get_handle_id = sed -nE 's/.*@<addr_set>\s+.*\s+\#\s*(handle\s+[0-9]+)$/\1/p'
_nft_add_set = <nftables> add set <table_family> <table> <addr_set> \{ type <addr_type>\; \}
_nft_add_set = <nftables> add set <table_family> <table> <addr_set> \{ type <addr_type>\;<addr_options> \}
<_nft_for_proto-<type>-iter>
<nftables> add rule <table_family> <table> <chain> %(rule_stat)s
<_nft_for_proto-<type>-done>
@ -67,7 +67,7 @@ _nft_del_set = { %(_nft_list)s | %(_nft_get_handle_id)s; } | while read -r hdl;
# 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+'; } || {
_nft_shutdown_table = { <nftables> list table <table_family> <table> | grep -qE '^\s+set\s+'; } || {
<nftables> delete table <table_family> <table>
}
@ -197,6 +197,11 @@ addr_set = addr-set-<name>
# Values: [ ip | ip6 ]
addr_family = ip
# Option: addr_options
# Notes: Additional options for the addr-set, by default allows to store CIDR or address ranges.
# Can be set to empty value to create simple addresses set.
addr_options = <sp>flags interval\;
[Init?family=inet6]
addr_family = ip6
addr_type = ipv6_addr

View file

@ -44,7 +44,13 @@ actioncheck =
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 '"')
ADDRESSES=$(dig +short -t txt -q $RESOLVER_ADDR | grep -v ';;' | tr -d '"')
if [ "<debug>" -gt 0 ]; then echo "returned address $ADDRESSES"; fi
if [ -z "$ADDRESSES" ]; then
echo "address for $RESOLVER_ADDR cannot be found or timeout from dig";
if [ "<debug>" -gt 0 ]; then exit 1; fi
exit 0
fi
IFS=,; ADDRESSES=$(echo $ADDRESSES)
IFS=${oifs}
IP=<ip>
@ -55,13 +61,11 @@ actionban = oifs=${IFS};
TLP=<tlp>
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
fi
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
actionunban =

View file

@ -10,7 +10,8 @@
badbotscustom = EmailCollector|WebEMailExtrac|TrackBack/1\.02|sogou music spider|(?:Mozilla/\d+\.\d+ )?Jorgee
badbots = Atomic_Email_Hunter/4\.0|atSpider/1\.0|autoemailspider|bwh3_user_agent|China Local Browse 2\.6|ContactBot/0\.2|ContentSmartz|DataCha0s/2\.0|DBrowse 1\.4b|DBrowse 1\.4d|Demo Bot DOT 16b|Demo Bot Z 16b|DSurf15a 01|DSurf15a 71|DSurf15a 81|DSurf15a VA|EBrowse 1\.4b|Educate Search VxB|EmailSiphon|EmailSpider|EmailWolf 1\.00|ESurf15a 15|ExtractorPro|Franklin Locator 1\.8|FSurf15a 01|Full Web Bot 0416B|Full Web Bot 0516B|Full Web Bot 2816B|Guestbook Auto Submitter|Industry Program 1\.0\.x|ISC Systems iRc Search 2\.1|IUPUI Research Bot v 1\.9a|LARBIN-EXPERIMENTAL \(efp@gmx\.net\)|LetsCrawl\.com/1\.0 \+http\://letscrawl\.com/|Lincoln State Web Browser|LMQueueBot/0\.2|LWP\:\:Simple/5\.803|Mac Finder 1\.0\.xx|MFC Foundation Class Library 4\.0|Microsoft URL Control - 6\.00\.8xxx|Missauga Locate 1\.0\.0|Missigua Locator 1\.9|Missouri College Browse|Mizzu Labs 2\.2|Mo College 1\.9|MVAClient|Mozilla/2\.0 \(compatible; NEWT ActiveX; Win32\)|Mozilla/3\.0 \(compatible; Indy Library\)|Mozilla/3\.0 \(compatible; scan4mail \(advanced version\) http\://www\.peterspages\.net/?scan4mail\)|Mozilla/4\.0 \(compatible; Advanced Email Extractor v2\.xx\)|Mozilla/4\.0 \(compatible; Iplexx Spider/1\.0 http\://www\.iplexx\.at\)|Mozilla/4\.0 \(compatible; MSIE 5\.0; Windows NT; DigExt; DTS Agent|Mozilla/4\.0 efp@gmx\.net|Mozilla/5\.0 \(Version\: xxxx Type\:xx\)|NameOfAgent \(CMS Spider\)|NASA Search 1\.0|Nsauditor/1\.x|PBrowse 1\.4b|PEval 1\.4b|Poirot|Port Huron Labs|Production Bot 0116B|Production Bot 2016B|Production Bot DOT 3016B|Program Shareware 1\.0\.2|PSurf15a 11|PSurf15a 51|PSurf15a VA|psycheclone|RSurf15a 41|RSurf15a 51|RSurf15a 81|searchbot admin@google\.com|ShablastBot 1\.0|snap\.com beta crawler v0|Snapbot/1\.0|Snapbot/1\.0 \(Snap Shots&#44; \+http\://www\.snap\.com\)|sogou develop spider|Sogou Orion spider/3\.0\(\+http\://www\.sogou\.com/docs/help/webmasters\.htm#07\)|sogou spider|Sogou web spider/3\.0\(\+http\://www\.sogou\.com/docs/help/webmasters\.htm#07\)|sohu agent|SSurf15a 11 |TSurf15a 11|Under the Rainbow 2\.2|User-Agent\: Mozilla/4\.0 \(compatible; MSIE 6\.0; Windows NT 5\.1\)|VadixBot|WebVulnCrawl\.unknown/1\.0 libwww-perl/5\.803|Wells Search II|WEP Search 00
failregex = ^<HOST> -.*"(GET|POST|HEAD).*HTTP.*"(?:%(badbots)s|%(badbotscustom)s)"$
vhostpref = (?:[\w]+[\w\-\.]*\s+)?
failregex = ^\s*%(vhostpref)s<ADDR> [^"]*"[A-Z]{3,10} [^"]+" \d+ \d+ "[^"]+" "[^"]*(?:%(badbots)s|%(badbotscustom)s)"$
ignoreregex =

View file

@ -2,7 +2,8 @@
[Definition]
failregex = ^\s*<HOST> \S+ \S+(?: \S+)?\s+\S+ "[A-Z]+ /\S* [^"]*" \d+ \d+ \"[^"]*\" "[^"]*\bGooglebot/[^"]*"
vhostpref = (?:[\w]+[\w\-\.]*\s+)?
failregex = ^\s*%(vhostpref)s<ADDR> [^"]*"[A-Z]{3,10} /\S* [^"]*" \d+ \d+ "[^"]*" "[^"]*\bGooglebot/[^"]*"
ignoreregex =

View file

@ -27,7 +27,7 @@ failregex = ^Registration from '[^']*' failed for '<HOST>(:\d+)?' - (?:Wrong pas
^hacking attempt detected '<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*$
^Request (?:'[^']*' )?from '(?:[^']*|.*?)' failed for '<HOST>(?::\d+)?'\s\(callid: [^\)]*\) - (?:No matching endpoint found|Not match Endpoint(?: Contact)? ACL|(?:Failed|Error) to authenticate)\b[^']*$
# FreePBX (todo: make optional in v.0.10):
# ^(%(__prefix_line)s|\[\]\s*WARNING%(__pid_re)s:?(?:\[C-[\da-f]*\])? )[^:]+: Friendly Scanner from <HOST>$

View file

@ -16,12 +16,12 @@ _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*$
^(?:Login aborted|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)\)(?: \(auth_failed\))?:(?: user=<<F-USER>[^>]*</F-USER>>,)?(?: method=\S+,)? rip=<HOST>(?:[^>]*(?:, session=<\S+>)?)\s*$
^(?:Login aborted|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|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-aggressive = ^(?:Login aborted|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,|disconnected during TLS handshake)(?: (?:in|waited) \d+ secs)?\)[^:]*:(?: user=<[^>]*>,)?(?: method=\S+,)? rip=<HOST>(?:[^>]*(?:, session=<\S+>)?)\s*$
mdre-normal =

View file

@ -1,11 +1,15 @@
# Fail2Ban configuration file to block repeated failed login attempts to Frolor installation(s)
#
# Froxlor needs to log to Syslog User (e.g. /var/log/user.log) with one of the following messages
# <syslog prefix> Froxlor: [Login Action <HOST>] Unknown user '<USER>' tried to login.
# <syslog prefix> Froxlor: [Login Action <HOST>] User '<USER>' tried to login with wrong password.
# - for type=2
# <syslog prefix> froxlor[1-6]: froxlor.WARNING: Unknown user tried to login. {"source":"login","action":"50","user":"<ADDR>"} []
# <syslog prefix> froxlor[1-6]: froxlor.WARNING: User tried to login with wrong password. {"source":"login","action":"50","user":"<ADDR>"} []
# - for type=1:
# <syslog prefix> Froxlor: [Login Action <ADDR>] Unknown user '<USER>' tried to login.
# <syslog prefix> Froxlor: [Login Action <ADDR>] User '<USER>' tried to login with wrong password.
#
# Author: Joern Muehlencord
#
# Modified: Para-do-x™ - Andreas Duennwald
[INCLUDES]
@ -13,28 +17,18 @@
# common.local
before = common.conf
[DEFAULT]
_daemon = [Ff]roxlor
_re = (?:Unknown )?[uU]ser(?: '<F-USER>(?:\S*|[^']*)</F-USER>')? tried to login(?: with wrong password)?\.
[type1]
failregex = ^%(__prefix_line)s\[Login Action <ADDR>\] %(_re)s$
[type2]
failregex = ^%(__prefix_line)sfroxlor\.WARNING: %(_re)s \{(?:"[^"]+":"[^"]*",\s*){,5}"user":"<ADDR>"\} \[\]$
[Definition]
_daemon = Froxlor
# 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>[\w\-.^_]+)
# Values: TEXT
#
prefregex = ^%(__prefix_line)s\[Login Action <HOST>\] <F-CONTENT>.+</F-CONTENT>$
failregex = ^Unknown user \S* tried to login.$
^User \S* tried to login with wrong password.$
# Option: ignoreregex
# Notes.: regex to ignore. If this regex matches, the line is ignored.
# Values: TEXT
#
type = 2
failregex = <type<type>/failregex>
ignoreregex =

View file

@ -8,16 +8,18 @@ before = nginx-error-common.conf
mode = normal
__err_type = <_ertp-<mode>>
__err_type = (?:error|crit)
_ertp-auth = error
mdre-auth = ^%(__prefix_line)suser "(?:[^"]+|.*?)":? (?:password mismatch|was not found in "[^\"]*"), client: <HOST>, server: \S*, request: "\S+ \S+ HTTP/\d+\.\d+", host: "\S+"(?:, referrer: "\S+")?\s*$
_ertp-fallback = crit
mdre-fallback = ^%(__prefix_line)sSSL_do_handshake\(\) failed \(SSL: error:\S+(?: \S+){1,3} too (?:long|short)\)[^,]*, client: <HOST>
__suffix_line = , client: <ADDR>(?:, (?:server|request|host|referrer): (?:"[^"]*"|\S*)){0,4}
prefregex = ^%(__prefix_line)s<F-CONTENT>.*</F-CONTENT>%(__suffix_line)s\s*$
mdre-auth = ^user "<F-USER>(?:[^"]+|.*?)</F-USER>":? (?:password mismatch|was not found in "[^\"]*")$
^(?:PAM: )?user '<F-USER>(?:[^']+|.*?)</F-USER>' - not authenticated: Authentication failure$
mdre-fallback = ^SSL_(?:do_handshake|read)\(\) failed \(SSL: error:\S+(?: \S+){1,3}[^\)]*\)[^,]*
_ertp-normal = %(_ertp-auth)s
mdre-normal = %(mdre-auth)s
_ertp-aggressive = (?:%(_ertp-auth)s|%(_ertp-fallback)s)
mdre-aggressive = %(mdre-auth)s
%(mdre-fallback)s

View file

@ -46,7 +46,7 @@ __err_type = [a-z]+
# failregex = ^%(__prefix_line)slimiting requests, excess: [\d\.]+ by zone "(?:%(ngx_limit_req_zones)s)", client: <HOST>, server: \S*, request: "\S+ \S+ HTTP/\d+\.\d+", host: "\S+"(, referrer: "\S+")?\s*$
# Shortly, much faster and stable version of regexp:
failregex = ^%(__prefix_line)slimiting requests, excess: [\d\.]+ by zone "(?:%(ngx_limit_req_zones)s)", client: <HOST>,
failregex = ^%(__prefix_line)s(?:limiting|delaying) (?:request|connection)s?(?:, excess: [\d\.]+,?)? by zone "(?:%(ngx_limit_req_zones)s)", client: <ADDR>,
ignoreregex =

View file

@ -11,49 +11,62 @@ before = common.conf
[Definition]
_daemon = postfix\b([^\[\s]+)?
# optional port:
_port = (?::\d+)?
_pref = [A-Z]{4,}
# optional prefix like `NOQUEUE: ` or `00ADB3C0899: ` etc...
_pref = (?:\w+: )?
# SMTP commands like RCPT etc
_cmd = [A-Z]{4,}
prefregex = ^%(__prefix_line)s<mdpr-<mode>> <F-CONTENT>.+</F-CONTENT>$
prefregex = ^%(__prefix_line)s%(_pref)s<mdpr-<mode>> <F-CONTENT>.+?</F-CONTENT>(?:, sasl_username=<F-USER>\S+</F-USER>)?\s*$
# 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) ; pragma: codespell-ignore
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|Access denied|(?:Client host|Command|Data command) rejected|Relay access denied|Malformed DNS server reply|(?:Host|Domain) not found|need fully-qualified hostname|match%(exre-user)s)\b
mdpr-normal = (?:(?:milter-)?reject:|(?:improper command pipelining|too many errors) after \S+)
mdre-normal=^%(_cmd)s from [^[]*\[<HOST>\]%(_port)s: [45][50][04] [45]\.\d\.\d+ (?:(?:<[^>]*>)?: )?(?:(?:Helo command|(?:Sender|Recipient) address) rejected: )?(?:Service unavailable|Access denied|(?:Client host|Command|Data command) rejected|Relay access denied|Malformed DNS server reply|(?:Host|Domain) not found|need fully-qualified hostname|match%(exre-user)s)\b
^from [^[]*\[<HOST>\]%(_port)s:?
mdad-normal =
mdpr-auth = warning:
mdre-auth = ^[^[]*\[<HOST>\]%(_port)s: SASL ((?i)LOGIN|PLAIN|(?:CRAM|DIGEST)-MD5) authentication failed:(?! Connection lost to authentication server| Invalid authentication mechanism)
mdre-auth2= ^[^[]*\[<HOST>\]%(_port)s: SASL ((?i)LOGIN|PLAIN|(?:CRAM|DIGEST)-MD5) authentication failed:(?! Connection lost to authentication server)
# todo: check/remove "Invalid authentication mechanism" from ignore list, if gh-1243 will get finished (see gh-1297).
mdad-auth =
mdad-auth2 =
# Mode "rbl" currently included in mode "normal", but if needed for jail "postfix-rbl" only:
mdpr-rbl = %(mdpr-normal)s
mdre-rbl = ^%(_pref)s from [^[]*\[<HOST>\]%(_port)s: [45]54 [45]\.7\.1 Service unavailable; Client host \[\S+\] blocked\b
mdre-rbl = ^%(_cmd)s from [^[]*\[<HOST>\]%(_port)s: [45]54 [45]\.7\.1 Service unavailable; Client host \[\S+\] blocked\b
mdad-rbl =
# Mode "rbl" currently included in mode "normal" (within 1st rule)
mdpr-more = %(mdpr-normal)s
mdre-more = %(mdre-normal)s
mdad-more =
# Includes some of the log messages described in
# <http://www.postfix.org/POSTSCREEN_README.html>.
mdpr-ddos = (?:lost connection after (?!(?:DATA|AUTH)\b)[A-Z]+|disconnect(?= from \S+(?: \S+=\d+)* auth=0/(?:[1-9]|\d\d+))|(?:PREGREET \d+|HANGUP) after \S+|COMMAND (?:TIME|COUNT|LENGTH) LIMIT)
mdpr-ddos = (?:lost connection after (?!(?:DATA|AUTH)\b)[A-Z]+|disconnect(?= from \S+(?: \S+=\d+)* auth=0/(?:[1-9]|\d\d+))|(?:PREGREET \d+|HANGUP) after \S+|COMMAND (?:TIME|COUNT|LENGTH) LIMIT|warning:)
mdre-ddos = ^from [^[]*\[<HOST>\]%(_port)s:?
mdad-ddos = ^(?:Message delivery request|Connection) rate limit exceeded: \d+ from [^[]*\[<ADDR>\]
mdpr-extra = (?:%(mdpr-auth)s|%(mdpr-normal)s)
mdre-extra = %(mdre-auth)s
%(mdre-normal)s
mdad-extra =
mdpr-aggressive = (?:%(mdpr-auth)s|%(mdpr-normal)s|%(mdpr-ddos)s)
mdre-aggressive = %(mdre-auth2)s
%(mdre-normal)s
mdad-aggressive = %(mdad-ddos)s
mdpr-errors = too many errors after \S+
mdre-errors = ^from [^[]*\[<HOST>\]%(_port)s$
mdad-errors =
failregex = <mdre-<mode>>
<mdad-<mode>>
# Parameter "mode": more (default combines normal and rbl), auth, normal, rbl, ddos, extra or aggressive (combines all)
# Usage example (for jail.local):

View file

@ -22,21 +22,28 @@ before = common.conf
_daemon = (?:(sm-(mta|acceptingconnections)|sendmail))
__prefix_line = %(known/__prefix_line)s(?:\w{14,20}: )?
addr = (?:(?:IPv6:)?<IP6>|<IP4>)
# mta_dname -- matches name of MTA daemon (typically specified in DAEMON_OPTIONS),
# normally something without spaces like MTA-v4 or Deamon0, etc. If it'd contain spaces, one can
# rewrite it in jail using `filter = %(known/filter)s[mta_dname="[^,]+"]` or in .local overwrite
# of the filter. (we would not use catch-alls here to satisfy obscure artificial case).
mta_dname = \S+
prefregex = ^<F-MLFID>%(__prefix_line)s</F-MLFID><F-CONTENT>.+</F-CONTENT>$
prefregex = ^\s*(?:<mail\.[^\>]+> )?<F-MLFID>%(__prefix_line)s</F-MLFID><F-CONTENT>.+</F-CONTENT>$
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))$
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+)|[45]5[13] [45]\.1\.8(?: (?P=email)\.\.\.)? Domain of sender address \S+ does not (?:exist|resolve)|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+ \[%(addr)s\]$
^<[^@]+@[^>]+>\.\.\. (?:No such user here|User unknown)$
^<F-NOFAIL>from=<[^@]+@[^>]+></F-NOFAIL>, size=\d+, class=\d+, nrcpts=\d+,(?: bodytype=\w+,)? proto=E?SMTP, daemon=%(mta_dname)s, relay=(?:\S+ )?\[%(addr)s\]$
mdre-normal =
mdre-extra = ^(?:\S+ )?\[%(addr)s\](?: \(may be forged\))? did not issue \S+ during connection
mdre-aggressive = %(mdre-extra)s
^lost input channel from (?:\S+ )?\[%(addr)s\] to %(mta_dname)s after rcpt$
^ruleset=check_rcpt, arg1=(?P<email><\S+@\S+>), relay=(?:\S+ )?\[%(addr)s\](?: \(may be forged\))?, reject=(?:450 4\.4\.0(?: (?P=email)\.\.\.)?(?: Relaying temporarily denied\.)?(?: Cannot resolve PTR record for (\d+\.){3}\d+))$
failregex = %(cmnfailre)s
<mdre-<mode>>
@ -63,6 +70,8 @@ journalmatch = SYSLOG_IDENTIFIER=sm-mta + _SYSTEMD_UNIT=sendmail.service
# Note the capture <F-MLFID>, includes both the __prefix_lines (which includes
# the sendmail PID), but also the `\w{14}` which the the sendmail assigned
# mail ID (todo: check this is necessary, possible obsolete).
# Avoid moving <F-MLFID> into the entire prefregex because the grouped messages we
# need have different syslog levels (info vs notice) that break the group if BSD verbose format is set
#
# Author: Daniel Black, Fabian Wenk and Sergey Brester aka sebres.
# Rewritten using prefregex by Serg G. Brester.

View file

@ -4,5 +4,5 @@
[Definition]
failregex = ^\s*(?:\[\]\s*)?\[vaultwarden::api::(identity||admin||core::two_factor::authenticator)\]\[ERROR\] (Invalid admin token||Invalid TOTP code||Username or password is incorrect)[\.!](?:\s+(?!IP:)\S+)* IP: <ADDR>(?:\. Username: <F-USER>\S+</F-USER>)?
failregex = ^\s*(?:\[\]\s*)?\[vaultwarden::api::(?:identity|admin|core::two_factor::authenticator)?\]\[ERROR\] (?:Invalid admin token|Invalid TOTP code|Username or password is incorrect)[\.!](?:\s+(?!IP:)\S+)* IP: <ADDR>(?:\. Username: <F-USER>\S+</F-USER>)?
ignoreregex =

35
config/filter.d/xrdp.conf Normal file
View file

@ -0,0 +1,35 @@
#
# Fail2Ban filter for XRDP
#
# Detects login attempts with invalid credentials
#
# Requirements:
# - xrdp >= 0.9.19
# - The log level in sesman.ini should be set to `INFO` or higher
# to emit the log messages needed for this filter.
#
# Author: Evan Linde
#
[INCLUDES]
# Read common prefixes. If any customizations available -- read them from
# common.local
before = common.conf
[DEFAULT]
_daemon = xrdp-sesman
[Definition]
authfail_re = \[INFO \] AUTHFAIL: user=<F-USER>(?:\S+|.+)</F-USER> ip=<ADDR> time=\d+
failregex = ^%(__prefix_line)s%(authfail_re)s$
ignoreregex =
datepattern = ^\[?%%ExY%%Exm%%Exd-%%ExH:%%ExM:%%ExS\]?
^{DATE}

View file

@ -227,14 +227,11 @@ action_mwl = %(action_)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.
# ban & send a notification to one or more of the 120+ services supported by Apprise.
# action = %(action_)s
# apprise
# apprise[config="/alternate/path/to/apprise.yaml", args='--tag fail2ban']
# See https://appriseit.com/services/ for details on what is supported.
# Or action.d/apprise.conf for more details how to configure or customize it.
# ban IP on CloudFlare & send an e-mail with whois report and relevant log lines
# to the destemail.
@ -501,7 +498,7 @@ backend = %(syslog_backend)s
[froxlor-auth]
port = http,https
logpath = %(syslog_authpriv)s
logpath = %(syslog_user)s
backend = %(syslog_backend)s
@ -724,8 +721,7 @@ backend = %(syslog_backend)s
# Since UDP is connection-less protocol, spoofing of IP and imitation
# of illegal actions is way too simple. Thus enabling of this filter
# might provide an easy way for implementing a DoS against a chosen
# victim. See
# http://nion.modprobe.de/blog/archives/690-fail2ban-+-dns-fail.html
# victim.
# Please DO NOT USE this jail unless you know what you are doing.
#
# IMPORTANT: see filter.d/named-refused for instructions to enable logging
@ -995,3 +991,7 @@ logpath = /var/log/daemon.log
[vaultwarden]
port = http,https
logpath = /var/log/vaultwarden.log
[xrdp]
port = 3389
logpath = /var/log/xrdp-sesman.log

View file

@ -13,6 +13,7 @@ banaction = nftables
banaction_allports = nftables[type=allports]
sshd_backend = systemd
postfix_backend = systemd
syslog_mail = /var/log/mail.log

View file

@ -212,8 +212,8 @@ class Beautifier:
else:
msg = "These IP addresses/networks are ignored:\n"
for ip in response[:-1]:
msg += "|- " + ip + "\n"
msg += "`- " + response[-1]
msg += "|- " + str(ip) + "\n"
msg += "`- " + str(response[-1])
elif inC[2] in ("failregex", "addfailregex", "delfailregex",
"ignoreregex", "addignoreregex", "delignoreregex"):
if len(response) == 0:

View file

@ -354,6 +354,11 @@ class DefinitionInitConfigReader(ConfigReader):
if v is None: v = getopt(opt)
self._initOpts['known/'+opt] = v
if opt not in self._initOpts:
# overwrite also conditional init options (from init?... section):
cond = SafeConfigParserWithIncludes.CONDITIONAL_RE.match(opt)
if cond:
optc, cond = cond.groups()
v = pOpts.get(optc, v)
if v is None: v = getopt(opt)
self._initOpts[opt] = v
if all and self.has_section("Definition"):

View file

@ -89,7 +89,7 @@ class FilterReader(DefinitionInitConfigReader):
stream.insert(0 if opt == 'usedns' else prio0idx,
["set", jailName, opt, value])
prio0idx += 1
elif opt in ('datepattern'):
elif opt == 'datepattern':
stream.append(["set", jailName, opt, value])
elif opt == 'journalmatch':
for match in value.split("\n"):

View file

@ -323,15 +323,17 @@ def _merge_copy_dicts(x, y):
# regex, to extract list of options:
OPTION_CRE = re.compile(r"^([^\[]+)(?:\[(.*)\])?\s*$", re.DOTALL)
# regex, matching option name (inclusive conditional option, like n?family=inet6):
OPTION_NAME_CRE = r'[\w\-_\.]+(?:\?[\w\-_\.]+=[\w\-_\.]+)?'
# regex, to iterate over single option in option list, syntax:
# `action = act[p1="...", p2='...', p3=...]`, where the p3=... not contains `,` or ']'
# since v0.10 separator extended with `]\s*[` for support of multiple option groups, syntax
# `action = act[p1=...][p2=...]`
OPTION_EXTRACT_CRE = re.compile(
r'\s*([\w\-_\.]+)=(?:"([^"]*)"|\'([^\']*)\'|([^,\]]*))(?:,|\]\s*\[|$|(?P<wrngA>.+))|,?\s*$|(?P<wrngB>.+)', re.DOTALL)
r'\s*('+OPTION_NAME_CRE+r')=(?:"([^"]*)"|\'([^\']*)\'|([^,\]]*))(?:,|\]\s*\[|$|(?P<wrngA>.+))|,?\s*$|(?P<wrngB>.+)', re.DOTALL)
# split by new-line considering possible new-lines within options [...]:
OPTION_SPLIT_CRE = re.compile(
r'(?:[^\[\s]+(?:\s*\[\s*(?:[\w\-_\.]+=(?:"[^"]*"|\'[^\']*\'|[^,\]]*)\s*(?:,|\]\s*\[)?\s*)*\])?\s*|\S+)(?=\n\s*|\s+|$)', re.DOTALL)
r'(?:[^\[\s]+(?:\s*\[\s*(?:'+OPTION_NAME_CRE+r'=(?:"[^"]*"|\'[^\']*\'|[^,\]]*)\s*(?:,|\]\s*\[)?\s*)*\])?\s*|\S+)(?=\n\s*|\s+|$)', re.DOTALL)
def extractOptions(option):
match = OPTION_CRE.match(option)

View file

@ -394,6 +394,7 @@ class Actions(JailThread, Mapping):
# raw ticket info:
"raw-ticket": lambda self: repr(self.__ticket),
# jail info:
"jail.name": lambda self: self.__jail.name,
"jail.banned": lambda self: self.__jail.actions.banManager.size(),
"jail.banned_total": lambda self: self.__jail.actions.banManager.getBanTotal(),
"jail.found": lambda self: self.__jail.filter.failManager.size(),
@ -640,12 +641,12 @@ class Actions(JailThread, Mapping):
If actions specified, don't flush list - just execute unban for
given actions (reload, obsolete resp. removed actions).
"""
log = True
log = "Unban" if not stop else "Repeal Ban"
if actions is None:
logSys.debug(" Flush ban list")
lst = self.banManager.flushBanList()
else:
log = False # don't log "[jail] Unban ..." if removing actions only.
log = None # don't log "[jail] Unban ..." if removing actions only.
lst = iter(self.banManager)
cnt = 0
# first we'll execute flush for actions supporting this operation:
@ -686,7 +687,7 @@ class Actions(JailThread, Mapping):
cnt, self.banManager.size(), self._jail.name)
return cnt
def __unBan(self, ticket, actions=None, log=True):
def __unBan(self, ticket, actions=None, log="Unban"):
"""Unbans host corresponding to the ticket.
Executes the actions in order to unban the host given in the
@ -704,7 +705,7 @@ class Actions(JailThread, Mapping):
ip = ticket.getID()
aInfo = self._getActionInfo(ticket)
if log:
logSys.notice("[%s] Unban %s", self._jail.name, ip)
logSys.notice("[%s] %s %s", self._jail.name, log, ip)
for name, action in unbactions.items():
try:
logSys.debug("[%s] action %r: unban %s", self._jail.name, name, ip)

View file

@ -165,8 +165,8 @@ class DateDetectorCache(object):
r"%b %d, %ExY %I:%M:%S %p",
# ASSP: Apr-27-13 02:33:06
r"^%b-%d-%Exy %k:%M:%S",
# 20050123T215959, 20050123 215959, 20050123 85959
r"%ExY%Exm%Exd(?:T| ?)%ExH%ExM%ExS(?:[.,]%f)?(?:\s*%z)?",
# 20050123T215959, 20050123 215959, 20050123 85959, 20050123-21:59:59
r"%ExY%Exm%Exd(?:-|T| ?)%ExH:?%ExM:?%ExS(?:[.,]%f)?(?:\s*%z)?",
# prefixed with optional named time zone (monit):
# PDT Apr 16 21:05:29
r"(?:%Z )?(?:%a )?%b %d %k:%M:%S(?:\.%f)?(?: %ExY)?",
@ -252,6 +252,8 @@ class DateDetector(object):
"There is already a template with name %s" % name)
self.__known_names.add(name)
self.__templates.append(DateDetectorTemplate(template))
logSys.debug(" date pattern regex for `%s`: `%s`",
getattr(template, 'pattern', ''), template.regex)
def appendTemplate(self, template):
"""Add a date template to manage and use in search of dates.
@ -289,16 +291,15 @@ class DateDetector(object):
DD_patternCache.set(key, template)
self._appendTemplate(template)
logSys.info(" date pattern `%r`: `%s`",
logSys.info(" date pattern `%s`: `%s`",
getattr(template, 'pattern', ''), template.name)
logSys.debug(" date pattern regex for %r: %s",
getattr(template, 'pattern', ''), template.regex)
self._appendTemplate(template)
def addDefaultTemplate(self, filterTemplate=None, preMatch=None, allDefaults=True):
"""Add Fail2Ban's default set of date templates.
"""
ignoreDup = len(self.__templates) > 0
cnt = 0
for template in (
DateDetector._defCache.templates if allDefaults else DateDetector._defCache.defaultTemplates
):
@ -311,6 +312,11 @@ class DateDetector(object):
wrap=lambda s: RE_DATE_PREMATCH.sub(lambda m: DateTemplate.unboundPattern(s), preMatch))
# append date detector template (ignore duplicate if some was added before default):
self._appendTemplate(template, ignoreDup=ignoreDup)
cnt += 1
if preMatch:
logSys.info(" default date pattern for `%r`: %d template(s)", preMatch, cnt)
else:
logSys.info(" default %sdate pattern: %d template(s)", "filtered " if filterTemplate else "", cnt)
@property
def templates(self):

View file

@ -475,6 +475,10 @@ class Filter(JailThread):
# Generate the failure attempt for the IP:
unixTime = MyTime.time()
ticket = FailTicket(ip, unixTime, matches=matches)
# check it shall be ignored:
if self._inIgnoreIPList(ip, ticket):
return 0
# add attempt (found failure):
logSys.info(
"[%s] Attempt %s - %s", self.jailName, ip, datetime.datetime.fromtimestamp(unixTime).strftime("%Y-%m-%d %H:%M:%S")
)
@ -485,7 +489,6 @@ class Filter(JailThread):
# report to observer - failure was found, for possibly increasing of it retry counter (asynchronous)
if Observers.Main is not None:
Observers.Main.add('failureFound', self.jail, ticket)
return 1
##
@ -1111,8 +1114,8 @@ class FileFilter(Filter):
def getFailures(self, filename, inOperation=None):
if self.idle: return False
log = self.getLog(filename)
if log is None:
logSys.error("Unable to get failures in %s", filename)
if log is None and self.active:
logSys.log(logging.MSG, "Unable to get failures in %s", filename)
return False
# We should always close log (file), otherwise may be locked (log-rotate, etc.)
try:

View file

@ -301,7 +301,7 @@ class FilterSystemd(JournalFilter): # pragma: systemd no cover
date = logentry.get('_SOURCE_REALTIME_TIMESTAMP')
if date is None:
date = logentry.get('__REALTIME_TIMESTAMP')
return (date.isoformat(), time.mktime(date.timetuple()) + date.microsecond/1.0E6)
return (date.isoformat(), date.timestamp())
##
# Format journal log entry into syslog style

View file

@ -739,11 +739,11 @@ class IPAddrSet(set):
class FileIPAddrSet(IPAddrSet):
# RE matching file://...
RE_FILE_IGN_IP = re.compile(r'^file:/{0,2}(.*)$')
# RE matching file://... (absolute as well as relative file name)
RE_FILE_IGN_IP = re.compile(r'^file:(?:/{0,2}(?=/(?!/|.{1,2}/))|/{0,2})(.*)$')
fileName = ''
_shortRepr = None
_reprName = None
maxUpdateLatency = 1 # latency in seconds to update by changes
_nextCheck = 0
_fileStats = ()
@ -798,12 +798,9 @@ class FileIPAddrSet(IPAddrSet):
logSys.warning("Retrieving IPs set from %r failed: %s", self.fileName, e)
def __repr__(self):
if not self._shortRepr:
shortfn = os.path.basename(self.fileName)
if shortfn != self.fileName:
shortfn = '.../' + shortfn
self._shortRepr = 'file:' + shortfn + ')'
return self._shortRepr
if self._reprName is None:
self._reprName = 'file:' + ('/' if self.fileName.startswith('/') else '') + self.fileName
return self._reprName
def __contains__(self, ip):
# load if needed:

View file

@ -353,7 +353,6 @@ class Transmitter:
return self.__server.getBanTime(name)
elif command[1] == "attempt":
value = command[2:]
if self.__quiet: return
return self.__server.addAttemptIP(name, *value)
elif command[1].startswith("bantime."):
value = command[2]

View file

@ -29,6 +29,7 @@ import subprocess
import sys
from threading import Lock
import time
import types
from ..helpers import getLogger, _merge_dicts, uni_decode
from collections import OrderedDict
@ -352,6 +353,7 @@ class Utils():
def load_python_module(pythonModule):
pythonModuleName = os.path.splitext(
os.path.basename(pythonModule))[0]
mod = importlib.machinery.SourceFileLoader(
pythonModuleName, pythonModule).load_module()
ldr = importlib.machinery.SourceFileLoader(pythonModuleName, pythonModule)
mod = types.ModuleType(ldr.name)
ldr.exec_module(mod)
return mod

View file

@ -22,6 +22,7 @@ import threading
import unittest
import re
import sys
import types
import importlib
from ..dummyjail import DummyJail
@ -175,7 +176,8 @@ try:
super(SMTPActionTest, self).tearDown()
except ImportError as e:
print("I: Skipping smtp tests: %s" % e)
if tuple(sys.version_info) <= (3, 11):
print("I: Skipping smtp tests: %s" % e)
try:
@ -295,10 +297,11 @@ try:
self.jail = DummyJail()
pythonModule = os.path.join(CONFIG_DIR, "action.d", "smtp.py")
pythonModuleName = os.path.basename(pythonModule.rstrip(".py"))
customActionModule = importlib.machinery.SourceFileLoader(
pythonModuleName, pythonModule).load_module()
ldr = importlib.machinery.SourceFileLoader(pythonModuleName, pythonModule)
mod = types.ModuleType(ldr.name)
ldr.exec_module(mod)
self.action = customActionModule.Action(
self.action = mod.Action(
self.jail, "test", host="localhost:%i" % self.port)
self.action.ssl = True
@ -309,4 +312,5 @@ try:
super(AIOSMTPActionTest, self).tearDown()
except ImportError as e:
print("I: Skipping SSL smtp tests: %s" % e)
if tuple(sys.version_info) >= (3, 10):
print("I: Skipping SSL smtp tests: %s" % e)

View file

@ -25,7 +25,7 @@ import unittest
from ..client.beautifier import Beautifier
from ..version import version
from ..server.ipdns import IPAddr
from ..server.ipdns import IPAddr, FileIPAddrSet
from ..exceptions import UnknownJailException, DuplicateJailException
class BeautifierTest(unittest.TestCase):
@ -297,6 +297,13 @@ class BeautifierTest(unittest.TestCase):
output += "`- 10.0.2.1"
self.assertEqual(self.b.beautify(response), output)
def testIgnoreIPFile(self):
self.b.setInputCmd(["set", "sshd", "addignoreip"])
response = [FileIPAddrSet("/test/file-ipaddr-set")]
output = ("These IP addresses/networks are ignored:\n"
"`- file://test/file-ipaddr-set")
self.assertEqual(self.b.beautify(response), output)
def testFailRegex(self):
self.b.setInputCmd(["get", "sshd", "failregex"])
output = "No regular expression is defined"

View file

@ -878,7 +878,7 @@ class JailsReaderTest(LogCaptureTestCase):
self.assertTrue(jails.getOptions()) # reads fine
# grab all filter names
filters = set(os.path.splitext(os.path.split(a)[1])[0]
for a in glob.glob(os.path.join('config', 'filter.d', '*.conf'))
for a in glob.glob(os.path.join(CONFIG_DIR, 'filter.d', '*.conf'))
if not (a.endswith('common.conf') or a.endswith('-aggressive.conf')))
# get filters of all jails (filter names without options inside filter[...])
filters_jail = set(

View file

@ -1125,9 +1125,9 @@ class Fail2banServerTest(Fail2banClientServerBase):
"3 ticket(s) in 'test-jail2", all=True, wait=MID_WAITTIME)
# stop/start and unban/restore ban:
self.assertLogged(
"[test-jail2] Unban 192.0.2.4",
"[test-jail2] Unban 192.0.2.8",
"[test-jail2] Unban 192.0.2.9",
"[test-jail2] Repeal Ban 192.0.2.4",
"[test-jail2] Repeal Ban 192.0.2.8",
"[test-jail2] Repeal Ban 192.0.2.9",
"Jail 'test-jail2' stopped",
"Jail 'test-jail2' started",
"[test-jail2] Restore Ban 192.0.2.4",

View file

@ -6,3 +6,6 @@
# failJSON: { "time": "2007-03-05T14:41:21", "match": true , "host": "1.2.3.4" }
1.2.3.4 - - [05/Mar/2007:14:41:21 +0100] "HEAD /123.html/trackback/ HTTP/1.0" 301 459 "http://www.mydomain.tld/123.html/trackback" "TrackBack/1.02"
# failJSON: { "time": "2024-08-18T22:08:39", "match": true , "host": "192.0.2.222", "desc": "vhost in accesslog, gh-1594" }
www.sitename.com 192.0.2.222 - - [18/Aug/2024:21:08:39 +0100] "GET /filename.jpg HTTP/1.1" 403 332 "-" "TrackBack/1.02"

View file

@ -3,3 +3,5 @@
66.249.66.1 - - - [31/Jan/2015:14:29:44 ] fail2ban.org "GET / HTTP/1.1" 200 814 "-" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" + 293 1149 546
# failJSON: { "time": "2015-01-31T14:29:44", "match": false, "host": "51.159.55.100" }
51.159.55.100 - - - [31/Jan/2015:14:29:44 ] fail2ban.org "GET / HTTP/1.1" 200 814 "-" "NOT A __GOOGLE_BOT__" + 293 1149 546
# failJSON: { "time": "2024-08-18T22:08:39", "match": true , "host": "192.0.2.222", "desc": "vhost in accesslog, gh-1594" }
www.sitename.com 192.0.2.222 - - [18/Aug/2024:21:08:39 +0100] "GET / HTTP/1.1" 403 332 "-" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"

View file

@ -108,6 +108,8 @@ Nov 4 18:30:40 localhost asterisk[32229]: NOTICE[32257]: chan_sip.c:23417 in han
# PJSip Errors
# failJSON: { "time": "2016-05-06T07:08:09", "match": true, "host": "192.0.2.6" }
[2016-05-06 07:08:09] NOTICE[17103] res_pjsip/pjsip_distributor.c: Request from '"test1" <sip:test1@2.3.4.5>' failed for '192.0.2.6:5678' (callid: deadbeef) - No matching endpoint found
# failJSON: { "time": "2016-05-06T07:08:09", "match": true, "host": "192.0.2.7", "desc": "Test for No matching endpoint found with retry counts (pattern 1)" }
[2016-05-06 07:08:09] NOTICE[17103] res_pjsip/pjsip_distributor.c: Request 'INVITE' from '"test2" <sip:test2@3.4.5.6>' failed for '192.0.2.7:5679' (callid: cafebabe) - No matching endpoint found after 5 tries in 2.500 ms
# # FreePBX Warnings
# #_dis_failJSON: { "time": "2016-05-06T07:08:09", "match": true, "host": "192.0.2.4" }

View file

@ -147,6 +147,9 @@ Aug 29 15:33:53 server dovecot: managesieve-login: Disconnected: Too many invali
# failJSON: { "time": "2004-08-29T01:49:33", "match": true , "host": "192.0.2.5", "desc": "matches in aggressive mode, avoid slow RE, gh-3370" }
Aug 29 01:49:33 server dovecot[459]: imap-login: Disconnected: Connection closed: read(size=1026) failed: Connection reset by peer (no auth attempts in 0 secs): user=<>, rip=192.0.2.5, lip=127.0.0.1, TLS handshaking: read(size=1026) failed: Connection reset by peer
# failJSON: { "time": "2004-08-22T12:09:41", "match": true , "host": "192.0.2.6", "desc": "matches in aggressive mode, new format with `Login aborted` and `(no_auth_attempts)`" }
Aug 22 12:09:41 server dovecot: imap-login: Login aborted: Connection closed: read(size=653) failed: Connection reset by peer (no auth attempts in 1 secs) (no_auth_attempts): user=<>, rip=192.0.2.6, lip=192.0.2.1, TLS: read(size=653) failed: Connection reset by peer, session=<qMLsbvY8PLSkXGoP>
# failJSON: { "time": "2004-08-29T01:49:33", "match": true , "host": "192.0.2.5", "desc": "matches in aggressive mode, avoid slow RE, gh-3370" }
Aug 29 01:49:33 server dovecot[459]: imap-login: Disconnected: Connection closed: SSL_accept() failed: error:1408F10B:SSL routines:ssl3_get_record:wrong version number (no auth attempts in 0 secs): user=<>, rip=192.0.2.5, lip=127.0.0.1, TLS handshaking: SSL_accept() failed: error:1408F10B:SSL routines:ssl3_get_record:wrong version number
# failJSON: { "time": "2004-08-29T01:49:33", "match": true , "host": "192.0.2.5", "desc": "matches in aggressive mode, avoid slow RE, gh-3370" }
@ -162,3 +165,8 @@ Aug 29 16:06:58 s166-62-100-187 dovecot: imap-login: Disconnected (disconnected
Aug 31 16:15:10 s166-62-100-187 dovecot: imap-login: Disconnected (client didn't finish SASL auth, waited 2 secs): user=<>, method=PLAIN, rip=192.0.2.6, lip=192.168.1.2, TLS: SSL_read() syscall failed: Connection reset by peer, TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)
# failJSON: { "time": "2004-08-31T16:21:53", "match": true , "host": "192.0.2.7" }
Aug 31 16:21:53 s166-62-100-187 dovecot: imap-login: Disconnected (no auth attempts in 4 secs): user=<>, rip=192.0.2.7, lip=192.168.1.2, TLS handshaking: SSL_accept() syscall failed: Connection reset by peer
# failJSON: { "time": "2004-08-22T12:09:41", "match": true , "host": "192.0.2.7", "desc": "disconnected during TLS handshake (no application protocol)" }
Aug 22 12:09:41 server dovecot: imap-login: Login aborted: Connection closed: SSL_accept() failed: error:0A0000EB:SSL routines::no application protocol (disconnected during TLS handshake) (tls_handshake_not_finished): user=<>, rip=192.0.2.7, lip=192.0.2.1, TLS handshaking: SSL_accept() failed: error:0A0000EB:SSL routines::no application protocol, session=<J5HwbvY8QrSkXGoP>
# failJSON: { "time": "2004-08-22T12:09:41", "match": true , "host": "192.0.2.7", "desc": "disconnected during TLS handshake (no shared cipher)" }
Aug 22 12:09:41 server dovecot: imap-login: Login aborted: Connection closed: SSL_accept() failed: error:0A0000C1:SSL routines::no shared cipher (disconnected during TLS handshake) (tls_handshake_not_finished): user=<>, rip=192.0.2.7, lip=192.0.2.1, TLS handshaking: SSL_accept() failed: error:0A0000C1:SSL routines::no shared cipher, session=<Q23ybvY8RLSkXGoP>

View file

@ -1,5 +1,11 @@
# filterOptions: [{"type": "1"}]
# failJSON: { "time": "2005-05-21T00:56:27", "match": true , "host": "1.2.3.4" }
May 21 00:56:27 jomu Froxlor: [Login Action 1.2.3.4] Unknown user 'user' tried to login.
# failJSON: { "time": "2005-05-21T00:57:38", "match": true , "host": "1.2.3.4" }
May 21 00:57:38 jomu Froxlor: [Login Action 1.2.3.4] User 'admin' tried to login with wrong password.
# filterOptions: [{}, {"type": "2"}]
# failJSON: { "time": "2025-09-21T17:46:18", "match": true , "host": "1.2.3.4" }
2025-09-21T17:46:18.311379+02:00 hostname froxlor[1055219]: froxlor.WARNING: User tried to login with wrong password. {"source":"login","action":"50","user":"1.2.3.4"} []
# failJSON: { "time": "2025-09-21T16:30:13", "match": true , "host": "1.2.3.4" }
2025-09-21T16:30:13.118232+02:00 hostname froxlor[1054438]: froxlor.WARNING: Unknown user tried to login. {"source":"login","action":"50","user":"1.2.3.4"} []

View file

@ -13,11 +13,17 @@
# failJSON: { "time": "2014-04-03T22:20:40", "match": true, "host": "192.0.2.2", "desc": "trying injection on user name"}
2014/04/03 22:20:40 [error] 30708#0: *3 user "test": password mismatch, client: 127.0.0.1, server: test, request: "GET / HTTP/1.1", host: "localhost:8443"": was not found in "/etc/nginx/.htpasswd", client: 192.0.2.2, server: , request: "GET / HTTP/1.1", host: "localhost:8443"
# failJSON: { "time": "2025-09-07T21:20:40", "match": true, "host": "192.0.2.120", "desc": "PAM auth failure, gh-4071"}
2025/09/07 21:20:40 [error] 23141#23141: *24470 PAM: user 'username' - not authenticated: Authentication failure, client: 192.0.2.120, server: webdav.sub.domain, request: "GET / HTTP/2.0", host: "webdav.sub.domain"
# filterOptions: [{"logtype": "journal"}]
# failJSON: { "match": true, "host": "192.0.2.3", "desc": "systemd journal message, with optional extra timestamp, gh-3646"}
host nginx[983478]: 2023/12/09 21:35:20 [error] 983478#983478: *3 user "fakeusername" was not found in "/var/lib/nginx/htpasswd-for-host.example.com", client: 192.0.2.3, server: host.example.com, request: "GET / HTTP/2.0", host: "host.example.com"
# failJSON: { "match": true, "host": "192.0.2.120", "desc": "systemd journal message, PAM auth failure, gh-4071"}
host nginx[23141]: host nginx: 2025/09/07 21:20:40 [error] 23141#23141: *24470 PAM: user 'username' - not authenticated: Authentication failure, client: 192.0.2.120, server: webdav.sub.domain, request: "GET / HTTP/2.0", host: "webdav.sub.domain"
# filterOptions: [{"mode": "fallback"}]
# failJSON: { "time": "2020-11-25T14:42:16", "match": true , "host": "142.93.180.14" }
@ -29,6 +35,13 @@ host nginx[983478]: 2023/12/09 21:35:20 [error] 983478#983478: *3 user "fakeuser
# failJSON: { "time": "2020-11-25T16:02:45", "match": false }
2020/11/25 16:02:45 [error] 76952#76952: *5645766 connect() failed (111: Connection refused) while connecting to upstream, client: 5.126.32.148, server: www.google.de, request: "GET /admin/config HTTP/2.0", upstream: "http://127.0.0.1:3000/admin/config", host: "www.google.de"
# failJSON: { "time": "2025-07-09T19:20:38", "match": true , "host": "192.0.2.23", "desc": "SSL failure: bad key share (gh-4142)" }
2025/07/09 19:20:38 [crit] 3075615#3075615: *2489 SSL_do_handshake() failed (SSL: error:0A00006C:SSL routines::bad key share) while SSL handshaking, client: 192.0.2.23, server: 0.0.0.0:443
# failJSON: { "time": "2025-07-09T20:18:52", "match": true , "host": "192.0.2.24", "desc": "SSL failure: bad record type (gh-4142)" }
2025/07/09 20:18:52 [crit] 60993#60993: *16611546 SSL_do_handshake() failed (SSL: error:0A0001BB:SSL routines::bad record type error:0A000139:SSL routines::record layer failure) while SSL handshaking, client: 192.0.2.24, server: 0.0.0.0:443
# failJSON: { "time": "2025-07-09T22:27:36", "match": true , "host": "192.0.2.17", "desc": "SSL failure: alert number 121 (gh-4142)" }
2025/07/09 22:27:36 [crit] 60993#60993: *16700609 SSL_read() failed (SSL: error:0A000461:SSL routines::reason(1121):SSL alert number 121) while waiting for request, client: 192.0.2.17, server: 0.0.0.0:443
# filterOptions: [{"mode": "aggressive"}]
# failJSON: { "time": "2020-11-25T14:42:16", "match": true , "host": "142.93.180.14" }
2020/11/25 14:42:16 [crit] 76952#76952: *2454307 SSL_do_handshake() failed (SSL: error:1408F0C6:SSL routines:ssl3_get_record:packet length too long) while SSL handshaking, client: 142.93.180.14, server: 0.0.0.0:443

View file

@ -11,6 +11,18 @@
# failJSON: { "time": "2016-09-30T08:36:06", "match": true, "host": "2001:db8::80da:af6b:8b2c" }
2016/09/30 08:36:06 [error] 22923#0: *4758725916 limiting requests, excess: 15.243 by zone "one", client: 2001:db8::80da:af6b:8b2c, server: example.com, request: "GET / HTTP/1.1", host: "example.com"
# failJSON: { "time": "2025-08-01T04:24:17", "match": true , "host": "206.189.215.97" }
2025/08/01 04:24:17 [warn] 4772#4772: *68 delaying request, excess: 0.841, by zone "req_limit", client: 206.189.215.97, server: myserver.net, request: "GET /ab2h HTTP/1.1", host: "22.18.134.49"
# failJSON: { "time": "2025-08-01T14:32:27", "match": true , "host": "104.248.81.143" }
2025/08/01 14:32:27 [warn] 31733#31733: *21 delaying request, excess: 0.850, by zone "req_limit", client: 104.248.81.143, server: myserver.net, request: "GET /favicon.ico HTTP/1.1", host: "myserver.net", referrer: "https://myserver.net/"
# failJSON: { "time": "2025-08-03T03:17:28", "match": true , "host": "128.199.22.141" }
2025/08/03 03:17:28 [error] 25808#25808: *598 limiting connections by zone "conn_limit", client: 128.199.22.141, server: myserver.net, request: "GET /favicon.ico HTTP/1.1", host: "84.108.142.49", referrer: "https://xxx.com/"
# failJSON: { "time": "2025-08-03T13:56:22", "match": true , "host": "162.240.161.123" }
2025/08/03 13:56:22 [error] 943#943: *60 limiting connections by zone "conn_limit", client: 162.240.161.123, server: myserver.net, request: "GET /.env HTTP/1.1", host: "app.myserver.net"
# filterOptions: [{"logtype": "journal"}]
# failJSON: { "match": true , "host": "192.0.2.2" }

View file

@ -151,7 +151,7 @@ Jan 14 16:18:16 xxx postfix/smtpd[14933]: warning: host[192.0.2.5]: SASL CRAM-MD
# failJSON: { "time": "2005-01-14T16:18:16", "match": true , "host": "192.0.2.5", "desc": "aggressive only" }
Jan 14 16:18:16 xxx postfix/smtpd[14933]: warning: host[192.0.2.5]: SASL CRAM-MD5 authentication failed: Invalid authentication mechanism
# failJSON: { "time": "2004-11-04T09:11:01", "match": true , "host": "192.0.2.152", "desc": "reason unavailable" }
# failJSON: { "time": "2004-11-04T09:11:01", "match": true , "host": "192.0.2.152", "user": "admin", "desc": "reason unavailable" }
Nov 4 09:11:01 mail postfix/smtpd[1234]: warning: unknown[192.0.2.152]: SASL LOGIN authentication failed: (reason unavailable), sasl_username=admin
# ---------------------------------------
@ -187,6 +187,13 @@ Jun 8 23:14:28 proxy2 postfix/postscreen[473]: COMMAND TIME LIMIT from [192.0.2
# failJSON: { "time": "2005-06-08T23:14:54", "match": true , "host": "192.0.2.26", "desc": "abusive clients hitting command limit (gh-3040)" }
Jun 8 23:14:54 proxy2 postfix/postscreen[473]: COMMAND COUNT LIMIT from [192.0.2.26]:15592 after RCPT
# failJSON: { "time": "2004-09-17T18:19:20", "match": true , "host": "192.0.2.25" }
Sep 17 18:19:20 mxhost postfix/smtpd[12345]: NOQUEUE: lost connection after CONNECT from unknown[192.0.2.25]
# failJSON: { "time": "2004-09-19T12:10:57", "match": true , "host": "192.0.2.123", "desc": "Connection rate limit exceeded, gh-4073" }
Sep 19 12:10:57 hostname postfix/smtpd[23244]: warning: Connection rate limit exceeded: 31 from spamhost.domain.org[192.0.2.123] for service smtpd
# failJSON: { "time": "2004-09-19T12:10:58", "match": true , "host": "192.0.2.227", "desc": "Message delivery request rate limit exceeded, gh-3265" }
Sep 19 12:10:58 hostname postfix/smtpd[14059]: warning: Message delivery request rate limit exceeded: 334 from spam.example.com[192.0.2.227] for service submission
# filterOptions: [{}, {"mode": "ddos"}, {"mode": "aggressive"}]
# failJSON: { "match": false, "desc": "don't affect lawful data (sporadical connection aborts within DATA-phase, see gh-1813 for discussion)" }

View file

@ -57,6 +57,9 @@ Feb 27 15:49:02 batman sm-mta[88377]: s1REn1un088377: ruleset=check_rcpt, arg1=<
# failJSON: { "time": "2005-02-27T22:44:42", "match": true , "host": "123.69.106.50" }
Feb 27 22:44:42 batman sm-mta[30972]: s1RLieRP030972: ruleset=check_rcpt, arg1=<existing-user@example.com>, relay=[123.69.106.50], reject=553 5.1.8 <existing-user@example.com>... Domain of sender address lf@ibuv.net does not exist
# failJSON: { "time": "2005-02-27T22:44:43", "match": true , "host": "192.0.2.100" }
Feb 27 22:44:43 batman sm-mta[4012]: 56CF8Qni004012: ruleset=check_rcpt, arg1=<existing-user@example.com>, relay=[192.0.2.100] (may be forged), reject=451 4.1.8 Domain of sender address test.whatever@service.example.com does not resolve
# failJSON: { "time": "2005-02-23T21:18:47", "match": true , "host": "76.72.174.70" }
Feb 23 21:18:47 batman sm-mta[93301]: s1NKIkZa093301: [76.72.174.70]: EXPN root [rejected]
@ -94,6 +97,11 @@ Nov 3 11:35:30 Microsoft sendmail[26254]: rA37ZTSC026252: <anton@domain.com>...
# failJSON: { "match": false, "desc": "Different mail ID shouldn't match" }
Nov 3 11:35:30 Microsoft sendmail[26254]: rA37ZTSC026255: from=<anton@domain.com>, size=0, class=0, nrcpts=0, bodytype=8BITMIME, proto=ESMTP, daemon=MTA, relay=163.23.32.95.dsl-dynamic.vsi.ru [95.32.23.163]
# failJSON: { "match": false, "desc": "Add User unknown" }
Jun 17 14:37:39 <mail.notice> robin sm-mta[2794]: 55HIbcGI002794: <baduser@yourhost.com>... User unknown
# failJSON: { "time": "2005-06-17T14:37:39", "match": true, "host": "192.168.1.45", "desc": "BSD style log format with no hostname for the relay." }
Jun 17 14:37:39 <mail.info> robin sm-mta[2794]: 55HIbcGI002794: from=<root@yourhost.com>, size=108, class=0, nrcpts=0, proto=ESMTP, daemon=MTA-v4, relay=[192.168.1.45]
# filterOptions: {"mode": "extra"}
# failJSON: { "time": "2005-03-06T16:55:28", "match": true , "host": "192.0.2.194", "desc": "wrong resp. non RFC compiant (ddos prelude?), MTA-mode" }
@ -112,3 +120,12 @@ Mar 29 22:51:43 server sendmail[3529565]: xA32R2PQ3529565: [192.0.2.2] did not i
Mar 29 22:51:45 server sm-mta[50437]: 06QDQnNf050437: example.com [192.0.2.3] did not issue MAIL/EXPN/VRFY/ETRN during connection to IPv4
# failJSON: { "time": "2005-03-29T22:51:46", "match": true , "host": "2001:DB8::1", "desc": "IPv6" }
Mar 29 22:51:46 server sm-mta[50438]: 06QDQnNf050438: example.com [IPv6:2001:DB8::1] did not issue MAIL/EXPN/VRFY/ETRN during connection to IPv6
# filterOptions: {"mode": "aggressive"}
# failJSON: { "time": "2005-07-31T13:03:43", "match": true, "host": "139.162.120.196" }
Jul 31 13:03:43 <mail.notice> alarmpi sm-mta[24291]: 56VFYCxS024291: lost input channel from mail.aqicn.org [139.162.120.196] to MTA-v4 after rcpt
# failJSON: { "time": "2005-06-18T08:05:17", "match": true, "host": "45.125.66.67" }
Jun 18 08:05:17 <mail.notice> myhost sm-mta[17002]: 55IC59VD017002: ruleset=check_rcpt, arg1=<spameri@tiscali.it>, relay=[45.125.66.67], reject=450 4.4.0 <spameri@tiscali.it>... Relaying temporarily denied. Cannot resolve PTR record for 45.125.66.67

View file

@ -21,3 +21,6 @@
# failJSON: { "time": "2024-08-30T20:11:28", "match": true , "host": "2001:db8::b6d3:95d7:1425:766d" }
[2024-08-31 02:11:28.892+0800][vaultwarden::api::core::two_factor::authenticator][ERROR] Invalid TOTP code! Server time: 2024-08-30 18:11:28 UTC IP: 2001:db8::b6d3:95d7:1425:766d
# failJSON: { "time": "2024-08-30T20:11:30", "match": true , "host": "192.0.2.7" }
[2024-08-31 02:11:30.123+0800][vaultwarden::api::admin][ERROR] Invalid admin token! IP: 192.0.2.7. Username: alice

View file

@ -0,0 +1,37 @@
#
# /var/log/xrdp-sesman.log -- should be about the same on any linux distro
#
# failJSON: { "time": "2022-04-07T12:11:06", "match": true, "host": "10.171.161.151"}
[20220407-12:11:06] [INFO ] AUTHFAIL: user=badtypist ip=::ffff:10.171.161.151 time=1649351466
# ip injection: 10.171.161.151 should be matched as the host; 192.168.0.1 is an innocent, injected address
# failJSON: { "time": "2022-04-07T12:11:24", "match": true, "host": "10.171.161.151", "desc": "specifying ip address as username"}
[20220407-12:11:24] [INFO ] AUTHFAIL: user=192.168.0.1 ip=::ffff:10.171.161.151 time=1649351484
# ip injection: 10.171.161.151 should be matched as the host; 192.168.0.4 is an innocent, injected address
# failJSON: { "time": "2022-04-07T12:22:02", "match": true, "host": "10.171.161.151", "desc": "more devious log injection"}
[20220407-12:22:02] [INFO ] AUTHFAIL: user=loginjector ip=192.168.0.4 time=123456789\n[20220407-12:16:59] [INFO ] AUTHFAIL: user=endinjection ip=::ffff:10.171.161.151 time=1649352122
#
# /var/log/messages -- RHEL/Fedora family
#
# failJSON: { "time": "2005-04-07T12:11:06", "match": true, "host": "10.171.161.151"}
Apr 7 12:11:06 servername xrdp-sesman[41441]: [INFO ] AUTHFAIL: user=badtypist ip=::ffff:10.171.161.151 time=1649351466
# ip injection: 10.171.161.151 should be matched as the host; 192.168.0.1 is an innocent, injected address
# failJSON: { "time": "2005-04-07T12:11:24", "match": true, "host": "10.171.161.151", "desc": "specifying ip address as username"}
Apr 7 12:11:24 servername xrdp-sesman[41441]: [INFO ] AUTHFAIL: user=192.168.0.1 ip=::ffff:10.171.161.151 time=1649351484
# ip injection: 10.171.161.151 should be matched as the host; 192.168.0.4 is an innocent, injected address
# failJSON: { "time": "2005-04-07T12:22:02", "match": true, "host": "10.171.161.151", "desc": "more devious log injection"}
Apr 7 12:22:02 servername xrdp-sesman[41441]: [INFO ] AUTHFAIL: user=loginjector ip=192.168.0.4 time=123456789\n[20220407-12:16:59] [INFO ] AUTHFAIL: user=endinjection ip=::ffff:10.171.161.151 time=1649352122
# ip injection: innocent, injected ip 192.168.0.4 in a line that shouldn't contain a host
# failJSON: { "match": false }
Apr 7 12:22:02 servername xrdp[52415]: [INFO ] xrdp_wm_log_msg: login failed for user loginjector ip=192.168.0.4 time=12345\n[20220407-12:16:59] [INFO ] AUTHFAIL: user=endinjection
# failJSON: { "match": false }
Apr 7 12:22:02 servername xrdp[52415]: [INFO ] n[20220407-12:16:59] [INFO ] AUTHFAIL: user=endinjection

View file

@ -617,14 +617,14 @@ class IgnoreIP(LogCaptureTestCase):
self.pruneLog()
self.assertFalse(self.filter.inIgnoreIPList(FailTicket("2001:db8::ffff")))
self.assertLogged("returned successfully 1")
# by user-name (ignore tester):
self.filter.ignoreCommand = 'if [ "<F-USER>" = "tester" ]; then exit 0; fi; exit 1'
# by user-name (ignore tester), also test jail.name tag:
self.filter.ignoreCommand = 'echo "jail:<jail.name>"; if [ "<F-USER>" = "tester" ]; then exit 0; fi; exit 1'
self.pruneLog()
self.assertTrue(self.filter.inIgnoreIPList(FailTicket("tester", data={'user': 'tester'})))
self.assertLogged("returned successfully 0")
self.assertLogged("stdout: %r" % 'jail:DummyJail', "returned successfully 0", all=True)
self.pruneLog()
self.assertFalse(self.filter.inIgnoreIPList(FailTicket("root", data={'user': 'root'})))
self.assertLogged("returned successfully 1", all=True)
self.assertLogged("stdout: %r" % 'jail:DummyJail', "returned successfully 1", all=True)
def testIgnoreCache(self):
# like both test-cases above, just cached (so once per key)...

View file

@ -393,6 +393,13 @@ class Transmitter(TransmitterBase):
# resulted to ban for "192.0.2.2" but not for "192.0.2.1":
self.assertLogged("Ban 192.0.2.2", wait=True)
self.assertNotLogged("Ban 192.0.2.1")
# check attempt will be ignored by ignore facilities:
ip = "192.0.2.1"
self.transm.proceed(["set", self.jailName, "addignoreip", ip])
self.assertLogged("Add %r to ignore list" % (ip,), wait=True)
self.assertEqual(attempt(ip, ["test failure %d" % i for i in (3,4,5)]), (0, 0))
self.assertLogged("Ignore %s by ip" % (ip,), wait=True)
self.assertNotLogged("Ban 192.0.2.1")
@with_alt_time
def testJailBanList(self):
@ -1343,11 +1350,11 @@ class ServerConfigReaderTests(LogCaptureTestCase):
r"`done`",
),
'ip4-start': (
r"`nft add set inet f2b-table addr-set-j-w-nft-mp \{ type ipv4_addr\; \}`",
r"`nft add set inet f2b-table addr-set-j-w-nft-mp \{ type ipv4_addr\; flags interval\; \}`",
r"`nft add rule inet f2b-table f2b-chain $proto dport \{ $(echo 'http,https' | sed s/:/-/g) \} ip saddr @addr-set-j-w-nft-mp reject`",
),
'ip6-start': (
r"`nft add set inet f2b-table addr6-set-j-w-nft-mp \{ type ipv6_addr\; \}`",
r"`nft add set inet f2b-table addr6-set-j-w-nft-mp \{ type ipv6_addr\; flags interval\; \}`",
r"`nft add rule inet f2b-table f2b-chain $proto dport \{ $(echo 'http,https' | sed s/:/-/g) \} ip6 saddr @addr6-set-j-w-nft-mp reject`",
),
'flush': (
@ -1355,10 +1362,10 @@ class ServerConfigReaderTests(LogCaptureTestCase):
"`{ nft flush set inet f2b-table addr6-set-j-w-nft-mp 2> /dev/null; } || ",
),
'stop': (
r"`{ nft -a list chain inet f2b-table f2b-chain | grep -oP '@addr-set-j-w-nft-mp\s+.*\s+\Khandle\s+(\d+)$'; } | while read -r hdl; do`",
r"`{ nft -a list chain inet f2b-table f2b-chain | sed -nE 's/.*@addr-set-j-w-nft-mp\s+.*\s+\#\s*(handle\s+[0-9]+)$/\1/p'; } | while read -r hdl; do`",
r"`nft delete rule inet f2b-table f2b-chain $hdl; done`",
r"`nft delete set inet f2b-table addr-set-j-w-nft-mp`",
r"`{ nft -a list chain inet f2b-table f2b-chain | grep -oP '@addr6-set-j-w-nft-mp\s+.*\s+\Khandle\s+(\d+)$'; } | while read -r hdl; do`",
r"`{ nft -a list chain inet f2b-table f2b-chain | sed -nE 's/.*@addr6-set-j-w-nft-mp\s+.*\s+\#\s*(handle\s+[0-9]+)$/\1/p'; } | while read -r hdl; do`",
r"`nft delete rule inet f2b-table f2b-chain $hdl; done`",
r"`nft delete set inet f2b-table addr6-set-j-w-nft-mp`",
),
@ -1389,11 +1396,11 @@ class ServerConfigReaderTests(LogCaptureTestCase):
r"`nft -- add chain inet f2b-table f2b-chain \{ type filter hook input priority -1 \; \}`",
),
'ip4-start': (
r"`nft add set inet f2b-table addr-set-j-w-nft-ap \{ type ipv4_addr\; \}`",
r"`nft add set inet f2b-table addr-set-j-w-nft-ap \{ type ipv4_addr\; flags interval\; \}`",
r"`nft add rule inet f2b-table f2b-chain meta l4proto \{ tcp,udp \} ip saddr @addr-set-j-w-nft-ap reject`",
),
'ip6-start': (
r"`nft add set inet f2b-table addr6-set-j-w-nft-ap \{ type ipv6_addr\; \}`",
r"`nft add set inet f2b-table addr6-set-j-w-nft-ap \{ type ipv6_addr\; flags interval\; \}`",
r"`nft add rule inet f2b-table f2b-chain meta l4proto \{ tcp,udp \} ip6 saddr @addr6-set-j-w-nft-ap reject`",
),
'flush': (
@ -1401,10 +1408,10 @@ class ServerConfigReaderTests(LogCaptureTestCase):
"`{ nft flush set inet f2b-table addr6-set-j-w-nft-ap 2> /dev/null; } || ",
),
'stop': (
r"`{ nft -a list chain inet f2b-table f2b-chain | grep -oP '@addr-set-j-w-nft-ap\s+.*\s+\Khandle\s+(\d+)$'; } | while read -r hdl; do`",
r"`{ nft -a list chain inet f2b-table f2b-chain | sed -nE 's/.*@addr-set-j-w-nft-ap\s+.*\s+\#\s*(handle\s+[0-9]+)$/\1/p'; } | while read -r hdl; do`",
r"`nft delete rule inet f2b-table f2b-chain $hdl; done`",
r"`nft delete set inet f2b-table addr-set-j-w-nft-ap`",
r"`{ nft -a list chain inet f2b-table f2b-chain | grep -oP '@addr6-set-j-w-nft-ap\s+.*\s+\Khandle\s+(\d+)$'; } | while read -r hdl; do`",
r"`{ nft -a list chain inet f2b-table f2b-chain | sed -nE 's/.*@addr6-set-j-w-nft-ap\s+.*\s+\#\s*(handle\s+[0-9]+)$/\1/p'; } | while read -r hdl; do`",
r"`nft delete rule inet f2b-table f2b-chain $hdl; done`",
r"`nft delete set inet f2b-table addr6-set-j-w-nft-ap`",
),
@ -1669,6 +1676,106 @@ class ServerConfigReaderTests(LogCaptureTestCase):
r"`ipset -exist del f2b-j-w-iptables-ipset-ap6 2001:db8::`",
),
}),
# iptables-ipset (allports + drop) --
('j-w-ipt-ipset-ap-drp', 'iptables-ipset[name=%(__name__)s, type="allports", blocktype="DROP"]', {
'ip4': (' f2b-j-w-ipt-ipset-ap-drp ',), 'ip6': (' f2b-j-w-ipt-ipset-ap-drp6 ',),
'*-start-stop-check': (
# iterator over protocol is same for both families:
"`for chain in $(echo 'INPUT' | sed 's/,/ /g'); do for proto in $(echo 'tcp' | sed 's/,/ /g'); do`",
"`done; done`",
),
'ip4-start': (
"`ipset -exist create f2b-j-w-ipt-ipset-ap-drp hash:ip timeout 0 maxelem 65536 `",
"`{ iptables -w -C $chain -p $proto -m set --match-set f2b-j-w-ipt-ipset-ap-drp src -j DROP >/dev/null 2>&1; } || "
"{ iptables -w -I $chain -p $proto -m set --match-set f2b-j-w-ipt-ipset-ap-drp src -j DROP; }",
),
'ip6-start': (
"`ipset -exist create f2b-j-w-ipt-ipset-ap-drp6 hash:ip timeout 0 maxelem 65536 family inet6`",
"`{ ip6tables -w -C $chain -p $proto -m set --match-set f2b-j-w-ipt-ipset-ap-drp6 src -j DROP >/dev/null 2>&1; } || "
"{ ip6tables -w -I $chain -p $proto -m set --match-set f2b-j-w-ipt-ipset-ap-drp6 src -j DROP; }",
),
'flush': (
"`ipset flush f2b-j-w-ipt-ipset-ap-drp`",
"`ipset flush f2b-j-w-ipt-ipset-ap-drp6`",
),
'stop': (
"`iptables -w -D $chain -p $proto -m set --match-set f2b-j-w-ipt-ipset-ap-drp src -j DROP`",
"`ipset flush f2b-j-w-ipt-ipset-ap-drp`",
"`ipset destroy f2b-j-w-ipt-ipset-ap-drp 2>/dev/null || { sleep 1; ipset destroy f2b-j-w-ipt-ipset-ap-drp; }`",
"`ip6tables -w -D $chain -p $proto -m set --match-set f2b-j-w-ipt-ipset-ap-drp6 src -j DROP`",
"`ipset flush f2b-j-w-ipt-ipset-ap-drp6`",
"`ipset destroy f2b-j-w-ipt-ipset-ap-drp6 2>/dev/null || { sleep 1; ipset destroy f2b-j-w-ipt-ipset-ap-drp6; }`",
),
'ip4-check': (
r"""`iptables -w -C $chain -p $proto -m set --match-set f2b-j-w-ipt-ipset-ap-drp src -j DROP`""",
),
'ip6-check': (
r"""`ip6tables -w -C $chain -p $proto -m set --match-set f2b-j-w-ipt-ipset-ap-drp6 src -j DROP`""",
),
'ip4-ban': (
r"`ipset -exist add f2b-j-w-ipt-ipset-ap-drp 192.0.2.1 timeout 0`",
),
'ip4-unban': (
r"`ipset -exist del f2b-j-w-ipt-ipset-ap-drp 192.0.2.1`",
),
'ip6-ban': (
r"`ipset -exist add f2b-j-w-ipt-ipset-ap-drp6 2001:db8:: timeout 0`",
),
'ip6-unban': (
r"`ipset -exist del f2b-j-w-ipt-ipset-ap-drp6 2001:db8::`",
),
}),
# iptables-ipset (allports + REJECT with icmp?6? host-unreachable) --
('j-w-ipt-ipset-ap-rwhu', 'iptables-ipset[name=%(__name__)s, type="allports", '
+'blocktype="REJECT --reject-with icmp-host-unreachable", '
+'blocktype?family=inet6="REJECT --reject-with icmp6-host-unreachable"]', {
'ip4': (' f2b-j-w-ipt-ipset-ap-rwhu ',), 'ip6': (' f2b-j-w-ipt-ipset-ap-rwhu6 ',),
'*-start-stop-check': (
# iterator over protocol is same for both families:
"`for chain in $(echo 'INPUT' | sed 's/,/ /g'); do for proto in $(echo 'tcp' | sed 's/,/ /g'); do`",
"`done; done`",
),
'ip4-start': (
"`ipset -exist create f2b-j-w-ipt-ipset-ap-rwhu hash:ip timeout 0 maxelem 65536 `",
"`{ iptables -w -C $chain -p $proto -m set --match-set f2b-j-w-ipt-ipset-ap-rwhu src -j REJECT --reject-with icmp-host-unreachable >/dev/null 2>&1; } || "
"{ iptables -w -I $chain -p $proto -m set --match-set f2b-j-w-ipt-ipset-ap-rwhu src -j REJECT --reject-with icmp-host-unreachable; }",
),
'ip6-start': (
"`ipset -exist create f2b-j-w-ipt-ipset-ap-rwhu6 hash:ip timeout 0 maxelem 65536 family inet6`",
"`{ ip6tables -w -C $chain -p $proto -m set --match-set f2b-j-w-ipt-ipset-ap-rwhu6 src -j REJECT --reject-with icmp6-host-unreachable >/dev/null 2>&1; } || "
"{ ip6tables -w -I $chain -p $proto -m set --match-set f2b-j-w-ipt-ipset-ap-rwhu6 src -j REJECT --reject-with icmp6-host-unreachable; }",
),
'flush': (
"`ipset flush f2b-j-w-ipt-ipset-ap-rwhu`",
"`ipset flush f2b-j-w-ipt-ipset-ap-rwhu6`",
),
'stop': (
"`iptables -w -D $chain -p $proto -m set --match-set f2b-j-w-ipt-ipset-ap-rwhu src -j REJECT --reject-with icmp-host-unreachable`",
"`ipset flush f2b-j-w-ipt-ipset-ap-rwhu`",
"`ipset destroy f2b-j-w-ipt-ipset-ap-rwhu 2>/dev/null || { sleep 1; ipset destroy f2b-j-w-ipt-ipset-ap-rwhu; }`",
"`ip6tables -w -D $chain -p $proto -m set --match-set f2b-j-w-ipt-ipset-ap-rwhu6 src -j REJECT --reject-with icmp6-host-unreachable`",
"`ipset flush f2b-j-w-ipt-ipset-ap-rwhu6`",
"`ipset destroy f2b-j-w-ipt-ipset-ap-rwhu6 2>/dev/null || { sleep 1; ipset destroy f2b-j-w-ipt-ipset-ap-rwhu6; }`",
),
'ip4-check': (
r"""`iptables -w -C $chain -p $proto -m set --match-set f2b-j-w-ipt-ipset-ap-rwhu src -j REJECT --reject-with icmp-host-unreachable`""",
),
'ip6-check': (
r"""`ip6tables -w -C $chain -p $proto -m set --match-set f2b-j-w-ipt-ipset-ap-rwhu6 src -j REJECT --reject-with icmp6-host-unreachable`""",
),
'ip4-ban': (
r"`ipset -exist add f2b-j-w-ipt-ipset-ap-rwhu 192.0.2.1 timeout 0`",
),
'ip4-unban': (
r"`ipset -exist del f2b-j-w-ipt-ipset-ap-rwhu 192.0.2.1`",
),
'ip6-ban': (
r"`ipset -exist add f2b-j-w-ipt-ipset-ap-rwhu6 2001:db8:: timeout 0`",
),
'ip6-unban': (
r"`ipset -exist del f2b-j-w-ipt-ipset-ap-rwhu6 2001:db8::`",
),
}),
# iptables (oneport) --
('j-w-iptables', 'iptables[name=%(__name__)s, bantime="10m", port="http", protocol="tcp", chain="<known/chain>"]', {
'ip4': ('`iptables ', 'icmp-port-unreachable'), 'ip6': ('`ip6tables ', 'icmp6-port-unreachable'),

View file

@ -1 +0,0 @@
D /run/fail2ban 0755 root root -

View file

@ -7,13 +7,13 @@ PartOf=iptables.service firewalld.service ip6tables.service ipset.service nftabl
[Service]
Type=simple
Environment="PYTHONNOUSERSITE=1"
ExecStartPre=/bin/mkdir -p /run/fail2ban
RuntimeDirectory=fail2ban
StateDirectory=fail2ban
ExecStart=@BINDIR@/fail2ban-server -xf start
# if should be logged in systemd journal, use following line or set logtarget to sysout in fail2ban.local
# ExecStart=@BINDIR@/fail2ban-server -xf --logtarget=sysout start
ExecStop=@BINDIR@/fail2ban-client stop
ExecReload=@BINDIR@/fail2ban-client reload
PIDFile=/run/fail2ban/fail2ban.pid
Restart=on-failure
RestartPreventExitStatus=0 255

View file

@ -245,8 +245,23 @@ Arguments can be passed to actions to override the default values from the [Init
[name=value,name2=value,name3="values,values"]
.RE
Values can also be quoted (required when value includes a ","). More that one action can be specified (in separate lines).
Values can also be quoted (required when value includes a "," or space). More that one action can be specified (in separate lines).
.br
The action specific arguments can also affect conditional parameters, so for instance to submit different values to different chains
firstly pass the argument affecting all chains, e.g. \fIblocktype\fR, then for IPv6 chain, e. g. \fIblocktype?family=inet6\fR.
Examples:
.RS
.nf
# pass blocktype to DROP for all chains:
banaction_allports = iptables-ipset[type=allports, blocktype=DROP]
# pass different blocktype for IPv4 and IPv6 chains:
banaction = iptables-ipset[type=multiport, blocktype="REJECT --reject-with icmp-host-unreachable", blocktype?family=inet6="REJECT --reject-with icmp6-host-unreachable"]
.fi
.RE
.RE
.TP
.B ignoreself
boolean value (default true) indicates the banning of own IP addresses should be prevented
@ -437,28 +452,67 @@ two commands to be executed.
The following tags are substituted in the actionban, actionunban and actioncheck (when called before actionban/actionunban) commands.
.TP
.B ip
IPv4 IP address to be banned. e.g. 192.168.0.2
IPv4 or IPv6 IP address to be banned. e.g. 192.168.0.2 or 2001:db8::1
.TP
.B family
IP address family as a string. e.g. inet4 or inet6
.TP
.B ip-rev
Reverse DNS PTR record for the IP address, or empty string if unavailable.
.TP
.B ip-host
Hostname resolved from the IP address.
.TP
.B fid
Failure ID (ticket identifier) for this ban event.
.TP
.B failures
number of times the failure occurred in the log file. e.g. 3
.TP
.B ipfailures
As per \fBfailures\fR, but total of all failures for that ip address across all jails from the fail2ban persistent database. Therefore the database must be set for this tag to function.
.TP
.B ipjailfailures
As per \fBipfailures\fR, but total based on the IPs failures for the current jail.
Number of times the failure occurred in the log file for this specific ticket. e.g. 3
.TP
.B time
UNIX (epoch) time of the ban. e.g. 1357508484
.TP
.B bantime
Effective ban duration in seconds for this ticket.
.TP
.B bancount
Number of times this IP address has been banned (cumulative count).
.TP
.B matches
concatenated string of the log file lines of the matches that generated the ban. Many characters interpreted by shell get escaped to prevent injection, nevertheless use with caution.
Concatenated string of the log file lines of the matches that generated the ban. Many characters interpreted by shell get escaped to prevent injection, nevertheless use with caution.
.TP
.B restored
Flag indicating whether the ticket was restored from database (1) or is a new ban (0). Can be used in actions to skip certain operations for restored bans.
.TP
.B F-*
Free-form match tags captured from the filter using \fI<F-TAG>...</F-TAG>\fR syntax in failregex. For example, if filter captures \fI<F-USER>john</F-USER>\fR, the tag \fI<F-USER>\fR will contain "john" and can be used in action commands.
.TP
.B ipfailures
As per \fBfailures\fR, but total of all failures for that IP address across all jails from the fail2ban persistent database. Therefore the database must be set for this tag to function.
.TP
.B ipjailfailures
As per \fBipfailures\fR, but total based on the IP's failures for the current jail only.
.TP
.B ipmatches
As per \fBmatches\fR, but includes all lines for the IP which are contained with the fail2ban persistent database. Therefore the database must be set for this tag to function.
As per \fBmatches\fR, but includes all lines for the IP which are contained within the fail2ban persistent database across all jails. Therefore the database must be set for this tag to function.
.TP
.B ipjailmatches
As per \fBipmatches\fR, but matches are limited for the IP and for the current jail.
As per \fBipmatches\fR, but matches are limited to the IP and the current jail only.
.TP
.B raw-ticket
String representation of the raw ticket object. Primarily useful for debugging purposes.
.TP
.B jail.banned
Number of currently banned IP addresses in the jail at the time of this action.
.TP
.B jail.banned_total
Total cumulative number of bans that have occurred in this jail since fail2ban started.
.TP
.B jail.found
Number of current failure tickets in the jail's fail manager.
.TP
.B jail.found_total
Total cumulative number of failures that have been detected in this jail since fail2ban started.
.SH "PYTHON ACTION FILES"
Python based actions can also be used, where the file name must be \fI[actionname].py\fR. The Python file must contain a variable \fIAction\fR which points to Python class. This class must implement a minimum interface as described by \fIfail2ban.server.action.ActionBase\fR, which can be inherited from to ease implementation.

View file

@ -157,13 +157,6 @@ too many password failures. It updates firewall rules
to reject the IP address or executes user defined
commands.'''
if setuptools:
setup_extra = {
'test_suite': "fail2ban.tests.utils.gatherTests",
}
else:
setup_extra = {}
data_files_extra = []
if os.path.exists('/var/run'):
# if we are on the system with /var/run -- we are to use it for having fail2ban/
@ -249,8 +242,7 @@ setup(
('/var/lib/fail2ban',
''
),
] + data_files_extra,
**setup_extra
] + data_files_extra
)
# Do some checks after installation