mirror of
https://github.com/chatmail/relay.git
synced 2026-05-13 09:24:43 +00:00
Compare commits
1 Commits
main
...
turnserver
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
59dceb202d |
@@ -10,7 +10,12 @@ from chatmaild.user import User
|
|||||||
def read_config(inipath):
|
def read_config(inipath):
|
||||||
assert Path(inipath).exists(), inipath
|
assert Path(inipath).exists(), inipath
|
||||||
cfg = iniconfig.IniConfig(inipath)
|
cfg = iniconfig.IniConfig(inipath)
|
||||||
return Config(inipath, params=cfg.sections["params"])
|
params = cfg.sections["params"]
|
||||||
|
default_config_content = get_default_config_content(params["mail_domain"])
|
||||||
|
df_params = iniconfig.IniConfig("ini", data=default_config_content)["params"]
|
||||||
|
new_params = dict(df_params.items())
|
||||||
|
new_params.update(params)
|
||||||
|
return Config(inipath, params=new_params)
|
||||||
|
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
@@ -31,18 +36,16 @@ class Config:
|
|||||||
|
|
||||||
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.get("max_mailbox_size", "500M")
|
self.max_mailbox_size = params["max_mailbox_size"]
|
||||||
self.max_message_size = int(params.get("max_message_size", 31457280))
|
self.max_message_size = int(params.get("max_message_size", "31457280"))
|
||||||
self.delete_mails_after = params.get("delete_mails_after", "20")
|
self.delete_mails_after = params["delete_mails_after"]
|
||||||
self.delete_large_after = params.get("delete_large_after", "7")
|
self.delete_large_after = params["delete_large_after"]
|
||||||
self.delete_inactive_users_after = int(
|
self.delete_inactive_users_after = int(params["delete_inactive_users_after"])
|
||||||
params.get("delete_inactive_users_after", 90)
|
self.username_min_length = int(params["username_min_length"])
|
||||||
)
|
self.username_max_length = int(params["username_max_length"])
|
||||||
self.username_min_length = int(params.get("username_min_length", 9))
|
self.password_min_length = int(params["password_min_length"])
|
||||||
self.username_max_length = int(params.get("username_max_length", 9))
|
self.passthrough_senders = params["passthrough_senders"].split()
|
||||||
self.password_min_length = int(params.get("password_min_length", 9))
|
self.passthrough_recipients = params["passthrough_recipients"].split()
|
||||||
self.passthrough_senders = params.get("passthrough_senders", "").split()
|
|
||||||
self.passthrough_recipients = params.get("passthrough_recipients", "").split()
|
|
||||||
self.www_folder = params.get("www_folder", "")
|
self.www_folder = params.get("www_folder", "")
|
||||||
self.filtermail_smtp_port = int(params.get("filtermail_smtp_port", "10080"))
|
self.filtermail_smtp_port = int(params.get("filtermail_smtp_port", "10080"))
|
||||||
self.filtermail_smtp_port_incoming = int(
|
self.filtermail_smtp_port_incoming = int(
|
||||||
@@ -63,6 +66,7 @@ class Config:
|
|||||||
self.acme_email = params.get("acme_email", "")
|
self.acme_email = params.get("acme_email", "")
|
||||||
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"
|
||||||
|
self.turn_socket_path = params.get("turn_socket_path", "/run/chatmail-turn/turn.socket")
|
||||||
if "iroh_relay" not in params:
|
if "iroh_relay" not in params:
|
||||||
self.iroh_relay = "https://" + raw_domain
|
self.iroh_relay = "https://" + raw_domain
|
||||||
self.enable_iroh_relay = True
|
self.enable_iroh_relay = True
|
||||||
@@ -161,7 +165,31 @@ def get_default_config_content(mail_domain, **overrides):
|
|||||||
for name, value in extra.items():
|
for name, value in extra.items():
|
||||||
new_line = f"{name} = {value}"
|
new_line = f"{name} = {value}"
|
||||||
new_lines.append(new_line)
|
new_lines.append(new_line)
|
||||||
return "\n".join(new_lines)
|
|
||||||
|
content = "\n".join(new_lines)
|
||||||
|
|
||||||
|
# apply testrun privacy overrides
|
||||||
|
|
||||||
|
if mail_domain.endswith(".testrun.org"):
|
||||||
|
override_inipath = inidir.joinpath("override-testrun.ini")
|
||||||
|
privacy = iniconfig.IniConfig(override_inipath)["privacy"]
|
||||||
|
lines = []
|
||||||
|
for line in content.split("\n"):
|
||||||
|
for key, value in privacy.items():
|
||||||
|
value_lines = value.format(mail_domain=mail_domain).strip().split("\n")
|
||||||
|
if not line.startswith(f"{key} =") or not value_lines:
|
||||||
|
continue
|
||||||
|
if len(value_lines) == 1:
|
||||||
|
lines.append(f"{key} = {value}")
|
||||||
|
else:
|
||||||
|
lines.append(f"{key} =")
|
||||||
|
for vl in value_lines:
|
||||||
|
lines.append(f" {vl}")
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
lines.append(line)
|
||||||
|
content = "\n".join(lines)
|
||||||
|
return content
|
||||||
|
|
||||||
|
|
||||||
def is_valid_ipv4(address: str) -> bool:
|
def is_valid_ipv4(address: str) -> bool:
|
||||||
|
|||||||
@@ -12,42 +12,42 @@ mail_domain = {mail_domain}
|
|||||||
#
|
#
|
||||||
|
|
||||||
# email sending rate per user and minute
|
# email sending rate per user and minute
|
||||||
#max_user_send_per_minute = 60
|
max_user_send_per_minute = 60
|
||||||
|
|
||||||
# per-user max burst size for sending rate limiting (GCRA bucket capacity)
|
# per-user max burst size for sending rate limiting (GCRA bucket capacity)
|
||||||
#max_user_send_burst_size = 10
|
max_user_send_burst_size = 10
|
||||||
|
|
||||||
# maximum mailbox size of a chatmail address
|
# maximum mailbox size of a chatmail address
|
||||||
# (Oldest messages will be removed automatically, so mailboxes never run full)
|
# Oldest messages will be removed automatically, so mailboxes never run full.
|
||||||
#max_mailbox_size = 500M
|
max_mailbox_size = 500M
|
||||||
|
|
||||||
# maximum message size for an e-mail in bytes
|
# maximum message size for an e-mail in bytes
|
||||||
#max_message_size = 31457280
|
max_message_size = 31457280
|
||||||
|
|
||||||
# days after which mails are unconditionally deleted
|
# days after which mails are unconditionally deleted
|
||||||
#delete_mails_after = 20
|
delete_mails_after = 20
|
||||||
|
|
||||||
# days after which large messages (>200k) are unconditionally deleted
|
# days after which large messages (>200k) are unconditionally deleted
|
||||||
#delete_large_after = 7
|
delete_large_after = 7
|
||||||
|
|
||||||
# days after which users without a successful login are deleted (database and mails)
|
# days after which users without a successful login are deleted (database and mails)
|
||||||
#delete_inactive_users_after = 90
|
delete_inactive_users_after = 90
|
||||||
|
|
||||||
# minimum length a username must have
|
# minimum length a username must have
|
||||||
#username_min_length = 9
|
username_min_length = 9
|
||||||
|
|
||||||
# maximum length a username can have
|
# maximum length a username can have
|
||||||
#username_max_length = 9
|
username_max_length = 9
|
||||||
|
|
||||||
# minimum length a password must have
|
# minimum length a password must have
|
||||||
#password_min_length = 9
|
password_min_length = 9
|
||||||
|
|
||||||
# list of chatmail addresses which can send outbound un-encrypted mail
|
# list of chatmail addresses which can send outbound un-encrypted mail
|
||||||
#passthrough_senders =
|
passthrough_senders =
|
||||||
|
|
||||||
# list of e-mail recipients for which to accept outbound un-encrypted mails
|
# list of e-mail recipients for which to accept outbound un-encrypted mails
|
||||||
# (space-separated, item may start with "@" to whitelist whole recipient domains)
|
# (space-separated, item may start with "@" to whitelist whole recipient domains)
|
||||||
#passthrough_recipients =
|
passthrough_recipients =
|
||||||
|
|
||||||
# Use externally managed TLS certificates instead of built-in acmetool.
|
# Use externally managed TLS certificates instead of built-in acmetool.
|
||||||
# Paths refer to files on the deployment server (not the build machine).
|
# Paths refer to files on the deployment server (not the build machine).
|
||||||
@@ -63,11 +63,22 @@ mail_domain = {mail_domain}
|
|||||||
# Deployment Details
|
# Deployment Details
|
||||||
#
|
#
|
||||||
|
|
||||||
|
# Path to the TURN server Unix socket
|
||||||
|
turn_socket_path = /run/chatmail-turn/turn.socket
|
||||||
|
|
||||||
|
# SMTP outgoing filtermail and reinjection
|
||||||
|
filtermail_smtp_port = 10080
|
||||||
|
postfix_reinject_port = 10025
|
||||||
|
|
||||||
|
# SMTP incoming filtermail and reinjection
|
||||||
|
filtermail_smtp_port_incoming = 10081
|
||||||
|
postfix_reinject_port_incoming = 10026
|
||||||
|
|
||||||
# if set to "True" IPv6 is disabled
|
# if set to "True" IPv6 is disabled
|
||||||
#disable_ipv6 = False
|
disable_ipv6 = False
|
||||||
|
|
||||||
# Your email adress, which will be used in acmetool to manage Let's Encrypt SSL certificates
|
# Your email adress, which will be used in acmetool to manage Let's Encrypt SSL certificates
|
||||||
#acme_email =
|
acme_email =
|
||||||
|
|
||||||
# Defaults to https://iroh.{{mail_domain}} and running `iroh-relay` on the chatmail
|
# Defaults to https://iroh.{{mail_domain}} and running `iroh-relay` on the chatmail
|
||||||
# service.
|
# service.
|
||||||
@@ -100,13 +111,13 @@ mail_domain = {mail_domain}
|
|||||||
# in per-maildir ".in/.out" files.
|
# in per-maildir ".in/.out" files.
|
||||||
# Note that you need to manually cleanup these files
|
# Note that you need to manually cleanup these files
|
||||||
# so use this option with caution on production servers.
|
# so use this option with caution on production servers.
|
||||||
#imap_rawlog = false
|
imap_rawlog = false
|
||||||
|
|
||||||
# set to true if you want to enable the IMAP COMPRESS Extension,
|
# set to true if you want to enable the IMAP COMPRESS Extension,
|
||||||
# which allows IMAP connections to be efficiently compressed.
|
# which allows IMAP connections to be efficiently compressed.
|
||||||
# WARNING: Enabling this makes it impossible to hibernate IMAP
|
# WARNING: Enabling this makes it impossible to hibernate IMAP
|
||||||
# processes which will result in much higher memory/RAM usage.
|
# processes which will result in much higher memory/RAM usage.
|
||||||
#imap_compress = false
|
imap_compress = false
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|||||||
16
chatmaild/src/chatmaild/ini/override-testrun.ini
Normal file
16
chatmaild/src/chatmaild/ini/override-testrun.ini
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
[privacy]
|
||||||
|
|
||||||
|
passthrough_recipients = privacy@testrun.org echo@{mail_domain}
|
||||||
|
|
||||||
|
privacy_postal =
|
||||||
|
Merlinux GmbH, Represented by the managing director H. Krekel,
|
||||||
|
Reichgrafen Str. 20, 79102 Freiburg, Germany
|
||||||
|
|
||||||
|
privacy_mail = privacy@testrun.org
|
||||||
|
privacy_pdo =
|
||||||
|
Prof. Dr. Fabian Schmieder, lexICT UG (limited), Ostfeldstr. 49, 30559 Hannover.
|
||||||
|
You can contact him at *delta-privacy@merlinux.eu* (Keyword: DPO)
|
||||||
|
privacy_supervisor =
|
||||||
|
State Commissioner for Data Protection and Freedom of Information of
|
||||||
|
Baden-Württemberg in 70173 Stuttgart, Germany.
|
||||||
@@ -79,12 +79,13 @@ class Metadata:
|
|||||||
|
|
||||||
|
|
||||||
class MetadataDictProxy(DictProxy):
|
class MetadataDictProxy(DictProxy):
|
||||||
def __init__(self, notifier, metadata, iroh_relay=None, turn_hostname=None):
|
def __init__(self, notifier, metadata, iroh_relay=None, turn_hostname=None, turn_socket_path=None):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.notifier = notifier
|
self.notifier = notifier
|
||||||
self.metadata = metadata
|
self.metadata = metadata
|
||||||
self.iroh_relay = iroh_relay
|
self.iroh_relay = iroh_relay
|
||||||
self.turn_hostname = turn_hostname
|
self.turn_hostname = turn_hostname
|
||||||
|
self.turn_socket_path = turn_socket_path
|
||||||
|
|
||||||
def handle_lookup(self, parts):
|
def handle_lookup(self, parts):
|
||||||
# Lpriv/43f5f508a7ea0366dff30200c15250e3/devicetoken\tlkj123poi@c2.testrun.org
|
# Lpriv/43f5f508a7ea0366dff30200c15250e3/devicetoken\tlkj123poi@c2.testrun.org
|
||||||
@@ -101,7 +102,7 @@ class MetadataDictProxy(DictProxy):
|
|||||||
return f"O{self.iroh_relay}\n"
|
return f"O{self.iroh_relay}\n"
|
||||||
case "turn":
|
case "turn":
|
||||||
try:
|
try:
|
||||||
res = turn_credentials()
|
res = turn_credentials(self.turn_socket_path)
|
||||||
except Exception:
|
except Exception:
|
||||||
logging.exception("failed to get TURN credentials")
|
logging.exception("failed to get TURN credentials")
|
||||||
return "N\n"
|
return "N\n"
|
||||||
@@ -135,6 +136,7 @@ def main():
|
|||||||
config = read_config(config_path)
|
config = read_config(config_path)
|
||||||
iroh_relay = config.iroh_relay
|
iroh_relay = config.iroh_relay
|
||||||
mail_domain = config.mail_domain
|
mail_domain = config.mail_domain
|
||||||
|
socket_path = config.turn_socket_path
|
||||||
|
|
||||||
vmail_dir = config.mailboxes_dir
|
vmail_dir = config.mailboxes_dir
|
||||||
if not vmail_dir.exists():
|
if not vmail_dir.exists():
|
||||||
@@ -152,6 +154,7 @@ def main():
|
|||||||
metadata=metadata,
|
metadata=metadata,
|
||||||
iroh_relay=iroh_relay,
|
iroh_relay=iroh_relay,
|
||||||
turn_hostname=mail_domain,
|
turn_hostname=mail_domain,
|
||||||
|
turn_socket_path=socket_path,
|
||||||
)
|
)
|
||||||
|
|
||||||
dictproxy.serve_forever_from_socket(socket)
|
dictproxy.serve_forever_from_socket(socket)
|
||||||
|
|||||||
@@ -13,12 +13,7 @@ def test_read_config_basic(example_config):
|
|||||||
assert not example_config.privacy_pdo and not example_config.privacy_postal
|
assert not example_config.privacy_pdo and not example_config.privacy_postal
|
||||||
|
|
||||||
inipath = example_config._inipath
|
inipath = example_config._inipath
|
||||||
inipath.write_text(
|
inipath.write_text(inipath.read_text().replace("60", "37"))
|
||||||
inipath.read_text().replace(
|
|
||||||
"#max_user_send_per_minute = 60",
|
|
||||||
"max_user_send_per_minute = 37",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
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"
|
||||||
@@ -36,17 +31,26 @@ def test_read_config_basic_using_defaults(tmp_path, maildomain):
|
|||||||
example_config = read_config(inipath)
|
example_config = read_config(inipath)
|
||||||
assert example_config.max_user_send_per_minute == 60
|
assert example_config.max_user_send_per_minute == 60
|
||||||
assert example_config.filtermail_smtp_port_incoming == 10081
|
assert example_config.filtermail_smtp_port_incoming == 10081
|
||||||
assert example_config.filtermail_smtp_port == 10080
|
|
||||||
assert example_config.postfix_reinject_port == 10025
|
|
||||||
assert example_config.max_user_send_per_minute == 60
|
def test_read_config_testrun(make_config):
|
||||||
assert example_config.max_mailbox_size == "500M"
|
config = make_config("something.testrun.org")
|
||||||
assert example_config.delete_mails_after == "20"
|
assert config.mail_domain == "something.testrun.org"
|
||||||
assert example_config.delete_large_after == "7"
|
assert len(config.privacy_postal.split("\n")) > 1
|
||||||
assert example_config.username_min_length == 9
|
assert len(config.privacy_supervisor.split("\n")) > 1
|
||||||
assert example_config.username_max_length == 9
|
assert len(config.privacy_pdo.split("\n")) > 1
|
||||||
assert example_config.password_min_length == 9
|
assert config.privacy_mail == "privacy@testrun.org"
|
||||||
assert example_config.passthrough_recipients == []
|
assert config.filtermail_smtp_port == 10080
|
||||||
assert example_config.passthrough_senders == []
|
assert config.postfix_reinject_port == 10025
|
||||||
|
assert config.max_user_send_per_minute == 60
|
||||||
|
assert config.max_mailbox_size == "500M"
|
||||||
|
assert config.delete_mails_after == "20"
|
||||||
|
assert config.delete_large_after == "7"
|
||||||
|
assert config.username_min_length == 9
|
||||||
|
assert config.username_max_length == 9
|
||||||
|
assert config.password_min_length == 9
|
||||||
|
assert "privacy@testrun.org" in config.passthrough_recipients
|
||||||
|
assert config.passthrough_senders == []
|
||||||
|
|
||||||
|
|
||||||
def test_config_userstate_paths(make_config, tmp_path):
|
def test_config_userstate_paths(make_config, tmp_path):
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
import socket
|
import socket
|
||||||
|
|
||||||
|
|
||||||
def turn_credentials() -> str:
|
def turn_credentials(turn_socket_path) -> str:
|
||||||
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as client_socket:
|
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as client_socket:
|
||||||
client_socket.settimeout(5)
|
client_socket.settimeout(5)
|
||||||
client_socket.connect("/run/chatmail-turn/turn.socket")
|
client_socket.connect(turn_socket_path)
|
||||||
with client_socket.makefile("rb") as file:
|
with client_socket.makefile("rb") as file:
|
||||||
return file.readline().decode("utf-8").strip()
|
return file.readline().decode("utf-8").strip()
|
||||||
|
|||||||
Reference in New Issue
Block a user