mirror of
https://github.com/chatmail/relay.git
synced 2026-05-10 16:04:37 +00:00
- better debugging for DNS queries
- don't try to guess IP addresses but insist on A and AAAA records - try to allow ipv4 or ipv6 only zones - move chatmail.zone generation to jinja so we can have conditionals
This commit is contained in:
@@ -1,15 +0,0 @@
|
||||
{chatmail_domain}. A {ipv4}
|
||||
{chatmail_domain}. AAAA {ipv6}
|
||||
{chatmail_domain}. MX 10 {chatmail_domain}.
|
||||
_submission._tcp.{chatmail_domain}. SRV 0 1 587 {chatmail_domain}.
|
||||
_submissions._tcp.{chatmail_domain}. SRV 0 1 465 {chatmail_domain}.
|
||||
_imap._tcp.{chatmail_domain}. SRV 0 1 143 {chatmail_domain}.
|
||||
_imaps._tcp.{chatmail_domain}. SRV 0 1 993 {chatmail_domain}.
|
||||
{chatmail_domain}. CAA 128 issue "letsencrypt.org;accounturi={acme_account_url}"
|
||||
{chatmail_domain}. TXT "v=spf1 a:{chatmail_domain} ~all"
|
||||
_dmarc.{chatmail_domain}. TXT "v=DMARC1;p=reject;adkim=s;aspf=s"
|
||||
_mta-sts.{chatmail_domain}. TXT "v=STSv1; id={sts_id}"
|
||||
mta-sts.{chatmail_domain}. CNAME {chatmail_domain}.
|
||||
www.{chatmail_domain}. CNAME {chatmail_domain}.
|
||||
{dkim_entry}
|
||||
_adsp._domainkey.{chatmail_domain}. TXT "dkim=discardable"
|
||||
21
cmdeploy/src/cmdeploy/chatmail.zone.j2
Normal file
21
cmdeploy/src/cmdeploy/chatmail.zone.j2
Normal file
@@ -0,0 +1,21 @@
|
||||
{% if ipv4 %}
|
||||
{{ chatmail_domain }}. A {{ ' '.join(ipv4) }}
|
||||
{% endif %}
|
||||
{% if ipv6 %}
|
||||
{{ chatmail_domain }}. AAAA {{ ' '.join(ipv6) }}
|
||||
{% endif %}
|
||||
{{ chatmail_domain }}. MX 10 {{ chatmail_domain }}.
|
||||
_submission._tcp.{{ chatmail_domain }}. SRV 0 1 587 {{ chatmail_domain }}.
|
||||
_submissions._tcp.{{ chatmail_domain }}. SRV 0 1 465 {{ chatmail_domain }}.
|
||||
_imap._tcp.{{ chatmail_domain }}. SRV 0 1 143 {{ chatmail_domain }}.
|
||||
_imaps._tcp.{{ chatmail_domain }}. SRV 0 1 993 {{ chatmail_domain }}.
|
||||
{% if acme_account_url %}
|
||||
{{ chatmail_domain }}. CAA 128 issue "letsencrypt.org;accounturi={{ acme_account_url }}"
|
||||
{% endif %}
|
||||
{{ chatmail_domain }}. TXT "v=spf1 a:{{ chatmail_domain }} ~all"
|
||||
_dmarc.{{ chatmail_domain }}. TXT "v=DMARC1;p=reject;adkim=s;aspf=s"
|
||||
_mta-sts.{{ chatmail_domain }}. TXT "v=STSv1; id={{ sts_id }}"
|
||||
mta-sts.{{ chatmail_domain }}. CNAME {{ chatmail_domain }}.
|
||||
www.{{ chatmail_domain }}. CNAME {{ chatmail_domain }}.
|
||||
{{ dkim_entry }}
|
||||
_adsp._domainkey.{{ chatmail_domain }}. TXT "dkim=discardable"
|
||||
@@ -2,13 +2,15 @@ import datetime
|
||||
import importlib
|
||||
import sys
|
||||
|
||||
from jinja2 import Template
|
||||
|
||||
from . import remote_funcs
|
||||
|
||||
|
||||
def show_dns(args, out) -> int:
|
||||
def show_dns(args, out, fullcheck=True) -> int:
|
||||
"""Check existing DNS records, optionally write them to zone file
|
||||
and return (exitcode, remote_data) tuple."""
|
||||
template = importlib.resources.files(__package__).joinpath("chatmail.zone.f")
|
||||
template = importlib.resources.files(__package__).joinpath("chatmail.zone.j2")
|
||||
mail_domain = args.config.mail_domain
|
||||
|
||||
def log_progress(data):
|
||||
@@ -20,19 +22,25 @@ def show_dns(args, out) -> int:
|
||||
|
||||
remote_data = sshexec(remote_funcs.perform_initial_checks, mail_domain=mail_domain)
|
||||
|
||||
assert remote_data["ipv4"] or remote_data["ipv6"]
|
||||
if not remote_data["ipv4"] and not remote_data["ipv6"]:
|
||||
print()
|
||||
print(f"no A and also no AAAA record set for domain: {mail_domain}")
|
||||
raise SystemExit(1)
|
||||
|
||||
with open(template, "r") as f:
|
||||
zonefile = f.read().format(
|
||||
acme_account_url=remote_data["acme_account_url"],
|
||||
dkim_entry=remote_data["dkim_entry"],
|
||||
ipv6=remote_data["ipv6"],
|
||||
ipv4=remote_data["ipv4"],
|
||||
sts_id=datetime.datetime.now().strftime("%Y%m%d%H%M"),
|
||||
chatmail_domain=args.config.mail_domain,
|
||||
)
|
||||
content = template.read_text()
|
||||
zonefile = Template(content).render(
|
||||
acme_account_url=remote_data.get("acme_account_url"),
|
||||
dkim_entry=remote_data.get("dkim_entry"),
|
||||
ipv4=remote_data["ipv4"],
|
||||
ipv6=remote_data["ipv6"],
|
||||
sts_id=datetime.datetime.now().strftime("%Y%m%d%H%M"),
|
||||
chatmail_domain=args.config.mail_domain,
|
||||
)
|
||||
zonefile = "\n".join([x.strip() for x in zonefile.split("\n") if x.strip()])
|
||||
|
||||
to_print = sshexec(remote_funcs.check_zonefile, zonefile=zonefile)
|
||||
to_print = sshexec(
|
||||
remote_funcs.check_zonefile, zonefile=zonefile, fullcheck=fullcheck
|
||||
)
|
||||
if not args.verbose:
|
||||
print()
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ without any installed dependencies.
|
||||
"""
|
||||
|
||||
import re
|
||||
import socket
|
||||
from subprocess import CalledProcessError, check_output
|
||||
|
||||
|
||||
@@ -36,10 +35,9 @@ def perform_initial_checks(mail_domain):
|
||||
if not shell("dig", fail_ok=True):
|
||||
shell("apt-get install -y dnsutils")
|
||||
shell(f"unbound-control flush_zone {mail_domain}", fail_ok=True)
|
||||
|
||||
res["dkim_entry"] = get_dkim_entry(mail_domain, dkim_selector="opendkim")
|
||||
res["ipv4"] = get_ip_address(socket.AF_INET)
|
||||
res["ipv6"] = get_ip_address(socket.AF_INET6)
|
||||
res["ipv4"] = query_dns("A", mail_domain)
|
||||
res["ipv6"] = query_dns("AAAA", mail_domain)
|
||||
return res
|
||||
|
||||
|
||||
@@ -53,29 +51,18 @@ def get_dkim_entry(mail_domain, dkim_selector):
|
||||
return f'{dkim_selector}._domainkey.{mail_domain}. TXT "{dkim_value}"'
|
||||
|
||||
|
||||
def get_ip_address(typ):
|
||||
sock = socket.socket(typ, socket.SOCK_DGRAM)
|
||||
sock.settimeout(0)
|
||||
host_port = "notifications.delta.chat", 443
|
||||
try:
|
||||
sock.connect(host_port)
|
||||
except OSError:
|
||||
print(f"failed to connect to: {host_port}")
|
||||
return None
|
||||
else:
|
||||
print(f"successfully connected to: {host_port}")
|
||||
return sock.getsockname()[0]
|
||||
|
||||
|
||||
def query_dns(typ, domain):
|
||||
res = shell(f"dig -r -q {domain} -t {typ} +short")
|
||||
print(res)
|
||||
return set(filter(None, res.split("\n")))
|
||||
|
||||
|
||||
def check_zonefile(zonefile):
|
||||
def check_zonefile(zonefile, fullcheck):
|
||||
diff = []
|
||||
|
||||
for zf_line in zonefile.splitlines():
|
||||
print("")
|
||||
print(f"dns-checking {zf_line!r}")
|
||||
zf_domain, zf_typ, zf_value = zf_line.split(maxsplit=2)
|
||||
zf_domain = zf_domain.rstrip(".")
|
||||
zf_value = zf_value.strip()
|
||||
|
||||
Reference in New Issue
Block a user