diff options
Diffstat (limited to 'nixos')
-rw-r--r-- | nixos/modules/module-list.nix | 1 | ||||
-rw-r--r-- | nixos/modules/services/networking/centrifugo.nix | 123 | ||||
-rw-r--r-- | nixos/tests/all-tests.nix | 1 | ||||
-rw-r--r-- | nixos/tests/centrifugo.nix | 80 |
4 files changed, 205 insertions, 0 deletions
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 4e2275cc661de..0dbcf4b3b2e22 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -865,6 +865,7 @@ ./services/networking/bitlbee.nix ./services/networking/blockbook-frontend.nix ./services/networking/blocky.nix + ./services/networking/centrifugo.nix ./services/networking/cgit.nix ./services/networking/charybdis.nix ./services/networking/chisel-server.nix diff --git a/nixos/modules/services/networking/centrifugo.nix b/nixos/modules/services/networking/centrifugo.nix new file mode 100644 index 0000000000000..143fe6a24dc38 --- /dev/null +++ b/nixos/modules/services/networking/centrifugo.nix @@ -0,0 +1,123 @@ +{ config, lib, pkgs, ... }: +let + cfg = config.services.centrifugo; + + settingsFormat = pkgs.formats.json { }; + + configFile = settingsFormat.generate "centrifugo.json" cfg.settings; +in +{ + options.services.centrifugo = { + enable = lib.mkEnableOption (lib.mdDoc "Centrifugo messaging server"); + + package = lib.mkPackageOptionMD pkgs "centrifugo" { }; + + settings = lib.mkOption { + type = settingsFormat.type; + default = { }; + description = lib.mdDoc '' + Declarative Centrifugo configuration. See the [Centrifugo + documentation] for a list of options. + + [Centrifugo documentation]: https://centrifugal.dev/docs/server/configuration + ''; + }; + + credentials = lib.mkOption { + type = lib.types.attrsOf lib.types.path; + default = { }; + example = { + CENTRIFUGO_UNI_GRPC_TLS_KEY = "/run/keys/centrifugo-uni-grpc-tls.key"; + }; + description = lib.mdDoc '' + Environment variables with absolute paths to credentials files to load + on service startup. + ''; + }; + + environmentFiles = lib.mkOption { + type = lib.types.listOf lib.types.path; + default = [ ]; + description = lib.mdDoc '' + Files to load environment variables from. Options set via environment + variables take precedence over {option}`settings`. + + See the [Centrifugo documentation] for the environment variable name + format. + + [Centrifugo documentation]: https://centrifugal.dev/docs/server/configuration#os-environment-variables + ''; + }; + + extraGroups = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + example = [ "redis-centrifugo" ]; + description = lib.mdDoc '' + Additional groups for the systemd service. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.services.centrifugo = { + description = "Centrifugo messaging server"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + serviceConfig = { + Type = "exec"; + + ExecStartPre = "${lib.getExe cfg.package} checkconfig --config ${configFile}"; + ExecStart = "${lib.getExe cfg.package} --config ${configFile}"; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + + Restart = "always"; + RestartSec = "1s"; + + # Copy files to the credentials directory with file name being the + # environment variable name. Note that "%d" specifier expands to the + # path of the credentials directory. + LoadCredential = lib.mapAttrsToList (name: value: "${name}:${value}") cfg.credentials; + Environment = lib.mapAttrsToList (name: _: "${name}=%d/${name}") cfg.credentials; + + EnvironmentFile = cfg.environmentFiles; + + SupplementaryGroups = cfg.extraGroups; + + DynamicUser = true; + UMask = "0077"; + + ProtectHome = true; + ProtectProc = "invisible"; + ProcSubset = "pid"; + ProtectClock = true; + ProtectHostname = true; + ProtectControlGroups = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + PrivateUsers = true; + PrivateDevices = true; + RestrictRealtime = true; + RestrictNamespaces = true; + RestrictAddressFamilies = [ + "AF_INET" + "AF_INET6" + "AF_UNIX" + ]; + DeviceAllow = [ "" ]; + DevicePolicy = "closed"; + CapabilityBoundingSet = [ "" ]; + MemoryDenyWriteExecute = true; + LockPersonality = true; + SystemCallArchitectures = "native"; + SystemCallErrorNumber = "EPERM"; + SystemCallFilter = [ + "@system-service" + "~@privileged" + ]; + }; + }; + }; +} diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index c221835f9a6ab..e6ccdc25a0f55 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -163,6 +163,7 @@ in { cassandra_3_0 = handleTest ./cassandra.nix { testPackage = pkgs.cassandra_3_0; }; cassandra_3_11 = handleTest ./cassandra.nix { testPackage = pkgs.cassandra_3_11; }; cassandra_4 = handleTest ./cassandra.nix { testPackage = pkgs.cassandra_4; }; + centrifugo = runTest ./centrifugo.nix; ceph-multi-node = handleTestOn [ "aarch64-linux" "x86_64-linux" ] ./ceph-multi-node.nix {}; ceph-single-node = handleTestOn [ "aarch64-linux" "x86_64-linux" ] ./ceph-single-node.nix {}; ceph-single-node-bluestore = handleTestOn [ "aarch64-linux" "x86_64-linux" ] ./ceph-single-node-bluestore.nix {}; diff --git a/nixos/tests/centrifugo.nix b/nixos/tests/centrifugo.nix new file mode 100644 index 0000000000000..45c2904f5585f --- /dev/null +++ b/nixos/tests/centrifugo.nix @@ -0,0 +1,80 @@ +let + redisPort = 6379; + centrifugoPort = 8080; + nodes = [ + "centrifugo1" + "centrifugo2" + "centrifugo3" + ]; +in +{ lib, ... }: { + name = "centrifugo"; + meta.maintainers = [ lib.maintainers.tie ]; + + nodes = lib.listToAttrs (lib.imap0 + (index: name: { + inherit name; + value = { config, ... }: { + services.centrifugo = { + enable = true; + settings = { + inherit name; + port = centrifugoPort; + # See https://centrifugal.dev/docs/server/engines#redis-sharding + engine = "redis"; + # Connect to local Redis shard via Unix socket. + redis_address = + let + otherNodes = lib.take index nodes ++ lib.drop (index + 1) nodes; + in + map (name: "${name}:${toString redisPort}") otherNodes ++ [ + "unix://${config.services.redis.servers.centrifugo.unixSocket}" + ]; + usage_stats_disable = true; + api_insecure = true; + }; + extraGroups = [ + config.services.redis.servers.centrifugo.user + ]; + }; + services.redis.servers.centrifugo = { + enable = true; + bind = null; # all interfaces + port = redisPort; + openFirewall = true; + settings.protected-mode = false; + }; + }; + }) + nodes); + + testScript = '' + import json + + redisPort = ${toString redisPort} + centrifugoPort = ${toString centrifugoPort} + + start_all() + + for machine in machines: + machine.wait_for_unit("redis-centrifugo.service") + machine.wait_for_open_port(redisPort) + + for machine in machines: + machine.wait_for_unit("centrifugo.service") + machine.wait_for_open_port(centrifugoPort) + + # See https://centrifugal.dev/docs/server/server_api#info + def list_nodes(machine): + curl = "curl --fail-with-body --silent" + body = "{}" + resp = json.loads(machine.succeed(f"{curl} -d '{body}' http://localhost:{centrifugoPort}/api/info")) + return resp["result"]["nodes"] + machineNames = {m.name for m in machines} + for machine in machines: + nodes = list_nodes(machine) + assert len(nodes) == len(machines) + nodeNames = {n['name'] for n in nodes} + assert machineNames == nodeNames + ''; +} |