mirror of
https://github.com/chatmail/relay.git
synced 2026-05-15 10:24:40 +00:00
Compare commits
10 Commits
link2xt/ad
...
link2xt/py
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
501351cfe5 | ||
|
|
c8d9f20a48 | ||
|
|
6a30db7ce0 | ||
|
|
9e9ab80422 | ||
|
|
5b9debfbdf | ||
|
|
788309b85a | ||
|
|
5bbb3d9b21 | ||
|
|
6bc2186912 | ||
|
|
8d5f91bf98 | ||
|
|
9ddf60d0fc |
@@ -160,6 +160,19 @@ def handle_dovecot_request(msg, db, config: Config):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def handle_dovecot_protocol(rfile, wfile, db: Database, config: Config):
|
||||||
|
while True:
|
||||||
|
msg = rfile.readline().strip().decode()
|
||||||
|
if not msg:
|
||||||
|
break
|
||||||
|
res = handle_dovecot_request(msg, db, config)
|
||||||
|
if res:
|
||||||
|
wfile.write(res.encode("ascii"))
|
||||||
|
wfile.flush()
|
||||||
|
else:
|
||||||
|
logging.warning("request had no answer: %r", msg)
|
||||||
|
|
||||||
|
|
||||||
class ThreadedUnixStreamServer(ThreadingMixIn, UnixStreamServer):
|
class ThreadedUnixStreamServer(ThreadingMixIn, UnixStreamServer):
|
||||||
request_queue_size = 100
|
request_queue_size = 100
|
||||||
|
|
||||||
@@ -173,16 +186,7 @@ def main():
|
|||||||
class Handler(StreamRequestHandler):
|
class Handler(StreamRequestHandler):
|
||||||
def handle(self):
|
def handle(self):
|
||||||
try:
|
try:
|
||||||
while True:
|
handle_dovecot_protocol(self.rfile, self.wfile, db, config)
|
||||||
msg = self.rfile.readline().strip().decode()
|
|
||||||
if not msg:
|
|
||||||
break
|
|
||||||
res = handle_dovecot_request(msg, db, config)
|
|
||||||
if res:
|
|
||||||
self.wfile.write(res.encode("ascii"))
|
|
||||||
self.wfile.flush()
|
|
||||||
else:
|
|
||||||
logging.warn("request had no answer: %r", msg)
|
|
||||||
except Exception:
|
except Exception:
|
||||||
logging.exception("Exception in the handler")
|
logging.exception("Exception in the handler")
|
||||||
raise
|
raise
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
|
import io
|
||||||
import json
|
import json
|
||||||
import pytest
|
import pytest
|
||||||
import threading
|
|
||||||
import queue
|
import queue
|
||||||
|
import threading
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
import chatmaild.doveauth
|
import chatmaild.doveauth
|
||||||
from chatmaild.doveauth import get_user_data, lookup_passdb, handle_dovecot_request
|
from chatmaild.doveauth import (
|
||||||
|
get_user_data,
|
||||||
|
lookup_passdb,
|
||||||
|
handle_dovecot_request,
|
||||||
|
handle_dovecot_protocol,
|
||||||
|
)
|
||||||
from chatmaild.database import DBError
|
from chatmaild.database import DBError
|
||||||
|
|
||||||
|
|
||||||
@@ -69,6 +75,15 @@ def test_handle_dovecot_request(db, example_config):
|
|||||||
assert userdata["password"].startswith("{SHA512-CRYPT}")
|
assert userdata["password"].startswith("{SHA512-CRYPT}")
|
||||||
|
|
||||||
|
|
||||||
|
def test_handle_dovecot_protocol(db, example_config):
|
||||||
|
rfile = io.BytesIO(
|
||||||
|
b"H3\t2\t0\t\tauth\nLshared/userdb/foobar@chat.example.org\tfoobar@chat.example.org\n"
|
||||||
|
)
|
||||||
|
wfile = io.BytesIO()
|
||||||
|
handle_dovecot_protocol(rfile, wfile, db, example_config)
|
||||||
|
assert wfile.getvalue() == b"N\n"
|
||||||
|
|
||||||
|
|
||||||
def test_50_concurrent_lookups_different_accounts(db, gencreds, example_config):
|
def test_50_concurrent_lookups_different_accounts(db, gencreds, example_config):
|
||||||
num_threads = 50
|
num_threads = 50
|
||||||
req_per_thread = 5
|
req_per_thread = 5
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
name = "cmdeploy"
|
name = "cmdeploy"
|
||||||
version = "0.2"
|
version = "0.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"pyinfra",
|
"pyinfra==3.0b0",
|
||||||
"pillow",
|
"pillow",
|
||||||
"qrcode",
|
"qrcode",
|
||||||
"markdown",
|
"markdown",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
Chat Mail pyinfra deploy.
|
Chat Mail pyinfra deploy.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import importlib.resources
|
import importlib.resources
|
||||||
import subprocess
|
import subprocess
|
||||||
@@ -9,13 +10,15 @@ import io
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from pyinfra import host
|
from pyinfra import host
|
||||||
from pyinfra.operations import apt, files, server, systemd, pip
|
from pyinfra.operations import apt, files, server, systemd, pip, util
|
||||||
from pyinfra.facts.files import File
|
from pyinfra.facts.files import File
|
||||||
from pyinfra.facts.systemd import SystemdEnabled
|
from pyinfra.facts.systemd import SystemdEnabled
|
||||||
from .acmetool import deploy_acmetool
|
from .acmetool import deploy_acmetool
|
||||||
|
|
||||||
from chatmaild.config import read_config, Config
|
from chatmaild.config import read_config, Config
|
||||||
|
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
|
|
||||||
def _build_chatmaild(dist_dir) -> None:
|
def _build_chatmaild(dist_dir) -> None:
|
||||||
dist_dir = Path(dist_dir).resolve()
|
dist_dir = Path(dist_dir).resolve()
|
||||||
@@ -126,10 +129,8 @@ def _install_remote_venv_with_chatmaild(config) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _configure_opendkim(domain: str, dkim_selector: str = "dkim") -> bool:
|
def _configure_opendkim(domain: str, dkim_selector: str = "dkim") -> Callable[[], bool]:
|
||||||
"""Configures OpenDKIM"""
|
"""Configures OpenDKIM"""
|
||||||
need_restart = False
|
|
||||||
|
|
||||||
server.group(name="Create opendkim group", group="opendkim", system=True)
|
server.group(name="Create opendkim group", group="opendkim", system=True)
|
||||||
server.user(
|
server.user(
|
||||||
name="Create opendkim user",
|
name="Create opendkim user",
|
||||||
@@ -152,7 +153,6 @@ def _configure_opendkim(domain: str, dkim_selector: str = "dkim") -> bool:
|
|||||||
mode="644",
|
mode="644",
|
||||||
config={"domain_name": domain, "opendkim_selector": dkim_selector},
|
config={"domain_name": domain, "opendkim_selector": dkim_selector},
|
||||||
)
|
)
|
||||||
need_restart |= main_config.changed
|
|
||||||
|
|
||||||
screen_script = files.put(
|
screen_script = files.put(
|
||||||
src=importlib.resources.files(__package__).joinpath("opendkim/screen.lua"),
|
src=importlib.resources.files(__package__).joinpath("opendkim/screen.lua"),
|
||||||
@@ -161,7 +161,6 @@ def _configure_opendkim(domain: str, dkim_selector: str = "dkim") -> bool:
|
|||||||
group="root",
|
group="root",
|
||||||
mode="644",
|
mode="644",
|
||||||
)
|
)
|
||||||
need_restart |= screen_script.changed
|
|
||||||
|
|
||||||
final_script = files.put(
|
final_script = files.put(
|
||||||
src=importlib.resources.files(__package__).joinpath("opendkim/final.lua"),
|
src=importlib.resources.files(__package__).joinpath("opendkim/final.lua"),
|
||||||
@@ -170,7 +169,6 @@ def _configure_opendkim(domain: str, dkim_selector: str = "dkim") -> bool:
|
|||||||
group="root",
|
group="root",
|
||||||
mode="644",
|
mode="644",
|
||||||
)
|
)
|
||||||
need_restart |= final_script.changed
|
|
||||||
|
|
||||||
files.directory(
|
files.directory(
|
||||||
name="Add opendkim directory to /etc",
|
name="Add opendkim directory to /etc",
|
||||||
@@ -189,7 +187,6 @@ def _configure_opendkim(domain: str, dkim_selector: str = "dkim") -> bool:
|
|||||||
mode="644",
|
mode="644",
|
||||||
config={"domain_name": domain, "opendkim_selector": dkim_selector},
|
config={"domain_name": domain, "opendkim_selector": dkim_selector},
|
||||||
)
|
)
|
||||||
need_restart |= keytable.changed
|
|
||||||
|
|
||||||
signing_table = files.template(
|
signing_table = files.template(
|
||||||
src=importlib.resources.files(__package__).joinpath("opendkim/SigningTable"),
|
src=importlib.resources.files(__package__).joinpath("opendkim/SigningTable"),
|
||||||
@@ -199,7 +196,7 @@ def _configure_opendkim(domain: str, dkim_selector: str = "dkim") -> bool:
|
|||||||
mode="644",
|
mode="644",
|
||||||
config={"domain_name": domain, "opendkim_selector": dkim_selector},
|
config={"domain_name": domain, "opendkim_selector": dkim_selector},
|
||||||
)
|
)
|
||||||
need_restart |= signing_table.changed
|
|
||||||
files.directory(
|
files.directory(
|
||||||
name="Add opendkim socket directory to /var/spool/postfix",
|
name="Add opendkim socket directory to /var/spool/postfix",
|
||||||
path="/var/spool/postfix/opendkim",
|
path="/var/spool/postfix/opendkim",
|
||||||
@@ -224,10 +221,12 @@ def _configure_opendkim(domain: str, dkim_selector: str = "dkim") -> bool:
|
|||||||
_sudo_user="opendkim",
|
_sudo_user="opendkim",
|
||||||
)
|
)
|
||||||
|
|
||||||
return need_restart
|
return util.any_changed(
|
||||||
|
main_config, screen_script, final_script, keytable, signing_table
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _install_mta_sts_daemon() -> bool:
|
def _install_mta_sts_daemon() -> Callable[[], bool]:
|
||||||
need_restart = False
|
need_restart = False
|
||||||
|
|
||||||
config = files.put(
|
config = files.put(
|
||||||
@@ -240,7 +239,6 @@ def _install_mta_sts_daemon() -> bool:
|
|||||||
group="root",
|
group="root",
|
||||||
mode="644",
|
mode="644",
|
||||||
)
|
)
|
||||||
need_restart |= config.changed
|
|
||||||
|
|
||||||
server.shell(
|
server.shell(
|
||||||
name="install postfix-mta-sts-resolver with pip",
|
name="install postfix-mta-sts-resolver with pip",
|
||||||
@@ -260,15 +258,12 @@ def _install_mta_sts_daemon() -> bool:
|
|||||||
group="root",
|
group="root",
|
||||||
mode="644",
|
mode="644",
|
||||||
)
|
)
|
||||||
need_restart |= systemd_unit.changed
|
|
||||||
|
|
||||||
return need_restart
|
return util.any_changed(config, systemd_unit)
|
||||||
|
|
||||||
|
|
||||||
def _configure_postfix(config: Config, debug: bool = False) -> bool:
|
def _configure_postfix(config: Config, debug: bool = False) -> Callable[[], bool]:
|
||||||
"""Configures Postfix SMTP server."""
|
"""Configures Postfix SMTP server."""
|
||||||
need_restart = False
|
|
||||||
|
|
||||||
main_config = files.template(
|
main_config = files.template(
|
||||||
src=importlib.resources.files(__package__).joinpath("postfix/main.cf.j2"),
|
src=importlib.resources.files(__package__).joinpath("postfix/main.cf.j2"),
|
||||||
dest="/etc/postfix/main.cf",
|
dest="/etc/postfix/main.cf",
|
||||||
@@ -277,7 +272,6 @@ def _configure_postfix(config: Config, debug: bool = False) -> bool:
|
|||||||
mode="644",
|
mode="644",
|
||||||
config=config,
|
config=config,
|
||||||
)
|
)
|
||||||
need_restart |= main_config.changed
|
|
||||||
|
|
||||||
master_config = files.template(
|
master_config = files.template(
|
||||||
src=importlib.resources.files(__package__).joinpath("postfix/master.cf.j2"),
|
src=importlib.resources.files(__package__).joinpath("postfix/master.cf.j2"),
|
||||||
@@ -288,7 +282,6 @@ def _configure_postfix(config: Config, debug: bool = False) -> bool:
|
|||||||
debug=debug,
|
debug=debug,
|
||||||
config=config,
|
config=config,
|
||||||
)
|
)
|
||||||
need_restart |= master_config.changed
|
|
||||||
|
|
||||||
header_cleanup = files.put(
|
header_cleanup = files.put(
|
||||||
src=importlib.resources.files(__package__).joinpath(
|
src=importlib.resources.files(__package__).joinpath(
|
||||||
@@ -299,27 +292,21 @@ def _configure_postfix(config: Config, debug: bool = False) -> bool:
|
|||||||
group="root",
|
group="root",
|
||||||
mode="644",
|
mode="644",
|
||||||
)
|
)
|
||||||
need_restart |= header_cleanup.changed
|
|
||||||
|
|
||||||
# Login map that 1:1 maps email address to login.
|
# Login map that 1:1 maps email address to login.
|
||||||
login_map = files.put(
|
login_map = files.put(
|
||||||
src=importlib.resources.files(__package__).joinpath(
|
src=importlib.resources.files(__package__).joinpath("postfix/login_map"),
|
||||||
"postfix/login_map"
|
|
||||||
),
|
|
||||||
dest="/etc/postfix/login_map",
|
dest="/etc/postfix/login_map",
|
||||||
user="root",
|
user="root",
|
||||||
group="root",
|
group="root",
|
||||||
mode="644",
|
mode="644",
|
||||||
)
|
)
|
||||||
need_restart |= login_map.changed
|
|
||||||
|
|
||||||
return need_restart
|
return util.any_changed(main_config, master_config, header_cleanup, login_map)
|
||||||
|
|
||||||
|
|
||||||
def _configure_dovecot(config: Config, debug: bool = False) -> bool:
|
def _configure_dovecot(config: Config, debug: bool = False) -> Callable[[], bool]:
|
||||||
"""Configures Dovecot IMAP server."""
|
"""Configures Dovecot IMAP server."""
|
||||||
need_restart = False
|
|
||||||
|
|
||||||
main_config = files.template(
|
main_config = files.template(
|
||||||
src=importlib.resources.files(__package__).joinpath("dovecot/dovecot.conf.j2"),
|
src=importlib.resources.files(__package__).joinpath("dovecot/dovecot.conf.j2"),
|
||||||
dest="/etc/dovecot/dovecot.conf",
|
dest="/etc/dovecot/dovecot.conf",
|
||||||
@@ -329,7 +316,6 @@ def _configure_dovecot(config: Config, debug: bool = False) -> bool:
|
|||||||
config=config,
|
config=config,
|
||||||
debug=debug,
|
debug=debug,
|
||||||
)
|
)
|
||||||
need_restart |= main_config.changed
|
|
||||||
auth_config = files.put(
|
auth_config = files.put(
|
||||||
src=importlib.resources.files(__package__).joinpath("dovecot/auth.conf"),
|
src=importlib.resources.files(__package__).joinpath("dovecot/auth.conf"),
|
||||||
dest="/etc/dovecot/auth.conf",
|
dest="/etc/dovecot/auth.conf",
|
||||||
@@ -337,7 +323,6 @@ def _configure_dovecot(config: Config, debug: bool = False) -> bool:
|
|||||||
group="root",
|
group="root",
|
||||||
mode="644",
|
mode="644",
|
||||||
)
|
)
|
||||||
need_restart |= auth_config.changed
|
|
||||||
|
|
||||||
files.template(
|
files.template(
|
||||||
src=importlib.resources.files(__package__).joinpath("dovecot/expunge.cron.j2"),
|
src=importlib.resources.files(__package__).joinpath("dovecot/expunge.cron.j2"),
|
||||||
@@ -359,13 +344,11 @@ def _configure_dovecot(config: Config, debug: bool = False) -> bool:
|
|||||||
persist=True,
|
persist=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
return need_restart
|
return util.any_changed(main_config, auth_config)
|
||||||
|
|
||||||
|
|
||||||
def _configure_nginx(domain: str, debug: bool = False) -> bool:
|
def _configure_nginx(domain: str, debug: bool = False) -> Callable[[], bool]:
|
||||||
"""Configures nginx HTTP server."""
|
"""Configures nginx HTTP server."""
|
||||||
need_restart = False
|
|
||||||
|
|
||||||
main_config = files.template(
|
main_config = files.template(
|
||||||
src=importlib.resources.files(__package__).joinpath("nginx/nginx.conf.j2"),
|
src=importlib.resources.files(__package__).joinpath("nginx/nginx.conf.j2"),
|
||||||
dest="/etc/nginx/nginx.conf",
|
dest="/etc/nginx/nginx.conf",
|
||||||
@@ -374,7 +357,6 @@ def _configure_nginx(domain: str, debug: bool = False) -> bool:
|
|||||||
mode="644",
|
mode="644",
|
||||||
config={"domain_name": domain},
|
config={"domain_name": domain},
|
||||||
)
|
)
|
||||||
need_restart |= main_config.changed
|
|
||||||
|
|
||||||
autoconfig = files.template(
|
autoconfig = files.template(
|
||||||
src=importlib.resources.files(__package__).joinpath("nginx/autoconfig.xml.j2"),
|
src=importlib.resources.files(__package__).joinpath("nginx/autoconfig.xml.j2"),
|
||||||
@@ -384,7 +366,6 @@ def _configure_nginx(domain: str, debug: bool = False) -> bool:
|
|||||||
mode="644",
|
mode="644",
|
||||||
config={"domain_name": domain},
|
config={"domain_name": domain},
|
||||||
)
|
)
|
||||||
need_restart |= autoconfig.changed
|
|
||||||
|
|
||||||
mta_sts_config = files.template(
|
mta_sts_config = files.template(
|
||||||
src=importlib.resources.files(__package__).joinpath("nginx/mta-sts.txt.j2"),
|
src=importlib.resources.files(__package__).joinpath("nginx/mta-sts.txt.j2"),
|
||||||
@@ -394,7 +375,6 @@ def _configure_nginx(domain: str, debug: bool = False) -> bool:
|
|||||||
mode="644",
|
mode="644",
|
||||||
config={"domain_name": domain},
|
config={"domain_name": domain},
|
||||||
)
|
)
|
||||||
need_restart |= mta_sts_config.changed
|
|
||||||
|
|
||||||
# install CGI newemail script
|
# install CGI newemail script
|
||||||
#
|
#
|
||||||
@@ -415,7 +395,7 @@ def _configure_nginx(domain: str, debug: bool = False) -> bool:
|
|||||||
mode="755",
|
mode="755",
|
||||||
)
|
)
|
||||||
|
|
||||||
return need_restart
|
return util.any_changed(main_config, autoconfig, mta_sts_config)
|
||||||
|
|
||||||
|
|
||||||
def _remove_rspamd() -> None:
|
def _remove_rspamd() -> None:
|
||||||
@@ -519,7 +499,12 @@ def deploy_chatmail(config_path: Path) -> None:
|
|||||||
service="opendkim.service",
|
service="opendkim.service",
|
||||||
running=True,
|
running=True,
|
||||||
enabled=True,
|
enabled=True,
|
||||||
restarted=opendkim_need_restart,
|
)
|
||||||
|
systemd.service(
|
||||||
|
name="Restart OpenDKIM",
|
||||||
|
service="opendkim.service",
|
||||||
|
restarted=True,
|
||||||
|
_if=opendkim_need_restart,
|
||||||
)
|
)
|
||||||
|
|
||||||
systemd.service(
|
systemd.service(
|
||||||
@@ -528,7 +513,12 @@ def deploy_chatmail(config_path: Path) -> None:
|
|||||||
daemon_reload=True,
|
daemon_reload=True,
|
||||||
running=True,
|
running=True,
|
||||||
enabled=True,
|
enabled=True,
|
||||||
restarted=mta_sts_need_restart,
|
)
|
||||||
|
systemd.service(
|
||||||
|
name="Restart MTA-STS daemon",
|
||||||
|
service="mta-sts-daemon.service",
|
||||||
|
restarted=True,
|
||||||
|
_if=mta_sts_need_restart,
|
||||||
)
|
)
|
||||||
|
|
||||||
systemd.service(
|
systemd.service(
|
||||||
@@ -536,7 +526,12 @@ def deploy_chatmail(config_path: Path) -> None:
|
|||||||
service="postfix.service",
|
service="postfix.service",
|
||||||
running=True,
|
running=True,
|
||||||
enabled=True,
|
enabled=True,
|
||||||
restarted=postfix_need_restart,
|
)
|
||||||
|
systemd.service(
|
||||||
|
name="Restart Postfix",
|
||||||
|
service="postfix.service",
|
||||||
|
restarted=True,
|
||||||
|
_if=postfix_need_restart,
|
||||||
)
|
)
|
||||||
|
|
||||||
systemd.service(
|
systemd.service(
|
||||||
@@ -544,7 +539,12 @@ def deploy_chatmail(config_path: Path) -> None:
|
|||||||
service="dovecot.service",
|
service="dovecot.service",
|
||||||
running=True,
|
running=True,
|
||||||
enabled=True,
|
enabled=True,
|
||||||
restarted=dovecot_need_restart,
|
)
|
||||||
|
systemd.service(
|
||||||
|
name="Restart Dovecot",
|
||||||
|
service="dovecot.service",
|
||||||
|
restarted=True,
|
||||||
|
_if=dovecot_need_restart,
|
||||||
)
|
)
|
||||||
|
|
||||||
systemd.service(
|
systemd.service(
|
||||||
@@ -552,7 +552,12 @@ def deploy_chatmail(config_path: Path) -> None:
|
|||||||
service="nginx.service",
|
service="nginx.service",
|
||||||
running=True,
|
running=True,
|
||||||
enabled=True,
|
enabled=True,
|
||||||
restarted=nginx_need_restart,
|
)
|
||||||
|
systemd.service(
|
||||||
|
name="Restart nginx",
|
||||||
|
service="nginx.service",
|
||||||
|
restarted=True,
|
||||||
|
_if=nginx_need_restart,
|
||||||
)
|
)
|
||||||
|
|
||||||
# This file is used by auth proxy.
|
# This file is used by auth proxy.
|
||||||
|
|||||||
@@ -69,7 +69,12 @@ def deploy_acmetool(nginx_hook=False, email="", domains=[]):
|
|||||||
service="acmetool-redirector.service",
|
service="acmetool-redirector.service",
|
||||||
running=True,
|
running=True,
|
||||||
enabled=True,
|
enabled=True,
|
||||||
restarted=service_file.changed,
|
)
|
||||||
|
systemd.service(
|
||||||
|
name="Restart acmetool-redirector service",
|
||||||
|
service="acmetool-redirector.service",
|
||||||
|
restarted=True,
|
||||||
|
_if=service_file.did_change,
|
||||||
)
|
)
|
||||||
|
|
||||||
server.shell(
|
server.shell(
|
||||||
|
|||||||
@@ -11,6 +11,5 @@ _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}. TXT "v=STSv1; id={sts_id}"
|
||||||
mta-sts.{chatmail_domain}. CNAME {chatmail_domain}.
|
mta-sts.{chatmail_domain}. CNAME {chatmail_domain}.
|
||||||
www.{chatmail_domain}. CNAME {chatmail_domain}.
|
www.{chatmail_domain}. CNAME {chatmail_domain}.
|
||||||
_smtp._tls.{chatmail_domain}. TXT "v=TLSRPTv1;rua=mailto:{email}"
|
|
||||||
{dkim_entry}
|
{dkim_entry}
|
||||||
_adsp._domainkey.{chatmail_domain}. TXT "dkim=discardable"
|
_adsp._domainkey.{chatmail_domain}. TXT "dkim=discardable"
|
||||||
|
|||||||
@@ -82,7 +82,6 @@ def show_dns(args, out) -> int:
|
|||||||
f.read()
|
f.read()
|
||||||
.format(
|
.format(
|
||||||
acme_account_url=acme_account_url,
|
acme_account_url=acme_account_url,
|
||||||
email=f"root@{args.config.mail_domain}",
|
|
||||||
sts_id=datetime.datetime.now().strftime("%Y%m%d%H%M"),
|
sts_id=datetime.datetime.now().strftime("%Y%m%d%H%M"),
|
||||||
chatmail_domain=args.config.mail_domain,
|
chatmail_domain=args.config.mail_domain,
|
||||||
dkim_entry=dkim_entry,
|
dkim_entry=dkim_entry,
|
||||||
@@ -102,7 +101,6 @@ def show_dns(args, out) -> int:
|
|||||||
for line in zonefile.splitlines():
|
for line in zonefile.splitlines():
|
||||||
line = line.format(
|
line = line.format(
|
||||||
acme_account_url=acme_account_url,
|
acme_account_url=acme_account_url,
|
||||||
email=f"root@{args.config.mail_domain}",
|
|
||||||
sts_id=datetime.datetime.now().strftime("%Y%m%d%H%M"),
|
sts_id=datetime.datetime.now().strftime("%Y%m%d%H%M"),
|
||||||
chatmail_domain=args.config.mail_domain,
|
chatmail_domain=args.config.mail_domain,
|
||||||
dkim_entry=dkim_entry,
|
dkim_entry=dkim_entry,
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
# delete all mails after {{ config.delete_mails_after }} days, in the Inbox
|
# delete all mails after {{ config.delete_mails_after }} days, in the Inbox
|
||||||
2 0 * * * dovecot find /home/vmail/mail/{{ config.mail_domain }}/*/cur -mtime +{{ config.delete_mails_after }} -type f -delete
|
2 0 * * * dovecot find /home/vmail/mail/{{ config.mail_domain }} -path '*/cur/*' -mtime +{{ config.delete_mails_after }} -type f -delete
|
||||||
# or in any IMAP subfolder
|
# or in any IMAP subfolder
|
||||||
2 0 * * * dovecot find /home/vmail/mail/{{ config.mail_domain }}/*/.*/cur -mtime +{{ config.delete_mails_after }} -type f -delete
|
2 0 * * * dovecot find /home/vmail/mail/{{ config.mail_domain }} -path '*/.*/cur/*' -mtime +{{ config.delete_mails_after }} -type f -delete
|
||||||
# even if they are unseen
|
# even if they are unseen
|
||||||
2 0 * * * dovecot find /home/vmail/mail/{{ config.mail_domain }}/*/new -mtime +{{ config.delete_mails_after }} -type f -delete
|
2 0 * * * dovecot find /home/vmail/mail/{{ config.mail_domain }} -path '*/new/*' -mtime +{{ config.delete_mails_after }} -type f -delete
|
||||||
2 0 * * * dovecot find /home/vmail/mail/{{ config.mail_domain }}/*/.*/new -mtime +{{ config.delete_mails_after }} -type f -delete
|
2 0 * * * dovecot find /home/vmail/mail/{{ config.mail_domain }} -path '*/.*/new/*' -mtime +{{ config.delete_mails_after }} -type f -delete
|
||||||
# or only temporary (but then they shouldn't be around after {{ config.delete_mails_after }} days anyway).
|
# or only temporary (but then they shouldn't be around after {{ config.delete_mails_after }} days anyway).
|
||||||
2 0 * * * dovecot find /home/vmail/mail/{{ config.mail_domain }}/*/tmp -mtime +{{ config.delete_mails_after }} -type f -delete
|
2 0 * * * dovecot find /home/vmail/mail/{{ config.mail_domain }} -path '*/tmp/*' -mtime +{{ config.delete_mails_after }} -type f -delete
|
||||||
2 0 * * * dovecot find /home/vmail/mail/{{ config.mail_domain }}/*/.*/tmp -mtime +{{ config.delete_mails_after }} -type f -delete
|
2 0 * * * dovecot find /home/vmail/mail/{{ config.mail_domain }} -path '*/.*/tmp/*' -mtime +{{ config.delete_mails_after }} -type f -delete
|
||||||
|
|||||||
@@ -23,6 +23,31 @@ smtp_tls_CApath=/etc/ssl/certs
|
|||||||
smtp_tls_security_level=may
|
smtp_tls_security_level=may
|
||||||
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
|
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
|
||||||
smtp_tls_policy_maps = socketmap:inet:127.0.0.1:8461:postfix
|
smtp_tls_policy_maps = socketmap:inet:127.0.0.1:8461:postfix
|
||||||
|
smtpd_tls_protocols = >=TLSv1.2
|
||||||
|
|
||||||
|
# Disable anonymous cipher suites
|
||||||
|
# and known insecure algorithms.
|
||||||
|
#
|
||||||
|
# Disabling anonymous ciphers
|
||||||
|
# does not generally improve security
|
||||||
|
# because clients that want to verify certificate
|
||||||
|
# will not select them anyway,
|
||||||
|
# but makes cipher suite list shorter and security scanners happy.
|
||||||
|
# See <https://www.postfix.org/TLS_README.html> for discussion.
|
||||||
|
#
|
||||||
|
# Only ancient insecure ciphers should be disabled here
|
||||||
|
# as MTA clients that do not support more secure cipher
|
||||||
|
# likely do not support MTA-STS either and will
|
||||||
|
# otherwise fall back to using plaintext connection.
|
||||||
|
smtpd_tls_exclude_ciphers = aNULL, RC4, MD5, DES
|
||||||
|
|
||||||
|
# Override client's preference order.
|
||||||
|
# <https://www.postfix.org/postconf.5.html#tls_preempt_cipherlist>
|
||||||
|
#
|
||||||
|
# This is mostly to ensure cipher suites with forward secrecy
|
||||||
|
# are preferred over non cipher suites without forward secrecy.
|
||||||
|
# See <https://www.postfix.org/FORWARD_SECRECY_README.html#server_fs>.
|
||||||
|
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 }}
|
myhostname = {{ config.mail_domain }}
|
||||||
|
|||||||
@@ -136,3 +136,15 @@ def test_hide_senders_ip_address(cmfactory):
|
|||||||
user2.direct_imap.select_folder("Inbox")
|
user2.direct_imap.select_folder("Inbox")
|
||||||
msg = user2.direct_imap.get_all_messages()[0]
|
msg = user2.direct_imap.get_all_messages()[0]
|
||||||
assert public_ip not in msg.obj.as_string()
|
assert public_ip not in msg.obj.as_string()
|
||||||
|
|
||||||
|
|
||||||
|
def test_echobot(cmfactory, chatmail_config, lp):
|
||||||
|
ac = cmfactory.get_online_accounts(1)[0]
|
||||||
|
|
||||||
|
lp.sec(f"Send message to echo@{chatmail_config.mail_domain}")
|
||||||
|
chat = ac.create_chat(f"echo@{chatmail_config.mail_domain}")
|
||||||
|
text = "hi, I hope you text me back"
|
||||||
|
chat.send_text(text)
|
||||||
|
lp.sec("Wait for reply from echobot")
|
||||||
|
reply = ac.wait_next_incoming_message()
|
||||||
|
assert reply.text == text
|
||||||
|
|||||||
Reference in New Issue
Block a user