2

So I've been experimenting with nftables in CentOS 8 and things are going pretty well, but I'm having a weird problem with fail2ban.

Fail2ban cannot ban IPs with nftables-multiport or nftables-allports. I'm seeing the following errors in fail2ban.log:

2020-01-01 22:34:44,233 fail2ban.actions        [4918]: NOTICE  [nginx-http-auth] Ban 193.148.18.251
2020-01-01 22:34:44,246 fail2ban.utils          [4918]: #39-Lev. 7fe1cc0af618 -- exec: nft add set ip fail2ban f2b-nginx-http-auth \{ type ipv4_addr\; \}
nft insert rule ip fail2ban input tcp dport \{ http,https \} ip saddr @f2b-nginx-http-auth drop
2020-01-01 22:34:44,246 fail2ban.utils          [4918]: ERROR   7fe1cc0af618 -- stderr: 'Error: Could not process rule: No such file or directory'
2020-01-01 22:34:44,246 fail2ban.utils          [4918]: ERROR   7fe1cc0af618 -- stderr: 'add set ip fail2ban f2b-nginx-http-auth { type ipv4_addr; }'
2020-01-01 22:34:44,246 fail2ban.utils          [4918]: ERROR   7fe1cc0af618 -- stderr: '           ^^^^^^^^'
2020-01-01 22:34:44,246 fail2ban.utils          [4918]: ERROR   7fe1cc0af618 -- stderr: 'Error: Could not process rule: No such file or directory'
2020-01-01 22:34:44,246 fail2ban.utils          [4918]: ERROR   7fe1cc0af618 -- stderr: 'insert rule ip fail2ban input tcp dport { http,https } ip saddr @f2b-nginx-http-auth drop'
2020-01-01 22:34:44,246 fail2ban.utils          [4918]: ERROR   7fe1cc0af618 -- stderr: '               ^^^^^^^^'
2020-01-01 22:34:44,246 fail2ban.utils          [4918]: ERROR   7fe1cc0af618 -- returned 1
2020-01-01 22:34:44,246 fail2ban.actions        [4918]: ERROR   Failed to execute ban jail 'nginx-http-auth' action 'nftables-multiport' info 'ActionInfo({'ip': '193.148.18.251', 'family': 'inet4', 'fid': <function Actions.ActionInfo.<lambda> at 0x7fe1cd131158>, 'raw-ticket': <function Actions.ActionInfo.<lambda> at 0x7fe1cd1316a8>})': Error starting action Jail('nginx-http-auth')/nftables-multiport

It's acting as though the table fail2ban and the corresponding chain input don't exist, except they do.

$ sudo nft list tables
table ip fail2ban
table inet filter
table ip nat
$ sudo nft list table ip fail2ban
table ip fail2ban {
    chain input {
        type filter hook input priority 100; policy accept;
    }
}

When I add sets and rules manually to the chain it works, just not when done by fail2ban. I also temporarily set SELinux to permissive so I know that's not the issue. If anyone has any suggestions I'd appreciate it!

Edit: Added jail.local as requested, also added action.d/nftables-common.local since modifications were made there too

jail.local:

#
# WARNING: heavily refactored in 0.9.0 release.  Please review and
#          customize settings for your setup.
#
# Changes:  in most of the cases you should not modify this
#           file, but provide customizations in jail.local file,
#           or separate .conf files under jail.d/ directory, e.g.:
#
# HOW TO ACTIVATE JAILS:
#
# YOU SHOULD NOT MODIFY THIS FILE.
#
# It will probably be overwritten or improved in a distribution update.
#
# Provide customizations in a jail.local file or a jail.d/customisation.local.
# For example to change the default bantime for all jails and to enable the
# ssh-iptables jail the following (uncommented) would appear in the .local file.
# See man 5 jail.conf for details.
#
# [DEFAULT]
# bantime = 1h
#
# [sshd]
# enabled = true
#
# See jail.conf(5) man page for more information



# Comments: use '#' for comment lines and ';' (following a space) for inline comments


[INCLUDES]

#before = paths-distro.conf
before = paths-fedora.conf

# The DEFAULT allows a global definition of the options. They can be overridden
# in each jail afterwards.

[DEFAULT]

#
# MISCELLANEOUS OPTIONS
#

# "ignoreself" specifies whether the local resp. own IP addresses should be ignored
# (default is true). Fail2ban will not ban a host which matches such addresses.
#ignoreself = true

# "ignoreip" can be a list of IP addresses, CIDR masks or DNS hosts. Fail2ban
# will not ban a host which matches an address in this list. Several addresses
# can be defined using space (and/or comma) separator.
ignoreip = 127.0.0.1/8 ::1 192.168.1.0/24

