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 = ''
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 = ''
139 If Monitor is set to <literal>no</literal>, this director will have
140 full access to this Storage daemon. If Monitor is set to
141 <literal>yes</literal>, 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 = ''
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 <literal>/dev/nst0</literal>, you would specify
165 <literal>/dev/sg0</literal> 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 = ''
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 <literal>"/path/mtx-changer %c %o %S %a %d"</literal>
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 = "";
199 type = types.listOf types.str;
200 };
201
202 extraAutochangerConfig = mkOption {
203 default = "";
204 type = types.lines;
205 description = ''
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 = ''
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 <literal>/dev/nst0</literal> or
227 <literal>/dev/rmt/0mbn</literal>. For a DVD-writer, it will be for
228 example <literal>/dev/hdc</literal>. 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 = ''
240 The specified name-string names the type of media supported by this
241 device, for example, <literal>DLT7000</literal>. 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 <literal>DDS-4</literal>
259 then during the restore, Bacula will be able to choose any Storage
260 Daemon that handles <literal>DDS-4</literal>. 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 = ''
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 = ''
299 Whether to enable the Bacula File Daemon.
300 '';
301 };
302
303 name = mkOption {
304 default = "${config.networking.hostName}-fd";
305 type = types.str;
306 description = ''
307 The client name that must be used by the Director when connecting.
308 Generally, it is a good idea to use a name related to the machine so
309 that error messages can be easily identified if you have multiple
310 Clients. This directive is required.
311 '';
312 };
313
314 port = mkOption {
315 default = 9102;
316 type = types.int;
317 description = ''
318 This specifies the port number on which the Client listens for
319 Director connections. It must agree with the FDPort specified in
320 the Client resource of the Director's configuration file.
321 '';
322 };
323
324 director = mkOption {
325 default = {};
326 description = ''
327 This option defines director resources in Bacula File Daemon.
328 '';
329 type = with types; attrsOf (submodule directorOptions);
330 };
331
332 extraClientConfig = mkOption {
333 default = "";
334 type = types.lines;
335 description = ''
336 Extra configuration to be passed in Client directive.
337 '';
338 example = ''
339 Maximum Concurrent Jobs = 20;
340 Heartbeat Interval = 30;
341 '';
342 };
343
344 extraMessagesConfig = mkOption {
345 default = "";
346 type = types.lines;
347 description = ''
348 Extra configuration to be passed in Messages directive.
349 '';
350 example = ''
351 console = all
352 '';
353 };
354 };
355
356 services.bacula-sd = {
357 enable = mkOption {
358 type = types.bool;
359 default = false;
360 description = ''
361 Whether to enable Bacula Storage Daemon.
362 '';
363 };
364
365 name = mkOption {
366 default = "${config.networking.hostName}-sd";
367 type = types.str;
368 description = ''
369 Specifies the Name of the Storage daemon.
370 '';
371 };
372
373 port = mkOption {
374 default = 9103;
375 type = types.int;
376 description = ''
377 Specifies port number on which the Storage daemon listens for
378 Director connections.
379 '';
380 };
381
382 director = mkOption {
383 default = {};
384 description = ''
385 This option defines Director resources in Bacula Storage Daemon.
386 '';
387 type = with types; attrsOf (submodule directorOptions);
388 };
389
390 device = mkOption {
391 default = {};
392 description = ''
393 This option defines Device resources in Bacula Storage Daemon.
394 '';
395 type = with types; attrsOf (submodule deviceOptions);
396 };
397
398 autochanger = mkOption {
399 default = {};
400 description = ''
401 This option defines Autochanger resources in Bacula Storage Daemon.
402 '';
403 type = with types; attrsOf (submodule autochangerOptions);
404 };
405
406 extraStorageConfig = mkOption {
407 default = "";
408 type = types.lines;
409 description = ''
410 Extra configuration to be passed in Storage directive.
411 '';
412 example = ''
413 Maximum Concurrent Jobs = 20;
414 Heartbeat Interval = 30;
415 '';
416 };
417
418 extraMessagesConfig = mkOption {
419 default = "";
420 type = types.lines;
421 description = ''
422 Extra configuration to be passed in Messages directive.
423 '';
424 example = ''
425 console = all
426 '';
427 };
428
429 };
430
431 services.bacula-dir = {
432 enable = mkOption {
433 type = types.bool;
434 default = false;
435 description = ''
436 Whether to enable Bacula Director Daemon.
437 '';
438 };
439
440 name = mkOption {
441 default = "${config.networking.hostName}-dir";
442 type = types.str;
443 description = ''
444 The director name used by the system administrator. This directive is
445 required.
446 '';
447 };
448
449 port = mkOption {
450 default = 9101;
451 type = types.int;
452 description = ''
453 Specify the port (a positive integer) on which the Director daemon
454 will listen for Bacula Console connections. This same port number
455 must be specified in the Director resource of the Console
456 configuration file. The default is 9101, so normally this directive
457 need not be specified. This directive should not be used if you
458 specify DirAddresses (N.B plural) directive.
459 '';
460 };
461
462 password = mkOption {
463 # TODO: required?
464 type = types.str;
465 description = ''
466 Specifies the password that must be supplied for a Director.
467 '';
468 };
469
470 extraMessagesConfig = mkOption {
471 default = "";
472 type = types.lines;
473 description = ''
474 Extra configuration to be passed in Messages directive.
475 '';
476 example = ''
477 console = all
478 '';
479 };
480
481 extraDirectorConfig = mkOption {
482 default = "";
483 type = types.lines;
484 description = ''
485 Extra configuration to be passed in Director directive.
486 '';
487 example = ''
488 Maximum Concurrent Jobs = 20;
489 Heartbeat Interval = 30;
490 '';
491 };
492
493 extraConfig = mkOption {
494 default = "";
495 type = types.lines;
496 description = ''
497 Extra configuration for Bacula Director Daemon.
498 '';
499 example = ''
500 TODO
501 '';
502 };
503 };
504 };
505
506 config = mkIf (fd_cfg.enable || sd_cfg.enable || dir_cfg.enable) {
507 systemd.services.bacula-fd = mkIf fd_cfg.enable {
508 after = [ "network.target" ];
509 description = "Bacula File Daemon";
510 wantedBy = [ "multi-user.target" ];
511 path = [ pkgs.bacula ];
512 serviceConfig = {
513 ExecStart = "${pkgs.bacula}/sbin/bacula-fd -f -u root -g bacula -c ${fd_conf}";
514 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
515 LogsDirectory = "bacula";
516 StateDirectory = "bacula";
517 };
518 };
519
520 systemd.services.bacula-sd = mkIf sd_cfg.enable {
521 after = [ "network.target" ];
522 description = "Bacula Storage Daemon";
523 wantedBy = [ "multi-user.target" ];
524 path = [ pkgs.bacula ];
525 serviceConfig = {
526 ExecStart = "${pkgs.bacula}/sbin/bacula-sd -f -u bacula -g bacula -c ${sd_conf}";
527 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
528 LogsDirectory = "bacula";
529 StateDirectory = "bacula";
530 };
531 };
532
533 services.postgresql.enable = dir_cfg.enable == true;
534
535 systemd.services.bacula-dir = mkIf dir_cfg.enable {
536 after = [ "network.target" "postgresql.service" ];
537 description = "Bacula Director Daemon";
538 wantedBy = [ "multi-user.target" ];
539 path = [ pkgs.bacula ];
540 serviceConfig = {
541 ExecStart = "${pkgs.bacula}/sbin/bacula-dir -f -u bacula -g bacula -c ${dir_conf}";
542 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
543 LogsDirectory = "bacula";
544 StateDirectory = "bacula";
545 };
546 preStart = ''
547 if ! test -e "${libDir}/db-created"; then
548 ${pkgs.postgresql}/bin/createuser --no-superuser --no-createdb --no-createrole bacula
549 #${pkgs.postgresql}/bin/createdb --owner bacula bacula
550
551 # populate DB
552 ${pkgs.bacula}/etc/create_bacula_database postgresql
553 ${pkgs.bacula}/etc/make_bacula_tables postgresql
554 ${pkgs.bacula}/etc/grant_bacula_privileges postgresql
555 touch "${libDir}/db-created"
556 else
557 ${pkgs.bacula}/etc/update_bacula_tables postgresql || true
558 fi
559 '';
560 };
561
562 environment.systemPackages = [ pkgs.bacula ];
563
564 users.users.bacula = {
565 group = "bacula";
566 uid = config.ids.uids.bacula;
567 home = "${libDir}";
568 createHome = true;
569 description = "Bacula Daemons user";
570 shell = "${pkgs.bash}/bin/bash";
571 };
572
573 users.groups.bacula.gid = config.ids.gids.bacula;
574 };
575}