mirror of
https://github.com/chatmail/relay.git
synced 2026-05-20 12:58:04 +00:00
Compare commits
31 Commits
j4n/hpk-lx
...
hpk/lxcdep
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
260ace5ab0 | ||
|
|
4b1dbc3d43 | ||
|
|
e6db359a34 | ||
|
|
a61bbcade2 | ||
|
|
de1a53b135 | ||
|
|
22a2b6a1c4 | ||
|
|
ef7e06d9f9 | ||
|
|
7450143c86 | ||
|
|
8a49489d54 | ||
|
|
dcb9fbe73f | ||
|
|
da5c248562 | ||
|
|
550498e936 | ||
|
|
c4b018b7f2 | ||
|
|
7570c57d7e | ||
|
|
68d1fa8f01 | ||
|
|
785efabe4c | ||
|
|
3ba2703d04 | ||
|
|
6c95eeea80 | ||
|
|
783904f244 | ||
|
|
405dc2d1ee | ||
|
|
62fe113b59 | ||
|
|
a102ed7d61 | ||
|
|
010e9de08e | ||
|
|
75c42dc8c7 | ||
|
|
cd16ef8c4a | ||
|
|
ea7b875eb1 | ||
|
|
1a578ecc66 | ||
|
|
3021e47866 | ||
|
|
b59417128c | ||
|
|
49705863d3 | ||
|
|
861fdf7a50 |
@@ -6,7 +6,10 @@ build-backend = "setuptools.build_meta"
|
|||||||
name = "chatmaild"
|
name = "chatmaild"
|
||||||
version = "0.3"
|
version = "0.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"aiosmtpd",
|
||||||
"iniconfig",
|
"iniconfig",
|
||||||
|
"deltachat-rpc-server",
|
||||||
|
"deltachat-rpc-client",
|
||||||
"filelock",
|
"filelock",
|
||||||
"requests",
|
"requests",
|
||||||
"crypt-r >= 3.13.1 ; python_version >= '3.11'",
|
"crypt-r >= 3.13.1 ; python_version >= '3.11'",
|
||||||
@@ -67,7 +70,6 @@ commands =
|
|||||||
deps = pytest
|
deps = pytest
|
||||||
pdbpp
|
pdbpp
|
||||||
pytest-localserver
|
pytest-localserver
|
||||||
aiosmtpd
|
|
||||||
execnet
|
execnet
|
||||||
commands = pytest -v -rsXx {posargs}
|
commands = pytest -v -rsXx {posargs}
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ dependencies = [
|
|||||||
"pillow",
|
"pillow",
|
||||||
"qrcode",
|
"qrcode",
|
||||||
"markdown",
|
"markdown",
|
||||||
|
"pytest",
|
||||||
"setuptools>=68",
|
"setuptools>=68",
|
||||||
"termcolor",
|
"termcolor",
|
||||||
"build",
|
"build",
|
||||||
@@ -20,7 +21,6 @@ dependencies = [
|
|||||||
"execnet",
|
"execnet",
|
||||||
"imap_tools",
|
"imap_tools",
|
||||||
"deltachat-rpc-client",
|
"deltachat-rpc-client",
|
||||||
"deltachat-rpc-server",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
|
|||||||
@@ -488,6 +488,10 @@ class ChatmailDeployer(Deployer):
|
|||||||
name="Install rsync",
|
name="Install rsync",
|
||||||
packages=["rsync"],
|
packages=["rsync"],
|
||||||
)
|
)
|
||||||
|
apt.packages(
|
||||||
|
name="Ensure cron is installed",
|
||||||
|
packages=["cron"],
|
||||||
|
)
|
||||||
|
|
||||||
def configure(self):
|
def configure(self):
|
||||||
# Ensure the per-domain mailbox directory exists before
|
# Ensure the per-domain mailbox directory exists before
|
||||||
|
|||||||
@@ -47,40 +47,33 @@ def get_filled_zone_file(remote_data):
|
|||||||
remote_data["sts_id"] = datetime.datetime.now().strftime("%Y%m%d%H%M")
|
remote_data["sts_id"] = datetime.datetime.now().strftime("%Y%m%d%H%M")
|
||||||
|
|
||||||
d = remote_data["mail_domain"]
|
d = remote_data["mail_domain"]
|
||||||
|
|
||||||
def rec(name, rtype, rdata, ttl=3600):
|
|
||||||
return f"{name:<40} {ttl:<6} IN {rtype:<5} {rdata}"
|
|
||||||
|
|
||||||
lines = ["; Required DNS entries"]
|
lines = ["; Required DNS entries"]
|
||||||
if remote_data.get("A"):
|
if remote_data.get("A"):
|
||||||
lines.append(rec(f"{d}.", "A", remote_data["A"]))
|
lines.append(f"{d}. 3600 IN A {remote_data['A']}")
|
||||||
if remote_data.get("AAAA"):
|
if remote_data.get("AAAA"):
|
||||||
lines.append(rec(f"{d}.", "AAAA", remote_data["AAAA"]))
|
lines.append(f"{d}. 3600 IN AAAA {remote_data['AAAA']}")
|
||||||
lines.append(rec(f"{d}.", "MX", f"10 {d}."))
|
lines.append(f"{d}. 3600 IN MX 10 {d}.")
|
||||||
if remote_data.get("strict_tls"):
|
if remote_data.get("strict_tls"):
|
||||||
lines.append(
|
lines.append(
|
||||||
rec(f"_mta-sts.{d}.", "TXT", f'"v=STSv1; id={remote_data["sts_id"]}"')
|
f'_mta-sts.{d}. 3600 IN TXT "v=STSv1; id={remote_data["sts_id"]}"'
|
||||||
)
|
)
|
||||||
lines.append(rec(f"mta-sts.{d}.", "CNAME", f"{d}."))
|
lines.append(f"mta-sts.{d}. 3600 IN CNAME {d}.")
|
||||||
lines.append(rec(f"www.{d}.", "CNAME", f"{d}."))
|
lines.append(f"www.{d}. 3600 IN CNAME {d}.")
|
||||||
lines.append(remote_data["dkim_entry"])
|
lines.append(remote_data["dkim_entry"])
|
||||||
lines.append("")
|
lines.append("")
|
||||||
lines.append("; Recommended DNS entries")
|
lines.append("; Recommended DNS entries")
|
||||||
lines.append(rec(f"{d}.", "TXT", '"v=spf1 a ~all"'))
|
lines.append(f'{d}. 3600 IN TXT "v=spf1 a ~all"')
|
||||||
lines.append(rec(f"_dmarc.{d}.", "TXT", '"v=DMARC1;p=reject;adkim=s;aspf=s"'))
|
lines.append(f'_dmarc.{d}. 3600 IN TXT "v=DMARC1;p=reject;adkim=s;aspf=s"')
|
||||||
if remote_data.get("acme_account_url"):
|
if remote_data.get("acme_account_url"):
|
||||||
lines.append(
|
lines.append(
|
||||||
rec(
|
f"{d}. 3600 IN CAA 0 issue"
|
||||||
f"{d}.",
|
f' "letsencrypt.org;accounturi={remote_data["acme_account_url"]}"'
|
||||||
"CAA",
|
|
||||||
f'0 issue "letsencrypt.org;accounturi={remote_data["acme_account_url"]}"',
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
lines.append(rec(f"_adsp._domainkey.{d}.", "TXT", '"dkim=discardable"'))
|
lines.append(f'_adsp._domainkey.{d}. 3600 IN TXT "dkim=discardable"')
|
||||||
lines.append(rec(f"_submission._tcp.{d}.", "SRV", f"0 1 587 {d}."))
|
lines.append(f"_submission._tcp.{d}. 3600 IN SRV 0 1 587 {d}.")
|
||||||
lines.append(rec(f"_submissions._tcp.{d}.", "SRV", f"0 1 465 {d}."))
|
lines.append(f"_submissions._tcp.{d}. 3600 IN SRV 0 1 465 {d}.")
|
||||||
lines.append(rec(f"_imap._tcp.{d}.", "SRV", f"0 1 143 {d}."))
|
lines.append(f"_imap._tcp.{d}. 3600 IN SRV 0 1 143 {d}.")
|
||||||
lines.append(rec(f"_imaps._tcp.{d}.", "SRV", f"0 1 993 {d}."))
|
lines.append(f"_imaps._tcp.{d}. 3600 IN SRV 0 1 993 {d}.")
|
||||||
lines.append("")
|
lines.append("")
|
||||||
return "\n".join(lines)
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import urllib.request
|
|||||||
|
|
||||||
from chatmaild.config import Config
|
from chatmaild.config import Config
|
||||||
from pyinfra import host
|
from pyinfra import host
|
||||||
from pyinfra.facts.deb import DebPackages
|
|
||||||
from pyinfra.facts.server import Arch, Command, Sysctl
|
from pyinfra.facts.server import Arch, Command, Sysctl
|
||||||
|
from pyinfra.facts.systemd import SystemdEnabled
|
||||||
from pyinfra.operations import apt, files, server, systemd
|
from pyinfra.operations import apt, files, server, systemd
|
||||||
|
|
||||||
from cmdeploy.basedeploy import (
|
from cmdeploy.basedeploy import (
|
||||||
@@ -12,19 +12,9 @@ from cmdeploy.basedeploy import (
|
|||||||
blocked_service_startup,
|
blocked_service_startup,
|
||||||
configure_remote_units,
|
configure_remote_units,
|
||||||
get_resource,
|
get_resource,
|
||||||
|
has_systemd,
|
||||||
)
|
)
|
||||||
|
|
||||||
DOVECOT_VERSION = "2.3.21+dfsg1-3"
|
|
||||||
|
|
||||||
DOVECOT_SHA256 = {
|
|
||||||
("core", "amd64"): "dd060706f52a306fa863d874717210b9fe10536c824afe1790eec247ded5b27d",
|
|
||||||
("core", "arm64"): "e7548e8a82929722e973629ecc40fcfa886894cef3db88f23535149e7f730dc9",
|
|
||||||
("imapd", "amd64"): "8d8dc6fc00bbb6cdb25d345844f41ce2f1c53f764b79a838eb2a03103eebfa86",
|
|
||||||
("imapd", "arm64"): "178fa877ddd5df9930e8308b518f4b07df10e759050725f8217a0c1fb3fd707f",
|
|
||||||
("lmtpd", "amd64"): "2f69ba5e35363de50962d42cccbfe4ed8495265044e244007d7ccddad77513ab",
|
|
||||||
("lmtpd", "arm64"): "89f52fb36524f5877a177dff4a713ba771fd3f91f22ed0af7238d495e143b38f",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class DovecotDeployer(Deployer):
|
class DovecotDeployer(Deployer):
|
||||||
daemon_reload = False
|
daemon_reload = False
|
||||||
@@ -36,22 +26,13 @@ class DovecotDeployer(Deployer):
|
|||||||
|
|
||||||
def install(self):
|
def install(self):
|
||||||
arch = host.get_fact(Arch)
|
arch = host.get_fact(Arch)
|
||||||
|
if has_systemd() and "dovecot.service" in host.get_fact(SystemdEnabled):
|
||||||
|
return # already installed and running
|
||||||
|
|
||||||
with blocked_service_startup():
|
with blocked_service_startup():
|
||||||
debs = []
|
_install_dovecot_package("core", arch)
|
||||||
for pkg in ("core", "imapd", "lmtpd"):
|
_install_dovecot_package("imapd", arch)
|
||||||
deb = _download_dovecot_package(pkg, arch)
|
_install_dovecot_package("lmtpd", arch)
|
||||||
if deb:
|
|
||||||
debs.append(deb)
|
|
||||||
if debs:
|
|
||||||
deb_list = " ".join(debs)
|
|
||||||
server.shell(
|
|
||||||
name="Install dovecot packages",
|
|
||||||
commands=[
|
|
||||||
f"dpkg --force-confdef --force-confold -i {deb_list} 2> /dev/null || true",
|
|
||||||
"DEBIAN_FRONTEND=noninteractive apt-get -y --fix-broken install",
|
|
||||||
f"dpkg --force-confdef --force-confold -i {deb_list}",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
def configure(self):
|
def configure(self):
|
||||||
configure_remote_units(self.config.mail_domain, self.units)
|
configure_remote_units(self.config.mail_domain, self.units)
|
||||||
@@ -84,37 +65,40 @@ def _pick_url(primary, fallback):
|
|||||||
return fallback
|
return fallback
|
||||||
|
|
||||||
|
|
||||||
def _download_dovecot_package(package: str, arch: str):
|
def _install_dovecot_package(package: str, arch: str):
|
||||||
"""Download a dovecot .deb if needed, return its path (or None)."""
|
|
||||||
arch = "amd64" if arch == "x86_64" else arch
|
arch = "amd64" if arch == "x86_64" else arch
|
||||||
arch = "arm64" if arch == "aarch64" else arch
|
arch = "arm64" if arch == "aarch64" else arch
|
||||||
|
primary_url = f"https://download.delta.chat/dovecot/dovecot-{package}_2.3.21%2Bdfsg1-3_{arch}.deb"
|
||||||
pkg_name = f"dovecot-{package}"
|
fallback_url = f"https://github.com/chatmail/dovecot/releases/download/upstream%2F2.3.21%2Bdfsg1/dovecot-{package}_2.3.21%2Bdfsg1-3_{arch}.deb"
|
||||||
sha256 = DOVECOT_SHA256.get((package, arch))
|
|
||||||
if sha256 is None:
|
|
||||||
apt.packages(packages=[pkg_name])
|
|
||||||
return None
|
|
||||||
|
|
||||||
installed_versions = host.get_fact(DebPackages).get(pkg_name, [])
|
|
||||||
if DOVECOT_VERSION in installed_versions:
|
|
||||||
return None
|
|
||||||
|
|
||||||
url_version = DOVECOT_VERSION.replace("+", "%2B")
|
|
||||||
deb_base = f"{pkg_name}_{url_version}_{arch}.deb"
|
|
||||||
primary_url = f"https://download.delta.chat/dovecot/{deb_base}"
|
|
||||||
fallback_url = f"https://github.com/chatmail/dovecot/releases/download/upstream%2F{url_version}/{deb_base}"
|
|
||||||
url = _pick_url(primary_url, fallback_url)
|
url = _pick_url(primary_url, fallback_url)
|
||||||
deb_filename = f"/root/{deb_base}"
|
deb_filename = "/root/" + url.split("/")[-1]
|
||||||
|
|
||||||
|
match (package, arch):
|
||||||
|
case ("core", "amd64"):
|
||||||
|
sha256 = "dd060706f52a306fa863d874717210b9fe10536c824afe1790eec247ded5b27d"
|
||||||
|
case ("core", "arm64"):
|
||||||
|
sha256 = "e7548e8a82929722e973629ecc40fcfa886894cef3db88f23535149e7f730dc9"
|
||||||
|
case ("imapd", "amd64"):
|
||||||
|
sha256 = "8d8dc6fc00bbb6cdb25d345844f41ce2f1c53f764b79a838eb2a03103eebfa86"
|
||||||
|
case ("imapd", "arm64"):
|
||||||
|
sha256 = "178fa877ddd5df9930e8308b518f4b07df10e759050725f8217a0c1fb3fd707f"
|
||||||
|
case ("lmtpd", "amd64"):
|
||||||
|
sha256 = "2f69ba5e35363de50962d42cccbfe4ed8495265044e244007d7ccddad77513ab"
|
||||||
|
case ("lmtpd", "arm64"):
|
||||||
|
sha256 = "89f52fb36524f5877a177dff4a713ba771fd3f91f22ed0af7238d495e143b38f"
|
||||||
|
case _:
|
||||||
|
apt.packages(packages=[f"dovecot-{package}"])
|
||||||
|
return
|
||||||
|
|
||||||
files.download(
|
files.download(
|
||||||
name=f"Download {pkg_name}",
|
name=f"Download dovecot-{package}",
|
||||||
src=url,
|
src=url,
|
||||||
dest=deb_filename,
|
dest=deb_filename,
|
||||||
sha256sum=sha256,
|
sha256sum=sha256,
|
||||||
cache_time=60 * 60 * 24 * 365 * 10, # never redownload the package
|
cache_time=60 * 60 * 24 * 365 * 10, # never redownload the package
|
||||||
)
|
)
|
||||||
|
|
||||||
return deb_filename
|
apt.deb(name=f"Install dovecot-{package}", src=deb_filename)
|
||||||
|
|
||||||
|
|
||||||
def _configure_dovecot(config: Config, debug: bool = False) -> (bool, bool):
|
def _configure_dovecot(config: Config, debug: bool = False) -> (bool, bool):
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ smtpd_tls_key_file={{ config.tls_key_path }}
|
|||||||
smtpd_tls_security_level=may
|
smtpd_tls_security_level=may
|
||||||
|
|
||||||
smtp_tls_CApath=/etc/ssl/certs
|
smtp_tls_CApath=/etc/ssl/certs
|
||||||
smtp_tls_security_level=verify
|
smtp_tls_security_level={{ "verify" if config.tls_cert_mode == "acme" else "encrypt" }}
|
||||||
# Send SNI extension when connecting to other servers.
|
# Send SNI extension when connecting to other servers.
|
||||||
# <https://www.postfix.org/postconf.5.html#smtp_tls_servername>
|
# <https://www.postfix.org/postconf.5.html#smtp_tls_servername>
|
||||||
smtp_tls_servername = hostname
|
smtp_tls_servername = hostname
|
||||||
@@ -88,22 +88,6 @@ inet_protocols = ipv4
|
|||||||
inet_protocols = all
|
inet_protocols = all
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
# Postfix does not try IPv4 and IPv6 connections
|
|
||||||
# concurrently as of version 3.7.11.
|
|
||||||
#
|
|
||||||
# When relay has both A (IPv4) and AAAA (IPv6) records,
|
|
||||||
# but broken IPv6 connectivity,
|
|
||||||
# every second message is delayed by the connection timeout
|
|
||||||
# <https://www.postfix.org/postconf.5.html#smtp_connect_timeout>
|
|
||||||
# which defaults to 30 seconds. Reducing timeouts is not a solution
|
|
||||||
# as this will result in a failure to connect to slow servers.
|
|
||||||
#
|
|
||||||
# As a workaround we always prefer IPv4 when it is available.
|
|
||||||
#
|
|
||||||
# The setting is documented at
|
|
||||||
# <https://www.postfix.org/postconf.5.html#smtp_address_preference>
|
|
||||||
smtp_address_preference=ipv4
|
|
||||||
|
|
||||||
virtual_transport = lmtp:unix:private/dovecot-lmtp
|
virtual_transport = lmtp:unix:private/dovecot-lmtp
|
||||||
virtual_mailbox_domains = {{ config.mail_domain }}
|
virtual_mailbox_domains = {{ config.mail_domain }}
|
||||||
lmtp_header_checks = regexp:/etc/postfix/lmtp_header_cleanup
|
lmtp_header_checks = regexp:/etc/postfix/lmtp_header_cleanup
|
||||||
|
|||||||
@@ -57,10 +57,9 @@ def get_dkim_entry(mail_domain, pre_command, dkim_selector):
|
|||||||
dkim_value_raw = f"v=DKIM1;k=rsa;p={dkim_pubkey};s=email;t=s"
|
dkim_value_raw = f"v=DKIM1;k=rsa;p={dkim_pubkey};s=email;t=s"
|
||||||
dkim_value = '" "'.join(re.findall(".{1,255}", dkim_value_raw))
|
dkim_value = '" "'.join(re.findall(".{1,255}", dkim_value_raw))
|
||||||
web_dkim_value = "".join(re.findall(".{1,255}", dkim_value_raw))
|
web_dkim_value = "".join(re.findall(".{1,255}", dkim_value_raw))
|
||||||
name = f"{dkim_selector}._domainkey.{mail_domain}."
|
|
||||||
return (
|
return (
|
||||||
f'{name:<40} 3600 IN TXT "{dkim_value}"',
|
f'{dkim_selector}._domainkey.{mail_domain}. 3600 IN TXT "{dkim_value}"',
|
||||||
f'{name:<40} 3600 IN TXT "{web_dkim_value}"',
|
f'{dkim_selector}._domainkey.{mail_domain}. 3600 IN TXT "{web_dkim_value}"',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
; Required DNS entries
|
; Required DNS entries
|
||||||
zftest.testrun.org. 3600 IN A 135.181.204.127
|
zftest.testrun.org. 3600 IN A 135.181.204.127
|
||||||
zftest.testrun.org. 3600 IN AAAA 2a01:4f9:c012:52f4::1
|
zftest.testrun.org. 3600 IN AAAA 2a01:4f9:c012:52f4::1
|
||||||
zftest.testrun.org. 3600 IN MX 10 zftest.testrun.org.
|
zftest.testrun.org. 3600 IN MX 10 zftest.testrun.org.
|
||||||
_mta-sts.zftest.testrun.org. 3600 IN TXT "v=STSv1; id=202403211706"
|
_mta-sts.zftest.testrun.org. 3600 IN TXT "v=STSv1; id=202403211706"
|
||||||
mta-sts.zftest.testrun.org. 3600 IN CNAME zftest.testrun.org.
|
mta-sts.zftest.testrun.org. 3600 IN CNAME zftest.testrun.org.
|
||||||
www.zftest.testrun.org. 3600 IN CNAME zftest.testrun.org.
|
www.zftest.testrun.org. 3600 IN CNAME zftest.testrun.org.
|
||||||
opendkim._domainkey.zftest.testrun.org. 3600 IN TXT "v=DKIM1;k=rsa;p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoYt82CVUyz2ouaqjX2kB+5J80knAyoOU3MGU5aWppmwUwwTvj/oSTSpkc5JMtVTRmKKr8NUDWAL1Yw7dfGqqPHdHfwwjS3BIvDzYx+hzgtz62RnfNgV+/2MAoNpfX7cAFIHdRzEHNtwugc3RDLquqPoupAE3Y2YRw2T5zG5fILh4vwIcJZL5Uq6B92j8wwJqOex" "33n+vm1NKQ9rxo/UsHAmZlJzpooXcG/4igTBxJyJlamVSRR6N7Nul1v//YJb7J6v2o0iPHW6uE0StzKaPPNC2IVosSRFbD9H2oqppltptFSNPlI0E+t0JBWHem6YK7xcugiO3ImMCaaU8g6Jt/wIDAQAB;s=email;t=s"
|
opendkim._domainkey.zftest.testrun.org. 3600 IN TXT "v=DKIM1;k=rsa;p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoYt82CVUyz2ouaqjX2kB+5J80knAyoOU3MGU5aWppmwUwwTvj/oSTSpkc5JMtVTRmKKr8NUDWAL1Yw7dfGqqPHdHfwwjS3BIvDzYx+hzgtz62RnfNgV+/2MAoNpfX7cAFIHdRzEHNtwugc3RDLquqPoupAE3Y2YRw2T5zG5fILh4vwIcJZL5Uq6B92j8wwJqOex" "33n+vm1NKQ9rxo/UsHAmZlJzpooXcG/4igTBxJyJlamVSRR6N7Nul1v//YJb7J6v2o0iPHW6uE0StzKaPPNC2IVosSRFbD9H2oqppltptFSNPlI0E+t0JBWHem6YK7xcugiO3ImMCaaU8g6Jt/wIDAQAB;s=email;t=s"
|
||||||
|
|
||||||
; Recommended DNS entries
|
; Recommended DNS entries
|
||||||
zftest.testrun.org. 3600 IN TXT "v=spf1 a ~all"
|
zftest.testrun.org. 3600 IN TXT "v=spf1 a ~all"
|
||||||
_dmarc.zftest.testrun.org. 3600 IN TXT "v=DMARC1;p=reject;adkim=s;aspf=s"
|
_dmarc.zftest.testrun.org. 3600 IN TXT "v=DMARC1;p=reject;adkim=s;aspf=s"
|
||||||
zftest.testrun.org. 3600 IN CAA 0 issue "letsencrypt.org;accounturi=https://acme-v02.api.letsencrypt.org/acme/acct/1371472956"
|
zftest.testrun.org. 3600 IN CAA 0 issue "letsencrypt.org;accounturi=https://acme-v02.api.letsencrypt.org/acme/acct/1371472956"
|
||||||
_adsp._domainkey.zftest.testrun.org. 3600 IN TXT "dkim=discardable"
|
_adsp._domainkey.zftest.testrun.org. 3600 IN TXT "dkim=discardable"
|
||||||
_submission._tcp.zftest.testrun.org. 3600 IN SRV 0 1 587 zftest.testrun.org.
|
_submission._tcp.zftest.testrun.org. 3600 IN SRV 0 1 587 zftest.testrun.org.
|
||||||
_submissions._tcp.zftest.testrun.org. 3600 IN SRV 0 1 465 zftest.testrun.org.
|
_submissions._tcp.zftest.testrun.org. 3600 IN SRV 0 1 465 zftest.testrun.org.
|
||||||
_imap._tcp.zftest.testrun.org. 3600 IN SRV 0 1 143 zftest.testrun.org.
|
_imap._tcp.zftest.testrun.org. 3600 IN SRV 0 1 143 zftest.testrun.org.
|
||||||
_imaps._tcp.zftest.testrun.org. 3600 IN SRV 0 1 993 zftest.testrun.org.
|
_imaps._tcp.zftest.testrun.org. 3600 IN SRV 0 1 993 zftest.testrun.org.
|
||||||
|
|||||||
@@ -132,28 +132,11 @@ def test_parse_zone_records():
|
|||||||
|
|
||||||
; Another comment
|
; Another comment
|
||||||
www.some.domain. 3600 IN CNAME some.domain.
|
www.some.domain. 3600 IN CNAME some.domain.
|
||||||
|
|
||||||
; Multi-word rdata
|
|
||||||
some.domain. 3600 IN MX 10 mail.some.domain.
|
|
||||||
|
|
||||||
; DKIM record (single line, multi-word TXT rdata)
|
|
||||||
dkim._domainkey.some.domain. 3600 IN TXT "v=DKIM1;k=rsa;p=MIIBIjANBgkqhkiG" "9w0BAQEFAAOCAQ8AMIIBCgKCAQEA"
|
|
||||||
|
|
||||||
; Another TXT record
|
|
||||||
_dmarc.some.domain. 3600 IN TXT "v=DMARC1;p=reject"
|
|
||||||
"""
|
"""
|
||||||
records = list(parse_zone_records(text))
|
records = list(parse_zone_records(text))
|
||||||
assert records == [
|
assert records == [
|
||||||
("some.domain", "3600", "A", "1.1.1.1"),
|
("some.domain", "3600", "A", "1.1.1.1"),
|
||||||
("www.some.domain", "3600", "CNAME", "some.domain."),
|
("www.some.domain", "3600", "CNAME", "some.domain."),
|
||||||
("some.domain", "3600", "MX", "10 mail.some.domain."),
|
|
||||||
(
|
|
||||||
"dkim._domainkey.some.domain",
|
|
||||||
"3600",
|
|
||||||
"TXT",
|
|
||||||
'"v=DKIM1;k=rsa;p=MIIBIjANBgkqhkiG" "9w0BAQEFAAOCAQ8AMIIBCgKCAQEA"',
|
|
||||||
),
|
|
||||||
("_dmarc.some.domain", "3600", "TXT", '"v=DMARC1;p=reject"'),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -15,10 +15,6 @@ as they would on a real Debian server or cloud VPS.
|
|||||||
Prerequisites
|
Prerequisites
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
- Around 4-5 GiB free disk space
|
|
||||||
- `systemd-networkd` for the automagic hostname resolution
|
|
||||||
- No other service occupying Port 53
|
|
||||||
|
|
||||||
Install `Incus <https://linuxcontainers.org/incus/>`_
|
Install `Incus <https://linuxcontainers.org/incus/>`_
|
||||||
(LXC container manager).
|
(LXC container manager).
|
||||||
See the `official installation guide
|
See the `official installation guide
|
||||||
|
|||||||
Reference in New Issue
Block a user