Merge pull request #3111 from modoboa/optional-IPV6-check

Added option to disable IPV6 mx checks
This commit is contained in:
Antoine Nguyen
2023-12-14 14:56:53 +01:00
committed by GitHub
7 changed files with 75 additions and 5 deletions
+3
View File
@@ -181,6 +181,7 @@ class AdminGlobalParametersSerializer(serializers.Serializer):
enable_mx_checks = serializers.BooleanField(default=True)
valid_mxs = serializers.CharField(allow_blank=True)
domains_must_have_authorized_mx = serializers.BooleanField(default=False)
enable_ipv6_mx_checks = serializers.BooleanField(default=True)
enable_spf_checks = serializers.BooleanField(default=True)
enable_dkim_checks = serializers.BooleanField(default=True)
enable_dmarc_checks = serializers.BooleanField(default=True)
@@ -274,6 +275,8 @@ class AdminGlobalParametersSerializer(serializers.Serializer):
raise serializers.ValidationError({
"valid_mxs": _("Define at least one authorized network / address")
})
if not data.get("enable_mx_checks"):
data["enable_ipv6_mx_checks"] = False
return data
+19 -1
View File
@@ -50,6 +50,14 @@ class AdminParametersForm(param_forms.AdminParametersForm):
)
)
enable_ipv6_mx_checks = YesNoField(
label=gettext_lazy("Enable IPV6 checks"),
initial=True,
help_text=gettext_lazy(
"Check if MX setup for ipv6 is valid"
)
)
enable_spf_checks = YesNoField(
label=gettext_lazy("Enable SPF checks"),
initial=True,
@@ -194,7 +202,8 @@ class AdminParametersForm(param_forms.AdminParametersForm):
# Visibility rules
visibility_rules = {
"valid_mxs": "enable_mx_checks=True",
"domains_must_have_authorized_mx": "enable_mx_checks=True"
"domains_must_have_authorized_mx": "enable_mx_checks=True",
"enable_ipv6_mx_checks": "enable_mx_checks=True"
}
def __init__(self, *args, **kwargs):
@@ -292,6 +301,8 @@ class AdminParametersForm(param_forms.AdminParametersForm):
"valid_mxs",
_("Define at least one authorized network / address")
)
if not cleaned_data.get("enable_mx_checks"):
cleaned_data["enable_ipv6_mx_checks"] = False
return cleaned_data
@@ -322,6 +333,13 @@ GLOBAL_PARAMETERS_STRUCT = collections.OrderedDict([
"does not use one of the defined addresses."
)
}),
("enable_ipv6_mx_checks", {
"label": gettext_lazy("Enable IPV6 checks"),
"display": "enable_mx_checks=true",
"help_text": gettext_lazy(
"Check if MX setup for ipv6 is valid"
)
}),
("enable_spf_checks", {
"label": gettext_lazy("Enable SPF checks"),
"help_text": gettext_lazy(
+4 -1
View File
@@ -226,7 +226,10 @@ def get_domain_mx_list(domain):
for dns_answer in dns_answers:
mx_domain = dns_answer.exchange.to_unicode(
omit_final_dot=True, idna_codec=IDNA_2008_UTS_46)
for rtype in ["A", "AAAA"]:
rtypes = ["A"]
if param_tools.get_global_parameter("enable_ipv6_mx_checks", app="admin"):
rtypes.append("AAAA")
for rtype in rtypes:
ip_answers = get_dns_records(mx_domain, rtype, resolver)
if not ip_answers:
continue
+25 -1
View File
@@ -10,7 +10,8 @@ from django.test import override_settings
from django.urls import reverse
from django.utils.translation import gettext as _
from modoboa.core import factories as core_factories
from modoboa.core import (
models as core_models, factories as core_factories)
from modoboa.lib.tests import ModoTestCase
from . import utils
from .. import factories, models
@@ -171,6 +172,29 @@ class MXTestCase(ModoTestCase):
% {"domain": "bad-response.example.com", "addr": "000.0.0.0"}),
)
@mock.patch("ipaddress.ip_address")
@mock.patch.object(dns.resolver.Resolver, "resolve")
def test_ipv6_logging(self, mock_query, mock_ip_address):
"""Test to check that AAAA missing record is logged consistently."""
mock_query.side_effect = utils.mock_dns_query_result
mock_ip_address.side_effect = utils.mock_ip_address_result
localconfig = core_models.LocalConfig.objects.first()
localconfig.parameters.set_value("enable_ipv6_mx_checks", True, "admin")
localconfig.save()
with LogCapture("modoboa.admin") as log:
get_domain_mx_list("test3.com")
log.check(
("modoboa.admin", "ERROR",
_("No AAAA record for %s")
% "mx3.example.com")
)
localconfig = core_models.LocalConfig.objects.first()
localconfig.parameters.set_value("enable_ipv6_mx_checks", False, "admin")
localconfig.save()
with LogCapture("modoboa.admin") as log2:
get_domain_mx_list("test3.com")
log2.check()
@override_settings(DNSBL_PROVIDERS=["zen.spamhaus.org"])
class DNSBLTestCase(ModoTestCase):
+9 -1
View File
@@ -43,7 +43,7 @@ _BAD_IP_RECORD = (socket.AF_INET, socket.SOCK_STREAM, 6, "", ("BAD", 25))
_IP_RECORDS = [_IPV4_RECORD_1, _IPV6_RECORD]
_POSSIBLE_DNS_RESULTS = {
"test3.com": [_MX_RECORD_2],
"test3.com": [_MX_RECORD_3],
"no-mx.example.com": NoAnswer(),
"does-not-exist.example.com": NXDOMAIN(),
"timeout.example.com": Timeout(),
@@ -79,12 +79,20 @@ _POSSIBLE_IP_RESULTS = {
"000.0.0.0": ValueError(),
}
_POSSIBLE_DNS_IPV6_RESULTS = {
settings.REDIS_HOST: socket.getaddrinfo(settings.REDIS_HOST, settings.REDIS_PORT),
"mx3.example.com": NoAnswer()
}
def mock_dns_query_result(qname, *args, **kwargs):
if "MX" in args:
if qname in _POSSIBLE_DNS_RESULTS:
return get_mock_dns_query_response(_POSSIBLE_DNS_RESULTS, qname)
return _MX_RECORDS
elif "AAAA" in args:
if qname in _POSSIBLE_DNS_IPV6_RESULTS:
return get_mock_dns_query_response(_POSSIBLE_DNS_IPV6_RESULTS, qname)
if qname in _POSSIBLE_DNS_RESULTS_NO_MX:
return get_mock_dns_query_response(_POSSIBLE_DNS_RESULTS_NO_MX, qname)
return _IP_SET_RECORDS
+10
View File
@@ -51,6 +51,7 @@ SETTINGS_SAMPLE = {
"core-ldap_admin_groups": "",
"core-ldap_groups_search_base": "",
"admin-enable_mx_checks": "True",
"admin-enable_ipv6_mx_checks": "True",
"core-ldap_search_base": "",
"admin-valid_mxs": "",
"limits-deflt_user_domains_limit": "0",
@@ -264,6 +265,15 @@ sha512crypt is not one of the available choices."]},
self.assertIn(
"sms_ovh_application_secret", errors["form_errors"])
def test_mx_check_settings_clean(self):
settings = SETTINGS_SAMPLE.copy()
settings["admin-enable_mx_checks"] = False
url = reverse("core:parameters")
response = self.client.post(url, settings, format="json")
self.assertEqual(response.status_code, 200)
self.assertFalse(
param_tools.get_global_parameter("enable_ipv6_mx_checks", "admin")
)
class UserSettings(param_forms.UserParametersForm):
"""Stupid user settings form."""
+5 -1
View File
@@ -5,6 +5,7 @@ import ipaddress
from django.utils.translation import gettext as _
from modoboa.admin import lib as admin_lib
from modoboa.parameters import tools as param_tools
from . import constants
@@ -43,7 +44,10 @@ def get_dmarc_record(domain):
def _get_simple_record(name):
"""We just want to know if name is declared."""
for rdtype in ["A", "CNAME", "AAAA"]:
rtypes = ["A", "CNAME"]
if param_tools.get_global_parameter("enable_ipv6_mx_checks", app="admin"):
rtypes.append("AAAA")
for rdtype in rtypes:
records = admin_lib.get_dns_records(name, rdtype)
if records is not None:
break