mirror of
https://github.com/chatmail/relay.git
synced 2026-05-12 17:14:36 +00:00
Compare commits
31 Commits
link2xt/no
...
support-se
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3a32817de8 | ||
|
|
c6dd4f9b21 | ||
|
|
a420e37612 | ||
|
|
5429f3e379 | ||
|
|
d2c98e9afc | ||
|
|
658d6923ae | ||
|
|
776bd87888 | ||
|
|
d7683ed3f7 | ||
|
|
0cc9f18468 | ||
|
|
889e18f803 | ||
|
|
773b8d1e00 | ||
|
|
dca6d35a6f | ||
|
|
d29d2d147b | ||
|
|
347dae1f84 | ||
|
|
63cbb83344 | ||
|
|
27d135fee7 | ||
|
|
ccd7c789f0 | ||
|
|
c7625fad81 | ||
|
|
5305dfab12 | ||
|
|
4478270fc9 | ||
|
|
e7c9992fdc | ||
|
|
a9d43c42f4 | ||
|
|
bbf2f0dd36 | ||
|
|
43c02377ef | ||
|
|
70f330b0e4 | ||
|
|
02eaa55441 | ||
|
|
6c3ec903c2 | ||
|
|
7d9b81863f | ||
|
|
af90d0a7de | ||
|
|
322bc9a3aa | ||
|
|
e4009854dc |
@@ -81,10 +81,11 @@ comprised of minimal setups of
|
|||||||
|
|
||||||
as well as two custom services that are integrated with these two:
|
as well as two custom services that are integrated with these two:
|
||||||
|
|
||||||
- `chatmaild/src/chatmaild/dictproxy.py` implements
|
- `chatmaild/src/chatmaild/doveauth.py` implements
|
||||||
create-on-login account creation semantics and is used
|
create-on-login account creation semantics and is used
|
||||||
by Dovecot during login authentication and by Postfix
|
by Dovecot during login authentication and by Postfix
|
||||||
which in turn uses Dovecot SASL to authenticate users
|
which in turn uses [Dovecot SASL](https://doc.dovecot.org/configuration_manual/authentication/dict/#complete-example-for-authenticating-via-a-unix-socket)
|
||||||
|
to authenticate users
|
||||||
to send mails for them.
|
to send mails for them.
|
||||||
|
|
||||||
- `chatmaild/src/chatmaild/filtermail.py` prevents
|
- `chatmaild/src/chatmaild/filtermail.py` prevents
|
||||||
|
|||||||
@@ -10,11 +10,14 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
doveauth-dictproxy = "chatmaild.dictproxy:main"
|
doveauth = "chatmaild.doveauth:main"
|
||||||
filtermail = "chatmaild.filtermail:main"
|
filtermail = "chatmaild.filtermail:main"
|
||||||
|
|
||||||
[tool.pytest.ini_options]
|
[tool.pytest.ini_options]
|
||||||
addopts = "-v -ra --strict-markers"
|
addopts = "-v -ra --strict-markers"
|
||||||
|
log_format = "%(asctime)s %(levelname)s %(message)s"
|
||||||
|
log_date_format = "%Y-%m-%d %H:%M:%S"
|
||||||
|
log_level = "INFO"
|
||||||
|
|
||||||
[tool.tox]
|
[tool.tox]
|
||||||
legacy_tox_ini = """
|
legacy_tox_ini = """
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ def handle_dovecot_request(msg, db, mail_domain):
|
|||||||
|
|
||||||
|
|
||||||
class ThreadedUnixStreamServer(ThreadingMixIn, UnixStreamServer):
|
class ThreadedUnixStreamServer(ThreadingMixIn, UnixStreamServer):
|
||||||
pass
|
request_queue_size = 100
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
@@ -128,14 +128,20 @@ def main():
|
|||||||
|
|
||||||
class Handler(StreamRequestHandler):
|
class Handler(StreamRequestHandler):
|
||||||
def handle(self):
|
def handle(self):
|
||||||
while True:
|
try:
|
||||||
msg = self.rfile.readline().strip().decode()
|
while True:
|
||||||
if not msg:
|
msg = self.rfile.readline().strip().decode()
|
||||||
break
|
if not msg:
|
||||||
res = handle_dovecot_request(msg, db, mail_domain)
|
break
|
||||||
if res:
|
res = handle_dovecot_request(msg, db, mail_domain)
|
||||||
self.wfile.write(res.encode("ascii"))
|
if res:
|
||||||
self.wfile.flush()
|
self.wfile.write(res.encode("ascii"))
|
||||||
|
self.wfile.flush()
|
||||||
|
else:
|
||||||
|
logging.warn("request had no answer: %r", msg)
|
||||||
|
except Exception:
|
||||||
|
logging.exception("Exception in the handler")
|
||||||
|
raise
|
||||||
|
|
||||||
try:
|
try:
|
||||||
os.unlink(socket)
|
os.unlink(socket)
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
Description=Dict authentication proxy for dovecot
|
Description=Dict authentication proxy for dovecot
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
ExecStart=/usr/local/bin/doveauth-dictproxy /run/dovecot/doveauth.socket vmail /home/vmail/passdb.sqlite
|
ExecStart=/usr/local/bin/doveauth /run/dovecot/doveauth.socket vmail /home/vmail/passdb.sqlite
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=30
|
RestartSec=30
|
||||||
|
|
||||||
@@ -149,7 +149,7 @@ class SendRateLimiter:
|
|||||||
def main():
|
def main():
|
||||||
args = sys.argv[1:]
|
args = sys.argv[1:]
|
||||||
assert len(args) == 1
|
assert len(args) == 1
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.WARN)
|
||||||
loop = asyncio.new_event_loop()
|
loop = asyncio.new_event_loop()
|
||||||
asyncio.set_event_loop(loop)
|
asyncio.set_event_loop(loop)
|
||||||
task = asyncmain_beforequeue(port=int(args[0]))
|
task = asyncmain_beforequeue(port=int(args[0]))
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ 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
|
||||||
from pyinfra.facts.files import File
|
from pyinfra.facts.files import File
|
||||||
|
from pyinfra.facts.systemd import SystemdEnabled
|
||||||
from .acmetool import deploy_acmetool
|
from .acmetool import deploy_acmetool
|
||||||
|
|
||||||
|
|
||||||
@@ -24,8 +25,8 @@ def _install_chatmaild() -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
apt.packages(
|
apt.packages(
|
||||||
name="apt install python3-aiosmtpd",
|
name="apt install python3-aiosmtpd python3-pip python3-venv",
|
||||||
packages=["python3-aiosmtpd", "python3-pip"],
|
packages=["python3-aiosmtpd", "python3-pip", "python3-venv"],
|
||||||
)
|
)
|
||||||
|
|
||||||
# --no-deps because aiosmtplib is installed with `apt`.
|
# --no-deps because aiosmtplib is installed with `apt`.
|
||||||
@@ -34,8 +35,17 @@ def _install_chatmaild() -> None:
|
|||||||
commands=[f"pip install --break-system-packages {remote_path}"],
|
commands=[f"pip install --break-system-packages {remote_path}"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# disable legacy doveauth-dictproxy.service
|
||||||
|
if host.get_fact(SystemdEnabled).get("doveauth-dictproxy.service"):
|
||||||
|
systemd.service(
|
||||||
|
name="Disable legacy doveauth-dictproxy.service",
|
||||||
|
service="doveauth-dictproxy.service",
|
||||||
|
running=False,
|
||||||
|
enabled=False,
|
||||||
|
)
|
||||||
|
|
||||||
for fn in (
|
for fn in (
|
||||||
"doveauth-dictproxy",
|
"doveauth",
|
||||||
"filtermail",
|
"filtermail",
|
||||||
):
|
):
|
||||||
files.put(
|
files.put(
|
||||||
@@ -123,6 +133,44 @@ def _configure_opendkim(domain: str, dkim_selector: str) -> bool:
|
|||||||
return need_restart
|
return need_restart
|
||||||
|
|
||||||
|
|
||||||
|
def _install_mta_sts_daemon() -> bool:
|
||||||
|
need_restart = False
|
||||||
|
|
||||||
|
config = files.put(
|
||||||
|
name="upload postfix-mta-sts-resolver config",
|
||||||
|
src=importlib.resources.files(__package__).joinpath(
|
||||||
|
"postfix/mta-sts-daemon.yml"
|
||||||
|
),
|
||||||
|
dest="/etc/mta-sts-daemon.yml",
|
||||||
|
user="root",
|
||||||
|
group="root",
|
||||||
|
mode="644",
|
||||||
|
)
|
||||||
|
need_restart |= config.changed
|
||||||
|
|
||||||
|
server.shell(
|
||||||
|
name="install postfix-mta-sts-resolver with pip",
|
||||||
|
commands=[
|
||||||
|
"python3 -m venv /usr/local/lib/postfix-mta-sts-resolver",
|
||||||
|
"/usr/local/lib/postfix-mta-sts-resolver/bin/pip install postfix-mta-sts-resolver",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
systemd_unit = files.put(
|
||||||
|
name="upload mta-sts-daemon systemd unit",
|
||||||
|
src=importlib.resources.files(__package__).joinpath(
|
||||||
|
"postfix/mta-sts-daemon.service"
|
||||||
|
),
|
||||||
|
dest="/etc/systemd/system/mta-sts-daemon.service",
|
||||||
|
user="root",
|
||||||
|
group="root",
|
||||||
|
mode="644",
|
||||||
|
)
|
||||||
|
need_restart |= systemd_unit.changed
|
||||||
|
|
||||||
|
return need_restart
|
||||||
|
|
||||||
|
|
||||||
def _configure_postfix(domain: str, debug: bool = False) -> bool:
|
def _configure_postfix(domain: str, debug: bool = False) -> bool:
|
||||||
"""Configures Postfix SMTP server."""
|
"""Configures Postfix SMTP server."""
|
||||||
need_restart = False
|
need_restart = False
|
||||||
@@ -197,7 +245,7 @@ def _configure_dovecot(mail_server: str, debug: bool = False) -> bool:
|
|||||||
return need_restart
|
return need_restart
|
||||||
|
|
||||||
|
|
||||||
def _configure_nginx(domain: str, debug: bool = False) -> bool:
|
def _configure_nginx(domain: str, mail_server: str) -> bool:
|
||||||
"""Configures nginx HTTP server."""
|
"""Configures nginx HTTP server."""
|
||||||
need_restart = False
|
need_restart = False
|
||||||
|
|
||||||
@@ -221,6 +269,16 @@ def _configure_nginx(domain: str, debug: bool = False) -> bool:
|
|||||||
)
|
)
|
||||||
need_restart |= autoconfig.changed
|
need_restart |= autoconfig.changed
|
||||||
|
|
||||||
|
mta_sts_config = files.template(
|
||||||
|
src=importlib.resources.files(__package__).joinpath("nginx/mta-sts.txt.j2"),
|
||||||
|
dest="/var/www/html/.well-known/mta-sts.txt",
|
||||||
|
user="root",
|
||||||
|
group="root",
|
||||||
|
mode="644",
|
||||||
|
config={"mail_server": mail_server},
|
||||||
|
)
|
||||||
|
need_restart |= mta_sts_config.changed
|
||||||
|
|
||||||
return need_restart
|
return need_restart
|
||||||
|
|
||||||
|
|
||||||
@@ -245,7 +303,7 @@ def deploy_chatmail(mail_domain: str, mail_server: str, dkim_selector: str) -> N
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Deploy acmetool to have TLS certificates.
|
# Deploy acmetool to have TLS certificates.
|
||||||
deploy_acmetool(nginx_hook=True, domains=[mail_server])
|
deploy_acmetool(nginx_hook=True, domains=[mail_server, f"mta-sts.{mail_server}"])
|
||||||
|
|
||||||
apt.packages(
|
apt.packages(
|
||||||
name="Install Postfix",
|
name="Install Postfix",
|
||||||
@@ -275,7 +333,8 @@ def deploy_chatmail(mail_domain: str, mail_server: str, dkim_selector: str) -> N
|
|||||||
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)
|
||||||
opendkim_need_restart = _configure_opendkim(mail_domain, dkim_selector)
|
opendkim_need_restart = _configure_opendkim(mail_domain, dkim_selector)
|
||||||
nginx_need_restart = _configure_nginx(mail_domain)
|
nginx_need_restart = _configure_nginx(mail_domain, mail_server)
|
||||||
|
mta_sts_need_restart = _install_mta_sts_daemon()
|
||||||
|
|
||||||
# deploy web pages and info if we have them
|
# deploy web pages and info if we have them
|
||||||
pkg_root = importlib.resources.files(__package__)
|
pkg_root = importlib.resources.files(__package__)
|
||||||
@@ -291,6 +350,15 @@ def deploy_chatmail(mail_domain: str, mail_server: str, dkim_selector: str) -> N
|
|||||||
restarted=opendkim_need_restart,
|
restarted=opendkim_need_restart,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
systemd.service(
|
||||||
|
name="Start and enable MTA-STS daemon",
|
||||||
|
service="mta-sts-daemon.service",
|
||||||
|
daemon_reload=True,
|
||||||
|
running=True,
|
||||||
|
enabled=True,
|
||||||
|
restarted=mta_sts_need_restart,
|
||||||
|
)
|
||||||
|
|
||||||
systemd.service(
|
systemd.service(
|
||||||
name="Start and enable Postfix",
|
name="Start and enable Postfix",
|
||||||
service="postfix.service",
|
service="postfix.service",
|
||||||
|
|||||||
@@ -46,8 +46,7 @@ def deploy_acmetool(nginx_hook=False, email="", domains=[]):
|
|||||||
mode="644",
|
mode="644",
|
||||||
)
|
)
|
||||||
|
|
||||||
for domain in domains:
|
server.shell(
|
||||||
server.shell(
|
name=f"Request certificate for: { ', '.join(domains) }",
|
||||||
name=f"Request certificate for {domain}",
|
commands=[f"acmetool want { ' '.join(domains)}"],
|
||||||
commands=[f"acmetool want {domain}"],
|
)
|
||||||
)
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ mail_plugins = quota
|
|||||||
# these are the capabilities Delta Chat cares about actually
|
# these are the capabilities Delta Chat cares about actually
|
||||||
# so let's keep the network overhead per login small
|
# so let's keep the network overhead per login small
|
||||||
# https://github.com/deltachat/deltachat-core-rust/blob/master/src/imap/capabilities.rs
|
# https://github.com/deltachat/deltachat-core-rust/blob/master/src/imap/capabilities.rs
|
||||||
imap_capability = IMAP4rev1 IDLE MOVE QUOTA CONDSTORE
|
imap_capability = IMAP4rev1 IDLE MOVE QUOTA CONDSTORE NOTIFY
|
||||||
|
|
||||||
|
|
||||||
# Authentication for system users.
|
# Authentication for system users.
|
||||||
|
|||||||
4
deploy-chatmail/src/deploy_chatmail/nginx/mta-sts.txt.j2
Normal file
4
deploy-chatmail/src/deploy_chatmail/nginx/mta-sts.txt.j2
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
version: STSv1
|
||||||
|
mode: enforce
|
||||||
|
mx: {{ config.mail_server }}
|
||||||
|
max_age: 2419200
|
||||||
@@ -20,8 +20,6 @@ http {
|
|||||||
|
|
||||||
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
|
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
|
||||||
ssl_prefer_server_ciphers on;
|
ssl_prefer_server_ciphers on;
|
||||||
ssl_certificate /var/lib/acme/live/{{ config.domain_name }}/fullchain;
|
|
||||||
ssl_certificate_key /var/lib/acme/live/{{ config.domain_name }}/privkey;
|
|
||||||
|
|
||||||
gzip on;
|
gzip on;
|
||||||
|
|
||||||
@@ -30,6 +28,8 @@ http {
|
|||||||
listen [::]:80 default_server;
|
listen [::]:80 default_server;
|
||||||
listen 443 ssl default_server;
|
listen 443 ssl default_server;
|
||||||
listen [::]:443 ssl default_server;
|
listen [::]:443 ssl default_server;
|
||||||
|
ssl_certificate /var/lib/acme/live/{{ config.domain_name }}/fullchain;
|
||||||
|
ssl_certificate_key /var/lib/acme/live/{{ config.domain_name }}/privkey;
|
||||||
|
|
||||||
root /var/www/html;
|
root /var/www/html;
|
||||||
|
|
||||||
@@ -37,6 +37,28 @@ http {
|
|||||||
|
|
||||||
server_name _;
|
server_name _;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
# First attempt to serve request as file, then
|
||||||
|
# as directory, then fall back to displaying a 404.
|
||||||
|
try_files $uri $uri/ =404;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
listen [::]:80;
|
||||||
|
listen 443 ssl;
|
||||||
|
listen [::]:443 ssl;
|
||||||
|
|
||||||
|
root /var/www/html;
|
||||||
|
|
||||||
|
index index.html index.htm;
|
||||||
|
|
||||||
|
server_name mta-sts.{{ config.domain_name }};
|
||||||
|
|
||||||
|
ssl_certificate /var/lib/acme/live/mta-sts.{{ config.domain_name }}/fullchain;
|
||||||
|
ssl_certificate_key /var/lib/acme/live/mta-sts.{{ config.domain_name }}/privkey;
|
||||||
|
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
# First attempt to serve request as file, then
|
# First attempt to serve request as file, then
|
||||||
# as directory, then fall back to displaying a 404.
|
# as directory, then fall back to displaying a 404.
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ smtpd_tls_security_level=may
|
|||||||
smtp_tls_CApath=/etc/ssl/certs
|
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
|
||||||
|
|
||||||
smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination
|
smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination
|
||||||
myhostname = {{ config.domain_name }}
|
myhostname = {{ config.domain_name }}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ submission inet n - y - - smtpd
|
|||||||
-o smtpd_recipient_restrictions=
|
-o smtpd_recipient_restrictions=
|
||||||
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
|
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
|
||||||
-o milter_macro_daemon_name=ORIGINATING
|
-o milter_macro_daemon_name=ORIGINATING
|
||||||
|
-o smtpd_client_connection_count_limit=1000
|
||||||
-o smtpd_proxy_filter=127.0.0.1:10080
|
-o smtpd_proxy_filter=127.0.0.1:10080
|
||||||
smtps inet n - y - - smtpd
|
smtps inet n - y - - smtpd
|
||||||
-o syslog_name=postfix/smtps
|
-o syslog_name=postfix/smtps
|
||||||
@@ -46,6 +47,7 @@ smtps inet n - y - - smtpd
|
|||||||
-o smtpd_sender_restrictions=$mua_sender_restrictions
|
-o smtpd_sender_restrictions=$mua_sender_restrictions
|
||||||
-o smtpd_recipient_restrictions=
|
-o smtpd_recipient_restrictions=
|
||||||
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
|
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
|
||||||
|
-o smtpd_client_connection_count_limit=1000
|
||||||
-o milter_macro_daemon_name=ORIGINATING
|
-o milter_macro_daemon_name=ORIGINATING
|
||||||
-o smtpd_proxy_filter=127.0.0.1:10080
|
-o smtpd_proxy_filter=127.0.0.1:10080
|
||||||
#628 inet n - y - - qmqpd
|
#628 inet n - y - - qmqpd
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Postfix MTA-STS resolver daemon
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=/usr/local/lib/postfix-mta-sts-resolver/bin/mta-sts-daemon
|
||||||
|
Restart=always
|
||||||
|
RestartSec=30
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
host: 127.0.0.1
|
||||||
|
port: 8461
|
||||||
|
reuse_port: true
|
||||||
|
shutdown_timeout: 20
|
||||||
|
cache:
|
||||||
|
type: internal
|
||||||
|
options:
|
||||||
|
cache_size: 10000
|
||||||
|
proactive_policy_fetching:
|
||||||
|
enabled: true
|
||||||
|
default_zone:
|
||||||
|
strict_testing: false
|
||||||
|
timeout: 4
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
: ${CHATMAIL_DOMAIN:=c1.testrun.org}
|
: ${CHATMAIL_DOMAIN:=c1.testrun.org}
|
||||||
|
: ${CHATMAIL_SERVER:=$CHATMAIL_DOMAIN}
|
||||||
: ${CHATMAIL_SSH:=$CHATMAIL_DOMAIN}
|
: ${CHATMAIL_SSH:=$CHATMAIL_DOMAIN}
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
@@ -8,13 +9,22 @@ EMAIL="root@$CHATMAIL_DOMAIN"
|
|||||||
ACME_ACCOUNT_URL="$($SSH -- acmetool account-url)"
|
ACME_ACCOUNT_URL="$($SSH -- acmetool account-url)"
|
||||||
|
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
$CHATMAIL_DOMAIN. MX 10 $CHATMAIL_DOMAIN.
|
$CHATMAIL_DOMAIN. MX 10 $CHATMAIL_SERVER.
|
||||||
$CHATMAIL_DOMAIN. TXT "v=spf1 a:$CHATMAIL_DOMAIN -all"
|
$CHATMAIL_DOMAIN. TXT "v=spf1 a:$CHATMAIL_SERVER -all"
|
||||||
_dmarc.$CHATMAIL_DOMAIN. TXT "v=DMARC1;p=reject;rua=mailto:$EMAIL;ruf=mailto:$EMAIL;fo=1;adkim=r;aspf=r"
|
_dmarc.$CHATMAIL_DOMAIN. TXT "v=DMARC1;p=reject;rua=mailto:$EMAIL;ruf=mailto:$EMAIL;fo=1;adkim=r;aspf=r"
|
||||||
_submission._tcp.$CHATMAIL_DOMAIN. SRV 0 1 587 $CHATMAIL_DOMAIN.
|
_submission._tcp.$CHATMAIL_SERVER. SRV 0 1 587 $CHATMAIL_SERVER.
|
||||||
_submissions._tcp.$CHATMAIL_DOMAIN. SRV 0 1 465 $CHATMAIL_DOMAIN.
|
_submissions._tcp.$CHATMAIL_SERVER. SRV 0 1 465 $CHATMAIL_SERVER.
|
||||||
_imap._tcp.$CHATMAIL_DOMAIN. SRV 0 1 143 $CHATMAIL_DOMAIN.
|
_imap._tcp.$CHATMAIL_SERVER. SRV 0 1 143 $CHATMAIL_SERVER.
|
||||||
_imaps._tcp.$CHATMAIL_DOMAIN. SRV 0 1 993 $CHATMAIL_DOMAIN.
|
_imaps._tcp.$CHATMAIL_SERVER. SRV 0 1 993 $CHATMAIL_SERVER.
|
||||||
$CHATMAIL_DOMAIN. IN CAA 0 issue "letsencrypt.org; accounturi=$ACME_ACCOUNT_URL"
|
$CHATMAIL_DOMAIN. IN CAA 128 issue "letsencrypt.org;accounturi=$ACME_ACCOUNT_URL"
|
||||||
|
_mta-sts.$CHATMAIL_DOMAIN. IN TXT "v=STSv1; id=$(date -u '+%Y%m%d%H%M')"
|
||||||
|
mta-sts.$CHATMAIL_SERVER. IN CNAME $CHATMAIL_SERVER.
|
||||||
|
_smtp._tls.$CHATMAIL_SERVER. IN TXT "v=TLSRPTv1;rua=mailto:$EMAIL"
|
||||||
EOF
|
EOF
|
||||||
|
if [ "$CHATMAIL_DOMAIN" != "$CHATMAIL_SERVER" ]; then
|
||||||
|
cat <<EOF
|
||||||
|
mta-sts.$CHATMAIL_DOMAIN. IN CNAME mta-sts.$CHATMAIL_SERVER.
|
||||||
|
_smtp._tls.$CHATMAIL_DOMAIN. IN CNAME _smtp._tls.$CHATMAIL_SERVER.
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
$SSH opendkim-genzone -F | sed 's/^;.*$//;/^$/d'
|
$SSH opendkim-genzone -F | sed 's/^;.*$//;/^$/d'
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import json
|
import json
|
||||||
|
import sys
|
||||||
import pytest
|
import pytest
|
||||||
import threading
|
import threading
|
||||||
import queue
|
import queue
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
import chatmaild.dictproxy
|
import chatmaild.doveauth
|
||||||
from chatmaild.dictproxy import get_user_data, lookup_passdb, handle_dovecot_request
|
from chatmaild.doveauth import get_user_data, lookup_passdb, handle_dovecot_request
|
||||||
from chatmaild.database import Database, DBError
|
from chatmaild.database import Database, DBError
|
||||||
|
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ def test_dont_overwrite_password_on_wrong_login(db):
|
|||||||
def test_nocreate_file(db, monkeypatch, tmpdir):
|
def test_nocreate_file(db, monkeypatch, tmpdir):
|
||||||
p = tmpdir.join("nocreate")
|
p = tmpdir.join("nocreate")
|
||||||
p.write("")
|
p.write("")
|
||||||
monkeypatch.setattr(chatmaild.dictproxy, "NOCREATE_FILE", str(p))
|
monkeypatch.setattr(chatmaild.doveauth, "NOCREATE_FILE", str(p))
|
||||||
lookup_passdb(db, "newuser1@something.org", "zequ0Aimuchoodaechik")
|
lookup_passdb(db, "newuser1@something.org", "zequ0Aimuchoodaechik")
|
||||||
assert not get_user_data(db, "newuser1@something.org")
|
assert not get_user_data(db, "newuser1@something.org")
|
||||||
|
|
||||||
@@ -60,27 +60,31 @@ def test_handle_dovecot_request(db):
|
|||||||
assert userdata["password"].startswith("{SHA512-CRYPT}")
|
assert userdata["password"].startswith("{SHA512-CRYPT}")
|
||||||
|
|
||||||
|
|
||||||
def test_100_concurrent_lookups(db):
|
def test_100_concurrent_lookups_different_accounts(db, gencreds):
|
||||||
num = 100
|
num_threads = 100
|
||||||
dbs = [Database(db.path) for i in range(num)]
|
req_per_thread = 5
|
||||||
print(f"created {num} databases")
|
|
||||||
results = queue.Queue()
|
results = queue.Queue()
|
||||||
|
|
||||||
def lookup(db):
|
def lookup(db):
|
||||||
try:
|
for i in range(req_per_thread):
|
||||||
lookup_passdb(db, "something@c1.testrun.org", "Pieg9aeToe3eghuthe5u")
|
addr, password = gencreds()
|
||||||
except Exception:
|
try:
|
||||||
results.put(traceback.format_exc())
|
lookup_passdb(db, addr, password)
|
||||||
else:
|
except Exception:
|
||||||
results.put(None)
|
results.put(traceback.format_exc())
|
||||||
|
else:
|
||||||
|
results.put(None)
|
||||||
|
|
||||||
threads = [threading.Thread(target=lookup, args=(db,), daemon=True) for db in dbs]
|
threads = []
|
||||||
|
for i in range(num_threads):
|
||||||
|
thread = threading.Thread(target=lookup, args=(db,), daemon=True)
|
||||||
|
threads.append(thread)
|
||||||
|
|
||||||
print(f"created {num} threads, starting them and waiting for results")
|
print(f"created {num_threads} threads, starting them and waiting for results")
|
||||||
for thread in threads:
|
for thread in threads:
|
||||||
thread.start()
|
thread.start()
|
||||||
|
|
||||||
for _ in dbs:
|
for i in range(num_threads * req_per_thread):
|
||||||
res = results.get()
|
res = results.get()
|
||||||
if res is not None:
|
if res is not None:
|
||||||
pytest.fail(f"concurrent lookup failed\n{res}")
|
pytest.fail(f"concurrent lookup failed\n{res}")
|
||||||
@@ -20,15 +20,14 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 9px;
|
padding: 9px;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
font-family: "Courier New", monospace;
|
font-family: "Swansea", "Helvetica", sans-serif;
|
||||||
color: white;
|
color: black;
|
||||||
background-position: left top;
|
}
|
||||||
background-image: url(collage-bg.png);
|
a {
|
||||||
background-repeat: no-repeat;
|
color: black;
|
||||||
background-size: 100% 100%;
|
|
||||||
}
|
}
|
||||||
h1, h2, h3 {
|
h1, h2, h3 {
|
||||||
font-size: 16px;
|
font-size: 18px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -37,24 +36,39 @@
|
|||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<img class="section" src="collage-top.png" />
|
<img class="section" src="collage-top.png" />
|
||||||
<div class="section text">
|
<div class="section text">
|
||||||
<h1>welcome to nine.testrun.org</h1>
|
<h1>Dear Delta Chat users and newcomers,</h1>
|
||||||
<p>
|
<p>
|
||||||
to get an account,
|
welcome to the first public "chat-mail instance",
|
||||||
invent a word with <i>exactly</i> nine characters
|
a small and lean e-mail provider for smooth chatting.
|
||||||
and append @nine.testrun.org to it.
|
Install Delta Chat or add an account:
|
||||||
eg. <b>hellofits@nine.testrun.org</b>
|
<ul>
|
||||||
|
<li>Tap "LOG INTO YOUR E-MAIL ACCOUNT".</li>
|
||||||
|
<li>Address: invent a word with <i>exactly</i> nine characters
|
||||||
|
and append @nine.testrun.org to it.</li>
|
||||||
|
<li>Password: invent at least 10 characters. The first login sets your password.</li>
|
||||||
|
</ul>
|
||||||
|
If the e-mail address is not yet taken, you'll get that account.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
if the email address is not yet taken, you'll get that account.
|
<img class="section" src="collage-down.png" />
|
||||||
the first login sets your password.
|
|
||||||
that's it.
|
<h2>What's behind it, how does it operate?</h2>
|
||||||
</p>
|
<p>nine.testrun.org is run
|
||||||
</div>
|
by a small group of devs and sysadmins, reachable via root@.
|
||||||
<img class="section" src="collage-down.png" />
|
They want to keep this instance running at least until end 2024.
|
||||||
<div class="section text">
|
Current limits:
|
||||||
<h1>faq</h1>
|
<ul>
|
||||||
<p><i>why are other email providers 1000 times more complicated?</i></p>
|
<li>Un-encrypted mails can not leave the chat-mail instance.</li>
|
||||||
<p>because they want to for $reasons</p>
|
<li>Use <a href="https://delta.chat/en/help#howtoe2ee">
|
||||||
|
guaranteed end-to-end encryption via QR code scans</a>
|
||||||
|
to setup contact with users outside of the chat-mail instance.
|
||||||
|
</li>
|
||||||
|
<li>You may send up to 60 messages per minute.</li>
|
||||||
|
<li>Messages are unconditionally removed 40 days after arrival.</li>
|
||||||
|
<li>Max storage per user is 100MB.</li>
|
||||||
|
</ul>
|
||||||
|
<h2>Why are other email providers 1000 times more complicated?</h2>
|
||||||
|
<p>¯\_(ツ)_/¯</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Reference in New Issue
Block a user