{ config, pkgs, lib, ... }: with lib; { config = { services.matrix-synapse = { enable = true; settings = { enable_metrics = true; enable_registration = true; allow_guest_access = false; server_name = "synapse.li"; listeners = [ { bind_addresses = ["::1" "127.0.0.1"]; port = 8008; resources = [ { names = [ "client" "federation" ]; compress = false; } ]; tls = false; type = "http"; x_forwarded = true; } { bind_addresses = ["::1" "127.0.0.1"]; port = 9092; resources = [ { names = [ "metrics" ]; compress = false; } ]; tls = false; type = "http"; } ]; tls_certificate_path = "/run/credentials/matrix-synapse.service/synapse.li.pem"; tls_private_key_path = "/run/credentials/matrix-synapse.service/synapse.li.key.pem"; turn_uris = ["turn:turn.synapse.li?transport=udp" "turn:turn.synapse.li?transport=tcp"]; turn_user_lifetime = "1h"; 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" ]; max_upload_size = "500M"; suppress_key_server_warning = true; }; extraConfigFiles = [ "/run/credentials/matrix-synapse.service/registration.yaml" "/run/credentials/matrix-synapse.service/turn-secret.yaml" ]; }; 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 = { wants = ["postgresql.service"]; 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}" ]; RuntimeDirectory = "matrix-synapse"; StateDirectory = "matrix-synapse"; PrivateTmp = true; PrivateDevices = true; CapabilityBoundingSet = []; AmbientCapabilities = []; ProtectSystem = "strict"; ProtectKernelTunables = true; ProtectKernelModules = true; ProtectControlGroups = true; ProtectClock = true; ProtectHostname = true; ProtectHome = "tmpfs"; ProtectKernelLogs = true; ProtectProc = "invisible"; ProcSubset = "pid"; PrivateNetwork = false; RestrictAddressFamilies = ["AF_INET" "AF_INET6" "AF_UNIX"]; IPAddressAllow = "any"; SystemCallArchitectures = "native"; SystemCallFilter = ["@system-service" "~@privileged @resources @obsolete"]; RestrictSUIDSGID = true; RemoveIPC = true; NoNewPrivileges = true; RestrictRealtime = true; RestrictNamespaces = true; LockPersonality = true; PrivateUsers = true; MemoryDenyWriteExecute = false; ReadWritePaths = ["/var/run/postgresql"]; }; }; 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"; extraConfig = "client_max_body_size 500M;"; }) ["/_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; add_header X-Frame-Options SAMEORIGIN; add_header X-Content-Type-Options nosniff; add_header X-XSS-Protection "1; mode=block"; add_header Content-Security-Policy "frame-ancestors 'none'"; ''; 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 = "/run/credentials/coturn.service/auth-secret"; 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" "auth-secret:${config.sops.secrets."coturn-auth-secret".path}" ]; }; }; sops.secrets."coturn-auth-secret" = { format = "binary"; sopsFile = ./coturn-auth-secret; }; }; }