diff --git a/Vagrantfile b/Vagrantfile index 757c2ec9..2ab2f405 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -8,7 +8,7 @@ Vagrant.configure("2") do |config| # to the public web. However, we currently don't want to expose SSH since # the machine's box will let anyone log into it. So instead we'll put the # machine on a private network. - config.vm.hostname = "mailinabox.lan" + config.vm.hostname = "box.mailinabox.lan" config.vm.network "private_network", ip: "192.168.56.4" config.vm.provision :shell, :inline => <<-SH @@ -18,7 +18,7 @@ Vagrant.configure("2") do |config| export NONINTERACTIVE=1 export PUBLIC_IP=auto export PUBLIC_IPV6=auto - export PRIMARY_HOSTNAME=auto + export BOX_HOSTNAME=auto #export SKIP_NETWORK_CHECKS=1 # Start the setup script. diff --git a/conf/ios-profile.xml b/conf/ios-profile.xml index 273c0bf6..b0513535 100644 --- a/conf/ios-profile.xml +++ b/conf/ios-profile.xml @@ -13,19 +13,19 @@ CalDAVAccountDescription - PRIMARY_HOSTNAME calendar + BOX_HOSTNAME calendar CalDAVHostName - PRIMARY_HOSTNAME + BOX_HOSTNAME CalDAVPort 443 CalDAVUseSSL PayloadDescription - PRIMARY_HOSTNAME (Mail-in-a-Box) + BOX_HOSTNAME (Mail-in-a-Box) PayloadDisplayName - PRIMARY_HOSTNAME calendar + BOX_HOSTNAME calendar PayloadIdentifier - email.mailinabox.mobileconfig.PRIMARY_HOSTNAME.CalDAV + email.mailinabox.mobileconfig.BOX_HOSTNAME.CalDAV PayloadOrganization PayloadType @@ -37,13 +37,13 @@ EmailAccountDescription - PRIMARY_HOSTNAME mail + BOX_HOSTNAME mail EmailAccountType EmailTypeIMAP IncomingMailServerAuthentication EmailAuthPassword IncomingMailServerHostName - PRIMARY_HOSTNAME + BOX_HOSTNAME IncomingMailServerPortNumber 993 IncomingMailServerUseSSL @@ -51,7 +51,7 @@ OutgoingMailServerAuthentication EmailAuthPassword OutgoingMailServerHostName - PRIMARY_HOSTNAME + BOX_HOSTNAME OutgoingMailServerPortNumber 465 OutgoingMailServerUseSSL @@ -59,11 +59,11 @@ OutgoingPasswordSameAsIncomingPassword PayloadDescription - PRIMARY_HOSTNAME (Mail-in-a-Box) + BOX_HOSTNAME (Mail-in-a-Box) PayloadDisplayName - PRIMARY_HOSTNAME mail + BOX_HOSTNAME mail PayloadIdentifier - email.mailinabox.mobileconfig.PRIMARY_HOSTNAME.E-Mail + email.mailinabox.mobileconfig.BOX_HOSTNAME.E-Mail PayloadOrganization PayloadType @@ -81,9 +81,9 @@ CardDAVAccountDescription - PRIMARY_HOSTNAME contacts + BOX_HOSTNAME contacts CardDAVHostName - PRIMARY_HOSTNAME + BOX_HOSTNAME CardDAVPort 443 CardDAVPrincipalURL @@ -91,11 +91,11 @@ CardDAVUseSSL PayloadDescription - PRIMARY_HOSTNAME (Mail-in-a-Box) + BOX_HOSTNAME (Mail-in-a-Box) PayloadDisplayName - PRIMARY_HOSTNAME contacts + BOX_HOSTNAME contacts PayloadIdentifier - email.mailinabox.mobileconfig.PRIMARY_HOSTNAME.carddav + email.mailinabox.mobileconfig.BOX_HOSTNAME.carddav PayloadOrganization PayloadType @@ -107,11 +107,11 @@ PayloadDescription - PRIMARY_HOSTNAME (Mail-in-a-Box) + BOX_HOSTNAME (Mail-in-a-Box) PayloadDisplayName - PRIMARY_HOSTNAME + BOX_HOSTNAME PayloadIdentifier - email.mailinabox.mobileconfig.PRIMARY_HOSTNAME + email.mailinabox.mobileconfig.BOX_HOSTNAME PayloadOrganization PayloadRemovalDisallowed diff --git a/conf/mozilla-autoconfig.xml b/conf/mozilla-autoconfig.xml index df9cce61..3ef20bb6 100644 --- a/conf/mozilla-autoconfig.xml +++ b/conf/mozilla-autoconfig.xml @@ -1,13 +1,13 @@ - - PRIMARY_HOSTNAME + + BOX_HOSTNAME - PRIMARY_HOSTNAME (Mail-in-a-Box) - PRIMARY_HOSTNAME + BOX_HOSTNAME (Mail-in-a-Box) + BOX_HOSTNAME - PRIMARY_HOSTNAME + BOX_HOSTNAME 993 SSL %EMAILADDRESS% @@ -15,7 +15,7 @@ - PRIMARY_HOSTNAME + BOX_HOSTNAME 465 SSL %EMAILADDRESS% @@ -24,14 +24,14 @@ false - - PRIMARY_HOSTNAME website. + + BOX_HOSTNAME website. - - + + %EMAILADDRESS% @@ -39,6 +39,6 @@ - + diff --git a/conf/mta-sts.txt b/conf/mta-sts.txt index e7bdc4c4..44f3fed7 100644 --- a/conf/mta-sts.txt +++ b/conf/mta-sts.txt @@ -1,4 +1,4 @@ -version: STSv1 -mode: MODE -mx: PRIMARY_HOSTNAME -max_age: 604800 +version: STSv1 +mode: MODE +mx: BOX_HOSTNAME +max_age: 604800 diff --git a/conf/nginx-primaryonly.conf b/conf/nginx-boxonly.conf similarity index 100% rename from conf/nginx-primaryonly.conf rename to conf/nginx-boxonly.conf diff --git a/conf/postfix_outgoing_mail_header_filters b/conf/postfix_outgoing_mail_header_filters index 7f4c6ff6..d6e43217 100644 --- a/conf/postfix_outgoing_mail_header_filters +++ b/conf/postfix_outgoing_mail_header_filters @@ -1,7 +1,7 @@ # Remove the first line of the Received: header. Note that we cannot fully remove the Received: header # because OpenDKIM requires that a header be present when signing outbound mail. The first line is # where the user's home IP address would be. -/^\s*Received:[^\n]*(.*)/ REPLACE Received: from authenticated-user (PRIMARY_HOSTNAME [PUBLIC_IP])$1 +/^\s*Received:[^\n]*(.*)/ REPLACE Received: from authenticated-user (BOX_HOSTNAME [PUBLIC_IP])$1 # Remove other typically private information. /^\s*User-Agent:/ IGNORE diff --git a/conf/zpush/autodiscover_config.php b/conf/zpush/autodiscover_config.php index 1a9a45a1..78d19c27 100644 --- a/conf/zpush/autodiscover_config.php +++ b/conf/zpush/autodiscover_config.php @@ -10,7 +10,7 @@ define('TIMEZONE', ''); // Defines the base path on the server define('BASE_PATH', dirname($_SERVER['SCRIPT_FILENAME']). '/'); -define('ZPUSH_HOST', 'PRIMARY_HOSTNAME'); +define('ZPUSH_HOST', 'BOX_HOSTNAME'); define('USE_FULLEMAIL_FOR_LOGIN', true); diff --git a/management/daemon.py b/management/daemon.py index 3aa6eed2..f9c49907 100755 --- a/management/daemon.py +++ b/management/daemon.py @@ -125,7 +125,7 @@ def index(): return render_template('index.html', - hostname=env['PRIMARY_HOSTNAME'], + hostname=env['BOX_HOSTNAME'], storage_root=env['STORAGE_ROOT'], no_users_exist=no_users_exist, diff --git a/management/dns_update.py b/management/dns_update.py index 186e14a5..b431f743 100755 --- a/management/dns_update.py +++ b/management/dns_update.py @@ -22,13 +22,13 @@ DOMAIN_RE = r"^(?!\-)(?:[*][.])?(?:[a-zA-Z\d\-_]{0,62}[a-zA-Z\d_]\.){1,126}(?!\d def get_dns_domains(env): # Add all domain names in use by email users and mail aliases, any # domains we serve web for (except www redirects because that would - # lead to infinite recursion here) and ensure PRIMARY_HOSTNAME is in the list. + # lead to infinite recursion here) and ensure BOX_HOSTNAME is in the list. from mailconfig import get_mail_domains from web_update import get_web_domains domains = set() domains |= set(get_mail_domains(env)) domains |= set(get_web_domains(env, include_www_redirects=False)) - domains.add(env['PRIMARY_HOSTNAME']) + domains.add(env['BOX_HOSTNAME']) return domains def get_dns_zones(env): @@ -144,10 +144,10 @@ def build_zones(env): auto_domains = web_domains - set(get_web_domains(env, include_auto=False)) domains |= auto_domains # www redirects not included in the initial list, see above - # Add ns1/ns2+PRIMARY_HOSTNAME which must also have A/AAAA records + # Add ns1/ns2+BOX_HOSTNAME which must also have A/AAAA records # when the box is acting as authoritative DNS server for its domains. for ns in ("ns1", "ns2"): - d = ns + "." + env["PRIMARY_HOSTNAME"] + d = ns + "." + env["BOX_HOSTNAME"] domains.add(d) auto_domains.add(d) @@ -161,9 +161,9 @@ def build_zones(env): for domain in domains } - # For MTA-STS, we'll need to check if the PRIMARY_HOSTNAME certificate is + # For MTA-STS, we'll need to check if the BOX_HOSTNAME certificate is # singned and valid. Check that now rather than repeatedly for each domain. - domains[env["PRIMARY_HOSTNAME"]]["certificate-is-valid"] = is_domain_cert_signed_and_valid(env["PRIMARY_HOSTNAME"], env) + domains[env["BOX_HOSTNAME"]]["certificate-is-valid"] = is_domain_cert_signed_and_valid(env["BOX_HOSTNAME"], env) # Load custom records to add to zones. additional_records = list(get_custom_dns_config(env)) @@ -186,19 +186,19 @@ def build_zone(domain, domain_properties, additional_records, env, is_zone=True) # 'False' in the tuple indicates these records would not be used if the zone # is managed outside of the box. if is_zone: - # Obligatory NS record to ns1.PRIMARY_HOSTNAME. - records.append((None, "NS", "ns1.%s." % env["PRIMARY_HOSTNAME"], False)) + # Obligatory NS record to ns1.BOX_HOSTNAME. + records.append((None, "NS", "ns1.%s." % env["BOX_HOSTNAME"], False)) - # NS record to ns2.PRIMARY_HOSTNAME or whatever the user overrides. + # NS record to ns2.BOX_HOSTNAME or whatever the user overrides. # User may provide one or more additional nameservers secondary_ns_list = get_secondary_dns(additional_records, mode="NS") \ - or ["ns2." + env["PRIMARY_HOSTNAME"]] + or ["ns2." + env["BOX_HOSTNAME"]] records.extend((None, "NS", secondary_ns+'.', False) for secondary_ns in secondary_ns_list) - # In PRIMARY_HOSTNAME... - if domain == env["PRIMARY_HOSTNAME"]: - # Set the A/AAAA records. Do this early for the PRIMARY_HOSTNAME so that the user cannot override them + # In BOX_HOSTNAME... + if domain == env["BOX_HOSTNAME"]: + # Set the A/AAAA records. Do this early for the BOX_HOSTNAME so that the user cannot override them # and we can provide different explanatory text. records.append((None, "A", env["PUBLIC_IP"], "Required. Sets the IP address of the box.")) if env.get("PUBLIC_IPV6"): records.append((None, "AAAA", env["PUBLIC_IPV6"], "Required. Sets the IPv6 address of the box.")) @@ -281,7 +281,7 @@ def build_zone(domain, domain_properties, additional_records, env, is_zone=True) if domain_properties[domain]["mail"]: # The MX record says where email for the domain should be delivered: Here! if not has_rec(None, "MX", prefix="10 "): - records.append((None, "MX", "10 %s." % env["PRIMARY_HOSTNAME"], "Required. Specifies the hostname (and priority) of the machine that handles @%s mail." % domain)) + records.append((None, "MX", "10 %s." % env["BOX_HOSTNAME"], "Required. Specifies the hostname (and priority) 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. @@ -304,14 +304,14 @@ def build_zone(domain, domain_properties, additional_records, env, is_zone=True) records.append(("_dmarc", "TXT", 'v=DMARC1; p=quarantine;', "Recommended. Specifies that mail that does not originate from the box but claims to be from @%s or which does not have a valid DKIM signature is suspect and should be quarantined by the recipient's mail system." % domain)) if domain_properties[domain]["user"]: - # Add CardDAV/CalDAV SRV records on the non-primary hostname that points to the primary hostname + # Add CardDAV/CalDAV SRV records on the non-box hostname that points to the box hostname # for autoconfiguration of mail clients (so only domains hosting user accounts need it). # The SRV record format is priority (0, whatever), weight (0, whatever), port, service provider hostname (w/ trailing dot). - if domain != env["PRIMARY_HOSTNAME"]: + if domain != env["BOX_HOSTNAME"]: for dav in ("card", "cal"): qname = "_" + dav + "davs._tcp" if not has_rec(qname, "SRV"): - records.append((qname, "SRV", "0 0 443 " + env["PRIMARY_HOSTNAME"] + ".", "Recommended. Specifies the hostname of the server that handles CardDAV/CalDAV services for email addresses on this domain.")) + records.append((qname, "SRV", "0 0 443 " + env["BOX_HOSTNAME"] + ".", "Recommended. Specifies the hostname of the server that handles CardDAV/CalDAV services for email addresses on this domain.")) # If this is a domain name that there are email addresses configured for, i.e. "something@" # this domain name, then the domain name is a MTA-STS (https://tools.ietf.org/html/rfc8461) @@ -324,7 +324,7 @@ def build_zone(domain, domain_properties, additional_records, env, is_zone=True) # # The policy itself is served at the "mta-sts" (no underscore) subdomain over HTTPS. Therefore # the TLS certificate used by Postfix for STARTTLS must be a valid certificate for the MX - # domain name (PRIMARY_HOSTNAME) *and* the TLS certificate used by nginx for HTTPS on the mta-sts + # domain name (BOX_HOSTNAME) *and* the TLS certificate used by nginx for HTTPS on the mta-sts # subdomain must be valid certificate for that domain. Do not set an MTA-STS policy if either # certificate in use is not valid (e.g. because it is self-signed and a valid certificate has not # yet been provisioned). Since we cannot provision a certificate without A/AAAA records, we @@ -332,7 +332,7 @@ def build_zone(domain, domain_properties, additional_records, env, is_zone=True) # being valid certificates. mta_sts_records = [ ] if domain_properties[domain]["mail"] \ - and domain_properties[env["PRIMARY_HOSTNAME"]]["certificate-is-valid"] \ + and domain_properties[env["BOX_HOSTNAME"]]["certificate-is-valid"] \ and is_domain_cert_signed_and_valid("mta-sts." + domain, env): # Compute an up-to-32-character hash of the policy file. We'll take a SHA-1 hash of the policy # file (20 bytes) and encode it as base-64 (28 bytes, using alphanumeric alternate characters @@ -479,7 +479,7 @@ def write_nsd_zone(domain, zonefile, records, env, force): # ldns-signzone, however. It used to say '; default zone domain'. # # The SOA contact address for all of the domains on this system is hostmaster - # @ the PRIMARY_HOSTNAME. Hopefully that's legit. + # @ the BOX_HOSTNAME. Hopefully that's legit. # # For the refresh through TTL fields, a good reference is: # https://www.ripe.net/publications/docs/ripe-203 @@ -492,7 +492,7 @@ def write_nsd_zone(domain, zonefile, records, env, force): $ORIGIN {domain}. $TTL 86400 ; default time to live -@ IN SOA ns1.{primary_domain}. hostmaster.{primary_domain}. ( +@ IN SOA ns1.{box_domain}. hostmaster.{box_domain}. ( __SERIAL__ ; serial number 7200 ; Refresh (secondary nameserver update interval) 3600 ; Retry (when refresh fails, how often to try again, should be lower than the refresh) @@ -502,7 +502,7 @@ $TTL 86400 ; default time to live """ # Replace replacement strings. - zone = zone.format(domain=domain, primary_domain=env["PRIMARY_HOSTNAME"]) + zone = zone.format(domain=domain, box_domain=env["BOX_HOSTNAME"]) # Add records. for subdomain, querytype, value, _explanation in records: diff --git a/management/email_administrator.py b/management/email_administrator.py index e5307e32..4f764ae9 100755 --- a/management/email_administrator.py +++ b/management/email_administrator.py @@ -22,7 +22,7 @@ env = load_environment() subject = sys.argv[1] # Administrator's email address. -admin_addr = "administrator@" + env['PRIMARY_HOSTNAME'] +admin_addr = "administrator@" + env['BOX_HOSTNAME'] # Read in STDIN. content = sys.stdin.read().strip() @@ -37,9 +37,9 @@ msg = MIMEMultipart('alternative') # In Python 3.6: #msg = Message() -msg['From'] = '"{}" <{}>'.format(env['PRIMARY_HOSTNAME'], admin_addr) +msg['From'] = '"{}" <{}>'.format(env['BOX_HOSTNAME'], admin_addr) msg['To'] = admin_addr -msg['Subject'] = "[{}] {}".format(env['PRIMARY_HOSTNAME'], subject) +msg['Subject'] = "[{}] {}".format(env['BOX_HOSTNAME'], subject) content_html = f'
{html.escape(content)}
' diff --git a/management/mailconfig.py b/management/mailconfig.py index e623eace..92f56f76 100755 --- a/management/mailconfig.py +++ b/management/mailconfig.py @@ -517,7 +517,7 @@ def add_auto_aliases(aliases, env): conn.commit() def get_system_administrator(env): - return "administrator@" + env['PRIMARY_HOSTNAME'] + return "administrator@" + env['BOX_HOSTNAME'] def get_required_aliases(env): # These are the aliases that must exist. @@ -527,7 +527,7 @@ def get_required_aliases(env): aliases.add(get_system_administrator(env)) # The hostmaster alias is exposed in the DNS SOA for each zone. - aliases.add("hostmaster@" + env['PRIMARY_HOSTNAME']) + aliases.add("hostmaster@" + env['BOX_HOSTNAME']) # Get a list of domains we serve mail for, except ones for which the only # email on that domain are the required aliases or a catch-all/domain-forwarder. diff --git a/management/mfa.py b/management/mfa.py index 6b56ad86..ec0ffd04 100644 --- a/management/mfa.py +++ b/management/mfa.py @@ -83,7 +83,7 @@ def provision_totp(email, env): # Make a URI that we encode within a QR code. uri = pyotp.TOTP(secret).provisioning_uri( name=email, - issuer_name=env["PRIMARY_HOSTNAME"] + " Mail-in-a-Box Control Panel" + issuer_name=env["BOX_HOSTNAME"] + " Mail-in-a-Box Control Panel" ) # Generate a QR code as a base64-encode PNG image. diff --git a/management/ssl_certificates.py b/management/ssl_certificates.py index 8c1b841e..59cde277 100755 --- a/management/ssl_certificates.py +++ b/management/ssl_certificates.py @@ -28,7 +28,7 @@ def get_ssl_certificates(env): if fn == 'ssl_certificate.pem': # This is always a symbolic link # to the certificate to use for - # PRIMARY_HOSTNAME. Don't let it + # BOX_HOSTNAME. Don't let it # be eligible for use because we # could end up creating a symlink # to itself --- we want to find @@ -73,8 +73,8 @@ def get_ssl_certificates(env): domains = { } for cert in certificates: # What domains is this certificate good for? - cert_domains, primary_domain = get_certificate_domains(cert["cert"]) - cert["primary_domain"] = primary_domain + cert_domains, common_name = get_certificate_domains(cert["cert"]) + cert["common_name"] = common_name # Is there a private key file for this certificate? private_key = private_keys.get(cert["cert"].public_key().public_numbers()) @@ -84,9 +84,9 @@ def get_ssl_certificates(env): # Add this cert to the list of certs usable for the domains. for domain in cert_domains: - # The primary hostname can only use a certificate mapped + # The box hostname can only use a certificate mapped # to the system private key. - if domain == env['PRIMARY_HOSTNAME'] and cert["private_key"]["filename"] != os.path.join(env['STORAGE_ROOT'], 'ssl', 'ssl_private_key.pem'): + if domain == env['BOX_HOSTNAME'] and cert["private_key"]["filename"] != os.path.join(env['STORAGE_ROOT'], 'ssl', 'ssl_private_key.pem'): continue domains.setdefault(domain, []).append(cert) @@ -134,7 +134,7 @@ def get_ssl_certificates(env): ret[domain] = { "private-key": cert["private_key"]["filename"], "certificate": cert["filename"], - "primary-domain": cert["primary_domain"], + "common-name": cert["common_name"], "certificate_object": cert["cert"], } @@ -148,12 +148,12 @@ def get_domain_ssl_files(domain, ssl_certificates, env, allow_missing_cert=False system_certificate = { "private-key": ssl_private_key, "certificate": ssl_certificate, - "primary-domain": env['PRIMARY_HOSTNAME'], + "common-name": env['BOX_HOSTNAME'], "certificate_object": load_pem(load_cert_chain(ssl_certificate)[0]), } - if use_main_cert and domain == env['PRIMARY_HOSTNAME']: - # The primary domain must use the server certificate because + if use_main_cert and domain == env['BOX_HOSTNAME']: + # The box domain must use the server certificate because # it is hard-coded in some service configuration files. return system_certificate @@ -263,7 +263,7 @@ def provision_certificates(env, limit_domains): # we'll create a list of lists of domains where the inner lists have # at most 100 items. By sorting we also get the DNS zone domain as the first # entry in each list (unless we overflow beyond 100) which ends up as the - # primary domain listed in each certificate. + # first domain listed in each certificate. from dns_update import get_dns_zones certs = { } for zone, _zonefile in get_dns_zones(env): @@ -467,21 +467,21 @@ def install_cert_copy_file(fn, env): def post_install_func(env): ret = [] - # Get the certificate to use for PRIMARY_HOSTNAME. + # Get the certificate to use for BOX_HOSTNAME. ssl_certificates = get_ssl_certificates(env) - cert = get_domain_ssl_files(env['PRIMARY_HOSTNAME'], ssl_certificates, env, use_main_cert=False) + cert = get_domain_ssl_files(env['BOX_HOSTNAME'], ssl_certificates, env, use_main_cert=False) if not cert: # Ruh-row, we don't have any certificate usable - # for the primary hostname. - ret.append("there is no valid certificate for " + env['PRIMARY_HOSTNAME']) + # for the box hostname. + ret.append("there is no valid certificate for " + env['BOX_HOSTNAME']) - # Symlink the best cert for PRIMARY_HOSTNAME to the system + # Symlink the best cert for BOX_HOSTNAME to the system # certificate path, which is hard-coded for various purposes, and then # restart postfix and dovecot. system_ssl_certificate = os.path.join(os.path.join(env["STORAGE_ROOT"], 'ssl', 'ssl_certificate.pem')) if cert and os.readlink(system_ssl_certificate) != cert['certificate']: # Update symlink. - ret.append("updating primary certificate") + ret.append("updating box certificate") ssl_certificate = cert['certificate'] os.unlink(system_ssl_certificate) os.symlink(ssl_certificate, system_ssl_certificate) @@ -523,7 +523,7 @@ def check_certificate(domain, ssl_certificate, ssl_private_key, warn_if_expiring # First check that the domain name is one of the names allowed by # the certificate. if domain is not None: - certificate_names, _cert_primary_name = get_certificate_domains(cert) + certificate_names, _cn = get_certificate_domains(cert) # Check that the domain appears among the acceptable names, or a wildcard # form of the domain name (which is a stricter check than the specs but @@ -558,9 +558,9 @@ def check_certificate(domain, ssl_certificate, ssl_private_key, warn_if_expiring if cert.issuer == cert.subject: return ("SELF-SIGNED", None) - # When selecting which certificate to use for non-primary domains, we check if the primary - # certificate or a www-parent-domain certificate is good for the domain. There's no need - # to run extra checks beyond this point. + # When selecting which certificate to use for non-registrable domains, we check if the + # registrable domain certificate or a www-parent-domain certificate is good for the domain. + # There's no need to run extra checks beyond this point. if just_check_domain: return ("OK", None) diff --git a/management/status_checks.py b/management/status_checks.py index 67aaaeb4..dd621df6 100755 --- a/management/status_checks.py +++ b/management/status_checks.py @@ -216,7 +216,7 @@ def check_software_updates(env, output): def check_system_aliases(env, output): # Check that the administrator alias exists since that's where all # admin email is automatically directed. - check_alias_exists("System administrator address", "administrator@" + env['PRIMARY_HOSTNAME'], env, output) + check_alias_exists("System administrator address", "administrator@" + env['BOX_HOSTNAME'], env, output) def check_free_disk_space(rounded_values, env, output): # Check free disk space. @@ -382,8 +382,8 @@ def run_domain_checks_on_domain(domain, rounded_time, env, dns_domains, dns_zone output.add_heading(domain) output.print_error("Domain name is invalid: " + str(e)) - if domain == env["PRIMARY_HOSTNAME"]: - check_primary_hostname_dns(domain, env, output, dns_domains, dns_zonefiles) + if domain == env["BOX_HOSTNAME"]: + check_box_hostname_dns(domain, env, output, dns_domains, dns_zonefiles) if domain in dns_domains: check_dns_zone(domain, env, output, dns_zonefiles) @@ -419,13 +419,13 @@ def run_domain_checks_on_domain(domain, rounded_time, env, dns_domains, dns_zone return (domain, output) -def check_primary_hostname_dns(domain, env, output, dns_domains, dns_zonefiles): +def check_box_hostname_dns(domain, env, output, dns_domains, dns_zonefiles): # If a DS record is set on the zone containing this domain, check DNSSEC now. has_dnssec = False for zone in dns_domains: if (zone == domain or domain.endswith("." + zone)) and query_dns(zone, "DS", nxdomain=None) is not None: has_dnssec = True - check_dnssec(zone, env, output, dns_zonefiles, is_checking_primary=True) + check_dnssec(zone, env, output, dns_zonefiles, is_checking_box_domain=True) ip = query_dns(domain, "A") ns_ips = query_dns("ns1." + domain, "A") + '/' + query_dns("ns2." + domain, "A") @@ -437,35 +437,35 @@ def check_primary_hostname_dns(domain, env, output, dns_domains, dns_zonefiles): # the nameserver, are reporting the right info --- but if the glue is incorrect this # will probably fail. if ns_ips == env['PUBLIC_IP'] + '/' + env['PUBLIC_IP']: - output.print_ok("Nameserver glue records are correct at registrar. [ns1/ns2.{} ↦ {}]".format(env['PRIMARY_HOSTNAME'], env['PUBLIC_IP'])) + output.print_ok("Nameserver glue records are correct at registrar. [ns1/ns2.{} ↦ {}]".format(env['BOX_HOSTNAME'], env['PUBLIC_IP'])) elif ip == env['PUBLIC_IP']: # The NS records are not what we expect, but the domain resolves correctly, so # the user may have set up external DNS. List this discrepancy as a warning. output.print_warning("""Nameserver glue records (ns1.{} and ns2.{}) should be configured at your domain name - registrar as having the IP address of this box ({}). They currently report addresses of {}. If you have set up External DNS, this may be OK.""".format(env['PRIMARY_HOSTNAME'], env['PRIMARY_HOSTNAME'], env['PUBLIC_IP'], ns_ips)) + registrar as having the IP address of this box ({}). They currently report addresses of {}. If you have set up External DNS, this may be OK.""".format(env['BOX_HOSTNAME'], env['BOX_HOSTNAME'], env['PUBLIC_IP'], ns_ips)) else: output.print_error("""Nameserver glue records are incorrect. The ns1.{} and ns2.{} nameservers must be configured at your domain name registrar as having the IP address {}. They currently report addresses of {}. It may take several hours for - public DNS to update after a change.""".format(env['PRIMARY_HOSTNAME'], env['PRIMARY_HOSTNAME'], env['PUBLIC_IP'], ns_ips)) + public DNS to update after a change.""".format(env['BOX_HOSTNAME'], env['BOX_HOSTNAME'], env['PUBLIC_IP'], ns_ips)) - # Check that PRIMARY_HOSTNAME resolves to PUBLIC_IP[V6] in public DNS. + # Check that BOX_HOSTNAME resolves to PUBLIC_IP[V6] in public DNS. ipv6 = query_dns(domain, "AAAA") if env.get("PUBLIC_IPV6") else None if ip == env['PUBLIC_IP'] and not (ipv6 and env['PUBLIC_IPV6'] and ipv6 != normalize_ip(env['PUBLIC_IPV6'])): - output.print_ok("Domain resolves to box's IP address. [{} ↦ {}]".format(env['PRIMARY_HOSTNAME'], my_ips)) + output.print_ok("Domain resolves to box's IP address. [{} ↦ {}]".format(env['BOX_HOSTNAME'], my_ips)) else: output.print_error("""This domain must resolve to this box's IP address ({}) in public DNS but it currently resolves to {}. It may take several hours for public DNS to update after a change. This problem may result from other issues listed above.""".format(my_ips, ip + ((" / " + ipv6) if ipv6 is not None else ""))) - # Check reverse DNS matches the PRIMARY_HOSTNAME. Note that it might not be + # Check reverse DNS matches the BOX_HOSTNAME. Note that it might not be # a DNS zone if it is a subdomain of another domain we have a zone for. existing_rdns_v4 = query_dns(dns.reversename.from_address(env['PUBLIC_IP']), "PTR") existing_rdns_v6 = query_dns(dns.reversename.from_address(env['PUBLIC_IPV6']), "PTR") if env.get("PUBLIC_IPV6") else None if existing_rdns_v4 == domain and existing_rdns_v6 in {None, domain}: - output.print_ok("Reverse DNS is set correctly at ISP. [{} ↦ {}]".format(my_ips, env['PRIMARY_HOSTNAME'])) + output.print_ok("Reverse DNS is set correctly at ISP. [{} ↦ {}]".format(my_ips, env['BOX_HOSTNAME'])) elif existing_rdns_v4 == existing_rdns_v6 or existing_rdns_v6 is None: output.print_error(f"""This box's reverse DNS is currently {existing_rdns_v4}, but it should be {domain}. Your ISP or cloud provider will have instructions on setting up reverse DNS for this box.""" ) @@ -518,10 +518,10 @@ def check_dns_zone(domain, env, output, dns_zonefiles): custom_dns_records = list(get_custom_dns_config(env)) # generator => list so we can reuse it correct_ip = "; ".join(sorted(get_custom_dns_records(custom_dns_records, domain, "A"))) or env['PUBLIC_IP'] custom_secondary_ns = get_secondary_dns(custom_dns_records, mode="NS") - secondary_ns = custom_secondary_ns or ["ns2." + env['PRIMARY_HOSTNAME']] + secondary_ns = custom_secondary_ns or ["ns2." + env['BOX_HOSTNAME']] existing_ns = query_dns(domain, "NS") - correct_ns = "; ".join(sorted(["ns1." + env["PRIMARY_HOSTNAME"], *secondary_ns])) + correct_ns = "; ".join(sorted(["ns1." + env["BOX_HOSTNAME"], *secondary_ns])) ip = query_dns(domain, "A") probably_external_dns = False @@ -595,7 +595,7 @@ def check_dns_zone_suggestions(domain, env, output, dns_zonefiles, domains_with_ check_dnssec(domain, env, output, dns_zonefiles) -def check_dnssec(domain, env, output, dns_zonefiles, is_checking_primary=False): +def check_dnssec(domain, env, output, dns_zonefiles, is_checking_box_domain=False): # See if the domain has a DS record set at the registrar. The DS record must # match one of the keys that we've used to sign the zone. It may use one of # several hashing algorithms. We've pre-generated all possible valid DS @@ -661,7 +661,7 @@ def check_dnssec(domain, env, output, dns_zonefiles, is_checking_primary=False): IMPORTANT: Do not delete existing DNSSEC 'DS' records for this domain until confirmation that the new DNSSEC 'DS' record for this domain is valid.""") else: - if is_checking_primary: + if is_checking_box_domain: output.print_error("""The DNSSEC 'DS' record for %s is incorrect. See further details below.""" % domain) return output.print_error("""This domain's DNSSEC DS record is incorrect. The chain of trust is broken between the public DNS system @@ -702,7 +702,7 @@ def check_dnssec(domain, env, output, dns_zonefiles, is_checking_primary=False): def check_mail_domain(domain, env, output): # Check the MX record. - recommended_mx = "10 " + env['PRIMARY_HOSTNAME'] + recommended_mx = "10 " + env['BOX_HOSTNAME'] mx = query_dns(domain, "MX", nxdomain=None) if mx is None or mx == "[timeout]": @@ -713,26 +713,26 @@ def check_mail_domain(domain, env, output): mxhost = mx.split('; ')[0].split(' ')[1] if mxhost is None: - # A missing MX record is okay on the primary hostname because - # the primary hostname's A record (the MX fallback) is... itself, + # A missing MX record is okay on the box hostname because + # the box hostname's A record (the MX fallback) is... itself, # which is what we want the MX to be. - if domain == env['PRIMARY_HOSTNAME']: + if domain == env['BOX_HOSTNAME']: output.print_ok(f"Domain's email is directed to this domain. [{domain} has no MX record, which is ok]") # And a missing MX record is okay on other domains if the A record - # matches the A record of the PRIMARY_HOSTNAME. Actually this will + # matches the A record of the BOX_HOSTNAME. Actually this will # probably confuse DANE TLSA, but we'll let that slide for now. else: domain_a = query_dns(domain, "A", nxdomain=None) - primary_a = query_dns(env['PRIMARY_HOSTNAME'], "A", nxdomain=None) - if domain_a is not None and domain_a == primary_a: + box_a = query_dns(env['BOX_HOSTNAME'], "A", nxdomain=None) + if domain_a is not None and domain_a == box_a: output.print_ok(f"Domain's email is directed to this domain. [{domain} has no MX record but its A record is OK]") else: output.print_error(f"""This domain's DNS MX record is not set. It should be '{recommended_mx}'. Mail will not be delivered to this box. It may take several hours for public DNS to update after a change. This problem may result from other issues listed here.""") - elif mxhost == env['PRIMARY_HOSTNAME']: + elif mxhost == env['BOX_HOSTNAME']: good_news = f"Domain's email is directed to this domain. [{domain} ↦ {mx}]" if mx != recommended_mx: good_news += f" This configuration is non-standard. The recommended configuration is '{recommended_mx}'." @@ -743,7 +743,7 @@ def check_mail_domain(domain, env, output): sts_resolver = postfix_mta_sts_resolver.resolver.STSResolver(loop=loop) valid, policy = loop.run_until_complete(sts_resolver.resolve(domain)) if valid == postfix_mta_sts_resolver.resolver.STSFetchResult.VALID: - if policy[1].get("mx") == [env['PRIMARY_HOSTNAME']] and policy[1].get("mode") == "enforce": # policy[0] is the policyid + if policy[1].get("mx") == [env['BOX_HOSTNAME']] and policy[1].get("mode") == "enforce": # policy[0] is the policyid output.print_ok("MTA-STS policy is present.") else: output.print_error(f"MTA-STS policy is present but has unexpected settings. [{policy[1]}]") @@ -763,7 +763,7 @@ def check_mail_domain(domain, env, output): # Stop if the domain is listed in the Spamhaus Domain Block List. # The user might have chosen a domain that was previously in use by a spammer # and will not be able to reliably send mail. - + # See https://www.spamhaus.org/news/article/807/using-our-public-mirrors-check-your-return-codes-now. for # information on spamhaus return codes dbl = query_dns(domain+'.dbl.spamhaus.org', "A", nxdomain=None) @@ -786,9 +786,9 @@ def check_mail_domain(domain, env, output): def check_web_domain(domain, rounded_time, ssl_certificates, env, output): # 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 + # for BOX_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']: + if domain != env['BOX_HOSTNAME']: ok_values = [] for (rtype, expected) in (("A", env['PUBLIC_IP']), ("AAAA", env.get('PUBLIC_IPV6'))): if not expected: continue # IPv6 is not configured @@ -805,7 +805,7 @@ def check_web_domain(domain, rounded_time, ssl_certificates, env, output): output.print_ok("Domain resolves to this box's IP address. [{} ↦ {}]".format(domain, '; '.join(ok_values))) - # We need a TLS certificate for PRIMARY_HOSTNAME because that's where the + # We need a TLS certificate for BOX_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, rounded_time, ssl_certificates, env, output) @@ -886,7 +886,7 @@ def check_ssl_cert(domain, rounded_time, ssl_certificates, env, output): elif cert_status == "SELF-SIGNED": # Offer instructions for purchasing a signed certificate. - if domain == env['PRIMARY_HOSTNAME']: + if domain == env['BOX_HOSTNAME']: output.print_error("""The TLS (SSL) certificate for this domain is currently self-signed. You will get a security warning when you check or send email and when visiting this domain in a web browser (for webmail or static site hosting).""") @@ -1140,9 +1140,9 @@ if __name__ == "__main__": with multiprocessing.pool.Pool(processes=10) as pool: run_and_output_changes(env, pool) - elif sys.argv[1] == "--check-primary-hostname": - # See if the primary hostname appears resolvable and has a signed certificate. - domain = env['PRIMARY_HOSTNAME'] + elif sys.argv[1] == "--check-box-hostname": + # See if the box hostname appears resolvable and has a signed certificate. + domain = env['BOX_HOSTNAME'] if query_dns(domain, "A") != env['PUBLIC_IP']: sys.exit(1) ssl_certificates = get_ssl_certificates(env) diff --git a/management/utils.py b/management/utils.py index 1dbbeb7e..bf1fbebd 100644 --- a/management/utils.py +++ b/management/utils.py @@ -73,8 +73,8 @@ def sort_domains(domain_names, env): # Sort the zones. zone_domains = sorted(zones.values(), key = lambda d : ( - # PRIMARY_HOSTNAME or the zone that contains it is always first. - not (d == env['PRIMARY_HOSTNAME'] or env['PRIMARY_HOSTNAME'].endswith("." + d)), + # BOX_HOSTNAME or the zone that contains it is always first. + not (d == env['BOX_HOSTNAME'] or env['BOX_HOSTNAME'].endswith("." + d)), # Then just dumb lexicographically. d, @@ -86,11 +86,11 @@ def sort_domains(domain_names, env): # First by zone. zone_domains.index(zones[d]), - # PRIMARY_HOSTNAME is always first within the zone that contains it. - d != env['PRIMARY_HOSTNAME'], + # BOX_HOSTNAME is always first within the zone that contains it. + d != env['BOX_HOSTNAME'], # Followed by any of its subdomains. - not d.endswith("." + env['PRIMARY_HOSTNAME']), + not d.endswith("." + env['BOX_HOSTNAME']), # Then in right-to-left lexicographic order of the .-separated parts of the name. list(reversed(d.split("."))), diff --git a/management/web_update.py b/management/web_update.py index c31fe8fc..2197641a 100644 --- a/management/web_update.py +++ b/management/web_update.py @@ -39,10 +39,10 @@ def get_web_domains(env, include_www_redirects=True, include_auto=True, exclude_ # IP address than this box. Remove those domains from our list. domains -= get_domains_with_a_records(env) - # Ensure the PRIMARY_HOSTNAME is in the list so we can serve webmail + # Ensure the BOX_HOSTNAME is in the list so we can serve webmail # as well as Z-Push for Exchange ActiveSync. This can't be removed # by a custom A/AAAA record and is never a 'www.' redirect. - domains.add(env['PRIMARY_HOSTNAME']) + domains.add(env['BOX_HOSTNAME']) # Sort the list so the nginx conf gets written in a stable order. return sort_domains(domains, env) @@ -86,18 +86,18 @@ def do_web_update(env): # Load the templates. template0 = read_conf("nginx.conf") template1 = read_conf("nginx-alldomains.conf") - template2 = read_conf("nginx-primaryonly.conf") + template2 = read_conf("nginx-boxonly.conf") template3 = "\trewrite ^(.*) https://$REDIRECT_DOMAIN$1 permanent;\n" - # Add the PRIMARY_HOST configuration first so it becomes nginx's default server. - nginx_conf += make_domain_config(env['PRIMARY_HOSTNAME'], [template0, template1, template2], ssl_certificates, env) + # Add the BOX_HOSTNAME configuration first so it becomes nginx's default server. + nginx_conf += make_domain_config(env['BOX_HOSTNAME'], [template0, template1, template2], ssl_certificates, env) # Add configuration all other web domains. has_root_proxy_or_redirect = get_web_domains_with_root_overrides(env) web_domains_not_redirect = get_web_domains(env, include_www_redirects=False) for domain in get_web_domains(env): - if domain == env['PRIMARY_HOSTNAME']: - # PRIMARY_HOSTNAME is handled above. + if domain == env['BOX_HOSTNAME']: + # BOX_HOSTNAME is handled above. continue if domain in web_domains_not_redirect: # This is a regular domain. @@ -250,7 +250,7 @@ def get_web_domains_info(env): def check_cert(domain): try: tls_cert = get_domain_ssl_files(domain, ssl_certificates, env, allow_missing_cert=True) - except OSError: # PRIMARY_HOSTNAME cert is missing + except OSError: # BOX_HOSTNAME cert is missing tls_cert = None if tls_cert is None: return ("danger", "No certificate installed.") cert_status, cert_status_details = check_certificate(domain, tls_cert["certificate"], tls_cert["private-key"]) diff --git a/setup/firstuser.sh b/setup/firstuser.sh index b6b1b3c8..71ce0c0e 100644 --- a/setup/firstuser.sh +++ b/setup/firstuser.sh @@ -34,8 +34,8 @@ if [ -z "$(management/cli.py user)" ]; then # But in a non-interactive shell, just make something up. # This is normally for testing. else - # Use me@PRIMARY_HOSTNAME - EMAIL_ADDR=me@$PRIMARY_HOSTNAME + # Use me@BOX_HOSTNAME + EMAIL_ADDR=me@$BOX_HOSTNAME EMAIL_PW=12345678 echo echo "Creating a new administrative mail account for $EMAIL_ADDR with password $EMAIL_PW." @@ -54,5 +54,5 @@ if [ -z "$(management/cli.py user)" ]; then hide_output management/cli.py user make-admin "$EMAIL_ADDR" # Create an alias to which we'll direct all automatically-created administrative aliases. - management/cli.py alias add "administrator@$PRIMARY_HOSTNAME" "$EMAIL_ADDR" > /dev/null + management/cli.py alias add "administrator@$BOX_HOSTNAME" "$EMAIL_ADDR" > /dev/null fi diff --git a/setup/mail-dovecot.sh b/setup/mail-dovecot.sh index b146e44a..158e449d 100755 --- a/setup/mail-dovecot.sh +++ b/setup/mail-dovecot.sh @@ -152,7 +152,7 @@ EOF # Setting a `postmaster_address` is required or LMTP won't start. An alias # will be created automatically by our management daemon. tools/editconf.py /etc/dovecot/conf.d/15-lda.conf \ - "postmaster_address=postmaster@$PRIMARY_HOSTNAME" + "postmaster_address=postmaster@$BOX_HOSTNAME" # ### Sieve diff --git a/setup/mail-postfix.sh b/setup/mail-postfix.sh index 7b642a2a..88822afa 100755 --- a/setup/mail-postfix.sh +++ b/setup/mail-postfix.sh @@ -57,7 +57,7 @@ tools/editconf.py /etc/postfix/main.cf \ inet_interfaces=all \ smtp_bind_address="$PRIVATE_IP" \ smtp_bind_address6="$PRIVATE_IPV6" \ - myhostname="$PRIMARY_HOSTNAME"\ + myhostname="$BOX_HOSTNAME"\ smtpd_banner="\$myhostname ESMTP Hi, I'm a Mail-in-a-Box (Ubuntu/Postfix; see https://mailinabox.email/)" \ mydestination=localhost @@ -121,7 +121,7 @@ cp conf/postfix_outgoing_mail_header_filters /etc/postfix/outgoing_mail_header_f # Modify the `outgoing_mail_header_filters` file to use the local machine name and ip # on the first received header line. This may help reduce the spam score of email by # removing the 127.0.0.1 reference. -sed -i "s/PRIMARY_HOSTNAME/$PRIMARY_HOSTNAME/" /etc/postfix/outgoing_mail_header_filters +sed -i "s/BOX_HOSTNAME/$BOX_HOSTNAME/" /etc/postfix/outgoing_mail_header_filters sed -i "s/PUBLIC_IP/$PUBLIC_IP/" /etc/postfix/outgoing_mail_header_filters # Enable TLS on incoming connections. It is not required on port 25, allowing for opportunistic diff --git a/setup/migrate.py b/setup/migrate.py index 94bea923..a0aec342 100755 --- a/setup/migrate.py +++ b/setup/migrate.py @@ -149,36 +149,36 @@ def migration_11(env): def migration_12(env): # Upgrading to Carddav Roundcube plugin to version 3+, it requires the carddav_* - # tables to be dropped. - # Checking that the roundcube database already exists. - if os.path.exists(os.path.join(env["STORAGE_ROOT"], "mail/roundcube/roundcube.sqlite")): - import sqlite3 - conn = sqlite3.connect(os.path.join(env["STORAGE_ROOT"], "mail/roundcube/roundcube.sqlite")) - c = conn.cursor() - # Get a list of all the tables that begin with 'carddav_' - c.execute("SELECT name FROM sqlite_master WHERE type = ? AND name LIKE ?", ('table', 'carddav_%')) - carddav_tables = c.fetchall() - # If there were tables that begin with 'carddav_', drop them - if carddav_tables: - for table in carddav_tables: - try: - table = table[0] - c = conn.cursor() - dropcmd = "DROP TABLE %s" % table - c.execute(dropcmd) - except: - print("Failed to drop table", table) - # Save. - conn.commit() - conn.close() + # tables to be dropped. + # Checking that the roundcube database already exists. + if os.path.exists(os.path.join(env["STORAGE_ROOT"], "mail/roundcube/roundcube.sqlite")): + import sqlite3 + conn = sqlite3.connect(os.path.join(env["STORAGE_ROOT"], "mail/roundcube/roundcube.sqlite")) + c = conn.cursor() + # Get a list of all the tables that begin with 'carddav_' + c.execute("SELECT name FROM sqlite_master WHERE type = ? AND name LIKE ?", ('table', 'carddav_%')) + carddav_tables = c.fetchall() + # If there were tables that begin with 'carddav_', drop them + if carddav_tables: + for table in carddav_tables: + try: + table = table[0] + c = conn.cursor() + dropcmd = "DROP TABLE %s" % table + c.execute(dropcmd) + except: + print("Failed to drop table", table) + # Save. + conn.commit() + conn.close() - # Delete all sessions, requiring users to login again to recreate carddav_* - # databases - conn = sqlite3.connect(os.path.join(env["STORAGE_ROOT"], "mail/roundcube/roundcube.sqlite")) - c = conn.cursor() - c.execute("delete from session;") - conn.commit() - conn.close() + # Delete all sessions, requiring users to login again to recreate carddav_* + # databases + conn = sqlite3.connect(os.path.join(env["STORAGE_ROOT"], "mail/roundcube/roundcube.sqlite")) + c = conn.cursor() + c.execute("delete from session;") + conn.commit() + conn.close() def migration_13(env): # Add the "mfa" table for configuring MFA for login to the control panel. @@ -190,6 +190,13 @@ def migration_14(env): db = os.path.join(env["STORAGE_ROOT"], 'mail/users.sqlite') shell("check_call", ["sqlite3", db, "CREATE TABLE auto_aliases (id INTEGER PRIMARY KEY AUTOINCREMENT, source TEXT NOT NULL UNIQUE, destination TEXT NOT NULL, permitted_senders TEXT);"]) +def migration_15(env): + # Replace PRIMARY_HOSTNAME with BOX_HOSTNAME in mailinabox.conf + shell("check_call", ["sed", "-i", "s/PRIMARY_HOSTNAME/BOX_HOSTNAME/g", "/etc/mailinabox.conf"]) + env["BOX_HOSTNAME"] = env.get("PRIMARY_HOSTNAME", env.get("BOX_HOSTNAME")) + env["PRIMARY_HOSTNAME"] = None + del env["PRIMARY_HOSTNAME"] + ########################################################### def get_current_migration(): diff --git a/setup/munin.sh b/setup/munin.sh index 017862de..15c988e4 100755 --- a/setup/munin.sh +++ b/setup/munin.sh @@ -24,12 +24,12 @@ includedir /etc/munin/munin-conf.d cgiurl_graph /admin/munin/cgi-graph # a simple host tree -[$PRIMARY_HOSTNAME] +[$BOX_HOSTNAME] address 127.0.0.1 # send alerts to the following address contacts admin -contact.admin.command mail -s "Munin notification \${var:host}" administrator@$PRIMARY_HOSTNAME +contact.admin.command mail -s "Munin notification \${var:host}" administrator@$BOX_HOSTNAME contact.admin.always_send warning critical EOF @@ -40,7 +40,7 @@ chown munin /var/log/munin/munin-cgi-graph.log # ensure munin-node knows the name of this machine # and reduce logging level to warning tools/editconf.py /etc/munin/munin-node.conf -s \ - host_name="$PRIMARY_HOSTNAME" \ + host_name="$BOX_HOSTNAME" \ log_level=1 # Update the activated plugins through munin's autoconfiguration. diff --git a/setup/network-checks.sh b/setup/network-checks.sh index 16b9a175..ed694b97 100644 --- a/setup/network-checks.sh +++ b/setup/network-checks.sh @@ -3,15 +3,15 @@ # the rest of the system setup so we may not yet have things installed. apt_get_quiet install bind9-host sed netcat-openbsd -# Stop if the PRIMARY_HOSTNAME is listed in the Spamhaus Domain Block List. +# Stop if the BOX_HOSTNAME is listed in the Spamhaus Domain Block List. # The user might have chosen a name that was previously in use by a spammer # and will not be able to reliably send mail. Do this after any automatic # choices made above. -if host "$PRIMARY_HOSTNAME.dbl.spamhaus.org" > /dev/null; then +if host "$BOX_HOSTNAME.dbl.spamhaus.org" > /dev/null; then echo - echo "The hostname you chose '$PRIMARY_HOSTNAME' is listed in the" + echo "The hostname you chose '$BOX_HOSTNAME' is listed in the" echo "Spamhaus Domain Block List. See http://www.spamhaus.org/dbl/" - echo "and http://www.spamhaus.org/query/domain/$PRIMARY_HOSTNAME." + echo "and http://www.spamhaus.org/query/domain/$BOX_HOSTNAME." echo echo "You will not be able to send mail using this domain name, so" echo "setup cannot continue." diff --git a/setup/nextcloud.sh b/setup/nextcloud.sh index 496ca0c0..9a750283 100755 --- a/setup/nextcloud.sh +++ b/setup/nextcloud.sh @@ -253,7 +253,7 @@ if [ ! -f "$STORAGE_ROOT/owncloud/owncloud.db" ]; then mkdir -p "$STORAGE_ROOT/owncloud" # Create an initial configuration file. - instanceid=oc$(echo "$PRIMARY_HOSTNAME" | sha1sum | fold -w 10 | head -n 1) + instanceid=oc$(echo "$BOX_HOSTNAME" | sha1sum | fold -w 10 | head -n 1) cat > "$STORAGE_ROOT/owncloud/config.php" < /etc/spamassassin/miab_spf_dmarc.cf << EOF # Evaluate DMARC Authentication-Results -header DMARC_PASS Authentication-Results =~ /$escapedprimaryhostname; dmarc=pass/ +header DMARC_PASS Authentication-Results =~ /$escapedboxhostname; dmarc=pass/ describe DMARC_PASS DMARC check passed score DMARC_PASS -0.1 -header DMARC_NONE Authentication-Results =~ /$escapedprimaryhostname; dmarc=none/ +header DMARC_NONE Authentication-Results =~ /$escapedboxhostname; dmarc=none/ describe DMARC_NONE DMARC record not found score DMARC_NONE 0.1 -header DMARC_FAIL_NONE Authentication-Results =~ /$escapedprimaryhostname; dmarc=fail \(p=none/ +header DMARC_FAIL_NONE Authentication-Results =~ /$escapedboxhostname; dmarc=fail \(p=none/ describe DMARC_FAIL_NONE DMARC check failed (p=none) score DMARC_FAIL_NONE 2.0 -header DMARC_FAIL_QUARANTINE Authentication-Results =~ /$escapedprimaryhostname; dmarc=fail \(p=quarantine/ +header DMARC_FAIL_QUARANTINE Authentication-Results =~ /$escapedboxhostname; dmarc=fail \(p=quarantine/ describe DMARC_FAIL_QUARANTINE DMARC check failed (p=quarantine) score DMARC_FAIL_QUARANTINE 5.0 -header DMARC_FAIL_REJECT Authentication-Results =~ /$escapedprimaryhostname; dmarc=fail \(p=reject/ +header DMARC_FAIL_REJECT Authentication-Results =~ /$escapedboxhostname; dmarc=fail \(p=reject/ describe DMARC_FAIL_REJECT DMARC check failed (p=reject) score DMARC_FAIL_REJECT 10.0 # Evaluate SPF Authentication-Results -header SPF_PASS Authentication-Results =~ /$escapedprimaryhostname; spf=pass/ +header SPF_PASS Authentication-Results =~ /$escapedboxhostname; spf=pass/ describe SPF_PASS SPF check passed score SPF_PASS -0.1 -header SPF_NONE Authentication-Results =~ /$escapedprimaryhostname; spf=none/ +header SPF_NONE Authentication-Results =~ /$escapedboxhostname; spf=none/ describe SPF_NONE SPF record not found score SPF_NONE 2.0 -header SPF_FAIL Authentication-Results =~ /$escapedprimaryhostname; spf=fail/ +header SPF_FAIL Authentication-Results =~ /$escapedboxhostname; spf=fail/ describe SPF_FAIL SPF check failed score SPF_FAIL 5.0 EOF diff --git a/setup/ssl.sh b/setup/ssl.sh index 0aa9b136..63334fc4 100755 --- a/setup/ssl.sh +++ b/setup/ssl.sh @@ -13,7 +13,7 @@ # * SMTP (opportunistic TLS for port 25 and submission on ports 465/587) # * HTTPS # -# The certificate is created with its CN set to the PRIMARY_HOSTNAME. It is +# The certificate is created with its CN set to the BOX_HOSTNAME. It is # also used for other domains served over HTTPS until the user installs a # better certificate for those domains. # @@ -74,10 +74,10 @@ if [ ! -f "$STORAGE_ROOT/ssl/ssl_certificate.pem" ]; then CSR=/tmp/ssl_cert_sign_req-$$.csr hide_output \ openssl req -new -key "$STORAGE_ROOT/ssl/ssl_private_key.pem" -out $CSR \ - -sha256 -subj "/CN=$PRIMARY_HOSTNAME" + -sha256 -subj "/CN=$BOX_HOSTNAME" # Generate the self-signed certificate. - CERT=$STORAGE_ROOT/ssl/$PRIMARY_HOSTNAME-selfsigned-$(date --rfc-3339=date | sed s/-//g).pem + CERT=$STORAGE_ROOT/ssl/$BOX_HOSTNAME-selfsigned-$(date --rfc-3339=date | sed s/-//g).pem hide_output \ openssl x509 -req -days 365 \ -in $CSR -signkey "$STORAGE_ROOT/ssl/ssl_private_key.pem" -out "$CERT" diff --git a/setup/start.sh b/setup/start.sh index dbf1c16c..139d837a 100755 --- a/setup/start.sh +++ b/setup/start.sh @@ -51,7 +51,7 @@ source setup/start.sh EOF chmod +x /usr/local/bin/mailinabox -# Ask the user for the PRIMARY_HOSTNAME, PUBLIC_IP, and PUBLIC_IPV6, +# Ask the user for the BOX_HOSTNAME, PUBLIC_IP, and PUBLIC_IPV6, # if values have not already been set in environment variables. When running # non-interactively, be sure to set values for all! Also sets STORAGE_USER and # STORAGE_ROOT. @@ -60,7 +60,7 @@ source setup/questions.sh # Run some network checks to make sure setup on this machine makes sense. # Skip on existing installs since we don't want this to block the ability to # upgrade, and these checks are also in the control panel status checks. -if [ -z "${DEFAULT_PRIMARY_HOSTNAME:-}" ]; then +if [ -z "${DEFAULT_BOX_HOSTNAME:-}" ]; then if [ -z "${SKIP_NETWORK_CHECKS:-}" ]; then source setup/network-checks.sh fi @@ -95,7 +95,7 @@ fi cat > /etc/mailinabox.conf << EOF; STORAGE_USER=$STORAGE_USER STORAGE_ROOT=$STORAGE_ROOT -PRIMARY_HOSTNAME=$PRIMARY_HOSTNAME +BOX_HOSTNAME=$BOX_HOSTNAME PUBLIC_IP=$PUBLIC_IP PUBLIC_IPV6=$PUBLIC_IPV6 PRIVATE_IP=$PRIVATE_IP @@ -160,9 +160,9 @@ echo "Your Mail-in-a-Box is running." echo echo "Please log in to the control panel for further instructions at:" echo -if management/status_checks.py --check-primary-hostname; then +if management/status_checks.py --check-box-hostname; then # Show the nice URL if it appears to be resolving and has a valid certificate. - echo "https://$PRIMARY_HOSTNAME/admin" + echo "https://$BOX_HOSTNAME/admin" echo echo "If you have a DNS problem put the box's IP address in the URL" echo "(https://$PUBLIC_IP/admin) but then check the TLS fingerprint:" diff --git a/setup/system.sh b/setup/system.sh index dd366401..6e1b8d4d 100755 --- a/setup/system.sh +++ b/setup/system.sh @@ -12,8 +12,8 @@ source setup/functions.sh # load our functions # # First set the hostname in the configuration file, then activate the setting -echo "$PRIMARY_HOSTNAME" > /etc/hostname -hostname "$PRIMARY_HOSTNAME" +echo "$BOX_HOSTNAME" > /etc/hostname +hostname "$BOX_HOSTNAME" # ### Fix permissions diff --git a/setup/web.sh b/setup/web.sh index 3aafcd88..af3a943e 100755 --- a/setup/web.sh +++ b/setup/web.sh @@ -104,7 +104,7 @@ fi mkdir -p /var/lib/mailinabox chmod a+rx /var/lib/mailinabox cat conf/ios-profile.xml \ - | sed "s/PRIMARY_HOSTNAME/$PRIMARY_HOSTNAME/" \ + | sed "s/BOX_HOSTNAME/$BOX_HOSTNAME/" \ | sed "s/UUID1/$(cat /proc/sys/kernel/random/uuid)/" \ | sed "s/UUID2/$(cat /proc/sys/kernel/random/uuid)/" \ | sed "s/UUID3/$(cat /proc/sys/kernel/random/uuid)/" \ @@ -118,7 +118,7 @@ chmod a+r /var/lib/mailinabox/mobileconfig.xml # https://wiki.mozilla.org/Thunderbird:Autoconfiguration:ConfigFileFormat # and https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Autoconfiguration/FileFormat/HowTo. cat conf/mozilla-autoconfig.xml \ - | sed "s/PRIMARY_HOSTNAME/$PRIMARY_HOSTNAME/" \ + | sed "s/BOX_HOSTNAME/$BOX_HOSTNAME/" \ > /var/lib/mailinabox/mozilla-autoconfig.xml chmod a+r /var/lib/mailinabox/mozilla-autoconfig.xml @@ -130,10 +130,10 @@ chmod a+r /var/lib/mailinabox/mozilla-autoconfig.xml # "MTA_STS_MODE=testing" which means "Messages will be delivered # as though there was no failure but a report will be sent if # TLS-RPT is configured" if you are not sure you want this yet. Or "none". -PUNY_PRIMARY_HOSTNAME=$(echo "$PRIMARY_HOSTNAME" | idn2) +PUNY_BOX_HOSTNAME=$(echo "$BOX_HOSTNAME" | idn2) cat conf/mta-sts.txt \ | sed "s/MODE/${MTA_STS_MODE}/" \ - | sed "s/PRIMARY_HOSTNAME/$PUNY_PRIMARY_HOSTNAME/" \ + | sed "s/BOX_HOSTNAME/$PUNY_BOX_HOSTNAME/" \ > /var/lib/mailinabox/mta-sts.txt chmod a+r /var/lib/mailinabox/mta-sts.txt diff --git a/setup/webmail.sh b/setup/webmail.sh index a203cb8c..a3515f96 100644 --- a/setup/webmail.sh +++ b/setup/webmail.sh @@ -132,7 +132,7 @@ cat > $RCM_CONFIG < ~256 bits for AES-256, see above \$config['plugins'] = array('html5_notifier', 'archive', 'zipdownload', 'password', 'managesieve', 'jqueryui', 'persistent_login', 'carddav'); @@ -158,7 +158,7 @@ cat > ${RCM_PLUGIN_DIR}/carddav/config.inc.php < 'ownCloud', 'username' => '%u', // login username 'password' => '%p', // login password - 'url' => 'https://${PRIMARY_HOSTNAME}/cloud/remote.php/dav/addressbooks/users/%u/contacts/', + 'url' => 'https://${BOX_HOSTNAME}/cloud/remote.php/dav/addressbooks/users/%u/contacts/', 'active' => true, 'readonly' => false, 'refresh_time' => '02:00:00', diff --git a/setup/zpush.sh b/setup/zpush.sh index 397ee4a9..ebe1add8 100755 --- a/setup/zpush.sh +++ b/setup/zpush.sh @@ -41,7 +41,7 @@ if [ $needs_update == 1 ]; then mv /tmp/z-push/*/src /usr/local/lib/z-push rm -rf /tmp/z-push.zip /tmp/z-push - # Create admin and top scripts with PHP_VER + # Create admin and top scripts with PHP_VER rm -f /usr/sbin/z-push-{admin,top} echo '#!/bin/bash' > /usr/sbin/z-push-admin echo php"$PHP_VER" /usr/local/lib/z-push/z-push-admin.php '"$@"' >> /usr/sbin/z-push-admin @@ -49,7 +49,7 @@ if [ $needs_update == 1 ]; then echo '#!/bin/bash' > /usr/sbin/z-push-top echo php"$PHP_VER" /usr/local/lib/z-push/z-push-top.php '"$@"' >> /usr/sbin/z-push-top chmod 755 /usr/sbin/z-push-top - + echo $VERSION > /usr/local/lib/z-push/version fi @@ -79,7 +79,7 @@ cp conf/zpush/backend_caldav.php /usr/local/lib/z-push/backend/caldav/config.php # Configure Autodiscover rm -f /usr/local/lib/z-push/autodiscover/config.php cp conf/zpush/autodiscover_config.php /usr/local/lib/z-push/autodiscover/config.php -sed -i "s/PRIMARY_HOSTNAME/$PRIMARY_HOSTNAME/" /usr/local/lib/z-push/autodiscover/config.php +sed -i "s/BOX_HOSTNAME/$BOX_HOSTNAME/" /usr/local/lib/z-push/autodiscover/config.php sed -i "s^define('TIMEZONE', .*^define('TIMEZONE', '$(cat /etc/timezone)');^" /usr/local/lib/z-push/autodiscover/config.php # Some directories it will use. diff --git a/tests/test_dns.py b/tests/test_dns.py index 29b681cc..f1ade5ba 100755 --- a/tests/test_dns.py +++ b/tests/test_dns.py @@ -11,22 +11,22 @@ import sys, re import dns.reversename, dns.resolver if len(sys.argv) < 3: - print("Usage: tests/dns.py ipaddress hostname [primary hostname]") + print("Usage: tests/dns.py ipaddress hostname [box hostname]") sys.exit(1) ipaddr, hostname = sys.argv[1:3] -primary_hostname = hostname +box_hostname = hostname if len(sys.argv) == 4: - primary_hostname = sys.argv[3] + box_hostname = sys.argv[3] def test(server, description): tests = [ (hostname, "A", ipaddr), - #(hostname, "NS", "ns1.%s.;ns2.%s." % (primary_hostname, primary_hostname)), - ("ns1." + primary_hostname, "A", ipaddr), - ("ns2." + primary_hostname, "A", ipaddr), + #(hostname, "NS", "ns1.%s.;ns2.%s." % (box_hostname, box_hostname)), + ("ns1." + box_hostname, "A", ipaddr), + ("ns2." + box_hostname, "A", ipaddr), ("www." + hostname, "A", ipaddr), - (hostname, "MX", "10 " + primary_hostname + "."), + (hostname, "MX", "10 " + box_hostname + "."), (hostname, "TXT", '"v=spf1 mx -all"'), ("mail._domainkey." + hostname, "TXT", '"v=DKIM1; k=rsa; s=email; " "p=__KEY__"'), #("_adsp._domainkey." + hostname, "TXT", "\"dkim=all\""), diff --git a/tools/readable_bash.py b/tools/readable_bash.py index 66f6196c..8f28a9f4 100644 --- a/tools/readable_bash.py +++ b/tools/readable_bash.py @@ -462,7 +462,7 @@ class BashScript(Grammar): v = v.replace("\n
", "")
 		v = re.sub("
([\w\W]*?)
", lambda m : "
" + strip_indent(m.group(1)) + "
", v) - v = re.sub(r"(\$?)PRIMARY_HOSTNAME", r"box.yourdomain.com", v) + v = re.sub(r"(\$?)BOX_HOSTNAME", r"box.yourdomain.com", v) v = re.sub(r"\$STORAGE_ROOT", r"$STORE", v) v = v.replace("`pwd`", "/path/to/mailinabox")