Debian 12 JournalCtl / Systemd configuration

Hi there,

Thanks for building a really great integration with Fail2ban!

So I have had this working for a while on other OS distributions, but recently we upgraded some servers to Debian 12, and it stopped working.

The main issue is that Debian 12 no longer uses systemd. I found a workaround by installing perl-systemd so that is now integrating the messages into Journalctl.

After reading some forums I read that you suggested to use LOG_LOCAL0 as the output to get them into the actual JournalCtl logs. This is working now in terms of reporting the messages properly to JournalCtl.

I still couldn’t get the fail2ban configurations to work afterwards though. So I contacted Fail2ban via this issue, and after making additional changes I was able to make fail2ban to read the messaging.

I wanted to report it back here just to be complete for others not to spend the numerous hours I did. Also, Serge suggested a few things that I think “might” be relevant or helpful for the existing plugin.

Suggestions:

  • Use one regex line to optimize speed (see below)
  • Remove the _daemon trigger since we can use the Regex to actually retrieve/ignore it
  • Use <ADDR> in light of <HOST> because it is also faster, but maybe there is a reason you are using <HOST>?

My changes:

  • I couldn’t use the %(__prefix_lines) because it was failing on the date output. In my case it was like 2024-06-06T07:39:58.980111-04:00, and in common.conf it doesn’t seem to have a proper date formatting. Then I realized that we don’t really need that and could just throw the entire parsing into the regex and be somewhat independent, but I don’t know if this works on other OSs (that would need testing)
  • Changed backend to use backend= systemd in the filter configuration
  • Added a journalmatch = SYSTEMD_UNIT=php8.0-fpm.service to target the proper journal. I couldn’t leave it blank since the conf fails, and I could never get logpath to work

Jail configuration

[wordpress-hard]
enabled = true
filter = wordpress-hard
backend = systemd
journalmatch = _SYSTEMD_UNIT=php8.0-fpm.service
maxretry = 1
port = http,https

[wordpress-soft]
enabled = true
filter = wordpress-soft
backend = systemd
journalmatch = _SYSTEMD_UNIT=php8.0-fpm.service
maxretry = 5
port = http,https

[wordpress-extra]
enabled = true
filter = wordpress-extra
backend = systemd
journalmatch = _SYSTEMD_UNIT=php8.0-fpm.service
maxretry = 3
port = http,https

wordpress-soft.conf

# Fail2Ban filter for soft failures
# Auto-generated: 2024-01-28T18:48:23+00:00
#

[INCLUDES]

before = common.conf

[Definition]

failregex = ([^\s]+)\s+(?:wordpress|wp)(?:\([^\)]*\))?\[\d+\]: (?:Emptyusername|Authentication failure for .*|REST authentication failure for .*|XML-RPC authentication failure for .*|Authentication attempt for unknown user .*|Blocked username authentication attempt for .*|Pingback requested from|Comment attempt on .* post \d+) from <ADDR>$

ignoreregex =

wordpress-hard.conf

# Fail2Ban filter for hard failures
# Auto-generated: 2024-01-28T18:48:23+00:00
#

[INCLUDES]

before = common.conf

[Definition]

failregex = ([^\s]+)\s+(?:wordpress|wp)(?:\([^\)]*\))?\[\d+\]: (?:Untrusted X-Forwarded-For header|REST authentication attempt for unknown user  .*|XML-RPC authentication attempt for unknown user .*|Immediately block connections|Blocked access from country '..'|XML-RPC request blocked|.*; Bogus Pingback|Akismet discarded spam comment|Spam comment \d+|Blocked authentication attempt for .*|XML-RPC multicall authentication failure|Pingback error .* generated|Blocked user enumeration attempt) from <ADDR>$

ignoreregex =

wordpress-extra.conf

# Fail2Ban filter for extra failures
# Auto-generated: 2024-01-28T18:48:23+00:00
#

[INCLUDES]

before = common.conf

[Definition]

failregex = ([^\s]+)\s+(?:wordpress|wp)(?:\([^\)]*\))?\[\d+\]: (?:Password reset requested for .*|Comment \d+) from <ADDR>$

ignoreregex = 

Thanks for the kind words and for taking the time to write this up!

I’m going to cover a few of the issues you’ve mentioned, but before I start there’s one crucial thing I must address.


Please do not use your rules as given here.

Using an unanchored rule is inherently dangerous: it allows an attacker to craft a message to ban any IP they choose.

This is not theoretical; it happened with Apache.


regex

The rules aren’t mashed into a single regex because:

  1. normal people wouldn’t be able to read it, and
  2. sysadmins wouldn’t (easily) be able to create custom filters.

I’ve tried hard to come up with filters that should work well for most people most of the time, but they’re only suggestions. If the grouping of rules doesn’t work for your site you should be able to change them.

So, one rule, one regex.

journald

My advice remains the same: if at all possible, avoid journald and use a real syslog:

  • rsyslog is a good traditional-style syslog; used by Debian until v12.
  • syslog-ng has all the bells and whistles you could ever want.

That said, I accept the Linux world is determined to rediscover why binary logging is a bad idea and will use journald.

WPf2b v5.3

:warning: This doesn’t work with v5.2.x. I plan to release v5.3 at the end of this week or early next.

wp-config.php:

// Make sure we're not using the short ("wp") tag
define('WP_FAIL2BAN_SYSLOG_SHORT_TAG', false);

// Don't include the HTTP host in the tag
define('WP_FAIL2BAN_SYSLOG_TAG_HOST`, false);

Basically, as the name suggests, it leaves the HTTP host out of the tag; it’s the best I can do without changing the filter rules. That means you can simplify the jails:

[DEFAULT]
backend = systemd
journalmatch = SYSLOG_IDENTIFIER=wordpress

[wordpress-hard]
enabled = true
filter = wordpress-hard.conf
maxretry = 1
port = http, https

WPf2b v6.0

:warning: This is all subject to change - v6 is still under development.

v6 will make things even simpler, and add support for some of the newer fail2ban features.

wp-config.php:

// Make sure we're not using the short ("wp") tag
define('WP_FAIL2BAN_SYSLOG_SHORT_TAG', false);

// Put the HTTP host in the body of the message
define('WP_FAIL2BAN_SYSLOG_INLINE_HOST`, true);

Again, as the name suggests, this will put the HTTP host in the body of the message. This is absolutely not backward-compatible in any way hence waiting for the next major version, and will produce messages like:

wordpress[1234]: Authentication attempt on www.example.com for unknown user foobar from 10.1.2.3

This will be matched by a modified regex:

^%(__prefix_line)sAuthentication attempt%(_site)s for unknown user <F-ALT_USER>.*</F-ALT_USER> from <ADDR>$

This provides some fields you can use in actions:

  • F-ALT_USER - the offending username,
  • F-SITE1 & F-SITE2 - (the names will certainly change) - The HTTP host matched from the tag, and from the message body, respectively.

journalmatch will be included in the filters, so the jails are even simpler:

[DEFAULT]
backend = systemd

[wordpress-hard]
enabled = true
filter = wordpress-hard.conf
maxretry = 1
port = http, https