Skip to content

Commit a1da4da

Browse files
committed
Implement the DANE-only lookup policyd
Snawoot/postfix-mta-sts-resolver#67 for context
1 parent d607ba0 commit a1da4da

File tree

4 files changed

+28
-2
lines changed

4 files changed

+28
-2
lines changed

core/admin/mailu/internal/views/postfix.py

+3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
import re
88
import srslib
99

10+
@internal.route("/postfix/dane/<domain_name>")
11+
def postfix_dane_map(domain_name):
12+
return flask.jsonify('dane-only') if utils.has_dane_record(domain_name) else flask.abort(404)
1013

1114
@internal.route("/postfix/domain/<domain_name>")
1215
def postfix_mailbox_domain(domain_name):

core/admin/mailu/utils.py

+23-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
except ImportError:
77
import pickle
88

9+
import dns
10+
import dns.resolver
11+
912
import hmac
1013
import secrets
1114
import time
@@ -25,7 +28,6 @@
2528
from werkzeug.datastructures import CallbackDict
2629
from werkzeug.contrib import fixers
2730

28-
2931
# Login configuration
3032
login = flask_login.LoginManager()
3133
login.login_view = "ui.login"
@@ -37,6 +39,26 @@ def handle_needs_login():
3739
flask.url_for('ui.login', next=flask.request.endpoint)
3840
)
3941

42+
# DNS stub configured to do DNSSEC enabled queries
43+
resolver = dns.resolver.Resolver()
44+
resolver.use_edns(0, 0, 1500)
45+
resolver.flags = dns.flags.AD | dns.flags.RD
46+
47+
def has_dane_record(domain, timeout=5):
48+
try:
49+
result = resolver.query(f'_25._tcp.{domain}', dns.rdatatype.TLSA,dns.rdataclass.IN, lifetime=timeout)
50+
if (result.response.flags & dns.flags.AD) == dns.flags.AD:
51+
for record in result:
52+
if isinstance(record, dns.rdtypes.ANY.TLSA.TLSA):
53+
record.validate()
54+
if record.usage in [2,3]: # postfix wants DANE-only
55+
return True
56+
except dns.resolver.NoNameservers:
57+
# this could be an attack / a failed DNSSEC lookup
58+
return True
59+
except:
60+
pass
61+
4062
# Rate limiter
4163
limiter = limiter.LimitWraperFactory()
4264

core/postfix/conf/main.cf

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ smtp_tls_mandatory_protocols = !SSLv2, !SSLv3
6060
smtp_tls_protocols =!SSLv2,!SSLv3
6161
smtp_tls_security_level = {{ OUTBOUND_TLS_LEVEL|default('dane') }}
6262
smtp_tls_dane_insecure_mx_policy = {% if DEFER_ON_TLS_ERROR == 'false' %}may{% else %}dane{% endif %}
63-
smtp_tls_policy_maps=hash:/etc/postfix/tls_policy.map, socketmap:unix:/tmp/mta-sts.socket:postfix
63+
smtp_tls_policy_maps=hash:/etc/postfix/tls_policy.map, ${podop}dane, socketmap:unix:/tmp/mta-sts.socket:postfix
6464
smtp_tls_CApath = /etc/ssl/certs
6565
smtp_tls_session_cache_database = lmdb:/dev/shm/postfix/smtp_scache
6666
smtpd_tls_session_cache_database = lmdb:/dev/shm/postfix/smtpd_scache

core/postfix/start.py

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ def start_podop():
2121
run_server(0, "postfix", "/tmp/podop.socket", [
2222
("transport", "url", url + "transport/§"),
2323
("alias", "url", url + "alias/§"),
24+
("dane", "url", url + "dane/§"),
2425
("domain", "url", url + "domain/§"),
2526
("mailbox", "url", url + "mailbox/§"),
2627
("recipientmap", "url", url + "recipient/map/§"),

0 commit comments

Comments
 (0)