nixos/umurmur: init (#387580)

Changed files
+393
nixos
doc
manual
modules
services
networking
tests
pkgs
by-name
um
umurmur
+6
nixos/doc/manual/redirects.json
···
"module-services-foundationdb-full-docs": [
"index.html#module-services-foundationdb-full-docs"
],
+
"module-service-umurmur": [
+
"index.html#module-service-umurmur"
+
],
+
"module-service-umurmur-quick-start": [
+
"index.html#module-service-umurmur-quick-start"
+
],
"module-borgbase": [
"index.html#module-borgbase"
],
+2
nixos/doc/manual/release-notes/rl-2505.section.md
···
- [Yggdrasil-Jumper](https://github.com/one-d-wide/yggdrasil-jumper) is an independent project that aims to transparently reduce latency of a connection over Yggdrasil network, utilizing NAT traversal to automatically bypass intermediary nodes.
+
- [uMurmur](https://umurmur.net), minimalistic Mumble server primarily targeted to run on embedded computers. Available as [services.umurmur](options.html#opt-services.umurmur).
+
- [Zenoh](https://zenoh.io/), a pub/sub/query protocol with low overhead. The Zenoh router daemon is available as [services.zenohd](options.html#opt-services.zenohd.enable)
- [ytdl-sub](https://github.com/jmbannon/ytdl-sub), a tool that downloads media via yt-dlp and prepares it for your favorite media player, including Kodi, Jellyfin, Plex, Emby, and modern music players. Available as [services.ytdl-sub](options.html#opt-services.ytdl-sub.instances).
+1
nixos/modules/module-list.nix
···
./services/networking/trickster.nix
./services/networking/twingate.nix
./services/networking/ucarp.nix
+
./services/networking/umurmur.nix
./services/networking/unbound.nix
./services/networking/unifi.nix
./services/networking/uptermd.nix
+33
nixos/modules/services/networking/umurmur.md
···
+
# uMurmur {#module-service-umurmur}
+
+
[uMurmur](http://umurmur.net/) is a minimalistic Mumble server primarily targeted to run on embedded computers. This module enables it (`umurmurd`).
+
+
## Quick Start {#module-service-umurmur-quick-start}
+
+
```nix
+
{
+
services.umurmur = {
+
enable = true;
+
openFirewall = true;
+
settings = {
+
port = 7365;
+
channels = [
+
{
+
name = "root";
+
parent = "";
+
description = "Root channel. No entry.";
+
noenter = true;
+
}
+
{
+
name = "lobby";
+
parent = "root";
+
description = "Lobby channel";
+
}
+
];
+
default_channel = "lobby";
+
};
+
};
+
}
+
```
+
+
See a full configuration in [umurmur.conf.example](https://github.com/umurmur/umurmur/blob/master/umurmur.conf.example)
+244
nixos/modules/services/networking/umurmur.nix
···
+
{
+
config,
+
lib,
+
pkgs,
+
...
+
}:
+
+
let
+
cfg = config.services.umurmur;
+
dumpAttrset =
+
x: top_level:
+
(lib.optionalString (!top_level) "{")
+
+ (lib.concatLines (lib.mapAttrsToList (name: value: "${name} = ${toConfigValue value false};") x))
+
+ (lib.optionalString (!top_level) "}");
+
dumpList = x: top_level: "(${lib.concatStringsSep ",\n" (map (y: "${toConfigValue y false}") x)})";
+
+
toConfigValue =
+
x: top_level:
+
if builtins.isList x then
+
dumpList x top_level
+
else if builtins.isAttrs x then
+
dumpAttrset x top_level
+
else
+
builtins.toJSON x;
+
dumpCfg = x: toConfigValue x true;
+
configAttrs = lib.filterAttrsRecursive (name: value: value != null) cfg.settings;
+
configFile = pkgs.writeTextFile {
+
name = "umurmur.conf";
+
checkPhase = ''
+
${lib.getExe cfg.package} -t -c "$target"
+
'';
+
text = "\n" + (dumpCfg configAttrs) + "\n";
+
};
+
in
+
{
+
options = {
+
services.umurmur = {
+
enable = lib.mkEnableOption "uMurmur Mumble server";
+
+
package = lib.mkPackageOption pkgs "umurmur" { };
+
+
openFirewall = lib.mkOption {
+
type = lib.types.bool;
+
default = false;
+
description = ''
+
Open ports in the firewall for the uMurmur Mumble server.
+
'';
+
};
+
+
settings = lib.mkOption {
+
type = lib.types.submodule {
+
freeformType =
+
let
+
valueType =
+
with lib.types;
+
oneOf [
+
bool
+
int
+
float
+
str
+
path
+
(listOf (attrsOf valueType))
+
]
+
// {
+
description = "uMurmur config value";
+
};
+
in
+
valueType;
+
options = {
+
welcometext = lib.mkOption {
+
type = lib.types.nullOr lib.types.str;
+
default = "Welcome to uMurmur!";
+
description = "Welcome message for connected clients.";
+
};
+
+
bindaddr = lib.mkOption {
+
type = lib.types.str;
+
default = "0.0.0.0";
+
description = "IPv4 address to bind to. Defaults binding on all addresses.";
+
};
+
+
bindaddr6 = lib.mkOption {
+
type = lib.types.str;
+
default = "::";
+
description = "IPv6 address to bind to. Defaults binding on all addresses.";
+
};
+
+
bindport = lib.mkOption {
+
type = lib.types.port;
+
default = 64739;
+
description = "Port to bind to (UDP and TCP).";
+
};
+
+
password = lib.mkOption {
+
type = lib.types.nullOr lib.types.str;
+
default = null;
+
description = "Required password to join server, if specified.";
+
};
+
+
max_bandwidth = lib.mkOption {
+
type = lib.types.int;
+
default = 48000;
+
description = ''
+
Maximum bandwidth (in bits per second) that clients may send
+
speech at.
+
'';
+
};
+
+
max_users = lib.mkOption {
+
type = lib.types.int;
+
default = 10;
+
description = "Maximum number of concurrent clients allowed.";
+
};
+
+
certificate = lib.mkOption {
+
type = lib.types.str;
+
default = "/var/lib/private/umurmur/cert.crt";
+
description = "Path to your SSL certificate. Generates self-signed automatically if not exists.";
+
};
+
+
private_key = lib.mkOption {
+
type = lib.types.str;
+
default = "/var/lib/private/umurmur/key.key";
+
description = "Path to your SSL key. Generates self-signed automatically if not exists.";
+
};
+
+
ca_path = lib.mkOption {
+
type = lib.types.nullOr lib.types.str;
+
default = null;
+
description = "Path to your SSL CA certificate.";
+
};
+
+
channels = lib.mkOption {
+
type = lib.types.listOf lib.types.attrs;
+
default = [
+
{
+
name = "root";
+
parent = "";
+
description = "Root channel.";
+
noenter = false;
+
}
+
];
+
description = "Channel tree definitions.";
+
};
+
+
channel_links = lib.mkOption {
+
type = lib.types.listOf lib.types.attrs;
+
default = [ ];
+
example = [
+
{
+
source = "Lobby";
+
destination = "Red team";
+
}
+
];
+
description = "Channel tree definitions.";
+
};
+
+
default_channel = lib.mkOption {
+
type = lib.types.str;
+
default = "root";
+
description = "The channel in which users will appear in when connecting.";
+
};
+
+
};
+
};
+
default = { };
+
description = "Settings of uMurmur. For reference see https://github.com/umurmur/umurmur/blob/master/umurmur.conf.example";
+
};
+
+
configFile = lib.mkOption rec {
+
type = lib.types.path;
+
default = configFile;
+
description = "Configuration file, default is generated from config.service.umurmur.settings";
+
defaultText = description;
+
};
+
};
+
};
+
+
config = lib.mkIf cfg.enable {
+
networking.firewall = lib.mkIf cfg.openFirewall {
+
allowedTCPPorts = [ cfg.settings.bindport ];
+
allowedUDPPorts = [ cfg.settings.bindport ];
+
};
+
+
systemd.services.umurmur = {
+
description = "uMurmur Mumble Server";
+
wants = [ "network.target" ];
+
after = [ "network.target" ];
+
wantedBy = [ "multi-user.target" ];
+
serviceConfig = {
+
Type = "exec";
+
ExecStart = "${lib.getExe cfg.package} -d -c ${cfg.configFile}";
+
Restart = "on-failure";
+
DynamicUser = true;
+
StateDirectory = "umurmur";
+
ReadWritePaths = "/dev/shm";
+
+
# hardening
+
UMask = 27;
+
MemoryDenyWriteExecute = true;
+
AmbientCapabilities = [ "" ];
+
CapabilityBoundingSet = [ "" ];
+
DevicePolicy = "closed";
+
LockPersonality = true;
+
NoNewPrivileges = true;
+
PrivateDevices = true;
+
PrivateTmp = true;
+
PrivateUsers = true;
+
ProtectSystem = "full";
+
ProtectClock = true;
+
ProtectControlGroups = true;
+
ProtectHome = true;
+
ProtectHostname = true;
+
ProtectKernelLogs = true;
+
ProtectKernelModules = true;
+
ProtectKernelTunables = true;
+
ProcSubset = "pid";
+
ProtectProc = "invisible";
+
RemoveIPC = true;
+
RestrictAddressFamilies = [
+
"AF_UNIX"
+
"AF_INET"
+
"AF_INET6"
+
];
+
RestrictNamespaces = true;
+
RestrictRealtime = true;
+
RestrictSUIDSGID = true;
+
SystemCallArchitectures = "native";
+
SystemCallFilter = [
+
"@system-service"
+
"~@cpu-emulation"
+
"~@debug"
+
"~@mount"
+
"~@obsolete"
+
"~@privileged"
+
"~@resources"
+
];
+
};
+
};
+
};
+
+
meta.maintainers = with lib.maintainers; [ _3JlOy-PYCCKUi ];
+
meta.doc = ./umurmur.md;
+
}
+1
nixos/tests/all-tests.nix
···
ucarp = handleTest ./ucarp.nix {};
udisks2 = handleTest ./udisks2.nix {};
ulogd = handleTest ./ulogd/ulogd.nix {};
+
umurmur = handleTest ./umurmur.nix {};
unbound = handleTest ./unbound.nix {};
unifi = handleTest ./unifi.nix {};
unit-php = handleTest ./web-servers/unit-php.nix {};
+99
nixos/tests/umurmur.nix
···
+
import ./make-test-python.nix (
+
{ pkgs, ... }:
+
+
let
+
client =
+
{ pkgs, ... }:
+
{
+
imports = [ ./common/x11.nix ];
+
environment.systemPackages = [ pkgs.mumble ];
+
};
+
port = 56457;
+
in
+
{
+
name = "mumble";
+
meta = with pkgs.lib.maintainers; {
+
maintainers = [ _3JlOy-PYCCKUi ];
+
};
+
+
nodes = {
+
server =
+
{ ... }:
+
{
+
services.umurmur = {
+
enable = true;
+
openFirewall = true;
+
settings = {
+
password = "testpassword";
+
channels = [
+
{
+
name = "root";
+
parent = "";
+
description = "Root channel. No entry.";
+
noenter = true;
+
}
+
{
+
name = "lobby";
+
parent = "root";
+
description = "Lobby channel";
+
}
+
];
+
default_channel = "lobby";
+
bindport = port;
+
};
+
};
+
};
+
+
client1 = client;
+
client2 = client;
+
};
+
+
testScript = ''
+
start_all()
+
+
server.wait_for_unit("umurmur.service")
+
client1.wait_for_x()
+
client2.wait_for_x()
+
+
client1.execute("mumble mumble://client1:testpassword\@server:${toString port}/lobby >&2 &")
+
client2.execute("mumble mumble://client2:testpassword\@server:${toString port}/lobby >&2 &")
+
+
# cancel client audio configuration
+
client1.wait_for_window(r"Audio Tuning Wizard")
+
client2.wait_for_window(r"Audio Tuning Wizard")
+
server.sleep(5) # wait because mumble is slow to register event handlers
+
client1.send_key("esc")
+
client2.send_key("esc")
+
+
# cancel client cert configuration
+
client1.wait_for_window(r"Certificate Management")
+
client2.wait_for_window(r"Certificate Management")
+
server.sleep(5) # wait because mumble is slow to register event handlers
+
client1.send_key("esc")
+
client2.send_key("esc")
+
+
# accept server certificate
+
client1.wait_for_window(r"^Mumble$")
+
client2.wait_for_window(r"^Mumble$")
+
server.sleep(5) # wait because mumble is slow to register event handlers
+
client1.send_chars("y")
+
client2.send_chars("y")
+
server.sleep(5) # wait because mumble is slow to register event handlers
+
+
# sometimes the wrong of the 2 windows is focused, we switch focus and try pressing "y" again
+
client1.send_key("alt-tab")
+
client2.send_key("alt-tab")
+
server.sleep(5) # wait because mumble is slow to register event handlers
+
client1.send_chars("y")
+
client2.send_chars("y")
+
+
# Find clients in logs
+
server.wait_until_succeeds(
+
"journalctl -eu umurmur -o cat | grep -q 'User client1 authenticated'"
+
)
+
server.wait_until_succeeds(
+
"journalctl -eu umurmur -o cat | grep -q 'User client2 authenticated'"
+
)
+
'';
+
}
+
)
+7
pkgs/by-name/um/umurmur/package.nix
···
openssl,
protobufc,
libconfig,
+
nixosTests,
}:
stdenv.mkDerivation rec {
···
"--with-ssl=openssl"
"--enable-shmapi"
];
+
+
passthru = {
+
tests = {
+
inherit (nixosTests) umurmur;
+
};
+
};
meta = with lib; {
description = "Minimalistic Murmur (Mumble server)";