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