From 753e82d490ece921b7f774ea274093c261dc5fa1 Mon Sep 17 00:00:00 2001 From: Son NK <> Date: Sun, 3 May 2020 12:01:31 +0200 Subject: [PATCH] Add DMARC --- .../dashboard/domain_detail/dns.html | 72 ++++++++++++++++++- app/dashboard/views/domain_detail.py | 33 +++++++-- app/dns_utils.py | 17 +---- tests/test_dns_utils.py | 5 -- 4 files changed, 98 insertions(+), 29 deletions(-) diff --git a/app/dashboard/templates/dashboard/domain_detail/dns.html b/app/dashboard/templates/dashboard/domain_detail/dns.html index 4a6d9a24..bd84d189 100644 --- a/app/dashboard/templates/dashboard/domain_detail/dns.html +++ b/app/dashboard/templates/dashboard/domain_detail/dns.html @@ -9,7 +9,7 @@ {% block domain_detail_content %}
-

{{ custom_domain.domain }}

+

{{ custom_domain.domain }}

Please follow the steps below to set up your domain.
@@ -167,7 +167,7 @@ Domain: dkim._domainkey.{{ custom_domain.domain }}
+ data-clipboard-text="dkim._domainkey">dkim._domainkey.{{ custom_domain.domain }}
Value: +
+ +
+
4. DMARC (Optional) + {% if custom_domain.dmarc_verified %} + + {% else %} + 🚫 + {% endif %} +
+ +
+ DMARC (Wikipedia↗) + is designed to protect the domain from unauthorized use, commonly known as email spoofing.
+ Built around SPF and DKIM, a DMARC policy tells the receiving mail server what to do if + neither of those authentication methods passes. +
+ +
Add the following TXT DNS record to your domain.
+ +
+ Record: TXT
+ Domain: _dmarc.{{ custom_domain.domain }}
+ Value: + + {{ dmarc_record }} + +
+ +
+ + {% if custom_domain.dmarc_verified %} + + {% else %} + + {% endif %} +
+ + {% if not dmarc_ok %} +
+ Your DNS is not correctly set. + The TXT record we obtain is: +
+ {% if not dmarc_errors %} + (Empty) + {% endif %} + + {% for r in dmarc_errors %} + {{ r }}
+ {% endfor %} +
+ {% if custom_domain.dmarc_verified %} + Without DMARC setup, emails sent from your alias might end up in the Spam/Junk folder. + {% endif %} +
+ {% endif %} +
+
{% endblock %} diff --git a/app/dashboard/views/domain_detail.py b/app/dashboard/views/domain_detail.py index 544875e4..534e68cd 100644 --- a/app/dashboard/views/domain_detail.py +++ b/app/dashboard/views/domain_detail.py @@ -1,12 +1,11 @@ from flask import render_template, request, redirect, url_for, flash from flask_login import login_required, current_user -from app.config import EMAIL_SERVERS_WITH_PRIORITY, DKIM_DNS_VALUE, EMAIL_DOMAIN +from app.config import EMAIL_SERVERS_WITH_PRIORITY, EMAIL_DOMAIN from app.dashboard.base import dashboard_bp from app.dns_utils import ( get_mx_domains, get_spf_domain, - get_dkim_record, get_txt_record, get_cname_record, ) @@ -27,8 +26,10 @@ def domain_detail_dns(custom_domain_id): # hardcode the DKIM selector here dkim_cname = f"dkim._domainkey.{EMAIL_DOMAIN}" - mx_ok = spf_ok = dkim_ok = True - mx_errors = spf_errors = dkim_errors = [] + dmarc_record = "v=DMARC1; p=quarantine; pct=100; adkim=s; aspf=s" + + mx_ok = spf_ok = dkim_ok = dmarc_ok = True + mx_errors = spf_errors = dkim_errors = dmarc_errors = [] if request.method == "POST": if request.form.get("form-name") == "check-mx": @@ -43,7 +44,7 @@ def domain_detail_dns(custom_domain_id): ] else: flash( - "Your domain is verified. Now it can be used to create custom alias", + "Your domain can start receiving emails. You can now use it to create alias", "success", ) custom_domain.verified = True @@ -58,7 +59,7 @@ def domain_detail_dns(custom_domain_id): if EMAIL_DOMAIN in spf_domains: custom_domain.spf_verified = True db.session.commit() - flash("The SPF is setup correctly", "success") + flash("SPF is setup correctly", "success") return redirect( url_for( "dashboard.domain_detail_dns", custom_domain_id=custom_domain.id @@ -75,7 +76,7 @@ def domain_detail_dns(custom_domain_id): elif request.form.get("form-name") == "check-dkim": dkim_record = get_cname_record(custom_domain.domain) if dkim_record == dkim_cname: - flash("The DKIM is setup correctly.", "success") + flash("DKIM is setup correctly.", "success") custom_domain.dkim_verified = True db.session.commit() @@ -89,6 +90,24 @@ def domain_detail_dns(custom_domain_id): dkim_ok = False dkim_errors = [dkim_record or "[Empty]"] + elif request.form.get("form-name") == "check-dmarc": + txt_records = get_txt_record("_dmarc." + custom_domain.domain) + if dmarc_record in txt_records: + custom_domain.dmarc_verified = True + db.session.commit() + flash("DMARC is setup correctly", "success") + return redirect( + url_for( + "dashboard.domain_detail_dns", custom_domain_id=custom_domain.id + ) + ) + else: + flash( + f"DMARC: The TXT record is not correctly set", "warning", + ) + dmarc_ok = False + dmarc_errors = txt_records + return render_template( "dashboard/domain_detail/dns.html", EMAIL_SERVERS_WITH_PRIORITY=EMAIL_SERVERS_WITH_PRIORITY, diff --git a/app/dns_utils.py b/app/dns_utils.py index 052c71d5..b4a1a8ed 100644 --- a/app/dns_utils.py +++ b/app/dns_utils.py @@ -71,6 +71,7 @@ def get_spf_domain(hostname) -> [str]: def get_txt_record(hostname) -> [str]: + """return all domains listed in *include:*""" try: answers = _get_dns_resolver().query(hostname, "TXT") except Exception: @@ -78,24 +79,10 @@ def get_txt_record(hostname) -> [str]: ret = [] - for a in answers: # type: dns.rdtypes.ANY.TXT.TXT - ret.append(a) - - return ret - - -def get_dkim_record(hostname) -> str: - """query the dkim._domainkey.{hostname} record and returns its value""" - try: - answers = _get_dns_resolver().query(f"dkim._domainkey.{hostname}", "TXT") - except Exception: - return "" - - ret = [] for a in answers: # type: dns.rdtypes.ANY.TXT.TXT for record in a.strings: record = record.decode() # record is bytes ret.append(record) - return "".join(ret) + return ret diff --git a/tests/test_dns_utils.py b/tests/test_dns_utils.py index e03acbb6..e48a370a 100644 --- a/tests/test_dns_utils.py +++ b/tests/test_dns_utils.py @@ -23,8 +23,3 @@ def test_get_txt_record(): r = get_txt_record(_DOMAIN) assert len(r) > 0 - - -def test_get_dkim_record(): - r = get_dkim_record(_DOMAIN) - assert r.startswith("v=DKIM1; k=rsa;")