nixos/livekit: init, nixos/lk-jwt-service: init (#399627)

Changed files
+295
nixos
modules
services
matrix
networking
tests
pkgs
by-name
li
livekit
lk
lk-jwt-service
+2
nixos/modules/module-list.nix
···
./services/matrix/dendrite.nix
./services/matrix/hebbot.nix
./services/matrix/hookshot.nix
+
./services/matrix/lk-jwt-service.nix
./services/matrix/matrix-alertmanager.nix
./services/matrix/maubot.nix
./services/matrix/mautrix-meta.nix
···
./services/networking/lambdabot.nix
./services/networking/legit.nix
./services/networking/libreswan.nix
+
./services/networking/livekit.nix
./services/networking/lldpd.nix
./services/networking/logmein-hamachi.nix
./services/networking/lokinet.nix
+93
nixos/modules/services/matrix/lk-jwt-service.nix
···
+
{
+
config,
+
lib,
+
pkgs,
+
...
+
}:
+
let
+
cfg = config.services.lk-jwt-service;
+
in
+
{
+
meta.maintainers = [ lib.maintainers.quadradical ];
+
options.services.lk-jwt-service = {
+
enable = lib.mkEnableOption "Enable lk-jwt-service";
+
package = lib.mkPackageOption pkgs "lk-jwt-service" { };
+
+
livekitUrl = lib.mkOption {
+
type = lib.types.strMatching "^wss?://.*";
+
example = "wss://example.com/livekit/sfu";
+
description = ''
+
The public websocket URL for livekit.
+
The proto needs to be either `wss://` (recommended) or `ws://` (insecure).
+
'';
+
};
+
+
keyFile = lib.mkOption {
+
type = lib.types.path;
+
description = ''
+
Path to a file containing the credential mapping (`<keyname>: <secret>`) to access LiveKit.
+
+
Example:
+
```
+
lk-jwt-service: f6lQGaHtM5HfgZjIcec3cOCRfiDqIine4CpZZnqdT5cE
+
```
+
+
For more information, see <https://github.com/element-hq/lk-jwt-service#configuration>.
+
'';
+
};
+
+
port = lib.mkOption {
+
type = lib.types.port;
+
default = 8080;
+
description = "Port that lk-jwt-service should listen on.";
+
};
+
};
+
+
config = lib.mkIf cfg.enable {
+
systemd.services.lk-jwt-service = {
+
description = "Minimal service to issue LiveKit JWTs for MatrixRTC";
+
documentation = [ "https://github.com/element-hq/lk-jwt-service" ];
+
wantedBy = [ "multi-user.target" ];
+
wants = [ "network-online.target" ];
+
after = [ "network-online.target" ];
+
environment = {
+
LIVEKIT_URL = cfg.livekitUrl;
+
LIVEKIT_JWT_PORT = toString cfg.port;
+
LIVEKIT_KEY_FILE = "/run/credentials/lk-jwt-service.service/livekit-secrets";
+
};
+
+
serviceConfig = {
+
LoadCredential = [ "livekit-secrets:${cfg.keyFile}" ];
+
ExecStart = lib.getExe cfg.package;
+
DynamicUser = true;
+
LockPersonality = true;
+
MemoryDenyWriteExecute = true;
+
ProtectClock = true;
+
ProtectControlGroups = true;
+
ProtectHostname = true;
+
ProtectKernelLogs = true;
+
ProtectKernelModules = true;
+
ProtectKernelTunables = true;
+
PrivateDevices = true;
+
PrivateMounts = true;
+
PrivateUsers = true;
+
RestrictAddressFamilies = [
+
"AF_INET"
+
"AF_INET6"
+
];
+
RestrictNamespaces = true;
+
RestrictRealtime = true;
+
ProtectHome = true;
+
SystemCallArchitectures = "native";
+
SystemCallFilter = [
+
"@system-service"
+
"~@privileged"
+
"~@resources"
+
];
+
Restart = "on-failure";
+
RestartSec = 5;
+
UMask = "077";
+
};
+
};
+
};
+
}
+141
nixos/modules/services/networking/livekit.nix
···
+
{
+
config,
+
lib,
+
pkgs,
+
utils,
+
...
+
}:
+
let
+
cfg = config.services.livekit;
+
format = pkgs.formats.json { };
+
in
+
{
+
meta.maintainers = with lib.maintainers; [ quadradical ];
+
options.services.livekit = {
+
enable = lib.mkEnableOption "Enable the livekit server";
+
package = lib.mkPackageOption pkgs "livekit" { };
+
+
keyFile = lib.mkOption {
+
type = lib.types.path;
+
description = ''
+
LiveKit key file holding one or multiple application secrets. Use `livekit-server generate-keys` to generate a random key name and secret.
+
+
The file should have the format `<keyname>: <secret>`. Example:
+
```
+
lk-jwt-service: f6lQGaHtM5HfgZjIcec3cOCRfiDqIine4CpZZnqdT5cE
+
```
+
+
Individual key/secret pairs need to be passed to clients to connect to this instance.
+
'';
+
};
+
+
openFirewall = lib.mkOption {
+
type = lib.types.bool;
+
default = false;
+
description = "Opens port range for LiveKit on the firewall.";
+
};
+
+
settings = lib.mkOption {
+
type = lib.types.submodule {
+
freeformType = format.type;
+
options = {
+
port = lib.mkOption {
+
type = lib.types.port;
+
default = 7880;
+
description = "Main TCP port for RoomService and RTC endpoint.";
+
};
+
+
rtc = {
+
port_range_start = lib.mkOption {
+
type = lib.types.int;
+
default = 50000;
+
description = "Start of UDP port range for WebRTC";
+
};
+
+
port_range_end = lib.mkOption {
+
type = lib.types.int;
+
default = 51000;
+
description = "End of UDP port range for WebRTC";
+
};
+
+
use_external_ip = lib.mkOption {
+
type = lib.types.bool;
+
default = false;
+
description = ''
+
When set to true, attempts to discover the host's public IP via STUN.
+
This is useful for cloud environments such as AWS & Google where hosts have an internal IP that maps to an external one.
+
'';
+
};
+
};
+
};
+
};
+
default = { };
+
description = ''
+
LiveKit configuration file expressed in nix.
+
+
For an example configuration, see <https://docs.livekit.io/home/self-hosting/deployment/#configuration>.
+
For all possible values, see <https://github.com/livekit/livekit/blob/master/config-sample.yaml>.
+
'';
+
};
+
};
+
+
config = lib.mkIf cfg.enable {
+
networking.firewall = lib.mkIf cfg.openFirewall {
+
allowedTCPPorts = [
+
cfg.settings.port
+
];
+
allowedUDPPortRanges = [
+
{
+
from = cfg.settings.rtc.port_range_start;
+
to = cfg.settings.rtc.port_range_end;
+
}
+
];
+
};
+
+
systemd.services.livekit = {
+
description = "LiveKit SFU server";
+
documentation = [ "https://docs.livekit.io" ];
+
wantedBy = [ "multi-user.target" ];
+
wants = [ "network-online.target" ];
+
after = [ "network-online.target" ];
+
+
serviceConfig = {
+
LoadCredential = [ "livekit-secrets:${cfg.keyFile}" ];
+
ExecStart = utils.escapeSystemdExecArgs [
+
(lib.getExe cfg.package)
+
"--config=${format.generate "livekit.json" cfg.settings}"
+
"--key-file=/run/credentials/livekit.service/livekit-secrets"
+
];
+
DynamicUser = true;
+
LockPersonality = true;
+
MemoryDenyWriteExecute = true;
+
ProtectClock = true;
+
ProtectControlGroups = true;
+
ProtectHostname = true;
+
ProtectKernelLogs = true;
+
ProtectKernelModules = true;
+
ProtectKernelTunables = true;
+
PrivateDevices = true;
+
PrivateMounts = true;
+
PrivateUsers = true;
+
RestrictAddressFamilies = [
+
"AF_INET"
+
"AF_INET6"
+
"AF_NETLINK"
+
];
+
RestrictNamespaces = true;
+
RestrictRealtime = true;
+
ProtectHome = true;
+
SystemCallArchitectures = "native";
+
SystemCallFilter = [
+
"@system-service"
+
"~@privileged"
+
"~@resources"
+
];
+
Restart = "on-failure";
+
RestartSec = 5;
+
UMask = "077";
+
};
+
};
+
};
+
}
+2
nixos/tests/all-tests.nix
···
lidarr = handleTest ./lidarr.nix { };
lightdm = handleTest ./lightdm.nix { };
lighttpd = runTest ./lighttpd.nix;
+
livekit = runTest ./networking/livekit.nix;
limesurvey = handleTest ./limesurvey.nix { };
limine = import ./limine { inherit runTest; };
listmonk = handleTestOn [ "x86_64-linux" "aarch64-linux" ] ./listmonk.nix { };
litellm = runTest ./litellm.nix;
litestream = handleTest ./litestream.nix { };
+
lk-jwt-service = runTest ./matrix/lk-jwt-service.nix;
lldap = handleTest ./lldap.nix { };
localsend = handleTest ./localsend.nix { };
locate = handleTest ./locate.nix { };
+26
nixos/tests/matrix/lk-jwt-service.nix
···
+
{
+
pkgs,
+
lib,
+
...
+
}:
+
{
+
name = "lk-jwt-service";
+
meta.maintainers = [ lib.maintainers.quadradical ];
+
+
nodes.machine = {
+
services.lk-jwt-service = {
+
enable = true;
+
keyFile = pkgs.writers.writeYAML "keys.yaml" {
+
key = "f6lQGaHtM5HfgZjIcec3cOCRfiDqIine4CpZZnqdT5cE";
+
};
+
livekitUrl = "wss://127.0.0.1:8100";
+
port = 8000;
+
};
+
};
+
+
testScript = ''
+
machine.wait_for_unit("lk-jwt-service.service")
+
machine.wait_for_open_port(8000)
+
machine.succeed('curl 127.0.0.1:8000/sfu/get -sLX POST -w "%{http_code}" | grep -q "^400"')
+
'';
+
}
+25
nixos/tests/networking/livekit.nix
···
+
{
+
pkgs,
+
lib,
+
...
+
}:
+
{
+
name = "livekit";
+
meta.maintainers = [ lib.maintainers.quadradical ];
+
+
nodes.machine = {
+
services.livekit = {
+
enable = true;
+
keyFile = pkgs.writers.writeYAML "keys.yaml" {
+
key = "f6lQGaHtM5HfgZjIcec3cOCRfiDqIine4CpZZnqdT5cE";
+
};
+
settings.port = 8000;
+
};
+
};
+
+
testScript = ''
+
machine.wait_for_unit("livekit.service")
+
machine.wait_for_open_port(8000)
+
machine.succeed("curl 127.0.0.1:8000 -L --fail")
+
'';
+
}
+3
pkgs/by-name/li/livekit/package.nix
···
lib,
buildGoModule,
fetchFromGitHub,
+
nixosTests,
}:
buildGoModule rec {
···
postInstall = ''
mv $out/bin/server $out/bin/livekit-server
'';
+
+
passthru.tests = nixosTests.livekit;
meta = with lib; {
description = "End-to-end stack for WebRTC. SFU media server and SDKs";
+3
pkgs/by-name/lk/lk-jwt-service/package.nix
···
lib,
buildGoModule,
fetchFromGitHub,
+
nixosTests,
}:
buildGoModule (finalAttrs: {
···
};
vendorHash = "sha256-47eJO1Ai78RuhlEPn/J1cd+YSqvmfUD8cuPZIqsdxvI=";
+
+
passthru.tests = nixosTests.lk-jwt-service;
meta = with lib; {
changelog = "https://github.com/element-hq/lk-jwt-service/releases/tag/${finalAttrs.src.tag}";