mirror of
https://github.com/mail-in-a-box/mailinabox.git
synced 2024-12-21 07:07:05 +00:00
write explanatory text as we build DNS zones so we can help the user manage DNS off of the box
This commit is contained in:
parent
91cf45c843
commit
7803ac9ca4
@ -129,36 +129,41 @@ def build_zone(domain, subdomains, additional_records, env, with_ns=True):
|
||||
records = []
|
||||
|
||||
# For top-level zones, define ourselves as the authoritative name server.
|
||||
# 'False' in the tuple indicates these records would not be used if the zone
|
||||
# is managed outside of the box.
|
||||
if with_ns:
|
||||
records.append((None, "NS", "ns1.%s." % env["PRIMARY_HOSTNAME"]))
|
||||
records.append((None, "NS", "ns2.%s." % env["PRIMARY_HOSTNAME"]))
|
||||
records.append((None, "NS", "ns1.%s." % env["PRIMARY_HOSTNAME"], False))
|
||||
records.append((None, "NS", "ns2.%s." % env["PRIMARY_HOSTNAME"], False))
|
||||
|
||||
# The MX record says where email for the domain should be delivered: Here!
|
||||
records.append((None, "MX", "10 %s." % env["PRIMARY_HOSTNAME"]))
|
||||
records.append((None, "MX", "10 %s." % env["PRIMARY_HOSTNAME"], "Required. Specifies the hostname of the machine that handles @%s mail." % domain))
|
||||
|
||||
# SPF record: Permit the box ('mx', see above) to send mail on behalf of
|
||||
# the domain, and no one else.
|
||||
records.append((None, "TXT", '"v=spf1 mx -all"'))
|
||||
records.append((None, "TXT", '"v=spf1 mx -all"', "Recomended. Specifies that only the box is permitted to send @%s mail." % domain))
|
||||
|
||||
# If we need to define DNS for any subdomains of this domain, include it
|
||||
# in the zone.
|
||||
for subdomain in subdomains:
|
||||
subdomain_qname = subdomain[0:-len("." + domain)]
|
||||
for child_qname, child_rtype, child_value in build_zone(subdomain, [], {}, env, with_ns=False):
|
||||
subzone = build_zone(subdomain, [], {}, env, with_ns=False)
|
||||
for child_qname, child_rtype, child_value, child_explanation in subzone:
|
||||
if child_qname == None:
|
||||
child_qname = subdomain_qname
|
||||
else:
|
||||
child_qname += "." + subdomain_qname
|
||||
records.append((child_qname, child_rtype, child_value))
|
||||
records.append((child_qname, child_rtype, child_value, child_explanation))
|
||||
|
||||
# In PRIMARY_HOSTNAME...
|
||||
if domain == env["PRIMARY_HOSTNAME"]:
|
||||
# Define ns1 and ns2.
|
||||
records.append(("ns1", "A", env["PUBLIC_IP"]))
|
||||
records.append(("ns2", "A", env["PUBLIC_IP"]))
|
||||
# 'False' in the tuple indicates these records would not be used if the zone
|
||||
# is managed outside of the box.
|
||||
records.append(("ns1", "A", env["PUBLIC_IP"], False))
|
||||
records.append(("ns2", "A", env["PUBLIC_IP"], False))
|
||||
|
||||
# Add a DANE TLSA record for SMTP.
|
||||
records.append(("_25._tcp", "TLSA", build_tlsa_record(env)))
|
||||
records.append(("_25._tcp", "TLSA", build_tlsa_record(env), "Recommended when DNSSEC is enabled. Advertises to mail servers connecting to the box that mandatory encryption should be used."))
|
||||
|
||||
def has_rec(qname, rtype):
|
||||
for rec in records:
|
||||
@ -175,17 +180,26 @@ def build_zone(domain, subdomains, additional_records, env, with_ns=True):
|
||||
qname = qname[0:len(qname)-len("." + domain)]
|
||||
if has_rec(qname, value): continue
|
||||
if isinstance(value, str):
|
||||
records.append((qname, "A", value))
|
||||
values = [("A", value)]
|
||||
elif isinstance(value, dict):
|
||||
for rtype, value2 in value.items():
|
||||
if rtype == "TXT": value2 = "\"" + value2 + "\""
|
||||
records.append((qname, rtype, value2))
|
||||
values = value.items()
|
||||
else:
|
||||
raise ValueError()
|
||||
for rtype, value2 in values:
|
||||
if rtype == "TXT": value2 = "\"" + value2 + "\""
|
||||
records.append((qname, rtype, value2, "(Set by user.)"))
|
||||
|
||||
# Add defaults if not overridden by the user's custom settings.
|
||||
if not has_rec(None, "A"): records.append((None, "A", env["PUBLIC_IP"]))
|
||||
if env.get('PUBLIC_IPV6') and not has_rec(None, "AAAA"): records.append((None, "AAAA", env["PUBLIC_IPV6"]))
|
||||
if not has_rec("www", "A"): records.append(("www", "A", env["PUBLIC_IP"]))
|
||||
if env.get('PUBLIC_IPV6') and not has_rec("www", "AAAA"): records.append(("www", "AAAA", env["PUBLIC_IPV6"]))
|
||||
defaults = [
|
||||
(None, "A", env["PUBLIC_IP"], "Optional. Sets the IP address that %s resolves to, e.g. for web hosting." % domain),
|
||||
("www", "A", env["PUBLIC_IP"], "Optional. Sets the IP address that www.%s resolves to, e.g. for web hosting." % domain),
|
||||
(None, "AAAA", env.get('PUBLIC_IPV6'), "Optional. Sets the IPv6 address that %s resolves to, e.g. for web hosting." % domain),
|
||||
("www", "AAAA", env.get('PUBLIC_IPV6'), "Optional. Sets the IPv6 address that www.%s resolves to, e.g. for web hosting." % domain),
|
||||
]
|
||||
for qname, rtype, value, explanation in defaults:
|
||||
if value is None or value.strip() == "": continue # skip IPV6 if not set
|
||||
if not has_rec(qname, rtype):
|
||||
records.append((qname, rtype, value, explanation))
|
||||
|
||||
# If OpenDKIM is in use..
|
||||
opendkim_record_file = os.path.join(env['STORAGE_ROOT'], 'mail/dkim/mail.txt')
|
||||
@ -193,12 +207,12 @@ def build_zone(domain, subdomains, additional_records, env, with_ns=True):
|
||||
# Append the DKIM TXT record to the zone as generated by OpenDKIM, after string formatting above.
|
||||
with open(opendkim_record_file) as orf:
|
||||
m = re.match(r"(\S+)\s+IN\s+TXT\s+(\(.*\))\s*;", orf.read(), re.S)
|
||||
records.append((m.group(1), "TXT", m.group(2)))
|
||||
records.append((m.group(1), "TXT", m.group(2), "Recommended. Specifies that only the box is permitted to send mail at this domain."))
|
||||
|
||||
# Append a DMARC record.
|
||||
records.append(("_dmarc", "TXT", '"v=DMARC1; p=quarantine"'))
|
||||
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))
|
||||
|
||||
# Sort the records. The None records *must* go first. Otherwise it doesn't matter.
|
||||
# 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 ""))
|
||||
|
||||
return records
|
||||
@ -255,7 +269,7 @@ $TTL 86400 ; default time to live
|
||||
zone = zone.format(domain=domain, primary_domain=env["PRIMARY_HOSTNAME"])
|
||||
|
||||
# Add records.
|
||||
for subdomain, querytype, value in records:
|
||||
for subdomain, querytype, value, explanation in records:
|
||||
if subdomain:
|
||||
zone += subdomain
|
||||
zone += "\tIN\t" + querytype + "\t"
|
||||
@ -490,7 +504,7 @@ def justtestingdotemail(domain, records):
|
||||
if not domain.endswith(".justtesting.email"):
|
||||
return
|
||||
|
||||
for subdomain, querytype, value in records:
|
||||
for subdomain, querytype, value, explanation in records:
|
||||
if querytype in ("NS",): continue
|
||||
if subdomain in ("www", "ns1", "ns2"): continue # don't do unnecessary things
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user