{ config, pkgs, lib, ... }: with lib; { config = { services.matrix-synapse = { enable = true; enable_metrics = true; enable_registration = true; allow_guest_access = false; server_name = "synapse.li"; listeners = [ { bind_address = "localhost"; port = 8008; resources = [ { names = [ "client" "federation" ]; compress = false; } ]; tls = false; type = "http"; x_forwarded = true; } ]; tls_certificate_path = "/run/credentials/matrix-synapse.service/synapse.li.pem"; tls_private_key_path = "/run/credentials/matrix-synapse.service/synapse.li.key.pem"; tls_dh_params_path = config.security.dhparams.params.matrix-synapse.path; turn_uris = ["turns:turn.synapse.li?transport=udp" "turns:turn.synapse.li?transport=tcp"]; turn_user_lifetime = "1h"; extraConfigFiles = [ "/run/credentials/matrix-synapse.service/registration.yaml" "/run/credentials/matrix-synapse.service/turn-secret.yaml" (pkgs.writeText "homeserver.yaml" (generators.toYAML {} { "refreshable_access_token_lifetime" = "5m"; # "nonrefreshable_access_token_lifetime" = "1w"; # TODO: uncomment once all (relevant) clients have support for refreshable tokens "refresh_token_lifetime" = "1w"; "registration_requires_token" = true; "admin_contact" = "mailto:matrix-admin@yggdrasil.li"; "url_preview_enabled" = true; "url_preview_ip_range_blacklist" = [ "127.0.0.0/8" "10.0.0.0/8" "172.16.0.0/12" "192.168.0.0/16" "100.64.0.0/10" "192.0.0.0/24" "169.254.0.0/16" "192.88.99.0/24" "198.18.0.0/15" "192.0.2.0/24" "198.51.100.0/24" "203.0.113.0/24" "224.0.0.0/4" "::1/128" "fe80::/10" "fc00::/7" "2001:db8::/32" "ff00::/8" "fec0::/10" "2a03:4000:52:ada::/64" ]; "url_preview_ip_range_whitelist" = [ "2a03:4000:52:ada::/128" ]; })) ]; }; sops.secrets."matrix-synapse-registration.yaml" = { format = "binary"; sopsFile = ./registration.yaml; }; sops.secrets."matrix-synapse-turn-secret.yaml" = { format = "binary"; sopsFile = ./coturn-auth-secret.yaml; }; systemd.services.matrix-synapse = { serviceConfig = { LoadCredential = [ "synapse.li.key.pem:${config.security.acme.certs."synapse.li".directory}/key.pem" "synapse.li.pem:${config.security.acme.certs."synapse.li".directory}/fullchain.pem" "registration.yaml:${config.sops.secrets."matrix-synapse-registration.yaml".path}" "turn-secret.yaml:${config.sops.secrets."matrix-synapse-turn-secret.yaml".path}" ]; }; }; services.nginx = { recommendedProxySettings = true; upstreams = { "matrix-synapse" = { servers = { "127.0.0.1:8008" = {}; }; }; }; virtualHosts."synapse.li" = { forceSSL = true; sslCertificate = "/run/credentials/nginx.service/synapse.li.pem"; sslCertificateKey = "/run/credentials/nginx.service/synapse.li.key.pem"; sslTrustedCertificate = "/run/credentials/nginx.service/synapse.li.chain.pem"; listen = [ { addr = "0.0.0.0"; port = 443; ssl = true; } { addr = "[::0]"; port = 443; ssl = true; } { addr = "0.0.0.0"; port = 8448; ssl = true; } { addr = "[::0]"; port = 8448; ssl = true; } ]; extraConfig = '' add_header Strict-Transport-Security "max-age=63072000" always; ''; locations = let corsHeaders = '' add_header Access-Control-Allow-Origin '*'; add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE, OPTIONS'; add_header Access-Control-Allow-Headers 'X-Requested-With, Content-Type, Authorization'; add_header Access-Control-Max-Age 7200; ''; in listToAttrs (map (n: nameValuePair n { proxyPass = "http://matrix-synapse"; }) ["/_matrix" "/_synapse/client" "/_synapse/admin"]) // { "= /.well-known/matrix/server" = { extraConfig = '' default_type application/json; ${corsHeaders} ''; return = "200 '${builtins.toJSON { "m.server" = "synapse.li:443"; }}'"; }; "= /.well-known/matrix/client" = { extraConfig = '' default_type application/json; ${corsHeaders} ''; return = "200 '${builtins.toJSON { "m.homeserver" = { "base_url" = "https://synapse.li"; }; "m.identity_server" = { "base_url" = "https://vector.im"; }; }}'"; }; "/".return = "301 https://element.synapse.li$request_uri"; }; }; virtualHosts."element.synapse.li" = { forceSSL = true; sslCertificate = "/run/credentials/nginx.service/element.synapse.li.pem"; sslCertificateKey = "/run/credentials/nginx.service/element.synapse.li.key.pem"; sslTrustedCertificate = "/run/credentials/nginx.service/element.synapse.li.chain.pem"; extraConfig = '' add_header Strict-Transport-Security "max-age=63072000" always; ''; root = pkgs.element-web.override { conf = { default_server_config."m.homeserver" = { "base_url" = "https://synapse.li"; "server_name" = "synapse.li"; }; }; }; }; }; security.acme.domains = { "element.synapse.li" = { zone = "synapse.li"; certCfg = { postRun = '' ${pkgs.systemd}/bin/systemctl try-restart nginx.service ''; }; }; "turn.synapse.li" = { zone = "synapse.li"; certCfg = { server = "https://acme.zerossl.com/v2/DV90"; extraLegoFlags = [ "--cert.timeout" "300" ]; postRun = '' ${pkgs.systemd}/bin/systemctl try-restart coturn.service ''; }; }; "synapse.li".certCfg = { postRun = '' ${pkgs.systemd}/bin/systemctl try-restart nginx.service ''; }; }; systemd.services.nginx = { serviceConfig = { LoadCredential = [ "synapse.li.key.pem:${config.security.acme.certs."synapse.li".directory}/key.pem" "synapse.li.pem:${config.security.acme.certs."synapse.li".directory}/fullchain.pem" "synapse.li.chain.pem:${config.security.acme.certs."synapse.li".directory}/chain.pem" "element.synapse.li.key.pem:${config.security.acme.certs."element.synapse.li".directory}/key.pem" "element.synapse.li.pem:${config.security.acme.certs."element.synapse.li".directory}/fullchain.pem" "element.synapse.li.chain.pem:${config.security.acme.certs."element.synapse.li".directory}/chain.pem" ]; }; }; services.coturn = rec { enable = true; no-cli = true; no-tcp-relay = true; min-port = 49000; max-port = 50000; use-auth-secret = true; static-auth-secret-file = config.sops.secrets."coturn-auth-secret".path; realm = "turn.synapse.li"; cert = "/run/credentials/coturn.service/turn.synapse.li.pem"; pkey = "/run/credentials/coturn.service/turn.synapse.li.key.pem"; dh-file = config.security.dhparams.params.coturn.path; relay-ips = ["202.61.241.61" "2a03:4000:52:ada::"]; extraConfig = '' # for debugging verbose # ban private IP ranges 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=127.0.0.0-127.255.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=::1 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 denied-peer-ip=2a03:4000:52:ada::1-2a03:4000:52:ada:ffff:ffff:ffff:ffff ''; }; systemd.services.coturn = { serviceConfig = { LoadCredential = [ "turn.synapse.li.key.pem:${config.security.acme.certs."turn.synapse.li".directory}/key.pem" "turn.synapse.li.pem:${config.security.acme.certs."turn.synapse.li".directory}/fullchain.pem" ]; }; }; sops.secrets."coturn-auth-secret" = { format = "binary"; sopsFile = ./coturn-auth-secret; owner = "turnserver"; group = "turnserver"; }; }; }