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