diff --git a/CHANGELOG.md b/CHANGELOG.md index 24118dda..d7af3644 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## untagged +- Setup TURN server + ([#621](https://github.com/chatmail/relay/pull/621)) + - Update iroh-relay to 0.35.0 ([#650](https://github.com/chatmail/relay/pull/650)) diff --git a/chatmaild/pyproject.toml b/chatmaild/pyproject.toml index 28dac0ca..8fa212f8 100644 --- a/chatmaild/pyproject.toml +++ b/chatmaild/pyproject.toml @@ -29,6 +29,7 @@ echobot = "chatmaild.echo:main" chatmail-metrics = "chatmaild.metrics:main" delete_inactive_users = "chatmaild.delete_inactive_users:main" lastlogin = "chatmaild.lastlogin:main" +turnserver = "chatmaild.turnserver:main" [project.entry-points.pytest11] "chatmaild.testplugin" = "chatmaild.tests.plugin" diff --git a/chatmaild/src/chatmaild/metadata.py b/chatmaild/src/chatmaild/metadata.py index 02ba24e8..df835062 100644 --- a/chatmaild/src/chatmaild/metadata.py +++ b/chatmaild/src/chatmaild/metadata.py @@ -7,6 +7,7 @@ from .config import read_config from .dictproxy import DictProxy from .filedict import FileDict from .notifier import Notifier +from .turnserver import turn_credentials def _is_valid_token_timestamp(timestamp, now): @@ -75,11 +76,12 @@ class Metadata: class MetadataDictProxy(DictProxy): - def __init__(self, notifier, metadata, iroh_relay=None): + def __init__(self, notifier, metadata, iroh_relay=None, turn_hostname=None): super().__init__() self.notifier = notifier self.metadata = metadata self.iroh_relay = iroh_relay + self.turn_hostname = turn_hostname def handle_lookup(self, parts): # Lpriv/43f5f508a7ea0366dff30200c15250e3/devicetoken\tlkj123poi@c2.testrun.org @@ -98,6 +100,11 @@ class MetadataDictProxy(DictProxy): ): # Handle `GETMETADATA "" /shared/vendor/deltachat/irohrelay` return f"O{self.iroh_relay}\n" + elif keyname == "vendor/vendor.dovecot/pvt/server/vendor/deltachat/turn": + res = turn_credentials() + port = 3478 + return f"O{self.turn_hostname}:{port}:{res}\n" + logging.warning(f"lookup ignored: {parts!r}") return "N\n" @@ -121,6 +128,7 @@ def main(): config = read_config(config_path) iroh_relay = config.iroh_relay + mail_domain = config.mail_domain vmail_dir = config.mailboxes_dir if not vmail_dir.exists(): @@ -134,7 +142,10 @@ def main(): notifier.start_notification_threads(metadata.remove_token_from_addr) dictproxy = MetadataDictProxy( - notifier=notifier, metadata=metadata, iroh_relay=iroh_relay + notifier=notifier, + metadata=metadata, + iroh_relay=iroh_relay, + turn_hostname=mail_domain, ) dictproxy.serve_forever_from_socket(socket) diff --git a/chatmaild/src/chatmaild/turnserver.py b/chatmaild/src/chatmaild/turnserver.py new file mode 100644 index 00000000..d5f4fa82 --- /dev/null +++ b/chatmaild/src/chatmaild/turnserver.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python3 +import socket + + +def turn_credentials() -> str: + with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as client_socket: + client_socket.connect("/run/chatmail-turn/turn.socket") + with client_socket.makefile("rb") as file: + return file.readline().decode("utf-8") diff --git a/cmdeploy/src/cmdeploy/__init__.py b/cmdeploy/src/cmdeploy/__init__.py index ed8db28a..8ad2ea35 100644 --- a/cmdeploy/src/cmdeploy/__init__.py +++ b/cmdeploy/src/cmdeploy/__init__.py @@ -128,6 +128,7 @@ def _install_remote_venv_with_chatmaild(config) -> None: "echobot", "chatmail-metadata", "lastlogin", + "turnserver", ): execpath = fn if fn != "filtermail-incoming" else "filtermail" params = dict( @@ -497,6 +498,56 @@ def check_config(config): return config +def deploy_turn_server(config): + (url, sha256sum) = { + "x86_64": ( + "https://github.com/chatmail/chatmail-turn/releases/download/v0.3/chatmail-turn-x86_64-linux", + "841e527c15fdc2940b0469e206188ea8f0af48533be12ecb8098520f813d41e4", + ), + "aarch64": ( + "https://github.com/chatmail/chatmail-turn/releases/download/v0.3/chatmail-turn-aarch64-linux", + "a5fc2d06d937b56a34e098d2cd72a82d3e89967518d159bf246dc69b65e81b42", + ), + }[host.get_fact(facts.server.Arch)] + + need_restart = False + + existing_sha256sum = host.get_fact(Sha256File, "/usr/local/bin/chatmail-turn") + if existing_sha256sum != sha256sum: + server.shell( + name="Download chatmail-turn", + commands=[ + f"(curl -L {url} >/usr/local/bin/chatmail-turn.new && (echo '{sha256sum} /usr/local/bin/chatmail-turn.new' | sha256sum -c) && mv /usr/local/bin/chatmail-turn.new /usr/local/bin/chatmail-turn)", + "chmod 755 /usr/local/bin/chatmail-turn", + ], + ) + need_restart = True + + source_path = importlib.resources.files(__package__).joinpath( + "service", "turnserver.service.f" + ) + content = source_path.read_text().format(mail_domain=config.mail_domain).encode() + + systemd_unit = files.put( + name="Upload turnserver.service", + src=io.BytesIO(content), + dest="/etc/systemd/system/turnserver.service", + user="root", + group="root", + mode="644", + ) + need_restart |= systemd_unit.changed + + systemd.service( + name="Setup turnserver service", + service="turnserver.service", + running=True, + enabled=True, + restarted=need_restart, + daemon_reload=systemd_unit.changed, + ) + + def deploy_mtail(config): # Uninstall mtail package, we are going to install a static binary. apt.packages(name="Uninstall mtail", packages=["mtail"], present=False) @@ -673,6 +724,8 @@ def deploy_chatmail(config_path: Path, disable_mail: bool) -> None: packages=["rsync"], ) + deploy_turn_server(config) + # Run local DNS resolver `unbound`. # `resolvconf` takes care of setting up /etc/resolv.conf # to use 127.0.0.1 as the resolver. diff --git a/cmdeploy/src/cmdeploy/iroh-relay.toml b/cmdeploy/src/cmdeploy/iroh-relay.toml index 35b2f4ab..0e606ceb 100644 --- a/cmdeploy/src/cmdeploy/iroh-relay.toml +++ b/cmdeploy/src/cmdeploy/iroh-relay.toml @@ -1,5 +1,11 @@ enable_relay = true http_bind_addr = "[::]:3340" -enable_stun = true + +# Disable built-in STUN server in iroh-relay 0.35 +# as we deploy our own TURN server instead. +# STUN server is going to be removed in iroh-relay 1.0 +# and this line can be removed after upgrade. +enable_stun = false + enable_metrics = false metrics_bind_addr = "127.0.0.1:9092" diff --git a/cmdeploy/src/cmdeploy/service/turnserver.service.f b/cmdeploy/src/cmdeploy/service/turnserver.service.f new file mode 100644 index 00000000..f320d78c --- /dev/null +++ b/cmdeploy/src/cmdeploy/service/turnserver.service.f @@ -0,0 +1,16 @@ +[Unit] +Description=A wrapper for the TURN server +After=network.target + +[Service] +Type=simple +Restart=always +ExecStart=/usr/local/bin/chatmail-turn --realm {mail_domain} --socket /run/chatmail-turn/turn.socket + +# Create /run/chatmail-turn +RuntimeDirectory=chatmail-turn +User=vmail +Group=vmail + +[Install] +WantedBy=multi-user.target