From 6f793472570f437aaf1c4e1d272701f4f307052b Mon Sep 17 00:00:00 2001 From: Sumner Evans Date: Wed, 17 May 2023 09:21:54 -0600 Subject: [PATCH] morak: decommission sumnerevans.com matrix server Signed-off-by: Sumner Evans --- host-configurations/morak.nix | 19 - modules/services/default.nix | 1 - modules/services/matrix/coturn.nix | 85 --- modules/services/matrix/default.nix | 12 - modules/services/matrix/linkedin-matrix.nix | 240 --------- .../matrix/matrix-vacation-responder.nix | 68 --- modules/services/matrix/maubot.nix | 127 ----- modules/services/matrix/mautrix-discord.nix | 220 -------- modules/services/matrix/mautrix-slack.nix | 230 -------- modules/services/matrix/quotesfilebot.nix | 70 --- .../matrix/synapse/cleanup-synapse.nix | 169 ------ modules/services/matrix/synapse/default.nix | 496 ------------------ .../services/matrix/synapse/shared-config.nix | 177 ------- 13 files changed, 1914 deletions(-) delete mode 100644 modules/services/matrix/coturn.nix delete mode 100644 modules/services/matrix/default.nix delete mode 100644 modules/services/matrix/linkedin-matrix.nix delete mode 100644 modules/services/matrix/matrix-vacation-responder.nix delete mode 100644 modules/services/matrix/maubot.nix delete mode 100644 modules/services/matrix/mautrix-discord.nix delete mode 100644 modules/services/matrix/mautrix-slack.nix delete mode 100644 modules/services/matrix/quotesfilebot.nix delete mode 100644 modules/services/matrix/synapse/cleanup-synapse.nix delete mode 100644 modules/services/matrix/synapse/default.nix delete mode 100644 modules/services/matrix/synapse/shared-config.nix diff --git a/host-configurations/morak.nix b/host-configurations/morak.nix index ae030fa..f0610aa 100644 --- a/host-configurations/morak.nix +++ b/host-configurations/morak.nix @@ -19,7 +19,6 @@ fileSystems = { "/" = { device = "/dev/disk/by-uuid/78831675-9f80-462b-b9fc-75a0efa368e5"; fsType = "ext4"; }; "/mnt/syncthing-data" = { device = "/dev/disk/by-uuid/930c8bdb-7b71-4bdf-b478-6e85218cad37"; fsType = "ext4"; }; - "/mnt/postgresql-data" = { device = "/dev/disk/by-uuid/3d8eb9ca-e8ea-4231-b2a6-4fc5367ccb8a"; fsType = "ext4"; }; "/mnt/syncthing-pictures-tmp" = { device = "/dev/disk/by-uuid/bfc8d39f-31e0-4261-9447-91bc7e39bb2f"; fsType = "ext4"; }; }; @@ -122,11 +121,6 @@ # Mumble services.murmur.enable = true; - # PosgreSQL - services.postgresql.enable = true; - services.postgresql.dataDir = "/mnt/postgresql-data/${config.services.postgresql.package.psqlSchema}"; - services.postgresqlBackup.enable = true; - # Restic backup services.backup.healthcheckId = "6c9caf62-4f7b-4ef7-82ac-d858d3bcbcb5"; services.backup.healthcheckPruneId = "f90ed04a-2596-49d0-a89d-764780a27fc6"; @@ -135,17 +129,4 @@ services.backup.backups.syncthing-pictures-tmp-data = { path = "/mnt/syncthing-pictures-tmp"; }; - - # Synapse - services.matrix-synapse-custom.enable = true; - services.matrix-synapse-custom.registrationSharedSecretFile = ../secrets/matrix/registration-shared-secret/morak; - services.matrix-synapse-custom.extraConfig = { - database.allow_unsafe_locale = true; - }; - services.cleanup-synapse.environmentFile = "/etc/nixos/secrets/matrix/cleanup-synapse/morak"; - services.matrix-vacation-responder = { - enable = true; - username = "@sumner:sumnerevans.com"; - homeserver = "https://matrix.sumnerevans.com"; - }; } diff --git a/modules/services/default.nix b/modules/services/default.nix index 37b85d2..86db127 100644 --- a/modules/services/default.nix +++ b/modules/services/default.nix @@ -2,7 +2,6 @@ { imports = [ ./gui - ./matrix ./acme.nix ./airsonic.nix diff --git a/modules/services/matrix/coturn.nix b/modules/services/matrix/coturn.nix deleted file mode 100644 index 4cd9407..0000000 --- a/modules/services/matrix/coturn.nix +++ /dev/null @@ -1,85 +0,0 @@ -# See: https://nixos.org/nixos/manual/index.html#module-services-matrix-synapse -{ config, lib, pkgs, ... }: -let - turnDomain = "turn.${config.networking.domain}"; - certs = config.security.acme.certs; - staticAuthSecret = lib.removeSuffix "\n" (builtins.readFile ../../../secrets/coturn-static-auth-secret); -in -# TODO actually figure this out eventually - # TODO will need to convert to use matrix-synapse-custom -lib.mkIf (false && config.services.matrix-synapse.enable) { - 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 = staticAuthSecret; - realm = turnDomain; - cert = "${certs.${turnDomain}.directory}/full.pem"; - pkey = "${certs.${turnDomain}.directory}/key.pem"; - 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 - ''; - }; - - # open the firewall - networking.firewall = { - interfaces.enp2s0 = - let - ranges = with config.services.coturn; [ - { from = min-port; to = max-port; } - ]; - in - { - allowedUDPPortRanges = ranges; - allowedUDPPorts = [ 3478 ]; - allowedTCPPortRanges = ranges; - allowedTCPPorts = [ 3478 ]; - }; - }; - - # get a certificate - services.nginx.virtualHosts.${turnDomain}.enableACME = true; - security.acme.certs.${turnDomain} = { - group = "turnserver"; - postRun = "systemctl restart coturn.service"; - }; - users.groups.turnserver.members = [ "turnserver" "nginx" ]; - - # configure synapse to point users to coturn - services.matrix-synapse = with config.services.coturn; { - turn_uris = [ - "turn:${turnDomain}:3478?transport=udp" - "turn:${turnDomain}:3478?transport=tcp" - ]; - turn_shared_secret = staticAuthSecret; - turn_user_lifetime = "1h"; - }; -} diff --git a/modules/services/matrix/default.nix b/modules/services/matrix/default.nix deleted file mode 100644 index 50c4f03..0000000 --- a/modules/services/matrix/default.nix +++ /dev/null @@ -1,12 +0,0 @@ -{ - imports = [ - ./coturn.nix - ./linkedin-matrix.nix - ./matrix-vacation-responder.nix - ./maubot.nix - ./mautrix-discord.nix - ./mautrix-slack.nix - ./quotesfilebot.nix - ./synapse - ]; -} diff --git a/modules/services/matrix/linkedin-matrix.nix b/modules/services/matrix/linkedin-matrix.nix deleted file mode 100644 index bf33f7c..0000000 --- a/modules/services/matrix/linkedin-matrix.nix +++ /dev/null @@ -1,240 +0,0 @@ -{ config, lib, pkgs, ... }: with lib; let - cfg = config.services.linkedin-matrix; - synapseCfg = config.services.matrix-synapse-custom; - - linkedin-matrix = pkgs.callPackage ../../../pkgs/linkedin-matrix.nix { }; - - linkedinMatrixAppserviceConfig = { - id = "linkedin"; - url = "http://${cfg.listenAddress}:${toString cfg.listenPort}"; - as_token = cfg.appServiceToken; - hs_token = cfg.homeserverToken; - rate_limited = false; - sender_localpart = "XDUsekmAmWcmL1FWrgZ8E7ih-p0vffI3kMiezV43Sw29GLBQAQ-0_GRJXMQXlVb0"; - "de.sorunome.msc2409.push_ephemeral" = true; - push_ephemeral = true; - namespaces = { - users = [ - { regex = "@li_.*:nevarro.space"; exclusive = true; } - { regex = "@linkedinbot:nevarro.space"; exclusive = true; } - ]; - aliases = [ ]; - rooms = [ ]; - }; - }; - - yamlFormat = pkgs.formats.yaml { }; - - linkedinMatrixAppserviceConfigYaml = yamlFormat.generate "linkedin-matrix-registration.yaml" linkedinMatrixAppserviceConfig; - - linkedinMatrixConfig = { - homeserver = { - address = cfg.homeserver; - domain = config.networking.domain; - verify_ssl = false; - asmux = false; - http_retry_count = 4; - }; - - metrics = { - enabled = true; - listen_port = 9010; - }; - - appservice = { - address = "http://${cfg.listenAddress}:${toString cfg.listenPort}"; - hostname = cfg.listenAddress; - port = cfg.listenPort; - max_body_size = 1; - database = "postgresql://linkedinmatrix:linkedinmatrix@localhost/linkedin-matrix"; - database_opts = { min_size = 5; max_size = 10; }; - id = "linkedin"; - bot_username = cfg.botUsername; - bot_displayname = "LinkedIn bridge bot"; - bot_avatar = "mxc://sumnerevans.com/XMtwdeUBnxYvWNFFrfeTSHqB"; - as_token = cfg.appServiceToken; - hs_token = cfg.homeserverToken; - ephemeral_events = true; - }; - - bridge = { - username_template = "li_{userid}"; - displayname_template = "{displayname}"; - displayname_preference = [ "name" "first_name" ]; - set_topic_on_dms = true; - command_prefix = "!li"; - initial_chat_sync = 20; - invite_own_puppet_to_pm = false; - sync_with_custom_puppets = false; - sync_direct_chat_list = true; - space_support = { - enable = true; - name = "LinkedIn"; - }; - presence = false; - update_avatar_initial_sync = true; - login_shared_secret_map = { - "nevarro.space" = removeSuffix "\n" (readFile synapseCfg.sharedSecretAuthFile); - }; - federate_rooms = false; - encryption = { - allow = true; - default = true; - require = true; - allow_key_sharing = true; - verification_levels = { - receive = "unverified"; - send = "cross-signed-tofu"; - share = "unverified"; - }; - }; - delivery_receipts = true; - backfill = { - invite_own_puppet = true; - initial_limit = 20; - missed_limit = 20; - disable_notifications = true; - }; - temporary_disconnect_notices = true; - mute_bridging = true; - permissions = { - "nevarro.space" = "user"; - "@sumner:sumnerevans.com" = "admin"; - "@sumner:nevarro.space" = "admin"; - }; - }; - - logging = { - version = 1; - - formatters.journal_fmt.format = "[%(name)s] %(message)s"; - handlers = { - journal = { - class = "systemd.journal.JournalHandler"; - formatter = "journal_fmt"; - SYSLOG_IDENTIFIER = "linkedin-matrix"; - }; - }; - loggers = { - aiohttp.level = "DEBUG"; - mau.level = "DEBUG"; - paho.level = "DEBUG"; - root.level = "DEBUG"; - }; - root = { level = "DEBUG"; handlers = [ "journal" ]; }; - }; - }; - - linkedinMatrixConfigYaml = yamlFormat.generate "linkedin-config.yaml" linkedinMatrixConfig; -in -{ - options = { - services.linkedin-matrix = { - enable = mkEnableOption "linkedin-matrix, a LinkedIn Messaging <-> Matrix bridge"; - useLocalSynapse = mkOption { - type = types.bool; - default = true; - description = "Whether or not to use the local synapse instance."; - }; - homeserver = mkOption { - type = types.str; - default = "http://localhost:8008"; - description = "The URL of the Matrix homeserver."; - }; - listenAddress = mkOption { - type = types.str; - default = "127.0.0.1"; - description = "The address for linkedin-matrix to listen on."; - }; - listenPort = mkOption { - type = types.int; - default = 9899; - description = "The port for linkedin-matrix to listen on."; - }; - botUsername = mkOption { - type = types.str; - default = "linkedinbot"; - description = "The localpart of the linkedin-matrix admin bot's username."; - }; - appServiceToken = mkOption { - type = types.str; - description = '' - This is the token that the app service should use as its access_token - when using the Client-Server API. This can be anything you want. - ''; - }; - homeserverToken = mkOption { - type = types.str; - description = '' - This is the token that the homeserver will use when sending requests - to the app service. This can be anything you want. - ''; - }; - }; - }; - - config = mkIf cfg.enable { - meta.maintainers = [ maintainers.sumnerevans ]; - - assertions = [ - { - assertion = cfg.useLocalSynapse -> config.services.matrix-synapse-custom.enable; - message = '' - LinkedIn must be running on the same server as Synapse if - 'useLocalSynapse' is enabled. - ''; - } - ]; - - services.matrix-synapse-custom.appServiceConfigFiles = mkIf cfg.useLocalSynapse [ - linkedinMatrixAppserviceConfigYaml - ]; - - # Create a user for linkedin-matrix. - users.users.linkedinmatrix = { - group = "linkedinmatrix"; - isSystemUser = true; - }; - users.groups.linkedinmatrix = { }; - - # Create a database user for linkedin-matrix - services.postgresql.ensureDatabases = [ "linkedin-matrix" ]; - services.postgresql.ensureUsers = [ - { - name = "linkedinmatrix"; - ensurePermissions = { - "DATABASE \"linkedin-matrix\"" = "ALL PRIVILEGES"; - "ALL TABLES IN SCHEMA public" = "ALL PRIVILEGES"; - }; - } - ]; - - systemd.services.linkedin-matrix = { - description = "LinkedIn Messaging <-> Matrix Bridge"; - after = optional cfg.useLocalSynapse "matrix-synapse.target"; - wantedBy = [ "multi-user.target" ]; - serviceConfig = { - User = "linkedinmatrix"; - Group = "linkedinmatrix"; - ExecStart = '' - ${linkedin-matrix}/bin/linkedin-matrix \ - --config ${linkedinMatrixConfigYaml} \ - --no-update - ''; - Restart = "on-failure"; - }; - }; - - services.prometheus = { - enable = true; - scrapeConfigs = [ - { - job_name = "linkedinmatirx"; - scrape_interval = "15s"; - metrics_path = "/"; - static_configs = [{ targets = [ "0.0.0.0:9010" ]; }]; - } - ]; - }; - }; -} diff --git a/modules/services/matrix/matrix-vacation-responder.nix b/modules/services/matrix/matrix-vacation-responder.nix deleted file mode 100644 index f51c699..0000000 --- a/modules/services/matrix/matrix-vacation-responder.nix +++ /dev/null @@ -1,68 +0,0 @@ -{ config, lib, pkgs, ... }: with lib; let - cfg = config.services.matrix-vacation-responder; - matrix-vacation-responder = pkgs.callPackage ../../../pkgs/matrix-vacation-responder { }; - - vacationResponderConfig = { - homeserver = cfg.homeserver; - username = cfg.username; - password_file = cfg.passwordFile; - - vacation_message = '' - This is no longer my primary Matrix account. - Please send your messages to [@sumner:nevarro.space](https://matrix.to/#/@sumner:nevarro.space) - ''; - vacation_message_min_interval = 1440; - respond_to_groups = true; - }; - format = pkgs.formats.yaml { }; - matrixVacationResponderConfigYaml = format.generate "matrix-vacation-responder.config.yaml" vacationResponderConfig; -in -{ - options = { - services.matrix-vacation-responder = { - enable = mkEnableOption "matrix-vacation-responder"; - username = mkOption { - type = types.str; - }; - homeserver = mkOption { - type = types.str; - default = "http://localhost:8008"; - }; - passwordFile = mkOption { - type = types.path; - default = "/etc/nixos/secrets/matrix/bots/vacation-responder-password"; - }; - dataDir = mkOption { - type = types.path; - default = "/var/lib/matrixvacationresponder"; - }; - }; - }; - - config = mkIf cfg.enable { - systemd.services.matrix-vacation-responder = { - description = "Matrix Vacation Responder"; - after = [ "matrix-synapse.target" ]; - wantedBy = [ "multi-user.target" ]; - serviceConfig = { - ExecStart = '' - ${matrix-vacation-responder}/bin/matrix-vacation-responder \ - --config ${matrixVacationResponderConfigYaml} \ - --dbfile ${cfg.dataDir}/matrix-vacation-responder.db - ''; - Restart = "on-failure"; - User = "matrixvacationresponder"; - }; - }; - - users = { - users.matrixvacationresponder = { - group = "matrixvacationresponder"; - isSystemUser = true; - home = cfg.dataDir; - createHome = true; - }; - groups.matrixvacationresponder = { }; - }; - }; -} diff --git a/modules/services/matrix/maubot.nix b/modules/services/matrix/maubot.nix deleted file mode 100644 index c5cec58..0000000 --- a/modules/services/matrix/maubot.nix +++ /dev/null @@ -1,127 +0,0 @@ -{ config, lib, pkgs, ... }: with lib; let - cfg = config.services.maubot; - maubot = pkgs.callPackage ../../../pkgs/maubot.nix { }; - matrixDomain = "matrix.${config.networking.domain}"; - - maubotConfig = { - database = "postgresql://maubot:maubot@localhost/maubot"; - server = { - public_url = cfg.public_url; - }; - homeservers = cfg.homeservers; - admins = cfg.admins; - logging = { - version = 1; - formatters = { - normal = { - format = "[%(asctime)s] [%(levelname)s@%(name)s] %(message)s"; - }; - }; - handlers = { - console = { - class = "logging.StreamHandler"; - formatter = "normal"; - }; - }; - loggers = { - maubot = { level = "TRACE"; }; - mau = { level = "TRACE"; }; - aiohttp = { level = "DEBUG"; }; - }; - root = { - level = "TRACE"; - handlers = [ "console" ]; - }; - }; - }; - format = pkgs.formats.yaml { }; - configYaml = format.generate "maubot.config.yaml" maubotConfig; -in -{ - options = { - services.maubot = { - enable = mkEnableOption "maubot"; - public_url = mkOption { - type = types.str; - }; - homeservers = mkOption { - type = with types; attrsOf attrs; - default = { }; - }; - admins = mkOption { - type = with types; attrsOf str; - default = { }; - }; - dataDir = mkOption { - type = types.path; - default = "/var/lib/maubot"; - }; - }; - }; - - config = mkIf cfg.enable { - # Create a database user for maubot - services.postgresql.ensureDatabases = [ "maubot" ]; - services.postgresql.ensureUsers = [ - { - name = "maubot"; - ensurePermissions = { - "DATABASE \"maubot\"" = "ALL PRIVILEGES"; - "ALL TABLES IN SCHEMA public" = "ALL PRIVILEGES"; - }; - } - ]; - - systemd.services.maubot = { - description = "Maubot"; - after = [ "matrix-synapse.target" ]; - wantedBy = [ "multi-user.target" ]; - serviceConfig = { - ExecStart = pkgs.writeShellScript "start-maubot.sh" '' - mkdir -p ${cfg.dataDir}/{plugins,trash,logs} - - pwd - ${maubot}/bin/maubot \ - --config ${configYaml} - ''; - WorkingDirectory = cfg.dataDir; - Restart = "on-failure"; - User = "maubot"; - Group = "maubot"; - }; - }; - - services.nginx = { - virtualHosts = { - ${matrixDomain} = { - locations."/_matrix/maubot" = { - proxyPass = "http://0.0.0.0:29316"; # without a trailing / - extraConfig = '' - access_log /var/log/nginx/maubot.access.log; - ''; - }; - locations."/_matrix/maubot/v1/logs" = { - proxyPass = "http://localhost:29316"; - extraConfig = '' - access_log /var/log/nginx/maubot.access.log; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "Upgrade"; - proxy_set_header X-Forwarded-For $remote_addr; - ''; - }; - }; - }; - }; - - users = { - users.maubot = { - group = "maubot"; - isSystemUser = true; - home = cfg.dataDir; - createHome = true; - }; - groups.maubot = { }; - }; - }; -} diff --git a/modules/services/matrix/mautrix-discord.nix b/modules/services/matrix/mautrix-discord.nix deleted file mode 100644 index 99f72ea..0000000 --- a/modules/services/matrix/mautrix-discord.nix +++ /dev/null @@ -1,220 +0,0 @@ -{ config, lib, pkgs, ... }: with lib; let - cfg = config.services.mautrix-discord; - synapseCfg = config.services.matrix-synapse-custom; - - mautrix-discord = pkgs.callPackage ../../../pkgs/mautrix-discord.nix { }; - - mautrixDiscordAppserviceConfig = { - id = "discord"; - url = "http://${cfg.listenAddress}:${toString cfg.listenPort}"; - as_token = cfg.appServiceToken; - hs_token = cfg.homeserverToken; - rate_limited = false; - sender_localpart = "LI6W2mH43X68rSiZ1YLAQCSLtuSZlPBt"; - "de.sorunome.msc2409.push_ephemeral" = true; - push_ephemeral = true; - namespaces = { - users = [ - { regex = "^@discord_[0-9]+:nevarro.space$"; exclusive = true; } - { regex = "^@discordbot:nevarro.space$"; exclusive = true; } - ]; - aliases = [ ]; - rooms = [ ]; - }; - }; - - yamlFormat = pkgs.formats.yaml { }; - - mautrixDiscordAppserviceConfigYaml = yamlFormat.generate "mautrix-discord-registration.yaml" mautrixDiscordAppserviceConfig; - - mautrixDiscordConfig = { - homeserver = { - address = cfg.homeserver; - domain = config.networking.domain; - }; - - metrics = { - enabled = true; - listen_port = 9011; - }; - - appservice = { - address = "http://${cfg.listenAddress}:${toString cfg.listenPort}"; - hostname = cfg.listenAddress; - port = cfg.listenPort; - max_body_size = 1; - database = { - type = "sqlite3-fk-wal"; - uri = "file:${cfg.dataDir}/mautrix-discord.db?_txlock=immediate"; - max_open_conns = 20; - max_idle_cons = 2; - }; - id = "discord"; - bot_username = cfg.botUsername; - bot_displayname = "Discord bridge bot"; - bot_avatar = "mxc://nevarro.space/LWsPMGFektATJpgbSyfULDKR"; - as_token = cfg.appServiceToken; - hs_token = cfg.homeserverToken; - ephemeral_events = true; - }; - - bridge = { - username_template = "discord_{{.}}"; - displayname_template = "{{.Username}}#{{.Discriminator}} (D){{if .Bot}} (bot){{end}}"; - channel_name_template = "{{if or (eq .Type 3) (eq .Type 4)}}{{.Name}}{{else}}#{{.Name}}{{end}}"; - guild_name_template = "{{.Name}}"; - private_chat_portal_meta = false; - portal_message_buffer = 128; - startup_private_channel_create_limit = 5; - delivery_receipts = true; - message_error_notices = true; - restricted_rooms = true; - delete_portal_on_channel_delete = true; - federate_rooms = false; - login_shared_secret_map = { - "nevarro.space" = removeSuffix "\n" (readFile synapseCfg.sharedSecretAuthFile); - }; - command_prefix = "!dis"; - encryption = { - allow = true; - default = true; - require = true; - allow_key_sharing = true; - verification_levels = { - receive = "unverified"; - send = "cross-signed-tofu"; - share = "unverified"; - }; - }; - permissions = { - "nevarro.space" = "user"; - "@sumner:sumnerevans.com" = "admin"; - "@sumner:nevarro.space" = "admin"; - }; - }; - - logging = { - directory = "./logs"; - file_name_format = "{{.Date}}-{{.Index}}.log"; - file_date_format = "2006-01-02"; - file_mode = 384; - timestamp_format = "Jan _2, 2006 15:04:05"; - print_level = "debug"; - print_json = false; - file_json = false; - }; - }; - - mautrixDiscordConfigYaml = yamlFormat.generate "mautrix-discord-config.yaml" mautrixDiscordConfig; -in -{ - options = { - services.mautrix-discord = { - enable = mkEnableOption "mautrix-discord, a Discord <-> Matrix bridge."; - useLocalSynapse = mkOption { - type = types.bool; - default = true; - description = "Whether or not to use the local synapse instance."; - }; - homeserver = mkOption { - type = types.str; - default = "http://localhost:8008"; - description = "The URL of the Matrix homeserver."; - }; - listenAddress = mkOption { - type = types.str; - default = "127.0.0.1"; - description = "The address for mautrix-discord to listen on."; - }; - listenPort = mkOption { - type = types.int; - default = 9890; - description = "The port for mautrix-discord to listen on."; - }; - botUsername = mkOption { - type = types.str; - default = "discordbot"; - description = "The localpart of the mautrix-discord admin bot's username."; - }; - appServiceToken = mkOption { - type = types.str; - description = '' - This is the token that the app service should use as its access_token - when using the Client-Server API. This can be anything you want. - ''; - }; - homeserverToken = mkOption { - type = types.str; - description = '' - This is the token that the homeserver will use when sending requests - to the app service. This can be anything you want. - ''; - }; - dataDir = mkOption { - type = types.path; - default = "/var/lib/mautrix-discord"; - }; - }; - }; - - config = mkIf cfg.enable { - meta.maintainers = [ maintainers.sumnerevans ]; - - assertions = [ - { - assertion = cfg.useLocalSynapse -> config.services.matrix-synapse-custom.enable; - message = '' - Mautrix-Discord must be running on the same server as Synapse if - 'useLocalSynapse' is enabled. - ''; - } - ]; - - services.matrix-synapse-custom.appServiceConfigFiles = mkIf cfg.useLocalSynapse [ - mautrixDiscordAppserviceConfigYaml - ]; - - # Create a user for mautrix-discord. - users = { - users.mautrixdiscord = { - group = "mautrixdiscord"; - isSystemUser = true; - home = cfg.dataDir; - createHome = true; - }; - groups.mautrixdiscord = { }; - }; - - systemd.services.mautrix-discord = { - description = "Discord <-> Matrix Bridge"; - after = optional cfg.useLocalSynapse "matrix-synapse.target"; - wantedBy = [ "multi-user.target" ]; - preStart = '' - ${pkgs.coreutils}/bin/mkdir -p ${cfg.dataDir}/logs - ''; - serviceConfig = { - User = "mautrixdiscord"; - Group = "mautrixdiscord"; - ExecStart = '' - ${mautrix-discord}/bin/mautrix-discord \ - --config ${mautrixDiscordConfigYaml} \ - --no-update - ''; - WorkingDirectory = cfg.dataDir; - Restart = "on-failure"; - }; - }; - - services.prometheus = { - enable = true; - scrapeConfigs = [ - { - job_name = "mautrixdiscord"; - scrape_interval = "15s"; - metrics_path = "/"; - static_configs = [{ targets = [ "0.0.0.0:9011" ]; }]; - } - ]; - }; - }; -} diff --git a/modules/services/matrix/mautrix-slack.nix b/modules/services/matrix/mautrix-slack.nix deleted file mode 100644 index 690c84a..0000000 --- a/modules/services/matrix/mautrix-slack.nix +++ /dev/null @@ -1,230 +0,0 @@ -{ config, lib, pkgs, ... }: with lib; let - cfg = config.services.mautrix-slack; - synapseCfg = config.services.matrix-synapse-custom; - - mautrix-slack = pkgs.callPackage ../../../pkgs/mautrix-slack.nix { }; - - mautrixSlackAppserviceConfig = { - id = "slack"; - url = "http://${cfg.listenAddress}:${toString cfg.listenPort}"; - as_token = cfg.appServiceToken; - hs_token = cfg.homeserverToken; - rate_limited = false; - sender_localpart = "Eg7SOEMJLfuDEgaaEnxKpAKrjT0KOR7f"; - "de.sorunome.msc2409.push_ephemeral" = true; - push_ephemeral = true; - namespaces = { - users = [ - { regex = "^@slack_.+:nevarro.space$"; exclusive = true; } - { regex = "^@slackbot:nevarro.space$"; exclusive = true; } - ]; - aliases = [ ]; - rooms = [ ]; - }; - }; - - yamlFormat = pkgs.formats.yaml { }; - - mautrixSlackAppserviceConfigYaml = yamlFormat.generate "mautrix-slack-registration.yaml" mautrixSlackAppserviceConfig; - - mautrixSlackConfig = { - homeserver = { - address = cfg.homeserver; - domain = config.networking.domain; - }; - - metrics = { - enabled = true; - listen_port = 9012; - }; - - appservice = { - address = "http://${cfg.listenAddress}:${toString cfg.listenPort}"; - hostname = cfg.listenAddress; - port = cfg.listenPort; - max_body_size = 1; - database = { - type = "sqlite3-fk-wal"; - uri = "file:${cfg.dataDir}/mautrix-slack.db?_txlock=immediate"; - max_open_conns = 20; - max_idle_cons = 2; - }; - id = "slack"; - bot_username = cfg.botUsername; - bot_displayname = "Slack bridge bot"; - bot_avatar = "mxc://nevarro.space/yKNWtofJaVLfedQIlAAZbUco"; - as_token = cfg.appServiceToken; - hs_token = cfg.homeserverToken; - ephemeral_events = true; - }; - - bridge = { - username_template = "slack_{{.}}"; - displayname_template = "{{.RealName}} (S)"; - bot_displayname_template = "{{.Name}} (bot)"; - channel_name_template = "#{{.Name}}"; - portal_message_buffer = 128; - delivery_receipts = true; - message_error_notices = true; - federate_rooms = false; - login_shared_secret_map = { - "nevarro.space" = removeSuffix "\n" (readFile synapseCfg.sharedSecretAuthFile); - }; - command_prefix = "!slack"; - backfill = { - enable = true; - unread_hours_threshold = -1; - immediate_messages = 10; - incremental = { - messages_per_batch = 100; - post_batch_delay = 20; - max_messages = { - channel = -1; - group_dm = -1; - dm = -1; - }; - }; - }; - encryption = { - allow = true; - default = true; - require = true; - allow_key_sharing = true; - verification_levels = { - receive = "unverified"; - send = "cross-signed-tofu"; - share = "unverified"; - }; - }; - permissions = { - "nevarro.space" = "user"; - "@sumner:sumnerevans.com" = "admin"; - "@sumner:nevarro.space" = "admin"; - }; - }; - - logging = { - directory = "./logs"; - file_name_format = "{{.Date}}-{{.Index}}.log"; - file_date_format = "2006-01-02"; - file_mode = 384; - timestamp_format = "Jan _2, 2006 15:04:05"; - print_level = "debug"; - print_json = false; - file_json = false; - }; - }; - - mautrixSlackConfigYaml = yamlFormat.generate "mautrix-slack-config.yaml" mautrixSlackConfig; -in -{ - options = { - services.mautrix-slack = { - enable = mkEnableOption "mautrix-slack, a Slack <-> Matrix bridge."; - useLocalSynapse = mkOption { - type = types.bool; - default = true; - description = "Whether or not to use the local synapse instance."; - }; - homeserver = mkOption { - type = types.str; - default = "http://localhost:8008"; - description = "The URL of the Matrix homeserver."; - }; - listenAddress = mkOption { - type = types.str; - default = "127.0.0.1"; - description = "The address for mautrix-slack to listen on."; - }; - listenPort = mkOption { - type = types.int; - default = 9891; - description = "The port for mautrix-slack to listen on."; - }; - botUsername = mkOption { - type = types.str; - default = "slackbot"; - description = "The localpart of the mautrix-slack admin bot's username."; - }; - appServiceToken = mkOption { - type = types.str; - description = '' - This is the token that the app service should use as its access_token - when using the Client-Server API. This can be anything you want. - ''; - }; - homeserverToken = mkOption { - type = types.str; - description = '' - This is the token that the homeserver will use when sending requests - to the app service. This can be anything you want. - ''; - }; - dataDir = mkOption { - type = types.path; - default = "/var/lib/mautrix-slack"; - }; - }; - }; - - config = mkIf cfg.enable { - meta.maintainers = [ maintainers.sumnerevans ]; - - assertions = [ - { - assertion = cfg.useLocalSynapse -> config.services.matrix-synapse-custom.enable; - message = '' - Mautrix-Slack must be running on the same server as Synapse if - 'useLocalSynapse' is enabled. - ''; - } - ]; - - services.matrix-synapse-custom.appServiceConfigFiles = mkIf cfg.useLocalSynapse [ - mautrixSlackAppserviceConfigYaml - ]; - - # Create a user for mautrix-slack. - users = { - users.mautrixslack = { - group = "mautrixslack"; - isSystemUser = true; - home = cfg.dataDir; - createHome = true; - }; - groups.mautrixslack = { }; - }; - - systemd.services.mautrix-slack = { - description = "Slack <-> Matrix Bridge"; - after = optional cfg.useLocalSynapse "matrix-synapse.target"; - wantedBy = [ "multi-user.target" ]; - preStart = '' - ${pkgs.coreutils}/bin/mkdir -p ${cfg.dataDir}/logs - ''; - serviceConfig = { - User = "mautrixslack"; - Group = "mautrixslack"; - ExecStart = '' - ${mautrix-slack}/bin/mautrix-slack \ - --config ${mautrixSlackConfigYaml} \ - --no-update - ''; - WorkingDirectory = cfg.dataDir; - Restart = "on-failure"; - }; - }; - - services.prometheus = { - enable = true; - scrapeConfigs = [ - { - job_name = "mautrixslack"; - scrape_interval = "15s"; - metrics_path = "/"; - static_configs = [{ targets = [ "0.0.0.0:9012" ]; }]; - } - ]; - }; - }; -} diff --git a/modules/services/matrix/quotesfilebot.nix b/modules/services/matrix/quotesfilebot.nix deleted file mode 100644 index bc57f91..0000000 --- a/modules/services/matrix/quotesfilebot.nix +++ /dev/null @@ -1,70 +0,0 @@ -{ config, lib, pkgs, ... }: with lib; let - cfg = config.services.quotesfilebot; - quotesfilebot = pkgs.callPackage ../../../pkgs/quotesfilebot { }; - - quotesfilebotConfig = { - DefaultReactionEmoji = cfg.defaultReactionEmoji; - Username = cfg.username; - Homeserver = cfg.homeserver; - PasswordFile = cfg.passwordFile; - JoinMessage = cfg.joinMessage; - }; - format = pkgs.formats.json { }; - quotesfilebotConfigJson = format.generate "quotesfilebot.json" quotesfilebotConfig; -in -{ - options = { - services.quotesfilebot = { - enable = mkEnableOption "quotesfilebot"; - defaultReactionEmoji = mkOption { - type = types.str; - default = "💬"; - }; - username = mkOption { - type = types.str; - default = "@quotesfilebot:${config.networking.domain}"; - }; - homeserver = mkOption { - type = types.str; - default = "http://localhost:8008"; - }; - passwordFile = mkOption { - type = types.path; - default = "/var/lib/quotesfilebot/passwordfile"; - }; - dataDir = mkOption { - type = types.path; - default = "/var/lib/quotesfilebot"; - }; - joinMessage = mkOption { - type = types.str; - default = "I'm a quotesfilebot!"; - }; - }; - }; - - config = mkIf cfg.enable { - systemd.services.quotesfilebot = { - description = "Quotesfilebot"; - after = [ "matrix-synapse.target" ]; - wantedBy = [ "multi-user.target" ]; - serviceConfig = { - ExecStart = '' - ${quotesfilebot}/bin/quotes-file-bot --config ${quotesfilebotConfigJson} - ''; - Restart = "on-failure"; - User = "quotesfilebot"; - }; - }; - - users = { - users.quotesfilebot = { - group = "quotesfilebot"; - isSystemUser = true; - home = cfg.dataDir; - createHome = true; - }; - groups.quotesfilebot = { }; - }; - }; -} diff --git a/modules/services/matrix/synapse/cleanup-synapse.nix b/modules/services/matrix/synapse/cleanup-synapse.nix deleted file mode 100644 index 7c4fb2a..0000000 --- a/modules/services/matrix/synapse/cleanup-synapse.nix +++ /dev/null @@ -1,169 +0,0 @@ -# See: -# - https://levans.fr/shrink-synapse-database.html -# - https://foss-notes.blog.nomagic.uk/2021/03/matrix-database-house-cleaning/ -# - https://git.envs.net/envs/matrix-conf/src/branch/master/usr/local/bin -{ config, lib, pkgs, ... }: -with pkgs; -with lib; -let - cfg = config.services.cleanup-synapse; - synapseCfg = config.services.matrix-synapse-custom; - - adminUrl = "http://localhost:8008/_synapse/admin/v1"; - adminMediaRepoUrl = "http://localhost:8011/_synapse/admin/v1"; - adminCurl = ''${curl}/bin/curl --header "Authorization: Bearer $CLEANUP_ACCESS_TOKEN"''; - - # Delete old cached remote media - purgeRemoteMedia = writeShellScriptBin "purge-remote-media" '' - set -xe - - now=$(${coreutils}/bin/date +%s%N | ${coreutils}/bin/cut -b1-13) - nintey_days_ago=$(( now - 7776000000 )) - - ${adminCurl} \ - -X POST \ - -H "Content-Type: application/json" \ - -d "{}" \ - "${adminMediaRepoUrl}/purge_media_cache?before_ts=$nintey_days_ago" - ''; - - # Get rid of any rooms that aren't joined by anyone from the homeserver. - cleanupForgottenRooms = writeShellScriptBin "cleanup-forgotten" '' - set -xe - - roomlist=$(mktemp) - to_purge=$(mktemp) - - ${adminCurl} '${adminUrl}/rooms?limit=1000' > $roomlist - - # Find all of the rooms that have no local users. - ${jq}/bin/jq -r '.rooms[] | select(.joined_local_members == 0) | .room_id' < $roomlist > $to_purge - - while read room_id; do - echo "deleting $room_id..." - ${adminCurl} \ - -X DELETE \ - -H "Content-Type: application/json" \ - -d "{}" \ - "${adminUrl}/rooms/$room_id" - done < $to_purge - ''; - - # # Delete all non-local room history that is from before 90 days ago. - # cleanupHistory = writeShellScriptBin "cleanup-history" '' - # set -xe - # roomlist=$(mktemp) - - # ${adminCurl} '${adminUrl}/rooms?limit=1000' | - # ${jq}/bin/jq -r '.rooms[] | .room_id' > $roomlist - - # now=$(${coreutils}/bin/date +%s%N | ${coreutils}/bin/cut -b1-13) - # nintey_days_ago=$(( now - 7776000000 )) - - # while read room_id; do - # echo "purging history for $room_id..." - - # ${adminCurl} -X POST -H "Content-Type: application/json" \ - # -d "{ \"delete_local_events\": false, \"purge_up_to_ts\": $nintey_days_ago }" \ - # "${adminUrl}/purge_history/$room_id" - # done < $roomlist - # ''; - - largeStateRoomsQuery = "SELECT room_id FROM state_groups GROUP BY room_id ORDER BY count(*)"; - compressState = writeShellScriptBin "compress-state" '' - set -xe - bigrooms=$(mktemp) - echo "\\copy (${largeStateRoomsQuery}) to '$bigrooms' with CSV" | - ${postgresql}/bin/psql -d matrix-synapse - - echo 'Disabling autovacuum on state_groups_state' - echo 'ALTER TABLE state_groups_state SET (AUTOVACUUM_ENABLED = FALSE);' | - /run/wrappers/bin/sudo -u postgres ${postgresql}/bin/psql -d matrix-synapse - - while read room_id; do - echo "compressing state for $room_id" - - state_compressor=$(mktemp) - - ${matrix-synapse-tools.rust-synapse-compress-state}/bin/synapse_compress_state \ - -t \ - -o $state_compressor \ - -m 1000 \ - -p "host=localhost user=matrix-synapse password=synapse dbname=matrix-synapse" \ - -r $room_id - - if test -s "$state_compressor" - then - ${postgresql}/bin/psql -d matrix-synapse -c '\set ON_ERROR_STOP on' -f $state_compressor - fi - - echo "done compressing state for $room_id" - - rm $state_compressor - done <$bigrooms - - echo 'Enabling autovacuum on state_groups_state' - echo 'ALTER TABLE state_groups_state SET (AUTOVACUUM_ENABLED = TRUE);' | - /run/wrappers/bin/sudo -u postgres ${postgresql}/bin/psql -d matrix-synapse - - echo 'Running VACUUM and ANALYZE for state_groups_state ...' - echo 'VACUUM FULL ANALYZE state_groups_state' | - /run/wrappers/bin/sudo -u postgres ${postgresql}/bin/psql -d matrix-synapse - - rm $bigrooms - ''; - - reindexAndVaccum = writeShellScriptBin "reindex-and-vaccum" '' - set -xe - systemctl stop matrix-synapse.target - - echo 'REINDEX (VERBOSE) DATABASE "matrix-synapse"' | - /run/wrappers/bin/sudo -u postgres ${postgresql}/bin/psql -d matrix-synapse - - echo "VACUUM FULL VERBOSE" | - /run/wrappers/bin/sudo -u postgres ${postgresql}/bin/psql -d matrix-synapse - - systemctl start matrix-synapse.target - ''; - - cleanupSynapseScript = writeShellScriptBin "cleanup-synapse" '' - set -xe - ${purgeRemoteMedia}/bin/purge-remote-media - ${cleanupForgottenRooms}/bin/cleanup-forgotten - ${compressState}/bin/compress-state - ${reindexAndVaccum}/bin/reindex-and-vaccum - ''; -in -{ - options.services.cleanup-synapse = { - environmentFile = mkOption { - type = types.path; - description = "The environment file for the synapse cleanup script."; - }; - }; - - config = mkIf synapseCfg.enable { - systemd.services.cleanup-synapse = { - description = "Cleanup synapse"; - startAt = "*-10"; # Cleanup everything on the 10th of each month. - serviceConfig = { - ExecStart = "${cleanupSynapseScript}/bin/cleanup-synapse"; - EnvironmentFile = cfg.environmentFile; - PrivateTmp = true; - ProtectSystem = true; - ProtectHome = "read-only"; - }; - }; - - # Allow root to manage matrix-synapse database. - services.postgresql.ensureUsers = [ - { - name = "root"; - ensurePermissions = { - "DATABASE \"matrix-synapse\"" = "ALL PRIVILEGES"; - "ALL TABLES IN SCHEMA public" = "ALL PRIVILEGES"; - }; - } - ]; - }; -} diff --git a/modules/services/matrix/synapse/default.nix b/modules/services/matrix/synapse/default.nix deleted file mode 100644 index e4472f9..0000000 --- a/modules/services/matrix/synapse/default.nix +++ /dev/null @@ -1,496 +0,0 @@ -# See: https://nixos.org/nixos/manual/index.html#module-services-matrix-synapse -{ config, lib, pkgs, ... }: with lib; -let - matrixDomain = "matrix.${config.networking.domain}"; - cfg = config.services.matrix-synapse-custom; - - # Custom package that tracks with the latest release of Synapse. - package = pkgs.matrix-synapse.overridePythonAttrs (old: rec { - pname = "matrix-synapse"; - version = "1.82.0"; - format = "pyproject"; - - src = pkgs.fetchFromGitHub { - owner = "matrix-org"; - repo = "synapse"; - rev = "v${version}"; - hash = "sha256-j2lsdLYN5LqnIevUkD85i1XNIJa/Vpc1NHhIf2djlis="; - }; - - cargoDeps = pkgs.rustPackages.rustPlatform.fetchCargoTarball { - inherit src; - name = "${pname}-${version}"; - hash = "sha256-iEPfYZd8RWlG5z8BbzESD9O0QV60EBiIIaxm9skt8Uc="; - }; - - postPatch = '' - # Remove setuptools_rust from runtime dependencies - # https://github.com/matrix-org/synapse/blob/v1.69.0/pyproject.toml#L177-L185 - sed -i '/^setuptools_rust =/d' pyproject.toml - sed -i 's/^frozendict = ">=1,!=2.1.2,<2.3.5"/frozendict = ">=1,!=2.1.2,<2.3.6"/g' pyproject.toml - ''; - - # propagatedBuildInputs = (filter (i: i.pname != "matrix-common") old.propagatedBuildInputs) ++ [ - # (pkgs.python3Packages.matrix-common.overridePythonAttrs ( - # old: rec { - # pname = "matrix-common"; - # version = "1.3.0"; - - # src = pkgs.python3Packages.fetchPypi { - # inherit pname version; - # sha256 = "sha256-YuEhzM2fJDQXtX7DenbcRK6xmKelxnr9a4J1mS/yq9E="; - # }; - # } - # )) - # ]; - - doCheck = false; - }); - - packageWithModules = package.python.withPackages (ps: [ - (package.python.pkgs.toPythonModule package) - (pkgs.matrix-synapse-plugins.matrix-synapse-shared-secret-auth.overridePythonAttrs (old: rec { - pname = "matrix-synapse-shared-secret-auth"; - version = "2.0.1"; - - src = pkgs.fetchFromGitHub { - owner = "devture"; - repo = "matrix-synapse-shared-secret-auth"; - rev = version; - sha256 = "sha256-kaok5IwKx97FYDrVIGAtUJfExqDln5vxEKrZda2RdzE="; - }; - buildInputs = [ pkgs.matrix-synapse ]; - })) - ]); - - yamlFormat = pkgs.formats.yaml { }; - - sharedConfig = recursiveUpdate (import ./shared-config.nix ({ inherit config lib pkgs; })) cfg.extraConfig; - sharedConfigFile = yamlFormat.generate - "matrix-synapse-config.yaml" - sharedConfig; - - mkSynapseWorkerService = config: recursiveUpdate config { - after = [ "matrix-synapse.service" ]; - partOf = [ "matrix-synapse.target" ]; - wantedBy = [ "matrix-synapse.target" ]; - serviceConfig = { - Type = "notify"; - User = "matrix-synapse"; - Group = "matrix-synapse"; - WorkingDirectory = cfg.dataDir; - ExecReload = "${pkgs.util-linux}/bin/kill -HUP $MAINPID"; - Restart = "on-failure"; - UMask = "0077"; - }; - }; - - mkSynapseWorkerConfig = port: config: - let - newConfig = { - # The replication listener on the main synapse process. - worker_replication_host = "127.0.0.1"; - worker_replication_http_port = 9093; - - # Default to generic worker. - worker_app = "synapse.app.generic_worker"; - } // config; - newWorkerListeners = (config.worker_listeners or [ ]) ++ [ - { - type = "metrics"; - bind_address = ""; - port = port; - } - ]; - in - newConfig // { worker_listeners = newWorkerListeners; }; - - federationSender1ConfigFile = yamlFormat.generate - "federation-sender-1.yaml" - (mkSynapseWorkerConfig 9101 { - worker_app = "synapse.app.federation_sender"; - worker_name = "federation_sender1"; - }); - - federationSender2ConfigFile = yamlFormat.generate - "federation-sender-2.yaml" - (mkSynapseWorkerConfig 9106 { - worker_app = "synapse.app.federation_sender"; - worker_name = "federation_sender2"; - }); - - federationReader1ConfigFile = yamlFormat.generate - "federation-reader-1.yaml" - (mkSynapseWorkerConfig 9102 { - worker_name = "federation_reader1"; - worker_listeners = [ - # Federation - { - type = "http"; - port = 8009; - bind_address = "0.0.0.0"; - tls = false; - x_forwarded = true; - resources = [ - { names = [ "federation" ]; compress = false; } - ]; - } - ]; - }); - - eventPersister1ConfigFile = yamlFormat.generate - "event-persister-1.yaml" - (mkSynapseWorkerConfig 9103 { - worker_name = "event_persister1"; - # The event persister needs a replication listener - worker_listeners = [ - { - type = "http"; - port = 9091; - bind_address = "127.0.0.1"; - resources = [{ names = [ "replication" ]; }]; - } - ]; - }); - - synchotron1ConfigFile = yamlFormat.generate - "synchotron-1.yaml" - (mkSynapseWorkerConfig 9104 { - worker_name = "synchotron1"; - # The event persister needs a replication listener - worker_listeners = [ - { - type = "http"; - port = 8010; - bind_address = "0.0.0.0"; - resources = [{ names = [ "client" ]; }]; - } - ]; - }); - - mediaRepo1ConfigFile = yamlFormat.generate - "media-repo-1.yaml" - (mkSynapseWorkerConfig 9105 { - worker_name = "media_repo1"; - worker_app = "synapse.app.media_repository"; - # The event persister needs a replication listener - worker_listeners = [ - { - type = "http"; - port = 8011; - bind_address = "0.0.0.0"; - resources = [{ names = [ "media" ]; }]; - } - ]; - }); -in -{ - imports = [ - ./cleanup-synapse.nix - ]; - - options = { - services.matrix-synapse-custom = { - enable = mkEnableOption "Synapse, the reference Matrix homeserver"; - - appServiceConfigFiles = mkOption { - type = types.listOf types.path; - default = [ ]; - description = '' - A list of application service config file to use. - ''; - }; - - dataDir = mkOption { - type = types.path; - default = "/var/lib/matrix-synapse"; - description = '' - The directory where matrix-synapse stores its stateful data such as - certificates, media and uploads. - ''; - }; - - registrationSharedSecretFile = mkOption { - type = types.path; - description = '' - The path to a file that contains the shared registration secret. - ''; - }; - - sharedSecretAuthFile = mkOption { - type = with types; nullOr path; - default = null; - description = '' - The path to a file that contains the shared secret auth secret. - ''; - }; - - emailCfg = mkOption { - type = with types; attrsOf anything; - default = { }; - description = "The email configuration."; - }; - - extraConfig = mkOption { - type = yamlFormat.type; - default = { }; - }; - }; - }; - - config = mkIf cfg.enable { - # Create a user and group for Synapse - users.users.matrix-synapse = { - group = "matrix-synapse"; - home = cfg.dataDir; - createHome = true; - shell = "${pkgs.bash}/bin/bash"; - uid = config.ids.uids.matrix-synapse; - }; - - users.groups.matrix-synapse = { - gid = config.ids.gids.matrix-synapse; - }; - - systemd.targets.matrix-synapse = { - description = "Synapse processes"; - after = [ "network.target" "postgresql.service" ]; - wantedBy = [ "multi-user.target" ]; - }; - - # Run the main Synapse process - systemd.services.matrix-synapse = { - description = "Synapse Matrix homeserver"; - partOf = [ "matrix-synapse.target" ]; - wantedBy = [ "matrix-synapse.target" ]; - preStart = '' - ${packageWithModules}/bin/synapse_homeserver \ - --config-path ${sharedConfigFile} \ - --keys-directory ${cfg.dataDir} \ - --generate-keys - ''; - serviceConfig = { - Type = "notify"; - User = "matrix-synapse"; - Group = "matrix-synapse"; - WorkingDirectory = cfg.dataDir; - ExecStartPre = [ - ("+" + (pkgs.writeShellScript "matrix-synapse-fix-permissions" '' - chown matrix-synapse:matrix-synapse ${cfg.dataDir}/homeserver.signing.key - chmod 0600 ${cfg.dataDir}/homeserver.signing.key - '')) - ]; - ExecStart = '' - ${packageWithModules}/bin/synapse_homeserver \ - --config-path ${sharedConfigFile} \ - --keys-directory ${cfg.dataDir} - ''; - ExecReload = "${pkgs.util-linux}/bin/kill -HUP $MAINPID"; - Restart = "on-failure"; - UMask = "0077"; - }; - }; - - # Run the federation sender worker - systemd.services.matrix-synapse-federation-sender1 = mkSynapseWorkerService { - description = "Synapse Matrix federation sender 1"; - serviceConfig.ExecStart = '' - ${packageWithModules}/bin/python -m synapse.app.federation_sender \ - --config-path ${sharedConfigFile} \ - --config-path ${federationSender1ConfigFile} \ - --keys-directory ${cfg.dataDir} - ''; - }; - - systemd.services.matrix-synapse-federation-sender2 = mkSynapseWorkerService { - description = "Synapse Matrix federation sender 2"; - serviceConfig.ExecStart = '' - ${packageWithModules}/bin/python -m synapse.app.federation_sender \ - --config-path ${sharedConfigFile} \ - --config-path ${federationSender2ConfigFile} \ - --keys-directory ${cfg.dataDir} - ''; - }; - - # Run the federation reader worker - systemd.services.matrix-synapse-federation-reader1 = mkSynapseWorkerService { - description = "Synapse Matrix federation reader 1"; - serviceConfig.ExecStart = '' - ${packageWithModules}/bin/python -m synapse.app.generic_worker \ - --config-path ${sharedConfigFile} \ - --config-path ${federationReader1ConfigFile} \ - --keys-directory ${cfg.dataDir} - ''; - }; - - # Run the event persister worker - systemd.services.matrix-synapse-event-persister1 = mkSynapseWorkerService { - description = "Synapse Matrix event persister 1"; - serviceConfig.ExecStart = '' - ${packageWithModules}/bin/python -m synapse.app.generic_worker \ - --config-path ${sharedConfigFile} \ - --config-path ${eventPersister1ConfigFile} \ - --keys-directory ${cfg.dataDir} - ''; - }; - - # Run the synchotron worker - systemd.services.matrix-synapse-synchotron1 = mkSynapseWorkerService { - description = "Synapse Matrix synchotron 1"; - serviceConfig.ExecStart = '' - ${packageWithModules}/bin/python -m synapse.app.generic_worker \ - --config-path ${sharedConfigFile} \ - --config-path ${synchotron1ConfigFile} \ - --keys-directory ${cfg.dataDir} - ''; - }; - - # Run the media repo worker - systemd.services.matrix-synapse-media-repo1 = mkSynapseWorkerService { - description = "Synapse Matrix media repo 1"; - serviceConfig.ExecStart = '' - ${packageWithModules}/bin/python -m synapse.app.media_repository \ - --config-path ${sharedConfigFile} \ - --config-path ${mediaRepo1ConfigFile} \ - --keys-directory ${cfg.dataDir} - ''; - }; - - # Make sure that Postgres is setup for Synapse. - services.postgresql = { - enable = true; - initialScript = pkgs.writeText "synapse-init.sql" '' - CREATE ROLE "matrix-synapse" WITH LOGIN PASSWORD 'synapse'; - CREATE DATABASE "matrix-synapse" WITH OWNER "matrix-synapse" - TEMPLATE template0 - LC_COLLATE = "C" - LC_CTYPE = "C"; - ''; - }; - - # Ensure that Redis is setup for Synapse. - services.redis.servers."".enable = true; - - # Set up nginx to forward requests properly. - services.nginx = { - enable = true; - virtualHosts = { - ${config.networking.domain} = { - enableACME = true; - forceSSL = true; - - locations = - let - server = { "m.server" = "${matrixDomain}:443"; }; - client = { - "m.homeserver" = { "base_url" = "https://${matrixDomain}"; }; - "m.identity_server" = { "base_url" = "https://vector.im"; }; - }; - in - { - "= /.well-known/matrix/server" = { - extraConfig = '' - add_header Content-Type application/json; - ''; - return = "200 '${builtins.toJSON server}'"; - }; - "= /.well-known/matrix/client" = { - extraConfig = '' - add_header Content-Type application/json; - add_header Access-Control-Allow-Origin *; - ''; - return = "200 '${builtins.toJSON client}'"; - }; - }; - }; - - # Reverse proxy for Matrix client-server and server-server communication - ${matrixDomain} = { - enableACME = true; - forceSSL = true; - - # If they access root, redirect to Element. If they access the API, then - # forward on to Synapse. - locations."/".return = "301 https://app.element.io"; - locations."/_matrix" = { - proxyPass = "http://0.0.0.0:8008"; # without a trailing / - extraConfig = '' - access_log /var/log/nginx/matrix.access.log; - ''; - }; - locations."/_matrix/federation/" = { - proxyPass = "http://0.0.0.0:8009"; # without a trailing / - extraConfig = '' - access_log /var/log/nginx/matrix-federation.access.log; - ''; - }; - locations."~ ^/_matrix/client/.*/(sync|events|initialSync)" = { - proxyPass = "http://0.0.0.0:8010"; # without a trailing / - extraConfig = '' - access_log /var/log/nginx/matrix-synchotron.access.log; - ''; - }; - locations."~ ^/(_matrix/media|_synapse/admin/v1/(purge_media_cache|(room|user)/.*/media.*|media/.*|quarantine_media/.*|users/.*/media))" = { - proxyPass = "http://0.0.0.0:8011"; # without a trailing / - extraConfig = '' - access_log /var/log/nginx/matrix-media-repo.access.log; - ''; - }; - }; - }; - }; - - # Make sure that Prometheus is setup for Synapse. - services.prometheus = { - enable = true; - scrapeConfigs = [ - { - job_name = "synapse"; - scrape_interval = "15s"; - metrics_path = "/_synapse/metrics"; - static_configs = [ - { - targets = [ "0.0.0.0:9009" ]; - labels = { instance = matrixDomain; job = "master"; index = "1"; }; - } - { - # Federation sender 1 - targets = [ "0.0.0.0:9101" ]; - labels = { instance = matrixDomain; job = "federation_sender"; index = "1"; }; - } - { - # Federation sender 2 - targets = [ "0.0.0.0:9106" ]; - labels = { instance = matrixDomain; job = "federation_sender"; index = "2"; }; - } - { - # Federation reader 1 - targets = [ "0.0.0.0:9102" ]; - labels = { instance = matrixDomain; job = "federation_reader"; index = "1"; }; - } - { - # Event persister 1 - targets = [ "0.0.0.0:9103" ]; - labels = { instance = matrixDomain; job = "event_persister"; index = "1"; }; - } - { - # Synchotron 1 - targets = [ "0.0.0.0:9104" ]; - labels = { instance = matrixDomain; job = "synchotron"; index = "1"; }; - } - { - # Media repo 1 - targets = [ "0.0.0.0:9105" ]; - labels = { instance = matrixDomain; job = "media_repo"; index = "1"; }; - } - ]; - } - ]; - }; - - # Add a backup service. - services.backup.backups.matrix = { - path = config.services.matrix-synapse.dataDir; - }; - }; -} diff --git a/modules/services/matrix/synapse/shared-config.nix b/modules/services/matrix/synapse/shared-config.nix deleted file mode 100644 index 770e590..0000000 --- a/modules/services/matrix/synapse/shared-config.nix +++ /dev/null @@ -1,177 +0,0 @@ -# This is organized to match the sections in -# https://github.com/matrix-org/synapse/blob/develop/docs/sample_config.yaml -{ config, lib, pkgs }: with lib; -let - cfg = config.services.matrix-synapse-custom; - yamlFormat = pkgs.formats.yaml { }; - - logConfig = { - version = 1; - formatters.journal_fmt.format = "%(name)s: [%(request)s] %(message)s"; - filters.context = { - "()" = "synapse.util.logcontext.LoggingContextFilter"; - request = ""; - }; - handlers.journal = { - class = "systemd.journal.JournalHandler"; - formatter = "journal_fmt"; - filters = [ "context" ]; - SYSLOG_IDENTIFIER = "synapse"; - }; - root = { level = "INFO"; handlers = [ "journal" ]; }; - loggers = { - shared_secret_authenticator = { level = "INFO"; handlers = [ "journal" ]; }; - }; - disable_existing_loggers = false; - }; -in -{ - # Modules - modules = - if (cfg.sharedSecretAuthFile == null) then [ ] else [ - { - module = "shared_secret_authenticator.SharedSecretAuthProvider"; - config = { - shared_secret = removeSuffix "\n" (readFile cfg.sharedSecretAuthFile); - m_login_password_support_enabled = true; - }; - } - ]; - - # Experimental features - experimental_features = { - # Enable support for the suppressing edit notifications. - msc3958_supress_edit_notifs = true; - msc3952_intentional_mentions = true; - }; - - # Server - server_name = config.networking.domain; - pid_file = "/run/matrix-synapse.pid"; - default_room_version = "9"; - public_baseurl = "https://matrix.${config.networking.domain}"; - listeners = [ - # CS API and Federation - { - type = "http"; - port = 8008; - bind_address = "0.0.0.0"; - tls = false; - x_forwarded = true; - resources = [ - { names = [ "federation" "client" ]; compress = false; } - ]; - } - - # Metrics - { - port = 9009; - bind_address = "0.0.0.0"; - tls = false; - type = "metrics"; - } - - # Replication - { - type = "http"; - port = 9093; - bind_address = "127.0.0.1"; - resources = [{ names = [ "replication" ]; }]; - } - ]; - - # Caching - event_cache_size = "25K"; - caches.global_factor = 1.0; - - # Database - database = { - name = "psycopg2"; - args = { user = "matrix-synapse"; database = "matrix-synapse"; }; - }; - - # Logging - log_config = yamlFormat.generate "matrix-synapse-log-config.yaml" logConfig; - - # Media store - enable_media_repo = false; - media_store_path = "${cfg.dataDir}/media"; - max_upload_size = "250M"; - 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" - "169.254.0.0/16" - "::1/128" - "fe80::/64" - "fc00::/7" - ]; - - media_retention = { - remote_media_lifetime = "90d"; - }; - - url_preview_url_blacklist = [ - # blacklist any URL with a username in its URI - { username = "*"; } - - # Don't try previews for Linear. - { netloc = "linear.app"; } - ]; - - # TURN - # Configure coturn to point at the matrix.org servers. - # TODO actually figure this out eventually - turn_uris = [ - "turn:turn.matrix.org?transport=udp" - "turn:turn.matrix.org?transport=tcp" - ]; - turn_shared_secret = "n0t4ctuAllymatr1Xd0TorgSshar3d5ecret4obvIousreAsons"; - turn_user_lifetime = "1h"; - - # Registration - enable_registration = true; - registration_requires_token = true; - registration_shared_secret = removeSuffix "\n" (readFile cfg.registrationSharedSecretFile); - - # Metrics - enable_metrics = true; - report_stats = true; - - # API Configuration - app_service_config_files = cfg.appServiceConfigFiles; - - # Signing Keys - signing_key_path = "${cfg.dataDir}/homeserver.signing.key"; - trusted_key_servers = [ - { server_name = "matrix.org"; } - ]; - suppress_key_server_warning = true; - - # Email - email = cfg.emailCfg; - - # Workers - send_federation = false; - federation_sender_instances = [ - "federation_sender1" - "federation_sender2" - ]; - instance_map = { - event_persister1 = { - host = "localhost"; - port = 9091; - }; - }; - - stream_writers = { - events = "event_persister1"; - }; - - redis = { - enabled = true; - }; -}