diff --git a/flake.nix b/flake.nix index 3a93199d..a303019b 100644 --- a/flake.nix +++ b/flake.nix @@ -256,7 +256,10 @@ [ "nix-store" "xn--qbtm095lrg0bfka60z" ])) // (builtins.listToAttrs (builtins.map (site: { name = "${site}.chn.moe"; value.upstream.address = "wireguard.vps7.chn.moe"; }) - [ "xn--s8w913fdga" "misskey" "synapse" "matrix" "send" "kkmeeting" "api" "git" "grafana" ])); + [ + "xn--s8w913fdga" "misskey" "synapse" "syncv3.synapse" "matrix" "syncv3.matrix" + "send" "kkmeeting" "api" "git" "grafana" + ])); applications = { element.instances."element.chn.moe" = {}; @@ -328,7 +331,7 @@ synapse.instances = { synapse.matrixHostname = "synapse.chn.moe"; - matrix = { port = 8009; redisPort = 6380; hostname = "matrix.chn.moe"; }; + matrix = { port = 8009; redisPort = 6380; slidingSyncPort = 9001; }; }; xrdp = { enable = true; hostname = [ "vps7.chn.moe" ]; }; vaultwarden.enable = true; diff --git a/modules/services/synapse.nix b/modules/services/synapse.nix index a107586a..fb6c6a9a 100644 --- a/modules/services/synapse.nix +++ b/modules/services/synapse.nix @@ -3,22 +3,32 @@ inputs: { options.nixos.services.synapse.instances = let inherit (inputs.lib) mkOption types; in mkOption { - type = types.attrsOf (types.submodule { options = + type = types.attrsOf (types.submodule (submoduleInputs: { options = { autoStart = mkOption { type = types.bool; default = true; }; port = mkOption { type = types.ints.unsigned; default = 8008; }; redisPort = mkOption { type = types.ints.unsigned; default = 6379; }; - hostname = mkOption { type = types.nonEmptyStr; default = "synapse.chn.moe"; }; + slidingSyncPort = mkOption { type = types.ints.unsigned; default = 9000; }; + hostname = mkOption + { + type = types.nonEmptyStr; + default = "${submoduleInputs.config._module.args.name}.chn.moe"; + }; matrixHostname = mkOption { type = types.nonEmptyStr; default = "chn.moe"; }; + slidingSyncHostname = mkOption + { + type = types.nonEmptyStr; + default = "syncv3.${submoduleInputs.config.hostname}"; + }; # , synapse_homeserver --config-path homeserver.yaml --generate-config --report-stats=yes --server-name xxx - };}); + };})); default = {}; }; config = let inherit (inputs.config.nixos.services) synapse; inherit (inputs.lib) mkIf mkMerge; - inherit (builtins) map listToAttrs replaceStrings; + inherit (builtins) map listToAttrs replaceStrings concatLists; inherit (inputs.localLib) attrsToList; in { @@ -40,162 +50,208 @@ inputs: systemd = mkMerge (map (instance: let workdir = "/var/lib/synapse/${instance.name}"; in { - services."synapse-${instance.name}" = - let - package = inputs.pkgs.matrix-synapse.override - { extras = [ "url-preview" "postgres" "redis" ]; plugins = []; }; - config = inputs.config.sops.templates."synapse/${instance.name}.yaml".path; - homeserver = "${package}/bin/synapse_homeserver"; - in + services = + { + "synapse-${instance.name}" = + let + package = inputs.pkgs.matrix-synapse.override + { extras = [ "url-preview" "postgres" "redis" ]; plugins = []; }; + config = inputs.config.sops.templates."synapse/${instance.name}/config.yaml".path; + homeserver = "${package}/bin/synapse_homeserver"; + in + { + description = "synapse-${instance.name}"; + enable = instance.value.autoStart; + after = [ "network-online.target" "postgresql.service" ]; + requires = [ "postgresql.service" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = + { + ExecStart = "${homeserver} --config-path ${config} --keys-directory ${workdir}"; + Type = "notify"; + User = "synapse-${instance.name}"; + Group = "synapse-${instance.name}"; + WorkingDirectory = workdir; + ExecReload = "${inputs.pkgs.util-linux}/bin/kill -HUP $MAINPID"; + Restart = "on-failure"; + UMask = "0077"; + CapabilityBoundingSet = [ "" ]; + + # hardening + LockPersonality = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + PrivateUsers = true; + ProcSubset = "pid"; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProtectSystem = "strict"; + ReadWritePaths = [ workdir ]; + RemoveIPC = true; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ "@system-service" "~@resources" "~@privileged" ]; + }; + }; + "synapse-sliding-sync-${instance.name}" = { - description = "synapse-${instance.name}"; - enable = instance.value.autoStart; - after = [ "network-online.target" "postgresql.service" ]; - requires = [ "postgresql.service" ]; + after = [ "synapse-${instance.name}.service" ]; + wants = [ "synapse-${instance.name}.service" ]; wantedBy = [ "multi-user.target" ]; serviceConfig = { - ExecStart = "${homeserver} --config-path ${config} --keys-directory ${workdir}"; - Type = "notify"; User = "synapse-${instance.name}"; Group = "synapse-${instance.name}"; - WorkingDirectory = workdir; - ExecReload = "${inputs.pkgs.util-linux}/bin/kill -HUP $MAINPID"; + EnvironmentFile = inputs.config.sops.templates."synapse/${instance.name}-sliding-sync/env".path; + ExecStart = inputs.lib.getExe inputs.pkgs.matrix-sliding-sync; + WorkingDirectory = workdir + "-sliding-sync"; Restart = "on-failure"; - UMask = "0077"; - CapabilityBoundingSet = [ "" ]; - - # hardening - LockPersonality = true; - NoNewPrivileges = true; - PrivateDevices = true; - PrivateTmp = true; - PrivateUsers = true; - ProcSubset = "pid"; - ProtectClock = true; - ProtectControlGroups = true; - ProtectHome = true; - ProtectHostname = true; - ProtectKernelLogs = true; - ProtectKernelModules = true; - ProtectKernelTunables = true; - ProtectProc = "invisible"; - ProtectSystem = "strict"; - ReadWritePaths = [ workdir ]; - RemoveIPC = true; - RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ]; - RestrictNamespaces = true; - RestrictRealtime = true; - RestrictSUIDSGID = true; - SystemCallArchitectures = "native"; - SystemCallFilter = [ "@system-service" "~@resources" "~@privileged" ]; + RestartSec = "1s"; }; }; + }; tmpfiles.rules = [ "d /var/lib/synapse 0755 root root" "d ${workdir} 0700 synapse-${instance.name} synapse-${instance.name}" "Z ${workdir} - synapse-${instance.name} synapse-${instance.name}" + "d ${workdir}-sliding-sync 0700 synapse-${instance.name} synapse-${instance.name}" + "Z ${workdir}-sliding-sync - synapse-${instance.name} synapse-${instance.name}" ]; }) (attrsToList synapse.instances)); sops = mkMerge (map (instance: { - templates."synapse/${instance.name}.yaml" = + templates = { - owner = "synapse-${instance.name}"; - group = "synapse-${instance.name}"; - content = - let - inherit (inputs.config.sops) placeholder; - in builtins.readFile ((inputs.pkgs.formats.yaml {}).generate "${instance.name}.yaml" - { - server_name = instance.value.matrixHostname; - public_baseurl = "https://${instance.value.hostname}"; - listeners = - [{ - bind_addresses = [ "127.0.0.1" ]; - inherit (instance.value) port; - resources = [{ names = [ "client" "federation" ]; compress = false; }]; - tls = false; - type = "http"; - x_forwarded = true; - }]; - database = + "synapse/${instance.name}/config.yaml" = + { + owner = "synapse-${instance.name}"; + group = "synapse-${instance.name}"; + content = + let + inherit (inputs.config.sops) placeholder; + in builtins.readFile ((inputs.pkgs.formats.yaml {}).generate "${instance.name}.yaml" { - name = "psycopg2"; - args = + server_name = instance.value.matrixHostname; + public_baseurl = "https://${instance.value.hostname}"; + listeners = + [{ + bind_addresses = [ "127.0.0.1" ]; + inherit (instance.value) port; + resources = [{ names = [ "client" "federation" ]; compress = false; }]; + tls = false; + type = "http"; + x_forwarded = true; + }]; + database = { - user = "synapse_${replaceStrings [ "-" ] [ "_" ] instance.name}"; - password = placeholder."postgresql/synapse_${replaceStrings [ "-" ] [ "_" ] instance.name}"; - database = "synapse_${replaceStrings [ "-" ] [ "_" ] instance.name}"; - host = "127.0.0.1"; - port = "5432"; + name = "psycopg2"; + args = + { + user = "synapse_${replaceStrings [ "-" ] [ "_" ] instance.name}"; + password = placeholder."postgresql/synapse_${replaceStrings [ "-" ] [ "_" ] instance.name}"; + database = "synapse_${replaceStrings [ "-" ] [ "_" ] instance.name}"; + host = "127.0.0.1"; + port = "5432"; + }; + allow_unsafe_locale = true; }; - allow_unsafe_locale = true; - }; - redis = - { - enabled = true; - port = instance.value.redisPort; - password = placeholder."redis/synapse-${instance.name}"; - }; - turn_shared_secret = placeholder."synapse/${instance.name}/coturn"; - registration_shared_secret = placeholder."synapse/${instance.name}/registration"; - macaroon_secret_key = placeholder."synapse/${instance.name}/macaroon"; - form_secret = placeholder."synapse/${instance.name}/form"; - signing_key_path = inputs.config.sops.secrets."synapse/${instance.name}/signing-key".path; - email = - { - smtp_host = "mail.chn.moe"; - smtp_port = 25; - smtp_user = "bot@chn.moe"; - smtp_pass = placeholder."mail/bot"; - require_transport_security = true; - notif_from = "Your Friendly %(app)s homeserver "; - app_name = "Haonan Chen's synapse"; - }; - admin_contact = "mailto:chn@chn.moe"; - enable_registration = true; - registrations_require_3pid = [ "email" ]; - turn_uris = [ "turns:coturn.chn.moe" "turn:coturn.chn.moe" ]; - max_upload_size = "1024M"; - web_client_location = "https://element.chn.moe/"; - serve_server_wellknown = true; - report_stats = true; - trusted_key_servers = - [{ - server_name = "matrix.org"; - verify_keys."ed25519:auto" = "Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw"; - }]; - suppress_key_server_warning = true; - log_config = (inputs.pkgs.formats.yaml {}).generate "log.yaml" - { - version = 1; - formatters.precise.format = - "%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s"; - handlers.console = { class = "logging.StreamHandler"; formatter = "precise"; }; - root = { level = "INFO"; handlers = [ "console" ]; }; - disable_existing_loggers = true; - }; - pid_file = "/run/synapse-${instance.name}.pid"; - media_store_path = "/var/lib/synapse/${instance.name}/media_store"; - presence.enabled = true; - url_preview_enabled = true; - url_preview_ip_range_blacklist = - [ - "10.0.0.0/8" "100.64.0.0/10" "127.0.0.0/8" "169.254.0.0/16" "172.16.0.0/12" "192.0.0.0/24" - "192.0.2.0/24" "192.168.0.0/16" "192.88.99.0/24" "198.18.0.0/15" "198.51.100.0/24" "2001:db8::/32" - "203.0.113.0/24" "224.0.0.0/4" "::1/128" "fc00::/7" "fe80::/10" "fec0::/10" "ff00::/8" - ]; - max_image_pixels = "32M"; - dynamic_thumbnails = false; - }); + redis = + { + enabled = true; + port = instance.value.redisPort; + password = placeholder."redis/synapse-${instance.name}"; + }; + turn_shared_secret = placeholder."synapse/${instance.name}/coturn"; + registration_shared_secret = placeholder."synapse/${instance.name}/registration"; + macaroon_secret_key = placeholder."synapse/${instance.name}/macaroon"; + form_secret = placeholder."synapse/${instance.name}/form"; + signing_key_path = inputs.config.sops.secrets."synapse/${instance.name}/signing-key".path; + email = + { + smtp_host = "mail.chn.moe"; + smtp_port = 25; + smtp_user = "bot@chn.moe"; + smtp_pass = placeholder."mail/bot"; + require_transport_security = true; + notif_from = "Your Friendly %(app)s homeserver "; + app_name = "Haonan Chen's synapse"; + }; + admin_contact = "mailto:chn@chn.moe"; + enable_registration = true; + registrations_require_3pid = [ "email" ]; + turn_uris = [ "turns:coturn.chn.moe" "turn:coturn.chn.moe" ]; + max_upload_size = "1024M"; + web_client_location = "https://element.chn.moe/"; + serve_server_wellknown = true; + extra_well_known_client_content."org.matrix.msc3575.proxy".url = + "https://${instance.value.slidingSyncHostname}"; + report_stats = true; + trusted_key_servers = + [{ + server_name = "matrix.org"; + verify_keys."ed25519:auto" = "Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw"; + }]; + suppress_key_server_warning = true; + log_config = (inputs.pkgs.formats.yaml {}).generate "log.yaml" + { + version = 1; + formatters.precise.format = + "%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s"; + handlers.console = { class = "logging.StreamHandler"; formatter = "precise"; }; + root = { level = "INFO"; handlers = [ "console" ]; }; + disable_existing_loggers = true; + }; + pid_file = "/run/synapse-${instance.name}.pid"; + media_store_path = "/var/lib/synapse/${instance.name}/media_store"; + presence.enabled = true; + url_preview_enabled = true; + url_preview_ip_range_blacklist = + [ + "10.0.0.0/8" "100.64.0.0/10" "127.0.0.0/8" "169.254.0.0/16" "172.16.0.0/12" "192.0.0.0/24" + "192.0.2.0/24" "192.168.0.0/16" "192.88.99.0/24" "198.18.0.0/15" "198.51.100.0/24" "2001:db8::/32" + "203.0.113.0/24" "224.0.0.0/4" "::1/128" "fc00::/7" "fe80::/10" "fec0::/10" "ff00::/8" + ]; + max_image_pixels = "32M"; + dynamic_thumbnails = false; + }); + }; + "synapse/${instance.name}-sliding-sync/env" = + { + owner = "synapse-${instance.name}"; + group = "synapse-${instance.name}"; + content = + let + inherit (inputs.config.sops) placeholder; + pgString = "postgresql://" + + "synapse_${replaceStrings [ "-" ] [ "_" ] instance.name}" + + ":${placeholder."postgresql/synapse_${replaceStrings [ "-" ] [ "_" ] instance.name}"}" + + "@127.0.0.1:5432" + + "/synapse_${replaceStrings [ "-" ] [ "_" ] instance.name}_sliding_sync"; + in + '' + SYNCV3_SERVER=https://${instance.value.hostname} + SYNCV3_DB=${pgString} + SYNCV3_SECRET=${placeholder."synapse/${instance.name}/sliding-sync"} + SYNCV3_BINDADDR=127.0.0.1:${toString instance.value.slidingSyncPort} + ''; + }; }; secrets = (listToAttrs (map (secret: { name = "synapse/${instance.name}/${secret}"; value = {}; }) - [ "coturn" "registration" "macaroon" "form" ])) + [ "coturn" "registration" "macaroon" "form" "sliding-sync" ])) // { "synapse/${instance.name}/signing-key".owner = "synapse-${instance.name}"; } // { "mail/bot" = {}; }; }) @@ -205,13 +261,19 @@ inputs: postgresql = { enable = mkIf (synapse.instances != {}) true; - instances = listToAttrs (map + instances = listToAttrs (concatLists (map (instance: - { - name = "synapse_${replaceStrings [ "-" ] [ "_" ] instance.name}"; - value.initializeFlags = { TEMPLATE = "template0"; LC_CTYPE = "C"; LC_COLLATE = "C"; }; - }) - (attrsToList synapse.instances)); + [ + { + name = "synapse_${replaceStrings [ "-" ] [ "_" ] instance.name}"; + value.initializeFlags = { TEMPLATE = "template0"; LC_CTYPE = "C"; LC_COLLATE = "C"; }; + } + { + name = "synapse_${replaceStrings [ "-" ] [ "_" ] instance.name}_sliding_sync"; + value.user = "synapse_${replaceStrings [ "-" ] [ "_" ] instance.name}"; + } + ]) + (attrsToList synapse.instances))); }; redis.instances = listToAttrs (map (instance: { name = "synapse-${instance.name}"; value.port = instance.value.redisPort; }) @@ -219,13 +281,20 @@ inputs: nginx = { enable = mkIf (synapse.instances != {}) true; - https = listToAttrs (map + https = listToAttrs (concatLists (map (instance: with instance.value; - { - name = hostname; - value.location."/".proxy = { upstream = "http://127.0.0.1:${toString port}"; websocket = true; }; - }) - (attrsToList synapse.instances)); + [ + { + name = hostname; + value.location."/".proxy = { upstream = "http://127.0.0.1:${toString port}"; websocket = true; }; + } + { + name = slidingSyncHostname; + value.location."/".proxy = + { upstream = "http://127.0.0.1:${toString slidingSyncPort}"; websocket = true; }; + } + ]) + (attrsToList synapse.instances))); }; }; }; diff --git a/secrets/vps7/default.yaml b/secrets/vps7/default.yaml index ac8546bf..e6d9cb94 100644 --- a/secrets/vps7/default.yaml +++ b/secrets/vps7/default.yaml @@ -45,12 +45,14 @@ synapse: macaroon: ENC[AES256_GCM,data:2/8GuF/a+ocVtLN0PU17JDvXw/RoXX/CXFHPlI9THl5bY8lBm6tEawijnOKVoFLovfU=,iv:GPAr3ZjqLf9ixevsZoQgs4cPkv0VL4WJoFfQZOdThlw=,tag:HRt/igDEfUJ3K39mG7b9Fg==,type:str] form: ENC[AES256_GCM,data:Z9cYL9ibRWmOhAYtB269n0cWZSvL4zGgc03ZRag0m8cz2j0god/Fn/w6kx3cyGK1C70=,iv:Yst6WSV63IvbMF5nnicIoBj77eSwVMnAHtHrKo2UcDk=,tag:4qf6F2rdctcCf4J9vECvYg==,type:str] signing-key: ENC[AES256_GCM,data:BbPJiNcVTqMAL2XG3K3CIbsb8EM4r8ct/WxPK10FHRwAnqChKy3CAviYU9gewO/tNZXHvUYUAUbPww==,iv:IZB/40EE3DIxAqagdH/a4kcSmiec5l24XLCQKCQNaRo=,tag:/1t0WAPBYmYrPTx4V4wgkw==,type:str] + sliding-sync: ENC[AES256_GCM,data:POXExkTRRhXin4lD4MA61xsuzYXCT6U7QtQWtNnEb6kUWRrAvS9mqk+JTBn3onCzf2Azhi3WQOY/t+OiQFXI1w==,iv:GJfJSGb6t/q9KdVCr0dVVcD+e0yZUQzrJrtuhOlYJIE=,tag:ovd1ZXRkk7VoNo8KoYDViA==,type:str] matrix: coturn: ENC[AES256_GCM,data:MwZKkYMefshuk46Cne4wn9ooFH8RCDbrxp+MbLJWli9iPHuzJJzUuQNU9EDL0aNbzyYEMt/7DErw42z6KrpGww==,iv:u/SVVTgfJO2FakiYU+uLHXjA4tHU/W6ASsR3S31+pWs=,tag:VTeKNOKwm2bsiZAOVXeBOQ==,type:str] registration: ENC[AES256_GCM,data:+pA61vTg12lYUyXjLrHSY7y/ExfTQffLlGUI4HBOSFFPTck7bu68FrCaHOIBTtEMfjU=,iv:Ex/phkBZxglG8HiRz+m7h2HNanpq2Pxwbm08vdM3xFc=,tag:mM3YEa70FnCeYIUthK4TeA==,type:str] macaroon: ENC[AES256_GCM,data:/+RaayKiPPpVV7OWWdaSkSSRHMjb8d58lZcpvltN9cYkN1btvMViEgdLSlfqzRRlPUE=,iv:pg9GXgNsrVWKlUAiCKZ2pYXugRH6MsBIMpHKoYWYLik=,tag:/mj5Ak7XAX/FH7sNPEVALw==,type:str] form: ENC[AES256_GCM,data:7HF7HMUH1BTJgXXP6cpUiVj0jCwGW57bx9wKTJu7PnRsNuAam/+nKX7Zfg7WD+gSBlA=,iv:SYeUsuFVgAA6U6STCtKT5c5E8Kglh3x7hy6+Op4n0W8=,tag:eICmHTwwn0KcgNhdDGnusA==,type:str] signing-key: ENC[AES256_GCM,data:hzxxDbGp1L09O7+ueUSa5lJOY/QvF2zvHdpueEHjaPQEToQt9mr2loeTQHC7ObTegfLb9UHrI1jn4A==,iv:KngfahwYZZmDQ5LeOUPWptTMGAC8TZm1G0FWcrwCwsw=,tag:U9pW6/boBIpiswn67Ezrfw==,type:str] + sliding-sync: ENC[AES256_GCM,data:BeA6g98IWDP6hnLFI77QqG6esDwB6j3OPzAv3eJxWoTajAsByHSgSYP1vHN5Iok6IgvSSmkf0/HiOJy1Ca8IIA==,iv:ca+t/rYwc/fAVUcz0JTmrRQCOcbDNscbnE8BpHkx/OE=,tag:eEfhUChUt4kRnO82XqRY4g==,type:str] nebula: key: ENC[AES256_GCM,data:9o6EkfTWOU0KwnJsgHML4E7VOfzo3LHnlOkV8ubhi6aayXImC3lAaoPrqUI=,iv:KHprijN7z+4FIIW+D5klDM9a9VzMJ5xawPc7jJtbHmk=,tag:0DAmxoz8D5f38ndPbkNW+g==,type:str] vaultwarden: @@ -127,8 +129,8 @@ sops: SnFHS1Z0SXUzTFdEd29KTy9DU3Y3R0UKfhh+rUmWDrf+UGjclP57dHipPLFoXSqy HdelmfV6q4/c7ppx2E+oZw3VNgoZCsrxxzYZfwxHJiZb+5vkE0D8iA== -----END AGE ENCRYPTED FILE----- - lastmodified: "2023-12-19T13:41:44Z" - mac: ENC[AES256_GCM,data:+tj7+Q4bTzKNRY5N8Okj2DIl4YRSAO9SC1pzJ3hkp+dBF7uNnyVK+QOyN4cxoAQorPhH2lETHu4aK9Zmi6A4YiGUBCgnvBwvXpZw+iy2hmIDBISi9Y34wmTNAx8PW6BI2E+8d5dFFXiQbULjLbV4TolYyZVbvnCVHWTYff0ht8E=,iv:gpT+s6K+ey3yUzI+ShH1dOV+S/1o1PIRiNv4K0mBqXk=,tag:lupDndxajL01QsHL51r7Nw==,type:str] + lastmodified: "2023-12-20T06:27:19Z" + mac: ENC[AES256_GCM,data:i7AN+Sd4C61GSzT409mYd6D2tQzDyONIUsto52b1mV8hIJ4Q/U9VT5wumRjm4dGUWqrq9oFdD0/iUL1CmEdasBN7VFwNEpSYl6yhzU7zX3Re3N/0mffeW0Fx/38LdvywusJAHC9yWvsNMblKDnYxGm/UI2W/7QRMDyr8jnU6La0=,iv:Ua+K1m27GkkrUn+wcylkwrdWnq1yzFG1NMVzYAiW/6k=,tag:Gqqk5zOU3Ax2Al5CvXEV7g==,type:str] pgp: [] unencrypted_suffix: _unencrypted version: 3.8.1