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:
parent
699bccad80
commit
95e61bc110
|
@ -2,7 +2,7 @@
|
||||||
# and mail aliases and restarts nsd.
|
# 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
|
import rtyaml
|
||||||
|
|
||||||
from mailconfig import get_mail_domains
|
from mailconfig import get_mail_domains
|
||||||
|
@ -126,11 +126,15 @@ def build_zone(domain, zonefile, env, with_ns=True):
|
||||||
child_qname += "." + ph
|
child_qname += "." + ph
|
||||||
records.append((child_qname, child_rtype, child_value))
|
records.append((child_qname, child_rtype, child_value))
|
||||||
|
|
||||||
# In PUBLIC_HOSTNAME, also define ns1 and ns2.
|
# In PUBLIC_HOSTNAME...
|
||||||
if domain == env["PUBLIC_HOSTNAME"]:
|
if domain == env["PUBLIC_HOSTNAME"]:
|
||||||
|
# Define ns1 and ns2.
|
||||||
records.append(("ns1", "A", env["PUBLIC_IP"]))
|
records.append(("ns1", "A", env["PUBLIC_IP"]))
|
||||||
records.append(("ns2", "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):
|
def has_rec(qname, rtype):
|
||||||
for rec in records:
|
for rec in records:
|
||||||
if rec[0] == qname and rec[1] == rtype:
|
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):
|
def write_nsd_zone(domain, zonefile, records, env):
|
||||||
# We set the administrative email address for every domain to domain_contact@[domain.com].
|
# We set the administrative email address for every domain to domain_contact@[domain.com].
|
||||||
# You should probably create an alias to your email address.
|
# You should probably create an alias to your email address.
|
||||||
|
|
|
@ -81,12 +81,12 @@ def is_pid_valid(pid):
|
||||||
else:
|
else:
|
||||||
return True
|
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.
|
# A safe way to execute processes.
|
||||||
# Some processes like apt-get require being given a sane PATH.
|
# Some processes like apt-get require being given a sane PATH.
|
||||||
import subprocess
|
import subprocess
|
||||||
env.update({ "PATH": "/sbin:/bin:/usr/sbin:/usr/bin" })
|
env.update({ "PATH": "/sbin:/bin:/usr/sbin:/usr/bin" })
|
||||||
stderr = None if not capture_stderr else subprocess.STDOUT
|
stderr = None if not capture_stderr else subprocess.STDOUT
|
||||||
ret = getattr(subprocess, method)(cmd_args, env=env, stderr=stderr)
|
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
|
return ret
|
||||||
|
|
Loading…
Reference in New Issue