1{
2 lib,
3 pkgs,
4 config,
5 options,
6 ...
7}:
8
9let
10 cfg = config.services.nifi;
11 opt = options.services.nifi;
12
13 env = {
14 NIFI_OVERRIDE_NIFIENV = "true";
15 NIFI_HOME = "/var/lib/nifi";
16 NIFI_PID_DIR = "/run/nifi";
17 NIFI_LOG_DIR = "/var/log/nifi";
18 };
19
20 envFile = pkgs.writeText "nifi.env" (
21 lib.concatMapStrings (s: s + "\n") (
22 (lib.concatLists (
23 lib.mapAttrsToList (name: value: lib.optional (value != null) ''${name}="${toString value}"'') env
24 ))
25 )
26 );
27
28 nifiEnv = pkgs.writeShellScriptBin "nifi-env" ''
29 set -a
30 source "${envFile}"
31 eval -- "\$@"
32 '';
33
34in
35{
36 options = {
37 services.nifi = {
38 enable = lib.mkEnableOption "Apache NiFi";
39
40 package = lib.mkPackageOption pkgs "nifi" { };
41
42 user = lib.mkOption {
43 type = lib.types.str;
44 default = "nifi";
45 description = "User account where Apache NiFi runs.";
46 };
47
48 group = lib.mkOption {
49 type = lib.types.str;
50 default = "nifi";
51 description = "Group account where Apache NiFi runs.";
52 };
53
54 enableHTTPS = lib.mkOption {
55 type = lib.types.bool;
56 default = true;
57 description = "Enable HTTPS protocol. Don`t use in production.";
58 };
59
60 listenHost = lib.mkOption {
61 type = lib.types.str;
62 default = if cfg.enableHTTPS then "0.0.0.0" else "127.0.0.1";
63 defaultText = lib.literalExpression ''
64 if config.${opt.enableHTTPS}
65 then "0.0.0.0"
66 else "127.0.0.1"
67 '';
68 description = "Bind to an ip for Apache NiFi web-ui.";
69 };
70
71 listenPort = lib.mkOption {
72 type = lib.types.port;
73 default = if cfg.enableHTTPS then 8443 else 8080;
74 defaultText = lib.literalExpression ''
75 if config.${opt.enableHTTPS}
76 then "8443"
77 else "8000"
78 '';
79 description = "Bind to a port for Apache NiFi web-ui.";
80 };
81
82 proxyHost = lib.mkOption {
83 type = lib.types.nullOr lib.types.str;
84 default = if cfg.enableHTTPS then "0.0.0.0" else null;
85 defaultText = lib.literalExpression ''
86 if config.${opt.enableHTTPS}
87 then "0.0.0.0"
88 else null
89 '';
90 description = "Allow requests from a specific host.";
91 };
92
93 proxyPort = lib.mkOption {
94 type = lib.types.nullOr lib.types.port;
95 default = if cfg.enableHTTPS then 8443 else null;
96 defaultText = lib.literalExpression ''
97 if config.${opt.enableHTTPS}
98 then "8443"
99 else null
100 '';
101 description = "Allow requests from a specific port.";
102 };
103
104 initUser = lib.mkOption {
105 type = lib.types.nullOr lib.types.str;
106 default = null;
107 description = "Initial user account for Apache NiFi. Username must be at least 4 characters.";
108 };
109
110 initPasswordFile = lib.mkOption {
111 type = lib.types.nullOr lib.types.path;
112 default = null;
113 example = "/run/keys/nifi/password-nifi";
114 description = "nitial password for Apache NiFi. Password must be at least 12 characters.";
115 };
116
117 initJavaHeapSize = lib.mkOption {
118 type = lib.types.nullOr lib.types.int;
119 default = null;
120 example = 1024;
121 description = "Set the initial heap size for the JVM in MB.";
122 };
123
124 maxJavaHeapSize = lib.mkOption {
125 type = lib.types.nullOr lib.types.int;
126 default = null;
127 example = 2048;
128 description = "Set the initial heap size for the JVM in MB.";
129 };
130 };
131 };
132
133 config = lib.mkIf cfg.enable {
134 assertions = [
135 {
136 assertion = cfg.initUser != null || cfg.initPasswordFile == null;
137 message = ''
138 <option>services.nifi.initUser</option> needs to be set if <option>services.nifi.initPasswordFile</option> enabled.
139 '';
140 }
141 {
142 assertion = cfg.initUser == null || cfg.initPasswordFile != null;
143 message = ''
144 <option>services.nifi.initPasswordFile</option> needs to be set if <option>services.nifi.initUser</option> enabled.
145 '';
146 }
147 {
148 assertion = cfg.proxyHost == null || cfg.proxyPort != null;
149 message = ''
150 <option>services.nifi.proxyPort</option> needs to be set if <option>services.nifi.proxyHost</option> value specified.
151 '';
152 }
153 {
154 assertion = cfg.proxyHost != null || cfg.proxyPort == null;
155 message = ''
156 <option>services.nifi.proxyHost</option> needs to be set if <option>services.nifi.proxyPort</option> value specified.
157 '';
158 }
159 {
160 assertion = cfg.initJavaHeapSize == null || cfg.maxJavaHeapSize != null;
161 message = ''
162 <option>services.nifi.maxJavaHeapSize</option> needs to be set if <option>services.nifi.initJavaHeapSize</option> value specified.
163 '';
164 }
165 {
166 assertion = cfg.initJavaHeapSize != null || cfg.maxJavaHeapSize == null;
167 message = ''
168 <option>services.nifi.initJavaHeapSize</option> needs to be set if <option>services.nifi.maxJavaHeapSize</option> value specified.
169 '';
170 }
171 ];
172
173 warnings = lib.optional (cfg.enableHTTPS == false) ''
174 Please do not disable HTTPS mode in production. In this mode, access to the nifi is opened without authentication.
175 '';
176
177 systemd.tmpfiles.settings."10-nifi" = {
178 "/var/lib/nifi/conf".d = {
179 inherit (cfg) user group;
180 mode = "0750";
181 };
182 "/var/lib/nifi/lib"."L+" = {
183 argument = "${cfg.package}/lib";
184 };
185 };
186
187 systemd.services.nifi = {
188 description = "Apache NiFi";
189 after = [ "network.target" ];
190 wantedBy = [ "multi-user.target" ];
191
192 environment = env;
193 path = [ pkgs.gawk ];
194
195 serviceConfig = {
196 Type = "forking";
197 PIDFile = "/run/nifi/nifi.pid";
198 ExecStartPre = pkgs.writeScript "nifi-pre-start.sh" ''
199 #!/bin/sh
200 umask 077
201 test -f '/var/lib/nifi/conf/authorizers.xml' || (cp '${cfg.package}/share/nifi/conf/authorizers.xml' '/var/lib/nifi/conf/' && chmod 0640 '/var/lib/nifi/conf/authorizers.xml')
202 test -f '/var/lib/nifi/conf/bootstrap.conf' || (cp '${cfg.package}/share/nifi/conf/bootstrap.conf' '/var/lib/nifi/conf/' && chmod 0640 '/var/lib/nifi/conf/bootstrap.conf')
203 test -f '/var/lib/nifi/conf/bootstrap-hashicorp-vault.conf' || (cp '${cfg.package}/share/nifi/conf/bootstrap-hashicorp-vault.conf' '/var/lib/nifi/conf/' && chmod 0640 '/var/lib/nifi/conf/bootstrap-hashicorp-vault.conf')
204 test -f '/var/lib/nifi/conf/bootstrap-notification-services.xml' || (cp '${cfg.package}/share/nifi/conf/bootstrap-notification-services.xml' '/var/lib/nifi/conf/' && chmod 0640 '/var/lib/nifi/conf/bootstrap-notification-services.xml')
205 test -f '/var/lib/nifi/conf/logback.xml' || (cp '${cfg.package}/share/nifi/conf/logback.xml' '/var/lib/nifi/conf/' && chmod 0640 '/var/lib/nifi/conf/logback.xml')
206 test -f '/var/lib/nifi/conf/login-identity-providers.xml' || (cp '${cfg.package}/share/nifi/conf/login-identity-providers.xml' '/var/lib/nifi/conf/' && chmod 0640 '/var/lib/nifi/conf/login-identity-providers.xml')
207 test -f '/var/lib/nifi/conf/nifi.properties' || (cp '${cfg.package}/share/nifi/conf/nifi.properties' '/var/lib/nifi/conf/' && chmod 0640 '/var/lib/nifi/conf/nifi.properties')
208 test -f '/var/lib/nifi/conf/stateless-logback.xml' || (cp '${cfg.package}/share/nifi/conf/stateless-logback.xml' '/var/lib/nifi/conf/' && chmod 0640 '/var/lib/nifi/conf/stateless-logback.xml')
209 test -f '/var/lib/nifi/conf/stateless.properties' || (cp '${cfg.package}/share/nifi/conf/stateless.properties' '/var/lib/nifi/conf/' && chmod 0640 '/var/lib/nifi/conf/stateless.properties')
210 test -f '/var/lib/nifi/conf/state-management.xml' || (cp '${cfg.package}/share/nifi/conf/state-management.xml' '/var/lib/nifi/conf/' && chmod 0640 '/var/lib/nifi/conf/state-management.xml')
211 test -f '/var/lib/nifi/conf/zookeeper.properties' || (cp '${cfg.package}/share/nifi/conf/zookeeper.properties' '/var/lib/nifi/conf/' && chmod 0640 '/var/lib/nifi/conf/zookeeper.properties')
212 test -d '/var/lib/nifi/docs/html' || (mkdir -p /var/lib/nifi/docs && cp -r '${cfg.package}/share/nifi/docs/html' '/var/lib/nifi/docs/html')
213 ${lib.optionalString ((cfg.initUser != null) && (cfg.initPasswordFile != null)) ''
214 awk -F'[<|>]' '/property name="Username"/ {if ($3!="") f=1} END{exit !f}' /var/lib/nifi/conf/login-identity-providers.xml || ${cfg.package}/bin/nifi.sh set-single-user-credentials ${cfg.initUser} $(cat ${cfg.initPasswordFile})
215 ''}
216 ${lib.optionalString (cfg.enableHTTPS == false) ''
217 sed -i /var/lib/nifi/conf/nifi.properties \
218 -e 's|nifi.remote.input.secure=.*|nifi.remote.input.secure=false|g' \
219 -e 's|nifi.web.http.host=.*|nifi.web.http.host=${cfg.listenHost}|g' \
220 -e 's|nifi.web.http.port=.*|nifi.web.http.port=${(toString cfg.listenPort)}|g' \
221 -e 's|nifi.web.https.host=.*|nifi.web.https.host=|g' \
222 -e 's|nifi.web.https.port=.*|nifi.web.https.port=|g' \
223 -e 's|nifi.security.keystore=.*|nifi.security.keystore=|g' \
224 -e 's|nifi.security.keystoreType=.*|nifi.security.keystoreType=|g' \
225 -e 's|nifi.security.truststore=.*|nifi.security.truststore=|g' \
226 -e 's|nifi.security.truststoreType=.*|nifi.security.truststoreType=|g' \
227 -e '/nifi.security.keystorePasswd/s|^|#|' \
228 -e '/nifi.security.keyPasswd/s|^|#|' \
229 -e '/nifi.security.truststorePasswd/s|^|#|'
230 ''}
231 ${lib.optionalString (cfg.enableHTTPS == true) ''
232 sed -i /var/lib/nifi/conf/nifi.properties \
233 -e 's|nifi.remote.input.secure=.*|nifi.remote.input.secure=true|g' \
234 -e 's|nifi.web.http.host=.*|nifi.web.http.host=|g' \
235 -e 's|nifi.web.http.port=.*|nifi.web.http.port=|g' \
236 -e 's|nifi.web.https.host=.*|nifi.web.https.host=${cfg.listenHost}|g' \
237 -e 's|nifi.web.https.port=.*|nifi.web.https.port=${(toString cfg.listenPort)}|g' \
238 -e 's|nifi.security.keystore=.*|nifi.security.keystore=./conf/keystore.p12|g' \
239 -e 's|nifi.security.keystoreType=.*|nifi.security.keystoreType=PKCS12|g' \
240 -e 's|nifi.security.truststore=.*|nifi.security.truststore=./conf/truststore.p12|g' \
241 -e 's|nifi.security.truststoreType=.*|nifi.security.truststoreType=PKCS12|g' \
242 -e '/nifi.security.keystorePasswd/s|^#\+||' \
243 -e '/nifi.security.keyPasswd/s|^#\+||' \
244 -e '/nifi.security.truststorePasswd/s|^#\+||'
245 ''}
246 ${lib.optionalString
247 ((cfg.enableHTTPS == true) && (cfg.proxyHost != null) && (cfg.proxyPort != null))
248 ''
249 sed -i /var/lib/nifi/conf/nifi.properties \
250 -e 's|nifi.web.proxy.host=.*|nifi.web.proxy.host=${cfg.proxyHost}:${(toString cfg.proxyPort)}|g'
251 ''
252 }
253 ${lib.optionalString
254 ((cfg.enableHTTPS == false) || (cfg.proxyHost == null) && (cfg.proxyPort == null))
255 ''
256 sed -i /var/lib/nifi/conf/nifi.properties \
257 -e 's|nifi.web.proxy.host=.*|nifi.web.proxy.host=|g'
258 ''
259 }
260 ${lib.optionalString ((cfg.initJavaHeapSize != null) && (cfg.maxJavaHeapSize != null)) ''
261 sed -i /var/lib/nifi/conf/bootstrap.conf \
262 -e 's|java.arg.2=.*|java.arg.2=-Xms${(toString cfg.initJavaHeapSize)}m|g' \
263 -e 's|java.arg.3=.*|java.arg.3=-Xmx${(toString cfg.maxJavaHeapSize)}m|g'
264 ''}
265 ${lib.optionalString ((cfg.initJavaHeapSize == null) && (cfg.maxJavaHeapSize == null)) ''
266 sed -i /var/lib/nifi/conf/bootstrap.conf \
267 -e 's|java.arg.2=.*|java.arg.2=-Xms512m|g' \
268 -e 's|java.arg.3=.*|java.arg.3=-Xmx512m|g'
269 ''}
270 '';
271 ExecStart = "${cfg.package}/bin/nifi.sh start";
272 ExecStop = "${cfg.package}/bin/nifi.sh stop";
273 # User and group
274 User = cfg.user;
275 Group = cfg.group;
276 # Runtime directory and mode
277 RuntimeDirectory = "nifi";
278 RuntimeDirectoryMode = "0750";
279 # State directory and mode
280 StateDirectory = "nifi";
281 StateDirectoryMode = "0750";
282 # Logs directory and mode
283 LogsDirectory = "nifi";
284 LogsDirectoryMode = "0750";
285 # Proc filesystem
286 ProcSubset = "pid";
287 ProtectProc = "invisible";
288 # Access write directories
289 ReadWritePaths = [ cfg.initPasswordFile ];
290 UMask = "0027";
291 # Capabilities
292 CapabilityBoundingSet = "";
293 # Security
294 NoNewPrivileges = true;
295 # Sandboxing
296 ProtectSystem = "strict";
297 ProtectHome = true;
298 PrivateTmp = true;
299 PrivateDevices = true;
300 PrivateIPC = true;
301 PrivateUsers = true;
302 ProtectHostname = true;
303 ProtectClock = true;
304 ProtectKernelTunables = true;
305 ProtectKernelModules = true;
306 ProtectKernelLogs = true;
307 ProtectControlGroups = true;
308 RestrictAddressFamilies = [ "AF_INET AF_INET6" ];
309 RestrictNamespaces = true;
310 LockPersonality = true;
311 MemoryDenyWriteExecute = false;
312 RestrictRealtime = true;
313 RestrictSUIDSGID = true;
314 RemoveIPC = true;
315 PrivateMounts = true;
316 # System Call Filtering
317 SystemCallArchitectures = "native";
318 SystemCallFilter = [
319 "~@cpu-emulation @debug @keyring @memlock @mount @obsolete @resources @privileged @setuid"
320 "@chown"
321 ];
322 };
323 };
324
325 users.users = lib.mkMerge [
326 (lib.mkIf (cfg.user == "nifi") {
327 nifi = {
328 group = cfg.group;
329 isSystemUser = true;
330 home = cfg.package;
331 };
332 })
333 (lib.attrsets.setAttrByPath [ cfg.user "packages" ] [ cfg.package nifiEnv ])
334 ];
335
336 users.groups = lib.optionalAttrs (cfg.group == "nifi") {
337 nifi = { };
338 };
339 };
340}