# External command that will take an tagged arguments to ignore, e.g. <ip>,
# and return true if the IP is to be ignored. False otherwise.
#
# ignorecommand = /path/to/command <ip>
ignorecommand =

# "bantime" is the number of seconds that a host is banned.
bantime  = 10m

# A host is banned if it has generated "maxretry" during the last "findtime"
# seconds.
findtime  = 30m

# "maxretry" is the number of failures before a host get banned.
maxretry = 3

# "backend" specifies the backend used to get files modification.
# Available options are "pyinotify", "gamin", "polling", "systemd" and "auto".
# This option can be overridden in each jail as well.
#
# pyinotify: requires pyinotify (a file alteration monitor) to be installed.
#              If pyinotify is not installed, Fail2ban will use auto.
# gamin:     requires Gamin (a file alteration monitor) to be installed.
#              If Gamin is not installed, Fail2ban will use auto.
# polling:   uses a polling algorithm which does not require external libraries.
# systemd:   uses systemd python library to access the systemd journal.
#              Specifying "logpath" is not valid for this backend.
#              See "journalmatch" in the jails associated filter config
# auto:      will try to use the following backends, in order:
#              pyinotify, gamin, polling.
#
# Note: if systemd backend is chosen as the default but you enable a jail
#       for which logs are present only in its own log files, specify some other
#       backend for that jail (e.g. polling) and provide empty value for
#       journalmatch. See https://github.com/fail2ban/fail2ban/issues/959#issuecomment-74901200
backend = auto

# "usedns" specifies if jails should trust hostnames in logs,
#   warn when DNS lookups are performed, or ignore all hostnames in logs
#
# yes:   if a hostname is encountered, a DNS lookup will be performed.
# warn:  if a hostname is encountered, a DNS lookup will be performed,
#        but it will be logged as a warning.
# no:    if a hostname is encountered, will not be used for banning,
#        but it will be logged as info.
# raw:   use raw value (no hostname), allow use it for no-host filters/actions (example user)
usedns = warn

# "logencoding" specifies the encoding of the log files handled by the jail
#   This is used to decode the lines from the log file.
#   Typical examples:  "ascii", "utf-8"
#
#   auto:   will use the system locale setting
logencoding = auto

# "enabled" enables the jails.
#  By default all jails are disabled, and it should stay this way.
#  Enable only relevant to your setup jails in your .local or jail.d/*.conf
#
# true:  jail will be enabled and log files will get monitored for changes
# false: jail is not enabled
enabled = false


# "mode" defines the mode of the filter (see corresponding filter implementation for more info).
mode = normal

# "filter" defines the filter to use by the jail.
#  By default jails have names matching their filter name
#
filter = %(__name__)s[mode=%(mode)s]


#
# ACTIONS
#

# Some options used for actions

# Destination email address used solely for the interpolations in
# jail.{conf,local,d/*} configuration files.
destemail = user

# Sender email address used solely for some actions
sender = root

# E-mail action. Since 0.8.1 Fail2Ban uses sendmail MTA for the
# mailing. Change mta configuration parameter to mail if you want to
# revert to conventional 'mail'.
mta = mail

# Default protocol
protocol = tcp

# Specify chain where jumps would need to be added in ban-actions expecting parameter chain
chain = input

# Ports to be banned
# Usually should be overridden in a particular jail
port = 0:65535

# Format of user-agent https://tools.ietf.org/html/rfc7231#section-5.5.3
fail2ban_agent = Fail2Ban/%(fail2ban_version)s

#
# Action shortcuts. To be used to define action parameter

# Default banning action (e.g. iptables, iptables-new,
# iptables-multiport, shorewall, etc) It is used to define
# action_* variables. Can be overridden globally or per
# section within jail.local file
banaction = nftables-multiport
banaction_allports = nftables-allports

# The simplest action to take: ban only
action_ = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]

# ban & send an e-mail with whois report to the destemail.
action_mw = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
            %(mta)s-whois[name=%(__name__)s, sender="%(sender)s", dest="%(destemail)s", protocol="%(protocol)s", chain="%(chain)s"]

# ban & send an e-mail with whois report and relevant log lines
# to the destemail.
action_mwl = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
             %(mta)s-whois-lines[name=%(__name__)s, sender="%(sender)s", dest="%(destemail)s", logpath=%(logpath)s, chain="%(chain)s"]

# See the IMPORTANT note in action.d/xarf-login-attack for when to use this action
#
# ban & send a xarf e-mail to abuse contact of IP address and include relevant log lines
# to the destemail.
#action_xarf = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
#             xarf-login-attack[service=%(__name__)s, sender="%(sender)s", logpath=%(logpath)s, port="%(port)s"]

