mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-17 05:41:25 +02:00
@@ -8,10 +8,14 @@ from gevent import socket
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from modoboa.admin import constants
|
||||
from modoboa.admin import models
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.lib import email_utils
|
||||
|
||||
|
||||
class CheckDNSBLCommand(BaseCommand):
|
||||
@@ -19,6 +23,18 @@ class CheckDNSBLCommand(BaseCommand):
|
||||
|
||||
help = "Check defined domains against DNSBL providers."
|
||||
|
||||
@cached_property
|
||||
def providers(self):
|
||||
"""Return a list of DNSBL providers."""
|
||||
if not hasattr(settings, "DNSBL_PROVIDERS"):
|
||||
return constants.DNSBL_PROVIDERS
|
||||
return settings.DNSBL_PROVIDERS
|
||||
|
||||
@cached_property
|
||||
def sender(self):
|
||||
"""Return sender address for notifications."""
|
||||
return parameters.get_admin("SENDER_ADDRESS", app="core")
|
||||
|
||||
def add_arguments(self, parser):
|
||||
"""Add extra arguments to command."""
|
||||
parser.add_argument(
|
||||
@@ -37,6 +53,45 @@ class CheckDNSBLCommand(BaseCommand):
|
||||
results[ip] = False
|
||||
return provider, results
|
||||
|
||||
def store_domain_result(self, domain, provider, results):
|
||||
"""Store provider results for domain."""
|
||||
alerts = {}
|
||||
to_create = []
|
||||
for ip in results.keys():
|
||||
result = "" if not results[ip] else results[ip]
|
||||
dnsbl_result = models.DNSBLResult.objects.filter(
|
||||
domain=domain, provider=provider, mx=ip).first()
|
||||
if dnsbl_result is None:
|
||||
to_create.append(
|
||||
models.DNSBLResult(
|
||||
domain=domain, provider=provider, mx=ip,
|
||||
status=result)
|
||||
)
|
||||
else:
|
||||
if not dnsbl_result.status and result:
|
||||
if domain not in alerts:
|
||||
alerts[domain] = []
|
||||
alerts[domain].append((provider, ip))
|
||||
dnsbl_result.status = result
|
||||
dnsbl_result.save()
|
||||
models.DNSBLResult.objects.bulk_create(to_create)
|
||||
if not alerts:
|
||||
return
|
||||
content = render_to_string(
|
||||
"admin/notifications/domain_in_dnsbl.html", {
|
||||
"domain": domain, "alerts": alerts
|
||||
})
|
||||
subject = _("[modoboa] DNSBL issue(s) for domain {}").format(
|
||||
domain.name)
|
||||
for admin in domain.admins:
|
||||
if not admin.email:
|
||||
continue
|
||||
status, msg = email_utils.sendmail_simple(
|
||||
self.sender, admin.email,
|
||||
domainsubject=subject, content=content)
|
||||
if not status:
|
||||
print msg
|
||||
|
||||
def check_domain(self, domain, timeout):
|
||||
"""Check specified domain."""
|
||||
resolver = dns.resolver.Resolver()
|
||||
@@ -59,30 +114,24 @@ class CheckDNSBLCommand(BaseCommand):
|
||||
finally:
|
||||
if address is not None:
|
||||
ip_list.append(address)
|
||||
print domain.name, ip_list
|
||||
if len(ip_list) == 0:
|
||||
return
|
||||
if not hasattr(settings, "DNSBL_PROVIDERS"):
|
||||
providers = constants.DNSBL_PROVIDERS
|
||||
else:
|
||||
providers = settings.DNSBL_PROVIDERS
|
||||
jobs = [
|
||||
gevent.spawn(self.query, ip_list, provider)
|
||||
for provider in providers]
|
||||
for provider in self.providers]
|
||||
gevent.joinall(jobs, timeout)
|
||||
for job in jobs:
|
||||
if not job.successful():
|
||||
continue
|
||||
provider, results = job.value
|
||||
for ip in results.keys():
|
||||
result = "" if not results[ip] else results[ip]
|
||||
models.DNSBLResult.objects.update_or_create(
|
||||
domain=domain, provider=provider, mx=ip,
|
||||
status=result)
|
||||
self.store_domain_result(domain, provider, results)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
"""Command entry point."""
|
||||
if parameters.get_admin("ENABLE_DNSBL_CHECKS") == "no":
|
||||
return
|
||||
# Remove deprecated records first
|
||||
models.DNSBLResult.objects.exclude(
|
||||
provider__in=self.providers).delete()
|
||||
for domain in models.Domain.objects.filter(enabled=True):
|
||||
self.check_domain(domain, options.get("timeout"))
|
||||
|
||||
@@ -17,9 +17,9 @@ class Migration(migrations.Migration):
|
||||
name='DNSBLResult',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('provider', models.CharField(max_length=254)),
|
||||
('provider', models.CharField(max_length=254, db_index=True)),
|
||||
('mx', models.GenericIPAddressField()),
|
||||
('status', models.CharField(blank=True, max_length=45)),
|
||||
('status', models.CharField(blank=True, max_length=45, db_index=True)),
|
||||
('domain', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='admin.Domain')),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -232,9 +232,9 @@ class DNSBLResult(models.Model):
|
||||
"""Store a DNSBL query result."""
|
||||
|
||||
domain = models.ForeignKey(Domain)
|
||||
provider = models.CharField(max_length=254)
|
||||
provider = models.CharField(max_length=254, db_index=True)
|
||||
mx = models.GenericIPAddressField()
|
||||
status = models.CharField(max_length=45, blank=True)
|
||||
status = models.CharField(max_length=45, blank=True, db_index=True)
|
||||
|
||||
objects = models.Manager.from_queryset(DNSBLQuerySet)()
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
{% load i18n %}
|
||||
|
||||
{% blocktrans trimmed domain=domain.name %}
|
||||
Modoboa detected that domain {{ domain }} is listed by the following DNSBL providers:
|
||||
{% endblocktrans %}
|
||||
|
||||
{% for provider, ip in alerts %}
|
||||
{{ provider }}: {{ ip }}
|
||||
{% endfor %}
|
||||
|
||||
{% blocktrans trimmed %}
|
||||
The domain's reputation will be affected and there is a chance that
|
||||
emails coming from it are considered as spam. You should contact
|
||||
those providers and ask them to unlist detected IP address(es).
|
||||
{% endblocktrans %}
|
||||
@@ -4,6 +4,7 @@ from django.conf import settings
|
||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.lib import fields as lib_fields
|
||||
from modoboa.lib.cryptutils import random_key
|
||||
from modoboa.lib.form_utils import (
|
||||
YesNoField, SeparatorField, InlineRadioSelect
|
||||
@@ -209,6 +210,16 @@ class GeneralParametersForm(parameters.AdminParametersForm):
|
||||
required=False
|
||||
)
|
||||
|
||||
notif_sep = SeparatorField(label=ugettext_lazy("Notifications"))
|
||||
|
||||
sender_address = lib_fields.UTF8EmailField(
|
||||
label=_("Sender address"),
|
||||
initial="noreply@modoboa.org",
|
||||
help_text=_(
|
||||
"Email address used to send notifications."
|
||||
)
|
||||
)
|
||||
|
||||
sep3 = SeparatorField(label=ugettext_lazy("Miscellaneous"))
|
||||
|
||||
top_notifications_check_interval = forms.IntegerField(
|
||||
|
||||
Reference in New Issue
Block a user