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