parent
34142f1d9a
commit
1002ce25a0
@ -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 = { }; }; |
||||
}; |
||||
|
||||
} |
@ -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}") |
||||
''; |
||||
}) |
Loading…
Reference in new issue