at master 26 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7 8# TODO: test configuration when building nixexpr (use -t parameter) 9# TODO: support sqlite3 (it's deprecate?) and mysql 10 11let 12 inherit (lib) 13 concatStringsSep 14 literalExpression 15 mapAttrsToList 16 mkIf 17 mkOption 18 optional 19 optionalString 20 types 21 ; 22 libDir = "/var/lib/bacula"; 23 24 yes_no = bool: if bool then "yes" else "no"; 25 tls_conf = 26 tls_cfg: 27 optionalString tls_cfg.enable ( 28 concatStringsSep "\n" ( 29 [ "TLS Enable = yes;" ] 30 ++ optional (tls_cfg.require != null) "TLS Require = ${yes_no tls_cfg.require};" 31 ++ optional (tls_cfg.certificate != null) ''TLS Certificate = "${tls_cfg.certificate}";'' 32 ++ [ ''TLS Key = "${tls_cfg.key}";'' ] 33 ++ optional (tls_cfg.verifyPeer != null) "TLS Verify Peer = ${yes_no tls_cfg.verifyPeer};" 34 ++ optional ( 35 tls_cfg.allowedCN != [ ] 36 ) "TLS Allowed CN = ${concatStringsSep " " (tls_cfg.allowedCN)};" 37 ++ optional ( 38 tls_cfg.caCertificateFile != null 39 ) ''TLS CA Certificate File = "${tls_cfg.caCertificateFile}";'' 40 ) 41 ); 42 43 fd_cfg = config.services.bacula-fd; 44 fd_conf = pkgs.writeText "bacula-fd.conf" '' 45 Client { 46 Name = "${fd_cfg.name}"; 47 FDPort = ${toString fd_cfg.port}; 48 WorkingDirectory = ${libDir}; 49 Pid Directory = /run; 50 ${fd_cfg.extraClientConfig} 51 ${tls_conf fd_cfg.tls} 52 } 53 54 ${concatStringsSep "\n" ( 55 mapAttrsToList (name: value: '' 56 Director { 57 Name = "${name}"; 58 Password = ${value.password}; 59 Monitor = ${value.monitor}; 60 ${tls_conf value.tls} 61 } 62 '') fd_cfg.director 63 )} 64 65 Messages { 66 Name = Standard; 67 syslog = all, !skipped, !restored 68 ${fd_cfg.extraMessagesConfig} 69 } 70 ''; 71 72 sd_cfg = config.services.bacula-sd; 73 sd_conf = pkgs.writeText "bacula-sd.conf" '' 74 Storage { 75 Name = "${sd_cfg.name}"; 76 SDPort = ${toString sd_cfg.port}; 77 WorkingDirectory = ${libDir}; 78 Pid Directory = /run; 79 ${sd_cfg.extraStorageConfig} 80 ${tls_conf sd_cfg.tls} 81 } 82 83 ${concatStringsSep "\n" ( 84 mapAttrsToList (name: value: '' 85 Autochanger { 86 Name = "${name}"; 87 Device = ${concatStringsSep ", " (map (a: "\"${a}\"") value.devices)}; 88 Changer Device = ${value.changerDevice}; 89 Changer Command = ${value.changerCommand}; 90 ${value.extraAutochangerConfig} 91 } 92 '') sd_cfg.autochanger 93 )} 94 95 ${concatStringsSep "\n" ( 96 mapAttrsToList (name: value: '' 97 Device { 98 Name = "${name}"; 99 Archive Device = ${value.archiveDevice}; 100 Media Type = ${value.mediaType}; 101 ${value.extraDeviceConfig} 102 } 103 '') sd_cfg.device 104 )} 105 106 ${concatStringsSep "\n" ( 107 mapAttrsToList (name: value: '' 108 Director { 109 Name = "${name}"; 110 Password = ${value.password}; 111 Monitor = ${value.monitor}; 112 ${tls_conf value.tls} 113 } 114 '') sd_cfg.director 115 )} 116 117 Messages { 118 Name = Standard; 119 syslog = all, !skipped, !restored 120 ${sd_cfg.extraMessagesConfig} 121 } 122 ''; 123 124 dir_cfg = config.services.bacula-dir; 125 dir_conf = pkgs.writeText "bacula-dir.conf" '' 126 Director { 127 Name = "${dir_cfg.name}"; 128 Password = ${dir_cfg.password}; 129 DirPort = ${toString dir_cfg.port}; 130 Working Directory = ${libDir}; 131 Pid Directory = /run/; 132 QueryFile = ${pkgs.bacula}/etc/query.sql; 133 ${tls_conf dir_cfg.tls} 134 ${dir_cfg.extraDirectorConfig} 135 } 136 137 Catalog { 138 Name = PostgreSQL; 139 dbname = bacula; 140 user = bacula; 141 } 142 143 Messages { 144 Name = Standard; 145 syslog = all, !skipped, !restored 146 ${dir_cfg.extraMessagesConfig} 147 } 148 149 ${dir_cfg.extraConfig} 150 ''; 151 152 linkOption = 153 name: destination: "[${name}](#opt-${builtins.replaceStrings [ "<" ">" ] [ "_" "_" ] destination})"; 154 tlsLink = 155 destination: submodulePath: 156 linkOption "${submodulePath}.${destination}" "${submodulePath}.${destination}"; 157 158 tlsOptions = 159 submodulePath: 160 { ... }: 161 { 162 options = { 163 enable = mkOption { 164 type = types.bool; 165 default = false; 166 description = '' 167 Specifies if TLS should be enabled. 168 If this set to `false` TLS will be completely disabled, even if ${tlsLink "tls.require" submodulePath} is true. 169 ''; 170 }; 171 require = mkOption { 172 type = types.nullOr types.bool; 173 default = null; 174 description = '' 175 Require TLS or TLS-PSK encryption. 176 This directive is ignored unless one of ${tlsLink "tls.enable" submodulePath} is true or TLS PSK Enable is set to `yes`. 177 If TLS is not required while TLS or TLS-PSK are enabled, then the Bacula component 178 will connect with other components either with or without TLS or TLS-PSK 179 180 If ${tlsLink "tls.enable" submodulePath} or TLS-PSK is enabled and TLS is required, then the Bacula 181 component will refuse any connection request that does not use TLS. 182 ''; 183 }; 184 certificate = mkOption { 185 type = types.nullOr types.path; 186 default = null; 187 description = '' 188 The full path to the PEM encoded TLS certificate. 189 It will be used as either a client or server certificate, 190 depending on the connection direction. 191 This directive is required in a server context, but it may 192 not be specified in a client context if ${tlsLink "tls.verifyPeer" submodulePath} is 193 `false` in the corresponding server context. 194 ''; 195 }; 196 key = mkOption { 197 type = types.path; 198 description = '' 199 The path of a PEM encoded TLS private key. 200 It must correspond to the TLS certificate. 201 ''; 202 }; 203 verifyPeer = mkOption { 204 type = types.nullOr types.bool; 205 default = null; 206 description = '' 207 Verify peer certificate. 208 Instructs server to request and verify the client's X.509 certificate. 209 Any client certificate signed by a known-CA will be accepted. 210 Additionally, the client's X509 certificate Common Name must meet the value of the Address directive. 211 If ${tlsLink "tls.allowedCN" submodulePath} is used, 212 the client's x509 certificate Common Name must also correspond to 213 one of the CN specified in the ${tlsLink "tls.allowedCN" submodulePath} directive. 214 This directive is valid only for a server and not in client context. 215 216 Standard from Bacula is `true`. 217 ''; 218 }; 219 allowedCN = mkOption { 220 type = types.listOf types.str; 221 default = [ ]; 222 description = '' 223 Common name attribute of allowed peer certificates. 224 This directive is valid for a server and in a client context. 225 If this directive is specified, the peer certificate will be verified against this list. 226 In the case this directive is configured on a server side, the allowed 227 CN list will not be checked if ${tlsLink "tls.verifyPeer" submodulePath} is false. 228 ''; 229 }; 230 caCertificateFile = mkOption { 231 type = types.nullOr types.path; 232 default = null; 233 description = '' 234 The path specifying a PEM encoded TLS CA certificate(s). 235 Multiple certificates are permitted in the file. 236 One of TLS CA Certificate File or TLS CA Certificate Dir are required in a server context, unless 237 ${tlsLink "tls.verifyPeer" submodulePath} is false, and are always required in a client context. 238 ''; 239 }; 240 }; 241 }; 242 243 directorOptions = 244 submodulePath: 245 { ... }: 246 { 247 options = { 248 password = mkOption { 249 type = types.str; 250 # TODO: required? 251 description = '' 252 Specifies the password that must be supplied for the default Bacula 253 Console to be authorized. The same password must appear in the 254 Director resource of the Console configuration file. For added 255 security, the password is never passed across the network but instead 256 a challenge response hash code created with the password. This 257 directive is required. If you have either /dev/random or bc on your 258 machine, Bacula will generate a random password during the 259 configuration process, otherwise it will be left blank and you must 260 manually supply it. 261 262 The password is plain text. It is not generated through any special 263 process but as noted above, it is better to use random text for 264 security reasons. 265 ''; 266 }; 267 268 monitor = mkOption { 269 type = types.enum [ 270 "no" 271 "yes" 272 ]; 273 default = "no"; 274 example = "yes"; 275 description = '' 276 If Monitor is set to `no`, this director will have 277 full access to this Storage daemon. If Monitor is set to 278 `yes`, this director will only be able to fetch the 279 current status of this Storage daemon. 280 281 Please note that if this director is being used by a Monitor, we 282 highly recommend to set this directive to yes to avoid serious 283 security problems. 284 ''; 285 }; 286 287 tls = mkOption { 288 type = types.submodule (tlsOptions "${submodulePath}.director.<name>"); 289 description = '' 290 TLS Options for the Director in this Configuration. 291 ''; 292 }; 293 }; 294 }; 295 296 autochangerOptions = 297 { ... }: 298 { 299 options = { 300 changerDevice = mkOption { 301 type = types.str; 302 description = '' 303 The specified name-string must be the generic SCSI device name of the 304 autochanger that corresponds to the normal read/write Archive Device 305 specified in the Device resource. This generic SCSI device name 306 should be specified if you have an autochanger or if you have a 307 standard tape drive and want to use the Alert Command (see below). 308 For example, on Linux systems, for an Archive Device name of 309 `/dev/nst0`, you would specify 310 `/dev/sg0` for the Changer Device name. Depending 311 on your exact configuration, and the number of autochangers or the 312 type of autochanger, what you specify here can vary. This directive 313 is optional. See the Using AutochangersAutochangersChapter chapter of 314 this manual for more details of using this and the following 315 autochanger directives. 316 ''; 317 }; 318 319 changerCommand = mkOption { 320 type = types.str; 321 description = '' 322 The name-string specifies an external program to be called that will 323 automatically change volumes as required by Bacula. Normally, this 324 directive will be specified only in the AutoChanger resource, which 325 is then used for all devices. However, you may also specify the 326 different Changer Command in each Device resource. Most frequently, 327 you will specify the Bacula supplied mtx-changer script as follows: 328 329 `"/path/mtx-changer %c %o %S %a %d"` 330 331 and you will install the mtx on your system (found in the depkgs 332 release). An example of this command is in the default bacula-sd.conf 333 file. For more details on the substitution characters that may be 334 specified to configure your autochanger please see the 335 AutochangersAutochangersChapter chapter of this manual. For FreeBSD 336 users, you might want to see one of the several chio scripts in 337 examples/autochangers. 338 ''; 339 default = "/etc/bacula/mtx-changer %c %o %S %a %d"; 340 }; 341 342 devices = mkOption { 343 description = ""; 344 type = types.listOf types.str; 345 }; 346 347 extraAutochangerConfig = mkOption { 348 default = ""; 349 type = types.lines; 350 description = '' 351 Extra configuration to be passed in Autochanger directive. 352 ''; 353 example = '' 354 355 ''; 356 }; 357 }; 358 }; 359 360 deviceOptions = 361 { ... }: 362 { 363 options = { 364 archiveDevice = mkOption { 365 # TODO: required? 366 type = types.str; 367 description = '' 368 The specified name-string gives the system file name of the storage 369 device managed by this storage daemon. This will usually be the 370 device file name of a removable storage device (tape drive), for 371 example `/dev/nst0` or 372 `/dev/rmt/0mbn`. For a DVD-writer, it will be for 373 example `/dev/hdc`. It may also be a directory name 374 if you are archiving to disk storage. In this case, you must supply 375 the full absolute path to the directory. When specifying a tape 376 device, it is preferable that the "non-rewind" variant of the device 377 file name be given. 378 ''; 379 }; 380 381 mediaType = mkOption { 382 # TODO: required? 383 type = types.str; 384 description = '' 385 The specified name-string names the type of media supported by this 386 device, for example, `DLT7000`. Media type names are 387 arbitrary in that you set them to anything you want, but they must be 388 known to the volume database to keep track of which storage daemons 389 can read which volumes. In general, each different storage type 390 should have a unique Media Type associated with it. The same 391 name-string must appear in the appropriate Storage resource 392 definition in the Director's configuration file. 393 394 Even though the names you assign are arbitrary (i.e. you choose the 395 name you want), you should take care in specifying them because the 396 Media Type is used to determine which storage device Bacula will 397 select during restore. Thus you should probably use the same Media 398 Type specification for all drives where the Media can be freely 399 interchanged. This is not generally an issue if you have a single 400 Storage daemon, but it is with multiple Storage daemons, especially 401 if they have incompatible media. 402 403 For example, if you specify a Media Type of `DDS-4` 404 then during the restore, Bacula will be able to choose any Storage 405 Daemon that handles `DDS-4`. If you have an 406 autochanger, you might want to name the Media Type in a way that is 407 unique to the autochanger, unless you wish to possibly use the 408 Volumes in other drives. You should also ensure to have unique Media 409 Type names if the Media is not compatible between drives. This 410 specification is required for all devices. 411 412 In addition, if you are using disk storage, each Device resource will 413 generally have a different mount point or directory. In order for 414 Bacula to select the correct Device resource, each one must have a 415 unique Media Type. 416 ''; 417 }; 418 419 extraDeviceConfig = mkOption { 420 default = ""; 421 type = types.lines; 422 description = '' 423 Extra configuration to be passed in Device directive. 424 ''; 425 example = '' 426 LabelMedia = yes 427 Random Access = no 428 AutomaticMount = no 429 RemovableMedia = no 430 MaximumOpenWait = 60 431 AlwaysOpen = no 432 ''; 433 }; 434 }; 435 }; 436 437in 438{ 439 options = { 440 services.bacula-fd = { 441 enable = mkOption { 442 type = types.bool; 443 default = false; 444 description = '' 445 Whether to enable the Bacula File Daemon. 446 ''; 447 }; 448 449 name = mkOption { 450 default = "${config.networking.hostName}-fd"; 451 defaultText = literalExpression ''"''${config.networking.hostName}-fd"''; 452 type = types.str; 453 description = '' 454 The client name that must be used by the Director when connecting. 455 Generally, it is a good idea to use a name related to the machine so 456 that error messages can be easily identified if you have multiple 457 Clients. This directive is required. 458 ''; 459 }; 460 461 port = mkOption { 462 default = 9102; 463 type = types.port; 464 description = '' 465 This specifies the port number on which the Client listens for 466 Director connections. It must agree with the FDPort specified in 467 the Client resource of the Director's configuration file. 468 ''; 469 }; 470 471 director = mkOption { 472 default = { }; 473 description = '' 474 This option defines director resources in Bacula File Daemon. 475 ''; 476 type = types.attrsOf (types.submodule (directorOptions "services.bacula-fd")); 477 }; 478 479 tls = mkOption { 480 type = types.submodule (tlsOptions "services.bacula-fd"); 481 default = { }; 482 description = '' 483 TLS Options for the File Daemon. 484 Important notice: The backup won't be encrypted. 485 ''; 486 }; 487 488 extraClientConfig = mkOption { 489 default = ""; 490 type = types.lines; 491 description = '' 492 Extra configuration to be passed in Client directive. 493 ''; 494 example = '' 495 Maximum Concurrent Jobs = 20; 496 Heartbeat Interval = 30; 497 ''; 498 }; 499 500 extraMessagesConfig = mkOption { 501 default = ""; 502 type = types.lines; 503 description = '' 504 Extra configuration to be passed in Messages directive. 505 ''; 506 example = '' 507 console = all 508 ''; 509 }; 510 }; 511 512 services.bacula-sd = { 513 enable = mkOption { 514 type = types.bool; 515 default = false; 516 description = '' 517 Whether to enable Bacula Storage Daemon. 518 ''; 519 }; 520 521 name = mkOption { 522 default = "${config.networking.hostName}-sd"; 523 defaultText = literalExpression ''"''${config.networking.hostName}-sd"''; 524 type = types.str; 525 description = '' 526 Specifies the Name of the Storage daemon. 527 ''; 528 }; 529 530 port = mkOption { 531 default = 9103; 532 type = types.port; 533 description = '' 534 Specifies port number on which the Storage daemon listens for 535 Director connections. 536 ''; 537 }; 538 539 director = mkOption { 540 default = { }; 541 description = '' 542 This option defines Director resources in Bacula Storage Daemon. 543 ''; 544 type = types.attrsOf (types.submodule (directorOptions "services.bacula-sd")); 545 }; 546 547 device = mkOption { 548 default = { }; 549 description = '' 550 This option defines Device resources in Bacula Storage Daemon. 551 ''; 552 type = types.attrsOf (types.submodule deviceOptions); 553 }; 554 555 autochanger = mkOption { 556 default = { }; 557 description = '' 558 This option defines Autochanger resources in Bacula Storage Daemon. 559 ''; 560 type = types.attrsOf (types.submodule autochangerOptions); 561 }; 562 563 extraStorageConfig = mkOption { 564 default = ""; 565 type = types.lines; 566 description = '' 567 Extra configuration to be passed in Storage directive. 568 ''; 569 example = '' 570 Maximum Concurrent Jobs = 20; 571 Heartbeat Interval = 30; 572 ''; 573 }; 574 575 extraMessagesConfig = mkOption { 576 default = ""; 577 type = types.lines; 578 description = '' 579 Extra configuration to be passed in Messages directive. 580 ''; 581 example = '' 582 console = all 583 ''; 584 }; 585 tls = mkOption { 586 type = types.submodule (tlsOptions "services.bacula-sd"); 587 default = { }; 588 description = '' 589 TLS Options for the Storage Daemon. 590 Important notice: The backup won't be encrypted. 591 ''; 592 }; 593 594 }; 595 596 services.bacula-dir = { 597 enable = mkOption { 598 type = types.bool; 599 default = false; 600 description = '' 601 Whether to enable Bacula Director Daemon. 602 ''; 603 }; 604 605 name = mkOption { 606 default = "${config.networking.hostName}-dir"; 607 defaultText = literalExpression ''"''${config.networking.hostName}-dir"''; 608 type = types.str; 609 description = '' 610 The director name used by the system administrator. This directive is 611 required. 612 ''; 613 }; 614 615 port = mkOption { 616 default = 9101; 617 type = types.port; 618 description = '' 619 Specify the port (a positive integer) on which the Director daemon 620 will listen for Bacula Console connections. This same port number 621 must be specified in the Director resource of the Console 622 configuration file. The default is 9101, so normally this directive 623 need not be specified. This directive should not be used if you 624 specify DirAddresses (N.B plural) directive. 625 ''; 626 }; 627 628 password = mkOption { 629 # TODO: required? 630 type = types.str; 631 description = '' 632 Specifies the password that must be supplied for a Director. 633 ''; 634 }; 635 636 extraMessagesConfig = mkOption { 637 default = ""; 638 type = types.lines; 639 description = '' 640 Extra configuration to be passed in Messages directive. 641 ''; 642 example = '' 643 console = all 644 ''; 645 }; 646 647 extraDirectorConfig = mkOption { 648 default = ""; 649 type = types.lines; 650 description = '' 651 Extra configuration to be passed in Director directive. 652 ''; 653 example = '' 654 Maximum Concurrent Jobs = 20; 655 Heartbeat Interval = 30; 656 ''; 657 }; 658 659 extraConfig = mkOption { 660 default = ""; 661 type = types.lines; 662 description = '' 663 Extra configuration for Bacula Director Daemon. 664 ''; 665 example = '' 666 TODO 667 ''; 668 }; 669 670 tls = mkOption { 671 type = types.submodule (tlsOptions "services.bacula-dir"); 672 default = { }; 673 description = '' 674 TLS Options for the Director. 675 Important notice: The backup won't be encrypted. 676 ''; 677 }; 678 }; 679 }; 680 681 config = mkIf (fd_cfg.enable || sd_cfg.enable || dir_cfg.enable) { 682 systemd.slices.system-bacula = { 683 description = "Bacula Backup System Slice"; 684 documentation = [ 685 "man:bacula(8)" 686 "https://www.bacula.org/" 687 ]; 688 }; 689 690 systemd.services.bacula-fd = mkIf fd_cfg.enable { 691 after = [ "network.target" ]; 692 description = "Bacula File Daemon"; 693 wantedBy = [ "multi-user.target" ]; 694 path = [ pkgs.bacula ]; 695 serviceConfig = { 696 ExecStart = "${pkgs.bacula}/sbin/bacula-fd -f -u root -g bacula -c ${fd_conf}"; 697 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; 698 LogsDirectory = "bacula"; 699 StateDirectory = "bacula"; 700 Slice = "system-bacula.slice"; 701 }; 702 }; 703 704 systemd.services.bacula-sd = mkIf sd_cfg.enable { 705 after = [ "network.target" ]; 706 description = "Bacula Storage Daemon"; 707 wantedBy = [ "multi-user.target" ]; 708 path = [ pkgs.bacula ]; 709 serviceConfig = { 710 ExecStart = "${pkgs.bacula}/sbin/bacula-sd -f -u bacula -g bacula -c ${sd_conf}"; 711 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; 712 LogsDirectory = "bacula"; 713 StateDirectory = "bacula"; 714 Slice = "system-bacula.slice"; 715 }; 716 }; 717 718 services.postgresql.enable = lib.mkIf dir_cfg.enable true; 719 720 systemd.services.bacula-dir = mkIf dir_cfg.enable { 721 after = [ 722 "network.target" 723 "postgresql.target" 724 ]; 725 description = "Bacula Director Daemon"; 726 wantedBy = [ "multi-user.target" ]; 727 path = [ pkgs.bacula ]; 728 serviceConfig = { 729 ExecStart = "${pkgs.bacula}/sbin/bacula-dir -f -u bacula -g bacula -c ${dir_conf}"; 730 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; 731 LogsDirectory = "bacula"; 732 StateDirectory = "bacula"; 733 Slice = "system-bacula.slice"; 734 }; 735 preStart = '' 736 if ! test -e "${libDir}/db-created"; then 737 ${pkgs.postgresql}/bin/createuser --no-superuser --no-createdb --no-createrole bacula 738 #${pkgs.postgresql}/bin/createdb --owner bacula bacula 739 740 # populate DB 741 ${pkgs.bacula}/etc/create_bacula_database postgresql 742 ${pkgs.bacula}/etc/make_bacula_tables postgresql 743 ${pkgs.bacula}/etc/grant_bacula_privileges postgresql 744 touch "${libDir}/db-created" 745 else 746 ${pkgs.bacula}/etc/update_bacula_tables postgresql || true 747 fi 748 ''; 749 }; 750 751 environment.systemPackages = [ pkgs.bacula ]; 752 753 users.users.bacula = { 754 group = "bacula"; 755 uid = config.ids.uids.bacula; 756 home = "${libDir}"; 757 createHome = true; 758 description = "Bacula Daemons user"; 759 shell = "${pkgs.bash}/bin/bash"; 760 }; 761 762 users.groups.bacula.gid = config.ids.gids.bacula; 763 }; 764}