diff --git a/CHANGELOG.md b/CHANGELOG.md index 36656e53..56159450 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,12 +6,37 @@ In Development Mail: -* An MTA-STS policy for incoming mail is now published (in DNS and over HTTPS) when the primary hostname and email address domain both have a signed TLS certificate installed. +* An MTA-STS policy for incoming mail is now published (in DNS and over HTTPS) when the primary hostname and email address domain both have a signed TLS certificate installed, allowing senders to know that an encrypted connection should be enforced. * MTA-STS reporting is enabled with reports sent to administrator@ the primary hostname. +* The per-IP connection limit to the IMAP server has been doubled to allow more devices to connect at once, especially with multiple users behind a NAT. DNS: * autoconfig and autodiscover subdomains and CalDAV/CardDAV SRV records are no longer generated for domains that don't have user accounts since they are unnecessary. +* IPv6 addresses can now be specified for secondary DNS nameservers in the control panel. + +TLS: + +* TLS certificates are now provisioned in groups by parent domain to limit easy domain enumeration and make provisioning more resilient to errors for particular domains. + +Control Panel: + +* User passwords can now have spaces. +* Status checks for automatic subdomains have been moved into the section for the parent domain. +* Typo fixed. + +Web: + +* The default web page served on fresh installations now adds the `noindex` meta tag. +* The HSTS header is revised to also be sent on non-success responses. + +v0.47 (July 29, 2020) +--------------------- + +Security fixes: + +* Roundcube is updated to version 1.4.7 fixing a cross-site scripting (XSS) vulnerability with HTML messages with malicious svg/namespace (CVE-2020-15562) (https://roundcube.net/news/2020/07/05/security-updates-1.4.7-1.3.14-and-1.2.11). +* SSH connections are now rate-limited at the firewall level (in addition to fail2ban). v0.46 (June 11, 2020) --------------------- diff --git a/README.md b/README.md index fe0e5317..4b357605 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ by him: $ curl -s https://keybase.io/joshdata/key.asc | gpg --import gpg: key C10BDD81: public key "Joshua Tauberer " imported - $ git verify-tag v0.46 + $ git verify-tag v0.47 gpg: Signature made ..... using RSA key ID C10BDD81 gpg: Good signature from "Joshua Tauberer " gpg: WARNING: This key is not certified with a trusted signature! @@ -76,7 +76,7 @@ and on his [personal homepage](https://razor.occams.info/). (Of course, if this Checkout the tag corresponding to the most recent release: - $ git checkout v0.46 + $ git checkout v0.47 Begin the installation. diff --git a/conf/www_default.html b/conf/www_default.html index edefc428..68d0366b 100644 --- a/conf/www_default.html +++ b/conf/www_default.html @@ -1,6 +1,7 @@ this is a mail-in-a-box +

this is a mail-in-a-box

