diff --git a/CHANGELOG.md b/CHANGELOG.md index 3305d0a..5d5bd7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,12 +4,16 @@ CHANGELOG In Development -------------- +Advisories: +* This update replaces your DKIM signing key with a stronger key. Because of DNS caching/propagation, mail sent within a few hours after this update could be marked as spam by recipients. If you use External DNS, you will need to update your DNS records. + Mail: * Greylisting will now let some reputable senders pass through immediately. * Searching mail (via IMAP) will now be much faster using the dovecot lucene full text search plugin. * Users can no longer spoof arbitrary email addresses in outbound mail. The email address set in mail clients must be either a) the user's actual email address (login username) or b) any alias that the user sending the mail is listed as a direct recipient of. * Fix for deleting admin@ and postmaster@ addresses. * Roundcube is updated to version 1.1.2, plugins updated. +* The DKIM signing key has been increased to 2048 bits, from 1024, replacing the existing key. Web: * 'www' subdomains now automatically redirect to their parent domain (but you'll need to install an SSL certificate). diff --git a/management/dns_update.py b/management/dns_update.py index f6d71bb..5a3ca1d 100755 --- a/management/dns_update.py +++ b/management/dns_update.py @@ -250,8 +250,8 @@ def build_zone(domain, all_domains, additional_records, www_redirect_domains, en # Skip if the user has set a DKIM record already. opendkim_record_file = os.path.join(env['STORAGE_ROOT'], 'mail/dkim/mail.txt') with open(opendkim_record_file) as orf: - m = re.match(r'(\S+)\s+IN\s+TXT\s+\( "([^"]+)"\s+"([^"]+)"\s*\)', orf.read(), re.S) - val = m.group(2) + m.group(3) + m = re.match(r'(\S+)\s+IN\s+TXT\s+\( ((?:"[^"]+"\s+)+)\)', orf.read(), re.S) + val = "".join(re.findall(r'"([^"]+)"', m.group(2))) if not has_rec(m.group(1), "TXT", prefix="v=DKIM1; "): records.append((m.group(1), "TXT", val, "Recommended. Provides a way for recipients to verify that this machine sent @%s mail." % domain)) @@ -373,9 +373,16 @@ $TTL 1800 ; default time to live zone += subdomain zone += "\tIN\t" + querytype + "\t" if querytype == "TXT": - value = value.replace('\\', '\\\\') # escape backslashes - value = value.replace('"', '\\"') # escape quotes - value = '"' + value + '"' # wrap in quotes + # Divide into 255-byte max substrings. + v2 = "" + while len(value) > 0: + s = value[0:255] + value = value[255:] + s = s.replace('\\', '\\\\') # escape backslashes + s = s.replace('"', '\\"') # escape quotes + s = '"' + s + '"' # wrap in quotes + v2 += s + " " + value = v2 zone += value + "\n" # DNSSEC requires re-signing a zone periodically. That requires diff --git a/setup/dkim.sh b/setup/dkim.sh index 956f425..1c42e07 100755 --- a/setup/dkim.sh +++ b/setup/dkim.sh @@ -35,12 +35,17 @@ RequireSafeKeys false EOF fi -# Create a new DKIM key. This creates -# mail.private and mail.txt in $STORAGE_ROOT/mail/dkim. The former -# is the actual private key and the latter is the suggested DNS TXT -# entry which we'll want to include in our DNS setup. +# Create a new DKIM key. This creates mail.private and mail.txt +# in $STORAGE_ROOT/mail/dkim. The former is the private key and +# the latter is the suggested DNS TXT entry which we'll include +# in our DNS setup. Note tha the files are named after the +# 'selector' of the key, which we can change later on to support +# key rotation. +# +# A 1024-bit key is seen as a minimum standard by several providers +# such as Google. But they and others use a 2048 bit key, so we'll +# do the same. Keys beyond 2048 bits may exceed DNS record limits. if [ ! -f "$STORAGE_ROOT/mail/dkim/mail.private" ]; then - # Should we specify -h rsa-sha256? opendkim-genkey -b 2048 -r -s mail -D $STORAGE_ROOT/mail/dkim fi diff --git a/setup/migrate.py b/setup/migrate.py index 00fe42e..fc1877e 100755 --- a/setup/migrate.py +++ b/setup/migrate.py @@ -95,6 +95,11 @@ def migration_7(env): # Save. conn.commit() +def migration_8(env): + # Delete DKIM keys. We had generated 1024-bit DKIM keys. + # By deleting the key file we'll automatically generate + # a new key, which will be 2048 bits. + os.unlink(os.path.join(env['STORAGE_ROOT'], 'mail/dkim/mail.private')) def get_current_migration(): ver = 0