-
-
Notifications
You must be signed in to change notification settings - Fork 14.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1ac816c
commit 3e0fd26
Showing
3 changed files
with
359 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,272 @@ | ||
{ | ||
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. | ||
''); | ||
|
||
username = lib.mkOption { | ||
type = lib.types.str; | ||
description = '' | ||
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. | ||
::: {.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 | ||
] | ||
++ lib.optionals (cfg.pantalaimon.enable && cfg.pantalaimon.passwordFile != null) [ | ||
"--pantalaimon-password-path" | ||
"/run/credentials/draupnir.service/pantalaimon_password" | ||
] | ||
++ lib.optionals (!cfg.pantalaimon.enable && cfg.accessTokenFile != null) [ | ||
"--access-token-path" | ||
"/run/credentials/draupnir.service/access_token" | ||
] | ||
); | ||
|
||
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 ]; | ||
}; | ||
} |