diff options
author | Nguyễn Gia Phong <mcsinyx@disroot.org> | 2022-05-13 16:46:01 +0900 |
---|---|---|
committer | Nguyễn Gia Phong <mcsinyx@disroot.org> | 2022-06-06 13:50:58 +0900 |
commit | 22f3d4e4dbc415c880dabca13fcc11d45e8d53ab (patch) | |
tree | 367780ef2585b66b49774ea241cbb9561cc7a7b2 /nixos/modules/services/matrix | |
parent | 046d0253aa88e47e4d424e02bc548897d5eeacfe (diff) | |
download | nixpkgs-22f3d4e4dbc415c880dabca13fcc11d45e8d53ab.tar nixpkgs-22f3d4e4dbc415c880dabca13fcc11d45e8d53ab.tar.gz nixpkgs-22f3d4e4dbc415c880dabca13fcc11d45e8d53ab.tar.bz2 nixpkgs-22f3d4e4dbc415c880dabca13fcc11d45e8d53ab.tar.lz nixpkgs-22f3d4e4dbc415c880dabca13fcc11d45e8d53ab.tar.xz nixpkgs-22f3d4e4dbc415c880dabca13fcc11d45e8d53ab.tar.zst nixpkgs-22f3d4e4dbc415c880dabca13fcc11d45e8d53ab.zip |
nixos: move matrix services into their category
Diffstat (limited to 'nixos/modules/services/matrix')
-rw-r--r-- | nixos/modules/services/matrix/appservice-discord.nix | 161 | ||||
-rw-r--r-- | nixos/modules/services/matrix/appservice-irc.nix | 232 | ||||
-rw-r--r-- | nixos/modules/services/matrix/conduit.nix | 149 | ||||
-rw-r--r-- | nixos/modules/services/matrix/dendrite.nix | 297 | ||||
-rw-r--r-- | nixos/modules/services/matrix/mautrix-facebook.nix | 195 | ||||
-rw-r--r-- | nixos/modules/services/matrix/mautrix-telegram.nix | 181 | ||||
-rw-r--r-- | nixos/modules/services/matrix/synapse-log_config.yaml (renamed from nixos/modules/services/matrix/matrix-synapse-log_config.yaml) | 0 | ||||
-rw-r--r-- | nixos/modules/services/matrix/synapse.nix (renamed from nixos/modules/services/matrix/matrix-synapse.nix) | 4 | ||||
-rw-r--r-- | nixos/modules/services/matrix/synapse.xml (renamed from nixos/modules/services/matrix/matrix-synapse.xml) | 0 |
9 files changed, 1217 insertions, 2 deletions
diff --git a/nixos/modules/services/matrix/appservice-discord.nix b/nixos/modules/services/matrix/appservice-discord.nix new file mode 100644 index 00000000000..8a8c7f41e3c --- /dev/null +++ b/nixos/modules/services/matrix/appservice-discord.nix @@ -0,0 +1,161 @@ +{ config, options, pkgs, lib, ... }: + +with lib; + +let + dataDir = "/var/lib/matrix-appservice-discord"; + registrationFile = "${dataDir}/discord-registration.yaml"; + appDir = "${pkgs.matrix-appservice-discord}/${pkgs.matrix-appservice-discord.passthru.nodeAppDir}"; + cfg = config.services.matrix-appservice-discord; + opt = options.services.matrix-appservice-discord; + # TODO: switch to configGen.json once RFC42 is implemented + settingsFile = pkgs.writeText "matrix-appservice-discord-settings.json" (builtins.toJSON cfg.settings); + +in { + options = { + services.matrix-appservice-discord = { + enable = mkEnableOption "a bridge between Matrix and Discord"; + + settings = mkOption rec { + # TODO: switch to types.config.json as prescribed by RFC42 once it's implemented + type = types.attrs; + apply = recursiveUpdate default; + default = { + database = { + filename = "${dataDir}/discord.db"; + }; + + # empty values necessary for registration file generation + # actual values defined in environmentFile + auth = { + clientID = ""; + botToken = ""; + }; + }; + example = literalExpression '' + { + bridge = { + domain = "public-domain.tld"; + homeserverUrl = "http://public-domain.tld:8008"; + }; + } + ''; + description = '' + <filename>config.yaml</filename> configuration as a Nix attribute set. + </para> + + <para> + Configuration options should match those described in + <link xlink:href="https://github.com/Half-Shot/matrix-appservice-discord/blob/master/config/config.sample.yaml"> + config.sample.yaml</link>. + </para> + + <para> + <option>config.bridge.domain</option> and <option>config.bridge.homeserverUrl</option> + should be set to match the public host name of the Matrix homeserver for webhooks and avatars to work. + </para> + + <para> + Secret tokens should be specified using <option>environmentFile</option> + instead of this world-readable attribute set. + ''; + }; + + environmentFile = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + File containing environment variables to be passed to the matrix-appservice-discord service, + in which secret tokens can be specified securely by defining values for + <literal>APPSERVICE_DISCORD_AUTH_CLIENT_I_D</literal> and + <literal>APPSERVICE_DISCORD_AUTH_BOT_TOKEN</literal>. + ''; + }; + + url = mkOption { + type = types.str; + default = "http://localhost:${toString cfg.port}"; + defaultText = literalExpression ''"http://localhost:''${toString config.${opt.port}}"''; + description = '' + The URL where the application service is listening for HS requests. + ''; + }; + + port = mkOption { + type = types.port; + default = 9005; # from https://github.com/Half-Shot/matrix-appservice-discord/blob/master/package.json#L11 + description = '' + Port number on which the bridge should listen for internal communication with the Matrix homeserver. + ''; + }; + + localpart = mkOption { + type = with types; nullOr str; + default = null; + description = '' + The user_id localpart to assign to the AS. + ''; + }; + + serviceDependencies = mkOption { + type = with types; listOf str; + default = optional config.services.matrix-synapse.enable "matrix-synapse.service"; + defaultText = literalExpression '' + optional config.services.matrix-synapse.enable "matrix-synapse.service" + ''; + description = '' + List of Systemd services to require and wait for when starting the application service, + such as the Matrix homeserver if it's running on the same host. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + systemd.services.matrix-appservice-discord = { + description = "A bridge between Matrix and Discord."; + + wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ] ++ cfg.serviceDependencies; + after = [ "network-online.target" ] ++ cfg.serviceDependencies; + + preStart = '' + if [ ! -f '${registrationFile}' ]; then + ${pkgs.matrix-appservice-discord}/bin/matrix-appservice-discord \ + --generate-registration \ + --url=${escapeShellArg cfg.url} \ + ${optionalString (cfg.localpart != null) "--localpart=${escapeShellArg cfg.localpart}"} \ + --config='${settingsFile}' \ + --file='${registrationFile}' + fi + ''; + + serviceConfig = { + Type = "simple"; + Restart = "always"; + + ProtectSystem = "strict"; + ProtectHome = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + + DynamicUser = true; + PrivateTmp = true; + WorkingDirectory = appDir; + StateDirectory = baseNameOf dataDir; + UMask = 0027; + EnvironmentFile = cfg.environmentFile; + + ExecStart = '' + ${pkgs.matrix-appservice-discord}/bin/matrix-appservice-discord \ + --file='${registrationFile}' \ + --config='${settingsFile}' \ + --port='${toString cfg.port}' + ''; + }; + }; + }; + + meta.maintainers = with maintainers; [ pacien ]; +} diff --git a/nixos/modules/services/matrix/appservice-irc.nix b/nixos/modules/services/matrix/appservice-irc.nix new file mode 100644 index 00000000000..b041c9c82c5 --- /dev/null +++ b/nixos/modules/services/matrix/appservice-irc.nix @@ -0,0 +1,232 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.services.matrix-appservice-irc; + + pkg = pkgs.matrix-appservice-irc; + bin = "${pkg}/bin/matrix-appservice-irc"; + + jsonType = (pkgs.formats.json {}).type; + + configFile = pkgs.runCommand "matrix-appservice-irc.yml" { + # Because this program will be run at build time, we need `nativeBuildInputs` + nativeBuildInputs = [ (pkgs.python3.withPackages (ps: [ ps.pyyaml ps.jsonschema ])) ]; + preferLocalBuild = true; + + config = builtins.toJSON cfg.settings; + passAsFile = [ "config" ]; + } '' + # The schema is given as yaml, we need to convert it to json + python -c 'import json; import yaml; import sys; json.dump(yaml.safe_load(sys.stdin), sys.stdout)' \ + < ${pkg}/lib/node_modules/matrix-appservice-irc/config.schema.yml \ + > config.schema.json + python -m jsonschema config.schema.json -i $configPath + cp "$configPath" "$out" + ''; + registrationFile = "/var/lib/matrix-appservice-irc/registration.yml"; +in { + options.services.matrix-appservice-irc = with types; { + enable = mkEnableOption "the Matrix/IRC bridge"; + + port = mkOption { + type = port; + description = "The port to listen on"; + default = 8009; + }; + + needBindingCap = mkOption { + type = bool; + description = "Whether the daemon needs to bind to ports below 1024 (e.g. for the ident service)"; + default = false; + }; + + passwordEncryptionKeyLength = mkOption { + type = ints.unsigned; + description = "Length of the key to encrypt IRC passwords with"; + default = 4096; + example = 8192; + }; + + registrationUrl = mkOption { + type = str; + description = '' + The URL where the application service is listening for homeserver requests, + from the Matrix homeserver perspective. + ''; + example = "http://localhost:8009"; + }; + + localpart = mkOption { + type = str; + description = "The user_id localpart to assign to the appservice"; + default = "appservice-irc"; + }; + + settings = mkOption { + description = '' + Configuration for the appservice, see + <link xlink:href="https://github.com/matrix-org/matrix-appservice-irc/blob/${pkgs.matrix-appservice-irc.version}/config.sample.yaml"/> + for supported values + ''; + default = {}; + type = submodule { + freeformType = jsonType; + + options = { + homeserver = mkOption { + description = "Homeserver configuration"; + default = {}; + type = submodule { + freeformType = jsonType; + + options = { + url = mkOption { + type = str; + description = "The URL to the home server for client-server API calls"; + }; + + domain = mkOption { + type = str; + description = '' + The 'domain' part for user IDs on this home server. Usually + (but not always) is the "domain name" part of the homeserver URL. + ''; + }; + }; + }; + }; + + database = mkOption { + default = {}; + description = "Configuration for the database"; + type = submodule { + freeformType = jsonType; + + options = { + engine = mkOption { + type = str; + description = "Which database engine to use"; + default = "nedb"; + example = "postgres"; + }; + + connectionString = mkOption { + type = str; + description = "The database connection string"; + default = "nedb://var/lib/matrix-appservice-irc/data"; + example = "postgres://username:password@host:port/databasename"; + }; + }; + }; + }; + + ircService = mkOption { + default = {}; + description = "IRC bridge configuration"; + type = submodule { + freeformType = jsonType; + + options = { + passwordEncryptionKeyPath = mkOption { + type = str; + description = '' + Location of the key with which IRC passwords are encrypted + for storage. Will be generated on first run if not present. + ''; + default = "/var/lib/matrix-appservice-irc/passkey.pem"; + }; + + servers = mkOption { + type = submodule { freeformType = jsonType; }; + description = "IRC servers to connect to"; + }; + }; + }; + }; + }; + }; + }; + }; + config = mkIf cfg.enable { + systemd.services.matrix-appservice-irc = { + description = "Matrix-IRC bridge"; + before = [ "matrix-synapse.service" ]; # So the registration can be used by Synapse + wantedBy = [ "multi-user.target" ]; + + preStart = '' + umask 077 + # Generate key for crypting passwords + if ! [ -f "${cfg.settings.ircService.passwordEncryptionKeyPath}" ]; then + ${pkgs.openssl}/bin/openssl genpkey \ + -out "${cfg.settings.ircService.passwordEncryptionKeyPath}" \ + -outform PEM \ + -algorithm RSA \ + -pkeyopt "rsa_keygen_bits:${toString cfg.passwordEncryptionKeyLength}" + fi + # Generate registration file + if ! [ -f "${registrationFile}" ]; then + # The easy case: the file has not been generated yet + ${bin} --generate-registration --file ${registrationFile} --config ${configFile} --url ${cfg.registrationUrl} --localpart ${cfg.localpart} + else + # The tricky case: we already have a generation file. Because the NixOS configuration might have changed, we need to + # regenerate it. But this would give the service a new random ID and tokens, so we need to back up and restore them. + # 1. Backup + id=$(grep "^id:.*$" ${registrationFile}) + hs_token=$(grep "^hs_token:.*$" ${registrationFile}) + as_token=$(grep "^as_token:.*$" ${registrationFile}) + # 2. Regenerate + ${bin} --generate-registration --file ${registrationFile} --config ${configFile} --url ${cfg.registrationUrl} --localpart ${cfg.localpart} + # 3. Restore + sed -i "s/^id:.*$/$id/g" ${registrationFile} + sed -i "s/^hs_token:.*$/$hs_token/g" ${registrationFile} + sed -i "s/^as_token:.*$/$as_token/g" ${registrationFile} + fi + # Allow synapse access to the registration + if ${getBin pkgs.glibc}/bin/getent group matrix-synapse > /dev/null; then + chgrp matrix-synapse ${registrationFile} + chmod g+r ${registrationFile} + fi + ''; + + serviceConfig = rec { + Type = "simple"; + ExecStart = "${bin} --config ${configFile} --file ${registrationFile} --port ${toString cfg.port}"; + + ProtectHome = true; + PrivateDevices = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + StateDirectory = "matrix-appservice-irc"; + StateDirectoryMode = "755"; + + User = "matrix-appservice-irc"; + Group = "matrix-appservice-irc"; + + CapabilityBoundingSet = [ "CAP_CHOWN" ] ++ optional (cfg.needBindingCap) "CAP_NET_BIND_SERVICE"; + AmbientCapabilities = CapabilityBoundingSet; + NoNewPrivileges = true; + + LockPersonality = true; + RestrictRealtime = true; + PrivateMounts = true; + SystemCallFilter = "~@aio @clock @cpu-emulation @debug @keyring @memlock @module @mount @obsolete @raw-io @setuid @swap"; + SystemCallArchitectures = "native"; + # AF_UNIX is required to connect to a postgres socket. + RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6"; + }; + }; + + users.groups.matrix-appservice-irc = {}; + users.users.matrix-appservice-irc = { + description = "Service user for the Matrix-IRC bridge"; + group = "matrix-appservice-irc"; + isSystemUser = true; + }; + }; + + # uses attributes of the linked package + meta.buildDocsInSandbox = false; +} diff --git a/nixos/modules/services/matrix/conduit.nix b/nixos/modules/services/matrix/conduit.nix new file mode 100644 index 00000000000..108f64de7aa --- /dev/null +++ b/nixos/modules/services/matrix/conduit.nix @@ -0,0 +1,149 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.matrix-conduit; + + format = pkgs.formats.toml {}; + configFile = format.generate "conduit.toml" cfg.settings; +in + { + meta.maintainers = with maintainers; [ pstn piegames ]; + options.services.matrix-conduit = { + enable = mkEnableOption "matrix-conduit"; + + extraEnvironment = mkOption { + type = types.attrsOf types.str; + description = "Extra Environment variables to pass to the conduit server."; + default = {}; + example = { RUST_BACKTRACE="yes"; }; + }; + + package = mkOption { + type = types.package; + default = pkgs.matrix-conduit; + defaultText = "pkgs.matrix-conduit"; + example = "pkgs.matrix-conduit"; + description = '' + Package of the conduit matrix server to use. + ''; + }; + + settings = mkOption { + type = types.submodule { + freeformType = format.type; + options = { + global.server_name = mkOption { + type = types.str; + example = "example.com"; + description = "The server_name is the name of this server. It is used as a suffix for user # and room ids."; + }; + global.port = mkOption { + type = types.port; + default = 6167; + description = "The port Conduit will be running on. You need to set up a reverse proxy in your web server (e.g. apache or nginx), so all requests to /_matrix on port 443 and 8448 will be forwarded to the Conduit instance running on this port"; + }; + global.max_request_size = mkOption { + type = types.ints.positive; + default = 20000000; + description = "Max request size in bytes. Don't forget to also change it in the proxy."; + }; + global.allow_registration = mkOption { + type = types.bool; + default = false; + description = "Whether new users can register on this server."; + }; + global.allow_encryption = mkOption { + type = types.bool; + default = true; + description = "Whether new encrypted rooms can be created. Note: existing rooms will continue to work."; + }; + global.allow_federation = mkOption { + type = types.bool; + default = true; + description = '' + Whether this server federates with other servers. + ''; + }; + global.trusted_servers = mkOption { + type = types.listOf types.str; + default = [ "matrix.org" ]; + description = "Servers trusted with signing server keys."; + }; + global.address = mkOption { + type = types.str; + default = "::1"; + description = "Address to listen on for connections by the reverse proxy/tls terminator."; + }; + global.database_path = mkOption { + type = types.str; + default = "/var/lib/matrix-conduit/"; + readOnly = true; + description = '' + Path to the conduit database, the directory where conduit will save its data. + Note that due to using the DynamicUser feature of systemd, this value should not be changed + and is set to be read only. + ''; + }; + global.database_backend = mkOption { + type = types.enum [ "sqlite" "rocksdb" ]; + default = "sqlite"; + example = "rocksdb"; + description = '' + The database backend for the service. Switching it on an existing + instance will require manual migration of data. + ''; + }; + }; + }; + default = {}; + description = '' + Generates the conduit.toml configuration file. Refer to + <link xlink:href="https://gitlab.com/famedly/conduit/-/blob/master/conduit-example.toml"/> + for details on supported values. + Note that database_path can not be edited because the service's reliance on systemd StateDir. + ''; + }; + }; + + config = mkIf cfg.enable { + systemd.services.conduit = { + description = "Conduit Matrix Server"; + documentation = [ "https://gitlab.com/famedly/conduit/" ]; + wantedBy = [ "multi-user.target" ]; + environment = lib.mkMerge ([ + { CONDUIT_CONFIG = configFile; } + cfg.extraEnvironment + ]); + serviceConfig = { + DynamicUser = true; + User = "conduit"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + PrivateDevices = true; + PrivateMounts = true; + PrivateUsers = true; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ + "@system-service" + "~@privileged" + ]; + StateDirectory = "matrix-conduit"; + ExecStart = "${cfg.package}/bin/conduit"; + Restart = "on-failure"; + RestartSec = 10; + StartLimitBurst = 5; + }; + }; + }; + } diff --git a/nixos/modules/services/matrix/dendrite.nix b/nixos/modules/services/matrix/dendrite.nix new file mode 100644 index 00000000000..54052084b33 --- /dev/null +++ b/nixos/modules/services/matrix/dendrite.nix @@ -0,0 +1,297 @@ +{ config, lib, pkgs, ... }: +let + cfg = config.services.dendrite; + settingsFormat = pkgs.formats.yaml { }; + configurationYaml = settingsFormat.generate "dendrite.yaml" cfg.settings; + workingDir = "/var/lib/dendrite"; +in +{ + options.services.dendrite = { + enable = lib.mkEnableOption "matrix.org dendrite"; + httpPort = lib.mkOption { + type = lib.types.nullOr lib.types.port; + default = 8008; + description = '' + The port to listen for HTTP requests on. + ''; + }; + httpsPort = lib.mkOption { + type = lib.types.nullOr lib.types.port; + default = null; + description = '' + The port to listen for HTTPS requests on. + ''; + }; + tlsCert = lib.mkOption { + type = lib.types.nullOr lib.types.path; + example = "/var/lib/dendrite/server.cert"; + default = null; + description = '' + The path to the TLS certificate. + + <programlisting> + nix-shell -p dendrite --command "generate-keys --tls-cert server.crt --tls-key server.key" + </programlisting> + ''; + }; + tlsKey = lib.mkOption { + type = lib.types.nullOr lib.types.path; + example = "/var/lib/dendrite/server.key"; + default = null; + description = '' + The path to the TLS key. + + <programlisting> + nix-shell -p dendrite --command "generate-keys --tls-cert server.crt --tls-key server.key" + </programlisting> + ''; + }; + environmentFile = lib.mkOption { + type = lib.types.nullOr lib.types.path; + example = "/var/lib/dendrite/registration_secret"; + default = null; + description = '' + Environment file as defined in <citerefentry> + <refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum> + </citerefentry>. + Secrets may be passed to the service without adding them to the world-readable + Nix store, by specifying placeholder variables as the option value in Nix and + setting these variables accordingly in the environment file. Currently only used + for the registration secret to allow secure registration when + client_api.registration_disabled is true. + + <programlisting> + # snippet of dendrite-related config + services.dendrite.settings.client_api.registration_shared_secret = "$REGISTRATION_SHARED_SECRET"; + </programlisting> + + <programlisting> + # content of the environment file + REGISTRATION_SHARED_SECRET=verysecretpassword + </programlisting> + + Note that this file needs to be available on the host on which + <literal>dendrite</literal> is running. + ''; + }; + loadCredential = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + example = [ "private_key:/path/to/my_private_key" ]; + description = '' + This can be used to pass secrets to the systemd service without adding them to + the nix store. + To use the example setting, see the example of + <option>services.dendrite.settings.global.private_key</option>. + See the LoadCredential section of systemd.exec manual for more information. + ''; + }; + settings = lib.mkOption { + type = lib.types.submodule { + freeformType = settingsFormat.type; + options.global = { + server_name = lib.mkOption { + type = lib.types.str; + example = "example.com"; + description = '' + The domain name of the server, with optional explicit port. + This is used by remote servers to connect to this server. + This is also the last part of your UserID. + ''; + }; + private_key = lib.mkOption { + type = lib.types.either + lib.types.path + (lib.types.strMatching "^\\$CREDENTIALS_DIRECTORY/.+"); + example = "$CREDENTIALS_DIRECTORY/private_key"; + description = '' + The path to the signing private key file, used to sign + requests and events. + + <programlisting> + nix-shell -p dendrite --command "generate-keys --private-key matrix_key.pem" + </programlisting> + ''; + }; + trusted_third_party_id_servers = lib.mkOption { + type = lib.types.listOf lib.types.str; + example = [ "matrix.org" ]; + default = [ "matrix.org" "vector.im" ]; + description = '' + Lists of domains that the server will trust as identity + servers to verify third party identifiers such as phone + numbers and email addresses + ''; + }; + }; + options.app_service_api.database = { + connection_string = lib.mkOption { + type = lib.types.str; + default = "file:federationapi.db"; + description = '' + Database for the Appservice API. + ''; + }; + }; + options.client_api = { + registration_disabled = lib.mkOption { + type = lib.types.bool; + default = true; + description = '' + Whether to disable user registration to the server + without the shared secret. + ''; + }; + }; + options.federation_api.database = { + connection_string = lib.mkOption { + type = lib.types.str; + default = "file:federationapi.db"; + description = '' + Database for the Federation API. + ''; + }; + }; + options.key_server.database = { + connection_string = lib.mkOption { + type = lib.types.str; + default = "file:keyserver.db"; + description = '' + Database for the Key Server (for end-to-end encryption). + ''; + }; + }; + options.media_api = { + database = { + connection_string = lib.mkOption { + type = lib.types.str; + default = "file:mediaapi.db"; + description = '' + Database for the Media API. + ''; + }; + }; + base_path = lib.mkOption { + type = lib.types.str; + default = "${workingDir}/media_store"; + description = '' + Storage path for uploaded media. + ''; + }; + }; + options.room_server.database = { + connection_string = lib.mkOption { + type = lib.types.str; + default = "file:roomserver.db"; + description = '' + Database for the Room Server. + ''; + }; + }; + options.sync_api.database = { + connection_string = lib.mkOption { + type = lib.types.str; + default = "file:syncserver.db"; + description = '' + Database for the Sync API. + ''; + }; + }; + options.user_api = { + account_database = { + connection_string = lib.mkOption { + type = lib.types.str; + default = "file:userapi_accounts.db"; + description = '' + Database for the User API, accounts. + ''; + }; + }; + device_database = { + connection_string = lib.mkOption { + type = lib.types.str; + default = "file:userapi_devices.db"; + description = '' + Database for the User API, devices. + ''; + }; + }; + }; + options.mscs = { + database = { + connection_string = lib.mkOption { + type = lib.types.str; + default = "file:mscs.db"; + description = '' + Database for exerimental MSC's. + ''; + }; + }; + }; + }; + default = { }; + description = '' + Configuration for dendrite, see: + <link xlink:href="https://github.com/matrix-org/dendrite/blob/master/dendrite-config.yaml"/> + for available options with which to populate settings. + ''; + }; + openRegistration = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Allow open registration without secondary verification (reCAPTCHA). + ''; + }; + }; + + config = lib.mkIf cfg.enable { + assertions = [{ + assertion = cfg.httpsPort != null -> (cfg.tlsCert != null && cfg.tlsKey != null); + message = '' + If Dendrite is configured to use https, tlsCert and tlsKey must be provided. + + nix-shell -p dendrite --command "generate-keys --tls-cert server.crt --tls-key server.key" + ''; + }]; + + systemd.services.dendrite = { + description = "Dendrite Matrix homeserver"; + after = [ + "network.target" + ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "simple"; + DynamicUser = true; + StateDirectory = "dendrite"; + WorkingDirectory = workingDir; + RuntimeDirectory = "dendrite"; + RuntimeDirectoryMode = "0700"; + LimitNOFILE = 65535; + EnvironmentFile = lib.mkIf (cfg.environmentFile != null) cfg.environmentFile; + LoadCredential = cfg.loadCredential; + ExecStartPre = '' + ${pkgs.envsubst}/bin/envsubst \ + -i ${configurationYaml} \ + -o /run/dendrite/dendrite.yaml + ''; + ExecStart = lib.strings.concatStringsSep " " ([ + "${pkgs.dendrite}/bin/dendrite-monolith-server" + "--config /run/dendrite/dendrite.yaml" + ] ++ lib.optionals (cfg.httpPort != null) [ + "--http-bind-address :${builtins.toString cfg.httpPort}" + ] ++ lib.optionals (cfg.httpsPort != null) [ + "--https-bind-address :${builtins.toString cfg.httpsPort}" + "--tls-cert ${cfg.tlsCert}" + "--tls-key ${cfg.tlsKey}" + ] ++ lib.optionals cfg.openRegistration [ + "--really-enable-open-registration" + ]); + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + Restart = "on-failure"; + }; + }; + }; + meta.maintainers = lib.teams.matrix.members; +} diff --git a/nixos/modules/services/matrix/mautrix-facebook.nix b/nixos/modules/services/matrix/mautrix-facebook.nix new file mode 100644 index 00000000000..e046c791ac0 --- /dev/null +++ b/nixos/modules/services/matrix/mautrix-facebook.nix @@ -0,0 +1,195 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.services.mautrix-facebook; + settingsFormat = pkgs.formats.json {}; + settingsFile = settingsFormat.generate "mautrix-facebook-config.json" cfg.settings; + + puppetRegex = concatStringsSep + ".*" + (map + escapeRegex + (splitString + "{userid}" + cfg.settings.bridge.username_template)); +in { + options = { + services.mautrix-facebook = { + enable = mkEnableOption "Mautrix-Facebook, a Matrix-Facebook hybrid puppeting/relaybot bridge"; + + settings = mkOption rec { + apply = recursiveUpdate default; + type = settingsFormat.type; + default = { + homeserver = { + address = "http://localhost:8008"; + }; + + appservice = rec { + address = "http://${hostname}:${toString port}"; + hostname = "localhost"; + port = 29319; + + database = "postgresql://"; + + bot_username = "facebookbot"; + }; + + metrics.enabled = false; + manhole.enabled = false; + + bridge = { + encryption = { + allow = true; + default = true; + }; + username_template = "facebook_{userid}"; + }; + + logging = { + version = 1; + formatters.journal_fmt.format = "%(name)s: %(message)s"; + handlers.journal = { + class = "systemd.journal.JournalHandler"; + formatter = "journal_fmt"; + SYSLOG_IDENTIFIER = "mautrix-facebook"; + }; + root = { + level = "INFO"; + handlers = ["journal"]; + }; + }; + }; + example = literalExpression '' + { + homeserver = { + address = "http://localhost:8008"; + domain = "mydomain.example"; + }; + + bridge.permissions = { + "@admin:mydomain.example" = "admin"; + "mydomain.example" = "user"; + }; + } + ''; + description = '' + <filename>config.yaml</filename> configuration as a Nix attribute set. + Configuration options should match those described in + <link xlink:href="https://github.com/mautrix/facebook/blob/master/mautrix_facebook/example-config.yaml"> + example-config.yaml</link>. + </para> + + <para> + Secret tokens should be specified using <option>environmentFile</option> + instead of this world-readable attribute set. + ''; + }; + + environmentFile = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + File containing environment variables to be passed to the mautrix-telegram service. + + Any config variable can be overridden by setting <literal>MAUTRIX_FACEBOOK_SOME_KEY</literal> to override the <literal>some.key</literal> variable. + ''; + }; + + configurePostgresql = mkOption { + type = types.bool; + default = true; + description = '' + Enable PostgreSQL and create a user and database for mautrix-facebook. The default <literal>settings</literal> reference this database, if you disable this option you must provide a database URL. + ''; + }; + + registrationData = mkOption { + type = types.attrs; + default = {}; + description = '' + Output data for appservice registration. Simply make any desired changes and serialize to JSON. Note that this data contains secrets so think twice before putting it into the nix store. + + Currently <literal>as_token</literal> and <literal>hs_token</literal> need to be added as they are not known to this module. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + users.users.mautrix-facebook = { + group = "mautrix-facebook"; + isSystemUser = true; + }; + + services.postgresql = mkIf cfg.configurePostgresql { + ensureDatabases = ["mautrix-facebook"]; + ensureUsers = [{ + name = "mautrix-facebook"; + ensurePermissions = { + "DATABASE \"mautrix-facebook\"" = "ALL PRIVILEGES"; + }; + }]; + }; + + systemd.services.mautrix-facebook = rec { + wantedBy = [ "multi-user.target" ]; + wants = [ + "network-online.target" + ] ++ optional config.services.matrix-synapse.enable "matrix-synapse.service" + ++ optional cfg.configurePostgresql "postgresql.service"; + after = wants; + + serviceConfig = { + Type = "simple"; + Restart = "always"; + + User = "mautrix-facebook"; + + ProtectSystem = "strict"; + ProtectHome = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + PrivateTmp = true; + + EnvironmentFile = cfg.environmentFile; + + ExecStart = '' + ${pkgs.mautrix-facebook}/bin/mautrix-facebook --config=${settingsFile} + ''; + }; + }; + + services.mautrix-facebook = { + registrationData = { + id = "mautrix-facebook"; + + namespaces = { + users = [ + { + exclusive = true; + regex = escapeRegex "@${cfg.settings.appservice.bot_username}:${cfg.settings.homeserver.domain}"; + } + { + exclusive = true; + regex = "@${puppetRegex}:${escapeRegex cfg.settings.homeserver.domain}"; + } + ]; + aliases = []; + }; + + url = cfg.settings.appservice.address; + sender_localpart = "mautrix-facebook-sender"; + + rate_limited = false; + "de.sorunome.msc2409.push_ephemeral" = true; + push_ephemeral = true; + }; + }; + }; + + meta.maintainers = with maintainers; [ kevincox ]; +} diff --git a/nixos/modules/services/matrix/mautrix-telegram.nix b/nixos/modules/services/matrix/mautrix-telegram.nix new file mode 100644 index 00000000000..794c4dd9ddc --- /dev/null +++ b/nixos/modules/services/matrix/mautrix-telegram.nix @@ -0,0 +1,181 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + dataDir = "/var/lib/mautrix-telegram"; + registrationFile = "${dataDir}/telegram-registration.yaml"; + cfg = config.services.mautrix-telegram; + settingsFormat = pkgs.formats.json {}; + settingsFileUnsubstituted = settingsFormat.generate "mautrix-telegram-config-unsubstituted.json" cfg.settings; + settingsFile = "${dataDir}/config.json"; + +in { + options = { + services.mautrix-telegram = { + enable = mkEnableOption "Mautrix-Telegram, a Matrix-Telegram hybrid puppeting/relaybot bridge"; + + settings = mkOption rec { + apply = recursiveUpdate default; + inherit (settingsFormat) type; + default = { + appservice = rec { + database = "sqlite:///${dataDir}/mautrix-telegram.db"; + database_opts = {}; + hostname = "0.0.0.0"; + port = 8080; + address = "http://localhost:${toString port}"; + }; + + bridge = { + permissions."*" = "relaybot"; + relaybot.whitelist = [ ]; + double_puppet_server_map = {}; + login_shared_secret_map = {}; + }; + + logging = { + version = 1; + + formatters.precise.format = "[%(levelname)s@%(name)s] %(message)s"; + + handlers.console = { + class = "logging.StreamHandler"; + formatter = "precise"; + }; + + loggers = { + mau.level = "INFO"; + telethon.level = "INFO"; + + # prevent tokens from leaking in the logs: + # https://github.com/tulir/mautrix-telegram/issues/351 + aiohttp.level = "WARNING"; + }; + + # log to console/systemd instead of file + root = { + level = "INFO"; + handlers = [ "console" ]; + }; + }; + }; + example = literalExpression '' + { + homeserver = { + address = "http://localhost:8008"; + domain = "public-domain.tld"; + }; + + appservice.public = { + prefix = "/public"; + external = "https://public-appservice-address/public"; + }; + + bridge.permissions = { + "example.com" = "full"; + "@admin:example.com" = "admin"; + }; + } + ''; + description = '' + <filename>config.yaml</filename> configuration as a Nix attribute set. + Configuration options should match those described in + <link xlink:href="https://github.com/tulir/mautrix-telegram/blob/master/example-config.yaml"> + example-config.yaml</link>. + </para> + + <para> + Secret tokens should be specified using <option>environmentFile</option> + instead of this world-readable attribute set. + ''; + }; + + environmentFile = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + File containing environment variables to be passed to the mautrix-telegram service, + in which secret tokens can be specified securely by defining values for + <literal>MAUTRIX_TELEGRAM_APPSERVICE_AS_TOKEN</literal>, + <literal>MAUTRIX_TELEGRAM_APPSERVICE_HS_TOKEN</literal>, + <literal>MAUTRIX_TELEGRAM_TELEGRAM_API_ID</literal>, + <literal>MAUTRIX_TELEGRAM_TELEGRAM_API_HASH</literal> and optionally + <literal>MAUTRIX_TELEGRAM_TELEGRAM_BOT_TOKEN</literal>. + ''; + }; + + serviceDependencies = mkOption { + type = with types; listOf str; + default = optional config.services.matrix-synapse.enable "matrix-synapse.service"; + defaultText = literalExpression '' + optional config.services.matrix-synapse.enable "matrix-synapse.service" + ''; + description = '' + List of Systemd services to require and wait for when starting the application service. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + systemd.services.mautrix-telegram = { + description = "Mautrix-Telegram, a Matrix-Telegram hybrid puppeting/relaybot bridge."; + + wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ] ++ cfg.serviceDependencies; + after = [ "network-online.target" ] ++ cfg.serviceDependencies; + + preStart = '' + # Not all secrets can be passed as environment variable (yet) + # https://github.com/tulir/mautrix-telegram/issues/584 + [ -f ${settingsFile} ] && rm -f ${settingsFile} + old_umask=$(umask) + umask 0177 + ${pkgs.envsubst}/bin/envsubst \ + -o ${settingsFile} \ + -i ${settingsFileUnsubstituted} + umask $old_umask + + # generate the appservice's registration file if absent + if [ ! -f '${registrationFile}' ]; then + ${pkgs.mautrix-telegram}/bin/mautrix-telegram \ + --generate-registration \ + --base-config='${pkgs.mautrix-telegram}/${pkgs.mautrix-telegram.pythonModule.sitePackages}/mautrix_telegram/example-config.yaml' \ + --config='${settingsFile}' \ + --registration='${registrationFile}' + fi + '' + lib.optionalString (pkgs.mautrix-telegram ? alembic) '' + # run automatic database init and migration scripts + ${pkgs.mautrix-telegram.alembic}/bin/alembic -x config='${settingsFile}' upgrade head + ''; + + serviceConfig = { + Type = "simple"; + Restart = "always"; + + ProtectSystem = "strict"; + ProtectHome = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + + DynamicUser = true; + PrivateTmp = true; + WorkingDirectory = pkgs.mautrix-telegram; # necessary for the database migration scripts to be found + StateDirectory = baseNameOf dataDir; + UMask = 0027; + EnvironmentFile = cfg.environmentFile; + + ExecStart = '' + ${pkgs.mautrix-telegram}/bin/mautrix-telegram \ + --config='${settingsFile}' + ''; + }; + + restartTriggers = [ settingsFileUnsubstituted ]; + }; + }; + + meta.maintainers = with maintainers; [ pacien vskilet ]; +} diff --git a/nixos/modules/services/matrix/matrix-synapse-log_config.yaml b/nixos/modules/services/matrix/synapse-log_config.yaml index d85bdd1208f..d85bdd1208f 100644 --- a/nixos/modules/services/matrix/matrix-synapse-log_config.yaml +++ b/nixos/modules/services/matrix/synapse-log_config.yaml diff --git a/nixos/modules/services/matrix/matrix-synapse.nix b/nixos/modules/services/matrix/synapse.nix index 87a977f8e1e..b3108484fae 100644 --- a/nixos/modules/services/matrix/matrix-synapse.nix +++ b/nixos/modules/services/matrix/synapse.nix @@ -285,7 +285,7 @@ in { log_config = mkOption { type = types.path; - default = ./matrix-synapse-log_config.yaml; + default = ./synapse-log_config.yaml; description = '' The file that holds the logging configuration. ''; @@ -767,7 +767,7 @@ in { meta = { buildDocsInSandbox = false; - doc = ./matrix-synapse.xml; + doc = ./synapse.xml; maintainers = teams.matrix.members; }; diff --git a/nixos/modules/services/matrix/matrix-synapse.xml b/nixos/modules/services/matrix/synapse.xml index cf33957d58e..cf33957d58e 100644 --- a/nixos/modules/services/matrix/matrix-synapse.xml +++ b/nixos/modules/services/matrix/synapse.xml |