at 25.11-pre 14 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7 8with lib; 9 10let 11 cfg = config.services.murmur; 12 forking = cfg.logFile != null; 13 configFile = pkgs.writeText "murmurd.ini" '' 14 database=${cfg.stateDir}/murmur.sqlite 15 dbDriver=QSQLITE 16 17 autobanAttempts=${toString cfg.autobanAttempts} 18 autobanTimeframe=${toString cfg.autobanTimeframe} 19 autobanTime=${toString cfg.autobanTime} 20 21 logfile=${optionalString (cfg.logFile != null) cfg.logFile} 22 ${optionalString forking "pidfile=/run/murmur/murmurd.pid"} 23 24 welcometext="${cfg.welcometext}" 25 port=${toString cfg.port} 26 27 ${optionalString (cfg.hostName != "") "host=${cfg.hostName}"} 28 ${optionalString (cfg.password != "") "serverpassword=${cfg.password}"} 29 30 bandwidth=${toString cfg.bandwidth} 31 users=${toString cfg.users} 32 33 textmessagelength=${toString cfg.textMsgLength} 34 imagemessagelength=${toString cfg.imgMsgLength} 35 allowhtml=${boolToString cfg.allowHtml} 36 logdays=${toString cfg.logDays} 37 bonjour=${boolToString cfg.bonjour} 38 sendversion=${boolToString cfg.sendVersion} 39 40 ${optionalString (cfg.registerName != "") "registerName=${cfg.registerName}"} 41 ${optionalString (cfg.registerPassword != "") "registerPassword=${cfg.registerPassword}"} 42 ${optionalString (cfg.registerUrl != "") "registerUrl=${cfg.registerUrl}"} 43 ${optionalString (cfg.registerHostname != "") "registerHostname=${cfg.registerHostname}"} 44 45 certrequired=${boolToString cfg.clientCertRequired} 46 ${optionalString (cfg.sslCert != "") "sslCert=${cfg.sslCert}"} 47 ${optionalString (cfg.sslKey != "") "sslKey=${cfg.sslKey}"} 48 ${optionalString (cfg.sslCa != "") "sslCA=${cfg.sslCa}"} 49 50 ${optionalString (cfg.dbus != null) "dbus=${cfg.dbus}"} 51 52 ${cfg.extraConfig} 53 ''; 54in 55{ 56 imports = [ 57 (mkRenamedOptionModule [ "services" "murmur" "welcome" ] [ "services" "murmur" "welcometext" ]) 58 (mkRemovedOptionModule [ "services" "murmur" "pidfile" ] "Hardcoded to /run/murmur/murmurd.pid now") 59 ]; 60 61 options = { 62 services.murmur = { 63 enable = mkOption { 64 type = types.bool; 65 default = false; 66 description = "If enabled, start the Murmur Mumble server."; 67 }; 68 69 openFirewall = mkOption { 70 type = types.bool; 71 default = false; 72 description = '' 73 Open ports in the firewall for the Murmur Mumble server. 74 ''; 75 }; 76 77 user = mkOption { 78 type = types.str; 79 default = "murmur"; 80 description = '' 81 The name of an existing user to use to run the service. 82 If not specified, the default user will be created. 83 ''; 84 }; 85 86 group = mkOption { 87 type = types.str; 88 default = "murmur"; 89 description = '' 90 The name of an existing group to use to run the service. 91 If not specified, the default group will be created. 92 ''; 93 }; 94 95 stateDir = mkOption { 96 type = types.path; 97 default = "/var/lib/murmur"; 98 description = '' 99 Directory to store data for the server. 100 ''; 101 }; 102 103 autobanAttempts = mkOption { 104 type = types.int; 105 default = 10; 106 description = '' 107 Number of attempts a client is allowed to make in 108 `autobanTimeframe` seconds, before being 109 banned for `autobanTime`. 110 ''; 111 }; 112 113 autobanTimeframe = mkOption { 114 type = types.int; 115 default = 120; 116 description = '' 117 Timeframe in which a client can connect without being banned 118 for repeated attempts (in seconds). 119 ''; 120 }; 121 122 autobanTime = mkOption { 123 type = types.int; 124 default = 300; 125 description = "The amount of time an IP ban lasts (in seconds)."; 126 }; 127 128 logFile = mkOption { 129 type = types.nullOr types.path; 130 default = null; 131 example = "/var/log/murmur/murmurd.log"; 132 description = "Path to the log file for Murmur daemon. Empty means log to journald."; 133 }; 134 135 welcometext = mkOption { 136 type = types.str; 137 default = ""; 138 description = "Welcome message for connected clients."; 139 }; 140 141 port = mkOption { 142 type = types.port; 143 default = 64738; 144 description = "Ports to bind to (UDP and TCP)."; 145 }; 146 147 hostName = mkOption { 148 type = types.str; 149 default = ""; 150 description = "Host to bind to. Defaults binding on all addresses."; 151 }; 152 153 package = mkPackageOption pkgs "murmur" { }; 154 155 password = mkOption { 156 type = types.str; 157 default = ""; 158 description = "Required password to join server, if specified."; 159 }; 160 161 bandwidth = mkOption { 162 type = types.int; 163 default = 72000; 164 description = '' 165 Maximum bandwidth (in bits per second) that clients may send 166 speech at. 167 ''; 168 }; 169 170 users = mkOption { 171 type = types.int; 172 default = 100; 173 description = "Maximum number of concurrent clients allowed."; 174 }; 175 176 textMsgLength = mkOption { 177 type = types.int; 178 default = 5000; 179 description = "Max length of text messages. Set 0 for no limit."; 180 }; 181 182 imgMsgLength = mkOption { 183 type = types.int; 184 default = 131072; 185 description = "Max length of image messages. Set 0 for no limit."; 186 }; 187 188 allowHtml = mkOption { 189 type = types.bool; 190 default = true; 191 description = '' 192 Allow HTML in client messages, comments, and channel 193 descriptions. 194 ''; 195 }; 196 197 logDays = mkOption { 198 type = types.int; 199 default = 31; 200 description = '' 201 How long to store RPC logs for in the database. Set 0 to 202 keep logs forever, or -1 to disable DB logging. 203 ''; 204 }; 205 206 bonjour = mkOption { 207 type = types.bool; 208 default = false; 209 description = '' 210 Enable Bonjour auto-discovery, which allows clients over 211 your LAN to automatically discover Murmur servers. 212 ''; 213 }; 214 215 sendVersion = mkOption { 216 type = types.bool; 217 default = true; 218 description = "Send Murmur version in UDP response."; 219 }; 220 221 registerName = mkOption { 222 type = types.str; 223 default = ""; 224 description = '' 225 Public server registration name, and also the name of the 226 Root channel. Even if you don't publicly register your 227 server, you probably still want to set this. 228 ''; 229 }; 230 231 registerPassword = mkOption { 232 type = types.str; 233 default = ""; 234 description = '' 235 Public server registry password, used authenticate your 236 server to the registry to prevent impersonation; required for 237 subsequent registry updates. 238 ''; 239 }; 240 241 registerUrl = mkOption { 242 type = types.str; 243 default = ""; 244 description = "URL website for your server."; 245 }; 246 247 registerHostname = mkOption { 248 type = types.str; 249 default = ""; 250 description = '' 251 DNS hostname where your server can be reached. This is only 252 needed if you want your server to be accessed by its 253 hostname and not IP - but the name *must* resolve on the 254 internet properly. 255 ''; 256 }; 257 258 clientCertRequired = mkOption { 259 type = types.bool; 260 default = false; 261 description = "Require clients to authenticate via certificates."; 262 }; 263 264 sslCert = mkOption { 265 type = types.str; 266 default = ""; 267 description = "Path to your SSL certificate."; 268 }; 269 270 sslKey = mkOption { 271 type = types.str; 272 default = ""; 273 description = "Path to your SSL key."; 274 }; 275 276 sslCa = mkOption { 277 type = types.str; 278 default = ""; 279 description = "Path to your SSL CA certificate."; 280 }; 281 282 extraConfig = mkOption { 283 type = types.lines; 284 default = ""; 285 description = "Extra configuration to put into murmur.ini."; 286 }; 287 288 environmentFile = mkOption { 289 type = types.nullOr types.path; 290 default = null; 291 example = literalExpression ''"''${config.services.murmur.stateDir}/murmurd.env"''; 292 description = '' 293 Environment file as defined in {manpage}`systemd.exec(5)`. 294 295 Secrets may be passed to the service without adding them to the world-readable 296 Nix store, by specifying placeholder variables as the option value in Nix and 297 setting these variables accordingly in the environment file. 298 299 ``` 300 # snippet of murmur-related config 301 services.murmur.password = "$MURMURD_PASSWORD"; 302 ``` 303 304 ``` 305 # content of the environment file 306 MURMURD_PASSWORD=verysecretpassword 307 ``` 308 309 Note that this file needs to be available on the host on which 310 `murmur` is running. 311 ''; 312 }; 313 314 dbus = mkOption { 315 type = types.enum [ 316 null 317 "session" 318 "system" 319 ]; 320 default = null; 321 description = "Enable D-Bus remote control. Set to the bus you want Murmur to connect to."; 322 }; 323 }; 324 }; 325 326 config = mkIf cfg.enable { 327 users.users.murmur = mkIf (cfg.user == "murmur") { 328 description = "Murmur Service user"; 329 home = cfg.stateDir; 330 createHome = true; 331 uid = config.ids.uids.murmur; 332 group = cfg.group; 333 }; 334 users.groups.murmur = mkIf (cfg.group == "murmur") { 335 gid = config.ids.gids.murmur; 336 }; 337 338 networking.firewall = mkIf cfg.openFirewall { 339 allowedTCPPorts = [ cfg.port ]; 340 allowedUDPPorts = [ cfg.port ]; 341 }; 342 343 systemd.services.murmur = { 344 description = "Murmur Chat Service"; 345 wantedBy = [ "multi-user.target" ]; 346 after = [ "network.target" ]; 347 preStart = '' 348 ${pkgs.envsubst}/bin/envsubst \ 349 -o /run/murmur/murmurd.ini \ 350 -i ${configFile} 351 ''; 352 353 serviceConfig = { 354 # murmurd doesn't fork when logging to the console. 355 Type = if forking then "forking" else "simple"; 356 PIDFile = mkIf forking "/run/murmur/murmurd.pid"; 357 EnvironmentFile = mkIf (cfg.environmentFile != null) cfg.environmentFile; 358 ExecStart = "${cfg.package}/bin/mumble-server -ini /run/murmur/murmurd.ini"; 359 Restart = "always"; 360 RuntimeDirectory = "murmur"; 361 RuntimeDirectoryMode = "0700"; 362 User = cfg.user; 363 Group = cfg.group; 364 365 # service hardening 366 AmbientCapabilities = "CAP_NET_BIND_SERVICE"; 367 CapabilityBoundingSet = "CAP_NET_BIND_SERVICE"; 368 LockPersonality = true; 369 MemoryDenyWriteExecute = true; 370 NoNewPrivileges = true; 371 PrivateDevices = true; 372 PrivateTmp = true; 373 ProtectClock = true; 374 ProtectControlGroups = true; 375 ProtectHome = true; 376 ProtectHostname = true; 377 ProtectKernelLogs = true; 378 ProtectKernelModules = true; 379 ProtectKernelTunables = true; 380 ProtectSystem = "full"; 381 RestrictAddressFamilies = "~AF_PACKET AF_NETLINK"; 382 RestrictNamespaces = true; 383 RestrictSUIDSGID = true; 384 RestrictRealtime = true; 385 SystemCallArchitectures = "native"; 386 SystemCallFilter = "@system-service"; 387 UMask = 27; 388 }; 389 }; 390 391 # currently not included in upstream package, addition requested at 392 # https://github.com/mumble-voip/mumble/issues/6078 393 services.dbus.packages = mkIf (cfg.dbus == "system") [ 394 (pkgs.writeTextFile { 395 name = "murmur-dbus-policy"; 396 text = '' 397 <!DOCTYPE busconfig PUBLIC 398 "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" 399 "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> 400 <busconfig> 401 <policy user="${cfg.user}"> 402 <allow own="net.sourceforge.mumble.murmur"/> 403 </policy> 404 405 <policy context="default"> 406 <allow send_destination="net.sourceforge.mumble.murmur"/> 407 <allow receive_sender="net.sourceforge.mumble.murmur"/> 408 </policy> 409 </busconfig> 410 ''; 411 destination = "/share/dbus-1/system.d/murmur.conf"; 412 }) 413 ]; 414 415 security.apparmor.policies."bin.mumble-server".profile = 416 '' 417 include <tunables/global> 418 419 ${cfg.package}/bin/{mumble-server,.mumble-server-wrapped} { 420 include <abstractions/base> 421 include <abstractions/nameservice> 422 include <abstractions/ssl_certs> 423 include "${pkgs.apparmorRulesFromClosure { name = "mumble-server"; } cfg.package}" 424 pix ${cfg.package}/bin/.mumble-server-wrapped, 425 426 r ${config.environment.etc."os-release".source}, 427 r ${config.environment.etc."lsb-release".source}, 428 owner rwk ${cfg.stateDir}/murmur.sqlite, 429 owner rw ${cfg.stateDir}/murmur.sqlite-journal, 430 owner r ${cfg.stateDir}/, 431 r /run/murmur/murmurd.pid, 432 r /run/murmur/murmurd.ini, 433 r ${configFile}, 434 '' 435 + optionalString (cfg.logFile != null) '' 436 rw ${cfg.logFile}, 437 '' 438 + optionalString (cfg.sslCert != "") '' 439 r ${cfg.sslCert}, 440 '' 441 + optionalString (cfg.sslKey != "") '' 442 r ${cfg.sslKey}, 443 '' 444 + optionalString (cfg.sslCa != "") '' 445 r ${cfg.sslCa}, 446 '' 447 + optionalString (cfg.dbus != null) '' 448 dbus bus=${cfg.dbus} 449 '' 450 + '' 451 } 452 ''; 453 }; 454 455 meta.maintainers = with lib.maintainers; [ felixsinger ]; 456}