5

EDIT: added additional .conf filer and slightly changed wording as suggested by Marco


I'm running Fail2ban v0.10 which is supposed to support IPv6.

I've set up Fail2ban with nftables according to these instructions, with the exception that I used the 'inet' family for nftables instead of the ip family because I'd like to allow IPv6 traffic to my server.

The server is reachable over IPv6 and my firewall (nftables) seems to be configured correctly as far as I can see (the table inet filter).

However the 'table inet fail2ban' is why I'm making this post, it seems to me Fail2ban only reads the IPv4 logs, and blocks offending IPv4 hosts.

Am I reading this right? If so does anyone know how I can make Fail2ban work with IPv6 traffic as well? I know the Fail2ban v0.10 changelog states that not all ban-actions are IPv6 capable yet, but I can't seem to find a list.

A link to where I could finde the info is welcome as well, because I couldn't seem to finde that myself.

I've only included the recidive jail configs because I assume that if I can get jail to work with IPv6, I can do the same with the others, if I'm mistaken with that assumption please do tell me :)


My nftables ruleset:

table inet filter {
    chain input {
        type filter hook input priority 0; policy accept;
        ct state { related, established} accept
        ct state invalid drop
        iifname "lo" accept
        ip protocol icmp accept
        ip6 nexthdr ipv6-icmp accept
        tcp dport ssh accept
        tcp dport http accept
        tcp dport https accept
        limit rate 5/minute burst 5 packets counter packets 972 bytes 56710 log prefix " denied: " level debug
        drop
    }

    chain forward {
        type filter hook forward priority 0; policy accept;
        drop
    }

    chain output {
        type filter hook output priority 0; policy accept;
        accept
    }
}

table inet fail2ban {
    set f2b-sshd {
        type ipv4_addr
    }

    set f2b-nginx-botsearch {
        type ipv4_addr
    }

    set f2b-recidive {
        type ipv4_addr
    }

    chain INPUT {
        type filter hook input priority 100; policy accept;
        ip protocol hopopt-reserved ip saddr @f2b-recidive drop
        tcp dport { http, https} ip saddr @f2b-nginx-botsearch drop
        tcp dport { ssh} ip saddr @f2b-sshd drop
    }
}

/etc/nftables/fail2ban.conf

#!/usr/sbin/nft -f

table inet fail2ban {
        chain INPUT {
                type filter hook input priority 100;
        }
}

/etc/nftables.conf

#!/usr/bin/nft -f

table inet filter {
  chain input {
    type filter hook input priority 0;

    ct state {established, related} accept

    ct state invalid drop

    iifname lo accept

    ip protocol icmp accept
    ip6 nexthdr icmpv6 accept

    tcp dport ssh accept

    tcp dport http accept
    tcp dport https accept

    limit rate 5/minute burst 5 packets counter packets 0 bytes 0 log prefix " denied: " level debug

    drop
  }
  chain forward {
    type filter hook forward priority 0;
    drop
  }
  chain output {
    type filter hook output priority 0;
    accept
  }

}

include "/etc/nftables/fail2ban.conf"

/etc/fail2ban/action.d/nftables-common.local

[Init]
nftables_family = inet
nftables_table  = fail2ban

blocktype       = drop

nftables_set_prefix =

/etc/fail2ban/jail.local

[INCLUDES]

before = paths-arch.conf

[DEFAULT]

ignorecommand =

bantime  = 1h

findtime  = 10m

maxretry = 5

usedns = warn

logencoding = auto

enabled = false

filter = %(__name__)s

protocol = tcp

chain = INPUT

port = 0:65535

fail2ban_agent = Fail2Ban/%(fail2ban_version)s

banaction = nftables-multiport
banaction_allports = nftables-allports

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"]
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"]
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"]
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"]

action_blocklist_de  = blocklist_de[email="%(sender)s", service=%(filter)s, apikey="%(blocklist_de_apikey)s", agent="%(fail2ban_agent)s"]

action_badips = badips.py[category="%(__name__)s", banaction="%(banaction)s", agent="%(fail2ban_agent)s"]

