add ability to ignore sender hosts and/or networks

This commit is contained in:
2019-09-02 15:56:12 +02:00
parent 5ffae608ff
commit a7d472af68
3 changed files with 41 additions and 2 deletions

View File

@@ -49,6 +49,8 @@ The following configuration options are mandatory in each quarantine section:
SMTP port SMTP port
The following configuration options are optional in each quarantine section: The following configuration options are optional in each quarantine section:
* **ignore_hosts**
Comma-separated list of host and network addresses to be ignored by this quarantine.
* **reject_reason** * **reject_reason**
Reason to return to the client if action is set to reject. Reason to return to the client if action is set to reject.

View File

@@ -30,6 +30,12 @@ preferred_quarantine_action = last
[spam] [spam]
# Option: ignore_hosts
# Notes: Set a list of host and network addresses to be ignored by this quarantine.
# All the common host/network notations are supported, including IPv6.
# Value: [ HOST ]
#
ignore_hosts = 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
# Option: regex # Option: regex
# Notes: Set the regular expression to match against email headers. # Notes: Set the regular expression to match against email headers.

View File

@@ -25,7 +25,7 @@ from Milter.utils import parse_addr
from collections import defaultdict from collections import defaultdict
from io import BytesIO from io import BytesIO
from itertools import groupby from itertools import groupby
from netaddr import IPAddress, IPNetwork
from pyquarantine import quarantines from pyquarantine import quarantines
from pyquarantine import notifications from pyquarantine import notifications
from pyquarantine import whitelists from pyquarantine import whitelists
@@ -71,6 +71,20 @@ class QuarantineMilter(Milter.Base):
def set_configfiles(config_files): def set_configfiles(config_files):
QuarantineMilter._config_files = config_files QuarantineMilter._config_files = config_files
def connect(self, IPname, family, hostaddr):
self.logger.debug("accepted milter connection from {} port {}".format(*hostaddr))
ip = IPAddress(hostaddr[0])
for quarantine in self.config.copy():
for ignore in quarantine["ignore_hosts_list"]:
if ip in ignore:
self.logger.debug("host {} is ignored by quarantine {}".format(hostaddr[0], quarantine["name"]))
self.config.remove(quarantine)
break
if not self.config:
self.logger.debug("host {} is ignored by all quarantines, skip further processing", hostaddr[0])
return Milter.ACCEPT
return Milter.CONTINUE
@Milter.noreply @Milter.noreply
def envfrom(self, mailfrom, *str): def envfrom(self, mailfrom, *str):
self.mailfrom = "@".join(parse_addr(mailfrom)).lower() self.mailfrom = "@".join(parse_addr(mailfrom)).lower()
@@ -325,7 +339,8 @@ def generate_milter_config(configtest=False, config_files=[]):
# check if optional config options are present in config # check if optional config options are present in config
defaults = { defaults = {
"reject_reason": "Message rejected" "reject_reason": "Message rejected",
"ignore_hosts": ""
} }
for option in defaults.keys(): for option in defaults.keys():
if option not in config.keys() and \ if option not in config.keys() and \
@@ -391,6 +406,22 @@ def generate_milter_config(configtest=False, config_files=[]):
else: else:
raise RuntimeError("{}: unknown action '{}'".format(quarantine_name, action)) raise RuntimeError("{}: unknown action '{}'".format(quarantine_name, action))
# create host/network whitelist
config["ignore_hosts_list"] = []
ignored = set([ p.strip() for p in config["ignore_hosts"].split(",") if p])
for ignore in ignored:
if not ignore:
continue
# parse network notation
try:
net = IPNetwork(ignore)
except AddrFormatError as e:
raise RuntimeError("error parsing ignore_hosts: {}".format(e))
else:
config["ignore_hosts_list"].append(net)
if config["ignore_hosts_list"]:
logger.debug("{}: ignore hosts: {}".format(quarantine_name, ", ".join(ignored)))
milter_config.append(config) milter_config.append(config)
return global_config, milter_config return global_config, milter_config