From 1cda50ce05cc150af8d565301266e910b855959e Mon Sep 17 00:00:00 2001 From: Monson Shao Date: Fri, 3 Nov 2017 20:52:56 +0800 Subject: [PATCH 1/9] Rewrite nftables variables based on nftables' logic. Add an example for redirecting. --- config/action.d/nftables-allports.conf | 8 +- config/action.d/nftables-common.conf | 101 ++++++++++++++---------- config/action.d/nftables-multiport.conf | 8 +- 3 files changed, 66 insertions(+), 51 deletions(-) diff --git a/config/action.d/nftables-allports.conf b/config/action.d/nftables-allports.conf index 6c69da39..eb17287a 100644 --- a/config/action.d/nftables-allports.conf +++ b/config/action.d/nftables-allports.conf @@ -13,10 +13,10 @@ before = nftables-common.conf [Definition] -# Option: nftables_mode -# Notes.: additional expressions for nftables filter rule -# Values: nftables expressions +# Option: match +# Notes.: additional matches for nftables filter rule +# Values: nftables matches # -nftables_mode = meta l4proto +match = meta l4proto [Init] diff --git a/config/action.d/nftables-common.conf b/config/action.d/nftables-common.conf index 37045712..4ed78f2f 100644 --- a/config/action.d/nftables-common.conf +++ b/config/action.d/nftables-common.conf @@ -11,6 +11,14 @@ # used in all nftables based actions by default. # # The user can override the defaults in nftables-common.local +# Example: redirect flow to honeypot +# +# [Init] +# table_family = ip +# chain_type = nat +# chain_hook = prerouting +# chain_priority = -50 +# blocktype = counter redirect to 2222 [INCLUDES] @@ -18,35 +26,38 @@ after = nftables-common.local [Definition] -# Option: nftables_mode -# Notes.: additional expressions for nftables filter rule -# Values: nftables expressions +# Option: match +# Notes.: additional matches for nftables filter rule. +# leaving it empty will block all. (include udp and icmp) +# Values: nftables matches # -nftables_mode = dport \{ \} +match = dport \{ \} # Option: actionstart # Notes.: command executed on demand at the first ban (or at the start of Fail2Ban if actionstart_on_demand is set to false). # Values: CMD # -actionstart = add set \{ type \; \} - insert rule %(nftables_mode)s saddr @ +actionstart = add table f2b-table + -- add chain f2b-table f2b-chain \{ type hook priority \; \} + add set f2b-table \{ type \; \} + add rule f2b-table f2b-chain %(match)s saddr @ -_nft_list = --handle --numeric list chain -_nft_get_handle_id = grep -m1 ' saddr @ # handle' | grep -oe ' handle [0-9]*' +_nft_list = -a list chain f2b-table f2b-chain +_nft_get_handle_id = grep -m1 '@ ' | grep -oe ' handle [0-9]*' # Option: actionstop # Notes.: command executed at the stop of jail (or at the end of Fail2Ban) # Values: CMD # actionstop = HANDLE_ID=$(%(_nft_list)s | %(_nft_get_handle_id)s) - delete rule $HANDLE_ID - delete set + delete rule f2b-table f2b-chain $HANDLE_ID + delete set f2b-table # Option: actioncheck # Notes.: command executed once before each actionban command # Values: CMD # -actioncheck = list chain | grep -q '@[ \t]' +actioncheck = list chain f2b-table f2b-chain | grep -q '@[ \t]' # Option: actionban # Notes.: command executed when banning an IP. Take care that the @@ -54,7 +65,7 @@ actioncheck = list chain | # Tags: See jail.conf(5) man page # Values: CMD # -actionban = add element \{ \} +actionban = add element f2b-table # Option: actionunban # Notes.: command executed when unbanning an IP. Take care that the @@ -62,33 +73,38 @@ actionban = add element # Tags: See jail.conf(5) man page # Values: CMD # -actionunban = delete element \{ \} +actionunban = delete element f2b-table [Init] -# Option: nftables_type +# Option: table_family +# Notes.: address family to work in +# Values: [ip | ip6 | inet] Default: inet +table_family = inet + +# Option: chain_type +# Notes.: refers to the kind of chain to be created +# Values: [filter | route | nat] Default: filter +# +chain_type = filter + +# Option: chain_hook +# Notes.: refers to the kind of chain to be created +# Values: [ prerouting | input | forward | output | postrouting ] Default: input +# +chain_hook = input + +# Option: chain_priority +# Notes.: priority in the chain. +# Values: NUMBER Default: -1 +# +chain_priority = -1 + +# Option: addr_type # Notes.: address type to work with # Values: [ipv4_addr | ipv6_addr] Default: ipv4_addr # -nftables_type = ipv4_addr - -# Option: nftables_family -# Notes.: address family to work in -# Values: [ip | ip6 | inet] Default: inet -# -nftables_family = inet - -# Option: nftables_table -# Notes.: table in the address family to work in -# Values: STRING Default: filter -# -nftables_table = filter - -# Option: chain -# Notes specifies the nftables chain to which the Fail2Ban rules should be -# added -# Values: STRING Default: input -chain = input +addr_type = ipv4_addr # Default name of the filtering set # @@ -108,8 +124,8 @@ protocol = tcp # Option: blocktype # Note: This is what the action does with rules. This can be any jump target -# as per the nftables man page (section 8). Common values are drop -# reject, reject with icmp type host-unreachable +# as per the nftables man page (section 8). Common values are drop, +# reject, reject with icmpx type host-unreachable, redirect to 2222 # Values: STRING blocktype = reject @@ -118,18 +134,17 @@ blocktype = reject # Values: STRING nftables = nft -# Option: set_name +# Option: addr_set # Notes.: The name of the nft set used to store banned addresses # Values: STRING -set_name = f2b- +addr_set = addr-set- -# Option: address_family +# Option: addr_family # Notes.: The family of the banned addresses # Values: [ ip | ip6 ] -address_family = ip +addr_family = ip [Init?family=inet6] - -nftables_type = ipv6_addr -set_name = f2b-6 -address_family = ip6 +addr_family = ip6 +addr_type = ipv6_addr +addr_set = addr6-set- diff --git a/config/action.d/nftables-multiport.conf b/config/action.d/nftables-multiport.conf index d1afafb3..6e3775ae 100644 --- a/config/action.d/nftables-multiport.conf +++ b/config/action.d/nftables-multiport.conf @@ -13,10 +13,10 @@ before = nftables-common.conf [Definition] -# Option: nftables_mode -# Notes.: additional expressions for nftables filter rule -# Values: nftables expressions +# Option: match +# Notes.: additional matches for nftables filter rule +# Values: nftables matches # -nftables_mode = dport \{ \} +match = dport \{ \} [Init] From dde51b46820a3cf66b79667cc75a00f8465353af Mon Sep 17 00:00:00 2001 From: Ririsoft Date: Sat, 6 Oct 2018 15:06:14 +0200 Subject: [PATCH 2/9] fix actionban/unban ip definition syntax --- config/action.d/nftables-common.conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/action.d/nftables-common.conf b/config/action.d/nftables-common.conf index 4ed78f2f..6765fbb3 100644 --- a/config/action.d/nftables-common.conf +++ b/config/action.d/nftables-common.conf @@ -65,7 +65,7 @@ actioncheck = list chain f2b-table f2b-chain | grep -q # Tags: See jail.conf(5) man page # Values: CMD # -actionban = add element f2b-table +actionban = add element f2b-table { } # Option: actionunban # Notes.: command executed when unbanning an IP. Take care that the @@ -73,7 +73,7 @@ actionban = add element f2b-table # Tags: See jail.conf(5) man page # Values: CMD # -actionunban = delete element f2b-table +actionunban = delete element f2b-table { } [Init] From c59d49da225d415428847d97d86e853b79d7a884 Mon Sep 17 00:00:00 2001 From: sebres Date: Tue, 24 Sep 2019 18:46:41 +0200 Subject: [PATCH 3/9] nftables-allports: support multiple protocols in single rule; tests/servertestcase.py: added coverage for nftables actions --- config/action.d/nftables-allports.conf | 2 +- fail2ban/tests/servertestcase.py | 94 +++++++++++++++++++++++++- 2 files changed, 93 insertions(+), 3 deletions(-) diff --git a/config/action.d/nftables-allports.conf b/config/action.d/nftables-allports.conf index eb17287a..fad5273a 100644 --- a/config/action.d/nftables-allports.conf +++ b/config/action.d/nftables-allports.conf @@ -17,6 +17,6 @@ before = nftables-common.conf # Notes.: additional matches for nftables filter rule # Values: nftables matches # -match = meta l4proto +match = meta l4proto \{ \} [Init] diff --git a/fail2ban/tests/servertestcase.py b/fail2ban/tests/servertestcase.py index 4d4362db..6375ea24 100644 --- a/fail2ban/tests/servertestcase.py +++ b/fail2ban/tests/servertestcase.py @@ -1256,6 +1256,96 @@ class ServerConfigReaderTests(LogCaptureTestCase): # 'start', 'stop' - should be found (logged) on action start/stop, # etc. testJailsActions = ( + # nftables-multiport -- + ('j-w-nft-mp', 'nftables-multiport[name=%(__name__)s, port="http,https", protocol="tcp"]', { + 'ip4': ('ip ', 'ipv4_addr', 'addr-'), 'ip6': ('ip6 ', 'ipv6_addr', 'addr6-'), + '*-start': ( + r"`nft add table inet f2b-table`", + 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-mp \{ type ipv4_addr\; \}`", + r"`nft add rule inet f2b-table f2b-chain tcp dport \{ http,https \} 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 rule inet f2b-table f2b-chain tcp dport \{ http,https \} ip6 saddr @addr6-set-j-w-nft-mp reject`", + ), + 'flush': ( + # todo + ), + 'stop': ( + "`HANDLE_ID=$(nft -a list chain inet f2b-table f2b-chain | grep -m1 '@addr-set-j-w-nft-mp ' | grep -oe ' handle [0-9]*')`", + "`nft delete rule inet f2b-table f2b-chain $HANDLE_ID`", + "`nft delete set inet f2b-table addr-set-j-w-nft-mp`", + "`HANDLE_ID=$(nft -a list chain inet f2b-table f2b-chain | grep -m1 '@addr6-set-j-w-nft-mp ' | grep -oe ' handle [0-9]*')`", + "`nft delete rule inet f2b-table f2b-chain $HANDLE_ID`", + "`nft delete set inet f2b-table addr6-set-j-w-nft-mp`", + ), + 'ip4-check': ( + r"`nft list chain inet f2b-table f2b-chain | grep -q '@addr-set-j-w-nft-mp[ \t]'`", + ), + 'ip6-check': ( + r"`nft list chain inet f2b-table f2b-chain | grep -q '@addr6-set-j-w-nft-mp[ \t]'`", + ), + 'ip4-ban': ( + r"`nft add element inet f2b-table addr-set-j-w-nft-mp { 192.0.2.1 }`", + ), + 'ip4-unban': ( + r"`nft delete element inet f2b-table addr-set-j-w-nft-mp { 192.0.2.1 }`", + ), + 'ip6-ban': ( + r"`nft add element inet f2b-table addr6-set-j-w-nft-mp { 2001:db8:: }`", + ), + 'ip6-unban': ( + r"`nft delete element inet f2b-table addr6-set-j-w-nft-mp { 2001:db8:: }`", + ), + }), + # nft-allports -- + ('j-w-nft-ap', 'nftables-allports[name=%(__name__)s, protocol="tcp,udp"]', { + 'ip4': ('ip ', 'ipv4_addr', 'addr-'), 'ip6': ('ip6 ', 'ipv6_addr', 'addr6-'), + '*-start': ( + r"`nft add table inet f2b-table`", + 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 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 rule inet f2b-table f2b-chain meta l4proto \{ tcp,udp \} ip6 saddr @addr6-set-j-w-nft-ap reject`", + ), + 'flush': ( + # todo + ), + 'stop': ( + "`HANDLE_ID=$(nft -a list chain inet f2b-table f2b-chain | grep -m1 '@addr-set-j-w-nft-ap ' | grep -oe ' handle [0-9]*')`", + "`nft delete rule inet f2b-table f2b-chain $HANDLE_ID`", + "`nft delete set inet f2b-table addr-set-j-w-nft-ap`", + "`HANDLE_ID=$(nft -a list chain inet f2b-table f2b-chain | grep -m1 '@addr6-set-j-w-nft-ap ' | grep -oe ' handle [0-9]*')`", + "`nft delete rule inet f2b-table f2b-chain $HANDLE_ID`", + "`nft delete set inet f2b-table addr6-set-j-w-nft-ap`", + ), + 'ip4-check': ( + r"""`nft list chain inet f2b-table f2b-chain | grep -q '@addr-set-j-w-nft-ap[ \t]'`""", + ), + 'ip6-check': ( + r"""`nft list chain inet f2b-table f2b-chain | grep -q '@addr6-set-j-w-nft-ap[ \t]'`""", + ), + 'ip4-ban': ( + r"`nft add element inet f2b-table addr-set-j-w-nft-ap { 192.0.2.1 }`", + ), + 'ip4-unban': ( + r"`nft delete element inet f2b-table addr-set-j-w-nft-ap { 192.0.2.1 }`", + ), + 'ip6-ban': ( + r"`nft add element inet f2b-table addr6-set-j-w-nft-ap { 2001:db8:: }`", + ), + 'ip6-unban': ( + r"`nft delete element inet f2b-table addr6-set-j-w-nft-ap { 2001:db8:: }`", + ), + }), # dummy -- ('j-dummy', 'dummy[name=%(__name__)s, init="==", target="/tmp/fail2ban.dummy"]', { 'ip4': ('family: inet4',), 'ip6': ('family: inet6',), @@ -1847,7 +1937,7 @@ class ServerConfigReaderTests(LogCaptureTestCase): # test ban ip4 : self.pruneLog('# === ban-ipv4 ===') action.ban(ainfo['ip4']) - if tests.get('ip4-start'): self.assertLogged(*tests['ip4-start'], all=True) + if tests.get('ip4-start'): self.assertLogged(*tests.get('*-start', ())+tests['ip4-start'], all=True) if tests.get('ip6-start'): self.assertNotLogged(*tests['ip6-start'], all=True) self.assertLogged(*tests.get('ip4-check',())+tests['ip4-ban'], all=True) self.assertNotLogged(*tests['ip6'], all=True) @@ -1859,7 +1949,7 @@ class ServerConfigReaderTests(LogCaptureTestCase): # test ban ip6 : self.pruneLog('# === ban ipv6 ===') action.ban(ainfo['ip6']) - if tests.get('ip6-start'): self.assertLogged(*tests['ip6-start'], all=True) + if tests.get('ip6-start'): self.assertLogged(*tests.get('*-start', ())+tests['ip6-start'], all=True) if tests.get('ip4-start'): self.assertNotLogged(*tests['ip4-start'], all=True) self.assertLogged(*tests.get('ip6-check',())+tests['ip6-ban'], all=True) self.assertNotLogged(*tests['ip4'], all=True) From c753ffb11d8ad81c03a89398892b0e1870e8707f Mon Sep 17 00:00:00 2001 From: sebres Date: Tue, 24 Sep 2019 15:01:58 +0200 Subject: [PATCH 4/9] combine nftables actions to single action: - nftables-common is removed - nftables-allports is obsolete, replaced by nftables[type=allports] - nftables-multiport is obsolete, replaced by nftables[type=multiport] --- config/action.d/nftables-allports.conf | 11 +++------- config/action.d/nftables-multiport.conf | 11 +++------- .../{nftables-common.conf => nftables.conf} | 22 ++++++++++++++----- 3 files changed, 22 insertions(+), 22 deletions(-) rename config/action.d/{nftables-common.conf => nftables.conf} (89%) diff --git a/config/action.d/nftables-allports.conf b/config/action.d/nftables-allports.conf index fad5273a..908abe40 100644 --- a/config/action.d/nftables-allports.conf +++ b/config/action.d/nftables-allports.conf @@ -6,17 +6,12 @@ # Modified: Alexander Belykh # adapted for nftables # +# Obsolete: superseded by nftables[type=allports] [INCLUDES] -before = nftables-common.conf +before = nftables.conf [Definition] -# Option: match -# Notes.: additional matches for nftables filter rule -# Values: nftables matches -# -match = meta l4proto \{ \} - -[Init] +type = allports diff --git a/config/action.d/nftables-multiport.conf b/config/action.d/nftables-multiport.conf index 6e3775ae..ba3ec92c 100644 --- a/config/action.d/nftables-multiport.conf +++ b/config/action.d/nftables-multiport.conf @@ -6,17 +6,12 @@ # Modified: Alexander Belykh # adapted for nftables # +# Obsolete: superseded by nftables[type=multiport] [INCLUDES] -before = nftables-common.conf +before = nftables.conf [Definition] -# Option: match -# Notes.: additional matches for nftables filter rule -# Values: nftables matches -# -match = dport \{ \} - -[Init] +type = multiport \ No newline at end of file diff --git a/config/action.d/nftables-common.conf b/config/action.d/nftables.conf similarity index 89% rename from config/action.d/nftables-common.conf rename to config/action.d/nftables.conf index 6765fbb3..79efbde2 100644 --- a/config/action.d/nftables-common.conf +++ b/config/action.d/nftables.conf @@ -26,12 +26,22 @@ after = nftables-common.local [Definition] -# Option: match -# Notes.: additional matches for nftables filter rule. -# leaving it empty will block all. (include udp and icmp) -# Values: nftables matches +# Option: type +# Notes.: type of the action. +# Values: [ multiport | allports ] Default: multiport # -match = dport \{ \} +type = multiport + +rule_match-allports = meta l4proto \{ \} +rule_match-multiport = dport \{ \} +match = > + +# Option: rule_stat +# Notes.: statement for nftables filter rule. +# leaving it empty will block all (include udp and icmp) +# Values: nftables statement +# +rule_stat = %(match)s saddr @ # Option: actionstart # Notes.: command executed on demand at the first ban (or at the start of Fail2Ban if actionstart_on_demand is set to false). @@ -40,7 +50,7 @@ match = dport \{ \} actionstart = add table f2b-table -- add chain f2b-table f2b-chain \{ type hook priority \; \} add set f2b-table \{ type \; \} - add rule f2b-table f2b-chain %(match)s saddr @ + add rule f2b-table f2b-chain %(rule_stat)s _nft_list = -a list chain f2b-table f2b-chain _nft_get_handle_id = grep -m1 '@ ' | grep -oe ' handle [0-9]*' From abc4d9fe3713035d31a1a0a169a708d36febb15e Mon Sep 17 00:00:00 2001 From: sebres Date: Tue, 24 Sep 2019 19:43:56 +0200 Subject: [PATCH 5/9] allow to use multiple protocols in multiport (single set with multiple rules in chain): `banaction = nftables[type=multiport]` with `protocol="tcp,udp,sctp"` in jail replace 3 separate actions. more robust if deleting multiple references to set (rules in chain) --- config/action.d/nftables.conf | 19 +++++++++++++++---- fail2ban/tests/servertestcase.py | 25 ++++++++++++++----------- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/config/action.d/nftables.conf b/config/action.d/nftables.conf index 79efbde2..49e2e917 100644 --- a/config/action.d/nftables.conf +++ b/config/action.d/nftables.conf @@ -32,8 +32,9 @@ after = nftables-common.local # type = multiport +rule_match-custom = rule_match-allports = meta l4proto \{ \} -rule_match-multiport = dport \{ \} +rule_match-multiport = $proto dport \{ \} match = > # Option: rule_stat @@ -43,6 +44,14 @@ match = > # rule_stat = %(match)s saddr @ +# optional interator over protocol's: +_nft_for_proto-custom-iter = +_nft_for_proto-custom-done = +_nft_for_proto-allports-iter = +_nft_for_proto-allports-done = +_nft_for_proto-multiport-iter = for proto in $(echo '' | sed 's/,/ /g'); do +_nft_for_proto-multiport-done = done + # Option: actionstart # Notes.: command executed on demand at the first ban (or at the start of Fail2Ban if actionstart_on_demand is set to false). # Values: CMD @@ -50,17 +59,19 @@ rule_stat = %(match)s saddr @ actionstart = add table f2b-table -- add chain f2b-table f2b-chain \{ type hook priority \; \} add set f2b-table \{ type \; \} + <_nft_for_proto--iter> add rule f2b-table f2b-chain %(rule_stat)s + <_nft_for_proto--done> _nft_list = -a list chain f2b-table f2b-chain -_nft_get_handle_id = grep -m1 '@ ' | grep -oe ' handle [0-9]*' +_nft_get_handle_id = grep -oP '@ .* \Khandle (\d+)$' # Option: actionstop # Notes.: command executed at the stop of jail (or at the end of Fail2Ban) # Values: CMD # -actionstop = HANDLE_ID=$(%(_nft_list)s | %(_nft_get_handle_id)s) - delete rule f2b-table f2b-chain $HANDLE_ID +actionstop = $(%(_nft_list)s | %(_nft_get_handle_id)s) | while read -r hdl ; do + delete rule f2b-table f2b-chain $hdl; done delete set f2b-table # Option: actioncheck diff --git a/fail2ban/tests/servertestcase.py b/fail2ban/tests/servertestcase.py index 6375ea24..aaa5c42c 100644 --- a/fail2ban/tests/servertestcase.py +++ b/fail2ban/tests/servertestcase.py @@ -1257,29 +1257,32 @@ class ServerConfigReaderTests(LogCaptureTestCase): # etc. testJailsActions = ( # nftables-multiport -- - ('j-w-nft-mp', 'nftables-multiport[name=%(__name__)s, port="http,https", protocol="tcp"]', { + ('j-w-nft-mp', 'nftables-multiport[name=%(__name__)s, port="http,https", protocol="tcp,udp,sctp"]', { 'ip4': ('ip ', 'ipv4_addr', 'addr-'), 'ip6': ('ip6 ', 'ipv6_addr', 'addr6-'), '*-start': ( r"`nft add table inet f2b-table`", r"`nft -- add chain inet f2b-table f2b-chain \{ type filter hook input priority -1 \; \}`", + # iterator over protocol is same for both families: + r"`for proto in $(echo 'tcp,udp,sctp' | sed 's/,/ /g'); do`", + r"`done`", ), 'ip4-start': ( r"`nft add set inet f2b-table addr-set-j-w-nft-mp \{ type ipv4_addr\; \}`", - r"`nft add rule inet f2b-table f2b-chain tcp dport \{ http,https \} ip saddr @addr-set-j-w-nft-mp reject`", + r"`nft add rule inet f2b-table f2b-chain $proto dport \{ http,https \} 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 rule inet f2b-table f2b-chain tcp dport \{ http,https \} ip6 saddr @addr6-set-j-w-nft-mp reject`", + r"`nft add rule inet f2b-table f2b-chain $proto dport \{ http,https \} ip6 saddr @addr6-set-j-w-nft-mp reject`", ), 'flush': ( # todo ), 'stop': ( - "`HANDLE_ID=$(nft -a list chain inet f2b-table f2b-chain | grep -m1 '@addr-set-j-w-nft-mp ' | grep -oe ' handle [0-9]*')`", - "`nft delete rule inet f2b-table f2b-chain $HANDLE_ID`", + "`$(nft -a list chain inet f2b-table f2b-chain | grep -oP '@addr-set-j-w-nft-mp .* \Khandle (\d+)$') | while read -r hdl`", + "`nft delete rule inet f2b-table f2b-chain $hdl; done`", "`nft delete set inet f2b-table addr-set-j-w-nft-mp`", - "`HANDLE_ID=$(nft -a list chain inet f2b-table f2b-chain | grep -m1 '@addr6-set-j-w-nft-mp ' | grep -oe ' handle [0-9]*')`", - "`nft delete rule inet f2b-table f2b-chain $HANDLE_ID`", + "`$(nft -a list chain inet f2b-table f2b-chain | grep -oP '@addr6-set-j-w-nft-mp .* \Khandle (\d+)$') | while read -r hdl`", + "`nft delete rule inet f2b-table f2b-chain $hdl; done`", "`nft delete set inet f2b-table addr6-set-j-w-nft-mp`", ), 'ip4-check': ( @@ -1320,11 +1323,11 @@ class ServerConfigReaderTests(LogCaptureTestCase): # todo ), 'stop': ( - "`HANDLE_ID=$(nft -a list chain inet f2b-table f2b-chain | grep -m1 '@addr-set-j-w-nft-ap ' | grep -oe ' handle [0-9]*')`", - "`nft delete rule inet f2b-table f2b-chain $HANDLE_ID`", + "`$(nft -a list chain inet f2b-table f2b-chain | grep -oP '@addr-set-j-w-nft-ap .* \Khandle (\d+)$') | while read -r hdl`", + "`nft delete rule inet f2b-table f2b-chain $hdl; done`", "`nft delete set inet f2b-table addr-set-j-w-nft-ap`", - "`HANDLE_ID=$(nft -a list chain inet f2b-table f2b-chain | grep -m1 '@addr6-set-j-w-nft-ap ' | grep -oe ' handle [0-9]*')`", - "`nft delete rule inet f2b-table f2b-chain $HANDLE_ID`", + "`$(nft -a list chain inet f2b-table f2b-chain | grep -oP '@addr6-set-j-w-nft-ap .* \Khandle (\d+)$') | while read -r hdl`", + "`nft delete rule inet f2b-table f2b-chain $hdl; done`", "`nft delete set inet f2b-table addr6-set-j-w-nft-ap`", ), 'ip4-check': ( From 492205d30e514d8a3f1e74c2096ae9cdfe1c63da Mon Sep 17 00:00:00 2001 From: sebres Date: Tue, 24 Sep 2019 20:00:29 +0200 Subject: [PATCH 6/9] action.d/nftables.conf: implemented `actionflush` (allows flushing nftables sets resp. fast unban of all jail tickets at all) --- config/action.d/nftables.conf | 31 ++++++++++++++++++++++--------- fail2ban/tests/servertestcase.py | 6 ++++-- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/config/action.d/nftables.conf b/config/action.d/nftables.conf index 49e2e917..9099f959 100644 --- a/config/action.d/nftables.conf +++ b/config/action.d/nftables.conf @@ -52,27 +52,40 @@ _nft_for_proto-allports-done = _nft_for_proto-multiport-iter = for proto in $(echo '' | sed 's/,/ /g'); do _nft_for_proto-multiport-done = done +_nft_list = -a list chain f2b-table f2b-chain +_nft_get_handle_id = grep -oP '@ .* \Khandle (\d+)$' + +_nft_add_set = add set f2b-table \{ type \; \} + <_nft_for_proto--iter> + add rule f2b-table f2b-chain %(rule_stat)s + <_nft_for_proto--done> +_nft_del_set = $(%(_nft_list)s | %(_nft_get_handle_id)s) | while read -r hdl ; do + delete rule f2b-table f2b-chain $hdl; done + delete set f2b-table + # Option: actionstart # Notes.: command executed on demand at the first ban (or at the start of Fail2Ban if actionstart_on_demand is set to false). # Values: CMD # actionstart = add table f2b-table -- add chain f2b-table f2b-chain \{ type hook priority \; \} - add set f2b-table \{ type \; \} - <_nft_for_proto--iter> - add rule f2b-table f2b-chain %(rule_stat)s - <_nft_for_proto--done> + %(_nft_add_set)s -_nft_list = -a list chain f2b-table f2b-chain -_nft_get_handle_id = grep -oP '@ .* \Khandle (\d+)$' +# Option: actionflush +# Notes.: command executed once to flush IPS, by shutdown (resp. by stop of the jail or this action); +# uses `nft flush set ...` and as fallback (e. g. unsupported) recreates the set (with references) +# Values: CMD +# +actionflush = flush set f2b-table || ( + %(_nft_del_set)s + %(_nft_add_set)s + ) # Option: actionstop # Notes.: command executed at the stop of jail (or at the end of Fail2Ban) # Values: CMD # -actionstop = $(%(_nft_list)s | %(_nft_get_handle_id)s) | while read -r hdl ; do - delete rule f2b-table f2b-chain $hdl; done - delete set f2b-table +actionstop = %(_nft_del_set)s # Option: actioncheck # Notes.: command executed once before each actionban command diff --git a/fail2ban/tests/servertestcase.py b/fail2ban/tests/servertestcase.py index aaa5c42c..55ec75ad 100644 --- a/fail2ban/tests/servertestcase.py +++ b/fail2ban/tests/servertestcase.py @@ -1275,7 +1275,8 @@ class ServerConfigReaderTests(LogCaptureTestCase): r"`nft add rule inet f2b-table f2b-chain $proto dport \{ http,https \} ip6 saddr @addr6-set-j-w-nft-mp reject`", ), 'flush': ( - # todo + "`nft flush set inet f2b-table addr-set-j-w-nft-mp || ", + "`nft flush set inet f2b-table addr6-set-j-w-nft-mp || ", ), 'stop': ( "`$(nft -a list chain inet f2b-table f2b-chain | grep -oP '@addr-set-j-w-nft-mp .* \Khandle (\d+)$') | while read -r hdl`", @@ -1320,7 +1321,8 @@ class ServerConfigReaderTests(LogCaptureTestCase): r"`nft add rule inet f2b-table f2b-chain meta l4proto \{ tcp,udp \} ip6 saddr @addr6-set-j-w-nft-ap reject`", ), 'flush': ( - # todo + "`nft flush set inet f2b-table addr-set-j-w-nft-ap || ", + "`nft flush set inet f2b-table addr6-set-j-w-nft-ap || ", ), 'stop': ( "`$(nft -a list chain inet f2b-table f2b-chain | grep -oP '@addr-set-j-w-nft-ap .* \Khandle (\d+)$') | while read -r hdl`", From 8ea00c1d5db34366ef55ca9e6441b55e48d157c5 Mon Sep 17 00:00:00 2001 From: sebres Date: Wed, 25 Sep 2019 13:47:29 +0200 Subject: [PATCH 7/9] fixed mistake in config (semicolon after space as comment in configs?) and coverage, suppress errors by unsupported flush, better space handling in helper _nft_get_handle_id, etc --- config/action.d/nftables.conf | 6 +++--- fail2ban/tests/servertestcase.py | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/config/action.d/nftables.conf b/config/action.d/nftables.conf index 9099f959..e7186c47 100644 --- a/config/action.d/nftables.conf +++ b/config/action.d/nftables.conf @@ -53,13 +53,13 @@ _nft_for_proto-multiport-iter = for proto in $(echo '' | sed 's/,/ /g' _nft_for_proto-multiport-done = done _nft_list = -a list chain f2b-table f2b-chain -_nft_get_handle_id = grep -oP '@ .* \Khandle (\d+)$' +_nft_get_handle_id = grep -oP '@\s+.*\s+\Khandle\s+(\d+)$' _nft_add_set = add set f2b-table \{ type \; \} <_nft_for_proto--iter> add rule f2b-table f2b-chain %(rule_stat)s <_nft_for_proto--done> -_nft_del_set = $(%(_nft_list)s | %(_nft_get_handle_id)s) | while read -r hdl ; do +_nft_del_set = (%(_nft_list)s | %(_nft_get_handle_id)s) | while read -r hdl; do delete rule f2b-table f2b-chain $hdl; done delete set f2b-table @@ -76,7 +76,7 @@ actionstart = add table f2b-table # uses `nft flush set ...` and as fallback (e. g. unsupported) recreates the set (with references) # Values: CMD # -actionflush = flush set f2b-table || ( +actionflush = ( flush set f2b-table 2> /dev/null) || ( %(_nft_del_set)s %(_nft_add_set)s ) diff --git a/fail2ban/tests/servertestcase.py b/fail2ban/tests/servertestcase.py index 55ec75ad..901b7399 100644 --- a/fail2ban/tests/servertestcase.py +++ b/fail2ban/tests/servertestcase.py @@ -1275,14 +1275,14 @@ class ServerConfigReaderTests(LogCaptureTestCase): r"`nft add rule inet f2b-table f2b-chain $proto dport \{ http,https \} ip6 saddr @addr6-set-j-w-nft-mp reject`", ), 'flush': ( - "`nft flush set inet f2b-table addr-set-j-w-nft-mp || ", - "`nft flush set inet f2b-table addr6-set-j-w-nft-mp || ", + "`(nft flush set inet f2b-table addr-set-j-w-nft-mp 2> /dev/null) || ", + "`(nft flush set inet f2b-table addr6-set-j-w-nft-mp 2> /dev/null) || ", ), 'stop': ( - "`$(nft -a list chain inet f2b-table f2b-chain | grep -oP '@addr-set-j-w-nft-mp .* \Khandle (\d+)$') | while read -r hdl`", + "`(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`", "`nft delete rule inet f2b-table f2b-chain $hdl; done`", "`nft delete set inet f2b-table addr-set-j-w-nft-mp`", - "`$(nft -a list chain inet f2b-table f2b-chain | grep -oP '@addr6-set-j-w-nft-mp .* \Khandle (\d+)$') | while read -r hdl`", + "`(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`", "`nft delete rule inet f2b-table f2b-chain $hdl; done`", "`nft delete set inet f2b-table addr6-set-j-w-nft-mp`", ), @@ -1321,14 +1321,14 @@ class ServerConfigReaderTests(LogCaptureTestCase): r"`nft add rule inet f2b-table f2b-chain meta l4proto \{ tcp,udp \} ip6 saddr @addr6-set-j-w-nft-ap reject`", ), 'flush': ( - "`nft flush set inet f2b-table addr-set-j-w-nft-ap || ", - "`nft flush set inet f2b-table addr6-set-j-w-nft-ap || ", + "`(nft flush set inet f2b-table addr-set-j-w-nft-ap 2> /dev/null) || ", + "`(nft flush set inet f2b-table addr6-set-j-w-nft-ap 2> /dev/null) || ", ), 'stop': ( - "`$(nft -a list chain inet f2b-table f2b-chain | grep -oP '@addr-set-j-w-nft-ap .* \Khandle (\d+)$') | while read -r hdl`", + "`(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`", "`nft delete rule inet f2b-table f2b-chain $hdl; done`", "`nft delete set inet f2b-table addr-set-j-w-nft-ap`", - "`$(nft -a list chain inet f2b-table f2b-chain | grep -oP '@addr6-set-j-w-nft-ap .* \Khandle (\d+)$') | while read -r hdl`", + "`(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`", "`nft delete rule inet f2b-table f2b-chain $hdl; done`", "`nft delete set inet f2b-table addr6-set-j-w-nft-ap`", ), From d1a73d30046e0bfbdf932dfaa0c4e7168873008e Mon Sep 17 00:00:00 2001 From: sebres Date: Fri, 18 Oct 2019 11:18:34 +0200 Subject: [PATCH 8/9] filter.d/apache-auth.conf: - ignore errors from mod_evasive in `normal` mode (mode-controlled now) (gh-2548); - extended with option `mode` - `normal` (default) and `aggressive` close gh-2548 --- ChangeLog | 3 +++ config/filter.d/apache-auth.conf | 10 ++++++++++ config/filter.d/apache-common.conf | 4 +++- fail2ban/tests/files/logs/apache-auth | 8 ++++++++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index d9f0f171..11f43739 100644 --- a/ChangeLog +++ b/ChangeLog @@ -51,6 +51,9 @@ ver. 0.10.5-dev-1 (20??/??/??) - development edition * `action.d/badips.py`: fixed start of banaction on demand (which may be IP-family related), gh-2390 * `action.d/helpers-common.conf`: rewritten grep arguments, now options `-wF` used to match only whole words and fixed string (not as pattern), gh-2298 +* `filter.d/apache-auth.conf`: + - ignore errors from mod_evasive in `normal` mode (mode-controlled now) (gh-2548); + - extended with option `mode` - `normal` (default) and `aggressive` * `filter.d/sshd.conf`: - matches `Bad protocol version identification` in `ddos` and `aggressive` modes (gh-2404). - captures `Disconnecting ...: Change of username or service not allowed` (gh-2239, gh-2279) diff --git a/config/filter.d/apache-auth.conf b/config/filter.d/apache-auth.conf index 91c89b26..40f6d6e3 100644 --- a/config/filter.d/apache-auth.conf +++ b/config/filter.d/apache-auth.conf @@ -9,6 +9,16 @@ before = apache-common.conf [Definition] +# Mode for filter: normal (default) and aggressive (allows DDoS & brute force detection of mod_evasive) +mode = normal + +# ignore messages of mod_evasive module: +apache-pref-ign-normal = (?!evasive) +# allow "denied by server configuration" from all modules: +apache-pref-ign-aggressive = +# mode related ignore prefix for common _apache_error_client substitution: +apache-pref-ignore = > + prefregex = ^%(_apache_error_client)s (?:AH\d+: )?.+$ # auth_type = ((?:Digest|Basic): )? diff --git a/config/filter.d/apache-common.conf b/config/filter.d/apache-common.conf index 3eec83d0..6577fe7d 100644 --- a/config/filter.d/apache-common.conf +++ b/config/filter.d/apache-common.conf @@ -27,7 +27,9 @@ _daemon = (?:apache\d*|httpd(?:/\w+)?) apache-prefix = > -_apache_error_client = \[(:?error|\S+:\S+)\]( \[pid \d+(:\S+ \d+)?\])? \[client (:\d{1,5})?\] +apache-pref-ignore = + +_apache_error_client = \[(:?error|\S+:\S+)\]( \[pid \d+(:\S+ \d+)?\])? \[client (:\d{1,5})?\] datepattern = {^LN-BEG} diff --git a/fail2ban/tests/files/logs/apache-auth b/fail2ban/tests/files/logs/apache-auth index 93040b1d..fd3bae82 100644 --- a/fail2ban/tests/files/logs/apache-auth +++ b/fail2ban/tests/files/logs/apache-auth @@ -134,6 +134,14 @@ # failJSON: { "time": "2018-03-28T01:31:42", "match": true , "host": "91.49.82.139" } [Wed Mar 28 01:31:42.355210 2018] [ssl:error] [pid 6586] [client 91.49.82.139:58028] AH02033: No hostname was provided via SNI for a name based virtual host +# failJSON: { "match": false, "desc": "ignore mod_evasive errors in normal mode (gh-2548)" } +[Thu Oct 17 18:43:40.160521 2019] [evasive20:error] [pid 22589] [client 192.0.2.1:56175] client denied by server configuration: /path/index.php, referer: https://hostname/path/ + +# filterOptions: {"mode": "aggressive"} + +# failJSON: { "time": "2019-10-17T18:43:40", "match": true, "host": "192.0.2.1", "desc": "accept mod_evasive errors in aggressive mode (gh-2548)" } +[Thu Oct 17 18:43:40.160521 2019] [evasive20:error] [pid 22589] [client 192.0.2.1:56175] client denied by server configuration: /path/index.php, referer: https://hostname/path/ + # filterOptions: {"logging": "syslog"} # failJSON: { "time": "2005-02-15T16:23:00", "match": true , "host": "192.0.2.1", "desc": "using syslog (ErrorLog syslog)" } From 8b850864cf1baccbfacfebef1c5b1618c34dff93 Mon Sep 17 00:00:00 2001 From: "Sergey G. Brester" Date: Fri, 18 Oct 2019 12:00:17 +0200 Subject: [PATCH 9/9] amend to #2254: update changelog --- ChangeLog | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ChangeLog b/ChangeLog index 11f43739..67182248 100644 --- a/ChangeLog +++ b/ChangeLog @@ -113,6 +113,16 @@ filter = flt[logtype=short] attempts (failure) for IP (resp. failure-ID), see gh-2351; Syntax: - `fail2ban-client set attempt [ ... ]` +* `action.d/nftables.conf`: + - isolate fail2ban rules into a dedicated table and chain (gh-2254) + - `nftables-allports` supports multiple protocols in single rule now + - combined nftables actions to single action `nftables`: + * `nftables-common` is removed (replaced with single action `nftables` now) + * `nftables-allports` is obsolete, superseded by `nftables[type=allports]` + * `nftables-multiport` is obsolete, superseded by `nftables[type=multiport]` + - allowed multiple protocols in `nftables[type=multiport]` action (single set with multiple rules + in chain), following configuration in jail would replace 3 separate actions, see + https://github.com/fail2ban/fail2ban/pull/2254#issuecomment-534684675 * `action.d/badips.py`: option `loglevel` extended with level of summary message, following example configuration logging summary with NOTICE and rest with DEBUG log-levels: `action = badips.py[loglevel="debug, notice"]`