action_badips_report = badips[category="%(__name__)s", agent="%(fail2ban_agent)s"]

action_abuseipdb = abuseipdb

action = %(action_)s


[sshd]
enabled = true
mode    = normal
filter  = sshd[mode=%(mode)s]
port    = ssh
logpath = %(sshd_log)s
backend = %(sshd_backend)s


[nginx-botsearch]
enabled  = true
port     = http,https
logpath  = %(nginx_error_log)s
maxretry = 2

[recidive]
enabled = true
logpath  = /var/log/fail2ban.log
banaction = %(banaction_allports)s
bantime  = 1w
findtime = 1d
maxretry  = 3
protocol  = 0-255

/etc/fail2ban/filter.d/recidive.conf

[INCLUDES]

before = common.conf

[Definition]

_daemon = fail2ban\.actions\s*

_jailname = recidive

failregex = ^(%(__prefix_line)s| %(_daemon)s%(__pid_re)s?:\s+)NOTICE\s+\[(?!%(_jailname)s\])(?:.*)\]\s+Ban\s+<HOST>\s*$

ignoreregex =

[Init]

journalmatch = _SYSTEMD_UNIT=fail2ban.service PRIORITY=5

/etc/fail2ban/filter.d/common.conf

[DEFAULT]

_daemon = \S*

__pid_re = (?:\[\d+\])

__daemon_re = [\[\(]?%(_daemon)s(?:\(\S+\))?[\]\)]?:?

__daemon_extra_re = \[ID \d+ \S+\]

__daemon_combs_re = (?:%(__pid_re)s?:\s+%(__daemon_re)s|%(__daemon_re)s%(__pid_re)s?:?)

__kernel_prefix = kernel: \[ *\d+\.\d+\]

__hostname = \S+

__md5hex = (?:[\da-f]{2}:){15}[\da-f]{2}

__bsd_syslog_verbose = <[^.]+\.[^.]+>

__vserver = @vserver_\S+

__date_ambit = (?:\[\])

__prefix_line = %(__date_ambit)s?\s*(?:%(__bsd_syslog_verbose)s\s+)?(?:%(__hostname)s\s+)?(?:%(__kernel_prefix)s\s+)?(?:%(__vserver)s\s+)$

__pam_auth = pam_unix

datepattern = {^LN-BEG}
Didier
  • 53

2 Answers2

2

This might have been this bug, fixed in v0.10.1

danblack
  • 1,299
  • 13
  • 15
1

This seems to work fine for me on version 1.0.2 under Debian bullseye from the default repos.

The "inet" type table is the correct to have rules for either address family. "ip" would be specific for IPv4, or "ip6" would be specific for IPv6. "inet" in this case allows rules of both types.

On my system if an IPv6 address is banned a new set gets added to the fail2ban table, called "addr6-set-sshd" (for ssh). And a rule is added to the f2b-chain blocking this for IPv6. For example, this is the table when I do 'nft list ruleset'

table inet f2b-table {
    set addr-set-sshd {
        type ipv4_addr
        elements = { <blocked_v4_ips> }
    }
set addr6-set-sshd {
    type ipv6_addr
    elements = { &lt;blocked_v6_ips&gt; }
}

chain f2b-chain {
    type filter hook input priority filter - 1; policy accept;
    tcp dport 22 ip saddr @addr-set-sshd drop
    tcp dport 22 ip6 saddr @addr6-set-sshd drop
}

}

What may be confusing is that the drop rules and sets are not created if fail2ban has no entries to place in them. Which makes sense and would be most optimal. But that means if there are no blocked IPv6 addresses you won't see the entries there.

Given the vastness of the IPv6 address space scanning it for open ports is much less common than with IPv4. So it is not at all unlikely you've just had no attempts over v6 that would trigger creation.

If you run "fail2ban-client status " and see an IPv6 address in the banned list then you should see the entry when you do "nft list ruleset". Otherwise it's expected to be missing, and likely working, but you might want to test failing to be sure.