1 { config, options, lib, pkgs, ... }:
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 (
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}
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}"
19 "dbms.ssl.policy.${name}.private_key=${conf.baseDirectory}/${conf.privateKey}"
21 ${if lib.length (lib.splitString "/" conf.privateKey) > 1 then
22 "dbms.ssl.policy.${name}.public_certificate=${conf.publicCertificate}"
24 "dbms.ssl.policy.${name}.public_certificate=${conf.baseDirectory}/${conf.publicCertificate}"
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}
33 serverConfig = pkgs.writeText "neo4j.conf" ''
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}
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}
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
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}
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}
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}
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
93 ${cfg.extraServerConfig}
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")
114 options.services.neo4j = {
116 enable = lib.mkOption {
117 type = lib.types.bool;
120 Whether to enable Neo4j Community Edition.
124 constrainLoadCsv = lib.mkOption {
125 type = lib.types.bool;
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
134 Setting this option to `false` introduces
135 possible security problems.
139 defaultListenAddress = lib.mkOption {
140 type = lib.types.str;
141 default = "127.0.0.1";
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.
153 extraServerConfig = lib.mkOption {
154 type = lib.types.lines;
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.
163 package = lib.mkPackageOption pkgs "neo4j" { };
165 readOnly = lib.mkOption {
166 type = lib.types.bool;
169 Only allow read operations from this Neo4j instance.
173 workerCount = lib.mkOption {
174 type = lib.types.ints.between 0 44738;
177 Number of Neo4j worker threads, where the default of
178 `0` indicates a worker count equal to the number of
179 available processors.
184 enable = lib.mkOption {
185 type = lib.types.bool;
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).
194 listenAddress = lib.mkOption {
195 type = lib.types.str;
198 Neo4j listen address for BOLT traffic. The listen address is
199 expressed in the format `<ip-address>:<port-number>`.
203 sslPolicy = lib.mkOption {
204 type = lib.types.str;
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`.
221 tlsLevel = lib.mkOption {
222 type = lib.types.enum [ "REQUIRED" "OPTIONAL" "DISABLED" ];
223 default = "OPTIONAL";
225 SSL/TSL requirement level for BOLT traffic.
231 certificates = lib.mkOption {
232 type = lib.types.path;
233 default = "${cfg.directories.home}/certificates";
234 defaultText = lib.literalExpression ''"''${config.${opt.directories.home}}/certificates"'';
236 Directory for storing certificates to be used by Neo4j for
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.
254 data = lib.mkOption {
255 type = lib.types.path;
256 default = "${cfg.directories.home}/data";
257 defaultText = lib.literalExpression ''"''${config.${opt.directories.home}}/data"'';
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`.
268 home = lib.mkOption {
269 type = lib.types.path;
270 default = "/var/lib/neo4j";
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`.
279 imports = lib.mkOption {
280 type = lib.types.path;
281 default = "${cfg.directories.home}/import";
282 defaultText = lib.literalExpression ''"''${config.${opt.directories.home}}/import"'';
284 The root directory for file URLs used with the Cypher
285 `LOAD CSV` clause. Only meaningful when
286 {option}`constrainLoadCvs` is set to
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`.
295 plugins = lib.mkOption {
296 type = lib.types.path;
297 default = "${cfg.directories.home}/plugins";
298 defaultText = lib.literalExpression ''"''${config.${opt.directories.home}}/plugins"'';
300 Path of the database plugin directory. Compiled Java JAR files that
301 contain database procedures will be loaded if they are placed in
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`.
312 enable = lib.mkOption {
313 type = lib.types.bool;
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).
322 listenAddress = lib.mkOption {
323 type = lib.types.str;
326 Neo4j listen address for HTTP traffic. The listen address is
327 expressed in the format `<ip-address>:<port-number>`.
333 enable = lib.mkOption {
334 type = lib.types.bool;
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).
343 listenAddress = lib.mkOption {
344 type = lib.types.str;
347 Neo4j listen address for HTTPS traffic. The listen address is
348 expressed in the format `<ip-address>:<port-number>`.
352 sslPolicy = lib.mkOption {
353 type = lib.types.str;
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.
368 enable = lib.mkOption {
369 type = lib.types.bool;
372 Enable a remote shell server which Neo4j Shell clients can log in to.
373 Only applicable to {command}`neo4j-shell`.
378 ssl.policies = lib.mkOption {
379 type = with lib.types; attrsOf (submodule ({ name, config, options, ... }: {
382 allowKeyGeneration = lib.mkOption {
383 type = lib.types.bool;
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
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.
400 baseDirectory = lib.mkOption {
401 type = lib.types.path;
402 default = "${cfg.directories.certificates}/${name}";
403 defaultText = lib.literalExpression ''"''${config.${opt.directories.certificates}}/''${name}"'';
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`
418 ciphers = lib.mkOption {
419 type = lib.types.nullOr (lib.types.listOf lib.types.str);
422 Restrict the allowed ciphers of this policy to those defined
423 here. The default ciphers are those of the JVM platform.
427 clientAuth = lib.mkOption {
428 type = lib.types.enum [ "NONE" "OPTIONAL" "REQUIRE" ];
431 The client authentication stance for this policy.
435 privateKey = lib.mkOption {
436 type = lib.types.str;
437 default = "private.key";
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.
445 publicCertificate = lib.mkOption {
446 type = lib.types.str;
447 default = "public.crt";
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.
460 revokedDir = lib.mkOption {
461 type = lib.types.path;
462 default = "${config.baseDirectory}/revoked";
463 defaultText = lib.literalExpression ''"''${config.${options.baseDirectory}}/revoked"'';
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`.
476 tlsVersions = lib.mkOption {
477 type = lib.types.listOf lib.types.str;
478 default = [ "TLSv1.2" ];
480 Restrict the TLS protocol versions of this policy to those
485 trustAll = lib.mkOption {
486 type = lib.types.bool;
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
496 trustedDir = lib.mkOption {
497 type = lib.types.path;
498 default = "${config.baseDirectory}/trusted";
499 defaultText = lib.literalExpression ''"''${config.${options.baseDirectory}}/trusted"'';
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
516 directoriesToCreate = lib.mkOption {
517 type = lib.types.listOf lib.types.path;
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
530 config.directoriesToCreate = lib.optionals
531 (certDirOpt.highestPrio >= 1500 && options.baseDirectory.highestPrio >= 1500)
532 (map (opt: opt.value) (lib.filter isDefaultPathOption (lib.attrValues options)));
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
541 [SSL Framework](https://neo4j.com/docs/operations-manual/current/security/ssl-framework/)
548 ###### implementation
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);
562 lib.mkIf cfg.enable {
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}"; }
572 systemd.services.neo4j = {
573 description = "Neo4j Daemon";
574 wantedBy = [ "multi-user.target" ];
575 after = [ "network.target" ];
577 NEO4J_HOME = "${cfg.directories.home}";
578 NEO4J_CONF = "${cfg.directories.home}/conf";
581 ExecStart = "${cfg.package}/bin/neo4j console";
583 PermissionsStartOnly = true;
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" (
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}
606 environment.systemPackages = [ cfg.package ];
608 users.users.neo4j = {
611 description = "Neo4j daemon user";
612 home = cfg.directories.home;
614 users.groups.neo4j = {};
618 maintainers = with lib.maintainers; [ patternspandemic ];