From 76adaa1e208a54be38d5f43d9c2de5df37255e29 Mon Sep 17 00:00:00 2001 From: Jens Nolte <git@queezle.net> Date: Wed, 17 Nov 2021 21:26:19 +0100 Subject: [PATCH] Add coturn server to matrix-homeserver --- modules/matrix-homeserver/coturn.nix | 170 ++++++++++++++++++++++++++ modules/matrix-homeserver/default.nix | 34 ++++++ 2 files changed, 204 insertions(+) create mode 100644 modules/matrix-homeserver/coturn.nix diff --git a/modules/matrix-homeserver/coturn.nix b/modules/matrix-homeserver/coturn.nix new file mode 100644 index 0000000..d65e8e2 --- /dev/null +++ b/modules/matrix-homeserver/coturn.nix @@ -0,0 +1,170 @@ +{ config, pkgs, lib, ... }: +with lib; + +let + cfg = config.queezle.matrix-homeserver; + staticAuthSecretPath = cfg.coturn.authSecretPath; + synapseConfigPath = "/var/lib/matrix-synapse/coturn-secret.yaml"; + configFile = pkgs.writeText "coturn.config" '' + # static-auth-secret is appended to config when service is started + use-auth-secret + realm=${cfg.turnRealm} + + # Log to syslog + no-stdout-log + syslog + verbose + + pidfile /run/coturn/turnserver.pid + + # Hide version + no-software-attribute + + # Only allow encrypted client connections + no-udp + no-tcp + + no-cli + no-tcp-relay + + # Modern crypto / prevent TLS downgrade to 1.0 + no-tlsv1 + no-tlsv1_1 + + secure-stun + + no-multicast-peers + denied-peer-ip=0.0.0.0-0.255.255.255 + denied-peer-ip=10.0.0.0-10.255.255.255 + denied-peer-ip=100.64.0.0-100.127.255.255 + denied-peer-ip=169.254.0.0-169.254.255.255 + denied-peer-ip=172.16.0.0-172.31.255.255 + denied-peer-ip=192.0.0.0-192.0.0.255 + denied-peer-ip=192.0.2.0-192.0.2.255 + denied-peer-ip=192.88.99.0-192.88.99.255 + denied-peer-ip=192.168.0.0-192.168.255.255 + denied-peer-ip=198.18.0.0-198.19.255.255 + denied-peer-ip=198.51.100.0-198.51.100.255 + denied-peer-ip=203.0.113.0-203.0.113.255 + denied-peer-ip=240.0.0.0-255.255.255.255 + denied-peer-ip=64:ff9b::-64:ff9b::ffff:ffff + denied-peer-ip=::ffff:0.0.0.0-::ffff:255.255.255.255 + denied-peer-ip=100::-100::ffff:ffff:ffff:ffff + denied-peer-ip=2001::-2001:1ff:ffff:ffff:ffff:ffff:ffff:ffff + denied-peer-ip=2002::-2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff + denied-peer-ip=fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff + denied-peer-ip=fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff + ''; +in { + config = mkIf (cfg.enable && cfg.coturn.enable) { + assertions = [ + { + assertion = !config.services.coturn.enable; + message = "Cannot use services.coturn.enable and queezle.matrix-homeserver.coturn.enable at the same time."; + } + ]; + + queezle.matrix-homeserver = { + extraConfigFiles."turn-secret" = "/var/lib/matrix-homeserver/turn-secret.yaml"; + settings = { + turn_uris = [ + "turns:${cfg.turnRealm}?transport=udp" + "turns:${cfg.turnRealm}?transport=tcp" + ]; + # One day token lifetime + turn_user_lifetime = 86400000; + }; + }; + + networking.firewall = { + # Default TLS TURN listener + allowedTCPPorts = [ 5349 5350 ]; + # Default DTLS TURN listener + allowedUDPPorts = [ 5349 5350 ]; + # Default UDP TURN relay port range + allowedUDPPortRanges = [ + { + from = 49152; + to = 65535; + } + ]; + }; + + + systemd.services.coturn-generate = { + description = "generate shared secret for coturn and synapse"; + before = [ "matrix-synapse.service" ]; + wantedBy = [ "matrix-synapse.service" ]; + + serviceConfig = { + Type = "oneshot"; + ExecStart = pkgs.writeScript "coturn-generate" '' + #!${pkgs.zsh}/bin/zsh + set -eu + + umask u=rwx,go= + + if [[ ! -e ${staticAuthSecretPath} ]] { + ${pkgs.pwgen}/bin/pwgen -s 64 > '${staticAuthSecretPath}' + } + + > /var/lib/matrix-homeserver/turn-secret.yaml <<- EOF + turn_shared_secret: "$(< ${staticAuthSecretPath})" + EOF + ''; + + StateDirectory = "matrix-homeserver"; + }; + }; + + systemd.services.coturn = { + description = "coturn STUN/TURN server for matrix homeserver"; + + after = [ "network-online.target" ]; + requires = [ "coturn-generate.service" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + Type = "exec"; + ExecStart = + let command = pkgs.writeScriptBin "turnserver" '' + #!${pkgs.zsh}/bin/zsh + set -eu + + readonly config_file=$RUNTIME_DIRECTORY/turnserver.conf + + < "${configFile}" > $config_file + echo "static-auth-secret $(< $CREDENTIALS_DIRECTORY/staticAuthSecret)" >> $config_file + + exec ${cfg.coturn.package}/bin/turnserver \ + -c $config_file \ + --cert $CREDENTIALS_DIRECTORY/cert \ + --pkey $CREDENTIALS_DIRECTORY/pkey + ''; + in "${command}/bin/turnserver"; + RuntimeDirectory = "coturn"; + + LoadCredential = [ + "staticAuthSecret:${staticAuthSecretPath}" + "cert:${config.security.acme.certs.${cfg.coturn.useACMEHost}.directory}/cert.pem" + "pkey:${config.security.acme.certs.${cfg.coturn.useACMEHost}.directory}/key.pem" + ]; + + # TODO + #Restart = "on-failure"; + + DynamicUser = true; + User = "coturn"; + Group = "coturn"; + + ProtectHome = true; + ProtectProc = "invisible"; + ProtectKernelTunables = true; + ProtectControlGroups = true; + ProtectKernelLogs = true; + RestrictRealtime = true; + PrivateDevices = true; + }; + }; + }; +} diff --git a/modules/matrix-homeserver/default.nix b/modules/matrix-homeserver/default.nix index 0b66dcf..ee67cc5 100644 --- a/modules/matrix-homeserver/default.nix +++ b/modules/matrix-homeserver/default.nix @@ -11,6 +11,7 @@ in { ./reverse-proxy.nix ./element.nix ./well-known.nix + ./coturn.nix ./heisenbridge.nix ]; @@ -41,6 +42,11 @@ in { default = "element.${cfg.serverName}"; }; + turnRealm = mkOption { + type = types.str; + default = "turn.${cfg.serverName}"; + }; + useACMEHost = mkOption { type = types.str; default = null; @@ -126,6 +132,34 @@ in { }; }; + # Configure a TURN server to run on the same host as synapse. + coturn = { + enable = mkEnableOption "matrix-homeserver TURN server"; + + useACMEHost = mkOption { + type = types.str; + default = cfg.turnRealm; + }; + + authSecretPath = mkOption { + type = types.path; + default = "/var/lib/matrix-homeserver/coturn-static-auth-secret"; + description = '' + File path where the coturn static-auth-secret is stored. The secret will be automatically created. + Ensure the diretory exists and is not publicly readable when changing the path. + ''; + }; + + package = mkOption { + type = types.package; + default = pkgs.coturn; + defaultText = literalExpression "pkgs.coturn"; + description = '' + Overridable attribute of the coturn package to use. + ''; + }; + }; + # Heisenbridge IRC bouncer. Has to run un the same host as synapse. heisenbridge = { enable = mkEnableOption "heisenbridge"; -- GitLab