at 18.09-beta 24 kB view raw
1{ config, options, lib, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.services.neo4j; 7 certDirOpt = options.services.neo4j.directories.certificates; 8 isDefaultPathOption = opt: isOption opt && opt.type == types.path && opt.highestPrio >= 1500; 9 10 sslPolicies = mapAttrsToList ( 11 name: conf: '' 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} 16 ''} 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}'' 20 else 21 ''dbms.ssl.policy.${name}.private_key=${conf.baseDirectory}/${conf.privateKey}'' 22 } 23 ${if length (splitString "/" conf.privateKey) > 1 then 24 ''dbms.ssl.policy.${name}.public_certificate=${conf.publicCertificate}'' 25 else 26 ''dbms.ssl.policy.${name}.public_certificate=${conf.baseDirectory}/${conf.publicCertificate}'' 27 } 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} 32 '' 33 ) cfg.ssl.policies; 34 35 serverConfig = pkgs.writeText "neo4j.conf" '' 36 # General 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} 42 ''} 43 44 # Directories 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} 51 ''} 52 53 # HTTP Connector 54 ${optionalString (cfg.http.enable) '' 55 dbms.connector.http.enabled=${boolToString cfg.http.enable} 56 dbms.connector.http.listen_address=${cfg.http.listenAddress} 57 ''} 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 64 ''} 65 66 # HTTPS Connector 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} 70 71 # BOLT Connector 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} 76 77 # neo4j-shell 78 dbms.shell.enabled=${boolToString cfg.shell.enable} 79 80 # SSL Policies 81 ${concatStringsSep "\n" sslPolicies} 82 83 # Default retention policy from neo4j.conf 84 dbms.tx_log.rotation.retention_policy=1 days 85 86 # Default JVM parameters from neo4j.conf 87 dbms.jvm.additional=-XX:+UseG1GC 88 dbms.jvm.additional=-XX:-OmitStackTraceInFastThrow 89 dbms.jvm.additional=-XX:+AlwaysPreTouch 90 dbms.jvm.additional=-XX:+UnlockExperimentalVMOptions 91 dbms.jvm.additional=-XX:+TrustFinalNonStaticFields 92 dbms.jvm.additional=-XX:+DisableExplicitGC 93 dbms.jvm.additional=-Djdk.tls.ephemeralDHKeySize=2048 94 dbms.jvm.additional=-Djdk.tls.rejectClientInitiatedRenegotiation=true 95 dbms.jvm.additional=-Dunsupported.dbms.udc.source=tarball 96 97 # Usage Data Collector 98 dbms.udc.enabled=${boolToString cfg.udc.enable} 99 100 # Extra Configuration 101 ${cfg.extraServerConfig} 102 ''; 103 104in { 105 106 ###### interface 107 108 options.services.neo4j = { 109 110 enable = mkOption { 111 type = types.bool; 112 default = false; 113 description = '' 114 Whether to enable Neo4j Community Edition. 115 ''; 116 }; 117 118 allowUpgrade = mkOption { 119 type = types.bool; 120 default = false; 121 description = '' 122 Allow upgrade of Neo4j database files from an older version. 123 ''; 124 }; 125 126 constrainLoadCsv = mkOption { 127 type = types.bool; 128 default = true; 129 description = '' 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 134 subdirectories. 135 </para> 136 <para> 137 Setting this option to <literal>false</literal> introduces 138 possible security problems. 139 ''; 140 }; 141 142 defaultListenAddress = mkOption { 143 type = types.str; 144 default = "127.0.0.1"; 145 description = '' 146 Default network interface to listen for incoming connections. To 147 listen for connections on all interfaces, use "0.0.0.0". 148 </para> 149 <para> 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. 154 ''; 155 }; 156 157 extraServerConfig = mkOption { 158 type = types.lines; 159 default = ""; 160 description = '' 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. 164 ''; 165 }; 166 167 package = mkOption { 168 type = types.package; 169 default = pkgs.neo4j; 170 defaultText = "pkgs.neo4j"; 171 description = '' 172 Neo4j package to use. 173 ''; 174 }; 175 176 readOnly = mkOption { 177 type = types.bool; 178 default = false; 179 description = '' 180 Only allow read operations from this Neo4j instance. 181 ''; 182 }; 183 184 workerCount = mkOption { 185 type = types.ints.between 0 44738; 186 default = 0; 187 description = '' 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. 191 ''; 192 }; 193 194 bolt = { 195 enable = mkOption { 196 type = types.bool; 197 default = true; 198 description = '' 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). 202 ''; 203 }; 204 205 listenAddress = mkOption { 206 type = types.str; 207 default = ":7687"; 208 description = '' 209 Neo4j listen address for BOLT traffic. The listen address is 210 expressed in the format <literal>&lt;ip-address&gt;:&lt;port-number&gt;</literal>. 211 ''; 212 }; 213 214 sslPolicy = mkOption { 215 type = types.str; 216 default = "legacy"; 217 description = '' 218 Neo4j SSL policy for BOLT traffic. 219 </para> 220 <para> 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. 226 </para> 227 <para> 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>. 231 ''; 232 }; 233 234 tlsLevel = mkOption { 235 type = types.enum [ "REQUIRED" "OPTIONAL" "DISABLED" ]; 236 default = "OPTIONAL"; 237 description = '' 238 SSL/TSL requirement level for BOLT traffic. 239 ''; 240 }; 241 }; 242 243 directories = { 244 certificates = mkOption { 245 type = types.path; 246 default = "${cfg.directories.home}/certificates"; 247 description = '' 248 Directory for storing certificates to be used by Neo4j for 249 TLS connections. 250 </para> 251 <para> 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>. 255 </para> 256 <para> 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. 265 ''; 266 }; 267 268 data = mkOption { 269 type = types.path; 270 default = "${cfg.directories.home}/data"; 271 description = '' 272 Path of the data directory. You must not configure more than one 273 Neo4j installation to use the same data directory. 274 </para> 275 <para> 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>. 279 ''; 280 }; 281 282 home = mkOption { 283 type = types.path; 284 default = "/var/lib/neo4j"; 285 description = '' 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>. 290 ''; 291 }; 292 293 imports = mkOption { 294 type = types.path; 295 default = "${cfg.directories.home}/import"; 296 description = '' 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>. 301 </para> 302 <para> 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>. 306 ''; 307 }; 308 309 plugins = mkOption { 310 type = types.path; 311 default = "${cfg.directories.home}/plugins"; 312 description = '' 313 Path of the database plugin directory. Compiled Java JAR files that 314 contain database procedures will be loaded if they are placed in 315 this directory. 316 </para> 317 <para> 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>. 321 ''; 322 }; 323 }; 324 325 http = { 326 enable = mkOption { 327 type = types.bool; 328 default = true; 329 description = '' 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 335 firewall. 336 ''; 337 }; 338 339 listenAddress = mkOption { 340 type = types.str; 341 default = ":7474"; 342 description = '' 343 Neo4j listen address for HTTP traffic. The listen address is 344 expressed in the format <literal>&lt;ip-address&gt;:&lt;port-number&gt;</literal>. 345 ''; 346 }; 347 }; 348 349 https = { 350 enable = mkOption { 351 type = types.bool; 352 default = true; 353 description = '' 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). 357 ''; 358 }; 359 360 listenAddress = mkOption { 361 type = types.str; 362 default = ":7473"; 363 description = '' 364 Neo4j listen address for HTTPS traffic. The listen address is 365 expressed in the format <literal>&lt;ip-address&gt;:&lt;port-number&gt;</literal>. 366 ''; 367 }; 368 369 sslPolicy = mkOption { 370 type = types.str; 371 default = "legacy"; 372 description = '' 373 Neo4j SSL policy for HTTPS traffic. 374 </para> 375 <para> 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. 381 ''; 382 }; 383 }; 384 385 shell = { 386 enable = mkOption { 387 type = types.bool; 388 default = false; 389 description = '' 390 Enable a remote shell server which Neo4j Shell clients can log in to. 391 Only applicable to <command>neo4j-shell</command>. 392 ''; 393 }; 394 }; 395 396 ssl.policies = mkOption { 397 type = with types; attrsOf (submodule ({ name, config, options, ... }: { 398 options = { 399 400 allowKeyGeneration = mkOption { 401 type = types.bool; 402 default = false; 403 description = '' 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. 408 </para> 409 <para> 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. 413 </para> 414 <para> 415 Keys should in general be generated and distributed offline by a 416 trusted certificate authority and not by utilizing this mode. 417 ''; 418 }; 419 420 baseDirectory = mkOption { 421 type = types.path; 422 default = "${cfg.directories.certificates}/${name}"; 423 description = '' 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>. 429 </para> 430 <para> 431 It is also possible to override each individual 432 configuration with absolute paths. See the 433 <option>privateKey</option> and <option>publicCertificate</option> 434 policy options. 435 ''; 436 }; 437 438 ciphers = mkOption { 439 type = types.nullOr (types.listOf types.str); 440 default = null; 441 description = '' 442 Restrict the allowed ciphers of this policy to those defined 443 here. The default ciphers are those of the JVM platform. 444 ''; 445 }; 446 447 clientAuth = mkOption { 448 type = types.enum [ "NONE" "OPTIONAL" "REQUIRE" ]; 449 default = "REQUIRE"; 450 description = '' 451 The client authentication stance for this policy. 452 ''; 453 }; 454 455 privateKey = mkOption { 456 type = types.str; 457 default = "private.key"; 458 description = '' 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. 462 ''; 463 }; 464 465 publicCertificate = mkOption { 466 type = types.str; 467 default = "public.crt"; 468 description = '' 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. 473 </para> 474 <para> 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. 478 ''; 479 }; 480 481 revokedDir = mkOption { 482 type = types.path; 483 default = "${config.baseDirectory}/revoked"; 484 description = '' 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>. 493 ''; 494 }; 495 496 tlsVersions = mkOption { 497 type = types.listOf types.str; 498 default = [ "TLSv1.2" ]; 499 description = '' 500 Restrict the TLS protocol versions of this policy to those 501 defined here. 502 ''; 503 }; 504 505 trustAll = mkOption { 506 type = types.bool; 507 default = false; 508 description = '' 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 512 no security. 513 ''; 514 }; 515 516 trustedDir = mkOption { 517 type = types.path; 518 default = "${config.baseDirectory}/trusted"; 519 description = '' 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>. 528 </para> 529 <para> 530 The public certificate as defined by 531 <option>publicCertificate</option> is required to be duplicated 532 to this directory. 533 ''; 534 }; 535 536 directoriesToCreate = mkOption { 537 type = types.listOf types.path; 538 internal = true; 539 readOnly = true; 540 description = '' 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 544 default value. 545 ''; 546 }; 547 548 }; 549 550 config.directoriesToCreate = optionals 551 (certDirOpt.highestPrio >= 1500 && options.baseDirectory.highestPrio >= 1500) 552 (map (opt: opt.value) (filter isDefaultPathOption (attrValues options))); 553 554 })); 555 default = {}; 556 description = '' 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 560 on Neo4j's 561 <link xlink:href="https://neo4j.com/docs/operations-manual/current/security/ssl-framework/">SSL Framework</link> 562 for further details. 563 ''; 564 }; 565 566 udc = { 567 enable = mkOption { 568 type = types.bool; 569 default = false; 570 description = '' 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. 575 ''; 576 }; 577 }; 578 579 }; 580 581 ###### implementation 582 583 config = 584 let 585 # Assertion helpers 586 policyNameList = attrNames cfg.ssl.policies; 587 validPolicyNameList = [ "legacy" ] ++ policyNameList; 588 validPolicyNameString = concatStringsSep ", " validPolicyNameList; 589 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); 593 in 594 595 mkIf cfg.enable { 596 assertions = [ 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}"; } 603 ]; 604 605 systemd.services.neo4j = { 606 description = "Neo4j Daemon"; 607 wantedBy = [ "multi-user.target" ]; 608 after = [ "network.target" ]; 609 environment = { 610 NEO4J_HOME = "${cfg.package}/share/neo4j"; 611 NEO4J_CONF = "${cfg.directories.home}/conf"; 612 }; 613 serviceConfig = { 614 ExecStart = "${cfg.package}/bin/neo4j console"; 615 User = "neo4j"; 616 PermissionsStartOnly = true; 617 LimitNOFILE = 40000; 618 }; 619 620 preStart = '' 621 # Directories Setup 622 # Always ensure home exists with nested conf, logs directories. 623 mkdir -m 0700 -p ${cfg.directories.home}/{conf,logs} 624 625 # Create other sub-directories and policy directories that have been left at their default. 626 ${concatMapStringsSep "\n" ( 627 dir: '' 628 mkdir -m 0700 -p ${dir} 629 '') (defaultDirectoriesToCreate ++ policyDirectoriesToCreate)} 630 631 # Place the configuration where Neo4j can find it. 632 ln -fs ${serverConfig} ${cfg.directories.home}/conf/neo4j.conf 633 634 # Ensure neo4j user ownership 635 chown -R neo4j ${cfg.directories.home} 636 ''; 637 }; 638 639 environment.systemPackages = [ cfg.package ]; 640 641 users.users = singleton { 642 name = "neo4j"; 643 uid = config.ids.uids.neo4j; 644 description = "Neo4j daemon user"; 645 home = cfg.directories.home; 646 }; 647 }; 648 649 meta = { 650 maintainers = with lib.maintainers; [ patternspandemic ]; 651 }; 652}