mirror of
https://github.com/mail-in-a-box/mailinabox.git
synced 2026-03-12 17:07:23 +01:00
internationalized domain names (DNS, web, CSRs, normalize to Unicode in database, prohibit non-ASCII characters in user account names)
* For non-ASCII domain names, we will keep the Unicode encoding in our users/aliases table. This is nice for the user and also simplifies things like sorting domain names (using Unicode lexicographic order is good, using ASCII lexicogrpahic order on IDNA is confusing). * Write nsd config, nsd zone files, nginx config, and SSL CSRs with domains in IDNA-encoded ASCII. * When checking SSL certificates, treat the CN and SANs as IDNA. * Since Chrome has an interesting feature of converting Unicode to IDNA in <input type="email"> form fields, we'll also forcibly convert IDNA to Unicode in the domain part of email addresses before saving email addresses in the users/aliases tables so that the table is normalized to Unicode. * Don't allow non-ASCII characters in user account email addresses. Dovecot gets confused when querying the Sqlite database (which we observed even for non-word ASCII characters too, so it may not be related to the character encoding).
This commit is contained in:
@@ -382,17 +382,26 @@ $TTL 1800 ; default time to live
|
||||
"""
|
||||
|
||||
# Replace replacement strings.
|
||||
zone = zone.format(domain=domain, primary_domain=env["PRIMARY_HOSTNAME"])
|
||||
zone = zone.format(domain=domain.encode("idna").decode("ascii"), primary_domain=env["PRIMARY_HOSTNAME"].encode("idna").decode("ascii"))
|
||||
|
||||
# Add records.
|
||||
for subdomain, querytype, value, explanation in records:
|
||||
if subdomain:
|
||||
zone += subdomain
|
||||
zone += subdomain.encode("idna").decode("ascii")
|
||||
zone += "\tIN\t" + querytype + "\t"
|
||||
if querytype == "TXT":
|
||||
# Quote and escape.
|
||||
value = value.replace('\\', '\\\\') # escape backslashes
|
||||
value = value.replace('"', '\\"') # escape quotes
|
||||
value = '"' + value + '"' # wrap in quotes
|
||||
elif querytype in ("NS", "CNAME"):
|
||||
# These records must be IDNA-encoded.
|
||||
value = value.encode("idna").decode("ascii")
|
||||
elif querytype == "MX":
|
||||
# Also IDNA-encoded, but must parse first.
|
||||
priority, host = value.split(" ", 1)
|
||||
host = host.encode("idna").decode("ascii")
|
||||
value = priority + " " + host
|
||||
zone += value + "\n"
|
||||
|
||||
# DNSSEC requires re-signing a zone periodically. That requires
|
||||
@@ -486,7 +495,7 @@ server:
|
||||
zone:
|
||||
name: %s
|
||||
zonefile: %s
|
||||
""" % (domain, zonefile)
|
||||
""" % (domain.encode("idna").decode("ascii"), zonefile)
|
||||
|
||||
# If a custom secondary nameserver has been set, allow zone transfers
|
||||
# and notifies to that nameserver.
|
||||
@@ -531,6 +540,9 @@ def sign_zone(domain, zonefile, env):
|
||||
algo = dnssec_choose_algo(domain, env)
|
||||
dnssec_keys = load_env_vars_from_file(os.path.join(env['STORAGE_ROOT'], 'dns/dnssec/%s.conf' % algo))
|
||||
|
||||
# From here, use the IDNA encoding of the domain name.
|
||||
domain = domain.encode("idna").decode("ascii")
|
||||
|
||||
# In order to use the same keys for all domains, we have to generate
|
||||
# a new .key file with a DNSSEC record for the specific domain. We
|
||||
# can reuse the same key, but it won't validate without a DNSSEC
|
||||
|
||||
Reference in New Issue
Block a user