mirror of
				https://github.com/mail-in-a-box/mailinabox.git
				synced 2025-10-30 18:50:53 +00:00 
			
		
		
		
	whats_next: offer DNSSEC DS parameters rather than the full record and in validation allow for other digests than the one we suggest using
fixes #120 (hopefully), in which Gandi generates a SHA1 digest but we were only checking against a SHA256 digest Also see http://discourse.mailinabox.email/t/how-to-set-ds-record-for-gandi-net/24/1 in which a user asks about the DS parameters that Gandi asks for.
This commit is contained in:
		
							parent
							
								
									30178ef019
								
							
						
					
					
						commit
						6d4fab1e6a
					
				| @ -93,7 +93,7 @@ def do_dns_update(env, force=False): | |||||||
| 		# Thus we only sign a zone if write_nsd_zone returned True | 		# Thus we only sign a zone if write_nsd_zone returned True | ||||||
| 		# indicating the zone changed, and thus it got a new serial number. | 		# indicating the zone changed, and thus it got a new serial number. | ||||||
| 		# write_nsd_zone is smart enough to check if a zone's signature | 		# write_nsd_zone is smart enough to check if a zone's signature | ||||||
| 		# is nearing experiation and if so it'll bump the serial number | 		# is nearing expiration and if so it'll bump the serial number | ||||||
| 		# and return True so we get a chance to re-sign it. | 		# and return True so we get a chance to re-sign it. | ||||||
| 		sign_zone(domain, zonefile, env) | 		sign_zone(domain, zonefile, env) | ||||||
| 
 | 
 | ||||||
| @ -478,12 +478,17 @@ def sign_zone(domain, zonefile, env): | |||||||
| 	# zone being signed, so we can't use the .ds files generated when we created the keys. | 	# zone being signed, so we can't use the .ds files generated when we created the keys. | ||||||
| 	# The DS record points to the KSK only. Write this next to the zone file so we can | 	# The DS record points to the KSK only. Write this next to the zone file so we can | ||||||
| 	# get it later to give to the user with instructions on what to do with it. | 	# get it later to give to the user with instructions on what to do with it. | ||||||
|  | 	# | ||||||
|  | 	# We want to be able to validate DS records too, but multiple forms may be valid depending | ||||||
|  | 	# on the digest type. So we'll write all (both) valid records. Only one DS record should | ||||||
|  | 	# actually be deployed. Preferebly the first. | ||||||
|  | 	with open("/etc/nsd/zones/" + zonefile + ".ds", "w") as f: | ||||||
|  | 		for digest_type in ('2', '1'): | ||||||
| 			rr_ds = shell('check_output', ["/usr/bin/ldns-key2ds", | 			rr_ds = shell('check_output', ["/usr/bin/ldns-key2ds", | ||||||
| 				"-n", # output to stdout | 				"-n", # output to stdout | ||||||
| 		"-2", # SHA256 | 				"-" + digest_type, # 1=SHA1, 2=SHA256 | ||||||
| 				dnssec_keys["KSK"] + ".key" | 				dnssec_keys["KSK"] + ".key" | ||||||
| 			]) | 			]) | ||||||
| 	with open("/etc/nsd/zones/" + zonefile + ".ds", "w") as f: |  | ||||||
| 			f.write(rr_ds) | 			f.write(rr_ds) | ||||||
| 
 | 
 | ||||||
| 	# Remove our temporary file. | 	# Remove our temporary file. | ||||||
|  | |||||||
| @ -14,7 +14,7 @@ from dns_update import get_dns_zones | |||||||
| from web_update import get_web_domains, get_domain_ssl_files | from web_update import get_web_domains, get_domain_ssl_files | ||||||
| from mailconfig import get_mail_domains, get_mail_aliases | from mailconfig import get_mail_domains, get_mail_aliases | ||||||
| 
 | 
 | ||||||
| from utils import shell, sort_domains | from utils import shell, sort_domains, load_env_vars_from_file | ||||||
| 
 | 
 | ||||||
| def run_checks(env): | def run_checks(env): | ||||||
| 	run_system_checks(env) | 	run_system_checks(env) | ||||||
| @ -125,25 +125,47 @@ def check_dns_zone(domain, env, dns_zonefiles): | |||||||
| 			control panel to set the nameservers to %s.""" | 			control panel to set the nameservers to %s.""" | ||||||
| 				% (existing_ns, correct_ns) ) | 				% (existing_ns, correct_ns) ) | ||||||
| 
 | 
 | ||||||
| 	# See if the domain has a DS record set. | 	# See if the domain has a DS record set at the registrar. The DS record may have | ||||||
|  | 	# several forms. We have to be prepared to check for any valid record. We've | ||||||
|  | 	# pre-generated all of the valid digests --- read them in. | ||||||
|  | 	ds_correct = open('/etc/nsd/zones/' + dns_zonefiles[domain] + '.ds').read().strip().split("\n") | ||||||
|  | 	digests = { } | ||||||
|  | 	for rr_ds in ds_correct: | ||||||
|  | 		ds_keytag, ds_alg, ds_digalg, ds_digest = rr_ds.split("\t")[4].split(" ") | ||||||
|  | 		digests[ds_digalg] = ds_digest | ||||||
|  | 
 | ||||||
|  | 	# 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). | ||||||
|  | 	dnssec_keys = load_env_vars_from_file(os.path.join(env['STORAGE_ROOT'], 'dns/dnssec/keys.conf')) | ||||||
|  | 	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. | ||||||
| 	ds = query_dns(domain, "DS", nxdomain=None) | 	ds = query_dns(domain, "DS", nxdomain=None) | ||||||
| 	ds_correct = open('/etc/nsd/zones/' + dns_zonefiles[domain] + '.ds').read().strip() | 	ds_looks_valid = ds and len(ds.split(" ")) == 4 | ||||||
| 	ds_expected = re.sub(r"\S+\.\s+3600\s+IN\s+DS\s*", "", ds_correct) | 	if ds_looks_valid: ds = ds.split(" ") | ||||||
| 	if ds == ds_expected: | 	if ds_looks_valid and ds[0] == ds_keytag and ds[1] == '7' and ds[3] == digests.get(ds[2]): | ||||||
| 		print_ok("DNS 'DS' record is set correctly at registrar.") | 		print_ok("DNS 'DS' record is set correctly at registrar.") | ||||||
| 	elif ds == None: | 	else: | ||||||
|  | 		if ds == None: | ||||||
| 			print_error("""This domain's DNS DS record is not set. The DS record is optional. The DS record activates DNSSEC. | 			print_error("""This domain's DNS DS record is not set. The DS record is optional. The DS record activates DNSSEC. | ||||||
| 				To set a DS record, you must follow the instructions provided by your domain name registrar and provide to them this information:""") | 				To set a DS record, you must follow the instructions provided by your domain name registrar and provide to them this information:""") | ||||||
| 		print("") |  | ||||||
| 		print("   " + ds_correct) |  | ||||||
| 		print("") |  | ||||||
| 		else: | 		else: | ||||||
| 			print_error("""This domain's DNS DS record is incorrect. The chain of trust is broken between the public DNS system | 			print_error("""This domain's DNS DS record is incorrect. The chain of trust is broken between the public DNS system | ||||||
| 				and this machine's DNS server. It may take several hours for public DNS to update after a change. If you did not recently | 				and this machine's DNS server. It may take several hours for public DNS to update after a change. If you did not recently | ||||||
| 				make a change, you must resolve this immediately by following the instructions provided by your domain name registrar and | 				make a change, you must resolve this immediately by following the instructions provided by your domain name registrar and | ||||||
| 				provide to them this information:""") | 				provide to them this information:""") | ||||||
| 		print("") | 		print() | ||||||
| 		print("   " + ds_correct) | 		print("\tKey Tag: " + ds_keytag + ("" if not ds_looks_valid or ds[0] == ds_keytag else " (Got '%s')" % ds[0])) | ||||||
|  | 		print("\tKey Flags: KSK") | ||||||
|  | 		print("\tAlgorithm: 7 / RSASHA1-NSEC3-SHA1" + ("" if not ds_looks_valid or ds[1] == '7' else " (Got '%s')" % ds[1])) | ||||||
|  | 		print("\tDigest Type: 2 / SHA-256") | ||||||
|  | 		print("\tDigest: " + digests['2']) | ||||||
|  | 		if ds_looks_valid and ds[3] != digests.get(ds[2]): | ||||||
|  | 			print("\t(Got digest type %s and digest %s which do not match.)" % (ds[2], ds[3])) | ||||||
|  | 		print("\tPublic Key: " + dnsssec_pubkey) | ||||||
|  | 		print() | ||||||
|  | 		print("\tBulk/Record Format:") | ||||||
|  | 		print("\t" + ds_correct[0]) | ||||||
| 		print("") | 		print("") | ||||||
| 
 | 
 | ||||||
| def check_mail_domain(domain, env): | def check_mail_domain(domain, env): | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user