···
1
-
{ config, lib, pkgs, ... }:
1
+
{ config, options, lib, pkgs, ... }:
cfg = config.services.neo4j;
7
+
certDirOpt = options.services.neo4j.directories.certificates;
8
+
isDefaultPathOption = opt: isOption opt && opt.type == types.path && opt.highestPrio >= 1500;
10
+
sslPolicies = mapAttrsToList (
12
+
dbms.ssl.policy.${name}.allow_key_generation=${boolToString conf.allowKeyGeneration}
13
+
dbms.ssl.policy.${name}.base_directory=${conf.baseDirectory}
14
+
${optionalString (conf.ciphers != null) ''
15
+
dbms.ssl.policy.${name}.ciphers=${concatStringsSep "," conf.ciphers}
17
+
dbms.ssl.policy.${name}.client_auth=${conf.clientAuth}
18
+
${if length (splitString "/" conf.privateKey) > 1 then
19
+
''dbms.ssl.policy.${name}.private_key=${conf.privateKey}''
21
+
''dbms.ssl.policy.${name}.private_key=${conf.baseDirectory}/${conf.privateKey}''
23
+
${if length (splitString "/" conf.privateKey) > 1 then
24
+
''dbms.ssl.policy.${name}.public_certificate=${conf.publicCertificate}''
26
+
''dbms.ssl.policy.${name}.public_certificate=${conf.baseDirectory}/${conf.publicCertificate}''
28
+
dbms.ssl.policy.${name}.revoked_dir=${conf.revokedDir}
29
+
dbms.ssl.policy.${name}.tls_versions=${concatStringsSep "," conf.tlsVersions}
30
+
dbms.ssl.policy.${name}.trust_all=${boolToString conf.trustAll}
31
+
dbms.ssl.policy.${name}.trusted_dir=${conf.trustedDir}
serverConfig = pkgs.writeText "neo4j.conf" ''
9
-
dbms.directories.data=${cfg.dataDir}/data
10
-
dbms.directories.certificates=${cfg.certDir}
11
-
dbms.directories.logs=${cfg.dataDir}/logs
12
-
dbms.directories.plugins=${cfg.dataDir}/plugins
13
-
dbms.connector.http.type=HTTP
14
-
dbms.connector.http.enabled=true
15
-
dbms.connector.http.address=${cfg.listenAddress}:${toString cfg.port}
16
-
${optionalString cfg.enableBolt ''
17
-
dbms.connector.bolt.type=BOLT
18
-
dbms.connector.bolt.enabled=true
19
-
dbms.connector.bolt.tls_level=OPTIONAL
20
-
dbms.connector.bolt.address=${cfg.listenAddress}:${toString cfg.boltPort}
37
+
dbms.allow_upgrade=${boolToString cfg.allowUpgrade}
38
+
dbms.connectors.default_listen_address=${cfg.defaultListenAddress}
39
+
dbms.read_only=${boolToString cfg.readOnly}
40
+
${optionalString (cfg.workerCount > 0) ''
41
+
dbms.threads.worker_count=${toString cfg.workerCount}
45
+
dbms.directories.certificates=${cfg.directories.certificates}
46
+
dbms.directories.data=${cfg.directories.data}
47
+
dbms.directories.logs=${cfg.directories.home}/logs
48
+
dbms.directories.plugins=${cfg.directories.plugins}
49
+
${optionalString (cfg.constrainLoadCsv) ''
50
+
dbms.directories.import=${cfg.directories.imports}
22
-
${optionalString cfg.enableHttps ''
23
-
dbms.connector.https.type=HTTP
24
-
dbms.connector.https.enabled=true
25
-
dbms.connector.https.encryption=TLS
26
-
dbms.connector.https.address=${cfg.listenAddress}:${toString cfg.httpsPort}
54
+
${optionalString (cfg.http.enable) ''
55
+
dbms.connector.http.enabled=${boolToString cfg.http.enable}
56
+
dbms.connector.http.listen_address=${cfg.http.listenAddress}
28
-
dbms.shell.enabled=true
29
-
${cfg.extraServerConfig}
58
+
${optionalString (!cfg.http.enable) ''
59
+
# It is not possible to disable the HTTP connector. To fully prevent
60
+
# clients from connecting to HTTP, block the HTTP port (7474 by default)
61
+
# via firewall. listen_address is set to the loopback interface to
62
+
# prevent remote clients from connecting.
63
+
dbms.connector.http.listen_address=127.0.0.1
67
+
dbms.connector.https.enabled=${boolToString cfg.https.enable}
68
+
dbms.connector.https.listen_address=${cfg.https.listenAddress}
69
+
https.ssl_policy=${cfg.https.sslPolicy}
72
+
dbms.connector.bolt.enabled=${boolToString cfg.bolt.enable}
73
+
dbms.connector.bolt.listen_address=${cfg.bolt.listenAddress}
74
+
bolt.ssl_policy=${cfg.bolt.sslPolicy}
75
+
dbms.connector.bolt.tls_level=${cfg.bolt.tlsLevel}
78
+
dbms.shell.enabled=${boolToString cfg.shell.enable}
81
+
${concatStringsSep "\n" sslPolicies}
83
+
# Default retention policy from neo4j.conf
84
+
dbms.tx_log.rotation.retention_policy=1 days
# Default JVM parameters from neo4j.conf
dbms.jvm.additional=-XX:+UseG1GC
···
dbms.jvm.additional=-XX:+TrustFinalNonStaticFields
dbms.jvm.additional=-XX:+DisableExplicitGC
dbms.jvm.additional=-Djdk.tls.ephemeralDHKeySize=2048
94
+
dbms.jvm.additional=-Djdk.tls.rejectClientInitiatedRenegotiation=true
95
+
dbms.jvm.additional=-Dunsupported.dbms.udc.source=tarball
40
-
dbms.jvm.additional=-Dunsupported.dbms.udc.source=tarball
97
+
# Usage Data Collector
98
+
dbms.udc.enabled=${boolToString cfg.udc.enable}
100
+
# Extra Configuration
101
+
${cfg.extraServerConfig}
···
options.services.neo4j = {
49
-
description = "Whether to enable neo4j.";
114
+
Whether to enable Neo4j Community Edition.
118
+
allowUpgrade = mkOption {
122
+
Allow upgrade of Neo4j database files from an older version.
126
+
constrainLoadCsv = mkOption {
130
+
Sets the root directory for file URLs used with the Cypher
131
+
<literal>LOAD CSV</literal> clause to be that defined by
132
+
<option>directories.imports</option>. It restricts
133
+
access to only those files within that directory and its
137
+
Setting this option to <literal>false</literal> introduces
138
+
possible security problems.
142
+
defaultListenAddress = mkOption {
144
+
default = "127.0.0.1";
146
+
Default network interface to listen for incoming connections. To
147
+
listen for connections on all interfaces, use "0.0.0.0".
150
+
Specifies the default IP address and address part of connector
151
+
specific <option>listenAddress</option> options. To bind specific
152
+
connectors to a specific network interfaces, specify the entire
153
+
<option>listenAddress</option> option for that connector.
157
+
extraServerConfig = mkOption {
158
+
type = types.lines;
161
+
Extra configuration for Neo4j Community server. Refer to the
162
+
<link xlink:href="https://neo4j.com/docs/operations-manual/current/reference/configuration-settings/">complete reference</link>
163
+
of Neo4j configuration settings.
55
-
description = "Neo4j package to use.";
168
+
type = types.package;
defaultText = "pkgs.neo4j";
58
-
type = types.package;
172
+
Neo4j package to use.
61
-
listenAddress = mkOption {
62
-
description = "Neo4j listen address.";
63
-
default = "127.0.0.1";
176
+
readOnly = mkOption {
180
+
Only allow read operations from this Neo4j instance.
68
-
description = "Neo4j port to listen for HTTP traffic.";
184
+
workerCount = mkOption {
185
+
type = types.ints.between 0 44738;
188
+
Number of Neo4j worker threads, where the default of
189
+
<literal>0</literal> indicates a worker count equal to the number of
190
+
available processors.
73
-
enableBolt = mkOption {
74
-
description = "Enable bolt for Neo4j.";
195
+
enable = mkOption {
199
+
Enable the BOLT connector for Neo4j. Setting this option to
200
+
<literal>false</literal> will stop Neo4j from listening for incoming
201
+
connections on the BOLT port (7687 by default).
205
+
listenAddress = mkOption {
209
+
Neo4j listen address for BOLT traffic. The listen address is
210
+
expressed in the format <literal><ip-address>:<port-number></literal>.
214
+
sslPolicy = mkOption {
216
+
default = "legacy";
218
+
Neo4j SSL policy for BOLT traffic.
221
+
The legacy policy is a special policy which is not defined in
222
+
the policy configuration section, but rather derives from
223
+
<option>directories.certificates</option> and
224
+
associated files (by default: <filename>neo4j.key</filename> and
225
+
<filename>neo4j.cert</filename>). Its use will be deprecated.
228
+
Note: This connector must be configured to support/require
229
+
SSL/TLS for the legacy policy to actually be utilized. See
230
+
<option>bolt.tlsLevel</option>.
234
+
tlsLevel = mkOption {
235
+
type = types.enum [ "REQUIRED" "OPTIONAL" "DISABLED" ];
236
+
default = "OPTIONAL";
238
+
SSL/TSL requirement level for BOLT traffic.
79
-
boltPort = mkOption {
80
-
description = "Neo4j port to listen for BOLT traffic.";
244
+
certificates = mkOption {
246
+
default = "${cfg.directories.home}/certificates";
248
+
Directory for storing certificates to be used by Neo4j for
252
+
When setting this directory to something other than its default,
253
+
ensure the directory's existence, and that read/write permissions are
254
+
given to the Neo4j daemon user <literal>neo4j</literal>.
257
+
Note that changing this directory from its default will prevent
258
+
the directory structure required for each SSL policy from being
259
+
automatically generated. A policy's directory structure as defined by
260
+
its <option>baseDirectory</option>,<option>revokedDir</option> and
261
+
<option>trustedDir</option> must then be setup manually. The
262
+
existence of these directories is mandatory, as well as the presence
263
+
of the certificate file and the private key. Ensure the correct
264
+
permissions are set on these directories and files.
270
+
default = "${cfg.directories.home}/data";
272
+
Path of the data directory. You must not configure more than one
273
+
Neo4j installation to use the same data directory.
276
+
When setting this directory to something other than its default,
277
+
ensure the directory's existence, and that read/write permissions are
278
+
given to the Neo4j daemon user <literal>neo4j</literal>.
284
+
default = "/var/lib/neo4j";
286
+
Path of the Neo4j home directory. Other default directories are
287
+
subdirectories of this path. This directory will be created if
288
+
non-existent, and its ownership will be <command>chown</command> to
289
+
the Neo4j daemon user <literal>neo4j</literal>.
293
+
imports = mkOption {
295
+
default = "${cfg.directories.home}/import";
297
+
The root directory for file URLs used with the Cypher
298
+
<literal>LOAD CSV</literal> clause. Only meaningful when
299
+
<option>constrainLoadCvs</option> is set to
300
+
<literal>true</literal>.
303
+
When setting this directory to something other than its default,
304
+
ensure the directory's existence, and that read permission is
305
+
given to the Neo4j daemon user <literal>neo4j</literal>.
309
+
plugins = mkOption {
311
+
default = "${cfg.directories.home}/plugins";
313
+
Path of the database plugin directory. Compiled Java JAR files that
314
+
contain database procedures will be loaded if they are placed in
318
+
When setting this directory to something other than its default,
319
+
ensure the directory's existence, and that read permission is
320
+
given to the Neo4j daemon user <literal>neo4j</literal>.
85
-
enableHttps = mkOption {
86
-
description = "Enable https for Neo4j.";
326
+
enable = mkOption {
330
+
The HTTP connector is required for Neo4j, and cannot be disabled.
331
+
Setting this option to <literal>false</literal> will force the HTTP
332
+
connector's <option>listenAddress</option> to the loopback
333
+
interface to prevent connection of remote clients. To prevent all
334
+
clients from connecting, block the HTTP port (7474 by default) by
339
+
listenAddress = mkOption {
343
+
Neo4j listen address for HTTP traffic. The listen address is
344
+
expressed in the format <literal><ip-address>:<port-number></literal>.
91
-
httpsPort = mkOption {
92
-
description = "Neo4j port to listen for HTTPS traffic.";
350
+
enable = mkOption {
354
+
Enable the HTTPS connector for Neo4j. Setting this option to
355
+
<literal>false</literal> will stop Neo4j from listening for incoming
356
+
connections on the HTTPS port (7473 by default).
360
+
listenAddress = mkOption {
364
+
Neo4j listen address for HTTPS traffic. The listen address is
365
+
expressed in the format <literal><ip-address>:<port-number></literal>.
369
+
sslPolicy = mkOption {
371
+
default = "legacy";
373
+
Neo4j SSL policy for HTTPS traffic.
376
+
The legacy policy is a special policy which is not defined in the
377
+
policy configuration section, but rather derives from
378
+
<option>directories.certificates</option> and
379
+
associated files (by default: <filename>neo4j.key</filename> and
380
+
<filename>neo4j.cert</filename>). Its use will be deprecated.
97
-
certDir = mkOption {
98
-
description = "Neo4j TLS certificates directory.";
99
-
default = "${cfg.dataDir}/certificates";
386
+
enable = mkOption {
390
+
Enable a remote shell server which Neo4j Shell clients can log in to.
391
+
Only applicable to <command>neo4j-shell</command>.
103
-
dataDir = mkOption {
104
-
description = "Neo4j data directory.";
105
-
default = "/var/lib/neo4j";
396
+
ssl.policies = mkOption {
397
+
type = with types; attrsOf (submodule ({ name, config, options, ... }: {
400
+
allowKeyGeneration = mkOption {
404
+
Allows the generation of a private key and associated self-signed
405
+
certificate. Only performed when both objects cannot be found for
406
+
this policy. It is recommended to turn this off again after keys
407
+
have been generated.
410
+
The public certificate is required to be duplicated to the
411
+
directory holding trusted certificates as defined by the
412
+
<option>trustedDir</option> option.
415
+
Keys should in general be generated and distributed offline by a
416
+
trusted certificate authority and not by utilizing this mode.
420
+
baseDirectory = mkOption {
422
+
default = "${cfg.directories.certificates}/${name}";
424
+
The mandatory base directory for cryptographic objects of this
425
+
policy. This path is only automatically generated when this
426
+
option as well as <option>directories.certificates</option> are
427
+
left at their default. Ensure read/write permissions are given
428
+
to the Neo4j daemon user <literal>neo4j</literal>.
431
+
It is also possible to override each individual
432
+
configuration with absolute paths. See the
433
+
<option>privateKey</option> and <option>publicCertificate</option>
438
+
ciphers = mkOption {
439
+
type = types.nullOr (types.listOf types.str);
442
+
Restrict the allowed ciphers of this policy to those defined
443
+
here. The default ciphers are those of the JVM platform.
447
+
clientAuth = mkOption {
448
+
type = types.enum [ "NONE" "OPTIONAL" "REQUIRE" ];
449
+
default = "REQUIRE";
451
+
The client authentication stance for this policy.
455
+
privateKey = mkOption {
457
+
default = "private.key";
459
+
The name of private PKCS #8 key file for this policy to be found
460
+
in the <option>baseDirectory</option>, or the absolute path to
461
+
the key file. It is mandatory that a key can be found or generated.
465
+
publicCertificate = mkOption {
467
+
default = "public.crt";
469
+
The name of public X.509 certificate (chain) file in PEM format
470
+
for this policy to be found in the <option>baseDirectory</option>,
471
+
or the absolute path to the certificate file. It is mandatory
472
+
that a certificate can be found or generated.
475
+
The public certificate is required to be duplicated to the
476
+
directory holding trusted certificates as defined by the
477
+
<option>trustedDir</option> option.
481
+
revokedDir = mkOption {
483
+
default = "${config.baseDirectory}/revoked";
485
+
Path to directory of CRLs (Certificate Revocation Lists) in
486
+
PEM format. Must be an absolute path. The existence of this
487
+
directory is mandatory and will need to be created manually when:
488
+
setting this option to something other than its default; setting
489
+
either this policy's <option>baseDirectory</option> or
490
+
<option>directories.certificates</option> to something other than
491
+
their default. Ensure read/write permissions are given to the
492
+
Neo4j daemon user <literal>neo4j</literal>.
496
+
tlsVersions = mkOption {
497
+
type = types.listOf types.str;
498
+
default = [ "TLSv1.2" ];
500
+
Restrict the TLS protocol versions of this policy to those
505
+
trustAll = mkOption {
509
+
Makes this policy trust all remote parties. Enabling this is not
510
+
recommended and the policy's trusted directory will be ignored.
511
+
Use of this mode is discouraged. It would offer encryption but
516
+
trustedDir = mkOption {
518
+
default = "${config.baseDirectory}/trusted";
520
+
Path to directory of X.509 certificates in PEM format for
521
+
trusted parties. Must be an absolute path. The existence of this
522
+
directory is mandatory and will need to be created manually when:
523
+
setting this option to something other than its default; setting
524
+
either this policy's <option>baseDirectory</option> or
525
+
<option>directories.certificates</option> to something other than
526
+
their default. Ensure read/write permissions are given to the
527
+
Neo4j daemon user <literal>neo4j</literal>.
530
+
The public certificate as defined by
531
+
<option>publicCertificate</option> is required to be duplicated
536
+
directoriesToCreate = mkOption {
537
+
type = types.listOf types.path;
541
+
Directories of this policy that will be created automatically
542
+
when the certificates directory is left at its default value.
543
+
This includes all options of type path that are left at their
550
+
config.directoriesToCreate = optionals
551
+
(certDirOpt.highestPrio >= 1500 && options.baseDirectory.highestPrio >= 1500)
552
+
(map (opt: opt.value) (filter isDefaultPathOption (attrValues options)));
557
+
Defines the SSL policies for use with Neo4j connectors. Each attribute
558
+
of this set defines a policy, with the attribute name defining the name
559
+
of the policy and its namespace. Refer to the operations manual section
561
+
<link xlink:href="https://neo4j.com/docs/operations-manual/current/security/ssl-framework/">SSL Framework</link>
562
+
for further details.
109
-
extraServerConfig = mkOption {
110
-
description = "Extra configuration for neo4j server.";
112
-
type = types.lines;
567
+
enable = mkOption {
571
+
Enable the Usage Data Collector which Neo4j uses to collect usage
572
+
data. Refer to the operations manual section on the
573
+
<link xlink:href="https://neo4j.com/docs/operations-manual/current/configuration/usage-data-collector/">Usage Data Collector</link>
574
+
for more information.
118
-
config = mkIf cfg.enable {
119
-
systemd.services.neo4j = {
120
-
description = "Neo4j Daemon";
121
-
wantedBy = [ "multi-user.target" ];
122
-
after = [ "network.target" ];
124
-
NEO4J_HOME = "${cfg.package}/share/neo4j";
125
-
NEO4J_CONF = "${cfg.dataDir}/conf";
128
-
ExecStart = "${cfg.package}/bin/neo4j console";
130
-
PermissionsStartOnly = true;
131
-
LimitNOFILE = 40000;
585
+
# Assertion helpers
586
+
policyNameList = attrNames cfg.ssl.policies;
587
+
validPolicyNameList = [ "legacy" ] ++ policyNameList;
588
+
validPolicyNameString = concatStringsSep ", " validPolicyNameList;
590
+
# Capture various directories left at their default so they can be created.
591
+
defaultDirectoriesToCreate = map (opt: opt.value) (filter isDefaultPathOption (attrValues options.services.neo4j.directories));
592
+
policyDirectoriesToCreate = concatMap (pol: pol.directoriesToCreate) (attrValues cfg.ssl.policies);
597
+
{ assertion = !elem "legacy" policyNameList;
598
+
message = "The policy 'legacy' is special to Neo4j, and its name is reserved."; }
599
+
{ assertion = elem cfg.bolt.sslPolicy validPolicyNameList;
600
+
message = "Invalid policy assigned: `services.neo4j.bolt.sslPolicy = \"${cfg.bolt.sslPolicy}\"`, defined policies are: ${validPolicyNameString}"; }
601
+
{ assertion = elem cfg.https.sslPolicy validPolicyNameList;
602
+
message = "Invalid policy assigned: `services.neo4j.https.sslPolicy = \"${cfg.https.sslPolicy}\"`, defined policies are: ${validPolicyNameString}"; }
605
+
systemd.services.neo4j = {
606
+
description = "Neo4j Daemon";
607
+
wantedBy = [ "multi-user.target" ];
608
+
after = [ "network.target" ];
610
+
NEO4J_HOME = "${cfg.package}/share/neo4j";
611
+
NEO4J_CONF = "${cfg.directories.home}/conf";
614
+
ExecStart = "${cfg.package}/bin/neo4j console";
616
+
PermissionsStartOnly = true;
617
+
LimitNOFILE = 40000;
621
+
# Directories Setup
622
+
# Always ensure home exists with nested conf, logs directories.
623
+
mkdir -m 0700 -p ${cfg.directories.home}/{conf,logs}
625
+
# Create other sub-directories and policy directories that have been left at their default.
626
+
${concatMapStringsSep "\n" (
628
+
mkdir -m 0700 -p ${dir}
629
+
'') (defaultDirectoriesToCreate ++ policyDirectoriesToCreate)}
631
+
# Place the configuration where Neo4j can find it.
632
+
ln -fs ${serverConfig} ${cfg.directories.home}/conf/neo4j.conf
634
+
# Ensure neo4j user ownership
635
+
chown -R neo4j ${cfg.directories.home}
134
-
mkdir -m 0700 -p ${cfg.dataDir}/{data/graph.db,conf,logs}
135
-
ln -fs ${serverConfig} ${cfg.dataDir}/conf/neo4j.conf
136
-
if [ "$(id -u)" = 0 ]; then chown -R neo4j ${cfg.dataDir}; fi
140
-
environment.systemPackages = [ cfg.package ];
639
+
environment.systemPackages = [ cfg.package ];
142
-
users.users = singleton {
144
-
uid = config.ids.uids.neo4j;
145
-
description = "Neo4j daemon user";
146
-
home = cfg.dataDir;
641
+
users.users = singleton {
643
+
uid = config.ids.uids.neo4j;
644
+
description = "Neo4j daemon user";
645
+
home = cfg.directories.home;
650
+
maintainers = with lib.maintainers; [ patternspandemic ];