diff --git a/data/Dockerfiles/netfilter/main.py b/data/Dockerfiles/netfilter/main.py index 5b718a94b..65433f382 100644 --- a/data/Dockerfiles/netfilter/main.py +++ b/data/Dockerfiles/netfilter/main.py @@ -167,12 +167,18 @@ def ban(address): logger.logCrit('Banning %s for %d minutes' % (net, NET_BAN_TIME / 60 )) if type(ip) is ipaddress.IPv4Address and int(f2boptions['manage_external']) != 1: with lock: - logdebug("Calling tables.banIPv4(%s)" % net) - tables.banIPv4(net) + if not tables.isIPv4Banned(net): + logdebug("Calling tables.banIPv4(%s)" % net) + tables.banIPv4(net) + else: + logdebug("IPv4 %s is already banned." % net) elif int(f2boptions['manage_external']) != 1: with lock: - logdebug("Calling tables.banIPv6(%s)" % net) - tables.banIPv6(net) + if not tables.isIPv6Banned(net): + logdebug("Calling tables.banIPv6(%s)" % net) + tables.banIPv6(net) + else: + logdebug("IPv6 %s is already banned." % net) logdebug("Updating F2B_ACTIVE_BANS[%s]=%d" % (net, cur_time + NET_BAN_TIME)) diff --git a/data/Dockerfiles/netfilter/modules/IPTables.py b/data/Dockerfiles/netfilter/modules/IPTables.py index 3d3d43974..9fb4ce0bc 100644 --- a/data/Dockerfiles/netfilter/modules/IPTables.py +++ b/data/Dockerfiles/netfilter/modules/IPTables.py @@ -7,6 +7,15 @@ class IPTables: self.chain_name = chain_name self.logger = logger + def isIPBanned(self, net, family): + return False + + def isIPv4Banned(self,net): + return False + + def isIPv6Banned(self,net): + return False + def initChainIPv4(self): if not iptc.Chain(iptc.Table(iptc.Table.FILTER), self.chain_name) in iptc.Table(iptc.Table.FILTER).chains: iptc.Table(iptc.Table.FILTER).create_chain(self.chain_name) diff --git a/data/Dockerfiles/netfilter/modules/NFTables.py b/data/Dockerfiles/netfilter/modules/NFTables.py index 7740fa59f..705676082 100644 --- a/data/Dockerfiles/netfilter/modules/NFTables.py +++ b/data/Dockerfiles/netfilter/modules/NFTables.py @@ -15,6 +15,46 @@ class NFTables: self.search_current_chains() + def get_list_dict(self, family): + base_dict = self.get_base_dict() + chain_dict = {'chain': {'family': family, 'table': 'filter', 'name': self.chain_name,}} + list_dict = {'list': chain_dict} + base_dict['nftables'].append(list_dict) + return base_dict + + def isIPBanned(self, net, family): + list_dict = self.get_list_dict(family) + rules = self.nft_exec_dict(list_dict) + if '/' in net: + prefix, length = net.split('/') + else: + prefix = net + length = '32' if family == 'ip' else '128' + if not 'nftables' in rules: + return False + for rule in rules['nftables']: + if not 'rule' in rule: + continue #Skip anything that is not a rule + for expression in rule['rule']['expr']: + if not 'match' in expression: + continue #Skip anything that is not a match statement + right = expression['match']['right'] + try: + if isinstance(right, str): + if right == prefix: + return True + elif right['prefix']['addr'] == prefix and str(right['prefix']['len']) == length: + return True + except (KeyError,TypeError): + pass + return False + + def isIPv4Banned(self,net): + return self.isIPBanned(net, 'ip') + + def isIPv6Banned(self,net): + return self.isIPBanned(net, 'ip6') + def initChainIPv4(self): self.insert_mailcow_chains("ip") diff --git a/docker-compose.yml b/docker-compose.yml index f09afca2a..7074a5e4f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -502,7 +502,7 @@ services: - acme netfilter-mailcow: - image: ghcr.io/mailcow/netfilter:1.63 + image: ghcr.io/mailcow/netfilter:1.63a stop_grace_period: 30s restart: always privileged: true