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