diff --git a/management/dns_update.py b/management/dns_update.py index f2cf5ee..81e754e 100755 --- a/management/dns_update.py +++ b/management/dns_update.py @@ -48,6 +48,11 @@ def get_dns_zones(env): return zonefiles +def get_custom_dns_config(env): + try: + return rtyaml.load(open(os.path.join(env['STORAGE_ROOT'], 'dns/custom.yaml'))) + except: + return { } def do_dns_update(env): # What domains (and their zone filenames) should we build? @@ -55,10 +60,7 @@ def do_dns_update(env): zonefiles = get_dns_zones(env) # Custom records to add to zones. - try: - additional_records = rtyaml.load(open(os.path.join(env['STORAGE_ROOT'], 'dns/custom.yaml'))) - except: - additional_records = { } + additional_records = get_custom_dns_config(env) # Write zone files. os.makedirs('/etc/nsd/zones', exist_ok=True) diff --git a/management/web_update.py b/management/web_update.py index 63b67cf..5e4b09b 100644 --- a/management/web_update.py +++ b/management/web_update.py @@ -5,17 +5,32 @@ import os, os.path, re, rtyaml from mailconfig import get_mail_domains +from dns_update import get_custom_dns_config from utils import shell, safe_domain_name, sort_domains def get_web_domains(env): - # What domains should we serve HTTP/HTTPS for? + # What domains should we serve websites for? domains = set() - # Add all domain names in use by email users and mail aliases. + # At the least it's the PRIMARY_HOSTNAME so we can serve webmail + # as well as Z-Push for Exchange ActiveSync. + domains.add(env['PRIMARY_HOSTNAME']) + + # Also serve web for all mail domains so that we might at least + # provide Webfinger and ActiveSync auto-discover of email settings + # (though the latter isn't really working). These will require that + # an SSL cert be installed. domains |= get_mail_domains(env) - # Ensure the PRIMARY_HOSTNAME is in the list. - domains.add(env['PRIMARY_HOSTNAME']) + # ...Unless the domain has an A/AAAA record that maps it to a different + # IP address than this box. Remove those domains from our list. + dns = get_custom_dns_config(env) + for domain, value in dns.items(): + if domain not in domains: continue + if (isinstance(value, str) and (value != "local")) \ + or (isinstance(value, dict) and ("A" in value) and (value["A"] != "local")) \ + or (isinstance(value, dict) and ("AAAA" in value) and (value["AAAA"] != "local")): + domains.remove(domain) # Sort the list. Put PRIMARY_HOSTNAME first so it becomes the # default server (nginx's default_server). @@ -23,7 +38,6 @@ def get_web_domains(env): return domains - def do_web_update(env): # Build an nginx configuration file. nginx_conf = "" diff --git a/management/whats_next.py b/management/whats_next.py index e97dce5..1061f8b 100755 --- a/management/whats_next.py +++ b/management/whats_next.py @@ -66,11 +66,8 @@ def run_domain_checks(env): if domain in mail_domains: check_mail_domain(domain, env) - if domain == env["PRIMARY_HOSTNAME"] or domain in web_domains: - # We need a SSL certificate for PRIMARY_HOSTNAME because that's where the - # user will log in with IMAP or webmail. Any other domain we serve a - # website for also needs a signed certificate. - check_ssl_cert(domain, env) + if domain in web_domains: + check_web_domain(domain, env) print() @@ -128,18 +125,6 @@ def check_dns_zone(domain, env, dns_zonefiles): control panel to set the nameservers to %s.""" % (existing_ns, correct_ns) ) - # See if the domain's A record resolves to our PUBLIC_IP. This is already checked - # for PRIMARY_HOSTNAME, for which it is required. For other domains it is just nice - # to have if we want web. - if domain != env['PRIMARY_HOSTNAME']: - ip = query_dns(domain, "A") - if ip == env['PUBLIC_IP']: - print_ok("Domain resolves to this box's IP address. [%s => %s]" % (domain, env['PUBLIC_IP'])) - else: - print_error("""This domain should resolve to your box's IP address (%s) if you would like the box to serve - webmail or a website on this domain. The domain currently resolves to %s in public DNS. It may take several hours for - public DNS to update after a change. This problem may result from other issues listed here.""" % (env['PUBLIC_IP'], ip)) - # See if the domain has a DS record set. ds = query_dns(domain, "DS", nxdomain=None) ds_correct = open('/etc/nsd/zones/' + dns_zonefiles[domain] + '.ds').read().strip() @@ -198,6 +183,24 @@ def check_mail_domain(domain, env): # Check that the postmaster@ email address exists. check_alias_exists("postmaster@" + domain, env) +def check_web_domain(domain, env): + # See if the domain's A record resolves to our PUBLIC_IP. This is already checked + # for PRIMARY_HOSTNAME, for which it is required for mail specifically. For it and + # other domains, it is required to access its website. + if domain != env['PRIMARY_HOSTNAME']: + ip = query_dns(domain, "A") + if ip == env['PUBLIC_IP']: + print_ok("Domain resolves to this box's IP address. [%s => %s]" % (domain, env['PUBLIC_IP'])) + else: + print_error("""This domain should resolve to your box's IP address (%s) if you would like the box to serve + webmail or a website on this domain. The domain currently resolves to %s in public DNS. It may take several hours for + public DNS to update after a change. This problem may result from other issues listed here.""" % (env['PUBLIC_IP'], ip)) + + # We need a SSL certificate for PRIMARY_HOSTNAME because that's where the + # user will log in with IMAP or webmail. Any other domain we serve a + # website for also needs a signed certificate. + check_ssl_cert(domain, env) + def query_dns(qname, rtype, nxdomain='[Not Set]'): resolver = dns.resolver.get_default_resolver() try: