add DANE TLSA records to the PUBLIC_HOSTNAME's DNS

Postfix has a tls_security_level called "dane" which uses DNS-Based Authentication of Named Entities (DANE)
to require, if specified in the DNS of the MX host, an encrpyted connection with a known certificate.

This commit adds TLSA records.
This commit is contained in:
Joshua Tauberer 2014-06-19 01:39:27 +00:00
parent 699bccad80
commit 95e61bc110
2 changed files with 34 additions and 4 deletions

View File

@ -2,7 +2,7 @@
# and mail aliases and restarts nsd.
########################################################################
import os, os.path, urllib.parse, datetime, re
import os, os.path, urllib.parse, datetime, re, hashlib
import rtyaml
from mailconfig import get_mail_domains
@ -126,11 +126,15 @@ def build_zone(domain, zonefile, env, with_ns=True):
child_qname += "." + ph
records.append((child_qname, child_rtype, child_value))
# In PUBLIC_HOSTNAME, also define ns1 and ns2.
# In PUBLIC_HOSTNAME...
if domain == env["PUBLIC_HOSTNAME"]:
# Define ns1 and ns2.
records.append(("ns1", "A", env["PUBLIC_IP"]))
records.append(("ns2", "A", env["PUBLIC_IP"]))
# Add a TLSA record for SMTP.
records.append(("_25._tcp", "TLSA", build_tlsa_record(env)))
def has_rec(qname, rtype):
for rec in records:
if rec[0] == qname and rec[1] == rtype:
@ -175,6 +179,32 @@ def build_zone(domain, zonefile, env, with_ns=True):
########################################################################
def build_tlsa_record(env):
# A TLSA record in DNS specifies that connections on a port, e.g.
# the SMTP port, must use TLS and the certificate must match a
# particular certificate.
#
# Thanks to http://blog.huque.com/2012/10/dnssec-and-certificates.html
# for explaining all of this!
# Get the hex SHA256 of the DER-encoded server certificate:
certder = shell("check_output", [
"/usr/bin/openssl",
"x509",
"-in", os.path.join(env["STORAGE_ROOT"], "ssl", "ssl_certificate.pem"),
"-outform", "DER"
],
return_bytes=True)
certhash = hashlib.sha256(certder).hexdigest()
# Specify the TLSA parameters:
# 3: This is the certificate that the client should trust. No CA is needed.
# 0: The whole certificate is matched.
# 1: The certificate is SHA256'd here.
return "3 0 1 " + certhash
########################################################################
def write_nsd_zone(domain, zonefile, records, env):
# We set the administrative email address for every domain to domain_contact@[domain.com].
# You should probably create an alias to your email address.

View File

@ -81,12 +81,12 @@ def is_pid_valid(pid):
else:
return True
def shell(method, cmd_args, env={}, capture_stderr=False):
def shell(method, cmd_args, env={}, capture_stderr=False, return_bytes=False):
# A safe way to execute processes.
# Some processes like apt-get require being given a sane PATH.
import subprocess
env.update({ "PATH": "/sbin:/bin:/usr/sbin:/usr/bin" })
stderr = None if not capture_stderr else subprocess.STDOUT
ret = getattr(subprocess, method)(cmd_args, env=env, stderr=stderr)
if isinstance(ret, bytes): ret = ret.decode("utf8")
if not return_bytes and isinstance(ret, bytes): ret = ret.decode("utf8")
return ret