mirror of
https://github.com/chatmail/relay.git
synced 2026-05-11 16:34:39 +00:00
Compare commits
7 Commits
wwwdev
...
virtualenv
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b9480bd29 | ||
|
|
59c3730d84 | ||
|
|
84db074686 | ||
|
|
7eec0ab301 | ||
|
|
7cb8f90340 | ||
|
|
32360061b4 | ||
|
|
2055e9f5b8 |
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
<img width="800px" src="www/collage-top.png"/>
|
<img width="800px" src="www/src/collage-top.png"/>
|
||||||
|
|
||||||
# Chatmail instances optimized for Delta Chat apps
|
# Chatmail instances optimized for Delta Chat apps
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Description=Dict authentication proxy for dovecot
|
Description=Dict authentication proxy for dovecot
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
ExecStart=/usr/local/bin/doveauth /run/dovecot/doveauth.socket vmail /home/vmail/passdb.sqlite
|
ExecStart={execpath} /run/dovecot/doveauth.socket vmail /home/vmail/passdb.sqlite
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=30
|
RestartSec=30
|
||||||
|
|
||||||
@@ -34,6 +34,14 @@ def check_encrypted(message):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def is_passthrough_recipient(recipient):
|
||||||
|
"""Check whether a recipient is configured as passthrough."""
|
||||||
|
passthroughlist = ["privacy@testrun.org"]
|
||||||
|
if recipient in passthroughlist:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def check_mdn(message, envelope):
|
def check_mdn(message, envelope):
|
||||||
if len(envelope.rcpt_tos) != 1:
|
if len(envelope.rcpt_tos) != 1:
|
||||||
return False
|
return False
|
||||||
@@ -118,6 +126,9 @@ def check_DATA(envelope):
|
|||||||
if envelope.mail_from == recipient:
|
if envelope.mail_from == recipient:
|
||||||
# Always allow sending emails to self.
|
# Always allow sending emails to self.
|
||||||
continue
|
continue
|
||||||
|
if is_passthrough_recipient(recipient):
|
||||||
|
# Always allow recipients marked as passthrough
|
||||||
|
continue
|
||||||
res = recipient.split("@")
|
res = recipient.split("@")
|
||||||
if len(res) != 2:
|
if len(res) != 2:
|
||||||
return f"500 Invalid address <{recipient}>"
|
return f"500 Invalid address <{recipient}>"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Description=Chatmail Postfix BeforeQeue filter
|
Description=Chatmail Postfix BeforeQeue filter
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
ExecStart=/usr/local/bin/filtermail 10080
|
ExecStart={execpath} 10080
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=30
|
RestartSec=30
|
||||||
|
|
||||||
@@ -1,39 +1,66 @@
|
|||||||
"""
|
"""
|
||||||
Chat Mail pyinfra deploy.
|
Chat Mail pyinfra deploy.
|
||||||
"""
|
"""
|
||||||
|
import sys
|
||||||
import importlib.resources
|
import importlib.resources
|
||||||
|
import subprocess
|
||||||
|
import shutil
|
||||||
|
import io
|
||||||
import configparser
|
import configparser
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from pyinfra import host
|
from pyinfra import host
|
||||||
from pyinfra.operations import apt, files, server, systemd
|
from pyinfra.operations import apt, files, server, systemd, pip
|
||||||
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
|
||||||
|
|
||||||
|
|
||||||
def _install_chatmaild() -> None:
|
def _build_chatmaild(dist_dir) -> None:
|
||||||
chatmaild_filename = "chatmaild-0.1.tar.gz"
|
dist_dir = Path(dist_dir).resolve()
|
||||||
chatmaild_path = importlib.resources.files(__package__).joinpath(
|
if dist_dir.exists():
|
||||||
f"../../../dist/{chatmaild_filename}"
|
shutil.rmtree(dist_dir)
|
||||||
)
|
dist_dir.mkdir()
|
||||||
remote_path = f"/tmp/{chatmaild_filename}"
|
subprocess.check_output(
|
||||||
if Path(str(chatmaild_path)).exists():
|
[sys.executable, "-m", "build", "-n"]
|
||||||
files.put(
|
+ ["--sdist", "chatmaild", "--outdir", str(dist_dir)]
|
||||||
name="Upload chatmaild source package",
|
|
||||||
src=chatmaild_path.open("rb"),
|
|
||||||
dest=remote_path,
|
|
||||||
)
|
)
|
||||||
|
entries = list(dist_dir.iterdir())
|
||||||
|
assert len(entries) == 1
|
||||||
|
return entries[0]
|
||||||
|
|
||||||
|
|
||||||
|
def _install_remote_venv_with_chatmaild() -> None:
|
||||||
|
dist_file = _build_chatmaild(dist_dir=Path("chatmaild/dist"))
|
||||||
|
remote_base_dir = "/usr/local/lib/chatmaild"
|
||||||
|
remote_dist_file = f"{remote_base_dir}/dist/{dist_file.name}"
|
||||||
|
remote_venv_dir = f"{remote_base_dir}/venv"
|
||||||
|
root_owned = dict(user="root", group="root", mode="644")
|
||||||
|
|
||||||
apt.packages(
|
apt.packages(
|
||||||
name="apt install python3-aiosmtpd python3-pip python3-venv",
|
name="apt install python3-virtualenv",
|
||||||
packages=["python3-aiosmtpd", "python3-pip", "python3-venv"],
|
packages=["python3-virtualenv"],
|
||||||
|
)
|
||||||
|
|
||||||
|
files.put(
|
||||||
|
name="Upload chatmaild source package",
|
||||||
|
src=dist_file.open("rb"),
|
||||||
|
dest=remote_dist_file,
|
||||||
|
create_remote_dir=True,
|
||||||
|
**root_owned,
|
||||||
|
)
|
||||||
|
|
||||||
|
pip.virtualenv(
|
||||||
|
name=f"chatmaild virtualenv {remote_venv_dir}",
|
||||||
|
path=remote_venv_dir,
|
||||||
|
always_copy=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
# --no-deps because aiosmtplib is installed with `apt`.
|
|
||||||
server.shell(
|
server.shell(
|
||||||
name="install chatmaild with pip",
|
name=f"forced pip-install {dist_file.name}",
|
||||||
commands=[f"pip install --break-system-packages {remote_path}"],
|
commands=[
|
||||||
|
f"{remote_venv_dir}/bin/pip install --force-reinstall {remote_dist_file}"
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
# disable legacy doveauth-dictproxy.service
|
# disable legacy doveauth-dictproxy.service
|
||||||
@@ -51,15 +78,15 @@ def _install_chatmaild() -> None:
|
|||||||
"doveauth",
|
"doveauth",
|
||||||
"filtermail",
|
"filtermail",
|
||||||
):
|
):
|
||||||
|
execpath = f"{remote_venv_dir}/bin/{fn}"
|
||||||
|
source_path = importlib.resources.files("chatmaild").joinpath(f"{fn}.service.f")
|
||||||
|
content = source_path.read_text().format(execpath=execpath).encode()
|
||||||
|
|
||||||
files.put(
|
files.put(
|
||||||
name=f"Upload {fn}.service",
|
name=f"Upload {fn}.service",
|
||||||
src=importlib.resources.files("chatmaild")
|
src=io.BytesIO(content),
|
||||||
.joinpath(f"{fn}.service")
|
|
||||||
.open("rb"),
|
|
||||||
dest=f"/etc/systemd/system/{fn}.service",
|
dest=f"/etc/systemd/system/{fn}.service",
|
||||||
user="root",
|
**root_owned,
|
||||||
group="root",
|
|
||||||
mode="644",
|
|
||||||
)
|
)
|
||||||
systemd.service(
|
systemd.service(
|
||||||
name=f"Setup {fn} service",
|
name=f"Setup {fn} service",
|
||||||
@@ -381,7 +408,7 @@ def deploy_chatmail(mail_domain: str, mail_server: str, dkim_selector: str) -> N
|
|||||||
build_webpages(src_dir, build_dir, config)
|
build_webpages(src_dir, build_dir, config)
|
||||||
files.rsync(f"{build_dir}/", "/var/www/html", flags=["-avz"])
|
files.rsync(f"{build_dir}/", "/var/www/html", flags=["-avz"])
|
||||||
|
|
||||||
_install_chatmaild()
|
_install_remote_venv_with_chatmaild()
|
||||||
debug = False
|
debug = False
|
||||||
dovecot_need_restart = _configure_dovecot(mail_server, debug=debug)
|
dovecot_need_restart = _configure_dovecot(mail_server, debug=debug)
|
||||||
postfix_need_restart = _configure_postfix(mail_domain, debug=debug)
|
postfix_need_restart = _configure_postfix(mail_domain, debug=debug)
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ def gen_qr(maildomain, url):
|
|||||||
height = size + text_height
|
height = size + text_height
|
||||||
|
|
||||||
image = Image.new("RGBA", (width, height), "white")
|
image = Image.new("RGBA", (width, height), "white")
|
||||||
qr_final_size = width
|
qr_final_size = width - (qr_padding * 2)
|
||||||
|
|
||||||
if num_lines:
|
if num_lines:
|
||||||
draw = ImageDraw.Draw(image)
|
draw = ImageDraw.Draw(image)
|
||||||
|
|||||||
@@ -4,12 +4,5 @@ echo -----------------------------------------
|
|||||||
echo deploying to $CHATMAIL_DOMAIN
|
echo deploying to $CHATMAIL_DOMAIN
|
||||||
echo -----------------------------------------
|
echo -----------------------------------------
|
||||||
|
|
||||||
echo WARNING: in five seconds deploy to $CHATMAIL_DOMAIN starts
|
|
||||||
sleep 5
|
|
||||||
|
|
||||||
venv/bin/python3 -m build -n --sdist chatmaild --outdir dist
|
|
||||||
|
|
||||||
venv/bin/pyinfra --ssh-user root "$CHATMAIL_DOMAIN" \
|
venv/bin/pyinfra --ssh-user root "$CHATMAIL_DOMAIN" \
|
||||||
deploy-chatmail/src/deploy_chatmail/deploy.py
|
deploy-chatmail/src/deploy_chatmail/deploy.py
|
||||||
|
|
||||||
rm -r dist/
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from chatmaild.filtermail import check_encrypted, check_DATA, SendRateLimiter, check_mdn
|
from chatmaild.filtermail import check_encrypted, check_DATA, SendRateLimiter, check_mdn, is_passthrough_recipient
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@@ -80,3 +80,28 @@ def test_send_rate_limiter():
|
|||||||
else:
|
else:
|
||||||
assert i == SendRateLimiter.MAX_USER_SEND_PER_MINUTE + 1
|
assert i == SendRateLimiter.MAX_USER_SEND_PER_MINUTE + 1
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
|
def test_excempt_privacy(maildata, gencreds):
|
||||||
|
from_addr = gencreds()[0]
|
||||||
|
to_addr = "privacy@testrun.org"
|
||||||
|
false_to = "privacy@tstrn.org"
|
||||||
|
false_to2 = "prvcy@testrun.org"
|
||||||
|
assert is_passthrough_recipient(to_addr)
|
||||||
|
|
||||||
|
msg = maildata("plain.eml", from_addr, to_addr)
|
||||||
|
|
||||||
|
class env:
|
||||||
|
mail_from = from_addr
|
||||||
|
rcpt_tos = [to_addr]
|
||||||
|
content = msg.as_bytes()
|
||||||
|
|
||||||
|
# assert that None/no error is returned
|
||||||
|
assert not check_DATA(envelope=env)
|
||||||
|
|
||||||
|
class env2:
|
||||||
|
mail_from = from_addr
|
||||||
|
rcpt_tos = [to_addr, false_to, false_to2]
|
||||||
|
content = msg.as_bytes()
|
||||||
|
|
||||||
|
assert "500" in check_DATA(envelope=env2)
|
||||||
|
|||||||
@@ -283,8 +283,10 @@ def cmfactory(request, gencreds, tmpdir, maildomain):
|
|||||||
pytest.importorskip("deltachat")
|
pytest.importorskip("deltachat")
|
||||||
from deltachat.testplugin import ACFactory
|
from deltachat.testplugin import ACFactory
|
||||||
|
|
||||||
|
data = request.getfixturevalue("data")
|
||||||
|
|
||||||
testproc = ChatmailTestProcess(request.config, maildomain, gencreds)
|
testproc = ChatmailTestProcess(request.config, maildomain, gencreds)
|
||||||
am = ACFactory(request=request, tmpdir=tmpdir, testprocess=testproc, data=None)
|
am = ACFactory(request=request, tmpdir=tmpdir, testprocess=testproc, data=data)
|
||||||
|
|
||||||
# nb. a bit hacky
|
# nb. a bit hacky
|
||||||
# would probably be better if deltachat's test machinery grows native support
|
# would probably be better if deltachat's test machinery grows native support
|
||||||
|
|||||||
Reference in New Issue
Block a user