nixos/draupnir: init

Co-authored-by: emilylange <git@emilylange.de>
Co-authored-by: Martin Weinelt <hexa@darmstadt.ccc.de>
Co-authored-by: teutat3s <10206665+teutat3s@users.noreply.github.com>

Rory& 4b153aad 7019863e

Changed files
+331
nixos
doc
manual
modules
+9
nixos/doc/manual/redirects.json
···
"module-services-davis-basic-usage": [
"index.html#module-services-davis-basic-usage"
],
+
"module-services-draupnir": [
+
"index.html#module-services-draupnir"
+
],
+
"module-services-draupnir-setup": [
+
"index.html#module-services-draupnir-setup"
+
],
+
"module-services-draupnir-setup-ems": [
+
"index.html#module-services-draupnir-setup-ems"
+
],
"module-services-castopod": [
"index.html#module-services-castopod"
],
+2
nixos/doc/manual/release-notes/rl-2511.section.md
···
- [Broadcast Box](https://github.com/Glimesh/broadcast-box), a WebRTC broadcast server. Available as [services.broadcast-box](options.html#opt-services.broadcast-box.enable).
+
- [Draupnir](https://github.com/the-draupnir-project/draupnir), a Matrix moderation bot. Available as [services.draupnir](#opt-services.draupnir.enable).
+
- [SuiteNumérique Docs](https://github.com/suitenumerique/docs), a collaborative note taking, wiki and documentation web platform and alternative to Notion or Outline. Available as [services.lasuite-docs](#opt-services.lasuite-docs.enable).
## Backward Incompatibilities {#sec-release-25.11-incompatibilities}
+1
nixos/modules/module-list.nix
···
./services/matrix/conduit.nix
./services/matrix/continuwuity.nix
./services/matrix/dendrite.nix
+
./services/matrix/draupnir.nix
./services/matrix/hebbot.nix
./services/matrix/hookshot.nix
./services/matrix/lk-jwt-service.nix
+62
nixos/modules/services/matrix/draupnir.md
···
+
# Draupnir (Matrix Moderation Bot) {#module-services-draupnir}
+
+
This chapter will show you how to set up your own, self-hosted
+
[Draupnir](https://github.com/the-draupnir-project/Draupnir) instance.
+
+
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, Draupnir is great for communities
+
wanting to protect their rooms without having to use their personal
+
accounts for moderation.
+
+
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. (This depends on homeserver configuration and implementation.)
+
+
See the [README](https://github.com/the-draupnir-project/draupnir#readme)
+
page and the [Moderator's guide](https://the-draupnir-project.github.io/draupnir-documentation/moderator/setting-up-and-configuring)
+
for additional instructions on how to setup and use Draupnir.
+
+
For [additional settings](#opt-services.draupnir.settings)
+
see [the default configuration](https://github.com/the-draupnir-project/Draupnir/blob/main/config/default.yaml).
+
+
## Draupnir Setup {#module-services-draupnir-setup}
+
+
First create a new unencrypted, private room which will be used as the management room for Draupnir.
+
This is the room in which moderators will interact with Draupnir and where it will log possible errors and debugging information.
+
You'll need to set this room ID or alias in [services.draupnir.settings.managementRoom](#opt-services.draupnir.settings.managementRoom).
+
+
Next, create a new user for Draupnir on your homeserver, if one does not already exist.
+
+
The Draupnir Matrix user expects to be free of any rate limiting.
+
See [Synapse #6286](https://github.com/matrix-org/synapse/issues/6286)
+
for an example on how to achieve this.
+
+
If you want Draupnir to be able to deactivate users, move room aliases, shut down rooms, etc.
+
you'll need to make the Draupnir user a Matrix server admin.
+
+
Now invite the Draupnir user to the management room.
+
Draupnir will automatically try to join this room on startup.
+
+
```nix
+
{
+
services.draupnir = {
+
enable = true;
+
+
settings = {
+
homeserverUrl = "https://matrix.org";
+
managementRoom = "!yyy:example.org";
+
};
+
+
secrets = {
+
accessToken = "/path/to/secret/containing/access-token";
+
};
+
};
+
}
+
```
+
+
### Element Matrix Services (EMS) {#module-services-draupnir-setup-ems}
+
+
If you are using a managed ["Element Matrix Services (EMS)"](https://ems.element.io/)
+
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.
+257
nixos/modules/services/matrix/draupnir.nix
···
+
{
+
config,
+
options,
+
lib,
+
pkgs,
+
...
+
}:
+
+
let
+
cfg = config.services.draupnir;
+
opt = options.services.draupnir;
+
+
format = pkgs.formats.yaml { };
+
configFile = format.generate "draupnir.yaml" cfg.settings;
+
+
inherit (lib)
+
literalExpression
+
mkEnableOption
+
mkOption
+
mkPackageOption
+
mkRemovedOptionModule
+
mkRenamedOptionModule
+
types
+
;
+
in
+
{
+
imports = [
+
# Removed options for those migrating from the Mjolnir module
+
(mkRenamedOptionModule
+
[ "services" "draupnir" "dataPath" ]
+
[ "services" "draupnir" "settings" "dataPath" ]
+
)
+
(mkRenamedOptionModule
+
[ "services" "draupnir" "homeserverUrl" ]
+
[ "services" "draupnir" "settings" "homeserverUrl" ]
+
)
+
(mkRenamedOptionModule
+
[ "services" "draupnir" "managementRoom" ]
+
[ "services" "draupnir" "settings" "managementRoom" ]
+
)
+
(mkRenamedOptionModule
+
[ "services" "draupnir" "accessTokenFile" ]
+
[ "services" "draupnir" "secrets" "accessToken" ]
+
)
+
(mkRemovedOptionModule [ "services" "draupnir" "pantalaimon" ] ''
+
`services.draupnir.pantalaimon.*` has been removed because it depends on the deprecated and vulnerable
+
libolm library for end-to-end encryption and upstream support for Pantalaimon in Draupnir is limited.
+
See <https://the-draupnir-project.github.io/draupnir-documentation/bot/encryption> for details.
+
If you nontheless require E2EE via Pantalaimon, you can configure `services.pantalaimon-headless.instances`
+
yourself and use that with `services.draupnir.settings.pantalaimon` and `services.draupnir.secrets.pantalaimon.password`.
+
'')
+
];
+
+
options.services.draupnir = {
+
enable = mkEnableOption "Draupnir, a moderations bot for Matrix";
+
+
package = mkPackageOption pkgs "draupnir" { };
+
+
settings = mkOption {
+
example = literalExpression ''
+
{
+
homeserverUrl = "https://matrix.org";
+
managementRoom = "#moderators:example.org";
+
+
autojoinOnlyIfManager = true;
+
automaticallyRedactForReasons = [ "spam" "advertising" ];
+
}
+
'';
+
description = ''
+
Free-form settings written to Draupnir's configuration file.
+
See [Draupnir's default configuration](https://github.com/the-draupnir-project/Draupnir/blob/main/config/default.yaml) for available settings.
+
'';
+
default = { };
+
type = types.submodule {
+
freeformType = format.type;
+
options = {
+
homeserverUrl = mkOption {
+
type = types.str;
+
example = "https://matrix.org";
+
description = ''
+
Base URL of the Matrix homeserver that provides the Client-Server API.
+
+
::: {.note}
+
When using Pantalaimon, set this to the Pantalaimon URL and
+
{option}`${opt.settings}.rawHomeserverUrl` to the public URL.
+
:::
+
'';
+
};
+
+
rawHomeserverUrl = mkOption {
+
type = types.str;
+
example = "https://matrix.org";
+
default = cfg.settings.homeserverUrl;
+
defaultText = literalExpression "config.${opt.settings}.homeserverUrl";
+
description = ''
+
Public base URL of the Matrix homeserver that provides the Client-Server API when using the Draupnir's
+
[Report forwarding feature](https://the-draupnir-project.github.io/draupnir-documentation/bot/homeserver-administration#report-forwarding).
+
+
::: {.warning}
+
When using Pantalaimon, do not set this to the Pantalaimon URL!
+
:::
+
'';
+
};
+
+
managementRoom = mkOption {
+
type = types.str;
+
example = "#moderators:example.org";
+
description = ''
+
The room ID or alias where moderators can use the bot's functionality.
+
+
The bot has no access controls, so anyone in this room can use the bot - secure this room!
+
Do not enable end-to-end encryption for this room, unless set up with Pantalaimon.
+
+
::: {.warning}
+
When using a room alias, make sure the alias used is on the local homeserver!
+
This prevents an issue where the control room becomes undefined when the alias can't be resolved.
+
:::
+
'';
+
};
+
+
dataPath = mkOption {
+
type = types.path;
+
readOnly = true;
+
default = "/var/lib/draupnir";
+
description = ''
+
The path Draupnir will store its state/data in.
+
+
::: {.warning}
+
This option is read-only.
+
:::
+
+
::: {.note}
+
If you want to customize where this data is stored, use a bind mount.
+
:::
+
'';
+
};
+
};
+
};
+
};
+
+
secrets = {
+
accessToken = mkOption {
+
type = types.nullOr types.path;
+
default = null;
+
description = ''
+
File containing the access token for Draupnir's Matrix account
+
to be used in place of {option}`${opt.settings}.accessToken`.
+
'';
+
};
+
+
pantalaimon.password = mkOption {
+
type = types.nullOr types.path;
+
default = null;
+
description = ''
+
File containing the password for Draupnir's Matrix account when used in
+
conjunction with Pantalaimon to be used in place of
+
{option}`${opt.settings}.pantalaimon.password`.
+
+
::: {.warning}
+
Take note that upstream has limited Pantalaimon and E2EE support:
+
<https://the-draupnir-project.github.io/draupnir-documentation/bot/encryption> and
+
<https://the-draupnir-project.github.io/draupnir-documentation/shared/dogfood#e2ee-support>.
+
:::
+
'';
+
};
+
+
web.synapseHTTPAntispam.authorization = mkOption {
+
type = types.nullOr types.path;
+
default = null;
+
description = ''
+
File containing the secret token when using the Synapse HTTP Antispam module
+
to be used in place of
+
{option}`${opt.settings}.web.synapseHTTPAntispam.authorization`.
+
+
See <https://the-draupnir-project.github.io/draupnir-documentation/bot/synapse-http-antispam> for details.
+
'';
+
};
+
};
+
};
+
+
config = lib.mkIf cfg.enable {
+
assertions = [
+
{
+
# Removed option for those migrating from the Mjolnir module - mkRemovedOption module does *not* work with submodules.
+
assertion = !(cfg.settings ? protectedRooms);
+
message = "Unset ${opt.settings}.protectedRooms, as it is unsupported on Draupnir. Add these rooms via `!draupnir rooms add` instead.";
+
}
+
];
+
+
systemd.services.draupnir = {
+
description = "Draupnir - a moderation bot for Matrix";
+
wants = [
+
"network-online.target"
+
"matrix-synapse.service"
+
"conduit.service"
+
"dendrite.service"
+
];
+
after = [
+
"network-online.target"
+
"matrix-synapse.service"
+
"conduit.service"
+
"dendrite.service"
+
];
+
wantedBy = [ "multi-user.target" ];
+
+
startLimitIntervalSec = 0;
+
serviceConfig = {
+
ExecStart = toString (
+
[
+
(lib.getExe cfg.package)
+
"--draupnir-config"
+
configFile
+
]
+
++ lib.optionals (cfg.secrets.accessToken != null) [
+
"--access-token-path"
+
"%d/access_token"
+
]
+
++ lib.optionals (cfg.secrets.pantalaimon.password != null) [
+
"--pantalaimon-password-path"
+
"%d/pantalaimon_password"
+
]
+
++ lib.optionals (cfg.secrets.web.synapseHTTPAntispam.authorization != null) [
+
"--http-antispam-authorization-path"
+
"%d/http_antispam_authorization"
+
]
+
);
+
+
WorkingDirectory = "/var/lib/draupnir";
+
StateDirectory = "draupnir";
+
StateDirectoryMode = "0700";
+
ProtectHome = true;
+
PrivateDevices = true;
+
Restart = "on-failure";
+
RestartSec = "5s";
+
DynamicUser = true;
+
LoadCredential =
+
lib.optionals (cfg.secrets.accessToken != null) [
+
"access_token:${cfg.secrets.accessToken}"
+
]
+
++ lib.optionals (cfg.secrets.pantalaimon.password != null) [
+
"pantalaimon_password:${cfg.secrets.pantalaimon.password}"
+
]
+
++ lib.optionals (cfg.secrets.web.synapseHTTPAntispam.authorization != null) [
+
"http_antispam_authorization:${cfg.secrets.web.synapseHTTPAntispam.authorization}"
+
];
+
};
+
};
+
};
+
+
meta = {
+
doc = ./draupnir.md;
+
maintainers = with lib.maintainers; [
+
RorySys
+
emilylange
+
];
+
};
+
}