diff options
author | sternenseemann <sternenseemann@systemli.org> | 2021-11-13 12:35:55 +0100 |
---|---|---|
committer | sternenseemann <sternenseemann@systemli.org> | 2021-11-13 12:35:55 +0100 |
commit | 1a1e32f023a2a8a928c1f06ed7b714e05c191524 (patch) | |
tree | da2925bcaece20726f708a27b91101ecdf88566d /nixos | |
parent | c0c7fbda14cd086d25ab9250025a32df1c1da61d (diff) | |
parent | 126db930e3544a4d42b0c9b2a2f1381cdd27004b (diff) | |
download | nixlib-1a1e32f023a2a8a928c1f06ed7b714e05c191524.tar nixlib-1a1e32f023a2a8a928c1f06ed7b714e05c191524.tar.gz nixlib-1a1e32f023a2a8a928c1f06ed7b714e05c191524.tar.bz2 nixlib-1a1e32f023a2a8a928c1f06ed7b714e05c191524.tar.lz nixlib-1a1e32f023a2a8a928c1f06ed7b714e05c191524.tar.xz nixlib-1a1e32f023a2a8a928c1f06ed7b714e05c191524.tar.zst nixlib-1a1e32f023a2a8a928c1f06ed7b714e05c191524.zip |
Merge remote-tracking branch 'origin/master' into staging-next
Conflicts were caused by 2043dbb6faa9e21b0fb500161542e30d6c8bc680 interacting with b2767b69ddc702eb926633a5f2336534352cb7b8.
Diffstat (limited to 'nixos')
-rw-r--r-- | nixos/modules/module-list.nix | 3 | ||||
-rw-r--r-- | nixos/modules/services/matrix/mjolnir.nix | 240 | ||||
-rw-r--r-- | nixos/modules/services/matrix/mjolnir.xml | 134 | ||||
-rw-r--r-- | nixos/modules/services/matrix/pantalaimon-options.nix | 70 | ||||
-rw-r--r-- | nixos/modules/services/matrix/pantalaimon.nix | 70 | ||||
-rw-r--r-- | nixos/modules/services/web-apps/openwebrx.nix | 33 | ||||
-rw-r--r-- | nixos/modules/virtualisation/lxc-container.nix | 2 | ||||
-rw-r--r-- | nixos/tests/all-tests.nix | 2 | ||||
-rw-r--r-- | nixos/tests/matrix/mjolnir.nix | 165 | ||||
-rw-r--r-- | nixos/tests/matrix/pantalaimon.nix | 65 |
10 files changed, 783 insertions, 1 deletions
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index c19dcd3293b6..49509171c3f6 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -485,6 +485,8 @@ ./services/mail/roundcube.nix ./services/mail/sympa.nix ./services/mail/nullmailer.nix + ./services/matrix/mjolnir.nix + ./services/matrix/pantalaimon.nix ./services/misc/ananicy.nix ./services/misc/airsonic.nix ./services/misc/ankisyncd.nix @@ -1016,6 +1018,7 @@ ./services/web-apps/pgpkeyserver-lite.nix ./services/web-apps/matomo.nix ./services/web-apps/moinmoin.nix + ./services/web-apps/openwebrx.nix ./services/web-apps/restya-board.nix ./services/web-apps/sogo.nix ./services/web-apps/rss-bridge.nix diff --git a/nixos/modules/services/matrix/mjolnir.nix b/nixos/modules/services/matrix/mjolnir.nix new file mode 100644 index 000000000000..8a54f93d98d8 --- /dev/null +++ b/nixos/modules/services/matrix/mjolnir.nix @@ -0,0 +1,240 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + cfg = config.services.mjolnir; + + yamlConfig = { + inherit (cfg) dataPath managementRoom protectedRooms; + + accessToken = "@ACCESS_TOKEN@"; # will be replaced in "generateConfig" + homeserverUrl = + if cfg.pantalaimon.enable then + "http://${cfg.pantalaimon.options.listenAddress}:${toString cfg.pantalaimon.options.listenPort}" + else + cfg.homeserverUrl; + + pantalaimon = { + inherit (cfg.pantalaimon) username; + + use = cfg.pantalaimon.enable; + password = "@PANTALAIMON_PASSWORD@"; # will be replaced in "generateConfig" + }; + }; + + moduleConfigFile = pkgs.writeText "module-config.yaml" ( + generators.toYAML { } (filterAttrs (_: v: v != null) + (fold recursiveUpdate { } [ yamlConfig cfg.settings ]))); + + # these config files will be merged one after the other to build the final config + configFiles = [ + "${pkgs.mjolnir}/share/mjolnir/config/default.yaml" + moduleConfigFile + ]; + + # this will generate the default.yaml file with all configFiles as inputs and + # replace all secret strings using replace-secret + generateConfig = pkgs.writeShellScript "mjolnir-generate-config" ( + let + yqEvalStr = concatImapStringsSep " * " (pos: _: "select(fileIndex == ${toString (pos - 1)})") configFiles; + yqEvalArgs = concatStringsSep " " configFiles; + in + '' + set -euo pipefail + + umask 077 + + # mjolnir will try to load a config from "./config/default.yaml" in the working directory + # -> let's place the generated config there + mkdir -p ${cfg.dataPath}/config + + # merge all config files into one, overriding settings of the previous one with the next config + # e.g. "eval-all 'select(fileIndex == 0) * select(fileIndex == 1)' filea.yaml fileb.yaml" will merge filea.yaml with fileb.yaml + ${pkgs.yq-go}/bin/yq eval-all -P '${yqEvalStr}' ${yqEvalArgs} > ${cfg.dataPath}/config/default.yaml + + ${optionalString (cfg.accessTokenFile != null) '' + ${pkgs.replace-secret}/bin/replace-secret '@ACCESS_TOKEN@' '${cfg.accessTokenFile}' ${cfg.dataPath}/config/default.yaml + ''} + ${optionalString (cfg.pantalaimon.passwordFile != null) '' + ${pkgs.replace-secret}/bin/replace-secret '@PANTALAIMON_PASSWORD@' '${cfg.pantalaimon.passwordFile}' ${cfg.dataPath}/config/default.yaml + ''} + '' + ); +in +{ + options.services.mjolnir = { + enable = mkEnableOption "Mjolnir, a moderation tool for Matrix"; + + homeserverUrl = mkOption { + type = types.str; + default = "https://matrix.org"; + description = '' + Where the homeserver is located (client-server URL). + + If <literal>pantalaimon.enable</literal> is <literal>true</literal>, this option will become the homeserver to which <literal>pantalaimon</literal> connects. + The listen address of <literal>pantalaimon</literal> will then become the <literal>homeserverUrl</literal> of <literal>mjolnir</literal>. + ''; + }; + + accessTokenFile = mkOption { + type = with types; nullOr path; + default = null; + description = '' + File containing the matrix access token for the <literal>mjolnir</literal> user. + ''; + }; + + pantalaimon = mkOption { + description = '' + <literal>pantalaimon</literal> options (enables E2E Encryption support). + + This will create a <literal>pantalaimon</literal> instance with the name "mjolnir". + ''; + default = { }; + type = types.submodule { + options = { + enable = mkEnableOption '' + If true, accessToken is ignored and the username/password below will be + used instead. The access token of the bot will be stored in the dataPath. + ''; + + username = mkOption { + type = types.str; + description = "The username to login with."; + }; + + passwordFile = mkOption { + type = with types; nullOr path; + default = null; + description = '' + File containing the matrix password for the <literal>mjolnir</literal> user. + ''; + }; + + options = mkOption { + type = types.submodule (import ./pantalaimon-options.nix); + default = { }; + description = '' + passthrough additional options to the <literal>pantalaimon</literal> service. + ''; + }; + }; + }; + }; + + dataPath = mkOption { + type = types.path; + default = "/var/lib/mjolnir"; + description = '' + The directory the bot should store various bits of information in. + ''; + }; + + managementRoom = mkOption { + type = types.str; + default = "#moderators:example.org"; + description = '' + The room ID where people can use the bot. The bot has no access controls, so + anyone in this room can use the bot - secure your room! + This should be a room alias or room ID - not a matrix.to URL. + Note: <literal>mjolnir</literal> is fairly verbose - expect a lot of messages from it. + ''; + }; + + protectedRooms = mkOption { + type = types.listOf types.str; + default = [ ]; + example = literalExpression '' + [ + "https://matrix.to/#/#yourroom:example.org" + "https://matrix.to/#/#anotherroom:example.org" + ] + ''; + description = '' + A list of rooms to protect (matrix.to URLs). + ''; + }; + + settings = mkOption { + default = { }; + type = (pkgs.formats.yaml { }).type; + example = literalExpression '' + { + autojoinOnlyIfManager = true; + automaticallyRedactForReasons = [ "spam" "advertising" ]; + } + ''; + description = '' + Additional settings (see <link xlink:href="https://github.com/matrix-org/mjolnir/blob/main/config/default.yaml">mjolnir default config</link> for available settings). These settings will override settings made by the module config. + ''; + }; + }; + + config = mkIf config.services.mjolnir.enable { + assertions = [ + { + assertion = !(cfg.pantalaimon.enable && cfg.pantalaimon.passwordFile == null); + message = "Specify pantalaimon.passwordFile"; + } + { + assertion = !(cfg.pantalaimon.enable && cfg.accessTokenFile != null); + message = "Do not specify accessTokenFile when using pantalaimon"; + } + { + assertion = !(!cfg.pantalaimon.enable && cfg.accessTokenFile == null); + message = "Specify accessTokenFile when not using pantalaimon"; + } + ]; + + services.pantalaimon-headless.instances."mjolnir" = mkIf cfg.pantalaimon.enable + { + homeserver = cfg.homeserverUrl; + } // cfg.pantalaimon.options; + + systemd.services.mjolnir = { + description = "mjolnir - a moderation tool for Matrix"; + wants = [ "network-online.target" ] ++ optionals (cfg.pantalaimon.enable) [ "pantalaimon-mjolnir.service" ]; + after = [ "network-online.target" ] ++ optionals (cfg.pantalaimon.enable) [ "pantalaimon-mjolnir.service" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + ExecStart = ''${pkgs.mjolnir}/bin/mjolnir''; + ExecStartPre = [ generateConfig ]; + WorkingDirectory = cfg.dataPath; + StateDirectory = "mjolnir"; + StateDirectoryMode = "0700"; + ProtectSystem = "strict"; + ProtectHome = true; + PrivateTmp = true; + NoNewPrivileges = true; + PrivateDevices = true; + User = "mjolnir"; + Restart = "on-failure"; + + /* TODO: wait for #102397 to be resolved. Then load secrets from $CREDENTIALS_DIRECTORY+"/NAME" + DynamicUser = true; + LoadCredential = [] ++ + optionals (cfg.accessTokenFile != null) [ + "access_token:${cfg.accessTokenFile}" + ] ++ + optionals (cfg.pantalaimon.passwordFile != null) [ + "pantalaimon_password:${cfg.pantalaimon.passwordFile}" + ]; + */ + }; + }; + + users = { + users.mjolnir = { + group = "mjolnir"; + isSystemUser = true; + }; + groups.mjolnir = { }; + }; + }; + + meta = { + doc = ./mjolnir.xml; + maintainers = with maintainers; [ jojosch ]; + }; +} diff --git a/nixos/modules/services/matrix/mjolnir.xml b/nixos/modules/services/matrix/mjolnir.xml new file mode 100644 index 000000000000..d462ddf7b01b --- /dev/null +++ b/nixos/modules/services/matrix/mjolnir.xml @@ -0,0 +1,134 @@ +<chapter xmlns="http://docbook.org/ns/docbook" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:xi="http://www.w3.org/2001/XInclude" + version="5.0" + xml:id="module-services-mjolnir"> + <title>Mjolnir (Matrix Moderation Tool)</title> + <para> + This chapter will show you how to set up your own, self-hosted + <link xlink:href="https://github.com/matrix-org/mjolnir">Mjolnir</link> + instance. + </para> + <para> + As an all-in-one moderation tool, it can protect your server from + malicious invites, spam messages, and whatever else you don't want. + In addition to server-level protection, Mjolnir is great for communities + wanting to protect their rooms without having to use their personal + accounts for moderation. + </para> + <para> + The bot by default includes support for bans, redactions, anti-spam, + server ACLs, room directory changes, room alias transfers, account + deactivation, room shutdown, and more. + </para> + <para> + See the <link xlink:href="https://github.com/matrix-org/mjolnir#readme">README</link> + page and the <link xlink:href="https://github.com/matrix-org/mjolnir/blob/main/docs/moderators.md">Moderator's guide</link> + for additional instructions on how to setup and use Mjolnir. + </para> + <para> + For <link linkend="opt-services.mjolnir.settings">additional settings</link> + see <link xlink:href="https://github.com/matrix-org/mjolnir/blob/main/config/default.yaml">the default configuration</link>. + </para> + <section xml:id="module-services-mjolnir-setup"> + <title>Mjolnir Setup</title> + <para> + First create a new Room which will be used as a management room for Mjolnir. In + this room, Mjolnir will log possible errors and debugging information. You'll + need to set this Room-ID in <link linkend="opt-services.mjolnir.managementRoom">services.mjolnir.managementRoom</link>. + </para> + <para> + Next, create a new user for Mjolnir on your homeserver, if not present already. + </para> + <para> + The Mjolnir Matrix user expects to be free of any rate limiting. + See <link xlink:href="https://github.com/matrix-org/synapse/issues/6286">Synapse #6286</link> + for an example on how to achieve this. + </para> + <para> + If you want Mjolnir to be able to deactivate users, move room aliases, shutdown rooms, etc. + you'll need to make the Mjolnir user a Matrix server admin. + </para> + <para> + Now invite the Mjolnir user to the management room. + </para> + <para> + It is recommended to use <link xlink:href="https://github.com/matrix-org/pantalaimon">Pantalaimon</link>, + so your management room can be encrypted. This also applies if you are looking to moderate an encrypted room. + </para> + <para> + To enable the Pantalaimon E2E Proxy for mjolnir, enable + <link linkend="opt-services.mjolnir.pantalaimon.enable">services.mjolnir.pantalaimon</link>. This will + autoconfigure a new Pantalaimon instance, which will connect to the homeserver + set in <link linkend="opt-services.mjolnir.homeserverUrl">services.mjolnir.homeserverUrl</link> and Mjolnir itself + will be configured to connect to the new Pantalaimon instance. + </para> +<programlisting> +{ + services.mjolnir = { + enable = true; + <link linkend="opt-services.mjolnir.homeserverUrl">homeserverUrl</link> = "https://matrix.domain.tld"; + <link linkend="opt-services.mjolnir.pantalaimon">pantalaimon</link> = { + <link linkend="opt-services.mjolnir.pantalaimon.enable">enable</link> = true; + <link linkend="opt-services.mjolnir.pantalaimon.username">username</link> = "mjolnir"; + <link linkend="opt-services.mjolnir.pantalaimon.passwordFile">passwordFile</link> = "/run/secrets/mjolnir-password"; + }; + <link linkend="opt-services.mjolnir.protectedRooms">protectedRooms</link> = [ + "https://matrix.to/#/!xxx:domain.tld" + ]; + <link linkend="opt-services.mjolnir.managementRoom">managementRoom</link> = "!yyy:domain.tld"; + }; +} +</programlisting> + <section xml:id="module-services-mjolnir-setup-ems"> + <title>Element Matrix Services (EMS)</title> + <para> + If you are using a managed <link xlink:href="https://ems.element.io/">"Element Matrix Services (EMS)"</link> + server, you will need to consent to the terms and conditions. Upon startup, an error + log entry with a URL to the consent page will be generated. + </para> + </section> + </section> + + <section xml:id="module-services-mjolnir-matrix-synapse-antispam"> + <title>Synapse Antispam Module</title> + <para> + A Synapse module is also available to apply the same rulesets the bot + uses across an entire homeserver. + </para> + <para> + To use the Antispam Module, add <package>matrix-synapse-plugins.matrix-synapse-mjolnir-antispam</package> + to the Synapse plugin list and enable the <literal>mjolnir.AntiSpam</literal> module. + </para> +<programlisting> +{ + services.matrix-synapse = { + plugins = with pkgs; [ + matrix-synapse-plugins.matrix-synapse-mjolnir-antispam + ]; + extraConfig = '' + modules: + - module: mjolnir.AntiSpam + config: + # Prevent servers/users in the ban lists from inviting users on this + # server to rooms. Default true. + block_invites: true + # Flag messages sent by servers/users in the ban lists as spam. Currently + # this means that spammy messages will appear as empty to users. Default + # false. + block_messages: false + # Remove users from the user directory search by filtering matrix IDs and + # display names by the entries in the user ban list. Default false. + block_usernames: false + # The room IDs of the ban lists to honour. Unlike other parts of Mjolnir, + # this list cannot be room aliases or permalinks. This server is expected + # to already be joined to the room - Mjolnir will not automatically join + # these rooms. + ban_lists: + - "!roomid:example.org" + ''; + }; +} +</programlisting> + </section> +</chapter> diff --git a/nixos/modules/services/matrix/pantalaimon-options.nix b/nixos/modules/services/matrix/pantalaimon-options.nix new file mode 100644 index 000000000000..035c57540d09 --- /dev/null +++ b/nixos/modules/services/matrix/pantalaimon-options.nix @@ -0,0 +1,70 @@ +{ config, lib, name, ... }: + +with lib; +{ + options = { + dataPath = mkOption { + type = types.path; + default = "/var/lib/pantalaimon-${name}"; + description = '' + The directory where <literal>pantalaimon</literal> should store its state such as the database file. + ''; + }; + + logLevel = mkOption { + type = types.enum [ "info" "warning" "error" "debug" ]; + default = "warning"; + description = '' + Set the log level of the daemon. + ''; + }; + + homeserver = mkOption { + type = types.str; + example = "https://matrix.org"; + description = '' + The URI of the homeserver that the <literal>pantalaimon</literal> proxy should + forward requests to, without the matrix API path but including + the http(s) schema. + ''; + }; + + ssl = mkOption { + type = types.bool; + default = true; + description = '' + Whether or not SSL verification should be enabled for outgoing + connections to the homeserver. + ''; + }; + + listenAddress = mkOption { + type = types.str; + default = "localhost"; + description = '' + The address where the daemon will listen to client connections + for this homeserver. + ''; + }; + + listenPort = mkOption { + type = types.port; + default = 8009; + description = '' + The port where the daemon will listen to client connections for + this homeserver. Note that the listen address/port combination + needs to be unique between different homeservers. + ''; + }; + + extraSettings = mkOption { + type = types.attrs; + default = { }; + description = '' + Extra configuration options. See + <link xlink:href="https://github.com/matrix-org/pantalaimon/blob/master/docs/man/pantalaimon.5.md">pantalaimon(5)</link> + for available options. + ''; + }; + }; +} diff --git a/nixos/modules/services/matrix/pantalaimon.nix b/nixos/modules/services/matrix/pantalaimon.nix new file mode 100644 index 000000000000..63b40099ca5d --- /dev/null +++ b/nixos/modules/services/matrix/pantalaimon.nix @@ -0,0 +1,70 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + cfg = config.services.pantalaimon-headless; + + iniFmt = pkgs.formats.ini { }; + + mkConfigFile = name: instanceConfig: iniFmt.generate "pantalaimon.conf" { + Default = { + LogLevel = instanceConfig.logLevel; + Notifications = false; + }; + + ${name} = (recursiveUpdate + { + Homeserver = instanceConfig.homeserver; + ListenAddress = instanceConfig.listenAddress; + ListenPort = instanceConfig.listenPort; + SSL = instanceConfig.ssl; + + # Set some settings to prevent user interaction for headless operation + IgnoreVerification = true; + UseKeyring = false; + } + instanceConfig.extraSettings + ); + }; + + mkPantalaimonService = name: instanceConfig: + nameValuePair "pantalaimon-${name}" { + description = "pantalaimon instance ${name} - E2EE aware proxy daemon for matrix clients"; + wants = [ "network-online.target" ]; + after = [ "network-online.target" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + ExecStart = ''${pkgs.pantalaimon-headless}/bin/pantalaimon --config ${mkConfigFile name instanceConfig} --data-path ${instanceConfig.dataPath}''; + Restart = "on-failure"; + DynamicUser = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + ProtectHome = true; + ProtectSystem = "strict"; + StateDirectory = "pantalaimon-${name}"; + }; + }; +in +{ + options.services.pantalaimon-headless.instances = mkOption { + default = { }; + type = types.attrsOf (types.submodule (import ./pantalaimon-options.nix)); + description = '' + Declarative instance config. + + Note: to use pantalaimon interactively, e.g. for a Matrix client which does not + support End-to-end encryption (like <literal>fractal</literal>), refer to the home-manager module. + ''; + }; + + config = mkIf (config.services.pantalaimon-headless.instances != { }) + { + systemd.services = mapAttrs' mkPantalaimonService config.services.pantalaimon-headless.instances; + }; + + meta = { + maintainers = with maintainers; [ jojosch ]; + }; +} diff --git a/nixos/modules/services/web-apps/openwebrx.nix b/nixos/modules/services/web-apps/openwebrx.nix new file mode 100644 index 000000000000..51005cd1e497 --- /dev/null +++ b/nixos/modules/services/web-apps/openwebrx.nix @@ -0,0 +1,33 @@ +{ config, lib, pkgs, ... }: +let + cfg = config.services.openwebrx; +in +{ + options.services.openwebrx = with lib; { + enable = mkEnableOption "OpenWebRX Web interface for Software-Defined Radios on http://localhost:8073"; + + package = mkOption { + type = types.package; + default = pkgs.openwebrx; + description = "OpenWebRX package to use for the service"; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.services.openwebrx = { + wantedBy = [ "multi-user.target" ]; + path = with pkgs; [ + csdr + alsaUtils + netcat + ]; + serviceConfig = { + ExecStart = "${cfg.package}/bin/openwebrx"; + Restart = "always"; + DynamicUser = true; + # openwebrx uses /var/lib/openwebrx by default + StateDirectory = [ "openwebrx" ]; + }; + }; + }; +} diff --git a/nixos/modules/virtualisation/lxc-container.nix b/nixos/modules/virtualisation/lxc-container.nix index c7d5ee1fd117..9816cc2332fb 100644 --- a/nixos/modules/virtualisation/lxc-container.nix +++ b/nixos/modules/virtualisation/lxc-container.nix @@ -61,7 +61,7 @@ in description = "Templates for LXD"; type = types.attrsOf (types.submodule (templateSubmodule)); default = {}; - example = literalExample '' + example = literalExpression '' { # create /etc/hostname on container creation "hostname" = { diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 5abb61c965c2..c85644e150af 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -261,6 +261,7 @@ in miniflux = handleTest ./miniflux.nix {}; minio = handleTest ./minio.nix {}; misc = handleTest ./misc.nix {}; + mjolnir = handleTest ./matrix/mjolnir.nix {}; mod_perl = handleTest ./mod_perl.nix {}; moinmoin = handleTest ./moinmoin.nix {}; mongodb = handleTest ./mongodb.nix {}; @@ -341,6 +342,7 @@ in packagekit = handleTest ./packagekit.nix {}; pam-oath-login = handleTest ./pam-oath-login.nix {}; pam-u2f = handleTest ./pam-u2f.nix {}; + pantalaimon = handleTest ./matrix/pantalaimon.nix {}; pantheon = handleTest ./pantheon.nix {}; paperless-ng = handleTest ./paperless-ng.nix {}; parsedmarc = handleTest ./parsedmarc {}; diff --git a/nixos/tests/matrix/mjolnir.nix b/nixos/tests/matrix/mjolnir.nix new file mode 100644 index 000000000000..bb55f6f5440b --- /dev/null +++ b/nixos/tests/matrix/mjolnir.nix @@ -0,0 +1,165 @@ +import ../make-test-python.nix ( + { pkgs, ... }: + let + # Set up SSL certs for Synapse to be happy. + runWithOpenSSL = file: cmd: pkgs.runCommand file + { + buildInputs = [ pkgs.openssl ]; + } + cmd; + + ca_key = runWithOpenSSL "ca-key.pem" "openssl genrsa -out $out 2048"; + ca_pem = runWithOpenSSL "ca.pem" '' + openssl req \ + -x509 -new -nodes -key ${ca_key} \ + -days 10000 -out $out -subj "/CN=snakeoil-ca" + ''; + key = runWithOpenSSL "matrix_key.pem" "openssl genrsa -out $out 2048"; + csr = runWithOpenSSL "matrix.csr" '' + openssl req \ + -new -key ${key} \ + -out $out -subj "/CN=localhost" \ + ''; + cert = runWithOpenSSL "matrix_cert.pem" '' + openssl x509 \ + -req -in ${csr} \ + -CA ${ca_pem} -CAkey ${ca_key} \ + -CAcreateserial -out $out \ + -days 365 + ''; + in + { + name = "mjolnir"; + meta = with pkgs.lib; { + maintainers = teams.matrix.members; + }; + + nodes = { + homeserver = { pkgs, ... }: { + services.matrix-synapse = { + enable = true; + database_type = "sqlite3"; + tls_certificate_path = "${cert}"; + tls_private_key_path = "${key}"; + enable_registration = true; + registration_shared_secret = "supersecret-registration"; + + listeners = [ + # The default but tls=false + { + "bind_address" = ""; + "port" = 8448; + "resources" = [ + { "compress" = true; "names" = [ "client" "webclient" ]; } + { "compress" = false; "names" = [ "federation" ]; } + ]; + "tls" = false; + "type" = "http"; + "x_forwarded" = false; + } + ]; + }; + + networking.firewall.allowedTCPPorts = [ 8448 ]; + + environment.systemPackages = [ + (pkgs.writeShellScriptBin "register_mjolnir_user" '' + exec ${pkgs.matrix-synapse}/bin/register_new_matrix_user \ + -u mjolnir \ + -p mjolnir-password \ + --admin \ + --shared-secret supersecret-registration \ + http://localhost:8448 + '' + ) + (pkgs.writeShellScriptBin "register_moderator_user" '' + exec ${pkgs.matrix-synapse}/bin/register_new_matrix_user \ + -u moderator \ + -p moderator-password \ + --no-admin \ + --shared-secret supersecret-registration \ + http://localhost:8448 + '' + ) + ]; + }; + + mjolnir = { pkgs, ... }: { + services.mjolnir = { + enable = true; + homeserverUrl = "http://homeserver:8448"; + pantalaimon = { + enable = true; + username = "mjolnir"; + passwordFile = pkgs.writeText "password.txt" "mjolnir-password"; + }; + managementRoom = "#moderators:homeserver"; + }; + }; + + client = { pkgs, ... }: { + environment.systemPackages = [ + (pkgs.writers.writePython3Bin "create_management_room_and_invite_mjolnir" + { libraries = [ pkgs.python3Packages.matrix-nio ]; } '' + import asyncio + + from nio import ( + AsyncClient, + EnableEncryptionBuilder + ) + + + async def main() -> None: + client = AsyncClient("http://homeserver:8448", "moderator") + + await client.login("moderator-password") + + room = await client.room_create( + name="Moderators", + alias="moderators", + initial_state=[EnableEncryptionBuilder().as_dict()], + ) + + await client.join(room.room_id) + await client.room_invite(room.room_id, "@mjolnir:homeserver") + + asyncio.run(main()) + '' + ) + ]; + }; + }; + + testScript = '' + with subtest("start homeserver"): + homeserver.start() + + homeserver.wait_for_unit("matrix-synapse.service") + homeserver.wait_until_succeeds("curl --fail -L http://localhost:8448/") + + with subtest("register users"): + # register mjolnir user + homeserver.succeed("register_mjolnir_user") + # register moderator user + homeserver.succeed("register_moderator_user") + + with subtest("start mjolnir"): + mjolnir.start() + + # wait for pantalaimon to be ready + mjolnir.wait_for_unit("pantalaimon-mjolnir.service") + mjolnir.wait_for_unit("mjolnir.service") + + mjolnir.wait_until_succeeds("curl --fail -L http://localhost:8009/") + + with subtest("ensure mjolnir can be invited to the management room"): + client.start() + + client.wait_until_succeeds("curl --fail -L http://homeserver:8448/") + + client.succeed("create_management_room_and_invite_mjolnir") + + mjolnir.wait_for_console_text("Startup complete. Now monitoring rooms") + ''; + } +) diff --git a/nixos/tests/matrix/pantalaimon.nix b/nixos/tests/matrix/pantalaimon.nix new file mode 100644 index 000000000000..fcb9904b2138 --- /dev/null +++ b/nixos/tests/matrix/pantalaimon.nix @@ -0,0 +1,65 @@ +import ../make-test-python.nix ( + { pkgs, ... }: + let + pantalaimonInstanceName = "testing"; + + # Set up SSL certs for Synapse to be happy. + runWithOpenSSL = file: cmd: pkgs.runCommand file + { + buildInputs = [ pkgs.openssl ]; + } + cmd; + + ca_key = runWithOpenSSL "ca-key.pem" "openssl genrsa -out $out 2048"; + ca_pem = runWithOpenSSL "ca.pem" '' + openssl req \ + -x509 -new -nodes -key ${ca_key} \ + -days 10000 -out $out -subj "/CN=snakeoil-ca" + ''; + key = runWithOpenSSL "matrix_key.pem" "openssl genrsa -out $out 2048"; + csr = runWithOpenSSL "matrix.csr" '' + openssl req \ + -new -key ${key} \ + -out $out -subj "/CN=localhost" \ + ''; + cert = runWithOpenSSL "matrix_cert.pem" '' + openssl x509 \ + -req -in ${csr} \ + -CA ${ca_pem} -CAkey ${ca_key} \ + -CAcreateserial -out $out \ + -days 365 + ''; + in + { + name = "pantalaimon"; + meta = with pkgs.lib; { + maintainers = teams.matrix.members; + }; + + machine = { pkgs, ... }: { + services.pantalaimon-headless.instances.${pantalaimonInstanceName} = { + homeserver = "https://localhost:8448"; + listenAddress = "0.0.0.0"; + listenPort = 8888; + logLevel = "debug"; + ssl = false; + }; + + services.matrix-synapse = { + enable = true; + database_type = "sqlite3"; + tls_certificate_path = "${cert}"; + tls_private_key_path = "${key}"; + }; + }; + + testScript = '' + start_all() + machine.wait_for_unit("pantalaimon-${pantalaimonInstanceName}.service") + machine.wait_for_unit("matrix-synapse.service") + machine.wait_until_succeeds( + "curl --fail -L http://localhost:8888/" + ) + ''; + } +) |