From 299a2315c1782ab2169e3fffc971323e5c88ecbe Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Thu, 25 Jun 2015 13:02:40 +0000 Subject: [PATCH] dkim 2048 bits - migration and zone file generation changes * Add a migration to delete any existing DKIM key so that existing machines get a fresh 2048-bit key. (Sadly we don't support key rotation so the change is immediate.) * Because the DNS record for a 2048-bit key is so much longer, the way we read OpenDKIM's DNS record text file had to be modified to combine an arbitrary number of TXT record quoted ("...") strings. * When writing out the TXT record value, the string must be split into quoted ("...") strings with a maximum length of 255 bytes each, per the DNS spec. * Added a changelog entry. --- CHANGELOG.md | 4 ++++ management/dns_update.py | 17 ++++++++++++----- setup/dkim.sh | 15 ++++++++++----- setup/migrate.py | 5 +++++ 4 files changed, 31 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3305d0a7..5d5bd7f0 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 f6d71bb7..5a3ca1dc 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 956f425c..1c42e076 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 00fe42e8..fc1877e4 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