Commit 4ae76aa2 authored by Joshua Tauberer's avatar Joshua Tauberer

dnssec: use RSASHA256 keys for .email domains

parent ba33669a
...@@ -479,8 +479,18 @@ zone: ...@@ -479,8 +479,18 @@ zone:
######################################################################## ########################################################################
def dnssec_choose_algo(domain, env):
if domain.endswith(".email"):
# At least at GoDaddy, this is the only algorithm supported.
return "RSASHA256"
# For any domain we were able to sign before, don't change the algorithm
# on existing users. We'll probably want to migrate to SHA256 later.
return "RSASHA1-NSEC3-SHA1"
def sign_zone(domain, zonefile, env): def sign_zone(domain, zonefile, env):
dnssec_keys = load_env_vars_from_file(os.path.join(env['STORAGE_ROOT'], 'dns/dnssec/keys.conf')) algo = dnssec_choose_algo(domain, env)
dnssec_keys = load_env_vars_from_file(os.path.join(env['STORAGE_ROOT'], 'dns/dnssec/%s.conf' % algo))
# In order to use the same keys for all domains, we have to generate # 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 # a new .key file with a DNSSEC record for the specific domain. We
......
...@@ -210,14 +210,15 @@ def check_dnssec(domain, env, dns_zonefiles, is_checking_primary=False): ...@@ -210,14 +210,15 @@ def check_dnssec(domain, env, dns_zonefiles, is_checking_primary=False):
# Some registrars may want the public key so they can compute the digest. The DS # Some registrars may want the public key so they can compute the digest. The DS
# record that we suggest using is for the KSK (and that's how the DS records were generated). # record that we suggest using is for the KSK (and that's how the DS records were generated).
dnssec_keys = load_env_vars_from_file(os.path.join(env['STORAGE_ROOT'], 'dns/dnssec/keys.conf')) alg_name_map = { '7': 'RSASHA1-NSEC3-SHA1', '8': 'RSASHA256' }
dnssec_keys = load_env_vars_from_file(os.path.join(env['STORAGE_ROOT'], 'dns/dnssec/%s.conf' % alg_name_map[ds_alg]))
dnsssec_pubkey = open(os.path.join(env['STORAGE_ROOT'], 'dns/dnssec/' + dnssec_keys['KSK'] + '.key')).read().split("\t")[3].split(" ")[3] dnsssec_pubkey = open(os.path.join(env['STORAGE_ROOT'], 'dns/dnssec/' + dnssec_keys['KSK'] + '.key')).read().split("\t")[3].split(" ")[3]
# Query public DNS for the DS record at the registrar. # Query public DNS for the DS record at the registrar.
ds = query_dns(domain, "DS", nxdomain=None) ds = query_dns(domain, "DS", nxdomain=None)
ds_looks_valid = ds and len(ds.split(" ")) == 4 ds_looks_valid = ds and len(ds.split(" ")) == 4
if ds_looks_valid: ds = ds.split(" ") if ds_looks_valid: ds = ds.split(" ")
if ds_looks_valid and ds[0] == ds_keytag and ds[1] == '7' and ds[3] == digests.get(ds[2]): if ds_looks_valid and ds[0] == ds_keytag and ds[1] == ds_alg and ds[3] == digests.get(ds[2]):
if is_checking_primary: return if is_checking_primary: return
env['out'].print_ok("DNSSEC 'DS' record is set correctly at registrar.") env['out'].print_ok("DNSSEC 'DS' record is set correctly at registrar.")
else: else:
...@@ -236,7 +237,9 @@ def check_dnssec(domain, env, dns_zonefiles, is_checking_primary=False): ...@@ -236,7 +237,9 @@ def check_dnssec(domain, env, dns_zonefiles, is_checking_primary=False):
env['out'].print_line("") env['out'].print_line("")
env['out'].print_line("Key Tag: " + ds_keytag + ("" if not ds_looks_valid or ds[0] == ds_keytag else " (Got '%s')" % ds[0])) env['out'].print_line("Key Tag: " + ds_keytag + ("" if not ds_looks_valid or ds[0] == ds_keytag else " (Got '%s')" % ds[0]))
env['out'].print_line("Key Flags: KSK") env['out'].print_line("Key Flags: KSK")
env['out'].print_line("Algorithm: 7 / RSASHA1-NSEC3-SHA1" + ("" if not ds_looks_valid or ds[1] == '7' else " (Got '%s')" % ds[1])) env['out'].print_line(
("Algorithm: %s / %s" % (ds_alg, alg_name_map[ds_alg]))
+ ("" if not ds_looks_valid or ds[1] == ds_alg else " (Got '%s')" % ds[1]))
# see http://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml # see http://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml
env['out'].print_line("Digest Type: 2 / SHA-256") env['out'].print_line("Digest Type: 2 / SHA-256")
# http://www.ietf.org/assignments/ds-rr-types/ds-rr-types.xml # http://www.ietf.org/assignments/ds-rr-types/ds-rr-types.xml
......
...@@ -36,25 +36,39 @@ sudo mkdir -p /var/run/nsd ...@@ -36,25 +36,39 @@ sudo mkdir -p /var/run/nsd
# Create DNSSEC signing keys. # Create DNSSEC signing keys.
mkdir -p "$STORAGE_ROOT/dns/dnssec"; mkdir -p "$STORAGE_ROOT/dns/dnssec";
if [ ! -f "$STORAGE_ROOT/dns/dnssec/keys.conf" ]; then
echo "Generating DNSSEC signing keys. This may take a few minutes..." # TLDs don't all support the same algorithms, so we'll generate keys using a few
# different algorithms.
#
# Supports RSASHA1-NSEC3-SHA1 (didn't test with RSASHA256):
# .info and .me.
#
# Requires RSASHA256
# .email
FIRST=1
for algo in RSASHA1-NSEC3-SHA1 RSASHA256; do
if [ ! -f "$STORAGE_ROOT/dns/dnssec/$algo.conf" ]; then
if [ $FIRST == 1 ]; then
echo "Generating DNSSEC signing keys. This may take a few minutes..."
FIRST=0
fi
# Create the Key-Signing Key (KSK) (-k) which is the so-called # Create the Key-Signing Key (KSK) (-k) which is the so-called
# Secure Entry Point. Use a NSEC3-compatible algorithm (best # Secure Entry Point. Use a NSEC3-compatible algorithm (best
# practice), and a nice and long keylength. The domain name we # practice), and a nice and long keylength. The domain name we
# provide ("_domain_") doesn't matter -- we'll use the same # provide ("_domain_") doesn't matter -- we'll use the same
# keys for all our domains. # keys for all our domains.
KSK=$(umask 077; cd $STORAGE_ROOT/dns/dnssec; ldns-keygen -a RSASHA1-NSEC3-SHA1 -b 2048 -k _domain_); KSK=$(umask 077; cd $STORAGE_ROOT/dns/dnssec; ldns-keygen -a $algo -b 2048 -k _domain_);
# Now create a Zone-Signing Key (ZSK) which is expected to be # Now create a Zone-Signing Key (ZSK) which is expected to be
# rotated more often than a KSK, although we have no plans to # rotated more often than a KSK, although we have no plans to
# rotate it (and doing so would be difficult to do without # rotate it (and doing so would be difficult to do without
# disturbing DNS availability.) Omit '-k' and use a shorter key. # disturbing DNS availability.) Omit '-k' and use a shorter key.
ZSK=$(umask 077; cd $STORAGE_ROOT/dns/dnssec; ldns-keygen -a RSASHA1-NSEC3-SHA1 -b 1024 _domain_); ZSK=$(umask 077; cd $STORAGE_ROOT/dns/dnssec; ldns-keygen -a $algo -b 1024 _domain_);
# These generate two sets of files like: # These generate two sets of files like:
# #
# * `K_domain_.+007+08882.ds`: DS record to provide to domain name registrar # * `K_domain_.+007+08882.ds`: DS record normally provided to domain name registrar (but it's actually invalid with "_domain_")
# * `K_domain_.+007+08882.key`: public key (goes into DS record & upstream DNS provider like your registrar) # * `K_domain_.+007+08882.key`: public key (goes into DS record & upstream DNS provider like your registrar)
# * `K_domain_.+007+08882.private`: private key (secret!) # * `K_domain_.+007+08882.private`: private key (secret!)
...@@ -62,11 +76,12 @@ if [ ! -f "$STORAGE_ROOT/dns/dnssec/keys.conf" ]; then ...@@ -62,11 +76,12 @@ if [ ! -f "$STORAGE_ROOT/dns/dnssec/keys.conf" ]; then
# options. So we'll store the names of the files we just generated. # options. So we'll store the names of the files we just generated.
# We might have multiple keys down the road. This will identify # We might have multiple keys down the road. This will identify
# what keys are the current keys. # what keys are the current keys.
cat > $STORAGE_ROOT/dns/dnssec/keys.conf << EOF; cat > $STORAGE_ROOT/dns/dnssec/$algo.conf << EOF;
KSK=$KSK KSK=$KSK
ZSK=$ZSK ZSK=$ZSK
EOF EOF
fi fi
done
# Force the dns_update script to be run every day to re-sign zones for DNSSEC. # Force the dns_update script to be run every day to re-sign zones for DNSSEC.
cat > /etc/cron.daily/mailinabox-dnssec << EOF; cat > /etc/cron.daily/mailinabox-dnssec << EOF;
......
...@@ -60,6 +60,13 @@ def migration_5(env): ...@@ -60,6 +60,13 @@ def migration_5(env):
# The secret key for encrypting backups was world readable. Fix here. # The secret key for encrypting backups was world readable. Fix here.
os.chmod(os.path.join(env["STORAGE_ROOT"], 'backup/secret_key.txt'), 0o600) os.chmod(os.path.join(env["STORAGE_ROOT"], 'backup/secret_key.txt'), 0o600)
def migration_6(env):
# We now will generate multiple DNSSEC keys for different algorithms, since TLDs may
# not support them all. .email only supports RSA/SHA-256. Rename the keys.conf file
# to be algorithm-specific.
basepath = os.path.join(env["STORAGE_ROOT"], 'dns/dnssec')
shutil.move(os.path.join(basepath, 'keys.conf'), os.path.join(basepath, 'RSASHA1-NSEC3-SHA1.conf'))
def get_current_migration(): def get_current_migration():
ver = 0 ver = 0
while True: while True:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment