summaryrefslogtreecommitdiffstats
path: root/nixos
diff options
context:
space:
mode:
authorJos van Bakel <jos@codeaddict.org>2021-06-26 13:58:40 +0200
committerJos van Bakel <jos@codeaddict.org>2021-08-18 20:18:08 +0200
commit1002ce25a07e2ff5448a19b4f06be9372aaf4659 (patch)
treec683b129572fbde2358c3c794edaea8d5a79f457 /nixos
parent34142f1d9a6de519a00a1a828a3ad7bceda55a89 (diff)
nixos/nats: init
Diffstat (limited to 'nixos')
-rw-r--r--nixos/modules/module-list.nix1
-rw-r--r--nixos/modules/services/networking/nats.nix159
-rw-r--r--nixos/tests/all-tests.nix1
-rw-r--r--nixos/tests/nats.nix65
4 files changed, 226 insertions, 0 deletions
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index f26977b12441..7b47dd77ec28 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -767,6 +767,7 @@
./services/networking/namecoind.nix
./services/networking/nar-serve.nix
./services/networking/nat.nix
+ ./services/networking/nats.nix
./services/networking/ndppd.nix
./services/networking/nebula.nix
./services/networking/networkmanager.nix
diff --git a/nixos/modules/services/networking/nats.nix b/nixos/modules/services/networking/nats.nix
new file mode 100644
index 000000000000..eb0c65bc6561
--- /dev/null
+++ b/nixos/modules/services/networking/nats.nix
@@ -0,0 +1,159 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.services.nats;
+
+ format = pkgs.formats.json { };
+
+ configFile = format.generate "nats.conf" cfg.settings;
+
+in {
+
+ ### Interface
+
+ options = {
+ services.nats = {
+ enable = mkEnableOption "NATS messaging system";
+
+ user = mkOption {
+ type = types.str;
+ default = "nats";
+ description = "User account under which NATS runs.";
+ };
+
+ group = mkOption {
+ type = types.str;
+ default = "nats";
+ description = "Group under which NATS runs.";
+ };
+
+ serverName = mkOption {
+ default = "nats";
+ example = "n1-c3";
+ type = types.str;
+ description = ''
+ Name of the NATS server, must be unique if clustered.
+ '';
+ };
+
+ jetstream = mkEnableOption "JetStream";
+
+ port = mkOption {
+ default = 4222;
+ example = 4222;
+ type = types.port;
+ description = ''
+ Port on which to listen.
+ '';
+ };
+
+ dataDir = mkOption {
+ default = "/var/lib/nats";
+ type = types.path;
+ description = ''
+ The NATS data directory. Only used if JetStream is enabled, for
+ storing stream metadata and messages.
+
+ If left as the default value this directory will automatically be
+ created before the NATS server starts, otherwise the sysadmin is
+ responsible for ensuring the directory exists with appropriate
+ ownership and permissions.
+ '';
+ };
+
+ settings = mkOption {
+ default = { };
+ type = format.type;
+ example = literalExample ''
+ {
+ jetstream = {
+ max_mem = "1G";
+ max_file = "10G";
+ };
+ };
+ '';
+ description = ''
+ Declarative NATS configuration. See the
+ <link xlink:href="https://docs.nats.io/nats-server/configuration">
+ NATS documentation</link> for a list of options.
+ '';
+ };
+ };
+ };
+
+ ### Implementation
+
+ config = mkIf cfg.enable {
+ services.nats.settings = {
+ server_name = cfg.serverName;
+ port = cfg.port;
+ jetstream = optionalAttrs cfg.jetstream { store_dir = cfg.dataDir; };
+ };
+
+ systemd.services.nats = {
+ description = "NATS messaging system";
+ wantedBy = [ "multi-user.target" ];
+ after = [ "network.target" ];
+
+ serviceConfig = mkMerge [
+ (mkIf (cfg.dataDir == "/var/lib/nats") {
+ StateDirectory = "nats";
+ StateDirectoryMode = "0750";
+ })
+ {
+ Type = "simple";
+ ExecStart = "${pkgs.nats-server}/bin/nats-server -c ${configFile}";
+ ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+ ExecStop = "${pkgs.coreutils}/bin/kill -SIGINT $MAINPID";
+ Restart = "on-failure";
+
+ User = cfg.user;
+ Group = cfg.group;
+
+ # Hardening
+ CapabilityBoundingSet = "";
+ LimitNOFILE = 800000; # JetStream requires 2 FDs open per stream.
+ LockPersonality = true;
+ MemoryDenyWriteExecute = true;
+ NoNewPrivileges = true;
+ PrivateDevices = true;
+ PrivateTmp = true;
+ PrivateUsers = true;
+ ProcSubset = "pid";
+ ProtectClock = true;
+ ProtectControlGroups = true;
+ ProtectHome = true;
+ ProtectHostname = true;
+ ProtectKernelLogs = true;
+ ProtectKernelModules = true;
+ ProtectKernelTunables = true;
+ ProtectProc = "invisible";
+ ProtectSystem = "strict";
+ ReadOnlyPaths = [ ];
+ ReadWritePaths = [ cfg.dataDir ];
+ RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
+ RestrictNamespaces = true;
+ RestrictRealtime = true;
+ RestrictSUIDSGID = true;
+ SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ];
+ UMask = "0077";
+ }
+ ];
+ };
+
+ users.users = mkIf (cfg.user == "nats") {
+ nats = {
+ description = "NATS daemon user";
+ isSystemUser = true;
+ group = cfg.group;
+ home = cfg.dataDir;
+ };
+ };
+
+ users.groups = mkIf (cfg.group == "nats") { nats = { }; };
+ };
+
+}
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index e1ad011b22df..d17904c776e7 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -283,6 +283,7 @@ in
nat.firewall = handleTest ./nat.nix { withFirewall = true; };
nat.firewall-conntrack = handleTest ./nat.nix { withFirewall = true; withConntrackHelpers = true; };
nat.standalone = handleTest ./nat.nix { withFirewall = false; };
+ nats = handleTest ./nats.nix {};
navidrome = handleTest ./navidrome.nix {};
ncdns = handleTest ./ncdns.nix {};
ndppd = handleTest ./ndppd.nix {};
diff --git a/nixos/tests/nats.nix b/nixos/tests/nats.nix
new file mode 100644
index 000000000000..bee36f262f4c
--- /dev/null
+++ b/nixos/tests/nats.nix
@@ -0,0 +1,65 @@
+let
+
+ port = 4222;
+ username = "client";
+ password = "password";
+ topic = "foo.bar";
+
+in import ./make-test-python.nix ({ pkgs, lib, ... }: {
+ name = "nats";
+ meta = with pkgs.lib; { maintainers = with maintainers; [ c0deaddict ]; };
+
+ nodes = let
+ client = { pkgs, ... }: {
+ environment.systemPackages = with pkgs; [ natscli ];
+ };
+ in {
+ server = { pkgs, ... }: {
+ networking.firewall.allowedTCPPorts = [ port ];
+ services.nats = {
+ inherit port;
+ enable = true;
+ settings = {
+ authorization = {
+ users = [{
+ user = username;
+ inherit password;
+ }];
+ };
+ };
+ };
+ };
+
+ client1 = client;
+ client2 = client;
+ };
+
+ testScript = let file = "/tmp/msg";
+ in ''
+ def nats_cmd(*args):
+ return (
+ "nats "
+ "--server=nats://server:${toString port} "
+ "--user=${username} "
+ "--password=${password} "
+ "{}"
+ ).format(" ".join(args))
+
+ start_all()
+ server.wait_for_unit("nats.service")
+
+ client1.fail("test -f ${file}")
+
+ # Subscribe on topic on client1 and echo messages to file.
+ client1.execute("({} | tee ${file} &)".format(nats_cmd("sub", "--raw", "${topic}")))
+
+ # Give client1 some time to subscribe.
+ client1.execute("sleep 2")
+
+ # Publish message on client2.
+ client2.execute(nats_cmd("pub", "${topic}", "hello"))
+
+ # Check if message has been received.
+ client1.succeed("grep -q hello ${file}")
+ '';
+})