From ab47144ae36fd79658e96eb22a9358be0337180a Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Fri, 26 Sep 2014 14:00:05 +0000 Subject: [PATCH] add strict SPF and DMARC records to any subdomains (including custom records) that do not have SPF/DMARC set closes #208 --- management/dns_update.py | 14 ++++++++++++-- management/templates/system-dns.html | 2 ++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/management/dns_update.py b/management/dns_update.py index dfd9ddb9..b9aa2612 100755 --- a/management/dns_update.py +++ b/management/dns_update.py @@ -184,9 +184,9 @@ def build_zone(domain, all_domains, additional_records, env, is_zone=True): child_qname += "." + subdomain_qname records.append((child_qname, child_rtype, child_value, child_explanation)) - def has_rec(qname, rtype): + def has_rec(qname, rtype, prefix=None): for rec in records: - if rec[0] == qname and rec[1] == rtype: + if rec[0] == qname and rec[1] == rtype and (prefix is None or rec[2].startswith(prefix)): return True return False @@ -218,6 +218,16 @@ def build_zone(domain, all_domains, additional_records, env, is_zone=True): # Append a DMARC record. records.append(("_dmarc", "TXT", 'v=DMARC1; p=quarantine', "Optional. Specifies that mail that does not originate from the box but claims to be from @%s is suspect and should be quarantined by the recipient's mail system." % domain)) + # For any subdomain with an A record but no SPF or DMARC record, add strict policy records. + all_resolvable_qnames = set(r[0] for r in records if r[1] in ("A", "AAAA")) + for qname in all_resolvable_qnames: + if not has_rec(qname, "TXT", prefix="v=spf1 "): + records.append((qname, "TXT", 'v=spf1 a mx -all', "Prevents unauthorized use of this domain name for outbound mail by requiring outbound mail to originate from the indicated host(s).")) + dmarc_qname = "_dmarc" + ("" if qname is None else "." + qname) + if not has_rec(dmarc_qname, "TXT", prefix="v=DMARC1; "): + records.append((dmarc_qname, "TXT", 'v=DMARC1; p=reject', "Prevents unauthorized use of this domain name for outbound mail by requiring a valid DKIM signature.")) + + # Sort the records. The None records *must* go first in the nsd zone file. Otherwise it doesn't matter. records.sort(key = lambda rec : list(reversed(rec[0].split(".")) if rec[0] is not None else "")) diff --git a/management/templates/system-dns.html b/management/templates/system-dns.html index bc8e554d..59a67170 100644 --- a/management/templates/system-dns.html +++ b/management/templates/system-dns.html @@ -50,6 +50,8 @@

Note that -d "" is merely to ensure curl sends a POST request. You do not need to put anything inside the quotes. You can also pass the value using typical form encoding in the POST body.

+

Strict SPF and DMARC records will be added to all custom domains unless you override them.

+

Examples:

# sets laptop.mydomain.com to point to the IP address of the machine you are executing curl on