def get_mx_domain_list(domain) -> [str]: """return list of MX domains for a given email. domain name ends *without* a dot (".") at the end. """ priority_domains = get_mx_domains(domain) return [d[:-1] for _, d in priority_domains]
def test_get_mx_domains(): r = get_mx_domains(_DOMAIN) assert len(r) > 0 for x in r: assert x[0] > 0 assert x[1]
def check_custom_domain(): LOG.d("Check verified domain for DNS issues") for custom_domain in CustomDomain.query.filter_by( verified=True ): # type: CustomDomain mx_domains = get_mx_domains(custom_domain.domain) if sorted(mx_domains) != sorted(EMAIL_SERVERS_WITH_PRIORITY): user = custom_domain.user LOG.warning( "The MX record is not correctly set for %s %s %s", custom_domain, user, mx_domains, ) custom_domain.nb_failed_checks += 1 # send alert if fail for 5 consecutive days if custom_domain.nb_failed_checks > 5: domain_dns_url = f"{URL}/dashboard/domains/{custom_domain.id}/dns" LOG.warning( "Alert domain MX check fails %s about %s", user, custom_domain ) send_email_with_rate_control( user, AlERT_WRONG_MX_RECORD_CUSTOM_DOMAIN, user.email, f"Please update {custom_domain.domain} DNS on SimpleLogin", render( "transactional/custom-domain-dns-issue.txt", custom_domain=custom_domain, domain_dns_url=domain_dns_url, ), render( "transactional/custom-domain-dns-issue.html", custom_domain=custom_domain, domain_dns_url=domain_dns_url, ), max_nb_alert=1, nb_day=30, ) # reset checks custom_domain.nb_failed_checks = 0 else: # reset checks custom_domain.nb_failed_checks = 0 db.session.commit()
def check_custom_domain(): LOG.d("Check verified domain for DNS issues") for custom_domain in CustomDomain.query.filter( CustomDomain.verified == True): mx_domains = get_mx_domains(custom_domain.domain) if sorted(mx_domains) != sorted(EMAIL_SERVERS_WITH_PRIORITY): user = custom_domain.user LOG.warning( "The MX record is not correctly set for %s %s %s", custom_domain, user, mx_domains, ) domain_dns_url = f"{URL}/dashboard/domains/{custom_domain.id}/dns" send_email_with_rate_control( user, AlERT_WRONG_MX_RECORD_CUSTOM_DOMAIN, user.email, f"Please update {custom_domain.domain} DNS on SimpleLogin", render( "transactional/custom-domain-dns-issue.txt", custom_domain=custom_domain, name=user.name or "", domain_dns_url=domain_dns_url, ), render( "transactional/custom-domain-dns-issue.html", custom_domain=custom_domain, name=user.name or "", domain_dns_url=domain_dns_url, ), max_nb_alert=1, nb_day=30, )
def check_single_custom_domain(custom_domain): mx_domains = get_mx_domains(custom_domain.domain) if not is_mx_equivalent(mx_domains, EMAIL_SERVERS_WITH_PRIORITY): user = custom_domain.user LOG.w( "The MX record is not correctly set for %s %s %s", custom_domain, user, mx_domains, ) custom_domain.nb_failed_checks += 1 # send alert if fail for 5 consecutive days if custom_domain.nb_failed_checks > 5: domain_dns_url = f"{URL}/dashboard/domains/{custom_domain.id}/dns" LOG.w("Alert domain MX check fails %s about %s", user, custom_domain) send_email_with_rate_control( user, AlERT_WRONG_MX_RECORD_CUSTOM_DOMAIN, user.email, f"Please update {custom_domain.domain} DNS on SimpleLogin", render( "transactional/custom-domain-dns-issue.txt.jinja2", custom_domain=custom_domain, domain_dns_url=domain_dns_url, ), max_nb_alert=1, nb_day=30, retries=3, ) # reset checks custom_domain.nb_failed_checks = 0 else: # reset checks custom_domain.nb_failed_checks = 0 Session.commit()
def domain_detail_dns(custom_domain_id): custom_domain = CustomDomain.get(custom_domain_id) if not custom_domain or custom_domain.user_id != current_user.id: flash("You cannot see this page", "warning") return redirect(url_for("dashboard.index")) spf_record = f"v=spf1 include:{EMAIL_DOMAIN} -all" # hardcode the DKIM selector here dkim_cname = f"dkim._domainkey.{EMAIL_DOMAIN}" 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": mx_domains = get_mx_domains(custom_domain.domain) if sorted(mx_domains) != sorted(EMAIL_SERVERS_WITH_PRIORITY): flash("The MX record is not correctly set", "warning") mx_ok = False # build mx_errors to show to user mx_errors = [ f"{priority} {domain}" for (priority, domain) in mx_domains ] else: flash( "Your domain can start receiving emails. You can now use it to create alias", "success", ) custom_domain.verified = True db.session.commit() return redirect( url_for("dashboard.domain_detail_dns", custom_domain_id=custom_domain.id)) elif request.form.get("form-name") == "check-spf": spf_domains = get_spf_domain(custom_domain.domain) if EMAIL_DOMAIN in spf_domains: custom_domain.spf_verified = True db.session.commit() flash("SPF is setup correctly", "success") return redirect( url_for("dashboard.domain_detail_dns", custom_domain_id=custom_domain.id)) else: custom_domain.spf_verified = False db.session.commit() flash( f"SPF: {EMAIL_DOMAIN} is not included in your SPF record.", "warning", ) spf_ok = False spf_errors = get_txt_record(custom_domain.domain) elif request.form.get("form-name") == "check-dkim": dkim_record = get_cname_record("dkim._domainkey." + custom_domain.domain) if dkim_record == dkim_cname: flash("DKIM is setup correctly.", "success") custom_domain.dkim_verified = True db.session.commit() return redirect( url_for("dashboard.domain_detail_dns", custom_domain_id=custom_domain.id)) else: custom_domain.dkim_verified = False db.session.commit() flash("DKIM: the CNAME record is not correctly set", "warning") 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: custom_domain.dmarc_verified = False db.session.commit() 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, **locals(), )
def domain_detail_dns(custom_domain_id): custom_domain: CustomDomain = CustomDomain.get(custom_domain_id) if not custom_domain or custom_domain.user_id != current_user.id: flash("You cannot see this page", "warning") return redirect(url_for("dashboard.index")) # generate a domain ownership txt token if needed if not custom_domain.ownership_verified and not custom_domain.ownership_txt_token: custom_domain.ownership_txt_token = random_string(30) Session.commit() spf_record = f"v=spf1 include:{EMAIL_DOMAIN} ~all" # hardcode the DKIM selector here dkim_cname = f"dkim._domainkey.{EMAIL_DOMAIN}" dmarc_record = "v=DMARC1; p=quarantine; pct=100; adkim=s; aspf=s" mx_ok = spf_ok = dkim_ok = dmarc_ok = ownership_ok = True mx_errors = spf_errors = dkim_errors = dmarc_errors = ownership_errors = [] if request.method == "POST": if request.form.get("form-name") == "check-ownership": txt_records = get_txt_record(custom_domain.domain) if custom_domain.get_ownership_dns_txt_value() in txt_records: flash( "Domain ownership is verified. Please proceed to the other records setup", "success", ) custom_domain.ownership_verified = True Session.commit() return redirect( url_for( "dashboard.domain_detail_dns", custom_domain_id=custom_domain.id, _anchor="dns-setup", ) ) else: flash("We can't find the needed TXT record", "error") ownership_ok = False ownership_errors = txt_records elif request.form.get("form-name") == "check-mx": mx_domains = get_mx_domains(custom_domain.domain) if not is_mx_equivalent(mx_domains, EMAIL_SERVERS_WITH_PRIORITY): flash("The MX record is not correctly set", "warning") mx_ok = False # build mx_errors to show to user mx_errors = [ f"{priority} {domain}" for (priority, domain) in mx_domains ] else: flash( "Your domain can start receiving emails. You can now use it to create alias", "success", ) custom_domain.verified = True Session.commit() return redirect( url_for( "dashboard.domain_detail_dns", custom_domain_id=custom_domain.id ) ) elif request.form.get("form-name") == "check-spf": spf_domains = get_spf_domain(custom_domain.domain) if EMAIL_DOMAIN in spf_domains: custom_domain.spf_verified = True Session.commit() flash("SPF is setup correctly", "success") return redirect( url_for( "dashboard.domain_detail_dns", custom_domain_id=custom_domain.id ) ) else: custom_domain.spf_verified = False Session.commit() flash( f"SPF: {EMAIL_DOMAIN} is not included in your SPF record.", "warning", ) spf_ok = False spf_errors = get_txt_record(custom_domain.domain) elif request.form.get("form-name") == "check-dkim": dkim_record = get_cname_record("dkim._domainkey." + custom_domain.domain) if dkim_record == dkim_cname: flash("DKIM is setup correctly.", "success") custom_domain.dkim_verified = True Session.commit() return redirect( url_for( "dashboard.domain_detail_dns", custom_domain_id=custom_domain.id ) ) else: custom_domain.dkim_verified = False Session.commit() flash("DKIM: the CNAME record is not correctly set", "warning") 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 Session.commit() flash("DMARC is setup correctly", "success") return redirect( url_for( "dashboard.domain_detail_dns", custom_domain_id=custom_domain.id ) ) else: custom_domain.dmarc_verified = False Session.commit() flash( "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, **locals(), )
def domain_detail_dns(custom_domain_id): # only premium user can see custom domain if not current_user.is_premium(): flash("Only premium user can add custom domains", "warning") return redirect(url_for("dashboard.index")) custom_domain = CustomDomain.get(custom_domain_id) if not custom_domain or custom_domain.user_id != current_user.id: flash("You cannot see this page", "warning") return redirect(url_for("dashboard.index")) mx_ok = spf_ok = dkim_ok = True mx_errors = spf_errors = dkim_errors = [] if request.method == "POST": if request.form.get("form-name") == "check-mx": mx_domains = get_mx_domains(custom_domain.domain) if sorted(mx_domains) != sorted(EMAIL_SERVERS_WITH_PRIORITY): mx_ok = False # build mx_errors to show to user mx_errors = [ f"{priority} {domain}" for (priority, domain) in mx_domains ] else: flash( "Your domain is verified. Now it can be used to create custom alias", "success", ) custom_domain.verified = True db.session.commit() return redirect( url_for("dashboard.domain_detail_dns", custom_domain_id=custom_domain.id)) elif request.form.get("form-name") == "check-spf": spf_domains = get_spf_domain(custom_domain.domain) if EMAIL_DOMAIN in spf_domains: custom_domain.spf_verified = True db.session.commit() flash("The SPF is setup correctly", "success") return redirect( url_for("dashboard.domain_detail_dns", custom_domain_id=custom_domain.id)) else: flash(f"{EMAIL_DOMAIN} is not included in your SPF record.", "warning") spf_ok = False spf_errors = get_txt_record(custom_domain.domain) elif request.form.get("form-name") == "check-dkim": dkim_record = get_dkim_record(custom_domain.domain) correct_dkim_record = f"v=DKIM1; k=rsa; p={DKIM_DNS_VALUE}" if dkim_record == correct_dkim_record: flash("The DKIM is setup correctly.", "success") custom_domain.dkim_verified = True db.session.commit() return redirect( url_for("dashboard.domain_detail_dns", custom_domain_id=custom_domain.id)) else: dkim_ok = False dkim_errors = get_txt_record( f"dkim._domainkey.{custom_domain.domain}") spf_record = f"v=spf1 include:{EMAIL_DOMAIN} -all" dkim_record = f"v=DKIM1; k=rsa; p={DKIM_DNS_VALUE}" return render_template( "dashboard/domain_detail/dns.html", EMAIL_SERVERS_WITH_PRIORITY=EMAIL_SERVERS_WITH_PRIORITY, **locals(), )
def custom_domain(): # only premium user can add custom domain if not current_user.is_premium(): flash("Only premium user can add custom domains", "warning") return redirect(url_for("dashboard.index")) custom_domains = CustomDomain.query.filter_by(user_id=current_user.id).all() new_custom_domain_form = NewCustomDomainForm() errors = {} if request.method == "POST": if request.form.get("form-name") == "delete": custom_domain_id = request.form.get("custom-domain-id") custom_domain = CustomDomain.get(custom_domain_id) if not custom_domain: flash("Unknown error. Refresh the page", "warning") return redirect(url_for("dashboard.custom_domain")) elif custom_domain.user_id != current_user.id: flash("You cannot delete this domain", "warning") return redirect(url_for("dashboard.custom_domain")) name = custom_domain.domain CustomDomain.delete(custom_domain_id) db.session.commit() flash(f"Domain {name} has been deleted successfully", "success") return redirect(url_for("dashboard.custom_domain")) elif request.form.get("form-name") == "create": if new_custom_domain_form.validate(): new_custom_domain = CustomDomain.create( domain=new_custom_domain_form.domain.data, user_id=current_user.id ) db.session.commit() flash( f"New domain {new_custom_domain.domain} has been created successfully", "success", ) return redirect(url_for("dashboard.custom_domain")) elif request.form.get("form-name") == "check-domain": custom_domain_id = request.form.get("custom-domain-id") custom_domain = CustomDomain.get(custom_domain_id) if not custom_domain: flash("Unknown error. Refresh the page", "warning") return redirect(url_for("dashboard.custom_domain")) elif custom_domain.user_id != current_user.id: flash("You cannot delete this domain", "warning") return redirect(url_for("dashboard.custom_domain")) else: spf_domains = get_spf_domain(custom_domain.domain) for email_server in EMAIL_SERVERS: email_server = email_server[:-1] # remove the trailing . if email_server not in spf_domains: flash( f"{email_server} is not included in your SPF record.", "warning", ) mx_domains = get_mx_domains(custom_domain.domain) if mx_domains != EMAIL_SERVERS: errors[ custom_domain.id ] = f"""Your DNS is not correctly set. The MX record we obtain is: {",".join(mx_domains)}""" else: flash( "Your domain is verified. Now it can be used to create custom alias", "success", ) custom_domain.verified = True db.session.commit() return render_template( "dashboard/custom_domain.html", custom_domains=custom_domains, new_custom_domain_form=new_custom_domain_form, EMAIL_SERVERS_WITH_PRIORITY=EMAIL_SERVERS_WITH_PRIORITY, errors=errors, )