diff --git a/management/daemon.py b/management/daemon.py index 572b6b4a..b7bf2a66 100755 --- a/management/daemon.py +++ b/management/daemon.py @@ -437,9 +437,8 @@ def system_status(): self.items[-1]["extra"].append({ "text": message, "monospace": monospace }) output = WebOutput() # Create a temporary pool of processes for the status checks - pool = multiprocessing.pool.Pool(processes=5) - run_checks(False, env, output, pool) - pool.terminate() + with multiprocessing.pool.Pool(processes=5) as pool: + run_checks(False, env, output, pool) return json_response(output.items) @app.route('/system/updates') diff --git a/management/dns_update.py b/management/dns_update.py index 80273a12..19830749 100755 --- a/management/dns_update.py +++ b/management/dns_update.py @@ -340,11 +340,13 @@ def build_zone(domain, all_domains, additional_records, www_redirect_domains, en # 'break' was not encountered above, so both domains are good mta_sts_enabled = True if mta_sts_enabled: - # Compute a 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 (60 bytes) but then just take its first 20 bytes - # which should be sufficient to change whenever the policy file changes. + # 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 + # instead of '+' and '/' which are not allowed in an MTA-STS policy id) but then just take its + # first 20 characters, which is more than sufficient to change whenever the policy file changes + # (and ensures any '=' padding at the end of the base64 encoding is dropped). with open("/var/lib/mailinabox/mta-sts.txt", "rb") as f: - mta_sts_policy_id = base64.b64encode(hashlib.sha1(f.read()).digest()).decode("ascii")[0:20] + mta_sts_policy_id = base64.b64encode(hashlib.sha1(f.read()).digest(), altchars=b"AA").decode("ascii")[0:20] mta_sts_records.extend([ ("_mta-sts", "TXT", "v=STSv1; id=" + mta_sts_policy_id, "Optional. Part of the MTA-STS policy for incoming mail. If set, a MTA-STS policy must also be published.") ]) @@ -965,18 +967,19 @@ def set_secondary_dns(hostnames, env): try: response = resolver.query(item, "A") except (dns.resolver.NoNameservers, dns.resolver.NXDOMAIN, dns.resolver.NoAnswer): - raise ValueError("Could not resolve the IP address of %s." % item) + try: + response = resolver.query(item, "AAAA") + except (dns.resolver.NoNameservers, dns.resolver.NXDOMAIN, dns.resolver.NoAnswer): + raise ValueError("Could not resolve the IP address of %s." % item) else: # Validate IP address. try: if "/" in item[4:]: v = ipaddress.ip_network(item[4:]) # raises a ValueError if there's a problem - if not isinstance(v, ipaddress.IPv4Network): raise ValueError("That's an IPv6 subnet.") else: v = ipaddress.ip_address(item[4:]) # raises a ValueError if there's a problem - if not isinstance(v, ipaddress.IPv4Address): raise ValueError("That's an IPv6 address.") except ValueError: - raise ValueError("'%s' is not an IPv4 address or subnet." % item[4:]) + raise ValueError("'%s' is not an IPv4 or IPv6 address or subnet." % item[4:]) # Set. set_custom_dns_record("_secondary_nameserver", "A", " ".join(hostnames), "set", env) diff --git a/management/mailconfig.py b/management/mailconfig.py index dd597cd6..b061ea7d 100755 --- a/management/mailconfig.py +++ b/management/mailconfig.py @@ -605,8 +605,6 @@ def validate_password(pw): # validate password if pw.strip() == "": raise ValueError("No password provided.") - if re.search(r"[\s]", pw): - raise ValueError("Passwords cannot contain spaces.") if len(pw) < 8: raise ValueError("Passwords must be at least eight characters.") diff --git a/management/status_checks.py b/management/status_checks.py index 101a3537..36da034a 100755 --- a/management/status_checks.py +++ b/management/status_checks.py @@ -1021,13 +1021,14 @@ if __name__ == "__main__": from utils import load_environment env = load_environment() - pool = multiprocessing.pool.Pool(processes=10) if len(sys.argv) == 1: - run_checks(False, env, ConsoleOutput(), pool) + with multiprocessing.pool.Pool(processes=10) as pool: + run_checks(False, env, ConsoleOutput(), pool) elif sys.argv[1] == "--show-changes": - run_and_output_changes(env, pool) + 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. diff --git a/management/templates/aliases.html b/management/templates/aliases.html index e8d0cb1c..848fcf49 100644 --- a/management/templates/aliases.html +++ b/management/templates/aliases.html @@ -288,7 +288,7 @@ function aliases_remove(elem) { }, function(r) { // Responses are multiple lines of pre-formatted text. - show_modal_error("Remove User", $("
").text(r));
+          show_modal_error("Remove Alias", $("
").text(r));
           show_aliases();
         });
     });
diff --git a/management/templates/custom-dns.html b/management/templates/custom-dns.html
index a2d5042d..6984b081 100644
--- a/management/templates/custom-dns.html
+++ b/management/templates/custom-dns.html
@@ -90,7 +90,7 @@
     

Multiple secondary servers can be separated with commas or spaces (i.e., ns2.hostingcompany.com ns3.hostingcompany.com). - To enable zone transfers to additional servers without listing them as secondary nameservers, add an IP address or subnet using xfr:10.20.30.40 or xfr:10.20.30.40/24. + To enable zone transfers to additional servers without listing them as secondary nameservers, add an IP address or subnet using xfr:10.20.30.40 or xfr:10.0.0.0/8.