| { base, config, lib, pkgs, ... }: |
| lib.recursiveUpdate { |
| services.matrix-synapse = { |
| enable = true; |
| withJemalloc = true; |
| |
| plugins = with config.services.matrix-synapse.package.plugins; |
| [ matrix-synapse-mjolnir-antispam ]; |
| |
| settings = rec { |
| server_name = "clicks.codes"; |
| auto_join_rooms = [ "#general:${server_name}" ]; |
| suppress_key_server_warning = true; |
| enable_registration = true; |
| registration_requires_token = true; |
| allow_public_rooms_over_federation = true; |
| allow_device_name_lookup_over_federation = true; |
| registration_shared_secret = "!!registration_shared_secret!!"; |
| public_baseurl = "https://matrix-backend.clicks.codes/"; |
| max_upload_size = "100M"; |
| listeners = [{ |
| x_forwarded = true; |
| tls = false; |
| resources = [{ |
| names = [ "client" "federation" ]; |
| compress = true; |
| }]; |
| port = 1030; |
| bind_addresses = [ "generic" ]; |
| }]; |
| enable_metrics = true; |
| database.args.database = "synapse"; |
| |
| oidc_providers = [ |
| { |
| idp_id = "keycloak"; |
| idp_name = "Clicks Keycloak"; |
| issuer = "https://login.clicks.codes/realms/master"; |
| client_id = "matrix"; |
| client_secret = "!!matrix_keycloak_client_secret!!"; |
| scopes = [ "openid" "profile" ]; |
| user_mapping_provider = { |
| config = { |
| localpart_template = "{{ user.preferred_username }}"; |
| display_name_template = "{{ user.name }}"; |
| }; |
| }; |
| backchannel_logout_enabled = true; |
| } |
| ]; |
| |
| log_config = lib.pipe { |
| version = 1; |
| formatters = { |
| precise = { |
| format = |
| "%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s"; |
| }; |
| }; |
| handlers = { |
| console = { |
| class = "logging.StreamHandler"; |
| formatter = "precise"; |
| }; |
| }; |
| loggers = { "synapse.storage.SQL" = { level = "WARNING"; }; }; |
| root = { |
| level = "ERROR"; |
| handlers = [ "console" ]; |
| }; |
| "disable_existing_loggers" = false; |
| } [ builtins.toJSON (builtins.toFile "logcfg.yaml") ]; |
| |
| server_notices = { |
| system_mxid_localpart = "system"; |
| system_mxid_display_name = "Clicks Administrators"; |
| room_name = "Announcements"; |
| }; |
| }; |
| |
| sliding-sync = { |
| enable = true; |
| settings = { |
| SYNCV3_SERVER = "https://matrix-backend.clicks.codes"; |
| SYNCV3_BINDADDR = "generic:1031"; |
| SYNCV3_LOG_LEVEL = "warn"; |
| }; |
| environmentFile = config.sops.secrets.matrix_sliding_sync_env.path; |
| createDatabase = true; |
| }; |
| }; |
| |
| networking.firewall.allowedTCPPorts = [ 3478 5349 ]; |
| networking.firewall.allowedUDPPorts = [ 3478 5349 ]; |
| |
| services.mjolnir = { |
| enable = true; |
| |
| settings = { |
| autojoinOnlyIfManager = true; |
| automaticallyRedactForReasons = |
| [ "nsfw" "gore" "spam" "harassment" "hate" ]; |
| recordIgnoredInvites = true; |
| admin.enableMakeRoomAdminCommand = true; |
| allowNoPrefix = true; |
| protections.wordlist.words = [ ]; |
| protectedRooms = [ "https://matrix.to/#/#global:clicks.codes" ]; |
| }; |
| |
| pantalaimon = { |
| enable = true; |
| username = "system"; |
| passwordFile = config.sops.secrets.mjolnir_password.path; |
| options = { |
| ssl = false; |
| listenAddress = "127.0.0.1"; |
| }; |
| }; |
| |
| homeserverUrl = "http://generic:1030"; |
| |
| managementRoom = "#moderation-commands:clicks.codes"; |
| }; |
| |
| sops.secrets = { |
| matrix_sliding_sync_env = { |
| mode = "0600"; |
| owner = config.users.users.root.name; |
| group = config.users.users.root.group; |
| sopsFile = ../../secrets/matrix_sliding_sync.env.bin; |
| format = "binary"; |
| }; |
| matrix_keycloak_client_secret = { |
| mode = "0400"; |
| owner = config.users.users.matrix-synapse.name; |
| group = config.users.users.matrix-synapse.group; |
| sopsFile = ../../secrets/matrix.json; |
| format = "json"; |
| }; |
| registration_shared_secret = { |
| mode = "0400"; |
| owner = config.users.users.root.name; |
| group = config.users.users.root.group; |
| sopsFile = ../../secrets/matrix.json; |
| format = "json"; |
| }; |
| matrix_private_key = { |
| mode = "0600"; |
| owner = config.users.users.matrix-synapse.name; |
| group = config.users.users.matrix-synapse.group; |
| sopsFile = ../../secrets/matrix_private_key.pem; |
| format = "binary"; |
| path = config.services.matrix-synapse.settings.signing_key_path; |
| }; |
| mjolnir_password = { |
| mode = "0600"; |
| owner = config.users.users.mjolnir.name; |
| group = config.users.users.mjolnir.group; |
| sopsFile = ../../secrets/matrix.json; |
| format = "json"; |
| }; |
| }; |
| |
| systemd.services.matrix-synapse.requires = [ "postgresql.service" ]; |
| systemd.services.matrix-synapse.after = [ "nginx.service" "keycloak.service" ]; |
| |
| } (let isDerived = base != null; |
| in if isDerived |
| # We cannot use mkIf as both sides are evaluated no matter the condition value |
| # Given we use base as an attrset, mkIf will error if base is null in here |
| then |
| let synapse_cfgfile = config.services.matrix-synapse.configFile; |
| in { |
| scalpel.trafos."synapse.yaml" = { |
| source = toString synapse_cfgfile; |
| matchers."registration_shared_secret".secret = |
| config.sops.secrets.registration_shared_secret.path; |
| matchers."matrix_keycloak_client_secret".secret = |
| config.sops.secrets.matrix_keycloak_client_secret.path; |
| owner = config.users.users.matrix-synapse.name; |
| group = config.users.users.matrix-synapse.group; |
| mode = "0400"; |
| }; |
| |
| systemd.services.matrix-synapse.serviceConfig.ExecStart = lib.mkForce |
| (builtins.replaceStrings [ "${synapse_cfgfile}" ] |
| [ "${config.scalpel.trafos."synapse.yaml".destination}" ] |
| "${base.config.systemd.services.matrix-synapse.serviceConfig.ExecStart}"); |
| |
| systemd.services.matrix-synapse.preStart = lib.mkForce |
| ("while [[ \"$(${pkgs.curl}/bin/curl -s -o /dev/null -w ''%{http_code}'' https://login.clicks.codes)\" != \"200\" ]]; do sleep 5; done; " + |
| (builtins.replaceStrings [ "${synapse_cfgfile}" ] |
| [ "${config.scalpel.trafos."synapse.yaml".destination}" ] |
| "${base.config.systemd.services.matrix-synapse.preStart}")); |
| |
| systemd.services.matrix-synapse.restartTriggers = [ synapse_cfgfile ]; |
| |
| environment.systemPackages = with lib; |
| let |
| cfg = config.services.matrix-synapse; |
| registerNewMatrixUser = let |
| isIpv6 = x: lib.length (lib.splitString ":" x) > 1; |
| listener = lib.findFirst (listener: |
| lib.any (resource: lib.any (name: name == "client") resource.names) |
| listener.resources) (lib.last cfg.settings.listeners) |
| cfg.settings.listeners; |
| # FIXME: Handle cases with missing client listener properly, |
| # don't rely on lib.last, this will not work. |
| |
| # add a tail, so that without any bind_addresses we still have a useable address |
| bindAddress = head (listener.bind_addresses ++ [ "127.0.0.1" ]); |
| listenerProtocol = if listener.tls then "https" else "http"; |
| in pkgs.writeShellScriptBin "matrix-synapse-register_new_matrix_user" '' |
| exec ${cfg.package}/bin/register_new_matrix_user \ |
| $@ \ |
| ${ |
| lib.concatMapStringsSep " " (x: "-c ${x}") |
| ([ config.scalpel.trafos."synapse.yaml".destination ] |
| ++ cfg.extraConfigFiles) |
| } \ |
| "${listenerProtocol}://${ |
| if (isIpv6 bindAddress) then |
| "[${bindAddress}]" |
| else |
| "${bindAddress}" |
| }:${builtins.toString listener.port}/" |
| ''; |
| in [ (lib.meta.hiPrio registerNewMatrixUser) ]; |
| } |
| else |
| { }) |