at 25.11-pre 5.0 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7 8with lib; 9 10let 11 12 cfg = config.services.nats; 13 14 format = pkgs.formats.json { }; 15 16 validateConfig = 17 file: 18 pkgs.callPackage ( 19 { runCommand, nats-server }: 20 runCommand "validate-nats-conf" 21 { 22 nativeBuildInputs = [ nats-server ]; 23 } 24 '' 25 nats-server --config "${file}" -t 26 ln -s "${file}" "$out" 27 '' 28 ) { }; 29 30 unvalidatedConfigFile = format.generate "nats.conf" cfg.settings; 31 32 configFile = 33 if cfg.validateConfig then validateConfig unvalidatedConfigFile else unvalidatedConfigFile; 34in 35{ 36 37 ### Interface 38 39 options = { 40 services.nats = { 41 enable = mkEnableOption "NATS messaging system"; 42 43 user = mkOption { 44 type = types.str; 45 default = "nats"; 46 description = "User account under which NATS runs."; 47 }; 48 49 group = mkOption { 50 type = types.str; 51 default = "nats"; 52 description = "Group under which NATS runs."; 53 }; 54 55 serverName = mkOption { 56 default = "nats"; 57 example = "n1-c3"; 58 type = types.str; 59 description = '' 60 Name of the NATS server, must be unique if clustered. 61 ''; 62 }; 63 64 jetstream = mkEnableOption "JetStream"; 65 66 port = mkOption { 67 default = 4222; 68 type = types.port; 69 description = '' 70 Port on which to listen. 71 ''; 72 }; 73 74 dataDir = mkOption { 75 default = "/var/lib/nats"; 76 type = types.path; 77 description = '' 78 The NATS data directory. Only used if JetStream is enabled, for 79 storing stream metadata and messages. 80 81 If left as the default value this directory will automatically be 82 created before the NATS server starts, otherwise the sysadmin is 83 responsible for ensuring the directory exists with appropriate 84 ownership and permissions. 85 ''; 86 }; 87 88 settings = mkOption { 89 default = { }; 90 type = format.type; 91 example = literalExpression '' 92 { 93 jetstream = { 94 max_mem = "1G"; 95 max_file = "10G"; 96 }; 97 }; 98 ''; 99 description = '' 100 Declarative NATS configuration. See the 101 [ 102 NATS documentation](https://docs.nats.io/nats-server/configuration) for a list of options. 103 ''; 104 }; 105 106 validateConfig = mkOption { 107 type = types.bool; 108 default = true; 109 description = '' 110 If true, validate nats config at build time. When the config can't 111 be checked during build time, for example when it includes other 112 files, disable this option. 113 ''; 114 }; 115 }; 116 }; 117 118 ### Implementation 119 120 config = mkIf cfg.enable { 121 services.nats.settings = { 122 server_name = cfg.serverName; 123 port = cfg.port; 124 jetstream = optionalAttrs cfg.jetstream { store_dir = cfg.dataDir; }; 125 }; 126 127 systemd.services.nats = { 128 description = "NATS messaging system"; 129 wantedBy = [ "multi-user.target" ]; 130 after = [ "network.target" ]; 131 132 serviceConfig = mkMerge [ 133 (mkIf (cfg.dataDir == "/var/lib/nats") { 134 StateDirectory = "nats"; 135 StateDirectoryMode = "0750"; 136 }) 137 { 138 Type = "simple"; 139 ExecStart = "${pkgs.nats-server}/bin/nats-server -c ${configFile}"; 140 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; 141 ExecStop = "${pkgs.coreutils}/bin/kill -SIGINT $MAINPID"; 142 Restart = "on-failure"; 143 144 User = cfg.user; 145 Group = cfg.group; 146 147 # Hardening 148 CapabilityBoundingSet = ""; 149 LimitNOFILE = 800000; # JetStream requires 2 FDs open per stream. 150 LockPersonality = true; 151 MemoryDenyWriteExecute = true; 152 NoNewPrivileges = true; 153 PrivateDevices = true; 154 PrivateTmp = true; 155 PrivateUsers = true; 156 ProcSubset = "pid"; 157 ProtectClock = true; 158 ProtectControlGroups = true; 159 ProtectHome = true; 160 ProtectHostname = true; 161 ProtectKernelLogs = true; 162 ProtectKernelModules = true; 163 ProtectKernelTunables = true; 164 ProtectProc = "invisible"; 165 ProtectSystem = "strict"; 166 ReadOnlyPaths = [ ]; 167 ReadWritePaths = [ cfg.dataDir ]; 168 RestrictAddressFamilies = [ 169 "AF_INET" 170 "AF_INET6" 171 ]; 172 RestrictNamespaces = true; 173 RestrictRealtime = true; 174 RestrictSUIDSGID = true; 175 SystemCallFilter = [ 176 "@system-service" 177 "~@privileged" 178 ]; 179 UMask = "0077"; 180 } 181 ]; 182 }; 183 184 users.users = mkIf (cfg.user == "nats") { 185 nats = { 186 description = "NATS daemon user"; 187 isSystemUser = true; 188 group = cfg.group; 189 home = cfg.dataDir; 190 }; 191 }; 192 193 users.groups = mkIf (cfg.group == "nats") { nats = { }; }; 194 }; 195 196}