at master 8.8 kB view raw
1{ 2 config, 3 options, 4 lib, 5 pkgs, 6 ... 7}: 8 9let 10 cfg = config.services.draupnir; 11 opt = options.services.draupnir; 12 13 format = pkgs.formats.yaml { }; 14 configFile = format.generate "draupnir.yaml" cfg.settings; 15 16 inherit (lib) 17 literalExpression 18 mkEnableOption 19 mkOption 20 mkPackageOption 21 mkRemovedOptionModule 22 mkRenamedOptionModule 23 types 24 ; 25in 26{ 27 imports = [ 28 # Removed options for those migrating from the Mjolnir module 29 (mkRenamedOptionModule 30 [ "services" "draupnir" "dataPath" ] 31 [ "services" "draupnir" "settings" "dataPath" ] 32 ) 33 (mkRenamedOptionModule 34 [ "services" "draupnir" "homeserverUrl" ] 35 [ "services" "draupnir" "settings" "homeserverUrl" ] 36 ) 37 (mkRenamedOptionModule 38 [ "services" "draupnir" "managementRoom" ] 39 [ "services" "draupnir" "settings" "managementRoom" ] 40 ) 41 (mkRenamedOptionModule 42 [ "services" "draupnir" "accessTokenFile" ] 43 [ "services" "draupnir" "secrets" "accessToken" ] 44 ) 45 (mkRemovedOptionModule [ "services" "draupnir" "pantalaimon" ] '' 46 `services.draupnir.pantalaimon.*` has been removed because it depends on the deprecated and vulnerable 47 libolm library for end-to-end encryption and upstream support for Pantalaimon in Draupnir is limited. 48 See <https://the-draupnir-project.github.io/draupnir-documentation/bot/encryption> for details. 49 If you nontheless require E2EE via Pantalaimon, you can configure `services.pantalaimon-headless.instances` 50 yourself and use that with `services.draupnir.settings.pantalaimon` and `services.draupnir.secrets.pantalaimon.password`. 51 '') 52 ]; 53 54 options.services.draupnir = { 55 enable = mkEnableOption "Draupnir, a moderations bot for Matrix"; 56 57 package = mkPackageOption pkgs "draupnir" { }; 58 59 settings = mkOption { 60 example = literalExpression '' 61 { 62 homeserverUrl = "https://matrix.org"; 63 managementRoom = "#moderators:example.org"; 64 65 autojoinOnlyIfManager = true; 66 automaticallyRedactForReasons = [ "spam" "advertising" ]; 67 } 68 ''; 69 description = '' 70 Free-form settings written to Draupnir's configuration file. 71 See [Draupnir's default configuration](https://github.com/the-draupnir-project/Draupnir/blob/main/config/default.yaml) for available settings. 72 ''; 73 default = { }; 74 type = types.submodule { 75 freeformType = format.type; 76 options = { 77 homeserverUrl = mkOption { 78 type = types.str; 79 example = "https://matrix.org"; 80 description = '' 81 Base URL of the Matrix homeserver that provides the Client-Server API. 82 83 ::: {.note} 84 When using Pantalaimon, set this to the Pantalaimon URL and 85 {option}`${opt.settings}.rawHomeserverUrl` to the public URL. 86 ::: 87 ''; 88 }; 89 90 rawHomeserverUrl = mkOption { 91 type = types.str; 92 example = "https://matrix.org"; 93 default = cfg.settings.homeserverUrl; 94 defaultText = literalExpression "config.${opt.settings}.homeserverUrl"; 95 description = '' 96 Public base URL of the Matrix homeserver that provides the Client-Server API when using the Draupnir's 97 [Report forwarding feature](https://the-draupnir-project.github.io/draupnir-documentation/bot/homeserver-administration#report-forwarding). 98 99 ::: {.warning} 100 When using Pantalaimon, do not set this to the Pantalaimon URL! 101 ::: 102 ''; 103 }; 104 105 managementRoom = mkOption { 106 type = types.str; 107 example = "#moderators:example.org"; 108 description = '' 109 The room ID or alias where moderators can use the bot's functionality. 110 111 The bot has no access controls, so anyone in this room can use the bot - secure this room! 112 Do not enable end-to-end encryption for this room, unless set up with Pantalaimon. 113 114 ::: {.warning} 115 When using a room alias, make sure the alias used is on the local homeserver! 116 This prevents an issue where the control room becomes undefined when the alias can't be resolved. 117 ::: 118 ''; 119 }; 120 121 dataPath = mkOption { 122 type = types.path; 123 readOnly = true; 124 default = "/var/lib/draupnir"; 125 description = '' 126 The path Draupnir will store its state/data in. 127 128 ::: {.warning} 129 This option is read-only. 130 ::: 131 132 ::: {.note} 133 If you want to customize where this data is stored, use a bind mount. 134 ::: 135 ''; 136 }; 137 }; 138 }; 139 }; 140 141 secrets = { 142 accessToken = mkOption { 143 type = types.nullOr types.path; 144 default = null; 145 description = '' 146 File containing the access token for Draupnir's Matrix account 147 to be used in place of {option}`${opt.settings}.accessToken`. 148 ''; 149 }; 150 151 pantalaimon.password = mkOption { 152 type = types.nullOr types.path; 153 default = null; 154 description = '' 155 File containing the password for Draupnir's Matrix account when used in 156 conjunction with Pantalaimon to be used in place of 157 {option}`${opt.settings}.pantalaimon.password`. 158 159 ::: {.warning} 160 Take note that upstream has limited Pantalaimon and E2EE support: 161 <https://the-draupnir-project.github.io/draupnir-documentation/bot/encryption> and 162 <https://the-draupnir-project.github.io/draupnir-documentation/shared/dogfood#e2ee-support>. 163 ::: 164 ''; 165 }; 166 167 web.synapseHTTPAntispam.authorization = mkOption { 168 type = types.nullOr types.path; 169 default = null; 170 description = '' 171 File containing the secret token when using the Synapse HTTP Antispam module 172 to be used in place of 173 {option}`${opt.settings}.web.synapseHTTPAntispam.authorization`. 174 175 See <https://the-draupnir-project.github.io/draupnir-documentation/bot/synapse-http-antispam> for details. 176 ''; 177 }; 178 }; 179 }; 180 181 config = lib.mkIf cfg.enable { 182 assertions = [ 183 { 184 # Removed option for those migrating from the Mjolnir module - mkRemovedOption module does *not* work with submodules. 185 assertion = !(cfg.settings ? protectedRooms); 186 message = "Unset ${opt.settings}.protectedRooms, as it is unsupported on Draupnir. Add these rooms via `!draupnir rooms add` instead."; 187 } 188 ]; 189 190 systemd.services.draupnir = { 191 description = "Draupnir - a moderation bot for Matrix"; 192 wants = [ 193 "network-online.target" 194 "matrix-synapse.service" 195 "conduit.service" 196 "dendrite.service" 197 ]; 198 after = [ 199 "network-online.target" 200 "matrix-synapse.service" 201 "conduit.service" 202 "dendrite.service" 203 ]; 204 wantedBy = [ "multi-user.target" ]; 205 206 startLimitIntervalSec = 0; 207 serviceConfig = { 208 ExecStart = toString ( 209 [ 210 (lib.getExe cfg.package) 211 "--draupnir-config" 212 configFile 213 ] 214 ++ lib.optionals (cfg.secrets.accessToken != null) [ 215 "--access-token-path" 216 "%d/access_token" 217 ] 218 ++ lib.optionals (cfg.secrets.pantalaimon.password != null) [ 219 "--pantalaimon-password-path" 220 "%d/pantalaimon_password" 221 ] 222 ++ lib.optionals (cfg.secrets.web.synapseHTTPAntispam.authorization != null) [ 223 "--http-antispam-authorization-path" 224 "%d/http_antispam_authorization" 225 ] 226 ); 227 228 WorkingDirectory = "/var/lib/draupnir"; 229 StateDirectory = "draupnir"; 230 StateDirectoryMode = "0700"; 231 ProtectHome = true; 232 PrivateDevices = true; 233 Restart = "on-failure"; 234 RestartSec = "5s"; 235 DynamicUser = true; 236 LoadCredential = 237 lib.optionals (cfg.secrets.accessToken != null) [ 238 "access_token:${cfg.secrets.accessToken}" 239 ] 240 ++ lib.optionals (cfg.secrets.pantalaimon.password != null) [ 241 "pantalaimon_password:${cfg.secrets.pantalaimon.password}" 242 ] 243 ++ lib.optionals (cfg.secrets.web.synapseHTTPAntispam.authorization != null) [ 244 "http_antispam_authorization:${cfg.secrets.web.synapseHTTPAntispam.authorization}" 245 ]; 246 }; 247 }; 248 }; 249 250 meta = { 251 doc = ./draupnir.md; 252 maintainers = with lib.maintainers; [ 253 RorySys 254 emilylange 255 ]; 256 }; 257}