at 21.11-pre 25 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 imports = [ 107 (mkRenamedOptionModule [ "services" "neo4j" "host" ] [ "services" "neo4j" "defaultListenAddress" ]) 108 (mkRenamedOptionModule [ "services" "neo4j" "listenAddress" ] [ "services" "neo4j" "defaultListenAddress" ]) 109 (mkRenamedOptionModule [ "services" "neo4j" "enableBolt" ] [ "services" "neo4j" "bolt" "enable" ]) 110 (mkRenamedOptionModule [ "services" "neo4j" "enableHttps" ] [ "services" "neo4j" "https" "enable" ]) 111 (mkRenamedOptionModule [ "services" "neo4j" "certDir" ] [ "services" "neo4j" "directories" "certificates" ]) 112 (mkRenamedOptionModule [ "services" "neo4j" "dataDir" ] [ "services" "neo4j" "directories" "home" ]) 113 (mkRemovedOptionModule [ "services" "neo4j" "port" ] "Use services.neo4j.http.listenAddress instead.") 114 (mkRemovedOptionModule [ "services" "neo4j" "boltPort" ] "Use services.neo4j.bolt.listenAddress instead.") 115 (mkRemovedOptionModule [ "services" "neo4j" "httpsPort" ] "Use services.neo4j.https.listenAddress instead.") 116 ]; 117 118 ###### interface 119 120 options.services.neo4j = { 121 122 enable = mkOption { 123 type = types.bool; 124 default = false; 125 description = '' 126 Whether to enable Neo4j Community Edition. 127 ''; 128 }; 129 130 allowUpgrade = mkOption { 131 type = types.bool; 132 default = false; 133 description = '' 134 Allow upgrade of Neo4j database files from an older version. 135 ''; 136 }; 137 138 constrainLoadCsv = mkOption { 139 type = types.bool; 140 default = true; 141 description = '' 142 Sets the root directory for file URLs used with the Cypher 143 <literal>LOAD CSV</literal> clause to be that defined by 144 <option>directories.imports</option>. It restricts 145 access to only those files within that directory and its 146 subdirectories. 147 </para> 148 <para> 149 Setting this option to <literal>false</literal> introduces 150 possible security problems. 151 ''; 152 }; 153 154 defaultListenAddress = mkOption { 155 type = types.str; 156 default = "127.0.0.1"; 157 description = '' 158 Default network interface to listen for incoming connections. To 159 listen for connections on all interfaces, use "0.0.0.0". 160 </para> 161 <para> 162 Specifies the default IP address and address part of connector 163 specific <option>listenAddress</option> options. To bind specific 164 connectors to a specific network interfaces, specify the entire 165 <option>listenAddress</option> option for that connector. 166 ''; 167 }; 168 169 extraServerConfig = mkOption { 170 type = types.lines; 171 default = ""; 172 description = '' 173 Extra configuration for Neo4j Community server. Refer to the 174 <link xlink:href="https://neo4j.com/docs/operations-manual/current/reference/configuration-settings/">complete reference</link> 175 of Neo4j configuration settings. 176 ''; 177 }; 178 179 package = mkOption { 180 type = types.package; 181 default = pkgs.neo4j; 182 defaultText = "pkgs.neo4j"; 183 description = '' 184 Neo4j package to use. 185 ''; 186 }; 187 188 readOnly = mkOption { 189 type = types.bool; 190 default = false; 191 description = '' 192 Only allow read operations from this Neo4j instance. 193 ''; 194 }; 195 196 workerCount = mkOption { 197 type = types.ints.between 0 44738; 198 default = 0; 199 description = '' 200 Number of Neo4j worker threads, where the default of 201 <literal>0</literal> indicates a worker count equal to the number of 202 available processors. 203 ''; 204 }; 205 206 bolt = { 207 enable = mkOption { 208 type = types.bool; 209 default = true; 210 description = '' 211 Enable the BOLT connector for Neo4j. Setting this option to 212 <literal>false</literal> will stop Neo4j from listening for incoming 213 connections on the BOLT port (7687 by default). 214 ''; 215 }; 216 217 listenAddress = mkOption { 218 type = types.str; 219 default = ":7687"; 220 description = '' 221 Neo4j listen address for BOLT traffic. The listen address is 222 expressed in the format <literal>&lt;ip-address&gt;:&lt;port-number&gt;</literal>. 223 ''; 224 }; 225 226 sslPolicy = mkOption { 227 type = types.str; 228 default = "legacy"; 229 description = '' 230 Neo4j SSL policy for BOLT traffic. 231 </para> 232 <para> 233 The legacy policy is a special policy which is not defined in 234 the policy configuration section, but rather derives from 235 <option>directories.certificates</option> and 236 associated files (by default: <filename>neo4j.key</filename> and 237 <filename>neo4j.cert</filename>). Its use will be deprecated. 238 </para> 239 <para> 240 Note: This connector must be configured to support/require 241 SSL/TLS for the legacy policy to actually be utilized. See 242 <option>bolt.tlsLevel</option>. 243 ''; 244 }; 245 246 tlsLevel = mkOption { 247 type = types.enum [ "REQUIRED" "OPTIONAL" "DISABLED" ]; 248 default = "OPTIONAL"; 249 description = '' 250 SSL/TSL requirement level for BOLT traffic. 251 ''; 252 }; 253 }; 254 255 directories = { 256 certificates = mkOption { 257 type = types.path; 258 default = "${cfg.directories.home}/certificates"; 259 description = '' 260 Directory for storing certificates to be used by Neo4j for 261 TLS connections. 262 </para> 263 <para> 264 When setting this directory to something other than its default, 265 ensure the directory's existence, and that read/write permissions are 266 given to the Neo4j daemon user <literal>neo4j</literal>. 267 </para> 268 <para> 269 Note that changing this directory from its default will prevent 270 the directory structure required for each SSL policy from being 271 automatically generated. A policy's directory structure as defined by 272 its <option>baseDirectory</option>,<option>revokedDir</option> and 273 <option>trustedDir</option> must then be setup manually. The 274 existence of these directories is mandatory, as well as the presence 275 of the certificate file and the private key. Ensure the correct 276 permissions are set on these directories and files. 277 ''; 278 }; 279 280 data = mkOption { 281 type = types.path; 282 default = "${cfg.directories.home}/data"; 283 description = '' 284 Path of the data directory. You must not configure more than one 285 Neo4j installation to use the same data directory. 286 </para> 287 <para> 288 When setting this directory to something other than its default, 289 ensure the directory's existence, and that read/write permissions are 290 given to the Neo4j daemon user <literal>neo4j</literal>. 291 ''; 292 }; 293 294 home = mkOption { 295 type = types.path; 296 default = "/var/lib/neo4j"; 297 description = '' 298 Path of the Neo4j home directory. Other default directories are 299 subdirectories of this path. This directory will be created if 300 non-existent, and its ownership will be <command>chown</command> to 301 the Neo4j daemon user <literal>neo4j</literal>. 302 ''; 303 }; 304 305 imports = mkOption { 306 type = types.path; 307 default = "${cfg.directories.home}/import"; 308 description = '' 309 The root directory for file URLs used with the Cypher 310 <literal>LOAD CSV</literal> clause. Only meaningful when 311 <option>constrainLoadCvs</option> is set to 312 <literal>true</literal>. 313 </para> 314 <para> 315 When setting this directory to something other than its default, 316 ensure the directory's existence, and that read permission is 317 given to the Neo4j daemon user <literal>neo4j</literal>. 318 ''; 319 }; 320 321 plugins = mkOption { 322 type = types.path; 323 default = "${cfg.directories.home}/plugins"; 324 description = '' 325 Path of the database plugin directory. Compiled Java JAR files that 326 contain database procedures will be loaded if they are placed in 327 this directory. 328 </para> 329 <para> 330 When setting this directory to something other than its default, 331 ensure the directory's existence, and that read permission is 332 given to the Neo4j daemon user <literal>neo4j</literal>. 333 ''; 334 }; 335 }; 336 337 http = { 338 enable = mkOption { 339 type = types.bool; 340 default = true; 341 description = '' 342 The HTTP connector is required for Neo4j, and cannot be disabled. 343 Setting this option to <literal>false</literal> will force the HTTP 344 connector's <option>listenAddress</option> to the loopback 345 interface to prevent connection of remote clients. To prevent all 346 clients from connecting, block the HTTP port (7474 by default) by 347 firewall. 348 ''; 349 }; 350 351 listenAddress = mkOption { 352 type = types.str; 353 default = ":7474"; 354 description = '' 355 Neo4j listen address for HTTP traffic. The listen address is 356 expressed in the format <literal>&lt;ip-address&gt;:&lt;port-number&gt;</literal>. 357 ''; 358 }; 359 }; 360 361 https = { 362 enable = mkOption { 363 type = types.bool; 364 default = true; 365 description = '' 366 Enable the HTTPS connector for Neo4j. Setting this option to 367 <literal>false</literal> will stop Neo4j from listening for incoming 368 connections on the HTTPS port (7473 by default). 369 ''; 370 }; 371 372 listenAddress = mkOption { 373 type = types.str; 374 default = ":7473"; 375 description = '' 376 Neo4j listen address for HTTPS traffic. The listen address is 377 expressed in the format <literal>&lt;ip-address&gt;:&lt;port-number&gt;</literal>. 378 ''; 379 }; 380 381 sslPolicy = mkOption { 382 type = types.str; 383 default = "legacy"; 384 description = '' 385 Neo4j SSL policy for HTTPS traffic. 386 </para> 387 <para> 388 The legacy policy is a special policy which is not defined in the 389 policy configuration section, but rather derives from 390 <option>directories.certificates</option> and 391 associated files (by default: <filename>neo4j.key</filename> and 392 <filename>neo4j.cert</filename>). Its use will be deprecated. 393 ''; 394 }; 395 }; 396 397 shell = { 398 enable = mkOption { 399 type = types.bool; 400 default = false; 401 description = '' 402 Enable a remote shell server which Neo4j Shell clients can log in to. 403 Only applicable to <command>neo4j-shell</command>. 404 ''; 405 }; 406 }; 407 408 ssl.policies = mkOption { 409 type = with types; attrsOf (submodule ({ name, config, options, ... }: { 410 options = { 411 412 allowKeyGeneration = mkOption { 413 type = types.bool; 414 default = false; 415 description = '' 416 Allows the generation of a private key and associated self-signed 417 certificate. Only performed when both objects cannot be found for 418 this policy. It is recommended to turn this off again after keys 419 have been generated. 420 </para> 421 <para> 422 The public certificate is required to be duplicated to the 423 directory holding trusted certificates as defined by the 424 <option>trustedDir</option> option. 425 </para> 426 <para> 427 Keys should in general be generated and distributed offline by a 428 trusted certificate authority and not by utilizing this mode. 429 ''; 430 }; 431 432 baseDirectory = mkOption { 433 type = types.path; 434 default = "${cfg.directories.certificates}/${name}"; 435 description = '' 436 The mandatory base directory for cryptographic objects of this 437 policy. This path is only automatically generated when this 438 option as well as <option>directories.certificates</option> are 439 left at their default. Ensure read/write permissions are given 440 to the Neo4j daemon user <literal>neo4j</literal>. 441 </para> 442 <para> 443 It is also possible to override each individual 444 configuration with absolute paths. See the 445 <option>privateKey</option> and <option>publicCertificate</option> 446 policy options. 447 ''; 448 }; 449 450 ciphers = mkOption { 451 type = types.nullOr (types.listOf types.str); 452 default = null; 453 description = '' 454 Restrict the allowed ciphers of this policy to those defined 455 here. The default ciphers are those of the JVM platform. 456 ''; 457 }; 458 459 clientAuth = mkOption { 460 type = types.enum [ "NONE" "OPTIONAL" "REQUIRE" ]; 461 default = "REQUIRE"; 462 description = '' 463 The client authentication stance for this policy. 464 ''; 465 }; 466 467 privateKey = mkOption { 468 type = types.str; 469 default = "private.key"; 470 description = '' 471 The name of private PKCS #8 key file for this policy to be found 472 in the <option>baseDirectory</option>, or the absolute path to 473 the key file. It is mandatory that a key can be found or generated. 474 ''; 475 }; 476 477 publicCertificate = mkOption { 478 type = types.str; 479 default = "public.crt"; 480 description = '' 481 The name of public X.509 certificate (chain) file in PEM format 482 for this policy to be found in the <option>baseDirectory</option>, 483 or the absolute path to the certificate file. It is mandatory 484 that a certificate can be found or generated. 485 </para> 486 <para> 487 The public certificate is required to be duplicated to the 488 directory holding trusted certificates as defined by the 489 <option>trustedDir</option> option. 490 ''; 491 }; 492 493 revokedDir = mkOption { 494 type = types.path; 495 default = "${config.baseDirectory}/revoked"; 496 description = '' 497 Path to directory of CRLs (Certificate Revocation Lists) in 498 PEM format. Must be an absolute path. The existence of this 499 directory is mandatory and will need to be created manually when: 500 setting this option to something other than its default; setting 501 either this policy's <option>baseDirectory</option> or 502 <option>directories.certificates</option> to something other than 503 their default. Ensure read/write permissions are given to the 504 Neo4j daemon user <literal>neo4j</literal>. 505 ''; 506 }; 507 508 tlsVersions = mkOption { 509 type = types.listOf types.str; 510 default = [ "TLSv1.2" ]; 511 description = '' 512 Restrict the TLS protocol versions of this policy to those 513 defined here. 514 ''; 515 }; 516 517 trustAll = mkOption { 518 type = types.bool; 519 default = false; 520 description = '' 521 Makes this policy trust all remote parties. Enabling this is not 522 recommended and the policy's trusted directory will be ignored. 523 Use of this mode is discouraged. It would offer encryption but 524 no security. 525 ''; 526 }; 527 528 trustedDir = mkOption { 529 type = types.path; 530 default = "${config.baseDirectory}/trusted"; 531 description = '' 532 Path to directory of X.509 certificates in PEM format for 533 trusted parties. Must be an absolute path. The existence of this 534 directory is mandatory and will need to be created manually when: 535 setting this option to something other than its default; setting 536 either this policy's <option>baseDirectory</option> or 537 <option>directories.certificates</option> to something other than 538 their default. Ensure read/write permissions are given to the 539 Neo4j daemon user <literal>neo4j</literal>. 540 </para> 541 <para> 542 The public certificate as defined by 543 <option>publicCertificate</option> is required to be duplicated 544 to this directory. 545 ''; 546 }; 547 548 directoriesToCreate = mkOption { 549 type = types.listOf types.path; 550 internal = true; 551 readOnly = true; 552 description = '' 553 Directories of this policy that will be created automatically 554 when the certificates directory is left at its default value. 555 This includes all options of type path that are left at their 556 default value. 557 ''; 558 }; 559 560 }; 561 562 config.directoriesToCreate = optionals 563 (certDirOpt.highestPrio >= 1500 && options.baseDirectory.highestPrio >= 1500) 564 (map (opt: opt.value) (filter isDefaultPathOption (attrValues options))); 565 566 })); 567 default = {}; 568 description = '' 569 Defines the SSL policies for use with Neo4j connectors. Each attribute 570 of this set defines a policy, with the attribute name defining the name 571 of the policy and its namespace. Refer to the operations manual section 572 on Neo4j's 573 <link xlink:href="https://neo4j.com/docs/operations-manual/current/security/ssl-framework/">SSL Framework</link> 574 for further details. 575 ''; 576 }; 577 578 udc = { 579 enable = mkOption { 580 type = types.bool; 581 default = false; 582 description = '' 583 Enable the Usage Data Collector which Neo4j uses to collect usage 584 data. Refer to the operations manual section on the 585 <link xlink:href="https://neo4j.com/docs/operations-manual/current/configuration/usage-data-collector/">Usage Data Collector</link> 586 for more information. 587 ''; 588 }; 589 }; 590 591 }; 592 593 ###### implementation 594 595 config = 596 let 597 # Assertion helpers 598 policyNameList = attrNames cfg.ssl.policies; 599 validPolicyNameList = [ "legacy" ] ++ policyNameList; 600 validPolicyNameString = concatStringsSep ", " validPolicyNameList; 601 602 # Capture various directories left at their default so they can be created. 603 defaultDirectoriesToCreate = map (opt: opt.value) (filter isDefaultPathOption (attrValues options.services.neo4j.directories)); 604 policyDirectoriesToCreate = concatMap (pol: pol.directoriesToCreate) (attrValues cfg.ssl.policies); 605 in 606 607 mkIf cfg.enable { 608 assertions = [ 609 { assertion = !elem "legacy" policyNameList; 610 message = "The policy 'legacy' is special to Neo4j, and its name is reserved."; } 611 { assertion = elem cfg.bolt.sslPolicy validPolicyNameList; 612 message = "Invalid policy assigned: `services.neo4j.bolt.sslPolicy = \"${cfg.bolt.sslPolicy}\"`, defined policies are: ${validPolicyNameString}"; } 613 { assertion = elem cfg.https.sslPolicy validPolicyNameList; 614 message = "Invalid policy assigned: `services.neo4j.https.sslPolicy = \"${cfg.https.sslPolicy}\"`, defined policies are: ${validPolicyNameString}"; } 615 ]; 616 617 systemd.services.neo4j = { 618 description = "Neo4j Daemon"; 619 wantedBy = [ "multi-user.target" ]; 620 after = [ "network.target" ]; 621 environment = { 622 NEO4J_HOME = "${cfg.package}/share/neo4j"; 623 NEO4J_CONF = "${cfg.directories.home}/conf"; 624 }; 625 serviceConfig = { 626 ExecStart = "${cfg.package}/bin/neo4j console"; 627 User = "neo4j"; 628 PermissionsStartOnly = true; 629 LimitNOFILE = 40000; 630 }; 631 632 preStart = '' 633 # Directories Setup 634 # Always ensure home exists with nested conf, logs directories. 635 mkdir -m 0700 -p ${cfg.directories.home}/{conf,logs} 636 637 # Create other sub-directories and policy directories that have been left at their default. 638 ${concatMapStringsSep "\n" ( 639 dir: '' 640 mkdir -m 0700 -p ${dir} 641 '') (defaultDirectoriesToCreate ++ policyDirectoriesToCreate)} 642 643 # Place the configuration where Neo4j can find it. 644 ln -fs ${serverConfig} ${cfg.directories.home}/conf/neo4j.conf 645 646 # Ensure neo4j user ownership 647 chown -R neo4j ${cfg.directories.home} 648 ''; 649 }; 650 651 environment.systemPackages = [ cfg.package ]; 652 653 users.users.neo4j = { 654 uid = config.ids.uids.neo4j; 655 description = "Neo4j daemon user"; 656 home = cfg.directories.home; 657 }; 658 }; 659 660 meta = { 661 maintainers = with lib.maintainers; [ patternspandemic ]; 662 }; 663}