diff --git a/cmdeploy/src/cmdeploy/chatmail.zone.f b/cmdeploy/src/cmdeploy/chatmail.zone.f deleted file mode 100644 index 6219d466..00000000 --- a/cmdeploy/src/cmdeploy/chatmail.zone.f +++ /dev/null @@ -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" diff --git a/cmdeploy/src/cmdeploy/chatmail.zone.j2 b/cmdeploy/src/cmdeploy/chatmail.zone.j2 new file mode 100644 index 00000000..edf0c28e --- /dev/null +++ b/cmdeploy/src/cmdeploy/chatmail.zone.j2 @@ -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" diff --git a/cmdeploy/src/cmdeploy/dns.py b/cmdeploy/src/cmdeploy/dns.py index ba6303ad..96b72130 100644 --- a/cmdeploy/src/cmdeploy/dns.py +++ b/cmdeploy/src/cmdeploy/dns.py @@ -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() diff --git a/cmdeploy/src/cmdeploy/remote_funcs.py b/cmdeploy/src/cmdeploy/remote_funcs.py index bf537e44..9628d99e 100644 --- a/cmdeploy/src/cmdeploy/remote_funcs.py +++ b/cmdeploy/src/cmdeploy/remote_funcs.py @@ -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()