diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix index a7ef2ca1803b..91df29236b8f 100644 --- a/nixos/modules/misc/ids.nix +++ b/nixos/modules/misc/ids.nix @@ -125,6 +125,7 @@ minecraft = 114; monetdb = 115; rippled = 116; + murmur = 117; # When adding a uid, make sure it doesn't match an existing gid. diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index fa9c222fc8d1..86bb87e91dec 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -182,6 +182,7 @@ ./services/networking/ircd-hybrid/default.nix ./services/networking/kippo.nix ./services/networking/minidlna.nix + ./services/networking/murmur.nix ./services/networking/nat.nix ./services/networking/networkmanager.nix ./services/networking/ngircd.nix diff --git a/nixos/modules/services/networking/murmur.nix b/nixos/modules/services/networking/murmur.nix new file mode 100644 index 000000000000..2a5549beaf89 --- /dev/null +++ b/nixos/modules/services/networking/murmur.nix @@ -0,0 +1,253 @@ +{ config, pkgs, ... }: + +with pkgs.lib; + +let + cfg = config.services.murmur; + configFile = pkgs.writeText "murmurd.ini" '' + database=/var/lib/murmur/murmur.sqlite + dbDriver=QSQLITE + + autobanAttempts=${toString cfg.autobanAttempts} + autobanTimeframe=${toString cfg.autobanTimeframe} + autobanTime=${toString cfg.autobanTime} + + logfile=/var/log/murmur/murmurd.log + pidfile=${cfg.pidfile} + + welcome="${cfg.welcome}" + port=${toString cfg.port} + + ${if cfg.hostName == "" then "" else "host="+cfg.hostName} + ${if cfg.password == "" then "" else "serverpassword="+cfg.password} + + bandwidth=${toString cfg.bandwidth} + users=${toString cfg.users} + + textmessagelength=${toString cfg.textMsgLength} + imagemessagelength=${toString cfg.imgMsgLength} + allowhtml=${if cfg.allowHtml then "true" else "false"} + logdays=${toString cfg.logDays} + bonjour=${if cfg.bonjour then "true" else "false"} + sendversion=${if cfg.sendVersion then "true" else "false"} + + ${if cfg.registerName == "" then "" else "registerName="+cfg.registerName} + ${if cfg.registerPassword == "" then "" else "registerPassword="+cfg.registerPassword} + ${if cfg.registerUrl == "" then "" else "registerUrl="+cfg.registerUrl} + ${if cfg.registerHostname == "" then "" else "registerHostname="+cfg.registerHostname} + + certrequired=${if cfg.clientCertRequired then "true" else "false"} + ${if cfg.sslCert == "" then "" else "sslCert="+cfg.sslCert} + ${if cfg.sslKey == "" then "" else "sslKey="+cfg.sslKey} + ''; +in +{ + options = { + services.murmur = { + enable = mkOption { + type = types.bool; + default = false; + description = "If enabled, start the Murmur Service."; + }; + + autobanAttempts = mkOption { + type = types.int; + default = 10; + description = '' + Number of attempts a client is allowed to make in + autobanTimeframe seconds, before being + banned for autobanTime. + ''; + }; + + autobanTimeframe = mkOption { + type = types.int; + default = 120; + description = '' + Timeframe in which a client can connect without being banned + for repeated attempts (in seconds). + ''; + }; + + autobanTime = mkOption { + type = types.int; + default = 300; + description = "The amount of time an IP ban lasts (in seconds)."; + }; + + pidfile = mkOption { + type = types.path; + default = "/tmp/murmurd.pid"; + description = "Path to PID file for Murmur daemon."; + }; + + welcome = mkOption { + type = types.str; + default = ""; + description = "Welcome message for connected clients."; + }; + + port = mkOption { + type = types.int; + default = 64738; + description = "Ports to bind to (UDP and TCP)."; + }; + + hostName = mkOption { + type = types.str; + default = ""; + description = "Host to bind to. Defaults binding on all addresses."; + }; + + password = mkOption { + type = types.str; + default = ""; + description = "Required password to join server, if specified."; + }; + + bandwidth = mkOption { + type = types.int; + default = 72000; + description = '' + Maximum bandwidth (in bits per second) that clients may send + speech at. + ''; + }; + + users = mkOption { + type = types.int; + default = 100; + description = "Maximum number of concurrent clients allowed."; + }; + + textMsgLength = mkOption { + type = types.int; + default = 5000; + description = "Max length of text messages. Set 0 for no limit."; + }; + + imgMsgLength = mkOption { + type = types.int; + default = 131072; + description = "Max length of image messages. Set 0 for no limit."; + }; + + allowHtml = mkOption { + type = types.bool; + default = true; + description = '' + Allow HTML in client messages, comments, and channel + descriptions. + ''; + }; + + logDays = mkOption { + type = types.int; + default = 31; + description = '' + How long to store RPC logs for in the database. Set 0 to + keep logs forever, or -1 to disable DB logging. + ''; + }; + + bonjour = mkOption { + type = types.bool; + default = false; + description = '' + Enable Bonjour auto-discovery, which allows clients over + your LAN to automatically discover Murmur servers. + ''; + }; + + sendVersion = mkOption { + type = types.bool; + default = true; + description = "Send Murmur version in UDP response."; + }; + + registerName = mkOption { + type = types.str; + default = ""; + description = '' + Public server registration name, and also the name of the + Root channel. Even if you don't publicly register your + server, you probably still want to set this. + ''; + }; + + registerPassword = mkOption { + type = types.str; + default = ""; + description = '' + Public server registry password, used authenticate your + server to the registry to prevent impersonation; required for + subsequent registry updates. + ''; + }; + + registerUrl = mkOption { + type = types.str; + default = ""; + description = "URL website for your server."; + }; + + registerHostname = mkOption { + type = types.str; + default = ""; + description = '' + DNS hostname where your server can be reached. This is only + needed if you want your server to be accessed by its + hostname and not IP - but the name *must* resolve on the + internet properly. + ''; + }; + + clientCertRequired = mkOption { + type = types.bool; + default = false; + description = "Require clients to authenticate via certificates."; + }; + + sslCert = mkOption { + type = types.str; + default = ""; + description = "Path to your SSL certificate."; + }; + + sslKey = mkOption { + type = types.str; + default = ""; + description = "Path to your SSL key."; + }; + }; + }; + + config = mkIf cfg.enable { + users.extraUsers.murmur = { + description = "Murmur Service user"; + home = "/var/lib/murmur"; + createHome = true; + uid = config.ids.uids.murmur; + }; + + systemd.services.murmur = { + description = "Murmur Chat Service"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target "]; + + serviceConfig = { + Type = "forking"; + PIDFile = cfg.pidfile; + Restart = "always"; + User = "murmur"; + ExecStart = "${pkgs.murmur}/bin/murmurd -ini ${configFile}"; + PermissionsStartOnly = true; + }; + + preStart = '' + mkdir -p /var/log/murmur + chown -R murmur /var/log/murmur + ''; + }; + }; +} diff --git a/nixos/tests/default.nix b/nixos/tests/default.nix index 4aeb7f55ac39..955c87b2714e 100644 --- a/nixos/tests/default.nix +++ b/nixos/tests/default.nix @@ -25,6 +25,7 @@ with import ../lib/testing.nix { inherit system minimal; }; mysql = makeTest (import ./mysql.nix); mysql_replication = makeTest (import ./mysql-replication.nix); munin = makeTest (import ./munin.nix); + mumble = makeTest (import ./mumble.nix); nat = makeTest (import ./nat.nix); nfs3 = makeTest (import ./nfs.nix { version = 3; }); #nfs4 = makeTest (import ./nfs.nix { version = 4; }); diff --git a/nixos/tests/mumble.nix b/nixos/tests/mumble.nix new file mode 100644 index 000000000000..509742f2899b --- /dev/null +++ b/nixos/tests/mumble.nix @@ -0,0 +1,54 @@ +{ pkgs, ... }: + +let + client = { config, pkgs, ... }: { + imports = [ ./common/x11.nix ]; + environment.systemPackages = [ pkgs.mumble ]; + }; +in +{ + nodes = { + server = { config, pkgs, ... }: { + services.murmur.enable = true; + services.murmur.registerName = "NixOS tests"; + }; + + client1 = client; + client2 = client; + }; + + testScript = '' + startAll; + + $server->waitForUnit("murmur.service"); + $client1->waitForX; + $client2->waitForX; + + $client1->execute("mumble mumble://client1\@server/test &"); + $client2->execute("mumble mumble://client2\@server/test &"); + + $server->sleep(10); # Wait for Mumble UI to pop up + + # cancel client audio configuration + $client1->sendKeys("esc"); + $client2->sendKeys("esc"); + $server->sleep(1); + + # cancel client cert configuration + $client1->sendKeys("esc"); + $client2->sendKeys("esc"); + $server->sleep(1); + + # accept server certificate + $client1->sendChars("y"); + $client2->sendChars("y"); + + # Find clients in logs + $server->waitUntilSucceeds("grep -q 'client1' /var/log/murmur/murmurd.log"); + $server->waitUntilSucceeds("grep -q 'client2' /var/log/murmur/murmurd.log"); + + $server->sleep(5); # wait to get screenshot + $client1->screenshot("screen1"); + $client2->screenshot("screen2"); + ''; +}