grafana-alloy: don't build the frontend twice
[NixPkgs.git] / nixos / modules / services / databases / neo4j.nix
blobc4e19a47ac7f0ce328c0566473421668c96b2d66
1 { config, options, lib, pkgs, ... }:
2 let
3   cfg = config.services.neo4j;
4   opt = options.services.neo4j;
5   certDirOpt = options.services.neo4j.directories.certificates;
6   isDefaultPathOption = opt: lib.isOption opt && opt.type == lib.types.path && opt.highestPrio >= 1500;
8   sslPolicies = lib.mapAttrsToList (
9     name: conf: ''
10       dbms.ssl.policy.${name}.allow_key_generation=${lib.boolToString conf.allowKeyGeneration}
11       dbms.ssl.policy.${name}.base_directory=${conf.baseDirectory}
12       ${lib.optionalString (conf.ciphers != null) ''
13         dbms.ssl.policy.${name}.ciphers=${lib.concatStringsSep "," conf.ciphers}
14       ''}
15       dbms.ssl.policy.${name}.client_auth=${conf.clientAuth}
16       ${if lib.length (lib.splitString "/" conf.privateKey) > 1 then
17         "dbms.ssl.policy.${name}.private_key=${conf.privateKey}"
18       else
19         "dbms.ssl.policy.${name}.private_key=${conf.baseDirectory}/${conf.privateKey}"
20       }
21       ${if lib.length (lib.splitString "/" conf.privateKey) > 1 then
22         "dbms.ssl.policy.${name}.public_certificate=${conf.publicCertificate}"
23       else
24         "dbms.ssl.policy.${name}.public_certificate=${conf.baseDirectory}/${conf.publicCertificate}"
25       }
26       dbms.ssl.policy.${name}.revoked_dir=${conf.revokedDir}
27       dbms.ssl.policy.${name}.tls_versions=${lib.concatStringsSep "," conf.tlsVersions}
28       dbms.ssl.policy.${name}.trust_all=${lib.boolToString conf.trustAll}
29       dbms.ssl.policy.${name}.trusted_dir=${conf.trustedDir}
30     ''
31   ) cfg.ssl.policies;
33   serverConfig = pkgs.writeText "neo4j.conf" ''
34     # General
35     server.default_listen_address=${cfg.defaultListenAddress}
36     server.databases.default_to_read_only=${lib.boolToString cfg.readOnly}
37     ${lib.optionalString (cfg.workerCount > 0) ''
38       dbms.threads.worker_count=${toString cfg.workerCount}
39     ''}
41     # Directories (readonly)
42     # dbms.directories.certificates=${cfg.directories.certificates}
43     server.directories.plugins=${cfg.directories.plugins}
44     server.directories.lib=${cfg.package}/share/neo4j/lib
45     ${lib.optionalString (cfg.constrainLoadCsv) ''
46       server.directories.import=${cfg.directories.imports}
47    ''}
49     # Directories (read and write)
50     server.directories.data=${cfg.directories.data}
51     server.directories.logs=${cfg.directories.home}/logs
52     server.directories.run=${cfg.directories.home}/run
54     # HTTP Connector
55     server.http.enabled=${lib.boolToString cfg.http.enable}
56     server.http.listen_address=${cfg.http.listenAddress}
57     server.http.advertised_address=${cfg.http.listenAddress}
59     # HTTPS Connector
60     server.https.enabled=${lib.boolToString cfg.https.enable}
61     server.https.listen_address=${cfg.https.listenAddress}
62     server.https.advertised_address=${cfg.https.listenAddress}
64     # BOLT Connector
65     server.bolt.enabled=${lib.boolToString cfg.bolt.enable}
66     server.bolt.listen_address=${cfg.bolt.listenAddress}
67     server.bolt.advertised_address=${cfg.bolt.listenAddress}
68     server.bolt.tls_level=${cfg.bolt.tlsLevel}
70     # SSL Policies
71     ${lib.concatStringsSep "\n" sslPolicies}
73     # Default retention policy from neo4j.conf
74     db.tx_log.rotation.retention_policy=1 days
76     # Default JVM parameters from neo4j.conf
77     server.jvm.additional=-XX:+UseG1GC
78     server.jvm.additional=-XX:-OmitStackTraceInFastThrow
79     server.jvm.additional=-XX:+AlwaysPreTouch
80     server.jvm.additional=-XX:+UnlockExperimentalVMOptions
81     server.jvm.additional=-XX:+TrustFinalNonStaticFields
82     server.jvm.additional=-XX:+DisableExplicitGC
83     server.jvm.additional=-Djdk.tls.ephemeralDHKeySize=2048
84     server.jvm.additional=-Djdk.tls.rejectClientInitiatedRenegotiation=true
85     server.jvm.additional=-Dunsupported.dbms.udc.source=tarball
87     #server.memory.off_heap.transaction_max_size=12000m
88     #server.memory.heap.max_size=12000m
89     #server.memory.pagecache.size=4g
90     #server.tx_state.max_off_heap_memory=8000m
92     # Extra Configuration
93     ${cfg.extraServerConfig}
94   '';
96 in {
98   imports = [
99     (lib.mkRenamedOptionModule [ "services" "neo4j" "host" ] [ "services" "neo4j" "defaultListenAddress" ])
100     (lib.mkRenamedOptionModule [ "services" "neo4j" "listenAddress" ] [ "services" "neo4j" "defaultListenAddress" ])
101     (lib.mkRenamedOptionModule [ "services" "neo4j" "enableBolt" ] [ "services" "neo4j" "bolt" "enable" ])
102     (lib.mkRenamedOptionModule [ "services" "neo4j" "enableHttps" ] [ "services" "neo4j" "https" "enable" ])
103     (lib.mkRenamedOptionModule [ "services" "neo4j" "certDir" ] [ "services" "neo4j" "directories" "certificates" ])
104     (lib.mkRenamedOptionModule [ "services" "neo4j" "dataDir" ] [ "services" "neo4j" "directories" "home" ])
105     (lib.mkRemovedOptionModule [ "services" "neo4j" "port" ] "Use services.neo4j.http.listenAddress instead.")
106     (lib.mkRemovedOptionModule [ "services" "neo4j" "boltPort" ] "Use services.neo4j.bolt.listenAddress instead.")
107     (lib.mkRemovedOptionModule [ "services" "neo4j" "httpsPort" ] "Use services.neo4j.https.listenAddress instead.")
108     (lib.mkRemovedOptionModule [ "services" "neo4j" "shell" "enabled" ] "shell.enabled was removed upstream")
109     (lib.mkRemovedOptionModule [ "services" "neo4j" "udc" "enabled" ] "udc.enabled was removed upstream")
110   ];
112   ###### interface
114   options.services.neo4j = {
116     enable = lib.mkOption {
117       type = lib.types.bool;
118       default = false;
119       description = ''
120         Whether to enable Neo4j Community Edition.
121       '';
122     };
124     constrainLoadCsv = lib.mkOption {
125       type = lib.types.bool;
126       default = true;
127       description = ''
128         Sets the root directory for file URLs used with the Cypher
129         `LOAD CSV` clause to be that defined by
130         {option}`directories.imports`. It restricts
131         access to only those files within that directory and its
132         subdirectories.
134         Setting this option to `false` introduces
135         possible security problems.
136       '';
137     };
139     defaultListenAddress = lib.mkOption {
140       type = lib.types.str;
141       default = "127.0.0.1";
142       description = ''
143         Default network interface to listen for incoming connections. To
144         listen for connections on all interfaces, use "0.0.0.0".
146         Specifies the default IP address and address part of connector
147         specific {option}`listenAddress` options. To bind specific
148         connectors to a specific network interfaces, specify the entire
149         {option}`listenAddress` option for that connector.
150       '';
151     };
153     extraServerConfig = lib.mkOption {
154       type = lib.types.lines;
155       default = "";
156       description = ''
157         Extra configuration for Neo4j Community server. Refer to the
158         [complete reference](https://neo4j.com/docs/operations-manual/current/reference/configuration-settings/)
159         of Neo4j configuration settings.
160       '';
161     };
163     package = lib.mkPackageOption pkgs "neo4j" { };
165     readOnly = lib.mkOption {
166       type = lib.types.bool;
167       default = false;
168       description = ''
169         Only allow read operations from this Neo4j instance.
170       '';
171     };
173     workerCount = lib.mkOption {
174       type = lib.types.ints.between 0 44738;
175       default = 0;
176       description = ''
177         Number of Neo4j worker threads, where the default of
178         `0` indicates a worker count equal to the number of
179         available processors.
180       '';
181     };
183     bolt = {
184       enable = lib.mkOption {
185         type = lib.types.bool;
186         default = true;
187         description = ''
188           Enable the BOLT connector for Neo4j. Setting this option to
189           `false` will stop Neo4j from listening for incoming
190           connections on the BOLT port (7687 by default).
191         '';
192       };
194       listenAddress = lib.mkOption {
195         type = lib.types.str;
196         default = ":7687";
197         description = ''
198           Neo4j listen address for BOLT traffic. The listen address is
199           expressed in the format `<ip-address>:<port-number>`.
200         '';
201       };
203       sslPolicy = lib.mkOption {
204         type = lib.types.str;
205         default = "legacy";
206         description = ''
207           Neo4j SSL policy for BOLT traffic.
209           The legacy policy is a special policy which is not defined in
210           the policy configuration section, but rather derives from
211           {option}`directories.certificates` and
212           associated files (by default: {file}`neo4j.key` and
213           {file}`neo4j.cert`). Its use will be deprecated.
215           Note: This connector must be configured to support/require
216           SSL/TLS for the legacy policy to actually be utilized. See
217           {option}`bolt.tlsLevel`.
218         '';
219       };
221       tlsLevel = lib.mkOption {
222         type = lib.types.enum [ "REQUIRED" "OPTIONAL" "DISABLED" ];
223         default = "OPTIONAL";
224         description = ''
225           SSL/TSL requirement level for BOLT traffic.
226         '';
227       };
228     };
230     directories = {
231       certificates = lib.mkOption {
232         type = lib.types.path;
233         default = "${cfg.directories.home}/certificates";
234         defaultText = lib.literalExpression ''"''${config.${opt.directories.home}}/certificates"'';
235         description = ''
236           Directory for storing certificates to be used by Neo4j for
237           TLS connections.
239           When setting this directory to something other than its default,
240           ensure the directory's existence, and that read/write permissions are
241           given to the Neo4j daemon user `neo4j`.
243           Note that changing this directory from its default will prevent
244           the directory structure required for each SSL policy from being
245           automatically generated. A policy's directory structure as defined by
246           its {option}`baseDirectory`,{option}`revokedDir` and
247           {option}`trustedDir` must then be setup manually. The
248           existence of these directories is mandatory, as well as the presence
249           of the certificate file and the private key. Ensure the correct
250           permissions are set on these directories and files.
251         '';
252       };
254       data = lib.mkOption {
255         type = lib.types.path;
256         default = "${cfg.directories.home}/data";
257         defaultText = lib.literalExpression ''"''${config.${opt.directories.home}}/data"'';
258         description = ''
259           Path of the data directory. You must not configure more than one
260           Neo4j installation to use the same data directory.
262           When setting this directory to something other than its default,
263           ensure the directory's existence, and that read/write permissions are
264           given to the Neo4j daemon user `neo4j`.
265         '';
266       };
268       home = lib.mkOption {
269         type = lib.types.path;
270         default = "/var/lib/neo4j";
271         description = ''
272           Path of the Neo4j home directory. Other default directories are
273           subdirectories of this path. This directory will be created if
274           non-existent, and its ownership will be {command}`chown` to
275           the Neo4j daemon user `neo4j`.
276         '';
277       };
279       imports = lib.mkOption {
280         type = lib.types.path;
281         default = "${cfg.directories.home}/import";
282         defaultText = lib.literalExpression ''"''${config.${opt.directories.home}}/import"'';
283         description = ''
284           The root directory for file URLs used with the Cypher
285           `LOAD CSV` clause. Only meaningful when
286           {option}`constrainLoadCvs` is set to
287           `true`.
289           When setting this directory to something other than its default,
290           ensure the directory's existence, and that read permission is
291           given to the Neo4j daemon user `neo4j`.
292         '';
293       };
295       plugins = lib.mkOption {
296         type = lib.types.path;
297         default = "${cfg.directories.home}/plugins";
298         defaultText = lib.literalExpression ''"''${config.${opt.directories.home}}/plugins"'';
299         description = ''
300           Path of the database plugin directory. Compiled Java JAR files that
301           contain database procedures will be loaded if they are placed in
302           this directory.
304           When setting this directory to something other than its default,
305           ensure the directory's existence, and that read permission is
306           given to the Neo4j daemon user `neo4j`.
307         '';
308       };
309     };
311     http = {
312       enable = lib.mkOption {
313         type = lib.types.bool;
314         default = true;
315         description = ''
316           Enable the HTTP connector for Neo4j. Setting this option to
317           `false` will stop Neo4j from listening for incoming
318           connections on the HTTPS port (7474 by default).
319         '';
320       };
322       listenAddress = lib.mkOption {
323         type = lib.types.str;
324         default = ":7474";
325         description = ''
326           Neo4j listen address for HTTP traffic. The listen address is
327           expressed in the format `<ip-address>:<port-number>`.
328         '';
329       };
330     };
332     https = {
333       enable = lib.mkOption {
334         type = lib.types.bool;
335         default = true;
336         description = ''
337           Enable the HTTPS connector for Neo4j. Setting this option to
338           `false` will stop Neo4j from listening for incoming
339           connections on the HTTPS port (7473 by default).
340         '';
341       };
343       listenAddress = lib.mkOption {
344         type = lib.types.str;
345         default = ":7473";
346         description = ''
347           Neo4j listen address for HTTPS traffic. The listen address is
348           expressed in the format `<ip-address>:<port-number>`.
349         '';
350       };
352       sslPolicy = lib.mkOption {
353         type = lib.types.str;
354         default = "legacy";
355         description = ''
356           Neo4j SSL policy for HTTPS traffic.
358           The legacy policy is a special policy which is not defined in the
359           policy configuration section, but rather derives from
360           {option}`directories.certificates` and
361           associated files (by default: {file}`neo4j.key` and
362           {file}`neo4j.cert`). Its use will be deprecated.
363         '';
364       };
365     };
367     shell = {
368       enable = lib.mkOption {
369         type = lib.types.bool;
370         default = false;
371         description = ''
372           Enable a remote shell server which Neo4j Shell clients can log in to.
373           Only applicable to {command}`neo4j-shell`.
374         '';
375       };
376     };
378     ssl.policies = lib.mkOption {
379       type = with lib.types; attrsOf (submodule ({ name, config, options, ... }: {
380         options = {
382           allowKeyGeneration = lib.mkOption {
383             type = lib.types.bool;
384             default = false;
385             description = ''
386               Allows the generation of a private key and associated self-signed
387               certificate. Only performed when both objects cannot be found for
388               this policy. It is recommended to turn this off again after keys
389               have been generated.
391               The public certificate is required to be duplicated to the
392               directory holding trusted certificates as defined by the
393               {option}`trustedDir` option.
395               Keys should in general be generated and distributed offline by a
396               trusted certificate authority and not by utilizing this mode.
397             '';
398           };
400           baseDirectory = lib.mkOption {
401             type = lib.types.path;
402             default = "${cfg.directories.certificates}/${name}";
403             defaultText = lib.literalExpression ''"''${config.${opt.directories.certificates}}/''${name}"'';
404             description = ''
405               The mandatory base directory for cryptographic objects of this
406               policy. This path is only automatically generated when this
407               option as well as {option}`directories.certificates` are
408               left at their default. Ensure read/write permissions are given
409               to the Neo4j daemon user `neo4j`.
411               It is also possible to override each individual
412               configuration with absolute paths. See the
413               {option}`privateKey` and {option}`publicCertificate`
414               policy options.
415             '';
416           };
418           ciphers = lib.mkOption {
419             type = lib.types.nullOr (lib.types.listOf lib.types.str);
420             default = null;
421             description = ''
422               Restrict the allowed ciphers of this policy to those defined
423               here. The default ciphers are those of the JVM platform.
424             '';
425           };
427           clientAuth = lib.mkOption {
428             type = lib.types.enum [ "NONE" "OPTIONAL" "REQUIRE" ];
429             default = "REQUIRE";
430             description = ''
431               The client authentication stance for this policy.
432             '';
433           };
435           privateKey = lib.mkOption {
436             type = lib.types.str;
437             default = "private.key";
438             description = ''
439               The name of private PKCS #8 key file for this policy to be found
440               in the {option}`baseDirectory`, or the absolute path to
441               the key file. It is mandatory that a key can be found or generated.
442             '';
443           };
445           publicCertificate = lib.mkOption {
446             type = lib.types.str;
447             default = "public.crt";
448             description = ''
449               The name of public X.509 certificate (chain) file in PEM format
450               for this policy to be found in the {option}`baseDirectory`,
451               or the absolute path to the certificate file. It is mandatory
452               that a certificate can be found or generated.
454               The public certificate is required to be duplicated to the
455               directory holding trusted certificates as defined by the
456               {option}`trustedDir` option.
457             '';
458           };
460           revokedDir = lib.mkOption {
461             type = lib.types.path;
462             default = "${config.baseDirectory}/revoked";
463             defaultText = lib.literalExpression ''"''${config.${options.baseDirectory}}/revoked"'';
464             description = ''
465               Path to directory of CRLs (Certificate Revocation Lists) in
466               PEM format. Must be an absolute path. The existence of this
467               directory is mandatory and will need to be created manually when:
468               setting this option to something other than its default; setting
469               either this policy's {option}`baseDirectory` or
470               {option}`directories.certificates` to something other than
471               their default. Ensure read/write permissions are given to the
472               Neo4j daemon user `neo4j`.
473             '';
474           };
476           tlsVersions = lib.mkOption {
477             type = lib.types.listOf lib.types.str;
478             default = [ "TLSv1.2" ];
479             description = ''
480               Restrict the TLS protocol versions of this policy to those
481               defined here.
482             '';
483           };
485           trustAll = lib.mkOption {
486             type = lib.types.bool;
487             default = false;
488             description = ''
489               Makes this policy trust all remote parties. Enabling this is not
490               recommended and the policy's trusted directory will be ignored.
491               Use of this mode is discouraged. It would offer encryption but
492               no security.
493             '';
494           };
496           trustedDir = lib.mkOption {
497             type = lib.types.path;
498             default = "${config.baseDirectory}/trusted";
499             defaultText = lib.literalExpression ''"''${config.${options.baseDirectory}}/trusted"'';
500             description = ''
501               Path to directory of X.509 certificates in PEM format for
502               trusted parties. Must be an absolute path. The existence of this
503               directory is mandatory and will need to be created manually when:
504               setting this option to something other than its default; setting
505               either this policy's {option}`baseDirectory` or
506               {option}`directories.certificates` to something other than
507               their default. Ensure read/write permissions are given to the
508               Neo4j daemon user `neo4j`.
510               The public certificate as defined by
511               {option}`publicCertificate` is required to be duplicated
512               to this directory.
513             '';
514           };
516           directoriesToCreate = lib.mkOption {
517             type = lib.types.listOf lib.types.path;
518             internal = true;
519             readOnly = true;
520             description = ''
521               Directories of this policy that will be created automatically
522               when the certificates directory is left at its default value.
523               This includes all options of type path that are left at their
524               default value.
525             '';
526           };
528         };
530         config.directoriesToCreate = lib.optionals
531           (certDirOpt.highestPrio >= 1500 && options.baseDirectory.highestPrio >= 1500)
532           (map (opt: opt.value) (lib.filter isDefaultPathOption (lib.attrValues options)));
534       }));
535       default = {};
536       description = ''
537         Defines the SSL policies for use with Neo4j connectors. Each attribute
538         of this set defines a policy, with the attribute name defining the name
539         of the policy and its namespace. Refer to the operations manual section
540         on Neo4j's
541         [SSL Framework](https://neo4j.com/docs/operations-manual/current/security/ssl-framework/)
542         for further details.
543       '';
544     };
546   };
548   ###### implementation
550   config =
551     let
552       # Assertion helpers
553       policyNameList = lib.attrNames cfg.ssl.policies;
554       validPolicyNameList = [ "legacy" ] ++ policyNameList;
555       validPolicyNameString = lib.concatStringsSep ", " validPolicyNameList;
557       # Capture various directories left at their default so they can be created.
558       defaultDirectoriesToCreate = map (opt: opt.value) (lib.filter isDefaultPathOption (lib.attrValues options.services.neo4j.directories));
559       policyDirectoriesToCreate = lib.concatMap (pol: pol.directoriesToCreate) (lib.attrValues cfg.ssl.policies);
560     in
562     lib.mkIf cfg.enable {
563       assertions = [
564         { assertion = !lib.elem "legacy" policyNameList;
565           message = "The policy 'legacy' is special to Neo4j, and its name is reserved."; }
566         { assertion = lib.elem cfg.bolt.sslPolicy validPolicyNameList;
567           message = "Invalid policy assigned: `services.neo4j.bolt.sslPolicy = \"${cfg.bolt.sslPolicy}\"`, defined policies are: ${validPolicyNameString}"; }
568         { assertion = lib.elem cfg.https.sslPolicy validPolicyNameList;
569           message = "Invalid policy assigned: `services.neo4j.https.sslPolicy = \"${cfg.https.sslPolicy}\"`, defined policies are: ${validPolicyNameString}"; }
570       ];
572       systemd.services.neo4j = {
573         description = "Neo4j Daemon";
574         wantedBy = [ "multi-user.target" ];
575         after = [ "network.target" ];
576         environment = {
577           NEO4J_HOME = "${cfg.directories.home}";
578           NEO4J_CONF = "${cfg.directories.home}/conf";
579         };
580         serviceConfig = {
581           ExecStart = "${cfg.package}/bin/neo4j console";
582           User = "neo4j";
583           PermissionsStartOnly = true;
584           LimitNOFILE = 40000;
585         };
587         preStart = ''
588           # Directories Setup
589           #   Always ensure home exists with nested conf, logs directories.
590           mkdir -m 0700 -p ${cfg.directories.home}/{conf,logs}
592           #   Create other sub-directories and policy directories that have been left at their default.
593           ${lib.concatMapStringsSep "\n" (
594             dir: ''
595               mkdir -m 0700 -p ${dir}
596           '') (defaultDirectoriesToCreate ++ policyDirectoriesToCreate)}
598           # Place the configuration where Neo4j can find it.
599           ln -fs ${serverConfig} ${cfg.directories.home}/conf/neo4j.conf
601           # Ensure neo4j user ownership
602           chown -R neo4j ${cfg.directories.home}
603         '';
604       };
606       environment.systemPackages = [ cfg.package ];
608       users.users.neo4j = {
609         isSystemUser = true;
610         group = "neo4j";
611         description = "Neo4j daemon user";
612         home = cfg.directories.home;
613       };
614       users.groups.neo4j = {};
615     };
617   meta = {
618     maintainers = with lib.maintainers; [ patternspandemic ];
619   };