mirror of
https://github.com/chatmail/relay.git
synced 2026-05-10 07:54:36 +00:00
retain "config.mail_domain" as the domain part of @ email addresses, so for ipv4 relays "[1.2.3.4]" and introduce config.ipv4_relay and config.mail_domain_bare helpers.
This commit is contained in:
@@ -21,9 +21,19 @@ def read_config(inipath):
|
|||||||
class Config:
|
class Config:
|
||||||
def __init__(self, inipath, params):
|
def __init__(self, inipath, params):
|
||||||
self._inipath = inipath
|
self._inipath = inipath
|
||||||
self.mail_domain = params["mail_domain"]
|
raw_domain = params["mail_domain"]
|
||||||
self.mail_domain_hostname = format_arpa_address(params["mail_domain"])
|
self.mail_domain_bare = raw_domain
|
||||||
self.mail_domain_deliverable = format_deliverable_domain(params["mail_domain"])
|
|
||||||
|
if is_valid_ipv4(raw_domain):
|
||||||
|
self.ipv4_relay = raw_domain
|
||||||
|
self.mail_domain = f"[{raw_domain}]"
|
||||||
|
self.postfix_myhostname = ipaddress.IPv4Address(raw_domain).reverse_pointer
|
||||||
|
else:
|
||||||
|
DomainValidator().validate_domain_re(raw_domain)
|
||||||
|
self.ipv4_relay = None
|
||||||
|
self.mail_domain = raw_domain
|
||||||
|
self.postfix_myhostname = raw_domain
|
||||||
|
|
||||||
self.max_user_send_per_minute = int(params.get("max_user_send_per_minute", 60))
|
self.max_user_send_per_minute = int(params.get("max_user_send_per_minute", 60))
|
||||||
self.max_user_send_burst_size = int(params.get("max_user_send_burst_size", 10))
|
self.max_user_send_burst_size = int(params.get("max_user_send_burst_size", 10))
|
||||||
self.max_mailbox_size = params["max_mailbox_size"]
|
self.max_mailbox_size = params["max_mailbox_size"]
|
||||||
@@ -57,7 +67,7 @@ class Config:
|
|||||||
self.imap_rawlog = params.get("imap_rawlog", "false").lower() == "true"
|
self.imap_rawlog = params.get("imap_rawlog", "false").lower() == "true"
|
||||||
self.imap_compress = params.get("imap_compress", "false").lower() == "true"
|
self.imap_compress = params.get("imap_compress", "false").lower() == "true"
|
||||||
if "iroh_relay" not in params:
|
if "iroh_relay" not in params:
|
||||||
self.iroh_relay = "https://" + params["mail_domain"]
|
self.iroh_relay = "https://" + raw_domain
|
||||||
self.enable_iroh_relay = True
|
self.enable_iroh_relay = True
|
||||||
else:
|
else:
|
||||||
self.iroh_relay = params["iroh_relay"].strip()
|
self.iroh_relay = params["iroh_relay"].strip()
|
||||||
@@ -83,19 +93,17 @@ class Config:
|
|||||||
)
|
)
|
||||||
self.tls_cert_mode = "external"
|
self.tls_cert_mode = "external"
|
||||||
self.tls_cert_path, self.tls_key_path = parts
|
self.tls_cert_path, self.tls_key_path = parts
|
||||||
elif self.mail_domain.startswith("_") or is_valid_ipv4(params["mail_domain"]):
|
elif raw_domain.startswith("_") or self.ipv4_relay:
|
||||||
self.tls_cert_mode = "self"
|
self.tls_cert_mode = "self"
|
||||||
self.tls_cert_path = "/etc/ssl/certs/mailserver.pem"
|
self.tls_cert_path = "/etc/ssl/certs/mailserver.pem"
|
||||||
self.tls_key_path = "/etc/ssl/private/mailserver.key"
|
self.tls_key_path = "/etc/ssl/private/mailserver.key"
|
||||||
else:
|
else:
|
||||||
self.tls_cert_mode = "acme"
|
self.tls_cert_mode = "acme"
|
||||||
self.tls_cert_path = f"/var/lib/acme/live/{self.mail_domain}/fullchain"
|
self.tls_cert_path = f"/var/lib/acme/live/{raw_domain}/fullchain"
|
||||||
self.tls_key_path = f"/var/lib/acme/live/{self.mail_domain}/privkey"
|
self.tls_key_path = f"/var/lib/acme/live/{raw_domain}/privkey"
|
||||||
|
|
||||||
# deprecated option
|
# deprecated option
|
||||||
mbdir = params.get(
|
mbdir = params.get("mailboxes_dir", f"/home/vmail/mail/{raw_domain}")
|
||||||
"mailboxes_dir", f"/home/vmail/mail/{self.mail_domain_deliverable}"
|
|
||||||
)
|
|
||||||
self.mailboxes_dir = Path(mbdir.strip())
|
self.mailboxes_dir = Path(mbdir.strip())
|
||||||
|
|
||||||
# old unused option (except for first migration from sqlite to maildir store)
|
# old unused option (except for first migration from sqlite to maildir store)
|
||||||
@@ -192,6 +200,7 @@ def is_valid_ipv4(address: str) -> bool:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def format_arpa_address(address: str) -> str:
|
def format_arpa_address(address: str) -> str:
|
||||||
if is_valid_ipv4(address):
|
if is_valid_ipv4(address):
|
||||||
return ipaddress.IPv4Address(address).reverse_pointer
|
return ipaddress.IPv4Address(address).reverse_pointer
|
||||||
@@ -199,8 +208,8 @@ def format_arpa_address(address: str) -> str:
|
|||||||
return address
|
return address
|
||||||
|
|
||||||
|
|
||||||
def format_deliverable_domain(mail_domain: str) -> str:
|
def format_mail_domain(raw_domain: str) -> str:
|
||||||
if is_valid_ipv4(mail_domain):
|
if is_valid_ipv4(raw_domain):
|
||||||
return f"[{mail_domain}]"
|
return f"[{raw_domain}]"
|
||||||
DomainValidator().validate_domain_re(mail_domain)
|
DomainValidator().validate_domain_re(raw_domain)
|
||||||
return mail_domain
|
return raw_domain
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ class AuthDictProxy(DictProxy):
|
|||||||
if namespace == "shared":
|
if namespace == "shared":
|
||||||
if type == "userdb":
|
if type == "userdb":
|
||||||
user = args[0]
|
user = args[0]
|
||||||
if user.endswith(f"@{config.mail_domain_deliverable}"):
|
if user.endswith(f"@{config.mail_domain}"):
|
||||||
res = self.lookup_userdb(user)
|
res = self.lookup_userdb(user)
|
||||||
if res:
|
if res:
|
||||||
reply_command = "O"
|
reply_command = "O"
|
||||||
@@ -116,7 +116,7 @@ class AuthDictProxy(DictProxy):
|
|||||||
reply_command = "N"
|
reply_command = "N"
|
||||||
elif type == "passdb":
|
elif type == "passdb":
|
||||||
user = args[1]
|
user = args[1]
|
||||||
if user.endswith(f"@{config.mail_domain_deliverable}"):
|
if user.endswith(f"@{config.mail_domain}"):
|
||||||
res = self.lookup_passdb(user, cleartext_password=args[0])
|
res = self.lookup_passdb(user, cleartext_password=args[0])
|
||||||
if res:
|
if res:
|
||||||
reply_command = "O"
|
reply_command = "O"
|
||||||
|
|||||||
@@ -22,9 +22,7 @@ def create_newemail_dict(config: Config):
|
|||||||
secrets.choice(ALPHANUMERIC_PUNCT)
|
secrets.choice(ALPHANUMERIC_PUNCT)
|
||||||
for _ in range(config.password_min_length + 3)
|
for _ in range(config.password_min_length + 3)
|
||||||
)
|
)
|
||||||
return dict(
|
return dict(email=f"{user}@{config.mail_domain}", password=f"{password}")
|
||||||
email=f"{user}@{config.mail_domain_deliverable}", password=f"{password}"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def create_dclogin_url(config, email, password):
|
def create_dclogin_url(config, email, password):
|
||||||
@@ -33,9 +31,9 @@ def create_dclogin_url(config, email, password):
|
|||||||
Uses ic=3 (AcceptInvalidCertificates) so chatmail clients
|
Uses ic=3 (AcceptInvalidCertificates) so chatmail clients
|
||||||
can connect to servers with self-signed TLS certificates.
|
can connect to servers with self-signed TLS certificates.
|
||||||
"""
|
"""
|
||||||
if config.mail_domain != config.mail_domain_deliverable:
|
if config.ipv4_relay:
|
||||||
imap_host = "&ih=" + config.mail_domain
|
imap_host = "&ih=" + config.ipv4_relay
|
||||||
smtp_host = "&sh=" + config.mail_domain
|
smtp_host = "&sh=" + config.ipv4_relay
|
||||||
else:
|
else:
|
||||||
imap_host = ""
|
imap_host = ""
|
||||||
smtp_host = ""
|
smtp_host = ""
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import pytest
|
|||||||
|
|
||||||
from chatmaild.config import (
|
from chatmaild.config import (
|
||||||
format_arpa_address,
|
format_arpa_address,
|
||||||
format_deliverable_domain,
|
format_mail_domain,
|
||||||
is_valid_ipv4,
|
is_valid_ipv4,
|
||||||
parse_size_mb,
|
parse_size_mb,
|
||||||
read_config,
|
read_config,
|
||||||
@@ -21,12 +21,12 @@ def test_read_config_basic(example_config):
|
|||||||
example_config = read_config(inipath)
|
example_config = read_config(inipath)
|
||||||
assert example_config.max_user_send_per_minute == 37
|
assert example_config.max_user_send_per_minute == 37
|
||||||
assert example_config.mail_domain == "chat.example.org"
|
assert example_config.mail_domain == "chat.example.org"
|
||||||
assert example_config.mail_domain_deliverable == "chat.example.org"
|
assert example_config.ipv4_relay is None
|
||||||
|
|
||||||
|
|
||||||
def test_read_config_deliverable(ipv4_config):
|
def test_read_config_ipv4(ipv4_config):
|
||||||
assert ipv4_config.mail_domain == "1.3.3.7"
|
assert ipv4_config.ipv4_relay == "1.3.3.7"
|
||||||
assert ipv4_config.mail_domain_deliverable == "[1.3.3.7]"
|
assert ipv4_config.mail_domain == "[1.3.3.7]"
|
||||||
|
|
||||||
|
|
||||||
def test_read_config_basic_using_defaults(tmp_path, maildomain):
|
def test_read_config_basic_using_defaults(tmp_path, maildomain):
|
||||||
@@ -188,6 +188,6 @@ def test_format_arpa_address(input, result, exception):
|
|||||||
("12394142", None, pytest.raises(ValueError)),
|
("12394142", None, pytest.raises(ValueError)),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_format_deliverable_domain(input, result, exception):
|
def test_format_mail_domain(input, result, exception):
|
||||||
with exception:
|
with exception:
|
||||||
assert result == format_deliverable_domain(input)
|
assert result == format_mail_domain(input)
|
||||||
|
|||||||
@@ -44,20 +44,12 @@ def test_invalid_username_length(example_config):
|
|||||||
config.username_min_length = 6
|
config.username_min_length = 6
|
||||||
config.username_max_length = 10
|
config.username_max_length = 10
|
||||||
password = create_newemail_dict(config)["password"]
|
password = create_newemail_dict(config)["password"]
|
||||||
|
assert not is_allowed_to_create(config, f"a1234@{config.mail_domain}", password)
|
||||||
|
assert is_allowed_to_create(config, f"012345@{config.mail_domain}", password)
|
||||||
|
assert is_allowed_to_create(config, f"0123456@{config.mail_domain}", password)
|
||||||
|
assert is_allowed_to_create(config, f"0123456789@{config.mail_domain}", password)
|
||||||
assert not is_allowed_to_create(
|
assert not is_allowed_to_create(
|
||||||
config, f"a1234@{config.mail_domain_deliverable}", password
|
config, f"0123456789x@{config.mail_domain}", password
|
||||||
)
|
|
||||||
assert is_allowed_to_create(
|
|
||||||
config, f"012345@{config.mail_domain_deliverable}", password
|
|
||||||
)
|
|
||||||
assert is_allowed_to_create(
|
|
||||||
config, f"0123456@{config.mail_domain_deliverable}", password
|
|
||||||
)
|
|
||||||
assert is_allowed_to_create(
|
|
||||||
config, f"0123456789@{config.mail_domain_deliverable}", password
|
|
||||||
)
|
|
||||||
assert not is_allowed_to_create(
|
|
||||||
config, f"0123456789x@{config.mail_domain_deliverable}", password
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -132,7 +124,7 @@ def test_invalid_localpart_characters(make_config):
|
|||||||
"""Test that is_allowed_to_create rejects localparts with invalid characters."""
|
"""Test that is_allowed_to_create rejects localparts with invalid characters."""
|
||||||
config = make_config("chat.example.org", {"username_min_length": "3"})
|
config = make_config("chat.example.org", {"username_min_length": "3"})
|
||||||
password = "zequ0Aimuchoodaechik"
|
password = "zequ0Aimuchoodaechik"
|
||||||
domain = config.mail_domain_deliverable
|
domain = config.mail_domain
|
||||||
|
|
||||||
# valid localparts
|
# valid localparts
|
||||||
assert is_allowed_to_create(config, f"abc123@{domain}", password)
|
assert is_allowed_to_create(config, f"abc123@{domain}", password)
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ def test_one_mail(
|
|||||||
print(line.decode("ascii"), file=sys.stderr)
|
print(line.decode("ascii"), file=sys.stderr)
|
||||||
pytest.fail("starting filtermail failed")
|
pytest.fail("starting filtermail failed")
|
||||||
|
|
||||||
addr = f"user1@{config.mail_domain_deliverable}"
|
addr = f"user1@{config.mail_domain}"
|
||||||
config.get_user(addr).set_password("l1k2j3l1k2j3l")
|
config.get_user(addr).set_password("l1k2j3l1k2j3l")
|
||||||
|
|
||||||
# send encrypted mail
|
# send encrypted mail
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ def test_print_new_account(capsys, monkeypatch, maildomain, tmpdir, example_conf
|
|||||||
assert lines[0] == "Content-Type: application/json"
|
assert lines[0] == "Content-Type: application/json"
|
||||||
assert not lines[1]
|
assert not lines[1]
|
||||||
dic = json.loads(lines[2])
|
dic = json.loads(lines[2])
|
||||||
assert dic["email"].endswith(f"@{example_config.mail_domain_deliverable}")
|
assert dic["email"].endswith(f"@{example_config.mail_domain}")
|
||||||
assert len(dic["password"]) >= 10
|
assert len(dic["password"]) >= 10
|
||||||
# default tls_cert=acme should not include dclogin_url
|
# default tls_cert=acme should not include dclogin_url
|
||||||
assert "dclogin_url" not in dic
|
assert "dclogin_url" not in dic
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import sys
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import pyinfra
|
import pyinfra
|
||||||
from chatmaild.config import read_config, write_initial_config, is_valid_ipv4
|
from chatmaild.config import read_config, write_initial_config
|
||||||
from packaging import version
|
from packaging import version
|
||||||
from termcolor import colored
|
from termcolor import colored
|
||||||
|
|
||||||
@@ -87,11 +87,11 @@ def run_cmd_options(parser):
|
|||||||
def run_cmd(args, out):
|
def run_cmd(args, out):
|
||||||
"""Deploy chatmail services on the remote server."""
|
"""Deploy chatmail services on the remote server."""
|
||||||
|
|
||||||
ssh_host = args.ssh_host if args.ssh_host else args.config.mail_domain
|
ssh_host = args.ssh_host if args.ssh_host else args.config.mail_domain_bare
|
||||||
sshexec = get_sshexec(ssh_host)
|
sshexec = get_sshexec(ssh_host)
|
||||||
require_iroh = args.config.enable_iroh_relay
|
require_iroh = args.config.enable_iroh_relay
|
||||||
strict_tls = args.config.tls_cert_mode == "acme"
|
strict_tls = args.config.tls_cert_mode == "acme"
|
||||||
if is_valid_ipv4(args.config.mail_domain):
|
if args.config.ipv4_relay:
|
||||||
args.dns_check_disabled = True
|
args.dns_check_disabled = True
|
||||||
if not args.dns_check_disabled:
|
if not args.dns_check_disabled:
|
||||||
remote_data = dns.get_initial_remote_data(sshexec, args.config.mail_domain)
|
remote_data = dns.get_initial_remote_data(sshexec, args.config.mail_domain)
|
||||||
@@ -121,7 +121,7 @@ def run_cmd(args, out):
|
|||||||
elif not args.dns_check_disabled and strict_tls and not remote_data["acme_account_url"]:
|
elif not args.dns_check_disabled and strict_tls and not remote_data["acme_account_url"]:
|
||||||
out.red("Deploy completed but letsencrypt not configured")
|
out.red("Deploy completed but letsencrypt not configured")
|
||||||
out.red("Run 'cmdeploy run' again")
|
out.red("Run 'cmdeploy run' again")
|
||||||
elif is_valid_ipv4(args.config.mail_domain):
|
elif args.config.ipv4_relay:
|
||||||
out.green("Deploy completed.")
|
out.green("Deploy completed.")
|
||||||
else:
|
else:
|
||||||
out.green("Deploy completed, call `cmdeploy dns` next.")
|
out.green("Deploy completed, call `cmdeploy dns` next.")
|
||||||
@@ -144,8 +144,9 @@ def dns_cmd_options(parser):
|
|||||||
|
|
||||||
def dns_cmd(args, out):
|
def dns_cmd(args, out):
|
||||||
"""Check DNS entries and optionally generate dns zone file."""
|
"""Check DNS entries and optionally generate dns zone file."""
|
||||||
if is_valid_ipv4(args.config.mail_domain):
|
if args.config.ipv4_relay:
|
||||||
print(f"[WARNING] {args.config.mail_domain} is not a domain, skipping DNS checks.")
|
ipv4 = args.config.ipv4_relay
|
||||||
|
print(f"[WARNING] {ipv4} is not a domain, skipping DNS checks.")
|
||||||
return 0
|
return 0
|
||||||
ssh_host = args.ssh_host if args.ssh_host else args.config.mail_domain
|
ssh_host = args.ssh_host if args.ssh_host else args.config.mail_domain
|
||||||
sshexec = get_sshexec(ssh_host, verbose=args.verbose)
|
sshexec = get_sshexec(ssh_host, verbose=args.verbose)
|
||||||
@@ -184,7 +185,7 @@ def status_cmd_options(parser):
|
|||||||
def status_cmd(args, out):
|
def status_cmd(args, out):
|
||||||
"""Display status for online chatmail instance."""
|
"""Display status for online chatmail instance."""
|
||||||
|
|
||||||
ssh_host = args.ssh_host if args.ssh_host else args.config.mail_domain
|
ssh_host = args.ssh_host if args.ssh_host else args.config.mail_domain_bare
|
||||||
sshexec = get_sshexec(ssh_host, verbose=args.verbose)
|
sshexec = get_sshexec(ssh_host, verbose=args.verbose)
|
||||||
|
|
||||||
out.green(f"chatmail domain: {args.config.mail_domain}")
|
out.green(f"chatmail domain: {args.config.mail_domain}")
|
||||||
|
|||||||
@@ -468,7 +468,7 @@ class ChatmailVenvDeployer(Deployer):
|
|||||||
|
|
||||||
def configure(self):
|
def configure(self):
|
||||||
_configure_remote_venv_with_chatmaild(self.config)
|
_configure_remote_venv_with_chatmaild(self.config)
|
||||||
configure_remote_units(self.config.mail_domain, self.units)
|
configure_remote_units(self.config.mail_domain_bare, self.units)
|
||||||
|
|
||||||
def activate(self):
|
def activate(self):
|
||||||
activate_remote_units(self.units)
|
activate_remote_units(self.units)
|
||||||
@@ -482,7 +482,7 @@ class ChatmailDeployer(Deployer):
|
|||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
self.config = config
|
self.config = config
|
||||||
self.mail_domain_deliverable = config.mail_domain_deliverable
|
self.mail_domain = config.mail_domain
|
||||||
|
|
||||||
def install(self):
|
def install(self):
|
||||||
files.put(
|
files.put(
|
||||||
@@ -522,7 +522,7 @@ class ChatmailDeployer(Deployer):
|
|||||||
server.shell(
|
server.shell(
|
||||||
name="Setup /etc/mailname",
|
name="Setup /etc/mailname",
|
||||||
commands=[
|
commands=[
|
||||||
f"echo {self.mail_domain_deliverable} >/etc/mailname; chmod 644 /etc/mailname"
|
f"echo {self.mail_domain} >/etc/mailname; chmod 644 /etc/mailname"
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -584,8 +584,7 @@ def deploy_chatmail(config_path: Path, disable_mail: bool, website_only: bool) -
|
|||||||
"""
|
"""
|
||||||
config = read_config(config_path)
|
config = read_config(config_path)
|
||||||
check_config(config)
|
check_config(config)
|
||||||
mail_domain = config.mail_domain
|
bare_host = config.mail_domain_bare
|
||||||
mail_domain_deliverable = config.mail_domain_deliverable
|
|
||||||
|
|
||||||
if website_only:
|
if website_only:
|
||||||
Deployment().perform_stages([WebsiteDeployer(config)])
|
Deployment().perform_stages([WebsiteDeployer(config)])
|
||||||
@@ -636,7 +635,7 @@ def deploy_chatmail(config_path: Path, disable_mail: bool, website_only: bool) -
|
|||||||
)
|
)
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
tls_deployer = get_tls_deployer(config, mail_domain)
|
tls_deployer = get_tls_deployer(config, bare_host)
|
||||||
|
|
||||||
all_deployers = [
|
all_deployers = [
|
||||||
ChatmailDeployer(config),
|
ChatmailDeployer(config),
|
||||||
@@ -644,13 +643,13 @@ def deploy_chatmail(config_path: Path, disable_mail: bool, website_only: bool) -
|
|||||||
FiltermailDeployer(),
|
FiltermailDeployer(),
|
||||||
JournaldDeployer(),
|
JournaldDeployer(),
|
||||||
UnboundDeployer(config),
|
UnboundDeployer(config),
|
||||||
TurnDeployer(mail_domain),
|
TurnDeployer(bare_host),
|
||||||
IrohDeployer(config.enable_iroh_relay),
|
IrohDeployer(config.enable_iroh_relay),
|
||||||
tls_deployer,
|
tls_deployer,
|
||||||
WebsiteDeployer(config),
|
WebsiteDeployer(config),
|
||||||
ChatmailVenvDeployer(config),
|
ChatmailVenvDeployer(config),
|
||||||
MtastsDeployer(),
|
MtastsDeployer(),
|
||||||
OpendkimDeployer(mail_domain_deliverable),
|
OpendkimDeployer(config.mail_domain),
|
||||||
# Dovecot should be started before Postfix
|
# Dovecot should be started before Postfix
|
||||||
# because it creates authentication socket
|
# because it creates authentication socket
|
||||||
# required by Postfix.
|
# required by Postfix.
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ class DovecotDeployer(Deployer):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def configure(self):
|
def configure(self):
|
||||||
configure_remote_units(self.config.mail_domain, self.units)
|
configure_remote_units(self.config.mail_domain_bare, self.units)
|
||||||
config_restart, self.daemon_reload = _configure_dovecot(self.config)
|
config_restart, self.daemon_reload = _configure_dovecot(self.config)
|
||||||
self.need_restart |= config_restart
|
self.need_restart |= config_restart
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ service imap {
|
|||||||
process_limit = 50000
|
process_limit = 50000
|
||||||
}
|
}
|
||||||
|
|
||||||
mail_server_admin = mailto:root@{{ config.mail_domain_deliverable }}
|
mail_server_admin = mailto:root@{{ config.mail_domain }}
|
||||||
mail_server_comment = Chatmail server
|
mail_server_comment = Chatmail server
|
||||||
|
|
||||||
# `zlib` enables compressing messages stored in the maildir.
|
# `zlib` enables compressing messages stored in the maildir.
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ from cmdeploy.basedeploy import Deployer, get_resource
|
|||||||
class OpendkimDeployer(Deployer):
|
class OpendkimDeployer(Deployer):
|
||||||
required_users = [("opendkim", None, ["opendkim"])]
|
required_users = [("opendkim", None, ["opendkim"])]
|
||||||
|
|
||||||
def __init__(self, mail_domain_deliverable):
|
def __init__(self, mail_domain):
|
||||||
self.mail_domain_deliverable = mail_domain_deliverable
|
self.mail_domain = mail_domain
|
||||||
|
|
||||||
def install(self):
|
def install(self):
|
||||||
apt.packages(
|
apt.packages(
|
||||||
@@ -22,7 +22,7 @@ class OpendkimDeployer(Deployer):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def configure(self):
|
def configure(self):
|
||||||
domain = self.mail_domain_deliverable
|
domain = self.mail_domain
|
||||||
dkim_selector = "opendkim"
|
dkim_selector = "opendkim"
|
||||||
"""Configures OpenDKIM"""
|
"""Configures OpenDKIM"""
|
||||||
need_restart = False
|
need_restart = False
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
myorigin = {{ config.mail_domain_deliverable }}
|
myorigin = {{ config.mail_domain }}
|
||||||
|
|
||||||
smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU)
|
smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU)
|
||||||
biff = no
|
biff = no
|
||||||
@@ -54,13 +54,13 @@ smtpd_tls_exclude_ciphers = aNULL, RC4, MD5, DES
|
|||||||
tls_preempt_cipherlist = yes
|
tls_preempt_cipherlist = yes
|
||||||
|
|
||||||
smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination
|
smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination
|
||||||
myhostname = {{ config.mail_domain_hostname }}
|
myhostname = {{ config.postfix_myhostname }}
|
||||||
alias_maps = hash:/etc/aliases
|
alias_maps = hash:/etc/aliases
|
||||||
alias_database = hash:/etc/aliases
|
alias_database = hash:/etc/aliases
|
||||||
|
|
||||||
# When postfix receives mail for $mydestination,
|
# When postfix receives mail for $mydestination,
|
||||||
# it hands it over to dovecot via $local_transport.
|
# it hands it over to dovecot via $local_transport.
|
||||||
mydestination = {{ config.mail_domain_deliverable }}
|
mydestination = {{ config.mail_domain }}
|
||||||
local_transport = lmtp:unix:private/dovecot-lmtp
|
local_transport = lmtp:unix:private/dovecot-lmtp
|
||||||
# postfix doesn't check whether local users exist or not:
|
# postfix doesn't check whether local users exist or not:
|
||||||
local_recipient_maps =
|
local_recipient_maps =
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ filter unix - n n - - lmtp
|
|||||||
-o syslog_name=postfix/reinject
|
-o syslog_name=postfix/reinject
|
||||||
-o milter_macro_daemon_name=ORIGINATING
|
-o milter_macro_daemon_name=ORIGINATING
|
||||||
-o cleanup_service_name=authclean
|
-o cleanup_service_name=authclean
|
||||||
{% if config.mail_domain == config.mail_domain_deliverable %} -o smtpd_milters=unix:opendkim/opendkim.sock
|
{% if not config.ipv4_relay %} -o smtpd_milters=unix:opendkim/opendkim.sock
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
# Local SMTP server for reinjecting incoming filtered mail
|
# Local SMTP server for reinjecting incoming filtered mail
|
||||||
|
|||||||
@@ -89,14 +89,14 @@ def test_concurrent_logins_same_account(
|
|||||||
assert login_results.get()
|
assert login_results.get()
|
||||||
|
|
||||||
|
|
||||||
def test_no_vrfy(cmfactory, chatmail_config):
|
def test_no_vrfy(cmfactory, chatmail_config, maildomain):
|
||||||
ac = cmfactory.get_online_account()
|
ac = cmfactory.get_online_account()
|
||||||
addr = ac.get_config("addr")
|
addr = ac.get_config("addr")
|
||||||
|
|
||||||
s = smtplib.SMTP(chatmail_config.mail_domain)
|
s = smtplib.SMTP(maildomain)
|
||||||
s.starttls()
|
s.starttls()
|
||||||
|
|
||||||
s.putcmd("vrfy", f"wrongaddress@{chatmail_config.mail_domain_deliverable}")
|
s.putcmd("vrfy", f"wrongaddress@{chatmail_config.mail_domain}")
|
||||||
result = s.getreply()
|
result = s.getreply()
|
||||||
print(result)
|
print(result)
|
||||||
s.putcmd("vrfy", addr)
|
s.putcmd("vrfy", addr)
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import time
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from chatmaild.config import read_config, format_deliverable_domain, is_valid_ipv4
|
from chatmaild.config import read_config, format_mail_domain, is_valid_ipv4
|
||||||
|
|
||||||
|
|
||||||
conftestdir = Path(__file__).parent
|
conftestdir = Path(__file__).parent
|
||||||
@@ -59,12 +59,12 @@ def chatmail_config(pytestconfig):
|
|||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
def maildomain(chatmail_config):
|
def maildomain(chatmail_config):
|
||||||
return chatmail_config.mail_domain
|
return chatmail_config.mail_domain_bare
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
def maildomain_deliverable(maildomain):
|
def maildomain_deliverable(maildomain):
|
||||||
return format_deliverable_domain(maildomain)
|
return format_mail_domain(maildomain)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
@@ -283,7 +283,7 @@ def gencreds(chatmail_config):
|
|||||||
next(count)
|
next(count)
|
||||||
|
|
||||||
def gen(domain=None):
|
def gen(domain=None):
|
||||||
domain = domain if domain else chatmail_config.mail_domain_deliverable
|
domain = domain if domain else chatmail_config.mail_domain
|
||||||
while 1:
|
while 1:
|
||||||
num = next(count)
|
num = next(count)
|
||||||
alphanumeric = "abcdefghijklmnopqrstuvwxyz1234567890"
|
alphanumeric = "abcdefghijklmnopqrstuvwxyz1234567890"
|
||||||
@@ -322,7 +322,7 @@ class ChatmailACFactory:
|
|||||||
|
|
||||||
def _make_transport(self, domain):
|
def _make_transport(self, domain):
|
||||||
"""Build a transport config dict for the given domain."""
|
"""Build a transport config dict for the given domain."""
|
||||||
domain_deliverable = format_deliverable_domain(domain)
|
domain_deliverable = format_mail_domain(domain)
|
||||||
addr, password = self.gencreds(domain_deliverable)
|
addr, password = self.gencreds(domain_deliverable)
|
||||||
transport = {
|
transport = {
|
||||||
"addr": addr,
|
"addr": addr,
|
||||||
@@ -347,7 +347,7 @@ class ChatmailACFactory:
|
|||||||
accounts = []
|
accounts = []
|
||||||
for _ in range(num):
|
for _ in range(num):
|
||||||
account = self.dc.add_account()
|
account = self.dc.add_account()
|
||||||
domain_deliverable = format_deliverable_domain(domain)
|
domain_deliverable = format_mail_domain(domain)
|
||||||
addr, password = self.gencreds(domain_deliverable)
|
addr, password = self.gencreds(domain_deliverable)
|
||||||
if _is_ip(domain):
|
if _is_ip(domain):
|
||||||
# Use DCLOGIN scheme with explicit server hosts,
|
# Use DCLOGIN scheme with explicit server hosts,
|
||||||
|
|||||||
Reference in New Issue
Block a user