Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draupnir: init at 2.0.0-beta.6 #274052

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
4fffcb7
maintainers: add RorySys
TheArcaneBrony Apr 25, 2024
1e396ac
draupnir: init at 2.0.0-beta.4
TheArcaneBrony Apr 25, 2024
bbadc5e
nixos/draupnir: init
TheArcaneBrony Apr 25, 2024
ee8cbf8
draupnir: add tests
TheArcaneBrony Apr 25, 2024
4ac0923
fixup! draupnir: init at 2.0.0-beta.4 - Remove hook call echos
TheArcaneBrony Jul 22, 2024
4727e58
fixup! draupnir: init at 2.0.0-beta.4 - set fetchYarnDeps name
TheArcaneBrony Jul 22, 2024
cbdb027
fixup! nixos/draupnir: init - remove dead code
TheArcaneBrony Jul 22, 2024
8c6092f
fixup! nixos/draupnir: init - Moderation bot, not tool
TheArcaneBrony Jul 22, 2024
0ed8bbc
fixup! nixos/draupnir: init - witout -> without
TheArcaneBrony Jul 22, 2024
65b7eff
fixup! nixos/draupnir: init - clean up null stripping
TheArcaneBrony Jul 22, 2024
dfc2fb1
fixup! nixos/draupnir: init - move homeserverUrl to global
TheArcaneBrony Jul 22, 2024
e3adb0c
fixup! nixos/draupnir: init - drop module config reference
TheArcaneBrony Jul 22, 2024
1fac8d3
fixup! draupnir: add tests - move homeserverUrl
TheArcaneBrony Jul 22, 2024
a13cc66
fixup! nixos/draupnir: init - remove parial assertion
TheArcaneBrony Jul 22, 2024
0c8581d
fixup! nixos/draupnir: init - Update docs somewhat
TheArcaneBrony Aug 8, 2024
dbf6ae4
fixup! draupnir: init at 2.0.0-beta.4 - Add sqlite as native build input
TheArcaneBrony Aug 8, 2024
c2a5e05
fixup! draupnir: init at 2.0.0-beta.6 - ugly sed hack to drop corepack
TheArcaneBrony Sep 15, 2024
bd7179d
fixup! draupnir: init at 2.0.0-beta.4 - Switch to pkgConfig for matri…
TheArcaneBrony Sep 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions maintainers/maintainer-list.nix
TheArcaneBrony marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -17503,6 +17503,13 @@
githubId = 76747196;
name = "Robert Rose";
};
RorySys = {
email = "[email protected]";
github = "TheArcaneBrony";
githubId = 13570458;
matrix = "@emma:rory.gay"; # preferred
name = "Rory&";
};
rosehobgoblin = {
name = "J. L. Bowden";
github = "rosehobgoblin";
Expand Down
1 change: 1 addition & 0 deletions nixos/modules/module-list.nix
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,7 @@
./services/matrix/conduit.nix
./services/matrix/dendrite.nix
./services/matrix/hebbot.nix
./services/matrix/draupnir.nix
./services/matrix/maubot.nix
./services/matrix/mautrix-facebook.nix
./services/matrix/mautrix-meta.nix
Expand Down
86 changes: 86 additions & 0 deletions nixos/modules/services/matrix/draupnir.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# 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 Room which will be used as a management room for Draupnir. In
this room, Draupnir will log possible errors and debugging information. You'll
need to set this Room-ID in [services.draupnir.settings.managementRoom](#opt-services.draupnir.settings.managementRoom).

Next, create a new user for Draupnir on your homeserver, if not present already.

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.

It is not recommended to use End to End Encryption when not needed,
as it is known to break parts of Draupnir.

To enable the Pantalaimon E2EE Proxy for Draupnir, enable
[services.draupnir.pantalaimon](#opt-services.draupnir.pantalaimon.enable). This will
autoconfigure a new Pantalaimon instance, which will connect to the homeserver
set in [services.draupnir.homeserverUrl](#opt-services.draupnir.homeserverUrl) and Draupnir itself
will be configured to connect to the new Pantalaimon instance.

```
{
services.draupnir = {
enable = true;

# Point this to your reverse proxy, if eg. Synapse's workers are in use!
homeserverUrl = "http://localhost:8008";

settings = {
managementRoom = "!yyy:domain.tld";
};
};
}
```

Additional config for Pantalaimon:
```
pantalaimon = {
enable = true;
username = "draupnir";
passwordFile = "/run/secrets/draupnir-password";
options = {
ssl = false;
};
};
```

### 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.

## Synapse Antispam Module {#module-services-draupnir-matrix-synapse-antispam}

Use the Mjolnir Antispam module, Draupnir made no changes here and as such was not packaged.
It may be possible that the Mjolir Antispam module does *not* work with Draupnir in the future,
nor is the one in the Draupnir repository maintained or tested.
238 changes: 238 additions & 0 deletions nixos/modules/services/matrix/draupnir.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
{ config, lib, pkgs, utils, ... }:

let
cfg = config.services.draupnir;

format = pkgs.formats.yaml {};
configFile = format.generate "draupnir.yaml" (lib.filterAttrsRecursive (_: value: value != null) cfg.settings);
in
{
#region Options
options.services.draupnir = {
enable = lib.mkEnableOption "Draupnir, a moderation bot for Matrix";

package = lib.mkPackageOption pkgs "draupnir" { };

accessTokenFile = lib.mkOption {
type = with lib.types; nullOr path;
default = null;
description = ''
File containing the access token for Draupnir's Matrix account.
Make sure this does not contain newlines if writing manually: `:set noeol nofixeol` for vim or -L for nano.
'';
};

homeserverUrl = lib.mkOption {
type = lib.types.str;
description = ''
Base URL of the Matrix homeserver, that provides the Client-Server API.
Will be used by either Draupnir directly, or by Pantalaimon, if enabled.
'';
};

#region Pantalaimon options
pantalaimon = lib.mkOption {
description = ''
`pantalaimon` options (enables E2E Encryption support).

This will create a `pantalaimon` instance with the name "draupnir".
'';
default = { };
type = lib.types.submodule {
options = {
enable = lib.mkEnableOption (''
pantalaimon, in order to enable E2EE support.
If `true`, accessToken is ignored and the username/password below will be
used instead. The access token of the bot will be stored in /var/lib/draupnir.
'');
Comment on lines +43 to +47
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
enable = lib.mkEnableOption (''
pantalaimon, in order to enable E2EE support.
If `true`, accessToken is ignored and the username/password below will be
used instead. The access token of the bot will be stored in /var/lib/draupnir.
'');
enable = lib.mkEnableOption '''
pantalaimon, in order to enable E2EE support.
If `true`, accessToken is ignored and the username/password below will be
used instead. The access token of the bot will be stored in /var/lib/draupnir.
'';


username = lib.mkOption {
type = lib.types.str;
description = ''
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest adding a default here:

default = "draupnir";

Account name on the Matrix homeserver.
'';
};

passwordFile = lib.mkOption {
type = with lib.types; nullOr path;
default = null;
description = ''
File containing the password for the Matrix account.
Make sure this does not contain newlines if writing manually: `:set noeol nofixeol` for vim or -L for nano.
'';
};

options = lib.mkOption {
type = lib.types.submodule (import ./pantalaimon-options.nix { inherit lib; inherit config; name = "draupnir"; });
default = { };
description = ''
Pass through additional options to the `pantalaimon` service.
'';
};
};
};
};
#endregion

#region Draupnir settings
settings = lib.mkOption {
example = lib.literalExpression ''
{
autojoinOnlyIfManager = true;
automaticallyRedactForReasons = [ "spam" "advertising" ];
}
'';
description = ''
Draupnir settings (see [Draupnir's default configuration](https://github.com/the-draupnir-project/Draupnir/blob/main/config/default.yaml) for available settings).
'';
default = { };
type = lib.types.submodule {
freeformType = format.type;
options = {
#region Readonly settings - these settings are not configurable
dataPath = lib.mkOption {
type = lib.types.str;
default = "/var/lib/draupnir";
readOnly = true;
description = ''
The path where Draupnir stores its data.
mweinelt marked this conversation as resolved.
Show resolved Hide resolved

::: {.note}
If you want to customize where this data is stored, use a bind mount.
:::
'';
};
#endregion

#region Base settings
managementRoom = lib.mkOption {
type = lib.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!

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.
'';
};
#endregion
};
};
};
#endregion
};
#endregion

#region Service configuration
config = lib.mkIf cfg.enable {
assertions = [
# pantalaimon enabled - use passwordFile instead of accessTokenFile
{
assertion = cfg.pantalaimon.enable -> cfg.pantalaimon.passwordFile != null;
message = "Set services.draupnir.pantailaimon.passwordFile, as it is required in order to use Pantalaimon.";
}
{
assertion = cfg.pantalaimon.enable -> cfg.accessTokenFile == null;
message = "Unset services.draupnir.accessTokenFile, as it has no effect when Pantalaimon is enabled.";
}

# pantalaimon disabled - use accessTokenFile instead of passwordFile
{
assertion = !cfg.pantalaimon.enable -> cfg.accessTokenFile != null;
message = "Set services.draupnir.accessTokenFile, as it is required in order to use Draupnir without Pantalaimon.";
}
{
assertion = !cfg.pantalaimon.enable -> cfg.pantalaimon.passwordFile == null;
message = "Unset services.draupnir.pantalaimon.passwordFile, as it has no effect when Pantalaimon is disabled.";
}
# Removed options for those migrating from the Mjolnir module - mkRemovedOption module does *not* work with submodules.

# Noop in v2, but should ideally not be used in mjolnir or 1.x either.
{
assertion = (cfg.settings ? protectedRooms) == false;
message = "Unset services.draupnir.settings.protectedRooms, as it is unsupported on Draupnir. Add these rooms via `!draupnir rooms add` instead.";
}
];

warnings = [ ]
# Unsupported but available options
# - Crypto
++ lib.optionals (cfg.pantalaimon.enable) [ ''Using Draupnir with Pantalaimon is known to break some features, and is thus unsupported.
Encryption support should only be enabled if you require an encrypted management room or use Draupnir in encrypted rooms.'' ]
++ lib.optionals (cfg.settings ? experimentalRustCrypto && cfg.settings.experimentalRustCrypto) [ ''Using Draupnir with experimental Rust Crypto support is untested and unsupported.
Encryption support should only be enabled if you require an encrypted management room or use Draupnir in encrypted rooms.'' ]

# - Deprecated options
++ lib.optionals (cfg.settings ? verboseLogging && cfg.settings.verboseLogging) [ "Verbose logging in Draupnir is deprecated and may be removed in a future version." ]
;

services.pantalaimon-headless.instances.draupnir = lib.mkIf cfg.pantalaimon.enable (cfg.pantalaimon.options // { homeserver = cfg.homeserverUrl; });
services.draupnir.settings.homeserverUrl = if cfg.pantalaimon.enable
then (with config.services.pantalaimon-headless.instances.draupnir; "http://${listenAddress}:${toString listenPort}/")
else cfg.homeserverUrl;
services.draupnir.settings.pantalaimon = lib.mkIf cfg.pantalaimon.enable ({
use = true;
username = cfg.pantalaimon.username;
});

systemd.services.draupnir = {
description = "Draupnir - a moderation bot for Matrix";
requires = lib.optionals (cfg.pantalaimon.enable) [
"pantalaimon-draupnir.service"
];
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" ];

serviceConfig = {
ExecStart = utils.escapeSystemdExecArgs ([
(lib.getExe cfg.package)
"--draupnir-config" configFile
mweinelt marked this conversation as resolved.
Show resolved Hide resolved
] ++ lib.optionals (cfg.pantalaimon.enable && cfg.pantalaimon.passwordFile != null) [
"--pantalaimon-password-path"
"/run/credentials/draupnir.service/pantalaimon_password"
mweinelt marked this conversation as resolved.
Show resolved Hide resolved
] ++ lib.optionals (!cfg.pantalaimon.enable && cfg.accessTokenFile != null) [
"--access-token-path"
"/run/credentials/draupnir.service/access_token"
mweinelt marked this conversation as resolved.
Show resolved Hide resolved
]);

WorkingDirectory = "/var/lib/draupnir";
StateDirectory = "draupnir";
StateDirectoryMode = "0700";
ProtectSystem = "strict";
ProtectHome = true;
PrivateTmp = true;
NoNewPrivileges = true;
PrivateDevices = true;
Restart = "on-failure";

DynamicUser = true;
LoadCredential =
lib.optionals (cfg.accessTokenFile != null) [
"access_token:${cfg.accessTokenFile}"
]
++ lib.optionals (cfg.pantalaimon.enable && cfg.pantalaimon.passwordFile != null) [
"pantalaimon_password:${cfg.pantalaimon.passwordFile}"
];
};
};
};
#endregion

meta = {
doc = ./draupnir.md;
maintainers = with lib.maintainers; [ RorySys ];
};
}
1 change: 1 addition & 0 deletions nixos/tests/all-tests.nix
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ in {
domination = handleTest ./domination.nix {};
dovecot = handleTest ./dovecot.nix {};
drawterm = discoverTests (import ./drawterm.nix);
draupnir = handleTest ./matrix/draupnir.nix {};
drbd = handleTest ./drbd.nix {};
dublin-traceroute = handleTest ./dublin-traceroute.nix {};
earlyoom = handleTestOn ["x86_64-linux"] ./earlyoom.nix {};
Expand Down
Loading
Loading