# ban IP on CloudFlare & send an e-mail with whois report and relevant log lines
# to the destemail.
#action_cf_mwl = cloudflare[cfuser="%(cfemail)s", cftoken="%(cfapikey)s"]
#                %(mta)s-whois-lines[name=%(__name__)s, sender="%(sender)s", dest="%(destemail)s", logpath=%(logpath)s, chain="%(chain)s"]

# Report block via blocklist.de fail2ban reporting service API
#
# See the IMPORTANT note in action.d/blocklist_de.conf for when to use this action.
# Specify expected parameters in file action.d/blocklist_de.local or if the interpolation
# `action_blocklist_de` used for the action, set value of `blocklist_de_apikey`
# in your `jail.local` globally (section [DEFAULT]) or per specific jail section (resp. in
# corresponding jail.d/my-jail.local file).
#
action_blocklist_de  = blocklist_de[email="%(sender)s", service=%(filter)s, apikey="%(blocklist_de_apikey)s", agent="%(fail2ban_agent)s"]

# Report ban via badips.com, and use as blacklist
#
# See BadIPsAction docstring in config/action.d/badips.py for
# documentation for this action.
#
# NOTE: This action relies on banaction being present on start and therefore
# should be last action defined for a jail.
#
action_badips = badips.py[category="%(__name__)s", banaction="%(banaction)s", agent="%(fail2ban_agent)s"]
#
# Report ban via badips.com (uses action.d/badips.conf for reporting only)
#
action_badips_report = badips[category="%(__name__)s", agent="%(fail2ban_agent)s"]

# Report ban via abuseipdb.com.
#
# See action.d/abuseipdb.conf for usage example and details.
#
action_abuseipdb = abuseipdb

# Choose default action.  To change, just override value of 'action' with the
# interpolation to the chosen action shortcut (e.g.  action_mw, action_mwl, etc) in jail.local
# globally (section [DEFAULT]) or per specific section
action = %(action_)s


#
# JAILS
#


# SSH

[sshd]
enabled = true
filter = sshd
port = ssh

# VSFTPD

[vsftpd]

enabled  = true
filter   = vsftpd
port     = ftp
logpath  = /var/log/vsftpd.log

# Nginx

[nginx-http-auth]

enabled  = true
filter   = nginx-http-auth
port     = http,https
logpath  = /var/log/nginx/*error*.log

[nginx-noscript]

enabled  = false
port     = http,https
filter   = nginx-noscript
logpath  = /var/log/nginx/*access*.log
maxretry = 6

[nginx-badbots]

enabled  = true
port     = http,https
filter   = nginx-badbots
logpath  = /var/log/nginx/*access*.log
maxretry = 2

[nginx-nohome]

enabled  = true
port     = http,https
filter   = nginx-nohome
logpath  = /var/log/nginx/*access*.log
maxretry = 2

[nginx-noproxy]

enabled  = true
port     = http,https
filter   = nginx-noproxy
logpath  = /var/log/nginx/*access*.log
maxretry = 2

nftables-common.local

[Init]
# Definition of the table used
nftables_family = ip
nftables_table  = fail2ban

# Drop packets
blocktype       = drop

# Remove nftables prefix. Set names are limited to 15 char so we want them all
nftables_set_prefix =

Edit2: fail2ban.log with --debug=all added to the nft commands in nftables-common.conf. It is too long to paste here directly so I put it on pastebin.

https://pastebin.com/F7aeNQjg

Edit3: nginx-http-auth.conf

$ cat /etc/fail2ban/filter.d/nginx-http-auth.conf
# fail2ban filter configuration for nginx

[Definition]

failregex = ^ \[error\] \d+#\d+: \*\d+ user "(?:[^"]+|.*?)":? (?:password mismatch|was not found in "[^\"]*"), client: <HOST>, server: \S*, request: "\S+ \S+ HTTP/\d+\.\d+", host: "\S+"(?:, referrer: "\S+")?\s*$
            ^ \[error\] \d+#\d+: \*\d+ no user/password was provided for basic authentication, client: <HOST>, server: \S+, request: "\S+ \S+ HTTP/\d+\.\d+", host: "\S+"\s*$

ignoreregex = 

datepattern = {^LN-BEG}
  • can you also share contents of your jail.local file. – Mohit Malviya Jan 3 at 6:39
  • Done. I also added nftables-common.local since I made some modifications there too. – b6-redacted Jan 3 at 15:23
  • First do you have version 10.4 from 2018. Second try and execute the commands manually. I found a typo for firewallcmd-ipset, a single missed space broke the whole ipset function, and wasn't fixed until it was reported. – cybernard Jan 3 at 15:45
  • Yes, v0.10.4. The nftables commands do work when typed manually. – b6-redacted Jan 3 at 16:01
  • Instead of typing them, copy and paste them from the log file. If there's a missing space or other trivial error you may not detect it. Especially the create command since that is where I detected a missing space. – cybernard Jan 3 at 16:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.